xref: /OK3568_Linux_fs/external/rkwifibt/drivers/rtl8822cs/os_dep/linux/wifi_regd.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
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,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, 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_2_4G)
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 	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, &regd);
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 	_irqL irqL;
661 	_list *list, *head = &wiphy_data->async_regd_change_list;
662 
663 	while (1) {
664 		_enter_critical_mutex(&wiphy_data->async_regd_change_mutex, &irqL);
665 		list = rtw_is_list_empty(head) ? NULL : get_next(head);
666 		if (list)
667 			rtw_list_delete(list);
668 		_exit_critical_mutex(&wiphy_data->async_regd_change_mutex, &irqL);
669 
670 		if (!list)
671 			break;
672 
673 		evt = LIST_CONTAINOR(list, struct async_regd_change_evt, list);
674 		rtw_regd_change_complete_sync(evt->wiphy, evt->chplan, 1);
675 		rtw_mfree(evt, sizeof(*evt));
676 	}
677 }
678 
rtw_regd_init(struct wiphy * wiphy)679 int rtw_regd_init(struct wiphy *wiphy)
680 {
681 	struct rtw_wiphy_data *wiphy_data = rtw_wiphy_priv(wiphy);
682 
683 #if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 9, 0))
684 	wiphy->reg_notifier = rtw_reg_notifier_return;
685 #else
686 	wiphy->reg_notifier = rtw_reg_notifier;
687 #endif
688 
689 #if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 14, 0))
690 	wiphy->flags &= ~WIPHY_FLAG_STRICT_REGULATORY;
691 	wiphy->flags &= ~WIPHY_FLAG_DISABLE_BEACON_HINTS;
692 #else
693 	wiphy->regulatory_flags &= ~REGULATORY_STRICT_REG;
694 	wiphy->regulatory_flags &= ~REGULATORY_DISABLE_BEACON_HINTS;
695 #endif
696 
697 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0))
698 	wiphy->regulatory_flags |= REGULATORY_IGNORE_STALE_KICKOFF;
699 #endif
700 
701 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 0, 0))
702 	if (rtw_regd_is_wiphy_self_managed(wiphy))
703 		wiphy->regulatory_flags |= REGULATORY_WIPHY_SELF_MANAGED;
704 #endif
705 
706 	_rtw_init_listhead(&wiphy_data->async_regd_change_list);
707 	_rtw_mutex_init(&wiphy_data->async_regd_change_mutex);
708 	_init_workitem(&wiphy_data->async_regd_change_work, async_regd_change_work_hdl, NULL);
709 
710 	return 0;
711 }
712 
rtw_regd_async_regd_change_list_free(struct wiphy * wiphy)713 static void rtw_regd_async_regd_change_list_free(struct wiphy *wiphy)
714 {
715 	struct rtw_wiphy_data *wiphy_data = rtw_wiphy_priv(wiphy);
716 	struct async_regd_change_evt *evt;
717 	struct get_chplan_resp *chplan;
718 	_irqL irqL;
719 	_list *cur, *head;
720 
721 	_enter_critical_mutex(&wiphy_data->async_regd_change_mutex, &irqL);
722 
723 	head = &wiphy_data->async_regd_change_list;
724 	cur = get_next(head);
725 
726 	while ((rtw_end_of_queue_search(head, cur)) == _FALSE) {
727 		evt = LIST_CONTAINOR(cur, struct async_regd_change_evt, list);
728 		chplan = evt->chplan;
729 		cur = get_next(cur);
730 		rtw_list_delete(&evt->list);
731 		rtw_vmfree(chplan, sizeof(*chplan) + sizeof(RT_CHANNEL_INFO) * chplan->chset_num);
732 		rtw_mfree(evt, sizeof(*evt));
733 	}
734 
735 	_exit_critical_mutex(&wiphy_data->async_regd_change_mutex, &irqL);
736 }
737 
rtw_regd_deinit(struct wiphy * wiphy)738 void rtw_regd_deinit(struct wiphy *wiphy)
739 {
740 	struct rtw_wiphy_data *wiphy_data = rtw_wiphy_priv(wiphy);
741 
742 	_cancel_workitem_sync(&wiphy_data->async_regd_change_work);
743 	rtw_regd_async_regd_change_list_free(wiphy);
744 	_rtw_mutex_free(&wiphy_data->async_regd_change_mutex);
745 }
746 
rtw_regd_change_complete_sync(struct wiphy * wiphy,struct get_chplan_resp * chplan,bool rtnl_lock_needed)747 void rtw_regd_change_complete_sync(struct wiphy *wiphy, struct get_chplan_resp *chplan, bool rtnl_lock_needed)
748 {
749 	if (chplan->regd_src == REGD_SRC_RTK_PRIV) {
750 		#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 0, 0))
751 		if (rtw_regd_is_wiphy_self_managed(wiphy))
752 			rtw_update_wiphy_regd(wiphy, chplan, rtnl_lock_needed);
753 		else
754 		#endif
755 			rtw_regd_overide_flags(wiphy, chplan, rtnl_lock_needed);
756 	}
757 #ifdef CONFIG_REGD_SRC_FROM_OS
758 	else if (chplan->regd_src == REGD_SRC_OS)
759 		rtw_regd_apply_dfs_flags(chplan, rtnl_lock_needed);
760 #endif
761 	else
762 		rtw_warn_on(1);
763 
764 	rtw_vmfree(chplan, sizeof(struct get_chplan_resp) + sizeof(RT_CHANNEL_INFO) * chplan->chset_num);
765 }
766 
rtw_regd_change_complete_async(struct wiphy * wiphy,struct get_chplan_resp * chplan)767 int rtw_regd_change_complete_async(struct wiphy *wiphy, struct get_chplan_resp *chplan)
768 {
769 	struct rtw_wiphy_data *wiphy_data = rtw_wiphy_priv(wiphy);
770 	struct async_regd_change_evt *evt;
771 	_irqL irqL;
772 
773 	evt = rtw_malloc(sizeof(*evt));
774 	if (!evt) {
775 		rtw_vmfree(chplan, sizeof(struct get_chplan_resp) + sizeof(RT_CHANNEL_INFO) * chplan->chset_num);
776 		return _FAIL;
777 	}
778 
779 	_rtw_init_listhead(&evt->list);
780 	evt->wiphy = wiphy;
781 	evt->chplan = chplan;
782 
783 	_enter_critical_mutex(&wiphy_data->async_regd_change_mutex, &irqL);
784 
785 	rtw_list_insert_tail(&evt->list, &wiphy_data->async_regd_change_list);
786 
787 	_exit_critical_mutex(&wiphy_data->async_regd_change_mutex, &irqL);
788 
789 	_set_workitem(&wiphy_data->async_regd_change_work);
790 
791 	return _SUCCESS;
792 }
793 #endif /* CONFIG_IOCTL_CFG80211 */
794