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 <linux/random.h> |
19 | #include "ath9k.h" |
20 | |
21 | static s8 (u8 ) |
22 | { |
23 | if (rssi_val == 128) |
24 | rssi_val = 0; |
25 | return (s8) rssi_val; |
26 | } |
27 | |
28 | static void ath_debug_send_fft_sample(struct ath_spec_scan_priv *spec_priv, |
29 | struct fft_sample_tlv *fft_sample_tlv) |
30 | { |
31 | int length; |
32 | if (!spec_priv->rfs_chan_spec_scan) |
33 | return; |
34 | |
35 | length = __be16_to_cpu(fft_sample_tlv->length) + |
36 | sizeof(*fft_sample_tlv); |
37 | relay_write(chan: spec_priv->rfs_chan_spec_scan, data: fft_sample_tlv, length); |
38 | } |
39 | |
40 | typedef int (ath_cmn_fft_idx_validator) (u8 *sample_end, int bytes_read); |
41 | |
42 | static int |
43 | ath_cmn_max_idx_verify_ht20_fft(u8 *sample_end, int bytes_read) |
44 | { |
45 | struct ath_ht20_mag_info *mag_info; |
46 | u8 *sample; |
47 | u16 max_magnitude; |
48 | u8 max_index; |
49 | u8 max_exp; |
50 | |
51 | /* Sanity check so that we don't read outside the read |
52 | * buffer |
53 | */ |
54 | if (bytes_read < SPECTRAL_HT20_SAMPLE_LEN - 1) |
55 | return -1; |
56 | |
57 | mag_info = (struct ath_ht20_mag_info *) (sample_end - |
58 | sizeof(struct ath_ht20_mag_info) + 1); |
59 | |
60 | sample = sample_end - SPECTRAL_HT20_SAMPLE_LEN + 1; |
61 | |
62 | max_index = spectral_max_index_ht20(bins: mag_info->all_bins); |
63 | max_magnitude = spectral_max_magnitude(bins: mag_info->all_bins); |
64 | |
65 | max_exp = mag_info->max_exp & 0xf; |
66 | |
67 | /* Don't try to read something outside the read buffer |
68 | * in case of a missing byte (so bins[0] will be outside |
69 | * the read buffer) |
70 | */ |
71 | if (bytes_read < SPECTRAL_HT20_SAMPLE_LEN && max_index < 1) |
72 | return -1; |
73 | |
74 | if ((sample[max_index] & 0xf8) != ((max_magnitude >> max_exp) & 0xf8)) |
75 | return -1; |
76 | else |
77 | return 0; |
78 | } |
79 | |
80 | static int |
81 | ath_cmn_max_idx_verify_ht20_40_fft(u8 *sample_end, int bytes_read) |
82 | { |
83 | struct ath_ht20_40_mag_info *mag_info; |
84 | u8 *sample; |
85 | u16 lower_mag, upper_mag; |
86 | u8 lower_max_index, upper_max_index; |
87 | u8 max_exp; |
88 | int dc_pos = SPECTRAL_HT20_40_NUM_BINS / 2; |
89 | |
90 | /* Sanity check so that we don't read outside the read |
91 | * buffer |
92 | */ |
93 | if (bytes_read < SPECTRAL_HT20_40_SAMPLE_LEN - 1) |
94 | return -1; |
95 | |
96 | mag_info = (struct ath_ht20_40_mag_info *) (sample_end - |
97 | sizeof(struct ath_ht20_40_mag_info) + 1); |
98 | |
99 | sample = sample_end - SPECTRAL_HT20_40_SAMPLE_LEN + 1; |
100 | |
101 | lower_mag = spectral_max_magnitude(bins: mag_info->lower_bins); |
102 | lower_max_index = spectral_max_index_ht40(bins: mag_info->lower_bins); |
103 | |
104 | upper_mag = spectral_max_magnitude(bins: mag_info->upper_bins); |
105 | upper_max_index = spectral_max_index_ht40(bins: mag_info->upper_bins); |
106 | |
107 | max_exp = mag_info->max_exp & 0xf; |
108 | |
109 | /* Don't try to read something outside the read buffer |
110 | * in case of a missing byte (so bins[0] will be outside |
111 | * the read buffer) |
112 | */ |
113 | if (bytes_read < SPECTRAL_HT20_40_SAMPLE_LEN && |
114 | ((upper_max_index < 1) || (lower_max_index < 1))) |
115 | return -1; |
116 | |
117 | if (((sample[upper_max_index + dc_pos] & 0xf8) != |
118 | ((upper_mag >> max_exp) & 0xf8)) || |
119 | ((sample[lower_max_index] & 0xf8) != |
120 | ((lower_mag >> max_exp) & 0xf8))) |
121 | return -1; |
122 | else |
123 | return 0; |
124 | } |
125 | |
126 | typedef int (ath_cmn_fft_sample_handler) (struct ath_rx_status *rs, |
127 | struct ath_spec_scan_priv *spec_priv, |
128 | u8 *sample_buf, u64 tsf, u16 freq, int chan_type); |
129 | |
130 | static int |
131 | ath_cmn_process_ht20_fft(struct ath_rx_status *rs, |
132 | struct ath_spec_scan_priv *spec_priv, |
133 | u8 *sample_buf, |
134 | u64 tsf, u16 freq, int chan_type) |
135 | { |
136 | struct fft_sample_ht20 fft_sample_20; |
137 | struct ath_common *common = ath9k_hw_common(ah: spec_priv->ah); |
138 | struct ath_hw *ah = spec_priv->ah; |
139 | struct ath_ht20_mag_info *mag_info; |
140 | struct fft_sample_tlv *tlv; |
141 | int i = 0; |
142 | int ret = 0; |
143 | int dc_pos = SPECTRAL_HT20_NUM_BINS / 2; |
144 | u16 magnitude, tmp_mag, length; |
145 | u8 max_index, bitmap_w, max_exp; |
146 | |
147 | length = sizeof(fft_sample_20) - sizeof(struct fft_sample_tlv); |
148 | fft_sample_20.tlv.type = ATH_FFT_SAMPLE_HT20; |
149 | fft_sample_20.tlv.length = __cpu_to_be16(length); |
150 | fft_sample_20.freq = __cpu_to_be16(freq); |
151 | fft_sample_20.rssi = fix_rssi_inv_only(rssi_val: rs->rs_rssi_ctl[0]); |
152 | fft_sample_20.noise = ah->noise; |
153 | |
154 | mag_info = (struct ath_ht20_mag_info *) (sample_buf + |
155 | SPECTRAL_HT20_NUM_BINS); |
156 | |
157 | magnitude = spectral_max_magnitude(bins: mag_info->all_bins); |
158 | fft_sample_20.max_magnitude = __cpu_to_be16(magnitude); |
159 | |
160 | max_index = spectral_max_index_ht20(bins: mag_info->all_bins); |
161 | fft_sample_20.max_index = max_index; |
162 | |
163 | bitmap_w = spectral_bitmap_weight(bins: mag_info->all_bins); |
164 | fft_sample_20.bitmap_weight = bitmap_w; |
165 | |
166 | max_exp = mag_info->max_exp & 0xf; |
167 | fft_sample_20.max_exp = max_exp; |
168 | |
169 | fft_sample_20.tsf = __cpu_to_be64(tsf); |
170 | |
171 | memcpy(fft_sample_20.data, sample_buf, SPECTRAL_HT20_NUM_BINS); |
172 | |
173 | ath_dbg(common, SPECTRAL_SCAN, "FFT HT20 frame: max mag 0x%X," |
174 | "max_mag_idx %i\n" , |
175 | magnitude >> max_exp, |
176 | max_index); |
177 | |
178 | if ((fft_sample_20.data[max_index] & 0xf8) != |
179 | ((magnitude >> max_exp) & 0xf8)) { |
180 | ath_dbg(common, SPECTRAL_SCAN, "Magnitude mismatch !\n" ); |
181 | ret = -1; |
182 | } |
183 | |
184 | /* DC value (value in the middle) is the blind spot of the spectral |
185 | * sample and invalid, interpolate it. |
186 | */ |
187 | fft_sample_20.data[dc_pos] = (fft_sample_20.data[dc_pos + 1] + |
188 | fft_sample_20.data[dc_pos - 1]) / 2; |
189 | |
190 | /* Check if the maximum magnitude is indeed maximum, |
191 | * also if the maximum value was at dc_pos, calculate |
192 | * a new one (since value at dc_pos is invalid). |
193 | */ |
194 | if (max_index == dc_pos) { |
195 | tmp_mag = 0; |
196 | for (i = 0; i < dc_pos; i++) { |
197 | if (fft_sample_20.data[i] > tmp_mag) { |
198 | tmp_mag = fft_sample_20.data[i]; |
199 | fft_sample_20.max_index = i; |
200 | } |
201 | } |
202 | |
203 | magnitude = tmp_mag << max_exp; |
204 | fft_sample_20.max_magnitude = __cpu_to_be16(magnitude); |
205 | |
206 | ath_dbg(common, SPECTRAL_SCAN, |
207 | "Calculated new lower max 0x%X at %i\n" , |
208 | tmp_mag, fft_sample_20.max_index); |
209 | } else |
210 | for (i = 0; i < SPECTRAL_HT20_NUM_BINS; i++) { |
211 | if (fft_sample_20.data[i] == (magnitude >> max_exp)) |
212 | ath_dbg(common, SPECTRAL_SCAN, |
213 | "Got max: 0x%X at index %i\n" , |
214 | fft_sample_20.data[i], i); |
215 | |
216 | if (fft_sample_20.data[i] > (magnitude >> max_exp)) { |
217 | ath_dbg(common, SPECTRAL_SCAN, |
218 | "Got bin %i greater than max: 0x%X\n" , |
219 | i, fft_sample_20.data[i]); |
220 | ret = -1; |
221 | } |
222 | } |
223 | |
224 | if (ret < 0) |
225 | return ret; |
226 | |
227 | tlv = (struct fft_sample_tlv *)&fft_sample_20; |
228 | |
229 | ath_debug_send_fft_sample(spec_priv, fft_sample_tlv: tlv); |
230 | |
231 | return 0; |
232 | } |
233 | |
234 | static int |
235 | ath_cmn_process_ht20_40_fft(struct ath_rx_status *rs, |
236 | struct ath_spec_scan_priv *spec_priv, |
237 | u8 *sample_buf, |
238 | u64 tsf, u16 freq, int chan_type) |
239 | { |
240 | struct fft_sample_ht20_40 fft_sample_40; |
241 | struct ath_common *common = ath9k_hw_common(ah: spec_priv->ah); |
242 | struct ath_hw *ah = spec_priv->ah; |
243 | struct ath9k_hw_cal_data *caldata = ah->caldata; |
244 | struct ath_ht20_40_mag_info *mag_info; |
245 | struct fft_sample_tlv *tlv; |
246 | int dc_pos = SPECTRAL_HT20_40_NUM_BINS / 2; |
247 | int i = 0; |
248 | int ret = 0; |
249 | s16 ext_nf; |
250 | u16 lower_mag, upper_mag, tmp_mag, length; |
251 | s8 , ; |
252 | u8 lower_max_index, upper_max_index; |
253 | u8 lower_bitmap_w, upper_bitmap_w, max_exp; |
254 | |
255 | if (caldata) |
256 | ext_nf = ath9k_hw_getchan_noise(ah, chan: ah->curchan, |
257 | nf: caldata->nfCalHist[3].privNF); |
258 | else |
259 | ext_nf = ATH_DEFAULT_NOISE_FLOOR; |
260 | |
261 | length = sizeof(fft_sample_40) - sizeof(struct fft_sample_tlv); |
262 | fft_sample_40.tlv.type = ATH_FFT_SAMPLE_HT20_40; |
263 | fft_sample_40.tlv.length = __cpu_to_be16(length); |
264 | fft_sample_40.freq = __cpu_to_be16(freq); |
265 | fft_sample_40.channel_type = chan_type; |
266 | |
267 | if (chan_type == NL80211_CHAN_HT40PLUS) { |
268 | lower_rssi = fix_rssi_inv_only(rssi_val: rs->rs_rssi_ctl[0]); |
269 | upper_rssi = fix_rssi_inv_only(rssi_val: rs->rs_rssi_ext[0]); |
270 | |
271 | fft_sample_40.lower_noise = ah->noise; |
272 | fft_sample_40.upper_noise = ext_nf; |
273 | } else { |
274 | lower_rssi = fix_rssi_inv_only(rssi_val: rs->rs_rssi_ext[0]); |
275 | upper_rssi = fix_rssi_inv_only(rssi_val: rs->rs_rssi_ctl[0]); |
276 | |
277 | fft_sample_40.lower_noise = ext_nf; |
278 | fft_sample_40.upper_noise = ah->noise; |
279 | } |
280 | |
281 | fft_sample_40.lower_rssi = lower_rssi; |
282 | fft_sample_40.upper_rssi = upper_rssi; |
283 | |
284 | mag_info = (struct ath_ht20_40_mag_info *) (sample_buf + |
285 | SPECTRAL_HT20_40_NUM_BINS); |
286 | |
287 | lower_mag = spectral_max_magnitude(bins: mag_info->lower_bins); |
288 | fft_sample_40.lower_max_magnitude = __cpu_to_be16(lower_mag); |
289 | |
290 | upper_mag = spectral_max_magnitude(bins: mag_info->upper_bins); |
291 | fft_sample_40.upper_max_magnitude = __cpu_to_be16(upper_mag); |
292 | |
293 | lower_max_index = spectral_max_index_ht40(bins: mag_info->lower_bins); |
294 | fft_sample_40.lower_max_index = lower_max_index; |
295 | |
296 | upper_max_index = spectral_max_index_ht40(bins: mag_info->upper_bins); |
297 | fft_sample_40.upper_max_index = upper_max_index; |
298 | |
299 | lower_bitmap_w = spectral_bitmap_weight(bins: mag_info->lower_bins); |
300 | fft_sample_40.lower_bitmap_weight = lower_bitmap_w; |
301 | |
302 | upper_bitmap_w = spectral_bitmap_weight(bins: mag_info->upper_bins); |
303 | fft_sample_40.upper_bitmap_weight = upper_bitmap_w; |
304 | |
305 | max_exp = mag_info->max_exp & 0xf; |
306 | fft_sample_40.max_exp = max_exp; |
307 | |
308 | fft_sample_40.tsf = __cpu_to_be64(tsf); |
309 | |
310 | memcpy(fft_sample_40.data, sample_buf, SPECTRAL_HT20_40_NUM_BINS); |
311 | |
312 | ath_dbg(common, SPECTRAL_SCAN, "FFT HT20/40 frame: lower mag 0x%X," |
313 | "lower_mag_idx %i, upper mag 0x%X," |
314 | "upper_mag_idx %i\n" , |
315 | lower_mag >> max_exp, |
316 | lower_max_index, |
317 | upper_mag >> max_exp, |
318 | upper_max_index); |
319 | |
320 | /* Check if we got the expected magnitude values at |
321 | * the expected bins |
322 | */ |
323 | if (((fft_sample_40.data[upper_max_index + dc_pos] & 0xf8) |
324 | != ((upper_mag >> max_exp) & 0xf8)) || |
325 | ((fft_sample_40.data[lower_max_index] & 0xf8) |
326 | != ((lower_mag >> max_exp) & 0xf8))) { |
327 | ath_dbg(common, SPECTRAL_SCAN, "Magnitude mismatch !\n" ); |
328 | ret = -1; |
329 | } |
330 | |
331 | /* DC value (value in the middle) is the blind spot of the spectral |
332 | * sample and invalid, interpolate it. |
333 | */ |
334 | fft_sample_40.data[dc_pos] = (fft_sample_40.data[dc_pos + 1] + |
335 | fft_sample_40.data[dc_pos - 1]) / 2; |
336 | |
337 | /* Check if the maximum magnitudes are indeed maximum, |
338 | * also if the maximum value was at dc_pos, calculate |
339 | * a new one (since value at dc_pos is invalid). |
340 | */ |
341 | if (lower_max_index == dc_pos) { |
342 | tmp_mag = 0; |
343 | for (i = 0; i < dc_pos; i++) { |
344 | if (fft_sample_40.data[i] > tmp_mag) { |
345 | tmp_mag = fft_sample_40.data[i]; |
346 | fft_sample_40.lower_max_index = i; |
347 | } |
348 | } |
349 | |
350 | lower_mag = tmp_mag << max_exp; |
351 | fft_sample_40.lower_max_magnitude = __cpu_to_be16(lower_mag); |
352 | |
353 | ath_dbg(common, SPECTRAL_SCAN, |
354 | "Calculated new lower max 0x%X at %i\n" , |
355 | tmp_mag, fft_sample_40.lower_max_index); |
356 | } else |
357 | for (i = 0; i < dc_pos; i++) { |
358 | if (fft_sample_40.data[i] == (lower_mag >> max_exp)) |
359 | ath_dbg(common, SPECTRAL_SCAN, |
360 | "Got lower mag: 0x%X at index %i\n" , |
361 | fft_sample_40.data[i], i); |
362 | |
363 | if (fft_sample_40.data[i] > (lower_mag >> max_exp)) { |
364 | ath_dbg(common, SPECTRAL_SCAN, |
365 | "Got lower bin %i higher than max: 0x%X\n" , |
366 | i, fft_sample_40.data[i]); |
367 | ret = -1; |
368 | } |
369 | } |
370 | |
371 | if (upper_max_index == dc_pos) { |
372 | tmp_mag = 0; |
373 | for (i = dc_pos; i < SPECTRAL_HT20_40_NUM_BINS; i++) { |
374 | if (fft_sample_40.data[i] > tmp_mag) { |
375 | tmp_mag = fft_sample_40.data[i]; |
376 | fft_sample_40.upper_max_index = i; |
377 | } |
378 | } |
379 | upper_mag = tmp_mag << max_exp; |
380 | fft_sample_40.upper_max_magnitude = __cpu_to_be16(upper_mag); |
381 | |
382 | ath_dbg(common, SPECTRAL_SCAN, |
383 | "Calculated new upper max 0x%X at %i\n" , |
384 | tmp_mag, fft_sample_40.upper_max_index); |
385 | } else |
386 | for (i = dc_pos; i < SPECTRAL_HT20_40_NUM_BINS; i++) { |
387 | if (fft_sample_40.data[i] == (upper_mag >> max_exp)) |
388 | ath_dbg(common, SPECTRAL_SCAN, |
389 | "Got upper mag: 0x%X at index %i\n" , |
390 | fft_sample_40.data[i], i); |
391 | |
392 | if (fft_sample_40.data[i] > (upper_mag >> max_exp)) { |
393 | ath_dbg(common, SPECTRAL_SCAN, |
394 | "Got upper bin %i higher than max: 0x%X\n" , |
395 | i, fft_sample_40.data[i]); |
396 | |
397 | ret = -1; |
398 | } |
399 | } |
400 | |
401 | if (ret < 0) |
402 | return ret; |
403 | |
404 | tlv = (struct fft_sample_tlv *)&fft_sample_40; |
405 | |
406 | ath_debug_send_fft_sample(spec_priv, fft_sample_tlv: tlv); |
407 | |
408 | return 0; |
409 | } |
410 | |
411 | static inline void |
412 | ath_cmn_copy_fft_frame(u8 *in, u8 *out, int sample_len, int sample_bytes) |
413 | { |
414 | switch (sample_bytes - sample_len) { |
415 | case -1: |
416 | /* First byte missing */ |
417 | memcpy(&out[1], in, |
418 | sample_len - 1); |
419 | break; |
420 | case 0: |
421 | /* Length correct, nothing to do. */ |
422 | memcpy(out, in, sample_len); |
423 | break; |
424 | case 1: |
425 | /* MAC added 2 extra bytes AND first byte |
426 | * is missing. |
427 | */ |
428 | memcpy(&out[1], in, 30); |
429 | out[31] = in[31]; |
430 | memcpy(&out[32], &in[33], |
431 | sample_len - 32); |
432 | break; |
433 | case 2: |
434 | /* MAC added 2 extra bytes at bin 30 and 32, |
435 | * remove them. |
436 | */ |
437 | memcpy(out, in, 30); |
438 | out[30] = in[31]; |
439 | memcpy(&out[31], &in[33], |
440 | sample_len - 31); |
441 | break; |
442 | default: |
443 | break; |
444 | } |
445 | } |
446 | |
447 | static int |
448 | ath_cmn_is_fft_buf_full(struct ath_spec_scan_priv *spec_priv) |
449 | { |
450 | int i = 0; |
451 | int ret = 0; |
452 | struct rchan_buf *buf; |
453 | struct rchan *rc = spec_priv->rfs_chan_spec_scan; |
454 | |
455 | for_each_possible_cpu(i) { |
456 | if ((buf = *per_cpu_ptr(rc->buf, i))) { |
457 | ret += relay_buf_full(buf); |
458 | } |
459 | } |
460 | |
461 | if (ret) |
462 | return 1; |
463 | else |
464 | return 0; |
465 | } |
466 | |
467 | /* returns 1 if this was a spectral frame, even if not handled. */ |
468 | int ath_cmn_process_fft(struct ath_spec_scan_priv *spec_priv, struct ieee80211_hdr *hdr, |
469 | struct ath_rx_status *rs, u64 tsf) |
470 | { |
471 | u8 sample_buf[SPECTRAL_SAMPLE_MAX_LEN] = {0}; |
472 | struct ath_hw *ah = spec_priv->ah; |
473 | struct ath_common *common = ath9k_hw_common(ah: spec_priv->ah); |
474 | struct ath_softc *sc = common->priv; |
475 | u8 num_bins, *vdata = (u8 *)hdr; |
476 | struct ath_radar_info *radar_info; |
477 | int len = rs->rs_datalen; |
478 | int i; |
479 | int got_slen = 0; |
480 | u8 *sample_start; |
481 | int sample_bytes = 0; |
482 | int ret = 0; |
483 | u16 fft_len, sample_len, freq = ah->curchan->chan->center_freq; |
484 | enum nl80211_channel_type chan_type; |
485 | ath_cmn_fft_idx_validator *fft_idx_validator; |
486 | ath_cmn_fft_sample_handler *fft_handler; |
487 | |
488 | /* AR9280 and before report via ATH9K_PHYERR_RADAR, AR93xx and newer |
489 | * via ATH9K_PHYERR_SPECTRAL. Haven't seen ATH9K_PHYERR_FALSE_RADAR_EXT |
490 | * yet, but this is supposed to be possible as well. |
491 | */ |
492 | if (rs->rs_phyerr != ATH9K_PHYERR_RADAR && |
493 | rs->rs_phyerr != ATH9K_PHYERR_FALSE_RADAR_EXT && |
494 | rs->rs_phyerr != ATH9K_PHYERR_SPECTRAL) |
495 | return 0; |
496 | |
497 | /* check if spectral scan bit is set. This does not have to be checked |
498 | * if received through a SPECTRAL phy error, but shouldn't hurt. |
499 | */ |
500 | radar_info = ((struct ath_radar_info *)&vdata[len]) - 1; |
501 | if (!(radar_info->pulse_bw_info & SPECTRAL_SCAN_BITMASK)) |
502 | return 0; |
503 | |
504 | if (!spec_priv->rfs_chan_spec_scan) |
505 | return 1; |
506 | |
507 | /* Output buffers are full, no need to process anything |
508 | * since there is no space to put the result anyway |
509 | */ |
510 | ret = ath_cmn_is_fft_buf_full(spec_priv); |
511 | if (ret == 1) { |
512 | ath_dbg(common, SPECTRAL_SCAN, "FFT report ignored, no space " |
513 | "left on output buffers\n" ); |
514 | return 1; |
515 | } |
516 | |
517 | chan_type = cfg80211_get_chandef_type(chandef: &common->hw->conf.chandef); |
518 | if ((chan_type == NL80211_CHAN_HT40MINUS) || |
519 | (chan_type == NL80211_CHAN_HT40PLUS)) { |
520 | fft_len = SPECTRAL_HT20_40_TOTAL_DATA_LEN; |
521 | sample_len = SPECTRAL_HT20_40_SAMPLE_LEN; |
522 | num_bins = SPECTRAL_HT20_40_NUM_BINS; |
523 | fft_idx_validator = &ath_cmn_max_idx_verify_ht20_40_fft; |
524 | fft_handler = &ath_cmn_process_ht20_40_fft; |
525 | } else { |
526 | fft_len = SPECTRAL_HT20_TOTAL_DATA_LEN; |
527 | sample_len = SPECTRAL_HT20_SAMPLE_LEN; |
528 | num_bins = SPECTRAL_HT20_NUM_BINS; |
529 | fft_idx_validator = ath_cmn_max_idx_verify_ht20_fft; |
530 | fft_handler = &ath_cmn_process_ht20_fft; |
531 | } |
532 | |
533 | ath_dbg(common, SPECTRAL_SCAN, "Got radar dump bw_info: 0x%X," |
534 | "len: %i fft_len: %i\n" , |
535 | radar_info->pulse_bw_info, |
536 | len, |
537 | fft_len); |
538 | sample_start = vdata; |
539 | for (i = 0; i < len - 2; i++) { |
540 | sample_bytes++; |
541 | |
542 | /* Only a single sample received, no need to look |
543 | * for the sample's end, do the correction based |
544 | * on the packet's length instead. Note that hw |
545 | * will always put the radar_info structure on |
546 | * the end. |
547 | */ |
548 | if (len <= fft_len + 2) { |
549 | sample_bytes = len - sizeof(struct ath_radar_info); |
550 | got_slen = 1; |
551 | } |
552 | |
553 | /* Search for the end of the FFT frame between |
554 | * sample_len - 1 and sample_len + 2. exp_max is 3 |
555 | * bits long and it's the only value on the last |
556 | * byte of the frame so since it'll be smaller than |
557 | * the next byte (the first bin of the next sample) |
558 | * 90% of the time, we can use it as a separator. |
559 | */ |
560 | if (vdata[i] <= 0x7 && sample_bytes >= sample_len - 1) { |
561 | |
562 | /* Got a frame length within boundaries, there are |
563 | * four scenarios here: |
564 | * |
565 | * a) sample_len -> We got the correct length |
566 | * b) sample_len + 2 -> 2 bytes added around bin[31] |
567 | * c) sample_len - 1 -> The first byte is missing |
568 | * d) sample_len + 1 -> b + c at the same time |
569 | * |
570 | * When MAC adds 2 extra bytes, bin[31] and bin[32] |
571 | * have the same value, so we can use that for further |
572 | * verification in cases b and d. |
573 | */ |
574 | |
575 | /* Did we go too far ? If so we couldn't determine |
576 | * this sample's boundaries, discard any further |
577 | * data |
578 | */ |
579 | if ((sample_bytes > sample_len + 2) || |
580 | ((sample_bytes > sample_len) && |
581 | (sample_start[31] != sample_start[32]))) |
582 | break; |
583 | |
584 | /* See if we got a valid frame by checking the |
585 | * consistency of mag_info fields. This is to |
586 | * prevent from "fixing" a correct frame. |
587 | * Failure is non-fatal, later frames may |
588 | * be valid. |
589 | */ |
590 | if (!fft_idx_validator(&vdata[i], i)) { |
591 | ath_dbg(common, SPECTRAL_SCAN, |
592 | "Found valid fft frame at %i\n" , i); |
593 | got_slen = 1; |
594 | } |
595 | |
596 | /* We expect 1 - 2 more bytes */ |
597 | else if ((sample_start[31] == sample_start[32]) && |
598 | (sample_bytes >= sample_len) && |
599 | (sample_bytes < sample_len + 2) && |
600 | (vdata[i + 1] <= 0x7)) |
601 | continue; |
602 | |
603 | /* Try to distinguish cases a and c */ |
604 | else if ((sample_bytes == sample_len - 1) && |
605 | (vdata[i + 1] <= 0x7)) |
606 | continue; |
607 | |
608 | got_slen = 1; |
609 | } |
610 | |
611 | if (got_slen) { |
612 | ath_dbg(common, SPECTRAL_SCAN, "FFT frame len: %i\n" , |
613 | sample_bytes); |
614 | |
615 | /* Only try to fix a frame if it's the only one |
616 | * on the report, else just skip it. |
617 | */ |
618 | if (sample_bytes != sample_len && len <= fft_len + 2) { |
619 | ath_cmn_copy_fft_frame(in: sample_start, |
620 | out: sample_buf, sample_len, |
621 | sample_bytes); |
622 | |
623 | ret = fft_handler(rs, spec_priv, sample_buf, |
624 | tsf, freq, chan_type); |
625 | |
626 | if (ret == 0) |
627 | RX_STAT_INC(sc, rx_spectral_sample_good); |
628 | else |
629 | RX_STAT_INC(sc, rx_spectral_sample_err); |
630 | |
631 | memset(sample_buf, 0, SPECTRAL_SAMPLE_MAX_LEN); |
632 | |
633 | /* Mix the received bins to the /dev/random |
634 | * pool |
635 | */ |
636 | add_device_randomness(buf: sample_buf, len: num_bins); |
637 | } |
638 | |
639 | /* Process a normal frame */ |
640 | if (sample_bytes == sample_len) { |
641 | ret = fft_handler(rs, spec_priv, sample_start, |
642 | tsf, freq, chan_type); |
643 | |
644 | if (ret == 0) |
645 | RX_STAT_INC(sc, rx_spectral_sample_good); |
646 | else |
647 | RX_STAT_INC(sc, rx_spectral_sample_err); |
648 | |
649 | /* Mix the received bins to the /dev/random |
650 | * pool |
651 | */ |
652 | add_device_randomness(buf: sample_start, len: num_bins); |
653 | } |
654 | |
655 | /* Short report processed, break out of the |
656 | * loop. |
657 | */ |
658 | if (len <= fft_len + 2) |
659 | return 1; |
660 | |
661 | sample_start = &vdata[i + 1]; |
662 | |
663 | /* -1 to grab sample_len -1, -2 since |
664 | * they 'll get increased by one. In case |
665 | * of failure try to recover by going byte |
666 | * by byte instead. |
667 | */ |
668 | if (ret == 0) { |
669 | i += num_bins - 2; |
670 | sample_bytes = num_bins - 2; |
671 | } |
672 | got_slen = 0; |
673 | } |
674 | } |
675 | |
676 | i -= num_bins - 2; |
677 | if (len - i != sizeof(struct ath_radar_info)) |
678 | ath_dbg(common, SPECTRAL_SCAN, "FFT report truncated" |
679 | "(bytes left: %i)\n" , |
680 | len - i); |
681 | return 1; |
682 | } |
683 | EXPORT_SYMBOL(ath_cmn_process_fft); |
684 | |
685 | /*********************/ |
686 | /* spectral_scan_ctl */ |
687 | /*********************/ |
688 | |
689 | static ssize_t read_file_spec_scan_ctl(struct file *file, char __user *user_buf, |
690 | size_t count, loff_t *ppos) |
691 | { |
692 | struct ath_spec_scan_priv *spec_priv = file->private_data; |
693 | char *mode = "" ; |
694 | unsigned int len; |
695 | |
696 | switch (spec_priv->spectral_mode) { |
697 | case SPECTRAL_DISABLED: |
698 | mode = "disable" ; |
699 | break; |
700 | case SPECTRAL_BACKGROUND: |
701 | mode = "background" ; |
702 | break; |
703 | case SPECTRAL_CHANSCAN: |
704 | mode = "chanscan" ; |
705 | break; |
706 | case SPECTRAL_MANUAL: |
707 | mode = "manual" ; |
708 | break; |
709 | } |
710 | len = strlen(mode); |
711 | return simple_read_from_buffer(to: user_buf, count, ppos, from: mode, available: len); |
712 | } |
713 | |
714 | void ath9k_cmn_spectral_scan_trigger(struct ath_common *common, |
715 | struct ath_spec_scan_priv *spec_priv) |
716 | { |
717 | struct ath_hw *ah = spec_priv->ah; |
718 | u32 rxfilter; |
719 | |
720 | if (IS_ENABLED(CONFIG_ATH9K_TX99)) |
721 | return; |
722 | |
723 | if (!ath9k_hw_ops(ah)->spectral_scan_trigger) { |
724 | ath_err(common, "spectrum analyzer not implemented on this hardware\n" ); |
725 | return; |
726 | } |
727 | |
728 | if (!spec_priv->spec_config.enabled) |
729 | return; |
730 | |
731 | ath_ps_ops(common)->wakeup(common); |
732 | rxfilter = ath9k_hw_getrxfilter(ah); |
733 | ath9k_hw_setrxfilter(ah, bits: rxfilter | |
734 | ATH9K_RX_FILTER_PHYRADAR | |
735 | ATH9K_RX_FILTER_PHYERR); |
736 | |
737 | /* TODO: usually this should not be neccesary, but for some reason |
738 | * (or in some mode?) the trigger must be called after the |
739 | * configuration, otherwise the register will have its values reset |
740 | * (on my ar9220 to value 0x01002310) |
741 | */ |
742 | ath9k_cmn_spectral_scan_config(common, spec_priv, spectral_mode: spec_priv->spectral_mode); |
743 | ath9k_hw_ops(ah)->spectral_scan_trigger(ah); |
744 | ath_ps_ops(common)->restore(common); |
745 | } |
746 | EXPORT_SYMBOL(ath9k_cmn_spectral_scan_trigger); |
747 | |
748 | int ath9k_cmn_spectral_scan_config(struct ath_common *common, |
749 | struct ath_spec_scan_priv *spec_priv, |
750 | enum spectral_mode spectral_mode) |
751 | { |
752 | struct ath_hw *ah = spec_priv->ah; |
753 | |
754 | if (!ath9k_hw_ops(ah)->spectral_scan_trigger) { |
755 | ath_err(common, "spectrum analyzer not implemented on this hardware\n" ); |
756 | return -1; |
757 | } |
758 | |
759 | switch (spectral_mode) { |
760 | case SPECTRAL_DISABLED: |
761 | spec_priv->spec_config.enabled = 0; |
762 | break; |
763 | case SPECTRAL_BACKGROUND: |
764 | /* send endless samples. |
765 | * TODO: is this really useful for "background"? |
766 | */ |
767 | spec_priv->spec_config.endless = 1; |
768 | spec_priv->spec_config.enabled = 1; |
769 | break; |
770 | case SPECTRAL_CHANSCAN: |
771 | case SPECTRAL_MANUAL: |
772 | spec_priv->spec_config.endless = 0; |
773 | spec_priv->spec_config.enabled = 1; |
774 | break; |
775 | default: |
776 | return -1; |
777 | } |
778 | |
779 | ath_ps_ops(common)->wakeup(common); |
780 | ath9k_hw_ops(ah)->spectral_scan_config(ah, &spec_priv->spec_config); |
781 | ath_ps_ops(common)->restore(common); |
782 | |
783 | spec_priv->spectral_mode = spectral_mode; |
784 | |
785 | return 0; |
786 | } |
787 | EXPORT_SYMBOL(ath9k_cmn_spectral_scan_config); |
788 | |
789 | static ssize_t write_file_spec_scan_ctl(struct file *file, |
790 | const char __user *user_buf, |
791 | size_t count, loff_t *ppos) |
792 | { |
793 | struct ath_spec_scan_priv *spec_priv = file->private_data; |
794 | struct ath_common *common = ath9k_hw_common(ah: spec_priv->ah); |
795 | char buf[32]; |
796 | ssize_t len; |
797 | |
798 | if (IS_ENABLED(CONFIG_ATH9K_TX99)) |
799 | return -EOPNOTSUPP; |
800 | |
801 | len = min(count, sizeof(buf) - 1); |
802 | if (copy_from_user(to: buf, from: user_buf, n: len)) |
803 | return -EFAULT; |
804 | |
805 | buf[len] = '\0'; |
806 | |
807 | if (strncmp("trigger" , buf, 7) == 0) { |
808 | ath9k_cmn_spectral_scan_trigger(common, spec_priv); |
809 | } else if (strncmp("background" , buf, 10) == 0) { |
810 | ath9k_cmn_spectral_scan_config(common, spec_priv, SPECTRAL_BACKGROUND); |
811 | ath_dbg(common, CONFIG, "spectral scan: background mode enabled\n" ); |
812 | } else if (strncmp("chanscan" , buf, 8) == 0) { |
813 | ath9k_cmn_spectral_scan_config(common, spec_priv, SPECTRAL_CHANSCAN); |
814 | ath_dbg(common, CONFIG, "spectral scan: channel scan mode enabled\n" ); |
815 | } else if (strncmp("manual" , buf, 6) == 0) { |
816 | ath9k_cmn_spectral_scan_config(common, spec_priv, SPECTRAL_MANUAL); |
817 | ath_dbg(common, CONFIG, "spectral scan: manual mode enabled\n" ); |
818 | } else if (strncmp("disable" , buf, 7) == 0) { |
819 | ath9k_cmn_spectral_scan_config(common, spec_priv, SPECTRAL_DISABLED); |
820 | ath_dbg(common, CONFIG, "spectral scan: disabled\n" ); |
821 | } else { |
822 | return -EINVAL; |
823 | } |
824 | |
825 | return count; |
826 | } |
827 | |
828 | static const struct file_operations fops_spec_scan_ctl = { |
829 | .read = read_file_spec_scan_ctl, |
830 | .write = write_file_spec_scan_ctl, |
831 | .open = simple_open, |
832 | .owner = THIS_MODULE, |
833 | .llseek = default_llseek, |
834 | }; |
835 | |
836 | /*************************/ |
837 | /* spectral_short_repeat */ |
838 | /*************************/ |
839 | |
840 | static ssize_t read_file_spectral_short_repeat(struct file *file, |
841 | char __user *user_buf, |
842 | size_t count, loff_t *ppos) |
843 | { |
844 | struct ath_spec_scan_priv *spec_priv = file->private_data; |
845 | char buf[32]; |
846 | unsigned int len; |
847 | |
848 | len = sprintf(buf, fmt: "%d\n" , spec_priv->spec_config.short_repeat); |
849 | return simple_read_from_buffer(to: user_buf, count, ppos, from: buf, available: len); |
850 | } |
851 | |
852 | static ssize_t write_file_spectral_short_repeat(struct file *file, |
853 | const char __user *user_buf, |
854 | size_t count, loff_t *ppos) |
855 | { |
856 | struct ath_spec_scan_priv *spec_priv = file->private_data; |
857 | unsigned long val; |
858 | ssize_t ret; |
859 | |
860 | ret = kstrtoul_from_user(s: user_buf, count, base: 0, res: &val); |
861 | if (ret) |
862 | return ret; |
863 | |
864 | if (val > 1) |
865 | return -EINVAL; |
866 | |
867 | spec_priv->spec_config.short_repeat = val; |
868 | return count; |
869 | } |
870 | |
871 | static const struct file_operations fops_spectral_short_repeat = { |
872 | .read = read_file_spectral_short_repeat, |
873 | .write = write_file_spectral_short_repeat, |
874 | .open = simple_open, |
875 | .owner = THIS_MODULE, |
876 | .llseek = default_llseek, |
877 | }; |
878 | |
879 | /******************/ |
880 | /* spectral_count */ |
881 | /******************/ |
882 | |
883 | static ssize_t read_file_spectral_count(struct file *file, |
884 | char __user *user_buf, |
885 | size_t count, loff_t *ppos) |
886 | { |
887 | struct ath_spec_scan_priv *spec_priv = file->private_data; |
888 | char buf[32]; |
889 | unsigned int len; |
890 | |
891 | len = sprintf(buf, fmt: "%d\n" , spec_priv->spec_config.count); |
892 | return simple_read_from_buffer(to: user_buf, count, ppos, from: buf, available: len); |
893 | } |
894 | |
895 | static ssize_t write_file_spectral_count(struct file *file, |
896 | const char __user *user_buf, |
897 | size_t count, loff_t *ppos) |
898 | { |
899 | struct ath_spec_scan_priv *spec_priv = file->private_data; |
900 | unsigned long val; |
901 | ssize_t ret; |
902 | |
903 | ret = kstrtoul_from_user(s: user_buf, count, base: 0, res: &val); |
904 | if (ret) |
905 | return ret; |
906 | if (val > 255) |
907 | return -EINVAL; |
908 | |
909 | spec_priv->spec_config.count = val; |
910 | return count; |
911 | } |
912 | |
913 | static const struct file_operations fops_spectral_count = { |
914 | .read = read_file_spectral_count, |
915 | .write = write_file_spectral_count, |
916 | .open = simple_open, |
917 | .owner = THIS_MODULE, |
918 | .llseek = default_llseek, |
919 | }; |
920 | |
921 | /*******************/ |
922 | /* spectral_period */ |
923 | /*******************/ |
924 | |
925 | static ssize_t read_file_spectral_period(struct file *file, |
926 | char __user *user_buf, |
927 | size_t count, loff_t *ppos) |
928 | { |
929 | struct ath_spec_scan_priv *spec_priv = file->private_data; |
930 | char buf[32]; |
931 | unsigned int len; |
932 | |
933 | len = sprintf(buf, fmt: "%d\n" , spec_priv->spec_config.period); |
934 | return simple_read_from_buffer(to: user_buf, count, ppos, from: buf, available: len); |
935 | } |
936 | |
937 | static ssize_t write_file_spectral_period(struct file *file, |
938 | const char __user *user_buf, |
939 | size_t count, loff_t *ppos) |
940 | { |
941 | struct ath_spec_scan_priv *spec_priv = file->private_data; |
942 | unsigned long val; |
943 | ssize_t ret; |
944 | |
945 | ret = kstrtoul_from_user(s: user_buf, count, base: 0, res: &val); |
946 | if (ret) |
947 | return ret; |
948 | |
949 | if (val > 255) |
950 | return -EINVAL; |
951 | |
952 | spec_priv->spec_config.period = val; |
953 | return count; |
954 | } |
955 | |
956 | static const struct file_operations fops_spectral_period = { |
957 | .read = read_file_spectral_period, |
958 | .write = write_file_spectral_period, |
959 | .open = simple_open, |
960 | .owner = THIS_MODULE, |
961 | .llseek = default_llseek, |
962 | }; |
963 | |
964 | /***********************/ |
965 | /* spectral_fft_period */ |
966 | /***********************/ |
967 | |
968 | static ssize_t read_file_spectral_fft_period(struct file *file, |
969 | char __user *user_buf, |
970 | size_t count, loff_t *ppos) |
971 | { |
972 | struct ath_spec_scan_priv *spec_priv = file->private_data; |
973 | char buf[32]; |
974 | unsigned int len; |
975 | |
976 | len = sprintf(buf, fmt: "%d\n" , spec_priv->spec_config.fft_period); |
977 | return simple_read_from_buffer(to: user_buf, count, ppos, from: buf, available: len); |
978 | } |
979 | |
980 | static ssize_t write_file_spectral_fft_period(struct file *file, |
981 | const char __user *user_buf, |
982 | size_t count, loff_t *ppos) |
983 | { |
984 | struct ath_spec_scan_priv *spec_priv = file->private_data; |
985 | unsigned long val; |
986 | ssize_t ret; |
987 | |
988 | ret = kstrtoul_from_user(s: user_buf, count, base: 0, res: &val); |
989 | if (ret) |
990 | return ret; |
991 | |
992 | if (val > 15) |
993 | return -EINVAL; |
994 | |
995 | spec_priv->spec_config.fft_period = val; |
996 | return count; |
997 | } |
998 | |
999 | static const struct file_operations fops_spectral_fft_period = { |
1000 | .read = read_file_spectral_fft_period, |
1001 | .write = write_file_spectral_fft_period, |
1002 | .open = simple_open, |
1003 | .owner = THIS_MODULE, |
1004 | .llseek = default_llseek, |
1005 | }; |
1006 | |
1007 | /*******************/ |
1008 | /* Relay interface */ |
1009 | /*******************/ |
1010 | |
1011 | static struct dentry *create_buf_file_handler(const char *filename, |
1012 | struct dentry *parent, |
1013 | umode_t mode, |
1014 | struct rchan_buf *buf, |
1015 | int *is_global) |
1016 | { |
1017 | struct dentry *buf_file; |
1018 | |
1019 | buf_file = debugfs_create_file(name: filename, mode, parent, data: buf, |
1020 | fops: &relay_file_operations); |
1021 | if (IS_ERR(ptr: buf_file)) |
1022 | return NULL; |
1023 | |
1024 | *is_global = 1; |
1025 | return buf_file; |
1026 | } |
1027 | |
1028 | static int remove_buf_file_handler(struct dentry *dentry) |
1029 | { |
1030 | debugfs_remove(dentry); |
1031 | |
1032 | return 0; |
1033 | } |
1034 | |
1035 | static const struct rchan_callbacks rfs_spec_scan_cb = { |
1036 | .create_buf_file = create_buf_file_handler, |
1037 | .remove_buf_file = remove_buf_file_handler, |
1038 | }; |
1039 | |
1040 | /*********************/ |
1041 | /* Debug Init/Deinit */ |
1042 | /*********************/ |
1043 | |
1044 | void ath9k_cmn_spectral_deinit_debug(struct ath_spec_scan_priv *spec_priv) |
1045 | { |
1046 | if (spec_priv->rfs_chan_spec_scan) { |
1047 | relay_close(chan: spec_priv->rfs_chan_spec_scan); |
1048 | spec_priv->rfs_chan_spec_scan = NULL; |
1049 | } |
1050 | } |
1051 | EXPORT_SYMBOL(ath9k_cmn_spectral_deinit_debug); |
1052 | |
1053 | void ath9k_cmn_spectral_init_debug(struct ath_spec_scan_priv *spec_priv, |
1054 | struct dentry *debugfs_phy) |
1055 | { |
1056 | spec_priv->rfs_chan_spec_scan = relay_open(base_filename: "spectral_scan" , |
1057 | parent: debugfs_phy, |
1058 | subbuf_size: 1024, n_subbufs: 256, cb: &rfs_spec_scan_cb, |
1059 | NULL); |
1060 | if (!spec_priv->rfs_chan_spec_scan) |
1061 | return; |
1062 | |
1063 | debugfs_create_file(name: "spectral_scan_ctl" , |
1064 | mode: 0600, |
1065 | parent: debugfs_phy, data: spec_priv, |
1066 | fops: &fops_spec_scan_ctl); |
1067 | debugfs_create_file(name: "spectral_short_repeat" , |
1068 | mode: 0600, |
1069 | parent: debugfs_phy, data: spec_priv, |
1070 | fops: &fops_spectral_short_repeat); |
1071 | debugfs_create_file(name: "spectral_count" , |
1072 | mode: 0600, |
1073 | parent: debugfs_phy, data: spec_priv, |
1074 | fops: &fops_spectral_count); |
1075 | debugfs_create_file(name: "spectral_period" , |
1076 | mode: 0600, |
1077 | parent: debugfs_phy, data: spec_priv, |
1078 | fops: &fops_spectral_period); |
1079 | debugfs_create_file(name: "spectral_fft_period" , |
1080 | mode: 0600, |
1081 | parent: debugfs_phy, data: spec_priv, |
1082 | fops: &fops_spectral_fft_period); |
1083 | } |
1084 | EXPORT_SYMBOL(ath9k_cmn_spectral_init_debug); |
1085 | |