blob: 12db498e1d92fd2f255d5de73eb8bf31d01cc8fc [file] [log] [blame]
Sujith Manoharanf65c0822013-12-18 09:53:18 +05301/*
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
Oleksij Rempel1111d422014-11-06 08:53:23 +010027static void ath_debug_send_fft_sample(struct ath_spec_scan_priv *spec_priv,
Sujith Manoharanf65c0822013-12-18 09:53:18 +053028 struct fft_sample_tlv *fft_sample_tlv)
29{
30 int length;
Oleksij Rempel1111d422014-11-06 08:53:23 +010031 if (!spec_priv->rfs_chan_spec_scan)
Sujith Manoharanf65c0822013-12-18 09:53:18 +053032 return;
33
34 length = __be16_to_cpu(fft_sample_tlv->length) +
35 sizeof(*fft_sample_tlv);
Oleksij Rempel1111d422014-11-06 08:53:23 +010036 relay_write(spec_priv->rfs_chan_spec_scan, fft_sample_tlv, length);
Sujith Manoharanf65c0822013-12-18 09:53:18 +053037}
38
Nick Kossifidis58b5e4c2015-04-29 23:51:14 +000039typedef 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;
Nick Kossifidis7fa580c2015-04-29 23:51:15 +000050 struct ath_common *common = ath9k_hw_common(spec_priv->ah);
Nick Kossifidis58b5e4c2015-04-29 23:51:14 +000051 struct ath_hw *ah = spec_priv->ah;
52 struct ath_ht20_mag_info *mag_info;
53 struct fft_sample_tlv *tlv;
Nick Kossifidis7fa580c2015-04-29 23:51:15 +000054 int i = 0;
55 int ret = 0;
Nick Kossifidis58b5e4c2015-04-29 23:51:14 +000056 int dc_pos = SPECTRAL_HT20_NUM_BINS / 2;
Nick Kossifidis7fa580c2015-04-29 23:51:15 +000057 u16 magnitude, tmp_mag, length;
58 u8 max_index, bitmap_w, max_exp;
Nick Kossifidis58b5e4c2015-04-29 23:51:14 +000059
60 length = sizeof(fft_sample_20) - sizeof(struct fft_sample_tlv);
61 fft_sample_20.tlv.type = ATH_FFT_SAMPLE_HT20;
62 fft_sample_20.tlv.length = __cpu_to_be16(length);
63 fft_sample_20.freq = __cpu_to_be16(freq);
64 fft_sample_20.rssi = fix_rssi_inv_only(rs->rs_rssi_ctl[0]);
65 fft_sample_20.noise = ah->noise;
66
67 mag_info = (struct ath_ht20_mag_info *) (sample_buf +
68 SPECTRAL_HT20_NUM_BINS);
69
70 magnitude = spectral_max_magnitude(mag_info->all_bins);
71 fft_sample_20.max_magnitude = __cpu_to_be16(magnitude);
72
73 max_index = spectral_max_index(mag_info->all_bins,
74 SPECTRAL_HT20_NUM_BINS);
75 fft_sample_20.max_index = max_index;
76
77 bitmap_w = spectral_bitmap_weight(mag_info->all_bins);
78 fft_sample_20.bitmap_weight = bitmap_w;
79
Nick Kossifidis7fa580c2015-04-29 23:51:15 +000080 max_exp = mag_info->max_exp & 0xf;
81 fft_sample_20.max_exp = max_exp;
Nick Kossifidis58b5e4c2015-04-29 23:51:14 +000082
83 fft_sample_20.tsf = __cpu_to_be64(tsf);
84
85 memcpy(fft_sample_20.data, sample_buf, SPECTRAL_HT20_NUM_BINS);
86
Nick Kossifidis7fa580c2015-04-29 23:51:15 +000087 ath_dbg(common, SPECTRAL_SCAN, "FFT HT20 frame: max mag 0x%X,"
88 "max_mag_idx %i\n",
89 magnitude >> max_exp,
90 max_index);
91
92 if (fft_sample_20.data[max_index] != (magnitude >> max_exp)) {
93 ath_dbg(common, SPECTRAL_SCAN, "Magnitude mismatch !\n");
94 ret = -1;
95 }
96
Nick Kossifidis58b5e4c2015-04-29 23:51:14 +000097 /* DC value (value in the middle) is the blind spot of the spectral
98 * sample and invalid, interpolate it.
99 */
100 fft_sample_20.data[dc_pos] = (fft_sample_20.data[dc_pos + 1] +
101 fft_sample_20.data[dc_pos - 1]) / 2;
102
Nick Kossifidis7fa580c2015-04-29 23:51:15 +0000103 /* Check if the maximum magnitude is indeed maximum,
104 * also if the maximum value was at dc_pos, calculate
105 * a new one (since value at dc_pos is invalid).
106 */
107 if (max_index == dc_pos) {
108 tmp_mag = 0;
109 for (i = 0; i < dc_pos; i++) {
110 if (fft_sample_20.data[i] > tmp_mag) {
111 tmp_mag = fft_sample_20.data[i];
112 fft_sample_20.max_index = i;
113 }
114 }
115
116 magnitude = tmp_mag << max_exp;
117 fft_sample_20.max_magnitude = __cpu_to_be16(magnitude);
118
119 ath_dbg(common, SPECTRAL_SCAN,
120 "Calculated new lower max 0x%X at %i\n",
121 tmp_mag, fft_sample_20.max_index);
122 } else
123 for (i = 0; i < SPECTRAL_HT20_NUM_BINS; i++) {
124 if (fft_sample_20.data[i] == (magnitude >> max_exp))
125 ath_dbg(common, SPECTRAL_SCAN,
126 "Got max: 0x%X at index %i\n",
127 fft_sample_20.data[i], i);
128
129 if (fft_sample_20.data[i] > (magnitude >> max_exp)) {
130 ath_dbg(common, SPECTRAL_SCAN,
131 "Got bin %i greater than max: 0x%X\n",
132 i, fft_sample_20.data[i]);
133 ret = -1;
134 }
135 }
136
137 if (ret < 0)
138 return ret;
139
Nick Kossifidis58b5e4c2015-04-29 23:51:14 +0000140 tlv = (struct fft_sample_tlv *)&fft_sample_20;
141
142 ath_debug_send_fft_sample(spec_priv, tlv);
143
144 return 0;
145}
146
147static int
148ath_cmn_process_ht20_40_fft(struct ath_rx_status *rs,
149 struct ath_spec_scan_priv *spec_priv,
150 u8 *sample_buf,
151 u64 tsf, u16 freq, int chan_type)
152{
153 struct fft_sample_ht20_40 fft_sample_40;
Nick Kossifidis7fa580c2015-04-29 23:51:15 +0000154 struct ath_common *common = ath9k_hw_common(spec_priv->ah);
Nick Kossifidis58b5e4c2015-04-29 23:51:14 +0000155 struct ath_hw *ah = spec_priv->ah;
156 struct ath9k_hw_cal_data *caldata = ah->caldata;
157 struct ath_ht20_40_mag_info *mag_info;
158 struct fft_sample_tlv *tlv;
159 int dc_pos = SPECTRAL_HT20_40_NUM_BINS / 2;
Nick Kossifidis7fa580c2015-04-29 23:51:15 +0000160 int i = 0;
161 int ret = 0;
Nick Kossifidis58b5e4c2015-04-29 23:51:14 +0000162 s16 ext_nf;
Nick Kossifidis7fa580c2015-04-29 23:51:15 +0000163 u16 lower_mag, upper_mag, tmp_mag, length;
Nick Kossifidis58b5e4c2015-04-29 23:51:14 +0000164 s8 lower_rssi, upper_rssi;
165 u8 lower_max_index, upper_max_index;
Nick Kossifidis7fa580c2015-04-29 23:51:15 +0000166 u8 lower_bitmap_w, upper_bitmap_w, max_exp;
Nick Kossifidis58b5e4c2015-04-29 23:51:14 +0000167
168 if (caldata)
169 ext_nf = ath9k_hw_getchan_noise(ah, ah->curchan,
170 caldata->nfCalHist[3].privNF);
171 else
172 ext_nf = ATH_DEFAULT_NOISE_FLOOR;
173
174 length = sizeof(fft_sample_40) - sizeof(struct fft_sample_tlv);
175 fft_sample_40.tlv.type = ATH_FFT_SAMPLE_HT20_40;
176 fft_sample_40.tlv.length = __cpu_to_be16(length);
177 fft_sample_40.freq = __cpu_to_be16(freq);
178 fft_sample_40.channel_type = chan_type;
179
180 if (chan_type == NL80211_CHAN_HT40PLUS) {
181 lower_rssi = fix_rssi_inv_only(rs->rs_rssi_ctl[0]);
182 upper_rssi = fix_rssi_inv_only(rs->rs_rssi_ext[0]);
183
184 fft_sample_40.lower_noise = ah->noise;
185 fft_sample_40.upper_noise = ext_nf;
186 } else {
187 lower_rssi = fix_rssi_inv_only(rs->rs_rssi_ext[0]);
188 upper_rssi = fix_rssi_inv_only(rs->rs_rssi_ctl[0]);
189
190 fft_sample_40.lower_noise = ext_nf;
191 fft_sample_40.upper_noise = ah->noise;
192 }
193
194 fft_sample_40.lower_rssi = lower_rssi;
195 fft_sample_40.upper_rssi = upper_rssi;
196
197 mag_info = (struct ath_ht20_40_mag_info *) (sample_buf +
198 SPECTRAL_HT20_40_NUM_BINS);
199
200 lower_mag = spectral_max_magnitude(mag_info->lower_bins);
201 fft_sample_40.lower_max_magnitude = __cpu_to_be16(lower_mag);
202
203 upper_mag = spectral_max_magnitude(mag_info->upper_bins);
204 fft_sample_40.upper_max_magnitude = __cpu_to_be16(upper_mag);
205
206 lower_max_index = spectral_max_index(mag_info->lower_bins,
207 SPECTRAL_HT20_40_NUM_BINS);
208 fft_sample_40.lower_max_index = lower_max_index;
209
210 upper_max_index = spectral_max_index(mag_info->upper_bins,
211 SPECTRAL_HT20_40_NUM_BINS);
212 fft_sample_40.upper_max_index = upper_max_index;
213
214 lower_bitmap_w = spectral_bitmap_weight(mag_info->lower_bins);
215 fft_sample_40.lower_bitmap_weight = lower_bitmap_w;
216
217 upper_bitmap_w = spectral_bitmap_weight(mag_info->upper_bins);
218 fft_sample_40.upper_bitmap_weight = upper_bitmap_w;
219
Nick Kossifidis7fa580c2015-04-29 23:51:15 +0000220 max_exp = mag_info->max_exp & 0xf;
221 fft_sample_40.max_exp = max_exp;
Nick Kossifidis58b5e4c2015-04-29 23:51:14 +0000222
223 fft_sample_40.tsf = __cpu_to_be64(tsf);
224
225 memcpy(fft_sample_40.data, sample_buf, SPECTRAL_HT20_40_NUM_BINS);
226
Nick Kossifidis7fa580c2015-04-29 23:51:15 +0000227 ath_dbg(common, SPECTRAL_SCAN, "FFT HT20/40 frame: lower mag 0x%X,"
228 "lower_mag_idx %i, upper mag 0x%X,"
229 "upper_mag_idx %i\n",
230 lower_mag >> max_exp,
231 lower_max_index,
232 upper_mag >> max_exp,
233 upper_max_index);
234
235 /* Some time hardware messes up the index and adds
236 * the index of the middle point (dc_pos). Try to fix it.
237 */
238 if ((upper_max_index - dc_pos > 0) &&
239 (fft_sample_40.data[upper_max_index] == (upper_mag >> max_exp))) {
240 upper_max_index -= dc_pos;
241 fft_sample_40.upper_max_index = upper_max_index;
242 }
243
244 if ((lower_max_index - dc_pos > 0) &&
245 (fft_sample_40.data[lower_max_index - dc_pos] ==
246 (lower_mag >> max_exp))) {
247 lower_max_index -= dc_pos;
248 fft_sample_40.lower_max_index = lower_max_index;
249 }
250
251 /* Check if we got the expected magnitude values at
252 * the expected bins
253 */
254 if ((fft_sample_40.data[upper_max_index + dc_pos]
255 != (upper_mag >> max_exp)) ||
256 (fft_sample_40.data[lower_max_index]
257 != (lower_mag >> max_exp))) {
258 ath_dbg(common, SPECTRAL_SCAN, "Magnitude mismatch !\n");
259 ret = -1;
260 }
261
Nick Kossifidis58b5e4c2015-04-29 23:51:14 +0000262 /* DC value (value in the middle) is the blind spot of the spectral
263 * sample and invalid, interpolate it.
264 */
265 fft_sample_40.data[dc_pos] = (fft_sample_40.data[dc_pos + 1] +
266 fft_sample_40.data[dc_pos - 1]) / 2;
267
Nick Kossifidis7fa580c2015-04-29 23:51:15 +0000268 /* Check if the maximum magnitudes are indeed maximum,
269 * also if the maximum value was at dc_pos, calculate
270 * a new one (since value at dc_pos is invalid).
271 */
272 if (lower_max_index == dc_pos) {
273 tmp_mag = 0;
274 for (i = 0; i < dc_pos; i++) {
275 if (fft_sample_40.data[i] > tmp_mag) {
276 tmp_mag = fft_sample_40.data[i];
277 fft_sample_40.lower_max_index = i;
278 }
279 }
280
281 lower_mag = tmp_mag << max_exp;
282 fft_sample_40.lower_max_magnitude = __cpu_to_be16(lower_mag);
283
284 ath_dbg(common, SPECTRAL_SCAN,
285 "Calculated new lower max 0x%X at %i\n",
286 tmp_mag, fft_sample_40.lower_max_index);
287 } else
288 for (i = 0; i < dc_pos; i++) {
289 if (fft_sample_40.data[i] == (lower_mag >> max_exp))
290 ath_dbg(common, SPECTRAL_SCAN,
291 "Got lower mag: 0x%X at index %i\n",
292 fft_sample_40.data[i], i);
293
294 if (fft_sample_40.data[i] > (lower_mag >> max_exp)) {
295 ath_dbg(common, SPECTRAL_SCAN,
296 "Got lower bin %i higher than max: 0x%X\n",
297 i, fft_sample_40.data[i]);
298 ret = -1;
299 }
300 }
301
302 if (upper_max_index == dc_pos) {
303 tmp_mag = 0;
304 for (i = dc_pos; i < SPECTRAL_HT20_40_NUM_BINS; i++) {
305 if (fft_sample_40.data[i] > tmp_mag) {
306 tmp_mag = fft_sample_40.data[i];
307 fft_sample_40.upper_max_index = i;
308 }
309 }
310 upper_mag = tmp_mag << max_exp;
311 fft_sample_40.upper_max_magnitude = __cpu_to_be16(upper_mag);
312
313 ath_dbg(common, SPECTRAL_SCAN,
314 "Calculated new upper max 0x%X at %i\n",
315 tmp_mag, i);
316 } else
317 for (i = dc_pos; i < SPECTRAL_HT20_40_NUM_BINS; i++) {
318 if (fft_sample_40.data[i] == (upper_mag >> max_exp))
319 ath_dbg(common, SPECTRAL_SCAN,
320 "Got upper mag: 0x%X at index %i\n",
321 fft_sample_40.data[i], i);
322
323 if (fft_sample_40.data[i] > (upper_mag >> max_exp)) {
324 ath_dbg(common, SPECTRAL_SCAN,
325 "Got upper bin %i higher than max: 0x%X\n",
326 i, fft_sample_40.data[i]);
327
328 ret = -1;
329 }
330 }
331
332 if (ret < 0)
333 return ret;
334
Nick Kossifidis58b5e4c2015-04-29 23:51:14 +0000335 tlv = (struct fft_sample_tlv *)&fft_sample_40;
336
337 ath_debug_send_fft_sample(spec_priv, tlv);
338
339 return 0;
340}
341
Sujith Manoharanf65c0822013-12-18 09:53:18 +0530342/* returns 1 if this was a spectral frame, even if not handled. */
Oleksij Rempel67dc74f2014-11-06 08:53:30 +0100343int ath_cmn_process_fft(struct ath_spec_scan_priv *spec_priv, struct ieee80211_hdr *hdr,
Sujith Manoharanf65c0822013-12-18 09:53:18 +0530344 struct ath_rx_status *rs, u64 tsf)
345{
Nick Kossifidis58b5e4c2015-04-29 23:51:14 +0000346 u8 sample_buf[SPECTRAL_SAMPLE_MAX_LEN] = {0};
Oleksij Rempel1111d422014-11-06 08:53:23 +0100347 struct ath_hw *ah = spec_priv->ah;
348 struct ath_common *common = ath9k_hw_common(spec_priv->ah);
Nick Kossifidis58b5e4c2015-04-29 23:51:14 +0000349 u8 num_bins, *vdata = (u8 *)hdr;
Sujith Manoharanf65c0822013-12-18 09:53:18 +0530350 struct ath_radar_info *radar_info;
351 int len = rs->rs_datalen;
Nick Kossifidis58b5e4c2015-04-29 23:51:14 +0000352 u16 fft_len, sample_len, freq = ah->curchan->chan->center_freq;
Sujith Manoharanf65c0822013-12-18 09:53:18 +0530353 enum nl80211_channel_type chan_type;
Nick Kossifidis58b5e4c2015-04-29 23:51:14 +0000354 ath_cmn_fft_sample_handler *fft_handler;
Sujith Manoharanf65c0822013-12-18 09:53:18 +0530355
356 /* AR9280 and before report via ATH9K_PHYERR_RADAR, AR93xx and newer
357 * via ATH9K_PHYERR_SPECTRAL. Haven't seen ATH9K_PHYERR_FALSE_RADAR_EXT
358 * yet, but this is supposed to be possible as well.
359 */
360 if (rs->rs_phyerr != ATH9K_PHYERR_RADAR &&
361 rs->rs_phyerr != ATH9K_PHYERR_FALSE_RADAR_EXT &&
362 rs->rs_phyerr != ATH9K_PHYERR_SPECTRAL)
363 return 0;
364
365 /* check if spectral scan bit is set. This does not have to be checked
366 * if received through a SPECTRAL phy error, but shouldn't hurt.
367 */
368 radar_info = ((struct ath_radar_info *)&vdata[len]) - 1;
369 if (!(radar_info->pulse_bw_info & SPECTRAL_SCAN_BITMASK))
370 return 0;
371
Oleksij Rempel1111d422014-11-06 08:53:23 +0100372 chan_type = cfg80211_get_chandef_type(&common->hw->conf.chandef);
Sujith Manoharanf65c0822013-12-18 09:53:18 +0530373 if ((chan_type == NL80211_CHAN_HT40MINUS) ||
374 (chan_type == NL80211_CHAN_HT40PLUS)) {
375 fft_len = SPECTRAL_HT20_40_TOTAL_DATA_LEN;
Nick Kossifidis58b5e4c2015-04-29 23:51:14 +0000376 sample_len = SPECTRAL_HT20_40_SAMPLE_LEN;
Sujith Manoharanf65c0822013-12-18 09:53:18 +0530377 num_bins = SPECTRAL_HT20_40_NUM_BINS;
Nick Kossifidis58b5e4c2015-04-29 23:51:14 +0000378 fft_handler = &ath_cmn_process_ht20_40_fft;
Sujith Manoharanf65c0822013-12-18 09:53:18 +0530379 } else {
380 fft_len = SPECTRAL_HT20_TOTAL_DATA_LEN;
Nick Kossifidis58b5e4c2015-04-29 23:51:14 +0000381 sample_len = SPECTRAL_HT20_SAMPLE_LEN;
Sujith Manoharanf65c0822013-12-18 09:53:18 +0530382 num_bins = SPECTRAL_HT20_NUM_BINS;
Nick Kossifidis58b5e4c2015-04-29 23:51:14 +0000383 fft_handler = &ath_cmn_process_ht20_fft;
Sujith Manoharanf65c0822013-12-18 09:53:18 +0530384 }
385
386 /* Variation in the data length is possible and will be fixed later */
387 if ((len > fft_len + 2) || (len < fft_len - 1))
388 return 1;
389
390 switch (len - fft_len) {
391 case 0:
392 /* length correct, nothing to do. */
Nick Kossifidis58b5e4c2015-04-29 23:51:14 +0000393 memcpy(sample_buf, vdata, sample_len);
Sujith Manoharanf65c0822013-12-18 09:53:18 +0530394 break;
395 case -1:
396 /* first byte missing, duplicate it. */
Nick Kossifidis58b5e4c2015-04-29 23:51:14 +0000397 memcpy(&sample_buf[1], vdata, sample_len - 1);
398 sample_buf[0] = vdata[0];
Sujith Manoharanf65c0822013-12-18 09:53:18 +0530399 break;
400 case 2:
401 /* MAC added 2 extra bytes at bin 30 and 32, remove them. */
Nick Kossifidis58b5e4c2015-04-29 23:51:14 +0000402 memcpy(sample_buf, vdata, 30);
403 sample_buf[30] = vdata[31];
404 memcpy(&sample_buf[31], &vdata[33], sample_len - 31);
Sujith Manoharanf65c0822013-12-18 09:53:18 +0530405 break;
406 case 1:
407 /* MAC added 2 extra bytes AND first byte is missing. */
Nick Kossifidis58b5e4c2015-04-29 23:51:14 +0000408 sample_buf[0] = vdata[0];
409 memcpy(&sample_buf[1], vdata, 30);
410 sample_buf[31] = vdata[31];
411 memcpy(&sample_buf[32], &vdata[33], sample_len - 32);
Sujith Manoharanf65c0822013-12-18 09:53:18 +0530412 break;
413 default:
414 return 1;
415 }
416
Nick Kossifidis58b5e4c2015-04-29 23:51:14 +0000417 fft_handler(rs, spec_priv, sample_buf, tsf, freq, chan_type);
Sujith Manoharanf65c0822013-12-18 09:53:18 +0530418
419 return 1;
420}
Oleksij Rempel67dc74f2014-11-06 08:53:30 +0100421EXPORT_SYMBOL(ath_cmn_process_fft);
Sujith Manoharanf65c0822013-12-18 09:53:18 +0530422
423/*********************/
424/* spectral_scan_ctl */
425/*********************/
426
427static ssize_t read_file_spec_scan_ctl(struct file *file, char __user *user_buf,
428 size_t count, loff_t *ppos)
429{
Oleksij Rempel1111d422014-11-06 08:53:23 +0100430 struct ath_spec_scan_priv *spec_priv = file->private_data;
Sujith Manoharanf65c0822013-12-18 09:53:18 +0530431 char *mode = "";
432 unsigned int len;
433
Oleksij Rempel1111d422014-11-06 08:53:23 +0100434 switch (spec_priv->spectral_mode) {
Sujith Manoharanf65c0822013-12-18 09:53:18 +0530435 case SPECTRAL_DISABLED:
436 mode = "disable";
437 break;
438 case SPECTRAL_BACKGROUND:
439 mode = "background";
440 break;
441 case SPECTRAL_CHANSCAN:
442 mode = "chanscan";
443 break;
444 case SPECTRAL_MANUAL:
445 mode = "manual";
446 break;
447 }
448 len = strlen(mode);
449 return simple_read_from_buffer(user_buf, count, ppos, mode, len);
450}
451
Oleksij Rempel67dc74f2014-11-06 08:53:30 +0100452void ath9k_cmn_spectral_scan_trigger(struct ath_common *common,
Oleksij Rempelf00a4222014-11-06 08:53:29 +0100453 struct ath_spec_scan_priv *spec_priv)
454{
455 struct ath_hw *ah = spec_priv->ah;
456 u32 rxfilter;
457
458 if (config_enabled(CONFIG_ATH9K_TX99))
459 return;
460
461 if (!ath9k_hw_ops(ah)->spectral_scan_trigger) {
462 ath_err(common, "spectrum analyzer not implemented on this hardware\n");
463 return;
464 }
465
466 ath_ps_ops(common)->wakeup(common);
467 rxfilter = ath9k_hw_getrxfilter(ah);
468 ath9k_hw_setrxfilter(ah, rxfilter |
469 ATH9K_RX_FILTER_PHYRADAR |
470 ATH9K_RX_FILTER_PHYERR);
471
472 /* TODO: usually this should not be neccesary, but for some reason
473 * (or in some mode?) the trigger must be called after the
474 * configuration, otherwise the register will have its values reset
475 * (on my ar9220 to value 0x01002310)
476 */
Oleksij Rempel67dc74f2014-11-06 08:53:30 +0100477 ath9k_cmn_spectral_scan_config(common, spec_priv, spec_priv->spectral_mode);
Oleksij Rempelf00a4222014-11-06 08:53:29 +0100478 ath9k_hw_ops(ah)->spectral_scan_trigger(ah);
479 ath_ps_ops(common)->restore(common);
480}
Oleksij Rempel67dc74f2014-11-06 08:53:30 +0100481EXPORT_SYMBOL(ath9k_cmn_spectral_scan_trigger);
Oleksij Rempelf00a4222014-11-06 08:53:29 +0100482
Oleksij Rempel67dc74f2014-11-06 08:53:30 +0100483int ath9k_cmn_spectral_scan_config(struct ath_common *common,
Oleksij Rempelf00a4222014-11-06 08:53:29 +0100484 struct ath_spec_scan_priv *spec_priv,
485 enum spectral_mode spectral_mode)
486{
487 struct ath_hw *ah = spec_priv->ah;
488
489 if (!ath9k_hw_ops(ah)->spectral_scan_trigger) {
490 ath_err(common, "spectrum analyzer not implemented on this hardware\n");
491 return -1;
492 }
493
494 switch (spectral_mode) {
495 case SPECTRAL_DISABLED:
496 spec_priv->spec_config.enabled = 0;
497 break;
498 case SPECTRAL_BACKGROUND:
499 /* send endless samples.
500 * TODO: is this really useful for "background"?
501 */
502 spec_priv->spec_config.endless = 1;
503 spec_priv->spec_config.enabled = 1;
504 break;
505 case SPECTRAL_CHANSCAN:
506 case SPECTRAL_MANUAL:
507 spec_priv->spec_config.endless = 0;
508 spec_priv->spec_config.enabled = 1;
509 break;
510 default:
511 return -1;
512 }
513
514 ath_ps_ops(common)->wakeup(common);
515 ath9k_hw_ops(ah)->spectral_scan_config(ah, &spec_priv->spec_config);
516 ath_ps_ops(common)->restore(common);
517
518 spec_priv->spectral_mode = spectral_mode;
519
520 return 0;
521}
Oleksij Rempel67dc74f2014-11-06 08:53:30 +0100522EXPORT_SYMBOL(ath9k_cmn_spectral_scan_config);
Oleksij Rempelf00a4222014-11-06 08:53:29 +0100523
Sujith Manoharanf65c0822013-12-18 09:53:18 +0530524static ssize_t write_file_spec_scan_ctl(struct file *file,
525 const char __user *user_buf,
526 size_t count, loff_t *ppos)
527{
Oleksij Rempel1111d422014-11-06 08:53:23 +0100528 struct ath_spec_scan_priv *spec_priv = file->private_data;
529 struct ath_common *common = ath9k_hw_common(spec_priv->ah);
Sujith Manoharanf65c0822013-12-18 09:53:18 +0530530 char buf[32];
531 ssize_t len;
532
533 if (config_enabled(CONFIG_ATH9K_TX99))
534 return -EOPNOTSUPP;
535
536 len = min(count, sizeof(buf) - 1);
537 if (copy_from_user(buf, user_buf, len))
538 return -EFAULT;
539
540 buf[len] = '\0';
541
542 if (strncmp("trigger", buf, 7) == 0) {
Oleksij Rempel67dc74f2014-11-06 08:53:30 +0100543 ath9k_cmn_spectral_scan_trigger(common, spec_priv);
Maks Naumovded3fb42014-08-16 00:41:07 -0700544 } else if (strncmp("background", buf, 10) == 0) {
Oleksij Rempel67dc74f2014-11-06 08:53:30 +0100545 ath9k_cmn_spectral_scan_config(common, spec_priv, SPECTRAL_BACKGROUND);
Sujith Manoharanf65c0822013-12-18 09:53:18 +0530546 ath_dbg(common, CONFIG, "spectral scan: background mode enabled\n");
547 } else if (strncmp("chanscan", buf, 8) == 0) {
Oleksij Rempel67dc74f2014-11-06 08:53:30 +0100548 ath9k_cmn_spectral_scan_config(common, spec_priv, SPECTRAL_CHANSCAN);
Sujith Manoharanf65c0822013-12-18 09:53:18 +0530549 ath_dbg(common, CONFIG, "spectral scan: channel scan mode enabled\n");
550 } else if (strncmp("manual", buf, 6) == 0) {
Oleksij Rempel67dc74f2014-11-06 08:53:30 +0100551 ath9k_cmn_spectral_scan_config(common, spec_priv, SPECTRAL_MANUAL);
Sujith Manoharanf65c0822013-12-18 09:53:18 +0530552 ath_dbg(common, CONFIG, "spectral scan: manual mode enabled\n");
553 } else if (strncmp("disable", buf, 7) == 0) {
Oleksij Rempel67dc74f2014-11-06 08:53:30 +0100554 ath9k_cmn_spectral_scan_config(common, spec_priv, SPECTRAL_DISABLED);
Sujith Manoharanf65c0822013-12-18 09:53:18 +0530555 ath_dbg(common, CONFIG, "spectral scan: disabled\n");
556 } else {
557 return -EINVAL;
558 }
559
560 return count;
561}
562
563static const struct file_operations fops_spec_scan_ctl = {
564 .read = read_file_spec_scan_ctl,
565 .write = write_file_spec_scan_ctl,
566 .open = simple_open,
567 .owner = THIS_MODULE,
568 .llseek = default_llseek,
569};
570
571/*************************/
572/* spectral_short_repeat */
573/*************************/
574
575static ssize_t read_file_spectral_short_repeat(struct file *file,
576 char __user *user_buf,
577 size_t count, loff_t *ppos)
578{
Oleksij Rempel1111d422014-11-06 08:53:23 +0100579 struct ath_spec_scan_priv *spec_priv = file->private_data;
Sujith Manoharanf65c0822013-12-18 09:53:18 +0530580 char buf[32];
581 unsigned int len;
582
Oleksij Rempel1111d422014-11-06 08:53:23 +0100583 len = sprintf(buf, "%d\n", spec_priv->spec_config.short_repeat);
Sujith Manoharanf65c0822013-12-18 09:53:18 +0530584 return simple_read_from_buffer(user_buf, count, ppos, buf, len);
585}
586
587static ssize_t write_file_spectral_short_repeat(struct file *file,
588 const char __user *user_buf,
589 size_t count, loff_t *ppos)
590{
Oleksij Rempel1111d422014-11-06 08:53:23 +0100591 struct ath_spec_scan_priv *spec_priv = file->private_data;
Sujith Manoharanf65c0822013-12-18 09:53:18 +0530592 unsigned long val;
593 char buf[32];
594 ssize_t len;
595
596 len = min(count, sizeof(buf) - 1);
597 if (copy_from_user(buf, user_buf, len))
598 return -EFAULT;
599
600 buf[len] = '\0';
601 if (kstrtoul(buf, 0, &val))
602 return -EINVAL;
603
Andrey Utkin3f557202014-07-17 16:56:37 +0300604 if (val > 1)
Sujith Manoharanf65c0822013-12-18 09:53:18 +0530605 return -EINVAL;
606
Oleksij Rempel1111d422014-11-06 08:53:23 +0100607 spec_priv->spec_config.short_repeat = val;
Sujith Manoharanf65c0822013-12-18 09:53:18 +0530608 return count;
609}
610
611static const struct file_operations fops_spectral_short_repeat = {
612 .read = read_file_spectral_short_repeat,
613 .write = write_file_spectral_short_repeat,
614 .open = simple_open,
615 .owner = THIS_MODULE,
616 .llseek = default_llseek,
617};
618
619/******************/
620/* spectral_count */
621/******************/
622
623static ssize_t read_file_spectral_count(struct file *file,
624 char __user *user_buf,
625 size_t count, loff_t *ppos)
626{
Oleksij Rempel1111d422014-11-06 08:53:23 +0100627 struct ath_spec_scan_priv *spec_priv = file->private_data;
Sujith Manoharanf65c0822013-12-18 09:53:18 +0530628 char buf[32];
629 unsigned int len;
630
Oleksij Rempel1111d422014-11-06 08:53:23 +0100631 len = sprintf(buf, "%d\n", spec_priv->spec_config.count);
Sujith Manoharanf65c0822013-12-18 09:53:18 +0530632 return simple_read_from_buffer(user_buf, count, ppos, buf, len);
633}
634
635static ssize_t write_file_spectral_count(struct file *file,
636 const char __user *user_buf,
637 size_t count, loff_t *ppos)
638{
Oleksij Rempel1111d422014-11-06 08:53:23 +0100639 struct ath_spec_scan_priv *spec_priv = file->private_data;
Sujith Manoharanf65c0822013-12-18 09:53:18 +0530640 unsigned long val;
641 char buf[32];
642 ssize_t len;
643
644 len = min(count, sizeof(buf) - 1);
645 if (copy_from_user(buf, user_buf, len))
646 return -EFAULT;
647
648 buf[len] = '\0';
649 if (kstrtoul(buf, 0, &val))
650 return -EINVAL;
651
Andrey Utkin3f557202014-07-17 16:56:37 +0300652 if (val > 255)
Sujith Manoharanf65c0822013-12-18 09:53:18 +0530653 return -EINVAL;
654
Oleksij Rempel1111d422014-11-06 08:53:23 +0100655 spec_priv->spec_config.count = val;
Sujith Manoharanf65c0822013-12-18 09:53:18 +0530656 return count;
657}
658
659static const struct file_operations fops_spectral_count = {
660 .read = read_file_spectral_count,
661 .write = write_file_spectral_count,
662 .open = simple_open,
663 .owner = THIS_MODULE,
664 .llseek = default_llseek,
665};
666
667/*******************/
668/* spectral_period */
669/*******************/
670
671static ssize_t read_file_spectral_period(struct file *file,
672 char __user *user_buf,
673 size_t count, loff_t *ppos)
674{
Oleksij Rempel1111d422014-11-06 08:53:23 +0100675 struct ath_spec_scan_priv *spec_priv = file->private_data;
Sujith Manoharanf65c0822013-12-18 09:53:18 +0530676 char buf[32];
677 unsigned int len;
678
Oleksij Rempel1111d422014-11-06 08:53:23 +0100679 len = sprintf(buf, "%d\n", spec_priv->spec_config.period);
Sujith Manoharanf65c0822013-12-18 09:53:18 +0530680 return simple_read_from_buffer(user_buf, count, ppos, buf, len);
681}
682
683static ssize_t write_file_spectral_period(struct file *file,
684 const char __user *user_buf,
685 size_t count, loff_t *ppos)
686{
Oleksij Rempel1111d422014-11-06 08:53:23 +0100687 struct ath_spec_scan_priv *spec_priv = file->private_data;
Sujith Manoharanf65c0822013-12-18 09:53:18 +0530688 unsigned long val;
689 char buf[32];
690 ssize_t len;
691
692 len = min(count, sizeof(buf) - 1);
693 if (copy_from_user(buf, user_buf, len))
694 return -EFAULT;
695
696 buf[len] = '\0';
697 if (kstrtoul(buf, 0, &val))
698 return -EINVAL;
699
Andrey Utkin3f557202014-07-17 16:56:37 +0300700 if (val > 255)
Sujith Manoharanf65c0822013-12-18 09:53:18 +0530701 return -EINVAL;
702
Oleksij Rempel1111d422014-11-06 08:53:23 +0100703 spec_priv->spec_config.period = val;
Sujith Manoharanf65c0822013-12-18 09:53:18 +0530704 return count;
705}
706
707static const struct file_operations fops_spectral_period = {
708 .read = read_file_spectral_period,
709 .write = write_file_spectral_period,
710 .open = simple_open,
711 .owner = THIS_MODULE,
712 .llseek = default_llseek,
713};
714
715/***********************/
716/* spectral_fft_period */
717/***********************/
718
719static ssize_t read_file_spectral_fft_period(struct file *file,
720 char __user *user_buf,
721 size_t count, loff_t *ppos)
722{
Oleksij Rempel1111d422014-11-06 08:53:23 +0100723 struct ath_spec_scan_priv *spec_priv = file->private_data;
Sujith Manoharanf65c0822013-12-18 09:53:18 +0530724 char buf[32];
725 unsigned int len;
726
Oleksij Rempel1111d422014-11-06 08:53:23 +0100727 len = sprintf(buf, "%d\n", spec_priv->spec_config.fft_period);
Sujith Manoharanf65c0822013-12-18 09:53:18 +0530728 return simple_read_from_buffer(user_buf, count, ppos, buf, len);
729}
730
731static ssize_t write_file_spectral_fft_period(struct file *file,
732 const char __user *user_buf,
733 size_t count, loff_t *ppos)
734{
Oleksij Rempel1111d422014-11-06 08:53:23 +0100735 struct ath_spec_scan_priv *spec_priv = file->private_data;
Sujith Manoharanf65c0822013-12-18 09:53:18 +0530736 unsigned long val;
737 char buf[32];
738 ssize_t len;
739
740 len = min(count, sizeof(buf) - 1);
741 if (copy_from_user(buf, user_buf, len))
742 return -EFAULT;
743
744 buf[len] = '\0';
745 if (kstrtoul(buf, 0, &val))
746 return -EINVAL;
747
Andrey Utkin3f557202014-07-17 16:56:37 +0300748 if (val > 15)
Sujith Manoharanf65c0822013-12-18 09:53:18 +0530749 return -EINVAL;
750
Oleksij Rempel1111d422014-11-06 08:53:23 +0100751 spec_priv->spec_config.fft_period = val;
Sujith Manoharanf65c0822013-12-18 09:53:18 +0530752 return count;
753}
754
755static const struct file_operations fops_spectral_fft_period = {
756 .read = read_file_spectral_fft_period,
757 .write = write_file_spectral_fft_period,
758 .open = simple_open,
759 .owner = THIS_MODULE,
760 .llseek = default_llseek,
761};
762
763/*******************/
764/* Relay interface */
765/*******************/
766
767static struct dentry *create_buf_file_handler(const char *filename,
768 struct dentry *parent,
769 umode_t mode,
770 struct rchan_buf *buf,
771 int *is_global)
772{
773 struct dentry *buf_file;
774
775 buf_file = debugfs_create_file(filename, mode, parent, buf,
776 &relay_file_operations);
777 *is_global = 1;
778 return buf_file;
779}
780
781static int remove_buf_file_handler(struct dentry *dentry)
782{
783 debugfs_remove(dentry);
784
785 return 0;
786}
787
Wei Yongjunf84d1b42013-12-20 10:22:51 +0800788static struct rchan_callbacks rfs_spec_scan_cb = {
Sujith Manoharanf65c0822013-12-18 09:53:18 +0530789 .create_buf_file = create_buf_file_handler,
790 .remove_buf_file = remove_buf_file_handler,
791};
792
793/*********************/
794/* Debug Init/Deinit */
795/*********************/
796
Oleksij Rempel67dc74f2014-11-06 08:53:30 +0100797void ath9k_cmn_spectral_deinit_debug(struct ath_spec_scan_priv *spec_priv)
Sujith Manoharanf65c0822013-12-18 09:53:18 +0530798{
Markus Elfringc0420ea2015-02-04 18:48:28 +0100799 if (config_enabled(CONFIG_ATH9K_DEBUGFS)) {
Oleksij Rempel1111d422014-11-06 08:53:23 +0100800 relay_close(spec_priv->rfs_chan_spec_scan);
801 spec_priv->rfs_chan_spec_scan = NULL;
Sujith Manoharanf65c0822013-12-18 09:53:18 +0530802 }
803}
Oleksij Rempel67dc74f2014-11-06 08:53:30 +0100804EXPORT_SYMBOL(ath9k_cmn_spectral_deinit_debug);
Sujith Manoharanf65c0822013-12-18 09:53:18 +0530805
Oleksij Rempel67dc74f2014-11-06 08:53:30 +0100806void ath9k_cmn_spectral_init_debug(struct ath_spec_scan_priv *spec_priv,
807 struct dentry *debugfs_phy)
Sujith Manoharanf65c0822013-12-18 09:53:18 +0530808{
Oleksij Rempel1111d422014-11-06 08:53:23 +0100809 spec_priv->rfs_chan_spec_scan = relay_open("spectral_scan",
Oleksij Rempelc10b75a2014-11-06 08:53:21 +0100810 debugfs_phy,
Sujith Manoharanf65c0822013-12-18 09:53:18 +0530811 1024, 256, &rfs_spec_scan_cb,
812 NULL);
813 debugfs_create_file("spectral_scan_ctl",
814 S_IRUSR | S_IWUSR,
Oleksij Rempel1111d422014-11-06 08:53:23 +0100815 debugfs_phy, spec_priv,
Sujith Manoharanf65c0822013-12-18 09:53:18 +0530816 &fops_spec_scan_ctl);
817 debugfs_create_file("spectral_short_repeat",
818 S_IRUSR | S_IWUSR,
Oleksij Rempel1111d422014-11-06 08:53:23 +0100819 debugfs_phy, spec_priv,
Sujith Manoharanf65c0822013-12-18 09:53:18 +0530820 &fops_spectral_short_repeat);
821 debugfs_create_file("spectral_count",
822 S_IRUSR | S_IWUSR,
Oleksij Rempel1111d422014-11-06 08:53:23 +0100823 debugfs_phy, spec_priv,
Sujith Manoharanf65c0822013-12-18 09:53:18 +0530824 &fops_spectral_count);
825 debugfs_create_file("spectral_period",
826 S_IRUSR | S_IWUSR,
Oleksij Rempel1111d422014-11-06 08:53:23 +0100827 debugfs_phy, spec_priv,
Sujith Manoharanf65c0822013-12-18 09:53:18 +0530828 &fops_spectral_period);
829 debugfs_create_file("spectral_fft_period",
830 S_IRUSR | S_IWUSR,
Oleksij Rempel1111d422014-11-06 08:53:23 +0100831 debugfs_phy, spec_priv,
Sujith Manoharanf65c0822013-12-18 09:53:18 +0530832 &fops_spectral_fft_period);
833}
Oleksij Rempel67dc74f2014-11-06 08:53:30 +0100834EXPORT_SYMBOL(ath9k_cmn_spectral_init_debug);