1 /******************************************************************************
2 *
3 * Copyright(c) 2009-2010 - 2017 Realtek Corporation.
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of version 2 of the GNU General Public License as
7 * published by the Free Software Foundation.
8 *
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 *****************************************************************************/
15
16 #include <drv_types.h>
17
18 #ifdef CONFIG_IOCTL_CFG80211
rtw_chset_hook_os_channels(struct rf_ctl_t * rfctl)19 void rtw_chset_hook_os_channels(struct rf_ctl_t *rfctl)
20 {
21 struct wiphy *wiphy = dvobj_to_wiphy(rfctl_to_dvobj(rfctl));
22 RT_CHANNEL_INFO *channel_set = rfctl->channel_set;
23 u8 max_chan_nums = rfctl->max_chan_nums;
24 struct ieee80211_channel *ch;
25 unsigned int i;
26 u16 channel;
27 u32 freq;
28
29 for (i = 0; i < max_chan_nums; i++) {
30 channel = channel_set[i].ChannelNum;
31 #if CONFIG_IEEE80211_BAND_6GHZ
32 if (channel_set[i].band == BAND_ON_6G)
33 continue; /* TODO: wiphy with 6G band */
34 else
35 #endif
36 freq = rtw_ch2freq(channel);
37 ch = ieee80211_get_channel(wiphy, freq);
38 if (!ch) {
39 rtw_warn_on(1);
40 continue;
41 }
42
43 channel_set[i].os_chan = ch;
44 }
45 }
46
47 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 0, 0))
rtw_regd_is_wiphy_self_managed(struct wiphy * wiphy)48 bool rtw_regd_is_wiphy_self_managed(struct wiphy *wiphy)
49 {
50 return rtw_rfctl_is_disable_sw_channel_plan(wiphy_to_dvobj(wiphy))
51 || !REGSTY_REGD_SRC_FROM_OS(dvobj_to_regsty(wiphy_to_dvobj(wiphy)));
52 }
53
rtw_get_ieee80211_reg_rule(struct get_chplan_resp * chplan,enum band_type band,int last_end_freq,int start_freq,int end_freq,int next_start_freq,u32 flags)54 static struct ieee80211_reg_rule rtw_get_ieee80211_reg_rule(struct get_chplan_resp *chplan, enum band_type band, int last_end_freq, int start_freq, int end_freq, int next_start_freq, u32 flags)
55 {
56 struct ieee80211_reg_rule rule = REG_RULE(
57 start_freq - 10, end_freq + 10, 20, 6, 20,
58 ((flags & RTW_CHF_NO_IR) ? NL80211_RRF_NO_IR : 0)
59 //| ((flags & RTW_CHF_DFS) ? NL80211_RRF_DFS : 0) /* TODO: DFS */
60 );
61
62 int regd_max_bw = 160;
63 int frange_max_bw = 160;
64
65 if (!(chplan->proto_en & CHPLAN_PROTO_EN_AC) || band == BAND_ON_24G)
66 regd_max_bw = 40;
67 /* TODO: !RFCTL_REG_EN_11HT(rfctl) limit to 20MHz */
68
69 while ((end_freq - start_freq + 20) < frange_max_bw) {
70 frange_max_bw /= 2;
71 if (frange_max_bw == 20)
72 break;
73 }
74 rule.freq_range.max_bandwidth_khz = MHZ_TO_KHZ(rtw_min(regd_max_bw, frange_max_bw));
75
76 if (regd_max_bw > frange_max_bw
77 && (rtw_freq_consecutive(last_end_freq, start_freq)
78 || rtw_freq_consecutive(end_freq, next_start_freq)
79 )
80 )
81 rule.flags |= NL80211_RRF_AUTO_BW;
82
83 if (regd_max_bw < 40)
84 rule.flags |= NL80211_RRF_NO_HT40;
85 if (regd_max_bw < 80)
86 rule.flags |= NL80211_RRF_NO_80MHZ;
87 if (regd_max_bw < 160)
88 rule.flags |= NL80211_RRF_NO_160MHZ;
89
90 return rule;
91 }
92
rtw_build_wiphy_regd(struct wiphy * wiphy,struct get_chplan_resp * chplan,struct ieee80211_regdomain ** regd)93 static int rtw_build_wiphy_regd(struct wiphy *wiphy, struct get_chplan_resp *chplan, struct ieee80211_regdomain **regd)
94 {
95 int i;
96 RT_CHANNEL_INFO *chinfo;
97 enum band_type start_band, band;
98 int last_end_freq, start_freq, end_freq, freq;
99 u32 start_flags, flags;
100 struct ieee80211_regdomain *r;
101 int rule_num = 0;
102 bool build = 0;
103
104 if (regd)
105 *regd = NULL;
106
107 loop:
108 start_band = BAND_MAX;
109 last_end_freq = 0;
110 for (i = 0; i < chplan->chset_num; i++) {
111 chinfo = &chplan->chset[i];
112 freq = rtw_ch2freq_by_band(chinfo->band, chinfo->ChannelNum);
113 if (!freq) {
114 RTW_WARN(FUNC_WIPHY_FMT" rtw_ch2freq_by_band(%s, %u) fail\n"
115 , FUNC_WIPHY_ARG(wiphy), band_str(chinfo->band), chinfo->ChannelNum);
116 continue;
117 }
118 band = chinfo->band;
119 flags = chinfo->flags & (RTW_CHF_NO_IR | RTW_CHF_DFS);
120
121 if (start_band == BAND_MAX) {
122 start_band = band;
123 start_freq = end_freq = freq;
124 start_flags = flags;
125 continue;
126 }
127
128 if (start_band == band
129 && start_flags == flags
130 && rtw_freq_consecutive(end_freq, freq)
131 ) {
132 end_freq = freq;
133 continue;
134 }
135
136 /* create rule */
137 if (build) {
138 RTW_DBG("add rule_%02d(%s, %d, %d, 0x%x)\n"
139 , r->n_reg_rules, band_str(start_band), start_freq, end_freq, start_flags);
140 r->reg_rules[r->n_reg_rules++] = rtw_get_ieee80211_reg_rule(chplan, start_band
141 , last_end_freq, start_freq, end_freq, freq, start_flags);
142 } else
143 rule_num++;
144
145 /* start a new rule */
146 start_band = band;
147 last_end_freq = end_freq;
148 start_freq = end_freq = freq;
149 start_flags = flags;
150 }
151
152 if (start_band != BAND_MAX) {
153 /* create rule */
154 if (build) {
155 RTW_DBG("add rule_%02d(%s, %d, %d, 0x%x)\n"
156 , r->n_reg_rules, band_str(start_band), start_freq, end_freq, start_flags);
157 r->reg_rules[r->n_reg_rules++] = rtw_get_ieee80211_reg_rule(chplan, start_band
158 , last_end_freq, start_freq, end_freq, 0, start_flags);
159 } else
160 rule_num++;
161 }
162
163 if (!build) {
164 /* switch to build phase */
165 build = 1;
166 if (!regd)
167 goto exit;
168
169 r = rtw_zmalloc(sizeof(**regd) + sizeof(struct ieee80211_reg_rule) * rule_num);
170 if (!r) {
171 rule_num = -1;
172 goto exit;
173 }
174
175 _rtw_memcpy(r->alpha2, chplan->alpha2, 2);
176 r->alpha2[2] = 0;
177 r->dfs_region = NL80211_DFS_UNSET;
178 goto loop;
179 }
180
181 *regd = r;
182
183 exit:
184 return rule_num;
185 }
186
rtw_regd_disable_no_20mhz_chs(struct wiphy * wiphy)187 static void rtw_regd_disable_no_20mhz_chs(struct wiphy *wiphy)
188 {
189 struct ieee80211_supported_band *sband;
190 struct ieee80211_channel *ch;
191 unsigned int i, j;
192
193 for (i = 0; i < NUM_NL80211_BANDS; i++) {
194 sband = wiphy->bands[i];
195 if (!sband)
196 continue;
197 for (j = 0; j < sband->n_channels; j++) {
198 ch = &sband->channels[j];
199 if (!ch)
200 continue;
201 if (ch->flags & IEEE80211_CHAN_NO_20MHZ) {
202 RTW_INFO(FUNC_WIPHY_FMT" disable band:%d ch:%u w/o 20MHz\n", FUNC_WIPHY_ARG(wiphy), ch->band, ch->hw_value);
203 ch->flags = IEEE80211_CHAN_DISABLED;
204 }
205 }
206 }
207 }
208
rtw_update_wiphy_regd(struct wiphy * wiphy,struct get_chplan_resp * chplan,bool rtnl_lock_needed)209 void rtw_update_wiphy_regd(struct wiphy *wiphy, struct get_chplan_resp *chplan, bool rtnl_lock_needed)
210 {
211 struct ieee80211_regdomain *regd;
212 int ret;
213
214 ret = rtw_build_wiphy_regd(wiphy, chplan, ®d);
215 if (ret == -1) {
216 RTW_WARN(FUNC_WIPHY_FMT" rtw_build_wiphy_regd() fail\n", FUNC_WIPHY_ARG(wiphy));
217 return;
218 }
219
220 if (ret == 0) {
221 RTW_WARN(FUNC_WIPHY_FMT" rtw_build_wiphy_regd() builds empty regd, bypass regd setting\n", FUNC_WIPHY_ARG(wiphy));
222 goto free_regd;
223 }
224
225 if (rtnl_lock_needed)
226 rtnl_lock();
227
228 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0))
229 ret = regulatory_set_wiphy_regd_sync(wiphy, regd);
230 #else
231 ret = regulatory_set_wiphy_regd_sync_rtnl(wiphy, regd);
232 #endif
233
234 rtw_regd_disable_no_20mhz_chs(wiphy);
235
236 if (rtnl_lock_needed)
237 rtnl_unlock();
238
239 if (ret != 0)
240 RTW_INFO(FUNC_WIPHY_FMT" regulatory_set_wiphy_regd_sync_rtnl return %d\n", FUNC_WIPHY_ARG(wiphy), ret);
241
242 free_regd:
243 rtw_mfree(regd, sizeof(*regd) + sizeof(struct ieee80211_reg_rule) * regd->n_reg_rules);
244 }
245 #endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 0, 0)) */
246
rtw_regd_overide_flags(struct wiphy * wiphy,struct get_chplan_resp * chplan,bool rtnl_lock_needed)247 static void rtw_regd_overide_flags(struct wiphy *wiphy, struct get_chplan_resp *chplan, bool rtnl_lock_needed)
248 {
249 RT_CHANNEL_INFO *channel_set = chplan->chset;
250 u8 max_chan_nums = chplan->chset_num;
251 struct ieee80211_supported_band *sband;
252 struct ieee80211_channel *ch;
253 unsigned int i, j;
254
255 if (rtnl_lock_needed)
256 rtnl_lock();
257
258 /* all channels disable */
259 for (i = 0; i < NUM_NL80211_BANDS; i++) {
260 sband = wiphy->bands[i];
261 if (!sband)
262 continue;
263 for (j = 0; j < sband->n_channels; j++) {
264 ch = &sband->channels[j];
265 if (!ch)
266 continue;
267 ch->flags = IEEE80211_CHAN_DISABLED;
268 }
269 }
270
271 /* channels apply by channel plans. */
272 for (i = 0; i < max_chan_nums; i++) {
273 ch = channel_set[i].os_chan;
274 if (!ch)
275 continue;
276
277 /* enable */
278 ch->flags = 0;
279
280 if (channel_set[i].flags & RTW_CHF_DFS) {
281 /*
282 * before integrating with nl80211 flow
283 * bypass IEEE80211_CHAN_RADAR when configured with radar detection
284 * to prevent from hostapd blocking DFS channels
285 */
286 #ifdef CONFIG_DFS_MASTER
287 if (chplan->dfs_domain == RTW_DFS_REGD_NONE)
288 #endif
289 ch->flags |= IEEE80211_CHAN_RADAR;
290 }
291
292 if (channel_set[i].flags & RTW_CHF_NO_IR) {
293 #if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 14, 0))
294 ch->flags |= IEEE80211_CHAN_NO_IBSS | IEEE80211_CHAN_PASSIVE_SCAN;
295 #else
296 ch->flags |= IEEE80211_CHAN_NO_IR;
297 #endif
298 }
299 }
300
301 if (rtnl_lock_needed)
302 rtnl_unlock();
303 }
304
305 #ifdef CONFIG_REGD_SRC_FROM_OS
rtw_regd_apply_dfs_flags(struct get_chplan_resp * chplan,bool rtnl_lock_needed)306 static void rtw_regd_apply_dfs_flags(struct get_chplan_resp *chplan, bool rtnl_lock_needed)
307 {
308 RT_CHANNEL_INFO *channel_set = chplan->chset;
309 u8 max_chan_nums = chplan->chset_num;
310 unsigned int i;
311 struct ieee80211_channel *chan;
312
313 if (rtnl_lock_needed)
314 rtnl_lock();
315
316 /* channels apply by channel plans. */
317 for (i = 0; i < max_chan_nums; i++) {
318 chan = channel_set[i].os_chan;
319 if (!chan) {
320 rtw_warn_on(1);
321 continue;
322 }
323 if (channel_set[i].flags & RTW_CHF_DFS) {
324 /*
325 * before integrating with nl80211 flow
326 * clear IEEE80211_CHAN_RADAR when configured with radar detection
327 * to prevent from hostapd blocking DFS channels
328 */
329 #ifdef CONFIG_DFS_MASTER
330 if (chplan->dfs_domain != RTW_DFS_REGD_NONE)
331 chan->flags &= ~IEEE80211_CHAN_RADAR;
332 #endif
333 }
334 }
335
336 if (rtnl_lock_needed)
337 rtnl_unlock();
338 }
339
340 /* init_channel_set_from_wiphy */
rtw_os_init_channel_set(_adapter * padapter,RT_CHANNEL_INFO * channel_set)341 u8 rtw_os_init_channel_set(_adapter *padapter, RT_CHANNEL_INFO *channel_set)
342 {
343 struct wiphy *wiphy = adapter_to_wiphy(padapter);
344 struct rf_ctl_t *rfctl = adapter_to_rfctl(padapter);
345 struct registry_priv *regsty = adapter_to_regsty(padapter);
346 struct ieee80211_channel *chan;
347 u8 chanset_size = 0;
348 int i, j;
349
350 _rtw_memset(channel_set, 0, sizeof(RT_CHANNEL_INFO) * MAX_CHANNEL_NUM);
351
352 for (i = NL80211_BAND_2GHZ; i <= NL80211_BAND_5GHZ; i++) {
353 if (!wiphy->bands[i])
354 continue;
355 for (j = 0; j < wiphy->bands[i]->n_channels; j++) {
356 chan = &wiphy->bands[i]->channels[j];
357 if (chan->flags & IEEE80211_CHAN_DISABLED)
358 continue;
359 if (rtw_regsty_is_excl_chs(regsty, chan->hw_value))
360 continue;
361
362 if (chanset_size >= MAX_CHANNEL_NUM) {
363 RTW_WARN("chset size can't exceed MAX_CHANNEL_NUM(%u)\n", MAX_CHANNEL_NUM);
364 i = NL80211_BAND_5GHZ + 1;
365 break;
366 }
367
368 channel_set[chanset_size].ChannelNum = chan->hw_value;
369 channel_set[chanset_size].band = nl80211_band_to_rtw_band(i);
370 #if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 14, 0))
371 if (chan->flags & (IEEE80211_CHAN_NO_IBSS | IEEE80211_CHAN_PASSIVE_SCAN))
372 #else
373 if (chan->flags & IEEE80211_CHAN_NO_IR)
374 #endif
375 channel_set[chanset_size].flags |= RTW_CHF_NO_IR;
376 if (chan->flags & IEEE80211_CHAN_RADAR)
377 channel_set[chanset_size].flags |= RTW_CHF_DFS;
378 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27))
379 if (chan->flags & IEEE80211_CHAN_NO_HT40PLUS)
380 channel_set[chanset_size].flags |= RTW_CHF_NO_HT40U;
381 if (chan->flags & IEEE80211_CHAN_NO_HT40MINUS)
382 channel_set[chanset_size].flags |= RTW_CHF_NO_HT40L;
383 #endif
384 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 9, 0))
385 if (chan->flags & IEEE80211_CHAN_NO_80MHZ)
386 channel_set[chanset_size].flags |= RTW_CHF_NO_80MHZ;
387 if (chan->flags & IEEE80211_CHAN_NO_160MHZ)
388 channel_set[chanset_size].flags |= RTW_CHF_NO_160MHZ;
389 #endif
390 channel_set[chanset_size].os_chan = chan;
391 chanset_size++;
392 }
393 }
394
395 #if CONFIG_IEEE80211_BAND_5GHZ
396 #ifdef CONFIG_DFS_MASTER
397 for (i = 0; i < chanset_size; i++)
398 channel_set[i].non_ocp_end_time = rtw_get_current_time();
399 #endif
400 #endif /* CONFIG_IEEE80211_BAND_5GHZ */
401
402 if (chanset_size)
403 RTW_INFO(FUNC_ADPT_FMT" ch num:%d\n"
404 , FUNC_ADPT_ARG(padapter), chanset_size);
405 else
406 RTW_WARN(FUNC_ADPT_FMT" final chset has no channel\n"
407 , FUNC_ADPT_ARG(padapter));
408
409 return chanset_size;
410 }
411
rtw_os_get_total_txpwr_regd_lmt_mbm(_adapter * adapter,u8 cch,enum channel_width bw)412 s16 rtw_os_get_total_txpwr_regd_lmt_mbm(_adapter *adapter, u8 cch, enum channel_width bw)
413 {
414 struct wiphy *wiphy = adapter_to_wiphy(adapter);
415 s16 mbm = UNSPECIFIED_MBM;
416 u8 *op_chs;
417 u8 op_ch_num;
418 u8 i;
419 u32 freq;
420 struct ieee80211_channel *ch;
421
422 if (!rtw_get_op_chs_by_cch_bw(cch, bw, &op_chs, &op_ch_num))
423 goto exit;
424
425 for (i = 0; i < op_ch_num; i++) {
426 freq = rtw_ch2freq(op_chs[i]);
427 ch = ieee80211_get_channel(wiphy, freq);
428 if (!ch) {
429 rtw_warn_on(1);
430 continue;
431 }
432 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0))
433 mbm = rtw_min(mbm, ch->max_reg_power * MBM_PDBM);
434 #else
435 /* require max_power == 0 (therefore orig_mpwr set to 0) when wiphy registration */
436 mbm = rtw_min(mbm, ch->max_power * MBM_PDBM);
437 #endif
438 }
439
440 exit:
441 return mbm;
442 }
443
444 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 3, 0))
nl80211_dfs_regions_to_rtw_dfs_region(enum nl80211_dfs_regions region)445 static enum rtw_dfs_regd nl80211_dfs_regions_to_rtw_dfs_region(enum nl80211_dfs_regions region)
446 {
447 switch (region) {
448 case NL80211_DFS_FCC:
449 return RTW_DFS_REGD_FCC;
450 case NL80211_DFS_ETSI:
451 return RTW_DFS_REGD_ETSI;
452 case NL80211_DFS_JP:
453 return RTW_DFS_REGD_MKK;
454 case NL80211_DFS_UNSET:
455 default:
456 return RTW_DFS_REGD_NONE;
457 }
458 };
459 #endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 3, 0)) */
460 #endif /* CONFIG_REGD_SRC_FROM_OS */
461
nl80211_reg_initiator_to_rtw_regd_inr(enum nl80211_reg_initiator initiator)462 static enum rtw_regd_inr nl80211_reg_initiator_to_rtw_regd_inr(enum nl80211_reg_initiator initiator)
463 {
464 switch (initiator) {
465 case NL80211_REGDOM_SET_BY_DRIVER:
466 return RTW_REGD_SET_BY_DRIVER;
467 case NL80211_REGDOM_SET_BY_CORE:
468 return RTW_REGD_SET_BY_CORE;
469 case NL80211_REGDOM_SET_BY_USER:
470 return RTW_REGD_SET_BY_USER;
471 case NL80211_REGDOM_SET_BY_COUNTRY_IE:
472 return RTW_REGD_SET_BY_COUNTRY_IE;
473 }
474 rtw_warn_on(1);
475 return RTW_REGD_SET_BY_NUM;
476 };
477
478 #ifdef CONFIG_RTW_DEBUG
nl80211_reg_initiator_str(enum nl80211_reg_initiator initiator)479 static const char *nl80211_reg_initiator_str(enum nl80211_reg_initiator initiator)
480 {
481 switch (initiator) {
482 case NL80211_REGDOM_SET_BY_DRIVER:
483 return "DRIVER";
484 case NL80211_REGDOM_SET_BY_CORE:
485 return "CORE";
486 case NL80211_REGDOM_SET_BY_USER:
487 return "USER";
488 case NL80211_REGDOM_SET_BY_COUNTRY_IE:
489 return "COUNTRY_IE";
490 }
491 rtw_warn_on(1);
492 return "UNKNOWN";
493 }
494
495 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0))
nl80211_user_reg_hint_type_str(enum nl80211_user_reg_hint_type type)496 static const char *nl80211_user_reg_hint_type_str(enum nl80211_user_reg_hint_type type)
497 {
498 switch (type) {
499 case NL80211_USER_REG_HINT_USER:
500 return "USER";
501 case NL80211_USER_REG_HINT_CELL_BASE:
502 return "CELL_BASE";
503 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 16, 0))
504 case NL80211_USER_REG_HINT_INDOOR:
505 return "INDOOR";
506 #endif
507 }
508 rtw_warn_on(1);
509 return "UNKNOWN";
510 }
511 #endif
512
513 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 3, 0))
nl80211_dfs_regions_str(enum nl80211_dfs_regions region)514 static const char *nl80211_dfs_regions_str(enum nl80211_dfs_regions region)
515 {
516 switch (region) {
517 case NL80211_DFS_UNSET:
518 return "UNSET";
519 case NL80211_DFS_FCC:
520 return "FCC";
521 case NL80211_DFS_ETSI:
522 return "ETSI";
523 case NL80211_DFS_JP:
524 return "JP";
525 }
526 rtw_warn_on(1);
527 return "UNKNOWN";
528 };
529 #endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 3, 0)) */
530
environment_cap_str(enum environment_cap cap)531 static const char *environment_cap_str(enum environment_cap cap)
532 {
533 switch (cap) {
534 case ENVIRON_ANY:
535 return "ANY";
536 case ENVIRON_INDOOR:
537 return "INDOOR";
538 case ENVIRON_OUTDOOR:
539 return "OUTDOOR";
540 }
541 rtw_warn_on(1);
542 return "UNKNOWN";
543 }
544
dump_requlatory_request(void * sel,struct regulatory_request * request)545 static void dump_requlatory_request(void *sel, struct regulatory_request *request)
546 {
547 u8 alpha2_len;
548
549 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 16, 0))
550 alpha2_len = 3;
551 #else
552 alpha2_len = 2;
553 #endif
554
555 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0))
556 RTW_PRINT_SEL(sel, "initiator:%s, wiphy_idx:%d, type:%s\n"
557 , nl80211_reg_initiator_str(request->initiator)
558 , request->wiphy_idx
559 , nl80211_user_reg_hint_type_str(request->user_reg_hint_type));
560 #else
561 RTW_PRINT_SEL(sel, "initiator:%s, wiphy_idx:%d\n"
562 , nl80211_reg_initiator_str(request->initiator)
563 , request->wiphy_idx);
564 #endif
565
566 RTW_PRINT_SEL(sel, "alpha2:%.*s\n", alpha2_len, request->alpha2);
567 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 3, 0))
568 RTW_PRINT_SEL(sel, "dfs_region:%s\n", nl80211_dfs_regions_str(request->dfs_region));
569 #endif
570
571 RTW_PRINT_SEL(sel, "intersect:%d\n", request->intersect);
572 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 38))
573 RTW_PRINT_SEL(sel, "processed:%d\n", request->processed);
574 #endif
575 #if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36))
576 RTW_PRINT_SEL(sel, "country_ie_checksum:0x%08x\n", request->country_ie_checksum);
577 #endif
578
579 RTW_PRINT_SEL(sel, "country_ie_env:%s\n", environment_cap_str(request->country_ie_env));
580 }
581 #endif /* CONFIG_RTW_DEBUG */
582
rtw_reg_notifier(struct wiphy * wiphy,struct regulatory_request * request)583 static void rtw_reg_notifier(struct wiphy *wiphy, struct regulatory_request *request)
584 {
585 struct dvobj_priv *dvobj = wiphy_to_dvobj(wiphy);
586 struct registry_priv *regsty = dvobj_to_regsty(dvobj);
587 enum rtw_regd_inr inr;
588
589 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 11, 0))
590 rtw_set_rtnl_lock_holder(dvobj, current);
591 #endif
592
593 #ifdef CONFIG_RTW_DEBUG
594 if (rtw_drv_log_level >= _DRV_INFO_) {
595 RTW_INFO(FUNC_WIPHY_FMT"\n", FUNC_WIPHY_ARG(wiphy));
596 dump_requlatory_request(RTW_DBGDUMP, request);
597 }
598 #endif
599
600 inr = nl80211_reg_initiator_to_rtw_regd_inr(request->initiator);
601
602 #ifdef CONFIG_REGD_SRC_FROM_OS
603 if (REGSTY_REGD_SRC_FROM_OS(regsty)) {
604 enum rtw_dfs_regd dfs_region = RTW_DFS_REGD_NONE;
605
606 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 3, 0))
607 dfs_region = nl80211_dfs_regions_to_rtw_dfs_region(request->dfs_region);
608 #endif
609
610 /* trigger command to sync regulatory form OS */
611 rtw_sync_os_regd_cmd(wiphy_to_adapter(wiphy), RTW_CMDF_WAIT_ACK, request->alpha2, dfs_region, inr);
612 } else
613 #endif
614 {
615 /* use alpha2 as input to select the corresponding channel plan settings defined by Realtek */
616 struct get_chplan_resp *chplan;
617
618 switch (request->initiator) {
619 case NL80211_REGDOM_SET_BY_USER:
620 rtw_set_country(wiphy_to_adapter(wiphy), request->alpha2, inr);
621 break;
622 case NL80211_REGDOM_SET_BY_DRIVER:
623 case NL80211_REGDOM_SET_BY_CORE:
624 case NL80211_REGDOM_SET_BY_COUNTRY_IE:
625 default:
626 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 0, 0))
627 rtw_warn_on(rtw_regd_is_wiphy_self_managed(wiphy));
628 #endif
629 if (rtw_get_chplan_cmd(wiphy_to_adapter(wiphy), RTW_CMDF_WAIT_ACK, &chplan) == _SUCCESS)
630 rtw_regd_change_complete_sync(wiphy, chplan, 0);
631 else
632 rtw_warn_on(1);
633 break;
634 }
635 }
636
637 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 11, 0))
638 rtw_set_rtnl_lock_holder(dvobj, NULL);
639 #endif
640 }
641
642 #if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 9, 0))
rtw_reg_notifier_return(struct wiphy * wiphy,struct regulatory_request * request)643 static int rtw_reg_notifier_return(struct wiphy *wiphy, struct regulatory_request *request)
644 {
645 rtw_reg_notifier(wiphy, request);
646 return 0;
647 }
648 #endif
649
650 struct async_regd_change_evt {
651 _list list; /* async_regd_change_list */
652 struct wiphy *wiphy;
653 struct get_chplan_resp *chplan;
654 };
655
async_regd_change_work_hdl(_workitem * work)656 static void async_regd_change_work_hdl(_workitem *work)
657 {
658 struct rtw_wiphy_data *wiphy_data = container_of(work, struct rtw_wiphy_data, async_regd_change_work);
659 struct async_regd_change_evt *evt;
660 _list *list, *head = &wiphy_data->async_regd_change_list;
661
662 while (1) {
663 _rtw_mutex_lock_interruptible(&wiphy_data->async_regd_change_mutex);
664 list = rtw_is_list_empty(head) ? NULL : get_next(head);
665 if (list)
666 rtw_list_delete(list);
667 _rtw_mutex_unlock(&wiphy_data->async_regd_change_mutex);
668
669 if (!list)
670 break;
671
672 evt = LIST_CONTAINOR(list, struct async_regd_change_evt, list);
673 rtw_regd_change_complete_sync(evt->wiphy, evt->chplan, 1);
674 rtw_mfree(evt, sizeof(*evt));
675 }
676 }
677
rtw_regd_init(struct wiphy * wiphy)678 int rtw_regd_init(struct wiphy *wiphy)
679 {
680 struct rtw_wiphy_data *wiphy_data = rtw_wiphy_priv(wiphy);
681
682 #if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 9, 0))
683 wiphy->reg_notifier = rtw_reg_notifier_return;
684 #else
685 wiphy->reg_notifier = rtw_reg_notifier;
686 #endif
687
688 #if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 14, 0))
689 wiphy->flags &= ~WIPHY_FLAG_STRICT_REGULATORY;
690 wiphy->flags &= ~WIPHY_FLAG_DISABLE_BEACON_HINTS;
691 #else
692 wiphy->regulatory_flags &= ~REGULATORY_STRICT_REG;
693 wiphy->regulatory_flags &= ~REGULATORY_DISABLE_BEACON_HINTS;
694 #endif
695
696 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0))
697 wiphy->regulatory_flags |= REGULATORY_IGNORE_STALE_KICKOFF;
698 #endif
699
700 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 0, 0))
701 if (rtw_regd_is_wiphy_self_managed(wiphy))
702 wiphy->regulatory_flags |= REGULATORY_WIPHY_SELF_MANAGED;
703 #endif
704
705 _rtw_init_listhead(&wiphy_data->async_regd_change_list);
706 _rtw_mutex_init(&wiphy_data->async_regd_change_mutex);
707 _init_workitem(&wiphy_data->async_regd_change_work, async_regd_change_work_hdl, NULL);
708
709 return 0;
710 }
711
rtw_regd_async_regd_change_list_free(struct wiphy * wiphy)712 static void rtw_regd_async_regd_change_list_free(struct wiphy *wiphy)
713 {
714 struct rtw_wiphy_data *wiphy_data = rtw_wiphy_priv(wiphy);
715 struct async_regd_change_evt *evt;
716 struct get_chplan_resp *chplan;
717 _list *cur, *head;
718
719 _rtw_mutex_lock_interruptible(&wiphy_data->async_regd_change_mutex);
720
721 head = &wiphy_data->async_regd_change_list;
722 cur = get_next(head);
723
724 while ((rtw_end_of_queue_search(head, cur)) == _FALSE) {
725 evt = LIST_CONTAINOR(cur, struct async_regd_change_evt, list);
726 chplan = evt->chplan;
727 cur = get_next(cur);
728 rtw_list_delete(&evt->list);
729 rtw_vmfree(chplan, sizeof(*chplan) + sizeof(RT_CHANNEL_INFO) * chplan->chset_num);
730 rtw_mfree(evt, sizeof(*evt));
731 }
732
733 _rtw_mutex_unlock(&wiphy_data->async_regd_change_mutex);
734 }
735
rtw_regd_deinit(struct wiphy * wiphy)736 void rtw_regd_deinit(struct wiphy *wiphy)
737 {
738 struct rtw_wiphy_data *wiphy_data = rtw_wiphy_priv(wiphy);
739
740 _cancel_workitem_sync(&wiphy_data->async_regd_change_work);
741 rtw_regd_async_regd_change_list_free(wiphy);
742 _rtw_mutex_free(&wiphy_data->async_regd_change_mutex);
743 }
744
rtw_regd_change_complete_sync(struct wiphy * wiphy,struct get_chplan_resp * chplan,bool rtnl_lock_needed)745 void rtw_regd_change_complete_sync(struct wiphy *wiphy, struct get_chplan_resp *chplan, bool rtnl_lock_needed)
746 {
747 if (chplan->regd_src == REGD_SRC_RTK_PRIV) {
748 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 0, 0))
749 if (rtw_regd_is_wiphy_self_managed(wiphy))
750 rtw_update_wiphy_regd(wiphy, chplan, rtnl_lock_needed);
751 else
752 #endif
753 rtw_regd_overide_flags(wiphy, chplan, rtnl_lock_needed);
754 }
755 #ifdef CONFIG_REGD_SRC_FROM_OS
756 else if (chplan->regd_src == REGD_SRC_OS)
757 rtw_regd_apply_dfs_flags(chplan, rtnl_lock_needed);
758 #endif
759 else
760 rtw_warn_on(1);
761
762 rtw_vmfree(chplan, sizeof(struct get_chplan_resp) + sizeof(RT_CHANNEL_INFO) * chplan->chset_num);
763 }
764
rtw_regd_change_complete_async(struct wiphy * wiphy,struct get_chplan_resp * chplan)765 int rtw_regd_change_complete_async(struct wiphy *wiphy, struct get_chplan_resp *chplan)
766 {
767 struct rtw_wiphy_data *wiphy_data = rtw_wiphy_priv(wiphy);
768 struct async_regd_change_evt *evt;
769
770 evt = rtw_malloc(sizeof(*evt));
771 if (!evt) {
772 rtw_vmfree(chplan, sizeof(struct get_chplan_resp) + sizeof(RT_CHANNEL_INFO) * chplan->chset_num);
773 return _FAIL;
774 }
775
776 _rtw_init_listhead(&evt->list);
777 evt->wiphy = wiphy;
778 evt->chplan = chplan;
779
780 _rtw_mutex_lock_interruptible(&wiphy_data->async_regd_change_mutex);
781
782 rtw_list_insert_tail(&evt->list, &wiphy_data->async_regd_change_list);
783
784 _rtw_mutex_unlock(&wiphy_data->async_regd_change_mutex);
785
786 _set_workitem(&wiphy_data->async_regd_change_work);
787
788 return _SUCCESS;
789 }
790 #endif /* CONFIG_IOCTL_CFG80211 */
791