diff options
Diffstat (limited to 'drivers/net/wireless/ath/ath9k/dfs.c')
-rw-r--r-- | drivers/net/wireless/ath/ath9k/dfs.c | 215 |
1 files changed, 215 insertions, 0 deletions
diff --git a/drivers/net/wireless/ath/ath9k/dfs.c b/drivers/net/wireless/ath/ath9k/dfs.c new file mode 100644 index 00000000000..e4e84a9e627 --- /dev/null +++ b/drivers/net/wireless/ath/ath9k/dfs.c | |||
@@ -0,0 +1,215 @@ | |||
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 | |||
24 | /* | ||
25 | * TODO: move into or synchronize this with generic header | ||
26 | * as soon as IF is defined | ||
27 | */ | ||
28 | struct dfs_radar_pulse { | ||
29 | u16 freq; | ||
30 | u64 ts; | ||
31 | u32 width; | ||
32 | u8 rssi; | ||
33 | }; | ||
34 | |||
35 | /* internal struct to pass radar data */ | ||
36 | struct ath_radar_data { | ||
37 | u8 pulse_bw_info; | ||
38 | u8 rssi; | ||
39 | u8 ext_rssi; | ||
40 | u8 pulse_length_ext; | ||
41 | u8 pulse_length_pri; | ||
42 | }; | ||
43 | |||
44 | /* convert pulse duration to usecs, considering clock mode */ | ||
45 | static u32 dur_to_usecs(struct ath_hw *ah, u32 dur) | ||
46 | { | ||
47 | const u32 AR93X_NSECS_PER_DUR = 800; | ||
48 | const u32 AR93X_NSECS_PER_DUR_FAST = (8000 / 11); | ||
49 | u32 nsecs; | ||
50 | |||
51 | if (IS_CHAN_A_FAST_CLOCK(ah, ah->curchan)) | ||
52 | nsecs = dur * AR93X_NSECS_PER_DUR_FAST; | ||
53 | else | ||
54 | nsecs = dur * AR93X_NSECS_PER_DUR; | ||
55 | |||
56 | return (nsecs + 500) / 1000; | ||
57 | } | ||
58 | |||
59 | #define PRI_CH_RADAR_FOUND 0x01 | ||
60 | #define EXT_CH_RADAR_FOUND 0x02 | ||
61 | static bool | ||
62 | ath9k_postprocess_radar_event(struct ath_softc *sc, | ||
63 | struct ath_radar_data *are, | ||
64 | struct dfs_radar_pulse *drp) | ||
65 | { | ||
66 | u8 rssi; | ||
67 | u16 dur; | ||
68 | |||
69 | ath_dbg(ath9k_hw_common(sc->sc_ah), ATH_DBG_DFS, | ||
70 | "pulse_bw_info=0x%x, pri,ext len/rssi=(%u/%u, %u/%u)\n", | ||
71 | are->pulse_bw_info, | ||
72 | are->pulse_length_pri, are->rssi, | ||
73 | are->pulse_length_ext, are->ext_rssi); | ||
74 | |||
75 | /* | ||
76 | * Only the last 2 bits of the BW info are relevant, they indicate | ||
77 | * which channel the radar was detected in. | ||
78 | */ | ||
79 | are->pulse_bw_info &= 0x03; | ||
80 | |||
81 | switch (are->pulse_bw_info) { | ||
82 | case PRI_CH_RADAR_FOUND: | ||
83 | /* radar in ctrl channel */ | ||
84 | dur = are->pulse_length_pri; | ||
85 | DFS_STAT_INC(sc, pri_phy_errors); | ||
86 | /* | ||
87 | * cannot use ctrl channel RSSI | ||
88 | * if extension channel is stronger | ||
89 | */ | ||
90 | rssi = (are->ext_rssi >= (are->rssi + 3)) ? 0 : are->rssi; | ||
91 | break; | ||
92 | case EXT_CH_RADAR_FOUND: | ||
93 | /* radar in extension channel */ | ||
94 | dur = are->pulse_length_ext; | ||
95 | DFS_STAT_INC(sc, ext_phy_errors); | ||
96 | /* | ||
97 | * cannot use extension channel RSSI | ||
98 | * if control channel is stronger | ||
99 | */ | ||
100 | rssi = (are->rssi >= (are->ext_rssi + 12)) ? 0 : are->ext_rssi; | ||
101 | break; | ||
102 | case (PRI_CH_RADAR_FOUND | EXT_CH_RADAR_FOUND): | ||
103 | /* | ||
104 | * Conducted testing, when pulse is on DC, both pri and ext | ||
105 | * durations are reported to be same | ||
106 | * | ||
107 | * Radiated testing, when pulse is on DC, different pri and | ||
108 | * ext durations are reported, so take the larger of the two | ||
109 | */ | ||
110 | if (are->pulse_length_ext >= are->pulse_length_pri) | ||
111 | dur = are->pulse_length_ext; | ||
112 | else | ||
113 | dur = are->pulse_length_pri; | ||
114 | DFS_STAT_INC(sc, dc_phy_errors); | ||
115 | |||
116 | /* when both are present use stronger one */ | ||
117 | rssi = (are->rssi < are->ext_rssi) ? are->ext_rssi : are->rssi; | ||
118 | break; | ||
119 | default: | ||
120 | /* | ||
121 | * Bogus bandwidth info was received in descriptor, | ||
122 | * so ignore this PHY error | ||
123 | */ | ||
124 | DFS_STAT_INC(sc, bwinfo_discards); | ||
125 | return false; | ||
126 | } | ||
127 | |||
128 | if (rssi == 0) { | ||
129 | DFS_STAT_INC(sc, rssi_discards); | ||
130 | return false; | ||
131 | } | ||
132 | |||
133 | /* | ||
134 | * TODO: check chirping pulses | ||
135 | * checks for chirping are dependent on the DFS regulatory domain | ||
136 | * used, which is yet TBD | ||
137 | */ | ||
138 | |||
139 | /* convert duration to usecs */ | ||
140 | drp->width = dur_to_usecs(sc->sc_ah, dur); | ||
141 | drp->rssi = rssi; | ||
142 | |||
143 | DFS_STAT_INC(sc, pulses_detected); | ||
144 | return true; | ||
145 | } | ||
146 | #undef PRI_CH_RADAR_FOUND | ||
147 | #undef EXT_CH_RADAR_FOUND | ||
148 | |||
149 | /* | ||
150 | * DFS: check PHY-error for radar pulse and feed the detector | ||
151 | */ | ||
152 | void ath9k_dfs_process_phyerr(struct ath_softc *sc, void *data, | ||
153 | struct ath_rx_status *rs, u64 mactime) | ||
154 | { | ||
155 | struct ath_radar_data ard; | ||
156 | u16 datalen; | ||
157 | char *vdata_end; | ||
158 | struct dfs_radar_pulse drp; | ||
159 | struct ath_hw *ah = sc->sc_ah; | ||
160 | struct ath_common *common = ath9k_hw_common(ah); | ||
161 | |||
162 | if ((!(rs->rs_phyerr != ATH9K_PHYERR_RADAR)) && | ||
163 | (!(rs->rs_phyerr != ATH9K_PHYERR_FALSE_RADAR_EXT))) { | ||
164 | ath_dbg(common, ATH_DBG_DFS, | ||
165 | "Error: rs_phyer=0x%x not a radar error\n", | ||
166 | rs->rs_phyerr); | ||
167 | return; | ||
168 | } | ||
169 | |||
170 | datalen = rs->rs_datalen; | ||
171 | if (datalen == 0) { | ||
172 | DFS_STAT_INC(sc, datalen_discards); | ||
173 | return; | ||
174 | } | ||
175 | |||
176 | ard.rssi = rs->rs_rssi_ctl0; | ||
177 | ard.ext_rssi = rs->rs_rssi_ext0; | ||
178 | |||
179 | /* | ||
180 | * hardware stores this as 8 bit signed value. | ||
181 | * we will cap it at 0 if it is a negative number | ||
182 | */ | ||
183 | if (ard.rssi & 0x80) | ||
184 | ard.rssi = 0; | ||
185 | if (ard.ext_rssi & 0x80) | ||
186 | ard.ext_rssi = 0; | ||
187 | |||
188 | vdata_end = (char *)data + datalen; | ||
189 | ard.pulse_bw_info = vdata_end[-1]; | ||
190 | ard.pulse_length_ext = vdata_end[-2]; | ||
191 | ard.pulse_length_pri = vdata_end[-3]; | ||
192 | |||
193 | ath_dbg(common, ATH_DBG_DFS, | ||
194 | "bw_info=%d, length_pri=%d, length_ext=%d, " | ||
195 | "rssi_pri=%d, rssi_ext=%d\n", | ||
196 | ard.pulse_bw_info, ard.pulse_length_pri, ard.pulse_length_ext, | ||
197 | ard.rssi, ard.ext_rssi); | ||
198 | |||
199 | drp.freq = ah->curchan->channel; | ||
200 | drp.ts = mactime; | ||
201 | if (ath9k_postprocess_radar_event(sc, &ard, &drp)) { | ||
202 | static u64 last_ts; | ||
203 | ath_dbg(common, ATH_DBG_DFS, | ||
204 | "ath9k_dfs_process_phyerr: channel=%d, ts=%llu, " | ||
205 | "width=%d, rssi=%d, delta_ts=%llu\n", | ||
206 | drp.freq, drp.ts, drp.width, drp.rssi, drp.ts-last_ts); | ||
207 | last_ts = drp.ts; | ||
208 | /* | ||
209 | * TODO: forward pulse to pattern detector | ||
210 | * | ||
211 | * ieee80211_add_radar_pulse(drp.freq, drp.ts, | ||
212 | * drp.width, drp.rssi); | ||
213 | */ | ||
214 | } | ||
215 | } | ||