1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /****************************************************************************** |
3 | * |
4 | * Copyright(c) 2007 - 2012 Realtek Corporation. All rights reserved. |
5 | * |
6 | ******************************************************************************/ |
7 | |
8 | #include <drv_types.h> |
9 | #include <rtw_debug.h> |
10 | |
11 | u8 rtw_validate_bssid(u8 *bssid) |
12 | { |
13 | u8 ret = true; |
14 | |
15 | if (is_zero_mac_addr(addr: bssid) |
16 | || is_broadcast_mac_addr(addr: bssid) |
17 | || is_multicast_mac_addr(addr: bssid) |
18 | ) { |
19 | ret = false; |
20 | } |
21 | |
22 | return ret; |
23 | } |
24 | |
25 | u8 rtw_validate_ssid(struct ndis_802_11_ssid *ssid) |
26 | { |
27 | u8 ret = true; |
28 | |
29 | if (ssid->ssid_length > 32) { |
30 | ret = false; |
31 | goto exit; |
32 | } |
33 | |
34 | exit: |
35 | return ret; |
36 | } |
37 | |
38 | u8 rtw_do_join(struct adapter *padapter) |
39 | { |
40 | struct list_head *plist, *phead; |
41 | u8 *pibss = NULL; |
42 | struct mlme_priv *pmlmepriv = &(padapter->mlmepriv); |
43 | struct __queue *queue = &(pmlmepriv->scanned_queue); |
44 | u8 ret = _SUCCESS; |
45 | |
46 | spin_lock_bh(lock: &(pmlmepriv->scanned_queue.lock)); |
47 | phead = get_list_head(queue); |
48 | plist = get_next(list: phead); |
49 | |
50 | pmlmepriv->cur_network.join_res = -2; |
51 | |
52 | set_fwstate(pmlmepriv, _FW_UNDER_LINKING); |
53 | |
54 | pmlmepriv->pscanned = plist; |
55 | |
56 | pmlmepriv->to_join = true; |
57 | |
58 | if (list_empty(head: &queue->queue)) { |
59 | spin_unlock_bh(lock: &(pmlmepriv->scanned_queue.lock)); |
60 | _clr_fwstate_(pmlmepriv, _FW_UNDER_LINKING); |
61 | |
62 | /* when set_ssid/set_bssid for rtw_do_join(), but scanning queue is empty */ |
63 | /* we try to issue sitesurvey firstly */ |
64 | |
65 | if (pmlmepriv->LinkDetectInfo.bBusyTraffic == false |
66 | || rtw_to_roam(adapter: padapter) > 0 |
67 | ) { |
68 | /* submit site_survey_cmd */ |
69 | ret = rtw_sitesurvey_cmd(padapter, ssid: &pmlmepriv->assoc_ssid, ssid_num: 1, NULL, ch_num: 0); |
70 | if (ret != _SUCCESS) |
71 | pmlmepriv->to_join = false; |
72 | |
73 | } else { |
74 | pmlmepriv->to_join = false; |
75 | ret = _FAIL; |
76 | } |
77 | |
78 | goto exit; |
79 | } else { |
80 | int select_ret; |
81 | |
82 | spin_unlock_bh(lock: &(pmlmepriv->scanned_queue.lock)); |
83 | select_ret = rtw_select_and_join_from_scanned_queue(pmlmepriv); |
84 | if (select_ret == _SUCCESS) { |
85 | pmlmepriv->to_join = false; |
86 | _set_timer(ptimer: &pmlmepriv->assoc_timer, MAX_JOIN_TIMEOUT); |
87 | } else { |
88 | if (check_fwstate(pmlmepriv, WIFI_ADHOC_STATE) == true) { |
89 | /* submit createbss_cmd to change to a ADHOC_MASTER */ |
90 | |
91 | /* pmlmepriv->lock has been acquired by caller... */ |
92 | struct wlan_bssid_ex *pdev_network = &(padapter->registrypriv.dev_network); |
93 | |
94 | pmlmepriv->fw_state = WIFI_ADHOC_MASTER_STATE; |
95 | |
96 | pibss = padapter->registrypriv.dev_network.mac_address; |
97 | |
98 | memcpy(&pdev_network->ssid, &pmlmepriv->assoc_ssid, sizeof(struct ndis_802_11_ssid)); |
99 | |
100 | rtw_update_registrypriv_dev_network(adapter: padapter); |
101 | |
102 | rtw_generate_random_ibss(pibss); |
103 | |
104 | if (rtw_createbss_cmd(padapter) != _SUCCESS) { |
105 | ret = false; |
106 | goto exit; |
107 | } |
108 | |
109 | pmlmepriv->to_join = false; |
110 | |
111 | } else { |
112 | /* can't associate ; reset under-linking */ |
113 | _clr_fwstate_(pmlmepriv, _FW_UNDER_LINKING); |
114 | |
115 | /* when set_ssid/set_bssid for rtw_do_join(), but there are no desired bss in scanning queue */ |
116 | /* we try to issue sitesurvey firstly */ |
117 | if (pmlmepriv->LinkDetectInfo.bBusyTraffic == false |
118 | || rtw_to_roam(adapter: padapter) > 0 |
119 | ) { |
120 | ret = rtw_sitesurvey_cmd(padapter, ssid: &pmlmepriv->assoc_ssid, ssid_num: 1, NULL, ch_num: 0); |
121 | if (ret != _SUCCESS) |
122 | pmlmepriv->to_join = false; |
123 | |
124 | } else { |
125 | ret = _FAIL; |
126 | pmlmepriv->to_join = false; |
127 | } |
128 | } |
129 | |
130 | } |
131 | |
132 | } |
133 | |
134 | exit: |
135 | return ret; |
136 | } |
137 | |
138 | u8 rtw_set_802_11_ssid(struct adapter *padapter, struct ndis_802_11_ssid *ssid) |
139 | { |
140 | u8 status = _SUCCESS; |
141 | |
142 | struct mlme_priv *pmlmepriv = &padapter->mlmepriv; |
143 | struct wlan_network *pnetwork = &pmlmepriv->cur_network; |
144 | |
145 | netdev_dbg(padapter->pnetdev, "set ssid [%s] fw_state = 0x%08x\n" , |
146 | ssid->ssid, get_fwstate(pmlmepriv)); |
147 | |
148 | if (padapter->hw_init_completed == false) { |
149 | status = _FAIL; |
150 | goto exit; |
151 | } |
152 | |
153 | spin_lock_bh(lock: &pmlmepriv->lock); |
154 | |
155 | if (check_fwstate(pmlmepriv, _FW_UNDER_SURVEY) == true) |
156 | goto handle_tkip_countermeasure; |
157 | else if (check_fwstate(pmlmepriv, _FW_UNDER_LINKING) == true) |
158 | goto release_mlme_lock; |
159 | |
160 | if (check_fwstate(pmlmepriv, _FW_LINKED|WIFI_ADHOC_MASTER_STATE) == true) { |
161 | if ((pmlmepriv->assoc_ssid.ssid_length == ssid->ssid_length) && |
162 | (!memcmp(p: &pmlmepriv->assoc_ssid.ssid, q: ssid->ssid, size: ssid->ssid_length))) { |
163 | if (check_fwstate(pmlmepriv, WIFI_STATION_STATE) == false) { |
164 | if (rtw_is_same_ibss(adapter: padapter, pnetwork) == false) { |
165 | /* if in WIFI_ADHOC_MASTER_STATE | WIFI_ADHOC_STATE, create bss or rejoin again */ |
166 | rtw_disassoc_cmd(padapter, deauth_timeout_ms: 0, enqueue: true); |
167 | |
168 | if (check_fwstate(pmlmepriv, _FW_LINKED) == true) |
169 | rtw_indicate_disconnect(adapter: padapter); |
170 | |
171 | rtw_free_assoc_resources(adapter: padapter, lock_scanned_queue: 1); |
172 | |
173 | if (check_fwstate(pmlmepriv, WIFI_ADHOC_MASTER_STATE) == true) { |
174 | _clr_fwstate_(pmlmepriv, WIFI_ADHOC_MASTER_STATE); |
175 | set_fwstate(pmlmepriv, WIFI_ADHOC_STATE); |
176 | } |
177 | } else { |
178 | goto release_mlme_lock;/* it means driver is in WIFI_ADHOC_MASTER_STATE, we needn't create bss again. */ |
179 | } |
180 | } else { |
181 | rtw_lps_ctrl_wk_cmd(padapter, lps_ctrl_type: LPS_CTRL_JOINBSS, enqueue: 1); |
182 | } |
183 | } else { |
184 | rtw_disassoc_cmd(padapter, deauth_timeout_ms: 0, enqueue: true); |
185 | |
186 | if (check_fwstate(pmlmepriv, _FW_LINKED) == true) |
187 | rtw_indicate_disconnect(adapter: padapter); |
188 | |
189 | rtw_free_assoc_resources(adapter: padapter, lock_scanned_queue: 1); |
190 | |
191 | if (check_fwstate(pmlmepriv, WIFI_ADHOC_MASTER_STATE) == true) { |
192 | _clr_fwstate_(pmlmepriv, WIFI_ADHOC_MASTER_STATE); |
193 | set_fwstate(pmlmepriv, WIFI_ADHOC_STATE); |
194 | } |
195 | } |
196 | } |
197 | |
198 | handle_tkip_countermeasure: |
199 | if (rtw_handle_tkip_countermeasure(adapter: padapter, caller: __func__) == _FAIL) { |
200 | status = _FAIL; |
201 | goto release_mlme_lock; |
202 | } |
203 | |
204 | if (rtw_validate_ssid(ssid) == false) { |
205 | status = _FAIL; |
206 | goto release_mlme_lock; |
207 | } |
208 | |
209 | memcpy(&pmlmepriv->assoc_ssid, ssid, sizeof(struct ndis_802_11_ssid)); |
210 | pmlmepriv->assoc_by_bssid = false; |
211 | |
212 | if (check_fwstate(pmlmepriv, _FW_UNDER_SURVEY) == true) |
213 | pmlmepriv->to_join = true; |
214 | else |
215 | status = rtw_do_join(padapter); |
216 | |
217 | release_mlme_lock: |
218 | spin_unlock_bh(lock: &pmlmepriv->lock); |
219 | |
220 | exit: |
221 | |
222 | return status; |
223 | } |
224 | |
225 | u8 rtw_set_802_11_connect(struct adapter *padapter, u8 *bssid, struct ndis_802_11_ssid *ssid) |
226 | { |
227 | u8 status = _SUCCESS; |
228 | bool bssid_valid = true; |
229 | bool ssid_valid = true; |
230 | struct mlme_priv *pmlmepriv = &padapter->mlmepriv; |
231 | |
232 | if (!ssid || rtw_validate_ssid(ssid) == false) |
233 | ssid_valid = false; |
234 | |
235 | if (!bssid || rtw_validate_bssid(bssid) == false) |
236 | bssid_valid = false; |
237 | |
238 | if (!ssid_valid && !bssid_valid) { |
239 | status = _FAIL; |
240 | goto exit; |
241 | } |
242 | |
243 | if (padapter->hw_init_completed == false) { |
244 | status = _FAIL; |
245 | goto exit; |
246 | } |
247 | |
248 | spin_lock_bh(lock: &pmlmepriv->lock); |
249 | |
250 | netdev_dbg(padapter->pnetdev, FUNC_ADPT_FMT " fw_state = 0x%08x\n" , |
251 | FUNC_ADPT_ARG(padapter), get_fwstate(pmlmepriv)); |
252 | |
253 | if (check_fwstate(pmlmepriv, _FW_UNDER_SURVEY) == true) |
254 | goto handle_tkip_countermeasure; |
255 | else if (check_fwstate(pmlmepriv, _FW_UNDER_LINKING) == true) |
256 | goto release_mlme_lock; |
257 | |
258 | handle_tkip_countermeasure: |
259 | if (rtw_handle_tkip_countermeasure(adapter: padapter, caller: __func__) == _FAIL) { |
260 | status = _FAIL; |
261 | goto release_mlme_lock; |
262 | } |
263 | |
264 | if (ssid && ssid_valid) |
265 | memcpy(&pmlmepriv->assoc_ssid, ssid, sizeof(struct ndis_802_11_ssid)); |
266 | else |
267 | memset(&pmlmepriv->assoc_ssid, 0, sizeof(struct ndis_802_11_ssid)); |
268 | |
269 | if (bssid && bssid_valid) { |
270 | memcpy(&pmlmepriv->assoc_bssid, bssid, ETH_ALEN); |
271 | pmlmepriv->assoc_by_bssid = true; |
272 | } else { |
273 | pmlmepriv->assoc_by_bssid = false; |
274 | } |
275 | |
276 | if (check_fwstate(pmlmepriv, _FW_UNDER_SURVEY) == true) |
277 | pmlmepriv->to_join = true; |
278 | else |
279 | status = rtw_do_join(padapter); |
280 | |
281 | release_mlme_lock: |
282 | spin_unlock_bh(lock: &pmlmepriv->lock); |
283 | |
284 | exit: |
285 | return status; |
286 | } |
287 | |
288 | u8 rtw_set_802_11_infrastructure_mode(struct adapter *padapter, |
289 | enum ndis_802_11_network_infrastructure networktype) |
290 | { |
291 | struct mlme_priv *pmlmepriv = &padapter->mlmepriv; |
292 | struct wlan_network *cur_network = &pmlmepriv->cur_network; |
293 | enum ndis_802_11_network_infrastructure *pold_state = &(cur_network->network.infrastructure_mode); |
294 | |
295 | if (*pold_state != networktype) { |
296 | if (*pold_state == Ndis802_11APMode) { |
297 | /* change to other mode from Ndis802_11APMode */ |
298 | cur_network->join_res = -1; |
299 | |
300 | stop_ap_mode(padapter); |
301 | } |
302 | |
303 | spin_lock_bh(lock: &pmlmepriv->lock); |
304 | |
305 | if ((check_fwstate(pmlmepriv, _FW_LINKED) == true) || (*pold_state == Ndis802_11IBSS)) |
306 | rtw_disassoc_cmd(padapter, deauth_timeout_ms: 0, enqueue: true); |
307 | |
308 | if ((check_fwstate(pmlmepriv, _FW_LINKED) == true) || |
309 | (check_fwstate(pmlmepriv, WIFI_ADHOC_MASTER_STATE) == true)) |
310 | rtw_free_assoc_resources(adapter: padapter, lock_scanned_queue: 1); |
311 | |
312 | if ((*pold_state == Ndis802_11Infrastructure) || (*pold_state == Ndis802_11IBSS)) { |
313 | if (check_fwstate(pmlmepriv, _FW_LINKED) == true) |
314 | rtw_indicate_disconnect(adapter: padapter); /* will clr Linked_state; before this function, we must have checked whether issue dis-assoc_cmd or not */ |
315 | } |
316 | |
317 | *pold_state = networktype; |
318 | |
319 | _clr_fwstate_(pmlmepriv, state: ~WIFI_NULL_STATE); |
320 | |
321 | switch (networktype) { |
322 | case Ndis802_11IBSS: |
323 | set_fwstate(pmlmepriv, WIFI_ADHOC_STATE); |
324 | break; |
325 | |
326 | case Ndis802_11Infrastructure: |
327 | set_fwstate(pmlmepriv, WIFI_STATION_STATE); |
328 | break; |
329 | |
330 | case Ndis802_11APMode: |
331 | set_fwstate(pmlmepriv, WIFI_AP_STATE); |
332 | start_ap_mode(padapter); |
333 | /* rtw_indicate_connect(padapter); */ |
334 | |
335 | break; |
336 | |
337 | case Ndis802_11AutoUnknown: |
338 | case Ndis802_11InfrastructureMax: |
339 | break; |
340 | } |
341 | |
342 | /* SecClearAllKeys(adapter); */ |
343 | |
344 | spin_unlock_bh(lock: &pmlmepriv->lock); |
345 | } |
346 | return true; |
347 | } |
348 | |
349 | |
350 | u8 rtw_set_802_11_disassociate(struct adapter *padapter) |
351 | { |
352 | struct mlme_priv *pmlmepriv = &padapter->mlmepriv; |
353 | |
354 | spin_lock_bh(lock: &pmlmepriv->lock); |
355 | |
356 | if (check_fwstate(pmlmepriv, _FW_LINKED) == true) { |
357 | rtw_disassoc_cmd(padapter, deauth_timeout_ms: 0, enqueue: true); |
358 | rtw_indicate_disconnect(adapter: padapter); |
359 | /* modify for CONFIG_IEEE80211W, none 11w can use it */ |
360 | rtw_free_assoc_resources_cmd(padapter); |
361 | rtw_pwr_wakeup(padapter); |
362 | } |
363 | |
364 | spin_unlock_bh(lock: &pmlmepriv->lock); |
365 | |
366 | return true; |
367 | } |
368 | |
369 | u8 rtw_set_802_11_bssid_list_scan(struct adapter *padapter, struct ndis_802_11_ssid *pssid, int ssid_max_num) |
370 | { |
371 | struct mlme_priv *pmlmepriv = &padapter->mlmepriv; |
372 | u8 res = true; |
373 | |
374 | if (!padapter) { |
375 | res = false; |
376 | goto exit; |
377 | } |
378 | if (padapter->hw_init_completed == false) { |
379 | res = false; |
380 | goto exit; |
381 | } |
382 | |
383 | if ((check_fwstate(pmlmepriv, _FW_UNDER_SURVEY|_FW_UNDER_LINKING) == true) || |
384 | (pmlmepriv->LinkDetectInfo.bBusyTraffic == true)) { |
385 | /* Scan or linking is in progress, do nothing. */ |
386 | res = true; |
387 | |
388 | } else { |
389 | if (rtw_is_scan_deny(adapter: padapter)) |
390 | return _SUCCESS; |
391 | |
392 | spin_lock_bh(lock: &pmlmepriv->lock); |
393 | |
394 | res = rtw_sitesurvey_cmd(padapter, ssid: pssid, ssid_num: ssid_max_num, NULL, ch_num: 0); |
395 | |
396 | spin_unlock_bh(lock: &pmlmepriv->lock); |
397 | } |
398 | exit: |
399 | |
400 | return res; |
401 | } |
402 | |
403 | u8 rtw_set_802_11_authentication_mode(struct adapter *padapter, enum ndis_802_11_authentication_mode authmode) |
404 | { |
405 | struct security_priv *psecuritypriv = &padapter->securitypriv; |
406 | int res; |
407 | u8 ret; |
408 | |
409 | psecuritypriv->ndisauthtype = authmode; |
410 | |
411 | if (psecuritypriv->ndisauthtype > 3) |
412 | psecuritypriv->dot11AuthAlgrthm = dot11AuthAlgrthm_8021X; |
413 | |
414 | res = rtw_set_auth(adapter: padapter, psecuritypriv); |
415 | |
416 | if (res == _SUCCESS) |
417 | ret = true; |
418 | else |
419 | ret = false; |
420 | |
421 | return ret; |
422 | } |
423 | |
424 | u8 rtw_set_802_11_add_wep(struct adapter *padapter, struct ndis_802_11_wep *wep) |
425 | { |
426 | |
427 | signed int keyid, res; |
428 | struct security_priv *psecuritypriv = &(padapter->securitypriv); |
429 | u8 ret = _SUCCESS; |
430 | |
431 | keyid = wep->key_index & 0x3fffffff; |
432 | |
433 | if (keyid >= 4) { |
434 | ret = false; |
435 | goto exit; |
436 | } |
437 | |
438 | switch (wep->key_length) { |
439 | case 5: |
440 | psecuritypriv->dot11PrivacyAlgrthm = _WEP40_; |
441 | break; |
442 | case 13: |
443 | psecuritypriv->dot11PrivacyAlgrthm = _WEP104_; |
444 | break; |
445 | default: |
446 | psecuritypriv->dot11PrivacyAlgrthm = _NO_PRIVACY_; |
447 | break; |
448 | } |
449 | |
450 | memcpy(&(psecuritypriv->dot11DefKey[keyid].skey[0]), &(wep->key_material), wep->key_length); |
451 | |
452 | psecuritypriv->dot11DefKeylen[keyid] = wep->key_length; |
453 | |
454 | psecuritypriv->dot11PrivacyKeyIndex = keyid; |
455 | |
456 | res = rtw_set_key(adapter: padapter, psecuritypriv, keyid, set_tx: 1, enqueue: true); |
457 | |
458 | if (res == _FAIL) |
459 | ret = false; |
460 | exit: |
461 | |
462 | return ret; |
463 | } |
464 | |
465 | /* |
466 | * rtw_get_cur_max_rate - |
467 | * @adapter: pointer to struct adapter structure |
468 | * |
469 | * Return 0 or 100Kbps |
470 | */ |
471 | u16 rtw_get_cur_max_rate(struct adapter *adapter) |
472 | { |
473 | int i = 0; |
474 | u16 rate = 0, max_rate = 0; |
475 | struct mlme_priv *pmlmepriv = &adapter->mlmepriv; |
476 | struct wlan_bssid_ex *pcur_bss = &pmlmepriv->cur_network.network; |
477 | struct sta_info *psta = NULL; |
478 | u8 short_GI = 0; |
479 | |
480 | if ((check_fwstate(pmlmepriv, _FW_LINKED) != true) |
481 | && (check_fwstate(pmlmepriv, WIFI_ADHOC_MASTER_STATE) != true)) |
482 | return 0; |
483 | |
484 | psta = rtw_get_stainfo(pstapriv: &adapter->stapriv, hwaddr: get_bssid(pmlmepriv)); |
485 | if (!psta) |
486 | return 0; |
487 | |
488 | short_GI = query_ra_short_GI(psta); |
489 | |
490 | if (is_supported_ht(psta->wireless_mode)) { |
491 | max_rate = rtw_mcs_rate(bw_40MHz: psta->bw_mode == CHANNEL_WIDTH_40 ? 1 : 0, |
492 | short_GI, |
493 | MCS_rate: psta->htpriv.ht_cap.mcs.rx_mask); |
494 | } else { |
495 | while ((pcur_bss->supported_rates[i] != 0) && (pcur_bss->supported_rates[i] != 0xFF)) { |
496 | rate = pcur_bss->supported_rates[i]&0x7F; |
497 | if (rate > max_rate) |
498 | max_rate = rate; |
499 | i++; |
500 | } |
501 | |
502 | max_rate = max_rate*10/2; |
503 | } |
504 | |
505 | return max_rate; |
506 | } |
507 | |