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_scan_instance.h"
17 #include "phl_scan.h"
18 #include "phl_regulation.h"
19
20 enum {
21 ACTIVE_PERIOD_MIN = 40,
22 ACTIVE_PERIOD_DEFAULT = 60,
23 ACTIVE_PERIOD_MAX = 80
24 };
25
26 enum {
27 PASSIVE_PERIOD_MIN = 50,
28 PASSIVE_PERIOD_DEFAULT = 80,
29 PASSIVE_PERIOD_MAX = 110
30 };
31
32 struct rtw_pickup_channel {
33 enum band_type band;
34 u8 channel;
35 u8 property;
36 u8 picked;
37 };
38
39 struct rtw_pickup_chplan_group {
40 u32 cnt;
41 struct rtw_pickup_channel ch[MAX_CH_NUM_GROUP];
42 };
43
_set_inst_ch(enum period_strategy strategy,struct instance_channel * dest,enum band_type band,u8 channel,u8 property)44 static void _set_inst_ch(enum period_strategy strategy,
45 struct instance_channel *dest,
46 enum band_type band,
47 u8 channel, u8 property)
48 {
49 u8 max_t[2] = {PASSIVE_PERIOD_MAX, ACTIVE_PERIOD_MAX};
50 u8 min_t[2] = {PASSIVE_PERIOD_MIN, ACTIVE_PERIOD_MIN};
51 u8 def_t[2] = {PASSIVE_PERIOD_DEFAULT, ACTIVE_PERIOD_DEFAULT};
52
53 dest->band = band;
54 dest->channel = channel;
55 dest->property = property;
56 dest->mode = NORMAL_SCAN_MODE;
57 dest->bw = CHANNEL_WIDTH_20;
58 dest->offset = CHAN_OFFSET_NO_EXT;
59
60 /* active or passive */
61 if ((dest->property & CH_PASSIVE) ||
62 (dest->property & CH_DFS))
63 dest->active = 0;
64 else
65 dest->active = 1;
66
67 if (strategy & PERIOD_ALL_MAX)
68 dest->period = max_t[dest->active];
69 else if (strategy & PERIOD_ALL_MIN)
70 dest->period = min_t[dest->active];
71 else {
72 if ((strategy & PERIOD_MIN_DFS) &&
73 (dest->property & CH_DFS))
74 dest->period = min_t[dest->active];
75 else
76 dest->period = def_t[dest->active];
77 }
78
79 PHL_INFO("[REGU], pick channel %d, active=%d, period=%d\n",
80 dest->channel, dest->active, dest->period);
81 }
82
_pick_active_channels(struct rtw_pickup_chplan_group * group,struct instance_strategy * strategy,struct instance * inst)83 static void _pick_active_channels(struct rtw_pickup_chplan_group *group,
84 struct instance_strategy *strategy,
85 struct instance *inst)
86 {
87 struct instance_channel *dest = NULL;
88 struct rtw_pickup_channel *src = NULL;
89 u32 i = 0;
90
91 dest = &inst->ch[inst->cnt];
92
93 for (i = 0; i < group->cnt; i++) {
94 src = &group->ch[i];
95
96 if (src->picked)
97 continue;
98
99 if (!(src->property & CH_PASSIVE) &&
100 !(src->property & CH_DFS) ) {
101 _set_inst_ch(strategy->period, dest,
102 src->band,
103 src->channel,
104 src->property);
105 inst->cnt++;
106 dest++;
107 src->picked = 1;
108 }
109 }
110 }
111
_pick_the_rest_channels(struct rtw_pickup_chplan_group * group,struct instance_strategy * strategy,struct instance * inst)112 static void _pick_the_rest_channels(struct rtw_pickup_chplan_group *group,
113 struct instance_strategy *strategy,
114 struct instance *inst)
115 {
116 struct instance_channel *dest = NULL;
117 struct rtw_pickup_channel *src = NULL;
118 u32 i = 0;
119
120 dest = &inst->ch[inst->cnt];
121
122 for (i = 0; i < group->cnt; i++) {
123 src = &group->ch[i];
124
125 if (!src->picked) {
126 _set_inst_ch(strategy->period, dest,
127 src->band,
128 src->channel,
129 src->property);
130 inst->cnt++;
131 dest++;
132 src->picked = 1;
133 }
134 }
135 }
136
_pick_5ghz_channels(struct rtw_pickup_chplan_group * group,struct instance_strategy * strategy,struct instance * inst)137 static void _pick_5ghz_channels(struct rtw_pickup_chplan_group *group,
138 struct instance_strategy *strategy,
139 struct instance *inst)
140 {
141 u8 order = strategy->order;
142 u8 i = 0;
143
144 if (order & ORDER_ACTIVE_PRIOR) {
145 for (i = FREQ_GROUP_5GHZ_BAND1;
146 i <= FREQ_GROUP_5GHZ_BAND4; i++) {
147 _pick_active_channels(&group[i], strategy, inst);
148 }
149 }
150
151 for (i = FREQ_GROUP_5GHZ_BAND1;
152 i <= FREQ_GROUP_5GHZ_BAND4; i++) {
153 _pick_the_rest_channels(&group[i], strategy, inst);
154 }
155 }
156
_pick_2ghz_channels(struct rtw_pickup_chplan_group * group,struct instance_strategy * strategy,struct instance * inst)157 static void _pick_2ghz_channels(struct rtw_pickup_chplan_group *group,
158 struct instance_strategy *strategy,
159 struct instance *inst)
160 {
161 u8 order = strategy->order;
162
163 if (order & ORDER_ACTIVE_PRIOR)
164 _pick_active_channels(&group[FREQ_GROUP_2GHZ],
165 strategy, inst);
166
167 _pick_the_rest_channels(&group[FREQ_GROUP_2GHZ],
168 strategy, inst);
169 }
170
_pick_6ghz_channels(struct rtw_pickup_chplan_group * group,struct instance_strategy * strategy,struct instance * inst)171 static void _pick_6ghz_channels(struct rtw_pickup_chplan_group *group,
172 struct instance_strategy *strategy,
173 struct instance *inst)
174 {
175 _pick_the_rest_channels(&group[FREQ_GROUP_6GHZ_PSC],
176 strategy, inst);
177 }
178
_generate_instance(struct rtw_pickup_chplan_group * group,struct instance_strategy * strategy,struct instance * inst)179 static void _generate_instance(
180 struct rtw_pickup_chplan_group *group,
181 struct instance_strategy *strategy,
182 struct instance *inst)
183 {
184 u8 order = strategy->order;
185
186 inst->cnt = 0;
187 if (order & ORDER_5GHZ_PRIOR) {
188 _pick_5ghz_channels(group, strategy, inst);
189 _pick_2ghz_channels(group, strategy, inst);
190 } else {
191 _pick_2ghz_channels(group, strategy, inst);
192 _pick_5ghz_channels(group, strategy, inst);
193 }
194
195 _pick_6ghz_channels(group, strategy, inst);
196 }
197
_select_channels_by_group(struct instance_strategy * strategy,struct rtw_regulation_chplan * plan,struct rtw_pickup_chplan_group * group)198 static void _select_channels_by_group(struct instance_strategy *strategy,
199 struct rtw_regulation_chplan *plan,
200 struct rtw_pickup_chplan_group *group)
201 {
202 u8 skip = strategy->skip;
203 u8 chnl = 0, property = 0, gpidx = 0, keep = 0;
204 enum band_type band = BAND_ON_24G;
205 u32 i = 0;
206
207 for (i = 0; i < plan->cnt; i++) {
208 band = plan->ch[i].band;
209 chnl = plan->ch[i].channel;
210 property = plan->ch[i].property;
211 keep = 0;
212
213 /* skip passive channels */
214 if ((skip & SKIP_PASSIVE) && (property & CH_PASSIVE))
215 continue;
216
217 /* skip DFS channels */
218 if ((skip & SKIP_DFS) && (property & CH_DFS))
219 continue;
220
221 if ((BAND_2GHZ(band)) && !(skip & SKIP_2GHZ)) {
222 gpidx = FREQ_GROUP_2GHZ;
223 keep = 1;
224 } else if ((BAND_5GHZ(band)) && !(skip & SKIP_5GHZ)) {
225 if (CH_5GHZ_BAND1(chnl))
226 gpidx = FREQ_GROUP_5GHZ_BAND1;
227 else if (CH_5GHZ_BAND2(chnl))
228 gpidx = FREQ_GROUP_5GHZ_BAND2;
229 else if (CH_5GHZ_BAND3(chnl))
230 gpidx = FREQ_GROUP_5GHZ_BAND3;
231 else if (CH_5GHZ_BAND4(chnl))
232 gpidx = FREQ_GROUP_5GHZ_BAND4;
233 else
234 continue;
235 keep = 1;
236 } else if ((BAND_6GHZ(band)) && !(skip & SKIP_6GHZ)) {
237 gpidx = FREQ_GROUP_6GHZ_PSC;
238 keep = 1;
239 }
240
241 if (keep) {
242 group[gpidx].ch[group[gpidx].cnt].band = band;
243 group[gpidx].ch[group[gpidx].cnt].channel = chnl;
244 group[gpidx].ch[group[gpidx].cnt].property = property;
245 group[gpidx].ch[group[gpidx].cnt].picked = 0;
246 group[gpidx].cnt++;
247 PHL_INFO("[REGU], keep group-%d channel %d, cnt=%d\n",
248 gpidx, chnl, group[gpidx].cnt);
249 }
250 }
251 }
252
rtw_phl_generate_scan_instance(struct instance_strategy * strategy,struct rtw_regulation_chplan * chplan,struct instance * inst)253 bool rtw_phl_generate_scan_instance(struct instance_strategy *strategy,
254 struct rtw_regulation_chplan *chplan,
255 struct instance *inst)
256 {
257 struct rtw_pickup_chplan_group group[FREQ_GROUP_MAX] = {0};
258 u32 i = 0;
259
260 if (!strategy || !inst || !chplan)
261 return false;
262
263 PHL_INFO("[REGU], Generate Scan Instance, strategy [skip=0x%x, order=0x%x, period=0x%x] \n",
264 strategy->skip, strategy->order, strategy->period);
265 PHL_INFO("[REGU], Channel Plan Source : \n");
266
267 for (i = 0; i < chplan->cnt; i++) {
268 PHL_INFO("[REGU], %d. band=%d, ch=%d, dfs=%d, passive=%d (property=0x%x)\n",
269 i + 1, chplan->ch[i].band, chplan->ch[i].channel,
270 ((chplan->ch[i].property & CH_DFS) ? 1 : 0),
271 ((chplan->ch[i].property & CH_PASSIVE) ? 1 : 0),
272 chplan->ch[i].property);
273 }
274
275 /* step1 : remove "skip channels" and select channels into groups */
276 _select_channels_by_group(strategy, chplan, group);
277
278
279 /* step2 : generate instance by strategy */
280 _generate_instance(group, strategy, inst);
281
282 PHL_INFO("[REGU], Output Scan Instance : \n");
283 for (i = 0; i < inst->cnt; i++) {
284 PHL_INFO("[REGU], %d. band=%d, ch=%d, active=%d, period=%d \n",
285 i + 1, inst->ch[i].band,
286 inst->ch[i].channel,
287 inst->ch[i].active,
288 inst->ch[i].period);
289 }
290
291 return true;
292 }
293
rtw_phl_scan_instance_insert_ch(void * phl,struct instance * inst,enum band_type band,u8 channel,u8 strategy_period)294 bool rtw_phl_scan_instance_insert_ch(void *phl, struct instance *inst,
295 enum band_type band, u8 channel,
296 u8 strategy_period)
297 {
298 struct rtw_regulation_channel ch = {0};
299 struct instance_channel *dest = NULL;
300
301 if (!phl || !inst)
302 return false;
303
304 if (inst->cnt >= MAX_SCAN_INSTANCE)
305 return false;
306
307 if (rtw_phl_regulation_query_ch(phl, band, channel, &ch)) {
308 dest = &inst->ch[inst->cnt];
309 _set_inst_ch(strategy_period, dest,
310 ch.band, ch.channel, ch.property);
311 inst->cnt++;
312 return true;
313 }
314
315 return false;
316 }
317
318
319
320