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
18int 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;
55out:
56	mutex_unlock(&ieee->wx_mutex);
57	return ret;
58}
59EXPORT_SYMBOL(rtllib_wx_set_freq);
60
61int 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}
74EXPORT_SYMBOL(rtllib_wx_get_freq);
75
76int 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}
103EXPORT_SYMBOL(rtllib_wx_get_wap);
104
105int 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);
151out:
152	mutex_unlock(&ieee->wx_mutex);
153	return ret;
154}
155EXPORT_SYMBOL(rtllib_wx_set_wap);
156
157int 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
186out:
187	spin_unlock_irqrestore(&ieee->lock, flags);
188
189	return ret;
190}
191EXPORT_SYMBOL(rtllib_wx_get_essid);
192
193int 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}
202EXPORT_SYMBOL(rtllib_wx_set_rate);
203
204int 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}
216EXPORT_SYMBOL(rtllib_wx_get_rate);
217
218int 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}
232EXPORT_SYMBOL(rtllib_wx_set_rts);
233
234int 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}
243EXPORT_SYMBOL(rtllib_wx_get_rts);
244
245int 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
284out:
285	mutex_unlock(&ieee->wx_mutex);
286	return set_mode_status;
287}
288EXPORT_SYMBOL(rtllib_wx_set_mode);
289
290void 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.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
357out:
358	mutex_unlock(&ieee->wx_mutex);
359}
360
361int 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
377out:
378	return ret;
379}
380EXPORT_SYMBOL(rtllib_wx_set_scan);
381
382int 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);
424out:
425	mutex_unlock(&ieee->wx_mutex);
426	return ret;
427}
428EXPORT_SYMBOL(rtllib_wx_set_essid);
429
430int 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}
436EXPORT_SYMBOL(rtllib_wx_get_mode);
437
438int 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}
446EXPORT_SYMBOL(rtllib_wx_get_name);
447
448/* this is mostly stolen from hostap */
449int 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	}
494exit:
495	mutex_unlock(&ieee->wx_mutex);
496	return ret;
497}
498EXPORT_SYMBOL(rtllib_wx_set_power);
499
500/* this is stolen from hostap */
501int 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
530exit:
531	mutex_unlock(&ieee->wx_mutex);
532	return 0;
533}
534EXPORT_SYMBOL(rtllib_wx_get_power);
535