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