root/drivers/net/wireless/ath/ath10k/spectral.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. send_fft_sample
  2. get_max_exp
  3. ath10k_spectral_fix_bin_size
  4. ath10k_spectral_process_fft
  5. ath10k_get_spectral_vdev
  6. ath10k_spectral_scan_trigger
  7. ath10k_spectral_scan_config
  8. read_file_spec_scan_ctl
  9. write_file_spec_scan_ctl
  10. read_file_spectral_count
  11. write_file_spectral_count
  12. read_file_spectral_bins
  13. write_file_spectral_bins
  14. create_buf_file_handler
  15. remove_buf_file_handler
  16. ath10k_spectral_start
  17. ath10k_spectral_vif_stop
  18. ath10k_spectral_create
  19. ath10k_spectral_destroy

   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 
  11 static 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 
  24 static 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 
  48 static 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 
  63 int 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 
 161 static 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 
 179 static 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 
 210 static 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 
 273 static 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 
 301 static 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 
 356 static 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 
 364 static 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 
 381 static 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 
 408 static 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 
 416 static 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 
 437 static 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 
 468 static 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 
 476 static 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 
 493 static int remove_buf_file_handler(struct dentry *dentry)
 494 {
 495         debugfs_remove(dentry);
 496 
 497         return 0;
 498 }
 499 
 500 static 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 
 505 int 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 
 521 int 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 
 529 int 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 
 554 void 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 }

/* [<][>][^][v][top][bottom][index][help] */