1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* Copyright(c) 2009-2012 Realtek Corporation.*/ |
3 | |
4 | #include "wifi.h" |
5 | #include "regd.h" |
6 | |
7 | static struct country_code_to_enum_rd all_countries[] = { |
8 | {COUNTRY_CODE_FCC, "US" }, |
9 | {COUNTRY_CODE_IC, "US" }, |
10 | {COUNTRY_CODE_ETSI, "EC" }, |
11 | {COUNTRY_CODE_SPAIN, "EC" }, |
12 | {COUNTRY_CODE_FRANCE, "EC" }, |
13 | {COUNTRY_CODE_MKK, "JP" }, |
14 | {COUNTRY_CODE_MKK1, "JP" }, |
15 | {COUNTRY_CODE_ISRAEL, "EC" }, |
16 | {COUNTRY_CODE_TELEC, "JP" }, |
17 | {COUNTRY_CODE_MIC, "JP" }, |
18 | {COUNTRY_CODE_GLOBAL_DOMAIN, "JP" }, |
19 | {COUNTRY_CODE_WORLD_WIDE_13, "EC" }, |
20 | {COUNTRY_CODE_TELEC_NETGEAR, "EC" }, |
21 | {COUNTRY_CODE_WORLD_WIDE_13_5G_ALL, "US" }, |
22 | }; |
23 | |
24 | /* |
25 | *Only these channels all allow active |
26 | *scan on all world regulatory domains |
27 | */ |
28 | #define RTL819x_2GHZ_CH01_11 \ |
29 | REG_RULE(2412-10, 2462+10, 40, 0, 20, 0) |
30 | |
31 | /* |
32 | *We enable active scan on these a case |
33 | *by case basis by regulatory domain |
34 | */ |
35 | #define RTL819x_2GHZ_CH12_13 \ |
36 | REG_RULE(2467-10, 2472+10, 40, 0, 20,\ |
37 | NL80211_RRF_PASSIVE_SCAN) |
38 | |
39 | #define RTL819x_2GHZ_CH14 \ |
40 | REG_RULE(2484-10, 2484+10, 40, 0, 20, \ |
41 | NL80211_RRF_PASSIVE_SCAN | \ |
42 | NL80211_RRF_NO_OFDM) |
43 | |
44 | /* 5G chan 36 - chan 64*/ |
45 | #define RTL819x_5GHZ_5150_5350 \ |
46 | REG_RULE(5150-10, 5350+10, 80, 0, 30, 0) |
47 | /* 5G chan 100 - chan 165*/ |
48 | #define RTL819x_5GHZ_5470_5850 \ |
49 | REG_RULE(5470-10, 5850+10, 80, 0, 30, 0) |
50 | /* 5G chan 149 - chan 165*/ |
51 | #define RTL819x_5GHZ_5725_5850 \ |
52 | REG_RULE(5725-10, 5850+10, 80, 0, 30, 0) |
53 | |
54 | #define RTL819x_5GHZ_ALL \ |
55 | (RTL819x_5GHZ_5150_5350, RTL819x_5GHZ_5470_5850) |
56 | |
57 | static const struct ieee80211_regdomain rtl_regdom_11 = { |
58 | .n_reg_rules = 1, |
59 | .alpha2 = "99" , |
60 | .reg_rules = { |
61 | RTL819x_2GHZ_CH01_11, |
62 | } |
63 | }; |
64 | |
65 | static const struct ieee80211_regdomain rtl_regdom_12_13 = { |
66 | .n_reg_rules = 2, |
67 | .alpha2 = "99" , |
68 | .reg_rules = { |
69 | RTL819x_2GHZ_CH01_11, |
70 | RTL819x_2GHZ_CH12_13, |
71 | } |
72 | }; |
73 | |
74 | static const struct ieee80211_regdomain rtl_regdom_no_midband = { |
75 | .n_reg_rules = 3, |
76 | .alpha2 = "99" , |
77 | .reg_rules = { |
78 | RTL819x_2GHZ_CH01_11, |
79 | RTL819x_5GHZ_5150_5350, |
80 | RTL819x_5GHZ_5725_5850, |
81 | } |
82 | }; |
83 | |
84 | static const struct ieee80211_regdomain rtl_regdom_60_64 = { |
85 | .n_reg_rules = 3, |
86 | .alpha2 = "99" , |
87 | .reg_rules = { |
88 | RTL819x_2GHZ_CH01_11, |
89 | RTL819x_2GHZ_CH12_13, |
90 | RTL819x_5GHZ_5725_5850, |
91 | } |
92 | }; |
93 | |
94 | static const struct ieee80211_regdomain rtl_regdom_14_60_64 = { |
95 | .n_reg_rules = 4, |
96 | .alpha2 = "99" , |
97 | .reg_rules = { |
98 | RTL819x_2GHZ_CH01_11, |
99 | RTL819x_2GHZ_CH12_13, |
100 | RTL819x_2GHZ_CH14, |
101 | RTL819x_5GHZ_5725_5850, |
102 | } |
103 | }; |
104 | |
105 | static const struct ieee80211_regdomain rtl_regdom_12_13_5g_all = { |
106 | .n_reg_rules = 4, |
107 | .alpha2 = "99" , |
108 | .reg_rules = { |
109 | RTL819x_2GHZ_CH01_11, |
110 | RTL819x_2GHZ_CH12_13, |
111 | RTL819x_5GHZ_5150_5350, |
112 | RTL819x_5GHZ_5470_5850, |
113 | } |
114 | }; |
115 | |
116 | static const struct ieee80211_regdomain rtl_regdom_14 = { |
117 | .n_reg_rules = 3, |
118 | .alpha2 = "99" , |
119 | .reg_rules = { |
120 | RTL819x_2GHZ_CH01_11, |
121 | RTL819x_2GHZ_CH12_13, |
122 | RTL819x_2GHZ_CH14, |
123 | } |
124 | }; |
125 | |
126 | static bool _rtl_is_radar_freq(u16 center_freq) |
127 | { |
128 | return center_freq >= 5260 && center_freq <= 5700; |
129 | } |
130 | |
131 | static void _rtl_reg_apply_beaconing_flags(struct wiphy *wiphy, |
132 | enum nl80211_reg_initiator initiator) |
133 | { |
134 | enum nl80211_band band; |
135 | struct ieee80211_supported_band *sband; |
136 | const struct ieee80211_reg_rule *reg_rule; |
137 | struct ieee80211_channel *ch; |
138 | unsigned int i; |
139 | |
140 | for (band = 0; band < NUM_NL80211_BANDS; band++) { |
141 | |
142 | if (!wiphy->bands[band]) |
143 | continue; |
144 | |
145 | sband = wiphy->bands[band]; |
146 | |
147 | for (i = 0; i < sband->n_channels; i++) { |
148 | ch = &sband->channels[i]; |
149 | if (_rtl_is_radar_freq(center_freq: ch->center_freq) || |
150 | (ch->flags & IEEE80211_CHAN_RADAR)) |
151 | continue; |
152 | if (initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE) { |
153 | reg_rule = freq_reg_info(wiphy, |
154 | center_freq: ch->center_freq); |
155 | if (IS_ERR(ptr: reg_rule)) |
156 | continue; |
157 | /* |
158 | *If 11d had a rule for this channel ensure |
159 | *we enable adhoc/beaconing if it allows us to |
160 | *use it. Note that we would have disabled it |
161 | *by applying our static world regdomain by |
162 | *default during init, prior to calling our |
163 | *regulatory_hint(). |
164 | */ |
165 | |
166 | if (!(reg_rule->flags & NL80211_RRF_NO_IBSS)) |
167 | ch->flags &= ~IEEE80211_CHAN_NO_IBSS; |
168 | if (!(reg_rule->flags & |
169 | NL80211_RRF_PASSIVE_SCAN)) |
170 | ch->flags &= |
171 | ~IEEE80211_CHAN_PASSIVE_SCAN; |
172 | } else { |
173 | if (ch->beacon_found) |
174 | ch->flags &= ~(IEEE80211_CHAN_NO_IBSS | |
175 | IEEE80211_CHAN_PASSIVE_SCAN); |
176 | } |
177 | } |
178 | } |
179 | } |
180 | |
181 | /* Allows active scan on Ch 12 and 13 */ |
182 | static void _rtl_reg_apply_active_scan_flags(struct wiphy *wiphy, |
183 | enum nl80211_reg_initiator |
184 | initiator) |
185 | { |
186 | struct ieee80211_supported_band *sband; |
187 | struct ieee80211_channel *ch; |
188 | const struct ieee80211_reg_rule *reg_rule; |
189 | |
190 | if (!wiphy->bands[NL80211_BAND_2GHZ]) |
191 | return; |
192 | sband = wiphy->bands[NL80211_BAND_2GHZ]; |
193 | |
194 | /* |
195 | *If no country IE has been received always enable active scan |
196 | *on these channels. This is only done for specific regulatory SKUs |
197 | */ |
198 | if (initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE) { |
199 | ch = &sband->channels[11]; /* CH 12 */ |
200 | if (ch->flags & IEEE80211_CHAN_PASSIVE_SCAN) |
201 | ch->flags &= ~IEEE80211_CHAN_PASSIVE_SCAN; |
202 | ch = &sband->channels[12]; /* CH 13 */ |
203 | if (ch->flags & IEEE80211_CHAN_PASSIVE_SCAN) |
204 | ch->flags &= ~IEEE80211_CHAN_PASSIVE_SCAN; |
205 | return; |
206 | } |
207 | |
208 | /* |
209 | *If a country IE has been recieved check its rule for this |
210 | *channel first before enabling active scan. The passive scan |
211 | *would have been enforced by the initial processing of our |
212 | *custom regulatory domain. |
213 | */ |
214 | |
215 | ch = &sband->channels[11]; /* CH 12 */ |
216 | reg_rule = freq_reg_info(wiphy, center_freq: ch->center_freq); |
217 | if (!IS_ERR(ptr: reg_rule)) { |
218 | if (!(reg_rule->flags & NL80211_RRF_PASSIVE_SCAN)) |
219 | if (ch->flags & IEEE80211_CHAN_PASSIVE_SCAN) |
220 | ch->flags &= ~IEEE80211_CHAN_PASSIVE_SCAN; |
221 | } |
222 | |
223 | ch = &sband->channels[12]; /* CH 13 */ |
224 | reg_rule = freq_reg_info(wiphy, center_freq: ch->center_freq); |
225 | if (!IS_ERR(ptr: reg_rule)) { |
226 | if (!(reg_rule->flags & NL80211_RRF_PASSIVE_SCAN)) |
227 | if (ch->flags & IEEE80211_CHAN_PASSIVE_SCAN) |
228 | ch->flags &= ~IEEE80211_CHAN_PASSIVE_SCAN; |
229 | } |
230 | } |
231 | |
232 | /* |
233 | *Always apply Radar/DFS rules on |
234 | *freq range 5260 MHz - 5700 MHz |
235 | */ |
236 | static void _rtl_reg_apply_radar_flags(struct wiphy *wiphy) |
237 | { |
238 | struct ieee80211_supported_band *sband; |
239 | struct ieee80211_channel *ch; |
240 | unsigned int i; |
241 | |
242 | if (!wiphy->bands[NL80211_BAND_5GHZ]) |
243 | return; |
244 | |
245 | sband = wiphy->bands[NL80211_BAND_5GHZ]; |
246 | |
247 | for (i = 0; i < sband->n_channels; i++) { |
248 | ch = &sband->channels[i]; |
249 | if (!_rtl_is_radar_freq(center_freq: ch->center_freq)) |
250 | continue; |
251 | |
252 | /* |
253 | *We always enable radar detection/DFS on this |
254 | *frequency range. Additionally we also apply on |
255 | *this frequency range: |
256 | *- If STA mode does not yet have DFS supports disable |
257 | * active scanning |
258 | *- If adhoc mode does not support DFS yet then disable |
259 | * adhoc in the frequency. |
260 | *- If AP mode does not yet support radar detection/DFS |
261 | *do not allow AP mode |
262 | */ |
263 | if (!(ch->flags & IEEE80211_CHAN_DISABLED)) |
264 | ch->flags |= IEEE80211_CHAN_RADAR | |
265 | IEEE80211_CHAN_NO_IBSS | |
266 | IEEE80211_CHAN_PASSIVE_SCAN; |
267 | } |
268 | } |
269 | |
270 | static void _rtl_reg_apply_world_flags(struct wiphy *wiphy, |
271 | enum nl80211_reg_initiator initiator, |
272 | struct rtl_regulatory *reg) |
273 | { |
274 | _rtl_reg_apply_beaconing_flags(wiphy, initiator); |
275 | _rtl_reg_apply_active_scan_flags(wiphy, initiator); |
276 | return; |
277 | } |
278 | |
279 | static int _rtl_reg_notifier_apply(struct wiphy *wiphy, |
280 | struct regulatory_request *request, |
281 | struct rtl_regulatory *reg) |
282 | { |
283 | /* We always apply this */ |
284 | _rtl_reg_apply_radar_flags(wiphy); |
285 | |
286 | switch (request->initiator) { |
287 | case NL80211_REGDOM_SET_BY_DRIVER: |
288 | case NL80211_REGDOM_SET_BY_CORE: |
289 | case NL80211_REGDOM_SET_BY_USER: |
290 | break; |
291 | case NL80211_REGDOM_SET_BY_COUNTRY_IE: |
292 | _rtl_reg_apply_world_flags(wiphy, initiator: request->initiator, reg); |
293 | break; |
294 | } |
295 | |
296 | return 0; |
297 | } |
298 | |
299 | static const struct ieee80211_regdomain *_rtl_regdomain_select( |
300 | struct rtl_regulatory *reg) |
301 | { |
302 | switch (reg->country_code) { |
303 | case COUNTRY_CODE_FCC: |
304 | return &rtl_regdom_no_midband; |
305 | case COUNTRY_CODE_IC: |
306 | return &rtl_regdom_11; |
307 | case COUNTRY_CODE_TELEC_NETGEAR: |
308 | return &rtl_regdom_60_64; |
309 | case COUNTRY_CODE_ETSI: |
310 | case COUNTRY_CODE_SPAIN: |
311 | case COUNTRY_CODE_FRANCE: |
312 | case COUNTRY_CODE_ISRAEL: |
313 | return &rtl_regdom_12_13; |
314 | case COUNTRY_CODE_MKK: |
315 | case COUNTRY_CODE_MKK1: |
316 | case COUNTRY_CODE_TELEC: |
317 | case COUNTRY_CODE_MIC: |
318 | return &rtl_regdom_14_60_64; |
319 | case COUNTRY_CODE_GLOBAL_DOMAIN: |
320 | return &rtl_regdom_14; |
321 | case COUNTRY_CODE_WORLD_WIDE_13: |
322 | case COUNTRY_CODE_WORLD_WIDE_13_5G_ALL: |
323 | return &rtl_regdom_12_13_5g_all; |
324 | default: |
325 | return &rtl_regdom_no_midband; |
326 | } |
327 | } |
328 | |
329 | static int _rtl_regd_init_wiphy(struct rtl_regulatory *reg, |
330 | struct wiphy *wiphy, |
331 | void (*reg_notifier)(struct wiphy *wiphy, |
332 | struct regulatory_request * |
333 | request)) |
334 | { |
335 | const struct ieee80211_regdomain *regd; |
336 | |
337 | wiphy->reg_notifier = reg_notifier; |
338 | |
339 | wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG; |
340 | wiphy->regulatory_flags &= ~REGULATORY_STRICT_REG; |
341 | wiphy->regulatory_flags &= ~REGULATORY_DISABLE_BEACON_HINTS; |
342 | regd = _rtl_regdomain_select(reg); |
343 | wiphy_apply_custom_regulatory(wiphy, regd); |
344 | _rtl_reg_apply_radar_flags(wiphy); |
345 | _rtl_reg_apply_world_flags(wiphy, initiator: NL80211_REGDOM_SET_BY_DRIVER, reg); |
346 | return 0; |
347 | } |
348 | |
349 | static struct country_code_to_enum_rd *_rtl_regd_find_country(u16 countrycode) |
350 | { |
351 | int i; |
352 | |
353 | for (i = 0; i < ARRAY_SIZE(all_countries); i++) { |
354 | if (all_countries[i].countrycode == countrycode) |
355 | return &all_countries[i]; |
356 | } |
357 | return NULL; |
358 | } |
359 | |
360 | static u8 channel_plan_to_country_code(u8 channelplan) |
361 | { |
362 | switch (channelplan) { |
363 | case 0x20: |
364 | case 0x21: |
365 | return COUNTRY_CODE_WORLD_WIDE_13; |
366 | case 0x22: |
367 | return COUNTRY_CODE_IC; |
368 | case 0x25: |
369 | return COUNTRY_CODE_ETSI; |
370 | case 0x32: |
371 | return COUNTRY_CODE_TELEC_NETGEAR; |
372 | case 0x41: |
373 | return COUNTRY_CODE_GLOBAL_DOMAIN; |
374 | case 0x7f: |
375 | return COUNTRY_CODE_WORLD_WIDE_13_5G_ALL; |
376 | default: |
377 | return COUNTRY_CODE_MAX; /*Error*/ |
378 | } |
379 | } |
380 | |
381 | int rtl_regd_init(struct ieee80211_hw *hw, |
382 | void (*reg_notifier)(struct wiphy *wiphy, |
383 | struct regulatory_request *request)) |
384 | { |
385 | struct rtl_priv *rtlpriv = rtl_priv(hw); |
386 | struct wiphy *wiphy = hw->wiphy; |
387 | struct country_code_to_enum_rd *country = NULL; |
388 | |
389 | if (!wiphy) |
390 | return -EINVAL; |
391 | |
392 | /* init country_code from efuse channel plan */ |
393 | rtlpriv->regd.country_code = |
394 | channel_plan_to_country_code(channelplan: rtlpriv->efuse.channel_plan); |
395 | |
396 | rtl_dbg(rtlpriv, COMP_REGD, DBG_DMESG, |
397 | "rtl: EEPROM regdomain: 0x%0x country code: %d\n" , |
398 | rtlpriv->efuse.channel_plan, rtlpriv->regd.country_code); |
399 | |
400 | if (rtlpriv->regd.country_code >= COUNTRY_CODE_MAX) { |
401 | rtl_dbg(rtlpriv, COMP_REGD, DBG_DMESG, |
402 | "rtl: EEPROM indicates invalid country code, world wide 13 should be used\n" ); |
403 | |
404 | rtlpriv->regd.country_code = COUNTRY_CODE_WORLD_WIDE_13; |
405 | } |
406 | |
407 | country = _rtl_regd_find_country(countrycode: rtlpriv->regd.country_code); |
408 | |
409 | if (country) { |
410 | rtlpriv->regd.alpha2[0] = country->iso_name[0]; |
411 | rtlpriv->regd.alpha2[1] = country->iso_name[1]; |
412 | } else { |
413 | rtlpriv->regd.alpha2[0] = '0'; |
414 | rtlpriv->regd.alpha2[1] = '0'; |
415 | } |
416 | |
417 | rtl_dbg(rtlpriv, COMP_REGD, DBG_TRACE, |
418 | "rtl: Country alpha2 being used: %c%c\n" , |
419 | rtlpriv->regd.alpha2[0], rtlpriv->regd.alpha2[1]); |
420 | |
421 | _rtl_regd_init_wiphy(reg: &rtlpriv->regd, wiphy, reg_notifier); |
422 | |
423 | return 0; |
424 | } |
425 | |
426 | void rtl_reg_notifier(struct wiphy *wiphy, struct regulatory_request *request) |
427 | { |
428 | struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); |
429 | struct rtl_priv *rtlpriv = rtl_priv(hw); |
430 | |
431 | rtl_dbg(rtlpriv, COMP_REGD, DBG_LOUD, "\n" ); |
432 | |
433 | _rtl_reg_notifier_apply(wiphy, request, reg: &rtlpriv->regd); |
434 | } |
435 | |