Commit | Line | Data |
---|---|---|
29942bc1 ZK |
1 | /* |
2 | * Copyright (c) 2008-2011 Atheros Communications Inc. | |
3 | * Copyright (c) 2011 Neratec Solutions AG | |
4 | * | |
5 | * Permission to use, copy, modify, and/or distribute this software for any | |
6 | * purpose with or without fee is hereby granted, provided that the above | |
7 | * copyright notice and this permission notice appear in all copies. | |
8 | * | |
9 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |
10 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
11 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |
12 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
13 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |
14 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |
15 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
16 | */ | |
17 | ||
18 | #include "hw.h" | |
19 | #include "hw-ops.h" | |
20 | #include "ath9k.h" | |
21 | #include "dfs.h" | |
22 | #include "dfs_debug.h" | |
23 | ||
29942bc1 ZK |
24 | /* internal struct to pass radar data */ |
25 | struct ath_radar_data { | |
26 | u8 pulse_bw_info; | |
27 | u8 rssi; | |
28 | u8 ext_rssi; | |
29 | u8 pulse_length_ext; | |
30 | u8 pulse_length_pri; | |
31 | }; | |
32 | ||
33 | /* convert pulse duration to usecs, considering clock mode */ | |
34 | static u32 dur_to_usecs(struct ath_hw *ah, u32 dur) | |
35 | { | |
36 | const u32 AR93X_NSECS_PER_DUR = 800; | |
37 | const u32 AR93X_NSECS_PER_DUR_FAST = (8000 / 11); | |
38 | u32 nsecs; | |
39 | ||
40 | if (IS_CHAN_A_FAST_CLOCK(ah, ah->curchan)) | |
41 | nsecs = dur * AR93X_NSECS_PER_DUR_FAST; | |
42 | else | |
43 | nsecs = dur * AR93X_NSECS_PER_DUR; | |
44 | ||
45 | return (nsecs + 500) / 1000; | |
46 | } | |
47 | ||
48 | #define PRI_CH_RADAR_FOUND 0x01 | |
49 | #define EXT_CH_RADAR_FOUND 0x02 | |
50 | static bool | |
51 | ath9k_postprocess_radar_event(struct ath_softc *sc, | |
56dc389f ZK |
52 | struct ath_radar_data *ard, |
53 | struct pulse_event *pe) | |
29942bc1 ZK |
54 | { |
55 | u8 rssi; | |
56 | u16 dur; | |
57 | ||
29942bc1 ZK |
58 | /* |
59 | * Only the last 2 bits of the BW info are relevant, they indicate | |
60 | * which channel the radar was detected in. | |
61 | */ | |
56dc389f | 62 | ard->pulse_bw_info &= 0x03; |
29942bc1 | 63 | |
56dc389f | 64 | switch (ard->pulse_bw_info) { |
29942bc1 ZK |
65 | case PRI_CH_RADAR_FOUND: |
66 | /* radar in ctrl channel */ | |
56dc389f | 67 | dur = ard->pulse_length_pri; |
29942bc1 ZK |
68 | DFS_STAT_INC(sc, pri_phy_errors); |
69 | /* | |
70 | * cannot use ctrl channel RSSI | |
71 | * if extension channel is stronger | |
72 | */ | |
56dc389f | 73 | rssi = (ard->ext_rssi >= (ard->rssi + 3)) ? 0 : ard->rssi; |
29942bc1 ZK |
74 | break; |
75 | case EXT_CH_RADAR_FOUND: | |
76 | /* radar in extension channel */ | |
56dc389f | 77 | dur = ard->pulse_length_ext; |
29942bc1 ZK |
78 | DFS_STAT_INC(sc, ext_phy_errors); |
79 | /* | |
80 | * cannot use extension channel RSSI | |
81 | * if control channel is stronger | |
82 | */ | |
56dc389f | 83 | rssi = (ard->rssi >= (ard->ext_rssi + 12)) ? 0 : ard->ext_rssi; |
29942bc1 ZK |
84 | break; |
85 | case (PRI_CH_RADAR_FOUND | EXT_CH_RADAR_FOUND): | |
86 | /* | |
87 | * Conducted testing, when pulse is on DC, both pri and ext | |
88 | * durations are reported to be same | |
89 | * | |
90 | * Radiated testing, when pulse is on DC, different pri and | |
91 | * ext durations are reported, so take the larger of the two | |
92 | */ | |
56dc389f ZK |
93 | if (ard->pulse_length_ext >= ard->pulse_length_pri) |
94 | dur = ard->pulse_length_ext; | |
29942bc1 | 95 | else |
56dc389f | 96 | dur = ard->pulse_length_pri; |
29942bc1 ZK |
97 | DFS_STAT_INC(sc, dc_phy_errors); |
98 | ||
99 | /* when both are present use stronger one */ | |
56dc389f | 100 | rssi = (ard->rssi < ard->ext_rssi) ? ard->ext_rssi : ard->rssi; |
29942bc1 ZK |
101 | break; |
102 | default: | |
103 | /* | |
104 | * Bogus bandwidth info was received in descriptor, | |
105 | * so ignore this PHY error | |
106 | */ | |
107 | DFS_STAT_INC(sc, bwinfo_discards); | |
108 | return false; | |
109 | } | |
110 | ||
111 | if (rssi == 0) { | |
112 | DFS_STAT_INC(sc, rssi_discards); | |
113 | return false; | |
114 | } | |
115 | ||
116 | /* | |
117 | * TODO: check chirping pulses | |
118 | * checks for chirping are dependent on the DFS regulatory domain | |
119 | * used, which is yet TBD | |
120 | */ | |
121 | ||
122 | /* convert duration to usecs */ | |
56dc389f ZK |
123 | pe->width = dur_to_usecs(sc->sc_ah, dur); |
124 | pe->rssi = rssi; | |
29942bc1 ZK |
125 | |
126 | DFS_STAT_INC(sc, pulses_detected); | |
127 | return true; | |
128 | } | |
58766977 ZK |
129 | |
130 | static void | |
131 | ath9k_dfs_process_radar_pulse(struct ath_softc *sc, struct pulse_event *pe) | |
132 | { | |
133 | struct dfs_pattern_detector *pd = sc->dfs_detector; | |
134 | DFS_STAT_INC(sc, pulses_processed); | |
135 | if (pd == NULL) | |
136 | return; | |
137 | if (!pd->add_pulse(pd, pe)) | |
138 | return; | |
139 | DFS_STAT_INC(sc, radar_detected); | |
140 | ieee80211_radar_detected(sc->hw); | |
141 | } | |
29942bc1 ZK |
142 | |
143 | /* | |
144 | * DFS: check PHY-error for radar pulse and feed the detector | |
145 | */ | |
146 | void ath9k_dfs_process_phyerr(struct ath_softc *sc, void *data, | |
147 | struct ath_rx_status *rs, u64 mactime) | |
148 | { | |
149 | struct ath_radar_data ard; | |
150 | u16 datalen; | |
151 | char *vdata_end; | |
56dc389f | 152 | struct pulse_event pe; |
29942bc1 ZK |
153 | struct ath_hw *ah = sc->sc_ah; |
154 | struct ath_common *common = ath9k_hw_common(ah); | |
155 | ||
b96f20b3 | 156 | DFS_STAT_INC(sc, pulses_total); |
56dc389f ZK |
157 | if ((rs->rs_phyerr != ATH9K_PHYERR_RADAR) && |
158 | (rs->rs_phyerr != ATH9K_PHYERR_FALSE_RADAR_EXT)) { | |
ec9a5705 | 159 | ath_dbg(common, DFS, |
29942bc1 ZK |
160 | "Error: rs_phyer=0x%x not a radar error\n", |
161 | rs->rs_phyerr); | |
b96f20b3 | 162 | DFS_STAT_INC(sc, pulses_no_dfs); |
29942bc1 ZK |
163 | return; |
164 | } | |
165 | ||
166 | datalen = rs->rs_datalen; | |
167 | if (datalen == 0) { | |
168 | DFS_STAT_INC(sc, datalen_discards); | |
169 | return; | |
170 | } | |
171 | ||
e45e91d8 FF |
172 | ard.rssi = rs->rs_rssi_ctl[0]; |
173 | ard.ext_rssi = rs->rs_rssi_ext[0]; | |
29942bc1 ZK |
174 | |
175 | /* | |
176 | * hardware stores this as 8 bit signed value. | |
177 | * we will cap it at 0 if it is a negative number | |
178 | */ | |
179 | if (ard.rssi & 0x80) | |
180 | ard.rssi = 0; | |
181 | if (ard.ext_rssi & 0x80) | |
182 | ard.ext_rssi = 0; | |
183 | ||
184 | vdata_end = (char *)data + datalen; | |
185 | ard.pulse_bw_info = vdata_end[-1]; | |
186 | ard.pulse_length_ext = vdata_end[-2]; | |
187 | ard.pulse_length_pri = vdata_end[-3]; | |
56dc389f ZK |
188 | pe.freq = ah->curchan->channel; |
189 | pe.ts = mactime; | |
58766977 ZK |
190 | if (!ath9k_postprocess_radar_event(sc, &ard, &pe)) |
191 | return; | |
192 | ||
193 | ath_dbg(common, DFS, | |
194 | "ath9k_dfs_process_phyerr: type=%d, freq=%d, ts=%llu, " | |
195 | "width=%d, rssi=%d, delta_ts=%llu\n", | |
196 | ard.pulse_bw_info, pe.freq, pe.ts, pe.width, pe.rssi, | |
197 | pe.ts - sc->dfs_prev_pulse_ts); | |
198 | sc->dfs_prev_pulse_ts = pe.ts; | |
199 | if (ard.pulse_bw_info & PRI_CH_RADAR_FOUND) | |
200 | ath9k_dfs_process_radar_pulse(sc, &pe); | |
201 | if (ard.pulse_bw_info & EXT_CH_RADAR_FOUND) { | |
202 | pe.freq += IS_CHAN_HT40PLUS(ah->curchan) ? 20 : -20; | |
203 | ath9k_dfs_process_radar_pulse(sc, &pe); | |
29942bc1 ZK |
204 | } |
205 | } | |
58766977 ZK |
206 | #undef PRI_CH_RADAR_FOUND |
207 | #undef EXT_CH_RADAR_FOUND |