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