1/* IEEE 802.11 SoftMAC layer
2 * Copyright (c) 2005 Andrea Merello <andrea.merello@gmail.com>
3 *
4 * Mostly extracted from the rtl8180-sa2400 driver for the
5 * in-kernel generic ieee802.11 stack.
6 *
7 * Some pieces of code might be stolen from ipw2100 driver
8 * copyright of who own it's copyright ;-)
9 *
10 * PS wx handler mostly stolen from hostap, copyright who
11 * own it's copyright ;-)
12 *
13 * released under the GPL
14 */
15
16
17#include <linux/etherdevice.h>
18
19#include "rtllib.h"
20#include "dot11d.h"
21/* FIXME: add A freqs */
22
23const long rtllib_wlan_frequencies[] = {
24	2412, 2417, 2422, 2427,
25	2432, 2437, 2442, 2447,
26	2452, 2457, 2462, 2467,
27	2472, 2484
28};
29EXPORT_SYMBOL(rtllib_wlan_frequencies);
30
31
32int rtllib_wx_set_freq(struct rtllib_device *ieee, struct iw_request_info *a,
33			     union iwreq_data *wrqu, char *b)
34{
35	int ret;
36	struct iw_freq *fwrq = &wrqu->freq;
37
38	down(&ieee->wx_sem);
39
40	if (ieee->iw_mode == IW_MODE_INFRA) {
41		ret = 0;
42		goto out;
43	}
44
45	/* if setting by freq convert to channel */
46	if (fwrq->e == 1) {
47		if ((fwrq->m >= (int) 2.412e8 &&
48		     fwrq->m <= (int) 2.487e8)) {
49			int f = fwrq->m / 100000;
50			int c = 0;
51
52			while ((c < 14) && (f != rtllib_wlan_frequencies[c]))
53				c++;
54
55			/* hack to fall through */
56			fwrq->e = 0;
57			fwrq->m = c + 1;
58		}
59	}
60
61	if (fwrq->e > 0 || fwrq->m > 14 || fwrq->m < 1) {
62		ret = -EOPNOTSUPP;
63		goto out;
64
65	} else { /* Set the channel */
66
67		if (ieee->active_channel_map[fwrq->m] != 1) {
68			ret = -EINVAL;
69			goto out;
70		}
71		ieee->current_network.channel = fwrq->m;
72		ieee->set_chan(ieee->dev, ieee->current_network.channel);
73
74		if (ieee->iw_mode == IW_MODE_ADHOC ||
75		    ieee->iw_mode == IW_MODE_MASTER)
76			if (ieee->state == RTLLIB_LINKED) {
77				rtllib_stop_send_beacons(ieee);
78				rtllib_start_send_beacons(ieee);
79			}
80	}
81
82	ret = 0;
83out:
84	up(&ieee->wx_sem);
85	return ret;
86}
87EXPORT_SYMBOL(rtllib_wx_set_freq);
88
89
90int rtllib_wx_get_freq(struct rtllib_device *ieee,
91			     struct iw_request_info *a,
92			     union iwreq_data *wrqu, char *b)
93{
94	struct iw_freq *fwrq = &wrqu->freq;
95
96	if (ieee->current_network.channel == 0)
97		return -1;
98	fwrq->m = rtllib_wlan_frequencies[ieee->current_network.channel-1] *
99		  100000;
100	fwrq->e = 1;
101	return 0;
102}
103EXPORT_SYMBOL(rtllib_wx_get_freq);
104
105int rtllib_wx_get_wap(struct rtllib_device *ieee,
106			    struct iw_request_info *info,
107			    union iwreq_data *wrqu, char *extra)
108{
109	unsigned long flags;
110
111	wrqu->ap_addr.sa_family = ARPHRD_ETHER;
112
113	if (ieee->iw_mode == IW_MODE_MONITOR)
114		return -1;
115
116	/* We want avoid to give to the user inconsistent infos*/
117	spin_lock_irqsave(&ieee->lock, flags);
118
119	if (ieee->state != RTLLIB_LINKED &&
120		ieee->state != RTLLIB_LINKED_SCANNING &&
121		ieee->wap_set == 0)
122
123		memset(wrqu->ap_addr.sa_data, 0, ETH_ALEN);
124	else
125		memcpy(wrqu->ap_addr.sa_data,
126		       ieee->current_network.bssid, ETH_ALEN);
127
128	spin_unlock_irqrestore(&ieee->lock, flags);
129
130	return 0;
131}
132EXPORT_SYMBOL(rtllib_wx_get_wap);
133
134
135int rtllib_wx_set_wap(struct rtllib_device *ieee,
136			 struct iw_request_info *info,
137			 union iwreq_data *awrq,
138			 char *extra)
139{
140
141	int ret = 0;
142	unsigned long flags;
143
144	short ifup = ieee->proto_started;
145	struct sockaddr *temp = (struct sockaddr *)awrq;
146
147	rtllib_stop_scan_syncro(ieee);
148
149	down(&ieee->wx_sem);
150	/* use ifconfig hw ether */
151	if (ieee->iw_mode == IW_MODE_MASTER) {
152		ret = -1;
153		goto out;
154	}
155
156	if (temp->sa_family != ARPHRD_ETHER) {
157		ret = -EINVAL;
158		goto out;
159	}
160
161	if (is_zero_ether_addr(temp->sa_data)) {
162		spin_lock_irqsave(&ieee->lock, flags);
163		memcpy(ieee->current_network.bssid, temp->sa_data, ETH_ALEN);
164		ieee->wap_set = 0;
165		spin_unlock_irqrestore(&ieee->lock, flags);
166		ret = -1;
167		goto out;
168	}
169
170
171	if (ifup)
172		rtllib_stop_protocol(ieee, true);
173
174	/* just to avoid to give inconsistent infos in the
175	 * get wx method. not really needed otherwise
176	 */
177	spin_lock_irqsave(&ieee->lock, flags);
178
179	ieee->cannot_notify = false;
180	memcpy(ieee->current_network.bssid, temp->sa_data, ETH_ALEN);
181	ieee->wap_set = !is_zero_ether_addr(temp->sa_data);
182
183	spin_unlock_irqrestore(&ieee->lock, flags);
184
185	if (ifup)
186		rtllib_start_protocol(ieee);
187out:
188	up(&ieee->wx_sem);
189	return ret;
190}
191EXPORT_SYMBOL(rtllib_wx_set_wap);
192
193int rtllib_wx_get_essid(struct rtllib_device *ieee, struct iw_request_info *a,
194			 union iwreq_data *wrqu, char *b)
195{
196	int len, ret = 0;
197	unsigned long flags;
198
199	if (ieee->iw_mode == IW_MODE_MONITOR)
200		return -1;
201
202	/* We want avoid to give to the user inconsistent infos*/
203	spin_lock_irqsave(&ieee->lock, flags);
204
205	if (ieee->current_network.ssid[0] == '\0' ||
206		ieee->current_network.ssid_len == 0) {
207		ret = -1;
208		goto out;
209	}
210
211	if (ieee->state != RTLLIB_LINKED &&
212		ieee->state != RTLLIB_LINKED_SCANNING &&
213		ieee->ssid_set == 0) {
214		ret = -1;
215		goto out;
216	}
217	len = ieee->current_network.ssid_len;
218	wrqu->essid.length = len;
219	strncpy(b, ieee->current_network.ssid, len);
220	wrqu->essid.flags = 1;
221
222out:
223	spin_unlock_irqrestore(&ieee->lock, flags);
224
225	return ret;
226
227}
228EXPORT_SYMBOL(rtllib_wx_get_essid);
229
230int rtllib_wx_set_rate(struct rtllib_device *ieee,
231			     struct iw_request_info *info,
232			     union iwreq_data *wrqu, char *extra)
233{
234
235	u32 target_rate = wrqu->bitrate.value;
236
237	ieee->rate = target_rate/100000;
238	return 0;
239}
240EXPORT_SYMBOL(rtllib_wx_set_rate);
241
242int rtllib_wx_get_rate(struct rtllib_device *ieee,
243			     struct iw_request_info *info,
244			     union iwreq_data *wrqu, char *extra)
245{
246	u32 tmp_rate = 0;
247
248	tmp_rate = TxCountToDataRate(ieee,
249				     ieee->softmac_stats.CurrentShowTxate);
250	wrqu->bitrate.value = tmp_rate * 500000;
251
252	return 0;
253}
254EXPORT_SYMBOL(rtllib_wx_get_rate);
255
256
257int rtllib_wx_set_rts(struct rtllib_device *ieee,
258			     struct iw_request_info *info,
259			     union iwreq_data *wrqu, char *extra)
260{
261	if (wrqu->rts.disabled || !wrqu->rts.fixed)
262		ieee->rts = DEFAULT_RTS_THRESHOLD;
263	else {
264		if (wrqu->rts.value < MIN_RTS_THRESHOLD ||
265				wrqu->rts.value > MAX_RTS_THRESHOLD)
266			return -EINVAL;
267		ieee->rts = wrqu->rts.value;
268	}
269	return 0;
270}
271EXPORT_SYMBOL(rtllib_wx_set_rts);
272
273int rtllib_wx_get_rts(struct rtllib_device *ieee,
274			     struct iw_request_info *info,
275			     union iwreq_data *wrqu, char *extra)
276{
277	wrqu->rts.value = ieee->rts;
278	wrqu->rts.fixed = 0;	/* no auto select */
279	wrqu->rts.disabled = (wrqu->rts.value == DEFAULT_RTS_THRESHOLD);
280	return 0;
281}
282EXPORT_SYMBOL(rtllib_wx_get_rts);
283
284int rtllib_wx_set_mode(struct rtllib_device *ieee, struct iw_request_info *a,
285			     union iwreq_data *wrqu, char *b)
286{
287	int set_mode_status = 0;
288
289	rtllib_stop_scan_syncro(ieee);
290	down(&ieee->wx_sem);
291	switch (wrqu->mode) {
292	case IW_MODE_MONITOR:
293	case IW_MODE_ADHOC:
294	case IW_MODE_INFRA:
295		break;
296	case IW_MODE_AUTO:
297		wrqu->mode = IW_MODE_INFRA;
298		break;
299	default:
300		set_mode_status = -EINVAL;
301		goto out;
302	}
303
304	if (wrqu->mode == ieee->iw_mode)
305		goto out;
306
307	if (wrqu->mode == IW_MODE_MONITOR) {
308		ieee->dev->type = ARPHRD_IEEE80211;
309		rtllib_EnableNetMonitorMode(ieee->dev, false);
310	} else {
311		ieee->dev->type = ARPHRD_ETHER;
312		if (ieee->iw_mode == IW_MODE_MONITOR)
313			rtllib_DisableNetMonitorMode(ieee->dev, false);
314	}
315
316	if (!ieee->proto_started) {
317		ieee->iw_mode = wrqu->mode;
318	} else {
319		rtllib_stop_protocol(ieee, true);
320		ieee->iw_mode = wrqu->mode;
321		rtllib_start_protocol(ieee);
322	}
323
324out:
325	up(&ieee->wx_sem);
326	return set_mode_status;
327}
328EXPORT_SYMBOL(rtllib_wx_set_mode);
329
330void rtllib_wx_sync_scan_wq(void *data)
331{
332	struct rtllib_device *ieee = container_of_work_rsl(data,
333				     struct rtllib_device, wx_sync_scan_wq);
334	short chan;
335	enum ht_extchnl_offset chan_offset = 0;
336	enum ht_channel_width bandwidth = 0;
337	int b40M = 0;
338
339	if (!(ieee->softmac_features & IEEE_SOFTMAC_SCAN)) {
340		rtllib_start_scan_syncro(ieee, 0);
341		goto out;
342	}
343
344	chan = ieee->current_network.channel;
345
346	if (ieee->LeisurePSLeave)
347		ieee->LeisurePSLeave(ieee->dev);
348	/* notify AP to be in PS mode */
349	rtllib_sta_ps_send_null_frame(ieee, 1);
350	rtllib_sta_ps_send_null_frame(ieee, 1);
351
352	rtllib_stop_all_queues(ieee);
353
354	if (ieee->data_hard_stop)
355		ieee->data_hard_stop(ieee->dev);
356	rtllib_stop_send_beacons(ieee);
357	ieee->state = RTLLIB_LINKED_SCANNING;
358	ieee->link_change(ieee->dev);
359	/* wait for ps packet to be kicked out successfully */
360	msleep(50);
361
362	if (ieee->ScanOperationBackupHandler)
363		ieee->ScanOperationBackupHandler(ieee->dev, SCAN_OPT_BACKUP);
364
365	if (ieee->pHTInfo->bCurrentHTSupport && ieee->pHTInfo->bEnableHT &&
366	    ieee->pHTInfo->bCurBW40MHz) {
367		b40M = 1;
368		chan_offset = ieee->pHTInfo->CurSTAExtChnlOffset;
369		bandwidth = (enum ht_channel_width)ieee->pHTInfo->bCurBW40MHz;
370		RT_TRACE(COMP_DBG, "Scan in 40M, force to 20M first:%d, %d\n",
371			 chan_offset, bandwidth);
372		ieee->SetBWModeHandler(ieee->dev, HT_CHANNEL_WIDTH_20,
373				       HT_EXTCHNL_OFFSET_NO_EXT);
374	}
375
376	rtllib_start_scan_syncro(ieee, 0);
377
378	if (b40M) {
379		RT_TRACE(COMP_DBG, "Scan in 20M, back to 40M\n");
380		if (chan_offset == HT_EXTCHNL_OFFSET_UPPER)
381			ieee->set_chan(ieee->dev, chan + 2);
382		else if (chan_offset == HT_EXTCHNL_OFFSET_LOWER)
383			ieee->set_chan(ieee->dev, chan - 2);
384		else
385			ieee->set_chan(ieee->dev, chan);
386		ieee->SetBWModeHandler(ieee->dev, bandwidth, chan_offset);
387	} else {
388		ieee->set_chan(ieee->dev, chan);
389	}
390
391	if (ieee->ScanOperationBackupHandler)
392		ieee->ScanOperationBackupHandler(ieee->dev, SCAN_OPT_RESTORE);
393
394	ieee->state = RTLLIB_LINKED;
395	ieee->link_change(ieee->dev);
396
397	/* Notify AP that I wake up again */
398	rtllib_sta_ps_send_null_frame(ieee, 0);
399
400	if (ieee->LinkDetectInfo.NumRecvBcnInPeriod == 0 ||
401	    ieee->LinkDetectInfo.NumRecvDataInPeriod == 0) {
402		ieee->LinkDetectInfo.NumRecvBcnInPeriod = 1;
403		ieee->LinkDetectInfo.NumRecvDataInPeriod = 1;
404	}
405
406	if (ieee->data_hard_resume)
407		ieee->data_hard_resume(ieee->dev);
408
409	if (ieee->iw_mode == IW_MODE_ADHOC || ieee->iw_mode == IW_MODE_MASTER)
410		rtllib_start_send_beacons(ieee);
411
412	rtllib_wake_all_queues(ieee);
413
414out:
415	up(&ieee->wx_sem);
416
417}
418
419int rtllib_wx_set_scan(struct rtllib_device *ieee, struct iw_request_info *a,
420			     union iwreq_data *wrqu, char *b)
421{
422	int ret = 0;
423
424	down(&ieee->wx_sem);
425
426	if (ieee->iw_mode == IW_MODE_MONITOR || !(ieee->proto_started)) {
427		ret = -1;
428		goto out;
429	}
430
431	if (ieee->state == RTLLIB_LINKED) {
432		queue_work_rsl(ieee->wq, &ieee->wx_sync_scan_wq);
433		/* intentionally forget to up sem */
434		return 0;
435	}
436
437out:
438	up(&ieee->wx_sem);
439	return ret;
440}
441EXPORT_SYMBOL(rtllib_wx_set_scan);
442
443int rtllib_wx_set_essid(struct rtllib_device *ieee,
444			struct iw_request_info *a,
445			union iwreq_data *wrqu, char *extra)
446{
447
448	int ret = 0, len, i;
449	short proto_started;
450	unsigned long flags;
451
452	rtllib_stop_scan_syncro(ieee);
453	down(&ieee->wx_sem);
454
455	proto_started = ieee->proto_started;
456
457	len = (wrqu->essid.length < IW_ESSID_MAX_SIZE) ? wrqu->essid.length :
458	       IW_ESSID_MAX_SIZE;
459
460	if (len > IW_ESSID_MAX_SIZE) {
461		ret = -E2BIG;
462		goto out;
463	}
464
465	if (ieee->iw_mode == IW_MODE_MONITOR) {
466		ret = -1;
467		goto out;
468	}
469
470	for (i = 0; i < len; i++) {
471		if (extra[i] < 0) {
472			ret = -1;
473			goto out;
474		}
475	}
476
477	if (proto_started)
478		rtllib_stop_protocol(ieee, true);
479
480
481	/* this is just to be sure that the GET wx callback
482	 * has consistent infos. not needed otherwise
483	 */
484	spin_lock_irqsave(&ieee->lock, flags);
485
486	if (wrqu->essid.flags && wrqu->essid.length) {
487		strncpy(ieee->current_network.ssid, extra, len);
488		ieee->current_network.ssid_len = len;
489		ieee->cannot_notify = false;
490		ieee->ssid_set = 1;
491	} else {
492		ieee->ssid_set = 0;
493		ieee->current_network.ssid[0] = '\0';
494		ieee->current_network.ssid_len = 0;
495	}
496	spin_unlock_irqrestore(&ieee->lock, flags);
497
498	if (proto_started)
499		rtllib_start_protocol(ieee);
500out:
501	up(&ieee->wx_sem);
502	return ret;
503}
504EXPORT_SYMBOL(rtllib_wx_set_essid);
505
506int rtllib_wx_get_mode(struct rtllib_device *ieee, struct iw_request_info *a,
507		       union iwreq_data *wrqu, char *b)
508{
509	wrqu->mode = ieee->iw_mode;
510	return 0;
511}
512EXPORT_SYMBOL(rtllib_wx_get_mode);
513
514int rtllib_wx_set_rawtx(struct rtllib_device *ieee,
515			struct iw_request_info *info,
516			union iwreq_data *wrqu, char *extra)
517{
518
519	int *parms = (int *)extra;
520	int enable = (parms[0] > 0);
521	short prev = ieee->raw_tx;
522
523	down(&ieee->wx_sem);
524
525	if (enable)
526		ieee->raw_tx = 1;
527	else
528		ieee->raw_tx = 0;
529
530	printk(KERN_INFO"raw TX is %s\n",
531	      ieee->raw_tx ? "enabled" : "disabled");
532
533	if (ieee->iw_mode == IW_MODE_MONITOR) {
534		if (prev == 0 && ieee->raw_tx) {
535			if (ieee->data_hard_resume)
536				ieee->data_hard_resume(ieee->dev);
537
538			netif_carrier_on(ieee->dev);
539		}
540
541		if (prev && ieee->raw_tx == 1)
542			netif_carrier_off(ieee->dev);
543	}
544
545	up(&ieee->wx_sem);
546
547	return 0;
548}
549EXPORT_SYMBOL(rtllib_wx_set_rawtx);
550
551int rtllib_wx_get_name(struct rtllib_device *ieee,
552			     struct iw_request_info *info,
553			     union iwreq_data *wrqu, char *extra)
554{
555	strcpy(wrqu->name, "802.11");
556
557	if (ieee->modulation & RTLLIB_CCK_MODULATION)
558		strcat(wrqu->name, "b");
559	if (ieee->modulation & RTLLIB_OFDM_MODULATION)
560		strcat(wrqu->name, "g");
561	if (ieee->mode & (IEEE_N_24G | IEEE_N_5G))
562		strcat(wrqu->name, "n");
563	return 0;
564}
565EXPORT_SYMBOL(rtllib_wx_get_name);
566
567
568/* this is mostly stolen from hostap */
569int rtllib_wx_set_power(struct rtllib_device *ieee,
570				 struct iw_request_info *info,
571				 union iwreq_data *wrqu, char *extra)
572{
573	int ret = 0;
574
575	if ((!ieee->sta_wake_up) ||
576	    (!ieee->enter_sleep_state) ||
577	    (!ieee->ps_is_queue_empty)) {
578		RTLLIB_DEBUG(RTLLIB_DL_ERR, "%s(): PS mode is tried to be use "
579			     "but driver missed a callback\n\n", __func__);
580		return -1;
581	}
582
583	down(&ieee->wx_sem);
584
585	if (wrqu->power.disabled) {
586		RT_TRACE(COMP_DBG, "===>%s(): power disable\n", __func__);
587		ieee->ps = RTLLIB_PS_DISABLED;
588		goto exit;
589	}
590	if (wrqu->power.flags & IW_POWER_TIMEOUT) {
591		ieee->ps_timeout = wrqu->power.value / 1000;
592		RT_TRACE(COMP_DBG, "===>%s():ps_timeout is %d\n", __func__,
593			 ieee->ps_timeout);
594	}
595
596	if (wrqu->power.flags & IW_POWER_PERIOD)
597		ieee->ps_period = wrqu->power.value / 1000;
598
599	switch (wrqu->power.flags & IW_POWER_MODE) {
600	case IW_POWER_UNICAST_R:
601		ieee->ps = RTLLIB_PS_UNICAST;
602		break;
603	case IW_POWER_MULTICAST_R:
604		ieee->ps = RTLLIB_PS_MBCAST;
605		break;
606	case IW_POWER_ALL_R:
607		ieee->ps = RTLLIB_PS_UNICAST | RTLLIB_PS_MBCAST;
608		break;
609
610	case IW_POWER_ON:
611		break;
612
613	default:
614		ret = -EINVAL;
615		goto exit;
616
617	}
618exit:
619	up(&ieee->wx_sem);
620	return ret;
621
622}
623EXPORT_SYMBOL(rtllib_wx_set_power);
624
625/* this is stolen from hostap */
626int rtllib_wx_get_power(struct rtllib_device *ieee,
627				 struct iw_request_info *info,
628				 union iwreq_data *wrqu, char *extra)
629{
630	down(&ieee->wx_sem);
631
632	if (ieee->ps == RTLLIB_PS_DISABLED) {
633		wrqu->power.disabled = 1;
634		goto exit;
635	}
636
637	wrqu->power.disabled = 0;
638
639	if ((wrqu->power.flags & IW_POWER_TYPE) == IW_POWER_TIMEOUT) {
640		wrqu->power.flags = IW_POWER_TIMEOUT;
641		wrqu->power.value = ieee->ps_timeout * 1000;
642	} else {
643		wrqu->power.flags = IW_POWER_PERIOD;
644		wrqu->power.value = ieee->ps_period * 1000;
645	}
646
647	if ((ieee->ps & (RTLLIB_PS_MBCAST | RTLLIB_PS_UNICAST)) ==
648	    (RTLLIB_PS_MBCAST | RTLLIB_PS_UNICAST))
649		wrqu->power.flags |= IW_POWER_ALL_R;
650	else if (ieee->ps & RTLLIB_PS_MBCAST)
651		wrqu->power.flags |= IW_POWER_MULTICAST_R;
652	else
653		wrqu->power.flags |= IW_POWER_UNICAST_R;
654
655exit:
656	up(&ieee->wx_sem);
657	return 0;
658
659}
660EXPORT_SYMBOL(rtllib_wx_get_power);
661