xref: /linux/drivers/staging/rtl8192e/rtllib_softmac_wx.c (revision 3a39d672e7f48b8d6b91a09afa4b55352773b4b5)
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 
rtllib_wx_set_freq(struct rtllib_device * ieee,struct iw_request_info * a,union iwreq_data * wrqu,char * b)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 
rtllib_wx_get_freq(struct rtllib_device * ieee,struct iw_request_info * a,union iwreq_data * wrqu,char * b)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 
rtllib_wx_get_wap(struct rtllib_device * ieee,struct iw_request_info * info,union iwreq_data * wrqu,char * extra)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 
rtllib_wx_set_wap(struct rtllib_device * ieee,struct iw_request_info * info,union iwreq_data * awrq,char * extra)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 
rtllib_wx_get_essid(struct rtllib_device * ieee,struct iw_request_info * a,union iwreq_data * wrqu,char * b)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 
rtllib_wx_set_rate(struct rtllib_device * ieee,struct iw_request_info * info,union iwreq_data * wrqu,char * extra)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 
rtllib_wx_get_rate(struct rtllib_device * ieee,struct iw_request_info * info,union iwreq_data * wrqu,char * extra)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 
rtllib_wx_set_rts(struct rtllib_device * ieee,struct iw_request_info * info,union iwreq_data * wrqu,char * extra)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 
rtllib_wx_get_rts(struct rtllib_device * ieee,struct iw_request_info * info,union iwreq_data * wrqu,char * extra)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 
rtllib_wx_set_mode(struct rtllib_device * ieee,struct iw_request_info * a,union iwreq_data * wrqu,char * b)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 
rtllib_wx_sync_scan_wq(void * data)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->scan_operation_backup_handler(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->cur_sta_ext_chnl_offset;
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->scan_operation_backup_handler(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.num_recv_bcn_in_period == 0 ||
351 	    ieee->link_detect_info.num_recv_data_in_period == 0) {
352 		ieee->link_detect_info.num_recv_bcn_in_period = 1;
353 		ieee->link_detect_info.num_recv_data_in_period = 1;
354 	}
355 	rtllib_wake_all_queues(ieee);
356 
357 out:
358 	mutex_unlock(&ieee->wx_mutex);
359 }
360 
rtllib_wx_set_scan(struct rtllib_device * ieee,struct iw_request_info * a,union iwreq_data * wrqu,char * b)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 
rtllib_wx_set_essid(struct rtllib_device * ieee,struct iw_request_info * a,union iwreq_data * wrqu,char * extra)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 
rtllib_wx_get_mode(struct rtllib_device * ieee,struct iw_request_info * a,union iwreq_data * wrqu,char * b)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 
rtllib_wx_get_name(struct rtllib_device * ieee,struct iw_request_info * info,union iwreq_data * wrqu,char * extra)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 */
rtllib_wx_set_power(struct rtllib_device * ieee,struct iw_request_info * info,union iwreq_data * wrqu,char * extra)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 */
rtllib_wx_get_power(struct rtllib_device * ieee,struct iw_request_info * info,union iwreq_data * wrqu,char * extra)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);
535