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