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
15
16 #include <linux/etherdevice.h>
17
18 #include "ieee80211.h"
19 #include "dot11d.h"
20 /* FIXME: add A freqs */
21
22 const long ieee80211_wlan_frequencies[] = {
23 2412, 2417, 2422, 2427,
24 2432, 2437, 2442, 2447,
25 2452, 2457, 2462, 2467,
26 2472, 2484
27 };
28 EXPORT_SYMBOL(ieee80211_wlan_frequencies);
29
ieee80211_wx_set_freq(struct ieee80211_device * ieee,struct iw_request_info * a,union iwreq_data * wrqu,char * b)30 int ieee80211_wx_set_freq(struct ieee80211_device *ieee, struct iw_request_info *a,
31 union iwreq_data *wrqu, char *b)
32 {
33 int ret;
34 struct iw_freq *fwrq = &wrqu->freq;
35
36 mutex_lock(&ieee->wx_mutex);
37
38 if (ieee->iw_mode == IW_MODE_INFRA) {
39 ret = -EOPNOTSUPP;
40 goto out;
41 }
42
43 /* if setting by freq convert to channel */
44 if (fwrq->e == 1) {
45 if ((fwrq->m >= (int)2.412e8 &&
46 fwrq->m <= (int)2.487e8)) {
47 int f = fwrq->m / 100000;
48 int c = 0;
49
50 while ((c < 14) && (f != ieee80211_wlan_frequencies[c]))
51 c++;
52
53 /* hack to fall through */
54 fwrq->e = 0;
55 fwrq->m = c + 1;
56 }
57 }
58
59 if (fwrq->e > 0 || fwrq->m > 14 || fwrq->m < 1) {
60 ret = -EOPNOTSUPP;
61 goto out;
62
63 } else { /* Set the channel */
64
65 if (!(GET_DOT11D_INFO(ieee)->channel_map)[fwrq->m]) {
66 ret = -EINVAL;
67 goto out;
68 }
69 ieee->current_network.channel = fwrq->m;
70 ieee->set_chan(ieee->dev, ieee->current_network.channel);
71
72 if (ieee->iw_mode == IW_MODE_ADHOC || ieee->iw_mode == IW_MODE_MASTER)
73 if (ieee->state == IEEE80211_LINKED) {
74 ieee80211_stop_send_beacons(ieee);
75 ieee80211_start_send_beacons(ieee);
76 }
77 }
78
79 ret = 0;
80 out:
81 mutex_unlock(&ieee->wx_mutex);
82 return ret;
83 }
84 EXPORT_SYMBOL(ieee80211_wx_set_freq);
85
ieee80211_wx_get_freq(struct ieee80211_device * ieee,struct iw_request_info * a,union iwreq_data * wrqu,char * b)86 int ieee80211_wx_get_freq(struct ieee80211_device *ieee,
87 struct iw_request_info *a,
88 union iwreq_data *wrqu, char *b)
89 {
90 struct iw_freq *fwrq = &wrqu->freq;
91
92 if (ieee->current_network.channel == 0)
93 return -1;
94 /* NM 0.7.0 will not accept channel any more. */
95 fwrq->m = ieee80211_wlan_frequencies[ieee->current_network.channel - 1] * 100000;
96 fwrq->e = 1;
97 /* fwrq->m = ieee->current_network.channel; */
98 /* fwrq->e = 0; */
99
100 return 0;
101 }
102 EXPORT_SYMBOL(ieee80211_wx_get_freq);
103
ieee80211_wx_get_wap(struct ieee80211_device * ieee,struct iw_request_info * info,union iwreq_data * wrqu,char * extra)104 int ieee80211_wx_get_wap(struct ieee80211_device *ieee,
105 struct iw_request_info *info,
106 union iwreq_data *wrqu, char *extra)
107 {
108 unsigned long flags;
109
110 wrqu->ap_addr.sa_family = ARPHRD_ETHER;
111
112 if (ieee->iw_mode == IW_MODE_MONITOR)
113 return -1;
114
115 /* We want avoid to give to the user inconsistent infos*/
116 spin_lock_irqsave(&ieee->lock, flags);
117
118 if (ieee->state != IEEE80211_LINKED &&
119 ieee->state != IEEE80211_LINKED_SCANNING &&
120 ieee->wap_set == 0)
121
122 eth_zero_addr(wrqu->ap_addr.sa_data);
123 else
124 memcpy(wrqu->ap_addr.sa_data,
125 ieee->current_network.bssid, ETH_ALEN);
126
127 spin_unlock_irqrestore(&ieee->lock, flags);
128
129 return 0;
130 }
131 EXPORT_SYMBOL(ieee80211_wx_get_wap);
132
ieee80211_wx_set_wap(struct ieee80211_device * ieee,struct iw_request_info * info,union iwreq_data * awrq,char * extra)133 int ieee80211_wx_set_wap(struct ieee80211_device *ieee,
134 struct iw_request_info *info,
135 union iwreq_data *awrq,
136 char *extra)
137 {
138
139 int ret = 0;
140 unsigned long flags;
141
142 short ifup = ieee->proto_started; /* dev->flags & IFF_UP; */
143 struct sockaddr *temp = (struct sockaddr *)awrq;
144
145 ieee->sync_scan_hurryup = 1;
146
147 mutex_lock(&ieee->wx_mutex);
148 /* use ifconfig hw ether */
149 if (ieee->iw_mode == IW_MODE_MASTER) {
150 ret = -1;
151 goto out;
152 }
153
154 if (temp->sa_family != ARPHRD_ETHER) {
155 ret = -EINVAL;
156 goto out;
157 }
158
159 if (ifup)
160 ieee80211_stop_protocol(ieee);
161
162 /* just to avoid to give inconsistent infos in the
163 * get wx method. not really needed otherwise
164 */
165 spin_lock_irqsave(&ieee->lock, flags);
166
167 memcpy(ieee->current_network.bssid, temp->sa_data, ETH_ALEN);
168 ieee->wap_set = !is_zero_ether_addr(temp->sa_data);
169
170 spin_unlock_irqrestore(&ieee->lock, flags);
171
172 if (ifup)
173 ieee80211_start_protocol(ieee);
174 out:
175 mutex_unlock(&ieee->wx_mutex);
176 return ret;
177 }
178 EXPORT_SYMBOL(ieee80211_wx_set_wap);
179
ieee80211_wx_get_essid(struct ieee80211_device * ieee,struct iw_request_info * a,union iwreq_data * wrqu,char * b)180 int ieee80211_wx_get_essid(struct ieee80211_device *ieee, struct iw_request_info *a, union iwreq_data *wrqu, char *b)
181 {
182 int len, ret = 0;
183 unsigned long flags;
184
185 if (ieee->iw_mode == IW_MODE_MONITOR)
186 return -1;
187
188 /* We want avoid to give to the user inconsistent infos*/
189 spin_lock_irqsave(&ieee->lock, flags);
190
191 if (ieee->current_network.ssid[0] == '\0' ||
192 ieee->current_network.ssid_len == 0) {
193 ret = -1;
194 goto out;
195 }
196
197 if (ieee->state != IEEE80211_LINKED &&
198 ieee->state != IEEE80211_LINKED_SCANNING &&
199 ieee->ssid_set == 0) {
200 ret = -1;
201 goto out;
202 }
203 len = ieee->current_network.ssid_len;
204 wrqu->essid.length = len;
205 strncpy(b, ieee->current_network.ssid, len);
206 wrqu->essid.flags = 1;
207
208 out:
209 spin_unlock_irqrestore(&ieee->lock, flags);
210
211 return ret;
212
213 }
214 EXPORT_SYMBOL(ieee80211_wx_get_essid);
215
ieee80211_wx_set_rate(struct ieee80211_device * ieee,struct iw_request_info * info,union iwreq_data * wrqu,char * extra)216 int ieee80211_wx_set_rate(struct ieee80211_device *ieee,
217 struct iw_request_info *info,
218 union iwreq_data *wrqu, char *extra)
219 {
220
221 u32 target_rate = wrqu->bitrate.value;
222
223 ieee->rate = target_rate / 100000;
224 /* FIXME: we might want to limit rate also in management protocols. */
225 return 0;
226 }
227 EXPORT_SYMBOL(ieee80211_wx_set_rate);
228
ieee80211_wx_get_rate(struct ieee80211_device * ieee,struct iw_request_info * info,union iwreq_data * wrqu,char * extra)229 int ieee80211_wx_get_rate(struct ieee80211_device *ieee,
230 struct iw_request_info *info,
231 union iwreq_data *wrqu, char *extra)
232 {
233 u32 tmp_rate;
234
235 tmp_rate = TxCountToDataRate(ieee, ieee->softmac_stats.CurrentShowTxate);
236
237 wrqu->bitrate.value = tmp_rate * 500000;
238
239 return 0;
240 }
241 EXPORT_SYMBOL(ieee80211_wx_get_rate);
242
ieee80211_wx_set_rts(struct ieee80211_device * ieee,struct iw_request_info * info,union iwreq_data * wrqu,char * extra)243 int ieee80211_wx_set_rts(struct ieee80211_device *ieee,
244 struct iw_request_info *info,
245 union iwreq_data *wrqu, char *extra)
246 {
247 if (wrqu->rts.disabled || !wrqu->rts.fixed) {
248 ieee->rts = DEFAULT_RTS_THRESHOLD;
249 } else {
250 if (wrqu->rts.value < MIN_RTS_THRESHOLD ||
251 wrqu->rts.value > MAX_RTS_THRESHOLD)
252 return -EINVAL;
253 ieee->rts = wrqu->rts.value;
254 }
255 return 0;
256 }
257 EXPORT_SYMBOL(ieee80211_wx_set_rts);
258
ieee80211_wx_get_rts(struct ieee80211_device * ieee,struct iw_request_info * info,union iwreq_data * wrqu,char * extra)259 int ieee80211_wx_get_rts(struct ieee80211_device *ieee,
260 struct iw_request_info *info,
261 union iwreq_data *wrqu, char *extra)
262 {
263 wrqu->rts.value = ieee->rts;
264 wrqu->rts.fixed = 0; /* no auto select */
265 wrqu->rts.disabled = (wrqu->rts.value == DEFAULT_RTS_THRESHOLD);
266 return 0;
267 }
268 EXPORT_SYMBOL(ieee80211_wx_get_rts);
269
ieee80211_wx_set_mode(struct ieee80211_device * ieee,struct iw_request_info * a,union iwreq_data * wrqu,char * b)270 int ieee80211_wx_set_mode(struct ieee80211_device *ieee, struct iw_request_info *a,
271 union iwreq_data *wrqu, char *b)
272 {
273
274 ieee->sync_scan_hurryup = 1;
275
276 mutex_lock(&ieee->wx_mutex);
277
278 if (wrqu->mode == ieee->iw_mode)
279 goto out;
280
281 if (wrqu->mode == IW_MODE_MONITOR)
282 ieee->dev->type = ARPHRD_IEEE80211;
283 else
284 ieee->dev->type = ARPHRD_ETHER;
285
286 if (!ieee->proto_started) {
287 ieee->iw_mode = wrqu->mode;
288 } else {
289 ieee80211_stop_protocol(ieee);
290 ieee->iw_mode = wrqu->mode;
291 ieee80211_start_protocol(ieee);
292 }
293
294 out:
295 mutex_unlock(&ieee->wx_mutex);
296 return 0;
297 }
298 EXPORT_SYMBOL(ieee80211_wx_set_mode);
299
ieee80211_wx_sync_scan_wq(struct work_struct * work)300 void ieee80211_wx_sync_scan_wq(struct work_struct *work)
301 {
302 struct ieee80211_device *ieee = container_of(work, struct ieee80211_device, wx_sync_scan_wq);
303 short chan;
304 enum ht_extension_chan_offset chan_offset = 0;
305 enum ht_channel_width bandwidth = 0;
306 int b40M = 0;
307
308 chan = ieee->current_network.channel;
309 netif_carrier_off(ieee->dev);
310
311 if (ieee->data_hard_stop)
312 ieee->data_hard_stop(ieee->dev);
313
314 ieee80211_stop_send_beacons(ieee);
315
316 ieee->state = IEEE80211_LINKED_SCANNING;
317 ieee->link_change(ieee->dev);
318 ieee->InitialGainHandler(ieee->dev, IG_Backup);
319 if (ieee->pHTInfo->bCurrentHTSupport && ieee->pHTInfo->bEnableHT && ieee->pHTInfo->bCurBW40MHz) {
320 b40M = 1;
321 chan_offset = ieee->pHTInfo->CurSTAExtChnlOffset;
322 bandwidth = (enum ht_channel_width)ieee->pHTInfo->bCurBW40MHz;
323 printk("Scan in 40M, force to 20M first:%d, %d\n", chan_offset, bandwidth);
324 ieee->SetBWModeHandler(ieee->dev, HT_CHANNEL_WIDTH_20, HT_EXTCHNL_OFFSET_NO_EXT);
325 }
326 ieee80211_start_scan_syncro(ieee);
327 if (b40M) {
328 printk("Scan in 20M, back to 40M\n");
329 if (chan_offset == HT_EXTCHNL_OFFSET_UPPER)
330 ieee->set_chan(ieee->dev, chan + 2);
331 else if (chan_offset == HT_EXTCHNL_OFFSET_LOWER)
332 ieee->set_chan(ieee->dev, chan - 2);
333 else
334 ieee->set_chan(ieee->dev, chan);
335 ieee->SetBWModeHandler(ieee->dev, bandwidth, chan_offset);
336 } else {
337 ieee->set_chan(ieee->dev, chan);
338 }
339
340 ieee->InitialGainHandler(ieee->dev, IG_Restore);
341 ieee->state = IEEE80211_LINKED;
342 ieee->link_change(ieee->dev);
343 /* To prevent the immediately calling watch_dog after scan. */
344 if (ieee->LinkDetectInfo.NumRecvBcnInPeriod == 0 || ieee->LinkDetectInfo.NumRecvDataInPeriod == 0) {
345 ieee->LinkDetectInfo.NumRecvBcnInPeriod = 1;
346 ieee->LinkDetectInfo.NumRecvDataInPeriod = 1;
347 }
348 if (ieee->data_hard_resume)
349 ieee->data_hard_resume(ieee->dev);
350
351 if (ieee->iw_mode == IW_MODE_ADHOC || ieee->iw_mode == IW_MODE_MASTER)
352 ieee80211_start_send_beacons(ieee);
353
354 netif_carrier_on(ieee->dev);
355 mutex_unlock(&ieee->wx_mutex);
356
357 }
358
ieee80211_wx_set_scan(struct ieee80211_device * ieee,struct iw_request_info * a,union iwreq_data * wrqu,char * b)359 int ieee80211_wx_set_scan(struct ieee80211_device *ieee, struct iw_request_info *a,
360 union iwreq_data *wrqu, char *b)
361 {
362 int ret = 0;
363
364 mutex_lock(&ieee->wx_mutex);
365
366 if (ieee->iw_mode == IW_MODE_MONITOR || !(ieee->proto_started)) {
367 ret = -1;
368 goto out;
369 }
370
371 if (ieee->state == IEEE80211_LINKED) {
372 queue_work(ieee->wq, &ieee->wx_sync_scan_wq);
373 /* intentionally forget to up sem */
374 return 0;
375 }
376
377 out:
378 mutex_unlock(&ieee->wx_mutex);
379 return ret;
380 }
381 EXPORT_SYMBOL(ieee80211_wx_set_scan);
382
ieee80211_wx_set_essid(struct ieee80211_device * ieee,struct iw_request_info * a,union iwreq_data * wrqu,char * extra)383 int ieee80211_wx_set_essid(struct ieee80211_device *ieee,
384 struct iw_request_info *a,
385 union iwreq_data *wrqu, char *extra)
386 {
387
388 int ret = 0, len;
389 short proto_started;
390 unsigned long flags;
391
392 ieee->sync_scan_hurryup = 1;
393 mutex_lock(&ieee->wx_mutex);
394
395 proto_started = ieee->proto_started;
396
397 if (wrqu->essid.length > IW_ESSID_MAX_SIZE) {
398 ret = -E2BIG;
399 goto out;
400 }
401
402 if (ieee->iw_mode == IW_MODE_MONITOR) {
403 ret = -1;
404 goto out;
405 }
406
407 if (proto_started)
408 ieee80211_stop_protocol(ieee);
409
410
411 /* this is just to be sure that the GET wx callback
412 * has consisten infos. not needed otherwise
413 */
414 spin_lock_irqsave(&ieee->lock, flags);
415
416 if (wrqu->essid.flags && wrqu->essid.length) {
417 /* first flush current network.ssid */
418 len = ((wrqu->essid.length - 1) < IW_ESSID_MAX_SIZE) ? (wrqu->essid.length - 1) : IW_ESSID_MAX_SIZE;
419 strncpy(ieee->current_network.ssid, extra, len + 1);
420 ieee->current_network.ssid_len = len + 1;
421 ieee->ssid_set = 1;
422 } else {
423 ieee->ssid_set = 0;
424 ieee->current_network.ssid[0] = '\0';
425 ieee->current_network.ssid_len = 0;
426 }
427 spin_unlock_irqrestore(&ieee->lock, flags);
428
429 if (proto_started)
430 ieee80211_start_protocol(ieee);
431 out:
432 mutex_unlock(&ieee->wx_mutex);
433 return ret;
434 }
435 EXPORT_SYMBOL(ieee80211_wx_set_essid);
436
ieee80211_wx_get_mode(struct ieee80211_device * ieee,struct iw_request_info * a,union iwreq_data * wrqu,char * b)437 int ieee80211_wx_get_mode(struct ieee80211_device *ieee, struct iw_request_info *a,
438 union iwreq_data *wrqu, char *b)
439 {
440
441 wrqu->mode = ieee->iw_mode;
442 return 0;
443 }
444 EXPORT_SYMBOL(ieee80211_wx_get_mode);
445
ieee80211_wx_set_rawtx(struct ieee80211_device * ieee,struct iw_request_info * info,union iwreq_data * wrqu,char * extra)446 int ieee80211_wx_set_rawtx(struct ieee80211_device *ieee,
447 struct iw_request_info *info,
448 union iwreq_data *wrqu, char *extra)
449 {
450
451 int *parms = (int *)extra;
452 int enable = (parms[0] > 0);
453 short prev = ieee->raw_tx;
454
455 mutex_lock(&ieee->wx_mutex);
456
457 if (enable)
458 ieee->raw_tx = 1;
459 else
460 ieee->raw_tx = 0;
461
462 netdev_info(ieee->dev, "raw TX is %s\n",
463 ieee->raw_tx ? "enabled" : "disabled");
464
465 if (ieee->iw_mode == IW_MODE_MONITOR) {
466 if (prev == 0 && ieee->raw_tx) {
467 if (ieee->data_hard_resume)
468 ieee->data_hard_resume(ieee->dev);
469
470 netif_carrier_on(ieee->dev);
471 }
472
473 if (prev && ieee->raw_tx == 1)
474 netif_carrier_off(ieee->dev);
475 }
476
477 mutex_unlock(&ieee->wx_mutex);
478
479 return 0;
480 }
481 EXPORT_SYMBOL(ieee80211_wx_set_rawtx);
482
ieee80211_wx_get_name(struct ieee80211_device * ieee,struct iw_request_info * info,union iwreq_data * wrqu,char * extra)483 int ieee80211_wx_get_name(struct ieee80211_device *ieee,
484 struct iw_request_info *info,
485 union iwreq_data *wrqu, char *extra)
486 {
487 strscpy(wrqu->name, "802.11", IFNAMSIZ);
488 if (ieee->modulation & IEEE80211_CCK_MODULATION) {
489 strlcat(wrqu->name, "b", IFNAMSIZ);
490 if (ieee->modulation & IEEE80211_OFDM_MODULATION)
491 strlcat(wrqu->name, "/g", IFNAMSIZ);
492 } else if (ieee->modulation & IEEE80211_OFDM_MODULATION) {
493 strlcat(wrqu->name, "g", IFNAMSIZ);
494 }
495
496 if (ieee->mode & (IEEE_N_24G | IEEE_N_5G))
497 strlcat(wrqu->name, "/n", IFNAMSIZ);
498
499 if ((ieee->state == IEEE80211_LINKED) ||
500 (ieee->state == IEEE80211_LINKED_SCANNING))
501 strlcat(wrqu->name, " linked", IFNAMSIZ);
502 else if (ieee->state != IEEE80211_NOLINK)
503 strlcat(wrqu->name, " link..", IFNAMSIZ);
504
505 return 0;
506 }
507 EXPORT_SYMBOL(ieee80211_wx_get_name);
508
509 /* this is mostly stolen from hostap */
ieee80211_wx_set_power(struct ieee80211_device * ieee,struct iw_request_info * info,union iwreq_data * wrqu,char * extra)510 int ieee80211_wx_set_power(struct ieee80211_device *ieee,
511 struct iw_request_info *info,
512 union iwreq_data *wrqu, char *extra)
513 {
514 int ret = 0;
515
516 mutex_lock(&ieee->wx_mutex);
517
518 if (wrqu->power.disabled) {
519 ieee->ps = IEEE80211_PS_DISABLED;
520 goto exit;
521 }
522 if (wrqu->power.flags & IW_POWER_TIMEOUT) {
523 /* ieee->ps_period = wrqu->power.value / 1000; */
524 ieee->ps_timeout = wrqu->power.value / 1000;
525 }
526
527 if (wrqu->power.flags & IW_POWER_PERIOD) {
528
529 /* ieee->ps_timeout = wrqu->power.value / 1000; */
530 ieee->ps_period = wrqu->power.value / 1000;
531 /* wrq->value / 1024; */
532
533 }
534 switch (wrqu->power.flags & IW_POWER_MODE) {
535 case IW_POWER_UNICAST_R:
536 ieee->ps = IEEE80211_PS_UNICAST;
537 break;
538 case IW_POWER_MULTICAST_R:
539 ieee->ps = IEEE80211_PS_MBCAST;
540 break;
541 case IW_POWER_ALL_R:
542 ieee->ps = IEEE80211_PS_UNICAST | IEEE80211_PS_MBCAST;
543 break;
544
545 case IW_POWER_ON:
546 /* ieee->ps = IEEE80211_PS_DISABLED; */
547 break;
548
549 default:
550 ret = -EINVAL;
551 goto exit;
552
553 }
554 exit:
555 mutex_unlock(&ieee->wx_mutex);
556 return ret;
557
558 }
559 EXPORT_SYMBOL(ieee80211_wx_set_power);
560
561 /* this is stolen from hostap */
ieee80211_wx_get_power(struct ieee80211_device * ieee,struct iw_request_info * info,union iwreq_data * wrqu,char * extra)562 int ieee80211_wx_get_power(struct ieee80211_device *ieee,
563 struct iw_request_info *info,
564 union iwreq_data *wrqu, char *extra)
565 {
566 mutex_lock(&ieee->wx_mutex);
567
568 if (ieee->ps == IEEE80211_PS_DISABLED) {
569 wrqu->power.disabled = 1;
570 goto exit;
571 }
572
573 wrqu->power.disabled = 0;
574
575 if ((wrqu->power.flags & IW_POWER_TYPE) == IW_POWER_TIMEOUT) {
576 wrqu->power.flags = IW_POWER_TIMEOUT;
577 wrqu->power.value = ieee->ps_timeout * 1000;
578 } else {
579 /* ret = -EOPNOTSUPP; */
580 /* goto exit; */
581 wrqu->power.flags = IW_POWER_PERIOD;
582 wrqu->power.value = ieee->ps_period * 1000;
583 /* ieee->current_network.dtim_period * ieee->current_network.beacon_interval * 1024; */
584 }
585
586 if ((ieee->ps & (IEEE80211_PS_MBCAST | IEEE80211_PS_UNICAST)) == (IEEE80211_PS_MBCAST | IEEE80211_PS_UNICAST))
587 wrqu->power.flags |= IW_POWER_ALL_R;
588 else if (ieee->ps & IEEE80211_PS_MBCAST)
589 wrqu->power.flags |= IW_POWER_MULTICAST_R;
590 else
591 wrqu->power.flags |= IW_POWER_UNICAST_R;
592
593 exit:
594 mutex_unlock(&ieee->wx_mutex);
595 return 0;
596
597 }
598 EXPORT_SYMBOL(ieee80211_wx_get_power);
599