1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* IEEE 802.11 SoftMAC layer |
3 | * Copyright (c) 2005 Andrea Merello <andrea.merello@gmail.com> |
4 | * |
5 | * Mostly extracted from the rtl8180-sa2400 driver for the |
6 | * in-kernel generic ieee802.11 stack. |
7 | * |
8 | * Some pieces of code might be stolen from ipw2100 driver |
9 | * copyright of who own it's copyright ;-) |
10 | * |
11 | * PS wx handler mostly stolen from hostap, copyright who |
12 | * own it's copyright ;-) |
13 | */ |
14 | #include <linux/etherdevice.h> |
15 | |
16 | #include "rtllib.h" |
17 | #include "dot11d.h" |
18 | |
19 | int rtllib_wx_set_freq(struct rtllib_device *ieee, struct iw_request_info *a, |
20 | union iwreq_data *wrqu, char *b) |
21 | { |
22 | int ret; |
23 | struct iw_freq *fwrq = &wrqu->freq; |
24 | |
25 | mutex_lock(&ieee->wx_mutex); |
26 | |
27 | if (ieee->iw_mode == IW_MODE_INFRA) { |
28 | ret = 0; |
29 | goto out; |
30 | } |
31 | |
32 | /* if setting by freq convert to channel */ |
33 | if (fwrq->e == 1) { |
34 | if ((fwrq->m >= (int)2.412e8 && |
35 | fwrq->m <= (int)2.487e8)) { |
36 | fwrq->m = ieee80211_freq_khz_to_channel(freq: fwrq->m / 100); |
37 | fwrq->e = 0; |
38 | } |
39 | } |
40 | |
41 | if (fwrq->e > 0 || fwrq->m > 14 || fwrq->m < 1) { |
42 | ret = -EOPNOTSUPP; |
43 | goto out; |
44 | |
45 | } else { /* Set the channel */ |
46 | |
47 | if (ieee->active_channel_map[fwrq->m] != 1) { |
48 | ret = -EINVAL; |
49 | goto out; |
50 | } |
51 | ieee->current_network.channel = fwrq->m; |
52 | ieee->set_chan(ieee->dev, ieee->current_network.channel); |
53 | } |
54 | |
55 | ret = 0; |
56 | out: |
57 | mutex_unlock(lock: &ieee->wx_mutex); |
58 | return ret; |
59 | } |
60 | EXPORT_SYMBOL(rtllib_wx_set_freq); |
61 | |
62 | int rtllib_wx_get_freq(struct rtllib_device *ieee, |
63 | struct iw_request_info *a, |
64 | union iwreq_data *wrqu, char *b) |
65 | { |
66 | struct iw_freq *fwrq = &wrqu->freq; |
67 | |
68 | if (ieee->current_network.channel == 0) |
69 | return -1; |
70 | fwrq->m = ieee80211_channel_to_freq_khz(chan: ieee->current_network.channel, |
71 | band: NL80211_BAND_2GHZ) * 100; |
72 | fwrq->e = 1; |
73 | return 0; |
74 | } |
75 | EXPORT_SYMBOL(rtllib_wx_get_freq); |
76 | |
77 | int rtllib_wx_get_wap(struct rtllib_device *ieee, |
78 | struct iw_request_info *info, |
79 | union iwreq_data *wrqu, char *) |
80 | { |
81 | unsigned long flags; |
82 | |
83 | wrqu->ap_addr.sa_family = ARPHRD_ETHER; |
84 | |
85 | if (ieee->iw_mode == IW_MODE_MONITOR) |
86 | return -1; |
87 | |
88 | /* We want avoid to give to the user inconsistent infos*/ |
89 | spin_lock_irqsave(&ieee->lock, flags); |
90 | |
91 | if (ieee->link_state != MAC80211_LINKED && |
92 | ieee->link_state != MAC80211_LINKED_SCANNING && |
93 | ieee->wap_set == 0) |
94 | |
95 | eth_zero_addr(addr: wrqu->ap_addr.sa_data); |
96 | else |
97 | memcpy(wrqu->ap_addr.sa_data, |
98 | ieee->current_network.bssid, ETH_ALEN); |
99 | |
100 | spin_unlock_irqrestore(lock: &ieee->lock, flags); |
101 | |
102 | return 0; |
103 | } |
104 | EXPORT_SYMBOL(rtllib_wx_get_wap); |
105 | |
106 | int rtllib_wx_set_wap(struct rtllib_device *ieee, |
107 | struct iw_request_info *info, |
108 | union iwreq_data *awrq, |
109 | char *) |
110 | { |
111 | int ret = 0; |
112 | unsigned long flags; |
113 | |
114 | short ifup = ieee->proto_started; |
115 | struct sockaddr *temp = (struct sockaddr *)awrq; |
116 | |
117 | rtllib_stop_scan_syncro(ieee); |
118 | |
119 | mutex_lock(&ieee->wx_mutex); |
120 | /* use ifconfig hw ether */ |
121 | |
122 | if (temp->sa_family != ARPHRD_ETHER) { |
123 | ret = -EINVAL; |
124 | goto out; |
125 | } |
126 | |
127 | if (is_zero_ether_addr(addr: temp->sa_data)) { |
128 | spin_lock_irqsave(&ieee->lock, flags); |
129 | ether_addr_copy(dst: ieee->current_network.bssid, src: temp->sa_data); |
130 | ieee->wap_set = 0; |
131 | spin_unlock_irqrestore(lock: &ieee->lock, flags); |
132 | ret = -1; |
133 | goto out; |
134 | } |
135 | |
136 | if (ifup) |
137 | rtllib_stop_protocol(ieee); |
138 | |
139 | /* just to avoid to give inconsistent infos in the |
140 | * get wx method. not really needed otherwise |
141 | */ |
142 | spin_lock_irqsave(&ieee->lock, flags); |
143 | |
144 | ieee->cannot_notify = false; |
145 | ether_addr_copy(dst: ieee->current_network.bssid, src: temp->sa_data); |
146 | ieee->wap_set = !is_zero_ether_addr(addr: temp->sa_data); |
147 | |
148 | spin_unlock_irqrestore(lock: &ieee->lock, flags); |
149 | |
150 | if (ifup) |
151 | rtllib_start_protocol(ieee); |
152 | out: |
153 | mutex_unlock(lock: &ieee->wx_mutex); |
154 | return ret; |
155 | } |
156 | EXPORT_SYMBOL(rtllib_wx_set_wap); |
157 | |
158 | int rtllib_wx_get_essid(struct rtllib_device *ieee, struct iw_request_info *a, |
159 | union iwreq_data *wrqu, char *b) |
160 | { |
161 | int len, ret = 0; |
162 | unsigned long flags; |
163 | |
164 | if (ieee->iw_mode == IW_MODE_MONITOR) |
165 | return -1; |
166 | |
167 | /* We want avoid to give to the user inconsistent infos*/ |
168 | spin_lock_irqsave(&ieee->lock, flags); |
169 | |
170 | if (ieee->current_network.ssid[0] == '\0' || |
171 | ieee->current_network.ssid_len == 0) { |
172 | ret = -1; |
173 | goto out; |
174 | } |
175 | |
176 | if (ieee->link_state != MAC80211_LINKED && |
177 | ieee->link_state != MAC80211_LINKED_SCANNING && |
178 | ieee->ssid_set == 0) { |
179 | ret = -1; |
180 | goto out; |
181 | } |
182 | len = ieee->current_network.ssid_len; |
183 | wrqu->essid.length = len; |
184 | strncpy(p: b, q: ieee->current_network.ssid, size: len); |
185 | wrqu->essid.flags = 1; |
186 | |
187 | out: |
188 | spin_unlock_irqrestore(lock: &ieee->lock, flags); |
189 | |
190 | return ret; |
191 | } |
192 | EXPORT_SYMBOL(rtllib_wx_get_essid); |
193 | |
194 | int rtllib_wx_set_rate(struct rtllib_device *ieee, |
195 | struct iw_request_info *info, |
196 | union iwreq_data *wrqu, char *) |
197 | { |
198 | u32 target_rate = wrqu->bitrate.value; |
199 | |
200 | ieee->rate = target_rate / 100000; |
201 | return 0; |
202 | } |
203 | EXPORT_SYMBOL(rtllib_wx_set_rate); |
204 | |
205 | int rtllib_wx_get_rate(struct rtllib_device *ieee, |
206 | struct iw_request_info *info, |
207 | union iwreq_data *wrqu, char *) |
208 | { |
209 | u32 tmp_rate; |
210 | |
211 | tmp_rate = TxCountToDataRate(ieee, |
212 | nDataRate: ieee->softmac_stats.CurrentShowTxate); |
213 | wrqu->bitrate.value = tmp_rate * 500000; |
214 | |
215 | return 0; |
216 | } |
217 | EXPORT_SYMBOL(rtllib_wx_get_rate); |
218 | |
219 | int rtllib_wx_set_rts(struct rtllib_device *ieee, |
220 | struct iw_request_info *info, |
221 | union iwreq_data *wrqu, char *) |
222 | { |
223 | if (wrqu->rts.disabled || !wrqu->rts.fixed) { |
224 | ieee->rts = DEFAULT_RTS_THRESHOLD; |
225 | } else { |
226 | if (wrqu->rts.value < MIN_RTS_THRESHOLD || |
227 | wrqu->rts.value > MAX_RTS_THRESHOLD) |
228 | return -EINVAL; |
229 | ieee->rts = wrqu->rts.value; |
230 | } |
231 | return 0; |
232 | } |
233 | EXPORT_SYMBOL(rtllib_wx_set_rts); |
234 | |
235 | int rtllib_wx_get_rts(struct rtllib_device *ieee, |
236 | struct iw_request_info *info, |
237 | union iwreq_data *wrqu, char *) |
238 | { |
239 | wrqu->rts.value = ieee->rts; |
240 | wrqu->rts.fixed = 0; /* no auto select */ |
241 | wrqu->rts.disabled = (wrqu->rts.value == DEFAULT_RTS_THRESHOLD); |
242 | return 0; |
243 | } |
244 | EXPORT_SYMBOL(rtllib_wx_get_rts); |
245 | |
246 | int rtllib_wx_set_mode(struct rtllib_device *ieee, struct iw_request_info *a, |
247 | union iwreq_data *wrqu, char *b) |
248 | { |
249 | int set_mode_status = 0; |
250 | |
251 | rtllib_stop_scan_syncro(ieee); |
252 | mutex_lock(&ieee->wx_mutex); |
253 | switch (wrqu->mode) { |
254 | case IW_MODE_MONITOR: |
255 | case IW_MODE_INFRA: |
256 | break; |
257 | case IW_MODE_AUTO: |
258 | wrqu->mode = IW_MODE_INFRA; |
259 | break; |
260 | default: |
261 | set_mode_status = -EINVAL; |
262 | goto out; |
263 | } |
264 | |
265 | if (wrqu->mode == ieee->iw_mode) |
266 | goto out; |
267 | |
268 | if (wrqu->mode == IW_MODE_MONITOR) { |
269 | ieee->dev->type = ARPHRD_IEEE80211; |
270 | rtllib_EnableNetMonitorMode(dev: ieee->dev, bInitState: false); |
271 | } else { |
272 | ieee->dev->type = ARPHRD_ETHER; |
273 | if (ieee->iw_mode == IW_MODE_MONITOR) |
274 | rtllib_DisableNetMonitorMode(dev: ieee->dev, bInitState: false); |
275 | } |
276 | |
277 | if (!ieee->proto_started) { |
278 | ieee->iw_mode = wrqu->mode; |
279 | } else { |
280 | rtllib_stop_protocol(ieee); |
281 | ieee->iw_mode = wrqu->mode; |
282 | rtllib_start_protocol(ieee); |
283 | } |
284 | |
285 | out: |
286 | mutex_unlock(lock: &ieee->wx_mutex); |
287 | return set_mode_status; |
288 | } |
289 | EXPORT_SYMBOL(rtllib_wx_set_mode); |
290 | |
291 | void rtllib_wx_sync_scan_wq(void *data) |
292 | { |
293 | struct rtllib_device *ieee = container_of(data, struct rtllib_device, wx_sync_scan_wq); |
294 | short chan; |
295 | enum ht_extchnl_offset chan_offset = 0; |
296 | enum ht_channel_width bandwidth = 0; |
297 | int b40M = 0; |
298 | |
299 | mutex_lock(&ieee->wx_mutex); |
300 | if (!(ieee->softmac_features & IEEE_SOFTMAC_SCAN)) { |
301 | rtllib_start_scan_syncro(ieee); |
302 | goto out; |
303 | } |
304 | |
305 | chan = ieee->current_network.channel; |
306 | |
307 | ieee->leisure_ps_leave(ieee->dev); |
308 | /* notify AP to be in PS mode */ |
309 | rtllib_sta_ps_send_null_frame(ieee, pwr: 1); |
310 | rtllib_sta_ps_send_null_frame(ieee, pwr: 1); |
311 | |
312 | rtllib_stop_all_queues(ieee); |
313 | ieee->link_state = MAC80211_LINKED_SCANNING; |
314 | ieee->link_change(ieee->dev); |
315 | /* wait for ps packet to be kicked out successfully */ |
316 | msleep(msecs: 50); |
317 | |
318 | ieee->ScanOperationBackupHandler(ieee->dev, SCAN_OPT_BACKUP); |
319 | |
320 | if (ieee->ht_info->current_ht_support && ieee->ht_info->enable_ht && |
321 | ieee->ht_info->bCurBW40MHz) { |
322 | b40M = 1; |
323 | chan_offset = ieee->ht_info->CurSTAExtChnlOffset; |
324 | bandwidth = (enum ht_channel_width)ieee->ht_info->bCurBW40MHz; |
325 | ieee->set_bw_mode_handler(ieee->dev, HT_CHANNEL_WIDTH_20, |
326 | HT_EXTCHNL_OFFSET_NO_EXT); |
327 | } |
328 | |
329 | rtllib_start_scan_syncro(ieee); |
330 | |
331 | if (b40M) { |
332 | if (chan_offset == HT_EXTCHNL_OFFSET_UPPER) |
333 | ieee->set_chan(ieee->dev, chan + 2); |
334 | else if (chan_offset == HT_EXTCHNL_OFFSET_LOWER) |
335 | ieee->set_chan(ieee->dev, chan - 2); |
336 | else |
337 | ieee->set_chan(ieee->dev, chan); |
338 | ieee->set_bw_mode_handler(ieee->dev, bandwidth, chan_offset); |
339 | } else { |
340 | ieee->set_chan(ieee->dev, chan); |
341 | } |
342 | |
343 | ieee->ScanOperationBackupHandler(ieee->dev, SCAN_OPT_RESTORE); |
344 | |
345 | ieee->link_state = MAC80211_LINKED; |
346 | ieee->link_change(ieee->dev); |
347 | |
348 | /* Notify AP that I wake up again */ |
349 | rtllib_sta_ps_send_null_frame(ieee, pwr: 0); |
350 | |
351 | if (ieee->link_detect_info.NumRecvBcnInPeriod == 0 || |
352 | ieee->link_detect_info.NumRecvDataInPeriod == 0) { |
353 | ieee->link_detect_info.NumRecvBcnInPeriod = 1; |
354 | ieee->link_detect_info.NumRecvDataInPeriod = 1; |
355 | } |
356 | rtllib_wake_all_queues(ieee); |
357 | |
358 | out: |
359 | mutex_unlock(lock: &ieee->wx_mutex); |
360 | } |
361 | |
362 | int rtllib_wx_set_scan(struct rtllib_device *ieee, struct iw_request_info *a, |
363 | union iwreq_data *wrqu, char *b) |
364 | { |
365 | int ret = 0; |
366 | |
367 | if (ieee->iw_mode == IW_MODE_MONITOR || !(ieee->proto_started)) { |
368 | ret = -1; |
369 | goto out; |
370 | } |
371 | |
372 | if (ieee->link_state == MAC80211_LINKED) { |
373 | schedule_work(work: &ieee->wx_sync_scan_wq); |
374 | /* intentionally forget to up sem */ |
375 | return 0; |
376 | } |
377 | |
378 | out: |
379 | return ret; |
380 | } |
381 | EXPORT_SYMBOL(rtllib_wx_set_scan); |
382 | |
383 | int rtllib_wx_set_essid(struct rtllib_device *ieee, |
384 | struct iw_request_info *a, |
385 | union iwreq_data *wrqu, char *) |
386 | { |
387 | int ret = 0, len; |
388 | short proto_started; |
389 | unsigned long flags; |
390 | |
391 | rtllib_stop_scan_syncro(ieee); |
392 | mutex_lock(&ieee->wx_mutex); |
393 | |
394 | proto_started = ieee->proto_started; |
395 | |
396 | len = min_t(__u16, wrqu->essid.length, IW_ESSID_MAX_SIZE); |
397 | |
398 | if (ieee->iw_mode == IW_MODE_MONITOR) { |
399 | ret = -1; |
400 | goto out; |
401 | } |
402 | |
403 | if (proto_started) |
404 | rtllib_stop_protocol(ieee); |
405 | |
406 | /* this is just to be sure that the GET wx callback |
407 | * has consistent infos. not needed otherwise |
408 | */ |
409 | spin_lock_irqsave(&ieee->lock, flags); |
410 | |
411 | if (wrqu->essid.flags && wrqu->essid.length) { |
412 | strncpy(p: ieee->current_network.ssid, q: extra, size: len); |
413 | ieee->current_network.ssid_len = len; |
414 | ieee->cannot_notify = false; |
415 | ieee->ssid_set = 1; |
416 | } else { |
417 | ieee->ssid_set = 0; |
418 | ieee->current_network.ssid[0] = '\0'; |
419 | ieee->current_network.ssid_len = 0; |
420 | } |
421 | spin_unlock_irqrestore(lock: &ieee->lock, flags); |
422 | |
423 | if (proto_started) |
424 | rtllib_start_protocol(ieee); |
425 | out: |
426 | mutex_unlock(lock: &ieee->wx_mutex); |
427 | return ret; |
428 | } |
429 | EXPORT_SYMBOL(rtllib_wx_set_essid); |
430 | |
431 | int rtllib_wx_get_mode(struct rtllib_device *ieee, struct iw_request_info *a, |
432 | union iwreq_data *wrqu, char *b) |
433 | { |
434 | wrqu->mode = ieee->iw_mode; |
435 | return 0; |
436 | } |
437 | EXPORT_SYMBOL(rtllib_wx_get_mode); |
438 | |
439 | int rtllib_wx_get_name(struct rtllib_device *ieee, struct iw_request_info *info, |
440 | union iwreq_data *wrqu, char *) |
441 | { |
442 | const char *n = ieee->mode & (WIRELESS_MODE_N_24G) ? "n" : "" ; |
443 | |
444 | scnprintf(buf: wrqu->name, size: sizeof(wrqu->name), fmt: "802.11bg%s" , n); |
445 | return 0; |
446 | } |
447 | EXPORT_SYMBOL(rtllib_wx_get_name); |
448 | |
449 | /* this is mostly stolen from hostap */ |
450 | int rtllib_wx_set_power(struct rtllib_device *ieee, |
451 | struct iw_request_info *info, |
452 | union iwreq_data *wrqu, char *) |
453 | { |
454 | int ret = 0; |
455 | |
456 | if ((!ieee->sta_wake_up) || |
457 | (!ieee->enter_sleep_state) || |
458 | (!ieee->ps_is_queue_empty)) { |
459 | netdev_warn(dev: ieee->dev, |
460 | format: "%s(): PS mode is tried to be use but driver missed a callback\n" , |
461 | __func__); |
462 | return -1; |
463 | } |
464 | |
465 | mutex_lock(&ieee->wx_mutex); |
466 | |
467 | if (wrqu->power.disabled) { |
468 | ieee->ps = RTLLIB_PS_DISABLED; |
469 | goto exit; |
470 | } |
471 | if (wrqu->power.flags & IW_POWER_TIMEOUT) |
472 | ieee->ps_timeout = wrqu->power.value / 1000; |
473 | |
474 | if (wrqu->power.flags & IW_POWER_PERIOD) |
475 | ieee->ps_period = wrqu->power.value / 1000; |
476 | |
477 | switch (wrqu->power.flags & IW_POWER_MODE) { |
478 | case IW_POWER_UNICAST_R: |
479 | ieee->ps = RTLLIB_PS_UNICAST; |
480 | break; |
481 | case IW_POWER_MULTICAST_R: |
482 | ieee->ps = RTLLIB_PS_MBCAST; |
483 | break; |
484 | case IW_POWER_ALL_R: |
485 | ieee->ps = RTLLIB_PS_UNICAST | RTLLIB_PS_MBCAST; |
486 | break; |
487 | |
488 | case IW_POWER_ON: |
489 | break; |
490 | |
491 | default: |
492 | ret = -EINVAL; |
493 | goto exit; |
494 | } |
495 | exit: |
496 | mutex_unlock(lock: &ieee->wx_mutex); |
497 | return ret; |
498 | } |
499 | EXPORT_SYMBOL(rtllib_wx_set_power); |
500 | |
501 | /* this is stolen from hostap */ |
502 | int rtllib_wx_get_power(struct rtllib_device *ieee, |
503 | struct iw_request_info *info, |
504 | union iwreq_data *wrqu, char *) |
505 | { |
506 | mutex_lock(&ieee->wx_mutex); |
507 | |
508 | if (ieee->ps == RTLLIB_PS_DISABLED) { |
509 | wrqu->power.disabled = 1; |
510 | goto exit; |
511 | } |
512 | |
513 | wrqu->power.disabled = 0; |
514 | |
515 | if ((wrqu->power.flags & IW_POWER_TYPE) == IW_POWER_TIMEOUT) { |
516 | wrqu->power.flags = IW_POWER_TIMEOUT; |
517 | wrqu->power.value = ieee->ps_timeout * 1000; |
518 | } else { |
519 | wrqu->power.flags = IW_POWER_PERIOD; |
520 | wrqu->power.value = ieee->ps_period * 1000; |
521 | } |
522 | |
523 | if ((ieee->ps & (RTLLIB_PS_MBCAST | RTLLIB_PS_UNICAST)) == |
524 | (RTLLIB_PS_MBCAST | RTLLIB_PS_UNICAST)) |
525 | wrqu->power.flags |= IW_POWER_ALL_R; |
526 | else if (ieee->ps & RTLLIB_PS_MBCAST) |
527 | wrqu->power.flags |= IW_POWER_MULTICAST_R; |
528 | else |
529 | wrqu->power.flags |= IW_POWER_UNICAST_R; |
530 | |
531 | exit: |
532 | mutex_unlock(lock: &ieee->wx_mutex); |
533 | return 0; |
534 | } |
535 | EXPORT_SYMBOL(rtllib_wx_get_power); |
536 | |