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
19int 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;
56out:
57 mutex_unlock(lock: &ieee->wx_mutex);
58 return ret;
59}
60EXPORT_SYMBOL(rtllib_wx_set_freq);
61
62int 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}
75EXPORT_SYMBOL(rtllib_wx_get_freq);
76
77int rtllib_wx_get_wap(struct rtllib_device *ieee,
78 struct iw_request_info *info,
79 union iwreq_data *wrqu, char *extra)
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}
104EXPORT_SYMBOL(rtllib_wx_get_wap);
105
106int rtllib_wx_set_wap(struct rtllib_device *ieee,
107 struct iw_request_info *info,
108 union iwreq_data *awrq,
109 char *extra)
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);
152out:
153 mutex_unlock(lock: &ieee->wx_mutex);
154 return ret;
155}
156EXPORT_SYMBOL(rtllib_wx_set_wap);
157
158int 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
187out:
188 spin_unlock_irqrestore(lock: &ieee->lock, flags);
189
190 return ret;
191}
192EXPORT_SYMBOL(rtllib_wx_get_essid);
193
194int rtllib_wx_set_rate(struct rtllib_device *ieee,
195 struct iw_request_info *info,
196 union iwreq_data *wrqu, char *extra)
197{
198 u32 target_rate = wrqu->bitrate.value;
199
200 ieee->rate = target_rate / 100000;
201 return 0;
202}
203EXPORT_SYMBOL(rtllib_wx_set_rate);
204
205int rtllib_wx_get_rate(struct rtllib_device *ieee,
206 struct iw_request_info *info,
207 union iwreq_data *wrqu, char *extra)
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}
217EXPORT_SYMBOL(rtllib_wx_get_rate);
218
219int rtllib_wx_set_rts(struct rtllib_device *ieee,
220 struct iw_request_info *info,
221 union iwreq_data *wrqu, char *extra)
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}
233EXPORT_SYMBOL(rtllib_wx_set_rts);
234
235int rtllib_wx_get_rts(struct rtllib_device *ieee,
236 struct iw_request_info *info,
237 union iwreq_data *wrqu, char *extra)
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}
244EXPORT_SYMBOL(rtllib_wx_get_rts);
245
246int 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
285out:
286 mutex_unlock(lock: &ieee->wx_mutex);
287 return set_mode_status;
288}
289EXPORT_SYMBOL(rtllib_wx_set_mode);
290
291void 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
358out:
359 mutex_unlock(lock: &ieee->wx_mutex);
360}
361
362int 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
378out:
379 return ret;
380}
381EXPORT_SYMBOL(rtllib_wx_set_scan);
382
383int rtllib_wx_set_essid(struct rtllib_device *ieee,
384 struct iw_request_info *a,
385 union iwreq_data *wrqu, char *extra)
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);
425out:
426 mutex_unlock(lock: &ieee->wx_mutex);
427 return ret;
428}
429EXPORT_SYMBOL(rtllib_wx_set_essid);
430
431int 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}
437EXPORT_SYMBOL(rtllib_wx_get_mode);
438
439int rtllib_wx_get_name(struct rtllib_device *ieee, struct iw_request_info *info,
440 union iwreq_data *wrqu, char *extra)
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}
447EXPORT_SYMBOL(rtllib_wx_get_name);
448
449/* this is mostly stolen from hostap */
450int rtllib_wx_set_power(struct rtllib_device *ieee,
451 struct iw_request_info *info,
452 union iwreq_data *wrqu, char *extra)
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 }
495exit:
496 mutex_unlock(lock: &ieee->wx_mutex);
497 return ret;
498}
499EXPORT_SYMBOL(rtllib_wx_set_power);
500
501/* this is stolen from hostap */
502int rtllib_wx_get_power(struct rtllib_device *ieee,
503 struct iw_request_info *info,
504 union iwreq_data *wrqu, char *extra)
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
531exit:
532 mutex_unlock(lock: &ieee->wx_mutex);
533 return 0;
534}
535EXPORT_SYMBOL(rtllib_wx_get_power);
536

source code of linux/drivers/staging/rtl8192e/rtllib_softmac_wx.c