linux/drivers/net/wireless/ath/ath10k/spectral.c
<<
>>
Prefs
   1// SPDX-License-Identifier: ISC
   2/*
   3 * Copyright (c) 2013-2017 Qualcomm Atheros, Inc.
   4 */
   5
   6#include <linux/relay.h>
   7#include "core.h"
   8#include "debug.h"
   9#include "wmi-ops.h"
  10
  11static void send_fft_sample(struct ath10k *ar,
  12                            const struct fft_sample_tlv *fft_sample_tlv)
  13{
  14        int length;
  15
  16        if (!ar->spectral.rfs_chan_spec_scan)
  17                return;
  18
  19        length = __be16_to_cpu(fft_sample_tlv->length) +
  20                 sizeof(*fft_sample_tlv);
  21        relay_write(ar->spectral.rfs_chan_spec_scan, fft_sample_tlv, length);
  22}
  23
  24static uint8_t get_max_exp(s8 max_index, u16 max_magnitude, size_t bin_len,
  25                           u8 *data)
  26{
  27        int dc_pos;
  28        u8 max_exp;
  29
  30        dc_pos = bin_len / 2;
  31
  32        /* peak index outside of bins */
  33        if (dc_pos < max_index || -dc_pos >= max_index)
  34                return 0;
  35
  36        for (max_exp = 0; max_exp < 8; max_exp++) {
  37                if (data[dc_pos + max_index] == (max_magnitude >> max_exp))
  38                        break;
  39        }
  40
  41        /* max_exp not found */
  42        if (data[dc_pos + max_index] != (max_magnitude >> max_exp))
  43                return 0;
  44
  45        return max_exp;
  46}
  47
  48static inline size_t ath10k_spectral_fix_bin_size(struct ath10k *ar,
  49                                                  size_t bin_len)
  50{
  51        /* some chipsets reports bin size as 2^n bytes + 'm' bytes in
  52         * report mode 2. First 2^n bytes carries inband tones and last
  53         * 'm' bytes carries band edge detection data mainly used in
  54         * radar detection purpose. Strip last 'm' bytes to make bin size
  55         * as a valid one. 'm' can take possible values of 4, 12.
  56         */
  57        if (!is_power_of_2(bin_len))
  58                bin_len -= ar->hw_params.spectral_bin_discard;
  59
  60        return bin_len;
  61}
  62
  63int ath10k_spectral_process_fft(struct ath10k *ar,
  64                                struct wmi_phyerr_ev_arg *phyerr,
  65                                const struct phyerr_fft_report *fftr,
  66                                size_t bin_len, u64 tsf)
  67{
  68        struct fft_sample_ath10k *fft_sample;
  69        u8 buf[sizeof(*fft_sample) + SPECTRAL_ATH10K_MAX_NUM_BINS];
  70        u16 freq1, freq2, total_gain_db, base_pwr_db, length, peak_mag;
  71        u32 reg0, reg1;
  72        u8 chain_idx, *bins;
  73        int dc_pos;
  74
  75        fft_sample = (struct fft_sample_ath10k *)&buf;
  76
  77        bin_len = ath10k_spectral_fix_bin_size(ar, bin_len);
  78
  79        if (bin_len < 64 || bin_len > SPECTRAL_ATH10K_MAX_NUM_BINS)
  80                return -EINVAL;
  81
  82        reg0 = __le32_to_cpu(fftr->reg0);
  83        reg1 = __le32_to_cpu(fftr->reg1);
  84
  85        length = sizeof(*fft_sample) - sizeof(struct fft_sample_tlv) + bin_len;
  86        fft_sample->tlv.type = ATH_FFT_SAMPLE_ATH10K;
  87        fft_sample->tlv.length = __cpu_to_be16(length);
  88
  89        /* TODO: there might be a reason why the hardware reports 20/40/80 MHz,
  90         * but the results/plots suggest that its actually 22/44/88 MHz.
  91         */
  92        switch (phyerr->chan_width_mhz) {
  93        case 20:
  94                fft_sample->chan_width_mhz = 22;
  95                break;
  96        case 40:
  97                fft_sample->chan_width_mhz = 44;
  98                break;
  99        case 80:
 100                /* TODO: As experiments with an analogue sender and various
 101                 * configurations (fft-sizes of 64/128/256 and 20/40/80 Mhz)
 102                 * show, the particular configuration of 80 MHz/64 bins does
 103                 * not match with the other samples at all. Until the reason
 104                 * for that is found, don't report these samples.
 105                 */
 106                if (bin_len == 64)
 107                        return -EINVAL;
 108                fft_sample->chan_width_mhz = 88;
 109                break;
 110        default:
 111                fft_sample->chan_width_mhz = phyerr->chan_width_mhz;
 112        }
 113
 114        fft_sample->relpwr_db = MS(reg1, SEARCH_FFT_REPORT_REG1_RELPWR_DB);
 115        fft_sample->avgpwr_db = MS(reg1, SEARCH_FFT_REPORT_REG1_AVGPWR_DB);
 116
 117        peak_mag = MS(reg1, SEARCH_FFT_REPORT_REG1_PEAK_MAG);
 118        fft_sample->max_magnitude = __cpu_to_be16(peak_mag);
 119        fft_sample->max_index = MS(reg0, SEARCH_FFT_REPORT_REG0_PEAK_SIDX);
 120        fft_sample->rssi = phyerr->rssi_combined;
 121
 122        total_gain_db = MS(reg0, SEARCH_FFT_REPORT_REG0_TOTAL_GAIN_DB);
 123        base_pwr_db = MS(reg0, SEARCH_FFT_REPORT_REG0_BASE_PWR_DB);
 124        fft_sample->total_gain_db = __cpu_to_be16(total_gain_db);
 125        fft_sample->base_pwr_db = __cpu_to_be16(base_pwr_db);
 126
 127        freq1 = phyerr->freq1;
 128        freq2 = phyerr->freq2;
 129        fft_sample->freq1 = __cpu_to_be16(freq1);
 130        fft_sample->freq2 = __cpu_to_be16(freq2);
 131
 132        chain_idx = MS(reg0, SEARCH_FFT_REPORT_REG0_FFT_CHN_IDX);
 133
 134        fft_sample->noise = __cpu_to_be16(phyerr->nf_chains[chain_idx]);
 135
 136        bins = (u8 *)fftr;
 137        bins += sizeof(*fftr) + ar->hw_params.spectral_bin_offset;
 138
 139        fft_sample->tsf = __cpu_to_be64(tsf);
 140
 141        /* max_exp has been directly reported by previous hardware (ath9k),
 142         * maybe its possible to get it by other means?
 143         */
 144        fft_sample->max_exp = get_max_exp(fft_sample->max_index, peak_mag,
 145                                          bin_len, bins);
 146
 147        memcpy(fft_sample->data, bins, bin_len);
 148
 149        /* DC value (value in the middle) is the blind spot of the spectral
 150         * sample and invalid, interpolate it.
 151         */
 152        dc_pos = bin_len / 2;
 153        fft_sample->data[dc_pos] = (fft_sample->data[dc_pos + 1] +
 154                                    fft_sample->data[dc_pos - 1]) / 2;
 155
 156        send_fft_sample(ar, &fft_sample->tlv);
 157
 158        return 0;
 159}
 160
 161static struct ath10k_vif *ath10k_get_spectral_vdev(struct ath10k *ar)
 162{
 163        struct ath10k_vif *arvif;
 164
 165        lockdep_assert_held(&ar->conf_mutex);
 166
 167        if (list_empty(&ar->arvifs))
 168                return NULL;
 169
 170        /* if there already is a vif doing spectral, return that. */
 171        list_for_each_entry(arvif, &ar->arvifs, list)
 172                if (arvif->spectral_enabled)
 173                        return arvif;
 174
 175        /* otherwise, return the first vif. */
 176        return list_first_entry(&ar->arvifs, typeof(*arvif), list);
 177}
 178
 179static int ath10k_spectral_scan_trigger(struct ath10k *ar)
 180{
 181        struct ath10k_vif *arvif;
 182        int res;
 183        int vdev_id;
 184
 185        lockdep_assert_held(&ar->conf_mutex);
 186
 187        arvif = ath10k_get_spectral_vdev(ar);
 188        if (!arvif)
 189                return -ENODEV;
 190        vdev_id = arvif->vdev_id;
 191
 192        if (ar->spectral.mode == SPECTRAL_DISABLED)
 193                return 0;
 194
 195        res = ath10k_wmi_vdev_spectral_enable(ar, vdev_id,
 196                                              WMI_SPECTRAL_TRIGGER_CMD_CLEAR,
 197                                              WMI_SPECTRAL_ENABLE_CMD_ENABLE);
 198        if (res < 0)
 199                return res;
 200
 201        res = ath10k_wmi_vdev_spectral_enable(ar, vdev_id,
 202                                              WMI_SPECTRAL_TRIGGER_CMD_TRIGGER,
 203                                              WMI_SPECTRAL_ENABLE_CMD_ENABLE);
 204        if (res < 0)
 205                return res;
 206
 207        return 0;
 208}
 209
 210static int ath10k_spectral_scan_config(struct ath10k *ar,
 211                                       enum ath10k_spectral_mode mode)
 212{
 213        struct wmi_vdev_spectral_conf_arg arg;
 214        struct ath10k_vif *arvif;
 215        int vdev_id, count, res = 0;
 216
 217        lockdep_assert_held(&ar->conf_mutex);
 218
 219        arvif = ath10k_get_spectral_vdev(ar);
 220        if (!arvif)
 221                return -ENODEV;
 222
 223        vdev_id = arvif->vdev_id;
 224
 225        arvif->spectral_enabled = (mode != SPECTRAL_DISABLED);
 226        ar->spectral.mode = mode;
 227
 228        res = ath10k_wmi_vdev_spectral_enable(ar, vdev_id,
 229                                              WMI_SPECTRAL_TRIGGER_CMD_CLEAR,
 230                                              WMI_SPECTRAL_ENABLE_CMD_DISABLE);
 231        if (res < 0) {
 232                ath10k_warn(ar, "failed to enable spectral scan: %d\n", res);
 233                return res;
 234        }
 235
 236        if (mode == SPECTRAL_DISABLED)
 237                return 0;
 238
 239        if (mode == SPECTRAL_BACKGROUND)
 240                count = WMI_SPECTRAL_COUNT_DEFAULT;
 241        else
 242                count = max_t(u8, 1, ar->spectral.config.count);
 243
 244        arg.vdev_id = vdev_id;
 245        arg.scan_count = count;
 246        arg.scan_period = WMI_SPECTRAL_PERIOD_DEFAULT;
 247        arg.scan_priority = WMI_SPECTRAL_PRIORITY_DEFAULT;
 248        arg.scan_fft_size = ar->spectral.config.fft_size;
 249        arg.scan_gc_ena = WMI_SPECTRAL_GC_ENA_DEFAULT;
 250        arg.scan_restart_ena = WMI_SPECTRAL_RESTART_ENA_DEFAULT;
 251        arg.scan_noise_floor_ref = WMI_SPECTRAL_NOISE_FLOOR_REF_DEFAULT;
 252        arg.scan_init_delay = WMI_SPECTRAL_INIT_DELAY_DEFAULT;
 253        arg.scan_nb_tone_thr = WMI_SPECTRAL_NB_TONE_THR_DEFAULT;
 254        arg.scan_str_bin_thr = WMI_SPECTRAL_STR_BIN_THR_DEFAULT;
 255        arg.scan_wb_rpt_mode = WMI_SPECTRAL_WB_RPT_MODE_DEFAULT;
 256        arg.scan_rssi_rpt_mode = WMI_SPECTRAL_RSSI_RPT_MODE_DEFAULT;
 257        arg.scan_rssi_thr = WMI_SPECTRAL_RSSI_THR_DEFAULT;
 258        arg.scan_pwr_format = WMI_SPECTRAL_PWR_FORMAT_DEFAULT;
 259        arg.scan_rpt_mode = WMI_SPECTRAL_RPT_MODE_DEFAULT;
 260        arg.scan_bin_scale = WMI_SPECTRAL_BIN_SCALE_DEFAULT;
 261        arg.scan_dbm_adj = WMI_SPECTRAL_DBM_ADJ_DEFAULT;
 262        arg.scan_chn_mask = WMI_SPECTRAL_CHN_MASK_DEFAULT;
 263
 264        res = ath10k_wmi_vdev_spectral_conf(ar, &arg);
 265        if (res < 0) {
 266                ath10k_warn(ar, "failed to configure spectral scan: %d\n", res);
 267                return res;
 268        }
 269
 270        return 0;
 271}
 272
 273static ssize_t read_file_spec_scan_ctl(struct file *file, char __user *user_buf,
 274                                       size_t count, loff_t *ppos)
 275{
 276        struct ath10k *ar = file->private_data;
 277        char *mode = "";
 278        size_t len;
 279        enum ath10k_spectral_mode spectral_mode;
 280
 281        mutex_lock(&ar->conf_mutex);
 282        spectral_mode = ar->spectral.mode;
 283        mutex_unlock(&ar->conf_mutex);
 284
 285        switch (spectral_mode) {
 286        case SPECTRAL_DISABLED:
 287                mode = "disable";
 288                break;
 289        case SPECTRAL_BACKGROUND:
 290                mode = "background";
 291                break;
 292        case SPECTRAL_MANUAL:
 293                mode = "manual";
 294                break;
 295        }
 296
 297        len = strlen(mode);
 298        return simple_read_from_buffer(user_buf, count, ppos, mode, len);
 299}
 300
 301static ssize_t write_file_spec_scan_ctl(struct file *file,
 302                                        const char __user *user_buf,
 303                                        size_t count, loff_t *ppos)
 304{
 305        struct ath10k *ar = file->private_data;
 306        char buf[32];
 307        ssize_t len;
 308        int res;
 309
 310        len = min(count, sizeof(buf) - 1);
 311        if (copy_from_user(buf, user_buf, len))
 312                return -EFAULT;
 313
 314        buf[len] = '\0';
 315
 316        mutex_lock(&ar->conf_mutex);
 317
 318        if (strncmp("trigger", buf, 7) == 0) {
 319                if (ar->spectral.mode == SPECTRAL_MANUAL ||
 320                    ar->spectral.mode == SPECTRAL_BACKGROUND) {
 321                        /* reset the configuration to adopt possibly changed
 322                         * debugfs parameters
 323                         */
 324                        res = ath10k_spectral_scan_config(ar,
 325                                                          ar->spectral.mode);
 326                        if (res < 0) {
 327                                ath10k_warn(ar, "failed to reconfigure spectral scan: %d\n",
 328                                            res);
 329                        }
 330                        res = ath10k_spectral_scan_trigger(ar);
 331                        if (res < 0) {
 332                                ath10k_warn(ar, "failed to trigger spectral scan: %d\n",
 333                                            res);
 334                        }
 335                } else {
 336                        res = -EINVAL;
 337                }
 338        } else if (strncmp("background", buf, 10) == 0) {
 339                res = ath10k_spectral_scan_config(ar, SPECTRAL_BACKGROUND);
 340        } else if (strncmp("manual", buf, 6) == 0) {
 341                res = ath10k_spectral_scan_config(ar, SPECTRAL_MANUAL);
 342        } else if (strncmp("disable", buf, 7) == 0) {
 343                res = ath10k_spectral_scan_config(ar, SPECTRAL_DISABLED);
 344        } else {
 345                res = -EINVAL;
 346        }
 347
 348        mutex_unlock(&ar->conf_mutex);
 349
 350        if (res < 0)
 351                return res;
 352
 353        return count;
 354}
 355
 356static const struct file_operations fops_spec_scan_ctl = {
 357        .read = read_file_spec_scan_ctl,
 358        .write = write_file_spec_scan_ctl,
 359        .open = simple_open,
 360        .owner = THIS_MODULE,
 361        .llseek = default_llseek,
 362};
 363
 364static ssize_t read_file_spectral_count(struct file *file,
 365                                        char __user *user_buf,
 366                                        size_t count, loff_t *ppos)
 367{
 368        struct ath10k *ar = file->private_data;
 369        char buf[32];
 370        size_t len;
 371        u8 spectral_count;
 372
 373        mutex_lock(&ar->conf_mutex);
 374        spectral_count = ar->spectral.config.count;
 375        mutex_unlock(&ar->conf_mutex);
 376
 377        len = sprintf(buf, "%d\n", spectral_count);
 378        return simple_read_from_buffer(user_buf, count, ppos, buf, len);
 379}
 380
 381static ssize_t write_file_spectral_count(struct file *file,
 382                                         const char __user *user_buf,
 383                                         size_t count, loff_t *ppos)
 384{
 385        struct ath10k *ar = file->private_data;
 386        unsigned long val;
 387        char buf[32];
 388        ssize_t len;
 389
 390        len = min(count, sizeof(buf) - 1);
 391        if (copy_from_user(buf, user_buf, len))
 392                return -EFAULT;
 393
 394        buf[len] = '\0';
 395        if (kstrtoul(buf, 0, &val))
 396                return -EINVAL;
 397
 398        if (val > 255)
 399                return -EINVAL;
 400
 401        mutex_lock(&ar->conf_mutex);
 402        ar->spectral.config.count = val;
 403        mutex_unlock(&ar->conf_mutex);
 404
 405        return count;
 406}
 407
 408static const struct file_operations fops_spectral_count = {
 409        .read = read_file_spectral_count,
 410        .write = write_file_spectral_count,
 411        .open = simple_open,
 412        .owner = THIS_MODULE,
 413        .llseek = default_llseek,
 414};
 415
 416static ssize_t read_file_spectral_bins(struct file *file,
 417                                       char __user *user_buf,
 418                                       size_t count, loff_t *ppos)
 419{
 420        struct ath10k *ar = file->private_data;
 421        char buf[32];
 422        unsigned int bins, fft_size, bin_scale;
 423        size_t len;
 424
 425        mutex_lock(&ar->conf_mutex);
 426
 427        fft_size = ar->spectral.config.fft_size;
 428        bin_scale = WMI_SPECTRAL_BIN_SCALE_DEFAULT;
 429        bins = 1 << (fft_size - bin_scale);
 430
 431        mutex_unlock(&ar->conf_mutex);
 432
 433        len = sprintf(buf, "%d\n", bins);
 434        return simple_read_from_buffer(user_buf, count, ppos, buf, len);
 435}
 436
 437static ssize_t write_file_spectral_bins(struct file *file,
 438                                        const char __user *user_buf,
 439                                        size_t count, loff_t *ppos)
 440{
 441        struct ath10k *ar = file->private_data;
 442        unsigned long val;
 443        char buf[32];
 444        ssize_t len;
 445
 446        len = min(count, sizeof(buf) - 1);
 447        if (copy_from_user(buf, user_buf, len))
 448                return -EFAULT;
 449
 450        buf[len] = '\0';
 451        if (kstrtoul(buf, 0, &val))
 452                return -EINVAL;
 453
 454        if (val < 64 || val > SPECTRAL_ATH10K_MAX_NUM_BINS)
 455                return -EINVAL;
 456
 457        if (!is_power_of_2(val))
 458                return -EINVAL;
 459
 460        mutex_lock(&ar->conf_mutex);
 461        ar->spectral.config.fft_size = ilog2(val);
 462        ar->spectral.config.fft_size += WMI_SPECTRAL_BIN_SCALE_DEFAULT;
 463        mutex_unlock(&ar->conf_mutex);
 464
 465        return count;
 466}
 467
 468static const struct file_operations fops_spectral_bins = {
 469        .read = read_file_spectral_bins,
 470        .write = write_file_spectral_bins,
 471        .open = simple_open,
 472        .owner = THIS_MODULE,
 473        .llseek = default_llseek,
 474};
 475
 476static struct dentry *create_buf_file_handler(const char *filename,
 477                                              struct dentry *parent,
 478                                              umode_t mode,
 479                                              struct rchan_buf *buf,
 480                                              int *is_global)
 481{
 482        struct dentry *buf_file;
 483
 484        buf_file = debugfs_create_file(filename, mode, parent, buf,
 485                                       &relay_file_operations);
 486        if (IS_ERR(buf_file))
 487                return NULL;
 488
 489        *is_global = 1;
 490        return buf_file;
 491}
 492
 493static int remove_buf_file_handler(struct dentry *dentry)
 494{
 495        debugfs_remove(dentry);
 496
 497        return 0;
 498}
 499
 500static const struct rchan_callbacks rfs_spec_scan_cb = {
 501        .create_buf_file = create_buf_file_handler,
 502        .remove_buf_file = remove_buf_file_handler,
 503};
 504
 505int ath10k_spectral_start(struct ath10k *ar)
 506{
 507        struct ath10k_vif *arvif;
 508
 509        lockdep_assert_held(&ar->conf_mutex);
 510
 511        list_for_each_entry(arvif, &ar->arvifs, list)
 512                arvif->spectral_enabled = 0;
 513
 514        ar->spectral.mode = SPECTRAL_DISABLED;
 515        ar->spectral.config.count = WMI_SPECTRAL_COUNT_DEFAULT;
 516        ar->spectral.config.fft_size = WMI_SPECTRAL_FFT_SIZE_DEFAULT;
 517
 518        return 0;
 519}
 520
 521int ath10k_spectral_vif_stop(struct ath10k_vif *arvif)
 522{
 523        if (!arvif->spectral_enabled)
 524                return 0;
 525
 526        return ath10k_spectral_scan_config(arvif->ar, SPECTRAL_DISABLED);
 527}
 528
 529int ath10k_spectral_create(struct ath10k *ar)
 530{
 531        /* The buffer size covers whole channels in dual bands up to 128 bins.
 532         * Scan with bigger than 128 bins needs to be run on single band each.
 533         */
 534        ar->spectral.rfs_chan_spec_scan = relay_open("spectral_scan",
 535                                                     ar->debug.debugfs_phy,
 536                                                     1140, 2500,
 537                                                     &rfs_spec_scan_cb, NULL);
 538        debugfs_create_file("spectral_scan_ctl",
 539                            0600,
 540                            ar->debug.debugfs_phy, ar,
 541                            &fops_spec_scan_ctl);
 542        debugfs_create_file("spectral_count",
 543                            0600,
 544                            ar->debug.debugfs_phy, ar,
 545                            &fops_spectral_count);
 546        debugfs_create_file("spectral_bins",
 547                            0600,
 548                            ar->debug.debugfs_phy, ar,
 549                            &fops_spectral_bins);
 550
 551        return 0;
 552}
 553
 554void ath10k_spectral_destroy(struct ath10k *ar)
 555{
 556        if (ar->spectral.rfs_chan_spec_scan) {
 557                relay_close(ar->spectral.rfs_chan_spec_scan);
 558                ar->spectral.rfs_chan_spec_scan = NULL;
 559        }
 560}
 561