1 /******************************************************************************
2 *
3 * Copyright(c) 2020 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 #include "phl_headers.h"
16 #include "phl_chnlplan.h"
17 #include "phl_chnlplan_6g.h"
18 #include "phl_country.h"
19
20 extern const struct chdef_6ghz chdef6g[MAX_CHDEF_6GHZ];
21 extern const struct regulatory_domain_mapping_6g rdmap6[MAX_RD_MAP_NUM_6GHZ];
22
_get_6ghz_ch_info(const struct chdef_6ghz * chdef,u8 group,u32 * ch,u32 * passive,u8 * max_num,u8 * ch_start)23 static void _get_6ghz_ch_info(const struct chdef_6ghz *chdef,
24 u8 group, u32 *ch, u32 *passive, u8 *max_num, u8 *ch_start)
25 {
26 switch (group) {
27 case FREQ_GROUP_6GHZ_UNII5:
28 *ch = ((chdef->support_ch_u5[2] << 16) |
29 (chdef->support_ch_u5[1] << 8) |
30 (chdef->support_ch_u5[0]));
31 *passive = ((chdef->passive_u5[2] << 16) |
32 (chdef->passive_u5[1] << 8) |
33 (chdef->passive_u5[0]));
34 *max_num = MAX_CH_NUM_UNII5;
35 *ch_start = 1;
36 break;
37 case FREQ_GROUP_6GHZ_UNII6:
38 *ch = chdef->support_ch_u6;
39 *passive = chdef->passive_u6;
40 *max_num = MAX_CH_NUM_UNII6;
41 *ch_start = 97;
42 break;
43 case FREQ_GROUP_6GHZ_UNII7:
44 *ch = ((chdef->support_ch_u7[2] << 16) |
45 (chdef->support_ch_u7[1] << 8) |
46 (chdef->support_ch_u7[0]));
47 *passive = ((chdef->passive_u7[2] << 16) |
48 (chdef->passive_u7[1] << 8) |
49 (chdef->passive_u7[0]));
50 *max_num = MAX_CH_NUM_UNII7;
51 *ch_start = 121;
52 break;
53 case FREQ_GROUP_6GHZ_UNII8:
54 *ch = ((chdef->support_ch_u8[1] << 8) |
55 (chdef->support_ch_u8[0]));
56 *passive = ((chdef->passive_u8[1] << 8) |
57 (chdef->passive_u8[0]));
58 *max_num = MAX_CH_NUM_UNII8;
59 *ch_start = 193;
60 break;
61 default:
62 *ch = 0;
63 *passive = 0;
64 *max_num = 0;
65 *ch_start = 0;
66 break;
67 }
68 }
69
_convert_ch6g(u8 unii_6g,struct rtw_regulation * rg,u32 * ch_cnt,struct rtw_regulation_channel * rch,u32 ch,u32 passive,u8 max_num,u8 ch_start)70 static void _convert_ch6g(u8 unii_6g, struct rtw_regulation *rg,
71 u32 *ch_cnt, struct rtw_regulation_channel *rch,
72 u32 ch, u32 passive, u8 max_num, u8 ch_start)
73 {
74 u16 i = 0;
75 u32 shift = 0;
76 u8 property = 0;
77 u32 cnt = 0;
78
79 PHL_INFO("[REGU], convert 6 ghz unii-%d channels, from %d, ch=0x%x, passive = 0x%x \n",
80 unii_6g, ch_start, ch, passive);
81
82 for (i = 0; i < max_num; i++) {
83 shift = (1 << i);
84 if (ch & shift) {
85 property = 0;
86 rch[*ch_cnt].band = BAND_ON_6G;
87 rch[*ch_cnt].channel = (u8)(ch_start + (i * 4));
88
89 if (passive & shift)
90 property |= CH_PASSIVE;
91 if ((rch[*ch_cnt].channel % 16) == 5)
92 property |= CH_PSC;
93
94 rch[*ch_cnt].property = property;
95 PHL_INFO("[REGU], ch: %d%s%s \n",
96 rch[*ch_cnt].channel,
97 ((property & CH_PASSIVE) ? ", passive" : ""),
98 ((property & CH_PSC) ? ", psc" : ""));
99 (*ch_cnt)++;
100 cnt++;
101 }
102 }
103
104 PHL_INFO("[REGU], converted channels : %d\n", cnt);
105 }
106
_update_psc_group(struct rtw_regulation * rg)107 static void _update_psc_group(struct rtw_regulation *rg)
108 {
109 u8 group = FREQ_GROUP_6GHZ_UNII5;
110 struct rtw_regulation_chplan_group *plan =
111 &rg->chplan[FREQ_GROUP_6GHZ_PSC];
112 struct rtw_regulation_chplan_group *src = NULL;
113 u32 i = 0, j = 0;
114
115 plan->cnt = 0;
116 for (i = 0; i < 4; i++) {
117 group = (u8)(i + FREQ_GROUP_6GHZ_UNII5);
118 src = &rg->chplan[group];
119
120 for (j = 0; j < src->cnt; j++) {
121 if (src->ch[j].property & CH_PSC) {
122 plan->ch[plan->cnt].band =
123 src->ch[j].band;
124 plan->ch[plan->cnt].channel =
125 src->ch[j].channel;
126 plan->ch[plan->cnt].property =
127 src->ch[j].property;
128 plan->cnt++;
129 }
130 }
131 }
132 }
133
_chnlplan_update_6g(struct rtw_regulation * rg,u8 regulation,u8 ch_idx)134 static bool _chnlplan_update_6g(struct rtw_regulation *rg,
135 u8 regulation, u8 ch_idx)
136 {
137 const struct chdef_6ghz *chdef = NULL;
138 struct rtw_regulation_chplan_group *plan = NULL;
139 u8 group = FREQ_GROUP_6GHZ_UNII5;
140 u8 max_num = 0, ch_start = 0;
141 u16 i = 0;
142 u32 ch = 0, passive = 0;
143 u32 total = 0;
144
145 if (regulation >= REGULATION_MAX)
146 return false;
147
148 for (i = 0; i < MAX_CHDEF_6GHZ; i++) {
149 if (ch_idx == chdef6g[i].idx) {
150 chdef = &chdef6g[i];
151 break;
152 }
153 }
154
155 if (!chdef)
156 return false;
157
158 rg->ch_idx6g = ch_idx;
159 rg->regulation_6g = regulation;
160
161 for (i = 0; i < 4; i++) {
162 group = (u8)(i + FREQ_GROUP_6GHZ_UNII5);
163 plan = &rg->chplan[group];
164 plan->cnt = 0;
165 _get_6ghz_ch_info(chdef, group,
166 &ch, &passive, &max_num, &ch_start);
167 _convert_ch6g((u8)(i + 5), rg, &plan->cnt, plan->ch,
168 ch, passive, max_num, ch_start);
169 total += plan->cnt;
170 }
171
172 _update_psc_group(rg);
173
174 PHL_INFO("[REGU], 6 GHz, total channel = %d\n", total);
175
176 return true;
177 }
178
_domain_index_6g(u16 domain)179 static u8 _domain_index_6g(u16 domain)
180 {
181 u8 i = 0;
182
183 for (i = 0; i < MAX_RD_MAP_NUM_6GHZ; i++) {
184 if (domain == rdmap6[i].domain_code) {
185 return i;
186 }
187 }
188
189 return MAX_RD_MAP_NUM_6GHZ;
190 }
191
_regulatory_domain_update_6g(struct rtw_regulation * rg,u16 domain,enum regulation_rsn reason)192 static bool _regulatory_domain_update_6g(struct rtw_regulation *rg,
193 u16 domain, enum regulation_rsn reason)
194 {
195 u8 regulation = REGULATION_NA;
196 u8 ch_idx = 0, did = 0;
197
198 rg->domain_6g.reason = reason;
199 if (domain == RSVD_DOMAIN) {
200 rg->domain_6g.code = RSVD_DOMAIN;
201 return true;
202 } else {
203 /* Note: only valid domain index can reach here */
204 did = _domain_index_6g(domain);
205 rg->domain_6g.code = rdmap6[did].domain_code;
206 regulation = rdmap6[did].regulation;
207 ch_idx = rdmap6[did].ch_idx;
208 return _chnlplan_update_6g(rg, regulation, ch_idx);
209 }
210 }
211
_get_group_chplan_6g(struct rtw_regulation * rg,struct rtw_regulation_chplan_group * group,struct rtw_regulation_chplan * plan)212 static void _get_group_chplan_6g(struct rtw_regulation *rg,
213 struct rtw_regulation_chplan_group *group,
214 struct rtw_regulation_chplan *plan)
215 {
216 u32 i = 0;
217
218 for (i = 0; i < group->cnt; i++) {
219 if (group->ch[i].channel) {
220 plan->ch[plan->cnt].band =
221 group->ch[i].band;
222 plan->ch[plan->cnt].channel =
223 group->ch[i].channel;
224 plan->ch[plan->cnt].property =
225 group->ch[i].property;
226 plan->cnt++;
227 }
228 }
229 }
230
_history_log_6g(struct rtw_regulation * rg,u16 domain,u8 reason)231 static void _history_log_6g(struct rtw_regulation *rg, u16 domain, u8 reason)
232 {
233 rg->history_6g[rg->history_cnt_6g].code = domain;
234 rg->history_6g[rg->history_cnt_6g].reason = reason;
235 rg->history_cnt_6g++;
236 if (rg->history_cnt_6g >= MAX_HISTORY_NUM)
237 rg->history_cnt_6g = 0;
238 }
239
regu_get_chnlplan_6g(struct rtw_regulation * rg,enum rtw_regulation_query type,struct rtw_regulation_chplan * plan)240 void regu_get_chnlplan_6g(struct rtw_regulation *rg,
241 enum rtw_regulation_query type,
242 struct rtw_regulation_chplan *plan)
243 {
244 struct rtw_regulation_chplan_group *group = NULL;
245
246 /* 6ghz */
247 if (rg->capability & CAPABILITY_6GHZ) {
248 /* unii5 */
249 if (type == REGULQ_CHPLAN_6GHZ ||
250 type == REGULQ_CHPLAN_6GHZ_UNII5) {
251 group = &rg->chplan[FREQ_GROUP_6GHZ_UNII5];
252 _get_group_chplan_6g(rg, group, plan);
253 }
254 /* unii6 */
255 if (type == REGULQ_CHPLAN_6GHZ ||
256 type == REGULQ_CHPLAN_6GHZ_UNII6) {
257 group = &rg->chplan[FREQ_GROUP_6GHZ_UNII6];
258 _get_group_chplan_6g(rg, group, plan);
259 }
260 /* unii7 */
261 if (type == REGULQ_CHPLAN_6GHZ ||
262 type == REGULQ_CHPLAN_6GHZ_UNII7) {
263 group = &rg->chplan[FREQ_GROUP_6GHZ_UNII7];
264 _get_group_chplan_6g(rg, group, plan);
265 }
266 /* unii8 */
267 if (type == REGULQ_CHPLAN_6GHZ ||
268 type == REGULQ_CHPLAN_6GHZ_UNII8) {
269 group = &rg->chplan[FREQ_GROUP_6GHZ_UNII8];
270 _get_group_chplan_6g(rg, group, plan);
271 }
272 /* psc */
273 if (type == REGULQ_CHPLAN_FULL ||
274 type == REGULQ_CHPLAN_6GHZ_PSC) {
275 group = &rg->chplan[FREQ_GROUP_6GHZ_PSC];
276 _get_group_chplan_6g(rg, group, plan);
277 }
278 }
279 }
280
regu_valid_domain_6g(u16 domain)281 bool regu_valid_domain_6g(u16 domain)
282 {
283 if (domain == RSVD_DOMAIN)
284 return true;
285
286 if (_domain_index_6g(domain) >= MAX_RD_MAP_NUM_6GHZ)
287 return false;
288
289 return true;
290 }
291
regu_set_domain_6g(void * phl,u16 domain,enum regulation_rsn reason)292 bool regu_set_domain_6g(void *phl, u16 domain,
293 enum regulation_rsn reason)
294 {
295 struct phl_info_t *phl_info = (struct phl_info_t *)phl;
296 struct rtw_regulation *rg = NULL;
297 void *d = NULL;
298
299 PHL_INFO("[REGU], set 6 ghz domain code = 0x%x, reason = 0x%x\n",
300 domain, reason);
301
302 if (!phl_info)
303 return false;
304
305 rg = &phl_info->regulation;
306 if (!rg->init)
307 return false;
308
309 if (!regu_valid_domain_6g(domain))
310 return false;
311
312 d = phl_to_drvpriv(phl_info);
313
314 _os_spinlock(d, &rg->lock, _bh, NULL);
315
316 _history_log_6g(rg, domain, reason);
317
318 if (_regulatory_domain_update_6g(rg, domain, reason))
319 rg->valid_6g = true;
320 else {
321 rg->valid_6g = false;
322 rg->invalid_cnt_6g++;
323 }
324 _os_spinunlock(d, &rg->lock, _bh, NULL);
325
326 PHL_INFO("[REGU], 6 ghz domain code valid = %d\n", rg->valid_6g);
327
328 return rg->valid_6g;
329 }
330
331
332