ath9k: Move processing of FFT frames to different functions
[linux-2.6-block.git] / drivers / net / wireless / ath / ath9k / common-spectral.c
CommitLineData
f65c0825
SM
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 "ath9k.h"
19
20static s8 fix_rssi_inv_only(u8 rssi_val)
21{
22 if (rssi_val == 128)
23 rssi_val = 0;
24 return (s8) rssi_val;
25}
26
1111d426 27static void ath_debug_send_fft_sample(struct ath_spec_scan_priv *spec_priv,
f65c0825
SM
28 struct fft_sample_tlv *fft_sample_tlv)
29{
30 int length;
1111d426 31 if (!spec_priv->rfs_chan_spec_scan)
f65c0825
SM
32 return;
33
34 length = __be16_to_cpu(fft_sample_tlv->length) +
35 sizeof(*fft_sample_tlv);
1111d426 36 relay_write(spec_priv->rfs_chan_spec_scan, fft_sample_tlv, length);
f65c0825
SM
37}
38
58b5e4c7
NK
39typedef int (ath_cmn_fft_sample_handler) (struct ath_rx_status *rs,
40 struct ath_spec_scan_priv *spec_priv,
41 u8 *sample_buf, u64 tsf, u16 freq, int chan_type);
42
43static int
44ath_cmn_process_ht20_fft(struct ath_rx_status *rs,
45 struct ath_spec_scan_priv *spec_priv,
46 u8 *sample_buf,
47 u64 tsf, u16 freq, int chan_type)
48{
49 struct fft_sample_ht20 fft_sample_20;
50 struct ath_hw *ah = spec_priv->ah;
51 struct ath_ht20_mag_info *mag_info;
52 struct fft_sample_tlv *tlv;
53 int dc_pos = SPECTRAL_HT20_NUM_BINS / 2;
54 u16 magnitude, length;
55 u8 max_index, bitmap_w;
56
57 length = sizeof(fft_sample_20) - sizeof(struct fft_sample_tlv);
58 fft_sample_20.tlv.type = ATH_FFT_SAMPLE_HT20;
59 fft_sample_20.tlv.length = __cpu_to_be16(length);
60 fft_sample_20.freq = __cpu_to_be16(freq);
61 fft_sample_20.rssi = fix_rssi_inv_only(rs->rs_rssi_ctl[0]);
62 fft_sample_20.noise = ah->noise;
63
64 mag_info = (struct ath_ht20_mag_info *) (sample_buf +
65 SPECTRAL_HT20_NUM_BINS);
66
67 magnitude = spectral_max_magnitude(mag_info->all_bins);
68 fft_sample_20.max_magnitude = __cpu_to_be16(magnitude);
69
70 max_index = spectral_max_index(mag_info->all_bins,
71 SPECTRAL_HT20_NUM_BINS);
72 fft_sample_20.max_index = max_index;
73
74 bitmap_w = spectral_bitmap_weight(mag_info->all_bins);
75 fft_sample_20.bitmap_weight = bitmap_w;
76
77 fft_sample_20.max_exp = mag_info->max_exp & 0xf;
78
79 fft_sample_20.tsf = __cpu_to_be64(tsf);
80
81 memcpy(fft_sample_20.data, sample_buf, SPECTRAL_HT20_NUM_BINS);
82
83 /* DC value (value in the middle) is the blind spot of the spectral
84 * sample and invalid, interpolate it.
85 */
86 fft_sample_20.data[dc_pos] = (fft_sample_20.data[dc_pos + 1] +
87 fft_sample_20.data[dc_pos - 1]) / 2;
88
89 tlv = (struct fft_sample_tlv *)&fft_sample_20;
90
91 ath_debug_send_fft_sample(spec_priv, tlv);
92
93 return 0;
94}
95
96static int
97ath_cmn_process_ht20_40_fft(struct ath_rx_status *rs,
98 struct ath_spec_scan_priv *spec_priv,
99 u8 *sample_buf,
100 u64 tsf, u16 freq, int chan_type)
101{
102 struct fft_sample_ht20_40 fft_sample_40;
103 struct ath_hw *ah = spec_priv->ah;
104 struct ath9k_hw_cal_data *caldata = ah->caldata;
105 struct ath_ht20_40_mag_info *mag_info;
106 struct fft_sample_tlv *tlv;
107 int dc_pos = SPECTRAL_HT20_40_NUM_BINS / 2;
108 s16 ext_nf;
109 u16 lower_mag, upper_mag, length;
110 s8 lower_rssi, upper_rssi;
111 u8 lower_max_index, upper_max_index;
112 u8 lower_bitmap_w, upper_bitmap_w;
113
114 if (caldata)
115 ext_nf = ath9k_hw_getchan_noise(ah, ah->curchan,
116 caldata->nfCalHist[3].privNF);
117 else
118 ext_nf = ATH_DEFAULT_NOISE_FLOOR;
119
120 length = sizeof(fft_sample_40) - sizeof(struct fft_sample_tlv);
121 fft_sample_40.tlv.type = ATH_FFT_SAMPLE_HT20_40;
122 fft_sample_40.tlv.length = __cpu_to_be16(length);
123 fft_sample_40.freq = __cpu_to_be16(freq);
124 fft_sample_40.channel_type = chan_type;
125
126 if (chan_type == NL80211_CHAN_HT40PLUS) {
127 lower_rssi = fix_rssi_inv_only(rs->rs_rssi_ctl[0]);
128 upper_rssi = fix_rssi_inv_only(rs->rs_rssi_ext[0]);
129
130 fft_sample_40.lower_noise = ah->noise;
131 fft_sample_40.upper_noise = ext_nf;
132 } else {
133 lower_rssi = fix_rssi_inv_only(rs->rs_rssi_ext[0]);
134 upper_rssi = fix_rssi_inv_only(rs->rs_rssi_ctl[0]);
135
136 fft_sample_40.lower_noise = ext_nf;
137 fft_sample_40.upper_noise = ah->noise;
138 }
139
140 fft_sample_40.lower_rssi = lower_rssi;
141 fft_sample_40.upper_rssi = upper_rssi;
142
143 mag_info = (struct ath_ht20_40_mag_info *) (sample_buf +
144 SPECTRAL_HT20_40_NUM_BINS);
145
146 lower_mag = spectral_max_magnitude(mag_info->lower_bins);
147 fft_sample_40.lower_max_magnitude = __cpu_to_be16(lower_mag);
148
149 upper_mag = spectral_max_magnitude(mag_info->upper_bins);
150 fft_sample_40.upper_max_magnitude = __cpu_to_be16(upper_mag);
151
152 lower_max_index = spectral_max_index(mag_info->lower_bins,
153 SPECTRAL_HT20_40_NUM_BINS);
154 fft_sample_40.lower_max_index = lower_max_index;
155
156 upper_max_index = spectral_max_index(mag_info->upper_bins,
157 SPECTRAL_HT20_40_NUM_BINS);
158 fft_sample_40.upper_max_index = upper_max_index;
159
160 lower_bitmap_w = spectral_bitmap_weight(mag_info->lower_bins);
161 fft_sample_40.lower_bitmap_weight = lower_bitmap_w;
162
163 upper_bitmap_w = spectral_bitmap_weight(mag_info->upper_bins);
164 fft_sample_40.upper_bitmap_weight = upper_bitmap_w;
165
166 fft_sample_40.max_exp = mag_info->max_exp & 0xf;
167
168 fft_sample_40.tsf = __cpu_to_be64(tsf);
169
170 memcpy(fft_sample_40.data, sample_buf, SPECTRAL_HT20_40_NUM_BINS);
171
172 /* DC value (value in the middle) is the blind spot of the spectral
173 * sample and invalid, interpolate it.
174 */
175 fft_sample_40.data[dc_pos] = (fft_sample_40.data[dc_pos + 1] +
176 fft_sample_40.data[dc_pos - 1]) / 2;
177
178 tlv = (struct fft_sample_tlv *)&fft_sample_40;
179
180 ath_debug_send_fft_sample(spec_priv, tlv);
181
182 return 0;
183}
184
f65c0825 185/* returns 1 if this was a spectral frame, even if not handled. */
67dc74f1 186int ath_cmn_process_fft(struct ath_spec_scan_priv *spec_priv, struct ieee80211_hdr *hdr,
f65c0825
SM
187 struct ath_rx_status *rs, u64 tsf)
188{
58b5e4c7 189 u8 sample_buf[SPECTRAL_SAMPLE_MAX_LEN] = {0};
1111d426
OR
190 struct ath_hw *ah = spec_priv->ah;
191 struct ath_common *common = ath9k_hw_common(spec_priv->ah);
58b5e4c7 192 u8 num_bins, *vdata = (u8 *)hdr;
f65c0825
SM
193 struct ath_radar_info *radar_info;
194 int len = rs->rs_datalen;
58b5e4c7 195 u16 fft_len, sample_len, freq = ah->curchan->chan->center_freq;
f65c0825 196 enum nl80211_channel_type chan_type;
58b5e4c7 197 ath_cmn_fft_sample_handler *fft_handler;
f65c0825
SM
198
199 /* AR9280 and before report via ATH9K_PHYERR_RADAR, AR93xx and newer
200 * via ATH9K_PHYERR_SPECTRAL. Haven't seen ATH9K_PHYERR_FALSE_RADAR_EXT
201 * yet, but this is supposed to be possible as well.
202 */
203 if (rs->rs_phyerr != ATH9K_PHYERR_RADAR &&
204 rs->rs_phyerr != ATH9K_PHYERR_FALSE_RADAR_EXT &&
205 rs->rs_phyerr != ATH9K_PHYERR_SPECTRAL)
206 return 0;
207
208 /* check if spectral scan bit is set. This does not have to be checked
209 * if received through a SPECTRAL phy error, but shouldn't hurt.
210 */
211 radar_info = ((struct ath_radar_info *)&vdata[len]) - 1;
212 if (!(radar_info->pulse_bw_info & SPECTRAL_SCAN_BITMASK))
213 return 0;
214
1111d426 215 chan_type = cfg80211_get_chandef_type(&common->hw->conf.chandef);
f65c0825
SM
216 if ((chan_type == NL80211_CHAN_HT40MINUS) ||
217 (chan_type == NL80211_CHAN_HT40PLUS)) {
218 fft_len = SPECTRAL_HT20_40_TOTAL_DATA_LEN;
58b5e4c7 219 sample_len = SPECTRAL_HT20_40_SAMPLE_LEN;
f65c0825 220 num_bins = SPECTRAL_HT20_40_NUM_BINS;
58b5e4c7 221 fft_handler = &ath_cmn_process_ht20_40_fft;
f65c0825
SM
222 } else {
223 fft_len = SPECTRAL_HT20_TOTAL_DATA_LEN;
58b5e4c7 224 sample_len = SPECTRAL_HT20_SAMPLE_LEN;
f65c0825 225 num_bins = SPECTRAL_HT20_NUM_BINS;
58b5e4c7 226 fft_handler = &ath_cmn_process_ht20_fft;
f65c0825
SM
227 }
228
229 /* Variation in the data length is possible and will be fixed later */
230 if ((len > fft_len + 2) || (len < fft_len - 1))
231 return 1;
232
233 switch (len - fft_len) {
234 case 0:
235 /* length correct, nothing to do. */
58b5e4c7 236 memcpy(sample_buf, vdata, sample_len);
f65c0825
SM
237 break;
238 case -1:
239 /* first byte missing, duplicate it. */
58b5e4c7
NK
240 memcpy(&sample_buf[1], vdata, sample_len - 1);
241 sample_buf[0] = vdata[0];
f65c0825
SM
242 break;
243 case 2:
244 /* MAC added 2 extra bytes at bin 30 and 32, remove them. */
58b5e4c7
NK
245 memcpy(sample_buf, vdata, 30);
246 sample_buf[30] = vdata[31];
247 memcpy(&sample_buf[31], &vdata[33], sample_len - 31);
f65c0825
SM
248 break;
249 case 1:
250 /* MAC added 2 extra bytes AND first byte is missing. */
58b5e4c7
NK
251 sample_buf[0] = vdata[0];
252 memcpy(&sample_buf[1], vdata, 30);
253 sample_buf[31] = vdata[31];
254 memcpy(&sample_buf[32], &vdata[33], sample_len - 32);
f65c0825
SM
255 break;
256 default:
257 return 1;
258 }
259
58b5e4c7 260 fft_handler(rs, spec_priv, sample_buf, tsf, freq, chan_type);
f65c0825
SM
261
262 return 1;
263}
67dc74f1 264EXPORT_SYMBOL(ath_cmn_process_fft);
f65c0825
SM
265
266/*********************/
267/* spectral_scan_ctl */
268/*********************/
269
270static ssize_t read_file_spec_scan_ctl(struct file *file, char __user *user_buf,
271 size_t count, loff_t *ppos)
272{
1111d426 273 struct ath_spec_scan_priv *spec_priv = file->private_data;
f65c0825
SM
274 char *mode = "";
275 unsigned int len;
276
1111d426 277 switch (spec_priv->spectral_mode) {
f65c0825
SM
278 case SPECTRAL_DISABLED:
279 mode = "disable";
280 break;
281 case SPECTRAL_BACKGROUND:
282 mode = "background";
283 break;
284 case SPECTRAL_CHANSCAN:
285 mode = "chanscan";
286 break;
287 case SPECTRAL_MANUAL:
288 mode = "manual";
289 break;
290 }
291 len = strlen(mode);
292 return simple_read_from_buffer(user_buf, count, ppos, mode, len);
293}
294
67dc74f1 295void ath9k_cmn_spectral_scan_trigger(struct ath_common *common,
f00a422c
OR
296 struct ath_spec_scan_priv *spec_priv)
297{
298 struct ath_hw *ah = spec_priv->ah;
299 u32 rxfilter;
300
301 if (config_enabled(CONFIG_ATH9K_TX99))
302 return;
303
304 if (!ath9k_hw_ops(ah)->spectral_scan_trigger) {
305 ath_err(common, "spectrum analyzer not implemented on this hardware\n");
306 return;
307 }
308
309 ath_ps_ops(common)->wakeup(common);
310 rxfilter = ath9k_hw_getrxfilter(ah);
311 ath9k_hw_setrxfilter(ah, rxfilter |
312 ATH9K_RX_FILTER_PHYRADAR |
313 ATH9K_RX_FILTER_PHYERR);
314
315 /* TODO: usually this should not be neccesary, but for some reason
316 * (or in some mode?) the trigger must be called after the
317 * configuration, otherwise the register will have its values reset
318 * (on my ar9220 to value 0x01002310)
319 */
67dc74f1 320 ath9k_cmn_spectral_scan_config(common, spec_priv, spec_priv->spectral_mode);
f00a422c
OR
321 ath9k_hw_ops(ah)->spectral_scan_trigger(ah);
322 ath_ps_ops(common)->restore(common);
323}
67dc74f1 324EXPORT_SYMBOL(ath9k_cmn_spectral_scan_trigger);
f00a422c 325
67dc74f1 326int ath9k_cmn_spectral_scan_config(struct ath_common *common,
f00a422c
OR
327 struct ath_spec_scan_priv *spec_priv,
328 enum spectral_mode spectral_mode)
329{
330 struct ath_hw *ah = spec_priv->ah;
331
332 if (!ath9k_hw_ops(ah)->spectral_scan_trigger) {
333 ath_err(common, "spectrum analyzer not implemented on this hardware\n");
334 return -1;
335 }
336
337 switch (spectral_mode) {
338 case SPECTRAL_DISABLED:
339 spec_priv->spec_config.enabled = 0;
340 break;
341 case SPECTRAL_BACKGROUND:
342 /* send endless samples.
343 * TODO: is this really useful for "background"?
344 */
345 spec_priv->spec_config.endless = 1;
346 spec_priv->spec_config.enabled = 1;
347 break;
348 case SPECTRAL_CHANSCAN:
349 case SPECTRAL_MANUAL:
350 spec_priv->spec_config.endless = 0;
351 spec_priv->spec_config.enabled = 1;
352 break;
353 default:
354 return -1;
355 }
356
357 ath_ps_ops(common)->wakeup(common);
358 ath9k_hw_ops(ah)->spectral_scan_config(ah, &spec_priv->spec_config);
359 ath_ps_ops(common)->restore(common);
360
361 spec_priv->spectral_mode = spectral_mode;
362
363 return 0;
364}
67dc74f1 365EXPORT_SYMBOL(ath9k_cmn_spectral_scan_config);
f00a422c 366
f65c0825
SM
367static ssize_t write_file_spec_scan_ctl(struct file *file,
368 const char __user *user_buf,
369 size_t count, loff_t *ppos)
370{
1111d426
OR
371 struct ath_spec_scan_priv *spec_priv = file->private_data;
372 struct ath_common *common = ath9k_hw_common(spec_priv->ah);
f65c0825
SM
373 char buf[32];
374 ssize_t len;
375
376 if (config_enabled(CONFIG_ATH9K_TX99))
377 return -EOPNOTSUPP;
378
379 len = min(count, sizeof(buf) - 1);
380 if (copy_from_user(buf, user_buf, len))
381 return -EFAULT;
382
383 buf[len] = '\0';
384
385 if (strncmp("trigger", buf, 7) == 0) {
67dc74f1 386 ath9k_cmn_spectral_scan_trigger(common, spec_priv);
ded3fb4c 387 } else if (strncmp("background", buf, 10) == 0) {
67dc74f1 388 ath9k_cmn_spectral_scan_config(common, spec_priv, SPECTRAL_BACKGROUND);
f65c0825
SM
389 ath_dbg(common, CONFIG, "spectral scan: background mode enabled\n");
390 } else if (strncmp("chanscan", buf, 8) == 0) {
67dc74f1 391 ath9k_cmn_spectral_scan_config(common, spec_priv, SPECTRAL_CHANSCAN);
f65c0825
SM
392 ath_dbg(common, CONFIG, "spectral scan: channel scan mode enabled\n");
393 } else if (strncmp("manual", buf, 6) == 0) {
67dc74f1 394 ath9k_cmn_spectral_scan_config(common, spec_priv, SPECTRAL_MANUAL);
f65c0825
SM
395 ath_dbg(common, CONFIG, "spectral scan: manual mode enabled\n");
396 } else if (strncmp("disable", buf, 7) == 0) {
67dc74f1 397 ath9k_cmn_spectral_scan_config(common, spec_priv, SPECTRAL_DISABLED);
f65c0825
SM
398 ath_dbg(common, CONFIG, "spectral scan: disabled\n");
399 } else {
400 return -EINVAL;
401 }
402
403 return count;
404}
405
406static const struct file_operations fops_spec_scan_ctl = {
407 .read = read_file_spec_scan_ctl,
408 .write = write_file_spec_scan_ctl,
409 .open = simple_open,
410 .owner = THIS_MODULE,
411 .llseek = default_llseek,
412};
413
414/*************************/
415/* spectral_short_repeat */
416/*************************/
417
418static ssize_t read_file_spectral_short_repeat(struct file *file,
419 char __user *user_buf,
420 size_t count, loff_t *ppos)
421{
1111d426 422 struct ath_spec_scan_priv *spec_priv = file->private_data;
f65c0825
SM
423 char buf[32];
424 unsigned int len;
425
1111d426 426 len = sprintf(buf, "%d\n", spec_priv->spec_config.short_repeat);
f65c0825
SM
427 return simple_read_from_buffer(user_buf, count, ppos, buf, len);
428}
429
430static ssize_t write_file_spectral_short_repeat(struct file *file,
431 const char __user *user_buf,
432 size_t count, loff_t *ppos)
433{
1111d426 434 struct ath_spec_scan_priv *spec_priv = file->private_data;
f65c0825
SM
435 unsigned long val;
436 char buf[32];
437 ssize_t len;
438
439 len = min(count, sizeof(buf) - 1);
440 if (copy_from_user(buf, user_buf, len))
441 return -EFAULT;
442
443 buf[len] = '\0';
444 if (kstrtoul(buf, 0, &val))
445 return -EINVAL;
446
3f557202 447 if (val > 1)
f65c0825
SM
448 return -EINVAL;
449
1111d426 450 spec_priv->spec_config.short_repeat = val;
f65c0825
SM
451 return count;
452}
453
454static const struct file_operations fops_spectral_short_repeat = {
455 .read = read_file_spectral_short_repeat,
456 .write = write_file_spectral_short_repeat,
457 .open = simple_open,
458 .owner = THIS_MODULE,
459 .llseek = default_llseek,
460};
461
462/******************/
463/* spectral_count */
464/******************/
465
466static ssize_t read_file_spectral_count(struct file *file,
467 char __user *user_buf,
468 size_t count, loff_t *ppos)
469{
1111d426 470 struct ath_spec_scan_priv *spec_priv = file->private_data;
f65c0825
SM
471 char buf[32];
472 unsigned int len;
473
1111d426 474 len = sprintf(buf, "%d\n", spec_priv->spec_config.count);
f65c0825
SM
475 return simple_read_from_buffer(user_buf, count, ppos, buf, len);
476}
477
478static ssize_t write_file_spectral_count(struct file *file,
479 const char __user *user_buf,
480 size_t count, loff_t *ppos)
481{
1111d426 482 struct ath_spec_scan_priv *spec_priv = file->private_data;
f65c0825
SM
483 unsigned long val;
484 char buf[32];
485 ssize_t len;
486
487 len = min(count, sizeof(buf) - 1);
488 if (copy_from_user(buf, user_buf, len))
489 return -EFAULT;
490
491 buf[len] = '\0';
492 if (kstrtoul(buf, 0, &val))
493 return -EINVAL;
494
3f557202 495 if (val > 255)
f65c0825
SM
496 return -EINVAL;
497
1111d426 498 spec_priv->spec_config.count = val;
f65c0825
SM
499 return count;
500}
501
502static const struct file_operations fops_spectral_count = {
503 .read = read_file_spectral_count,
504 .write = write_file_spectral_count,
505 .open = simple_open,
506 .owner = THIS_MODULE,
507 .llseek = default_llseek,
508};
509
510/*******************/
511/* spectral_period */
512/*******************/
513
514static ssize_t read_file_spectral_period(struct file *file,
515 char __user *user_buf,
516 size_t count, loff_t *ppos)
517{
1111d426 518 struct ath_spec_scan_priv *spec_priv = file->private_data;
f65c0825
SM
519 char buf[32];
520 unsigned int len;
521
1111d426 522 len = sprintf(buf, "%d\n", spec_priv->spec_config.period);
f65c0825
SM
523 return simple_read_from_buffer(user_buf, count, ppos, buf, len);
524}
525
526static ssize_t write_file_spectral_period(struct file *file,
527 const char __user *user_buf,
528 size_t count, loff_t *ppos)
529{
1111d426 530 struct ath_spec_scan_priv *spec_priv = file->private_data;
f65c0825
SM
531 unsigned long val;
532 char buf[32];
533 ssize_t len;
534
535 len = min(count, sizeof(buf) - 1);
536 if (copy_from_user(buf, user_buf, len))
537 return -EFAULT;
538
539 buf[len] = '\0';
540 if (kstrtoul(buf, 0, &val))
541 return -EINVAL;
542
3f557202 543 if (val > 255)
f65c0825
SM
544 return -EINVAL;
545
1111d426 546 spec_priv->spec_config.period = val;
f65c0825
SM
547 return count;
548}
549
550static const struct file_operations fops_spectral_period = {
551 .read = read_file_spectral_period,
552 .write = write_file_spectral_period,
553 .open = simple_open,
554 .owner = THIS_MODULE,
555 .llseek = default_llseek,
556};
557
558/***********************/
559/* spectral_fft_period */
560/***********************/
561
562static ssize_t read_file_spectral_fft_period(struct file *file,
563 char __user *user_buf,
564 size_t count, loff_t *ppos)
565{
1111d426 566 struct ath_spec_scan_priv *spec_priv = file->private_data;
f65c0825
SM
567 char buf[32];
568 unsigned int len;
569
1111d426 570 len = sprintf(buf, "%d\n", spec_priv->spec_config.fft_period);
f65c0825
SM
571 return simple_read_from_buffer(user_buf, count, ppos, buf, len);
572}
573
574static ssize_t write_file_spectral_fft_period(struct file *file,
575 const char __user *user_buf,
576 size_t count, loff_t *ppos)
577{
1111d426 578 struct ath_spec_scan_priv *spec_priv = file->private_data;
f65c0825
SM
579 unsigned long val;
580 char buf[32];
581 ssize_t len;
582
583 len = min(count, sizeof(buf) - 1);
584 if (copy_from_user(buf, user_buf, len))
585 return -EFAULT;
586
587 buf[len] = '\0';
588 if (kstrtoul(buf, 0, &val))
589 return -EINVAL;
590
3f557202 591 if (val > 15)
f65c0825
SM
592 return -EINVAL;
593
1111d426 594 spec_priv->spec_config.fft_period = val;
f65c0825
SM
595 return count;
596}
597
598static const struct file_operations fops_spectral_fft_period = {
599 .read = read_file_spectral_fft_period,
600 .write = write_file_spectral_fft_period,
601 .open = simple_open,
602 .owner = THIS_MODULE,
603 .llseek = default_llseek,
604};
605
606/*******************/
607/* Relay interface */
608/*******************/
609
610static struct dentry *create_buf_file_handler(const char *filename,
611 struct dentry *parent,
612 umode_t mode,
613 struct rchan_buf *buf,
614 int *is_global)
615{
616 struct dentry *buf_file;
617
618 buf_file = debugfs_create_file(filename, mode, parent, buf,
619 &relay_file_operations);
620 *is_global = 1;
621 return buf_file;
622}
623
624static int remove_buf_file_handler(struct dentry *dentry)
625{
626 debugfs_remove(dentry);
627
628 return 0;
629}
630
f84d1b49 631static struct rchan_callbacks rfs_spec_scan_cb = {
f65c0825
SM
632 .create_buf_file = create_buf_file_handler,
633 .remove_buf_file = remove_buf_file_handler,
634};
635
636/*********************/
637/* Debug Init/Deinit */
638/*********************/
639
67dc74f1 640void ath9k_cmn_spectral_deinit_debug(struct ath_spec_scan_priv *spec_priv)
f65c0825 641{
c0420ea0 642 if (config_enabled(CONFIG_ATH9K_DEBUGFS)) {
1111d426
OR
643 relay_close(spec_priv->rfs_chan_spec_scan);
644 spec_priv->rfs_chan_spec_scan = NULL;
f65c0825
SM
645 }
646}
67dc74f1 647EXPORT_SYMBOL(ath9k_cmn_spectral_deinit_debug);
f65c0825 648
67dc74f1
OR
649void ath9k_cmn_spectral_init_debug(struct ath_spec_scan_priv *spec_priv,
650 struct dentry *debugfs_phy)
f65c0825 651{
1111d426 652 spec_priv->rfs_chan_spec_scan = relay_open("spectral_scan",
c10b75af 653 debugfs_phy,
f65c0825
SM
654 1024, 256, &rfs_spec_scan_cb,
655 NULL);
656 debugfs_create_file("spectral_scan_ctl",
657 S_IRUSR | S_IWUSR,
1111d426 658 debugfs_phy, spec_priv,
f65c0825
SM
659 &fops_spec_scan_ctl);
660 debugfs_create_file("spectral_short_repeat",
661 S_IRUSR | S_IWUSR,
1111d426 662 debugfs_phy, spec_priv,
f65c0825
SM
663 &fops_spectral_short_repeat);
664 debugfs_create_file("spectral_count",
665 S_IRUSR | S_IWUSR,
1111d426 666 debugfs_phy, spec_priv,
f65c0825
SM
667 &fops_spectral_count);
668 debugfs_create_file("spectral_period",
669 S_IRUSR | S_IWUSR,
1111d426 670 debugfs_phy, spec_priv,
f65c0825
SM
671 &fops_spectral_period);
672 debugfs_create_file("spectral_fft_period",
673 S_IRUSR | S_IWUSR,
1111d426 674 debugfs_phy, spec_priv,
f65c0825
SM
675 &fops_spectral_fft_period);
676}
67dc74f1 677EXPORT_SYMBOL(ath9k_cmn_spectral_init_debug);