• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /asuswrt-rt-n18u-9.0.0.4.380.2695/release/src-rt-6.x.4708/linux/linux-2.6/drivers/staging/rtl8192e/ieee80211/
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#ifdef ENABLE_DOT11D
19#include "dot11d.h"
20#endif
21
22const long ieee80211_wlan_frequencies[] = {
23	2412, 2417, 2422, 2427,
24	2432, 2437, 2442, 2447,
25	2452, 2457, 2462, 2467,
26	2472, 2484
27};
28
29
30int ieee80211_wx_set_freq(struct ieee80211_device *ieee, struct iw_request_info *a,
31			     union iwreq_data *wrqu, char *b)
32{
33	int ret;
34	struct iw_freq *fwrq = & wrqu->freq;
35
36	down(&ieee->wx_sem);
37
38	if(ieee->iw_mode == IW_MODE_INFRA){
39		ret = -EOPNOTSUPP;
40		goto out;
41	}
42
43	/* if setting by freq convert to channel */
44	if (fwrq->e == 1) {
45		if ((fwrq->m >= (int) 2.412e8 &&
46		     fwrq->m <= (int) 2.487e8)) {
47			int f = fwrq->m / 100000;
48			int c = 0;
49
50			while ((c < 14) && (f != ieee80211_wlan_frequencies[c]))
51				c++;
52
53			/* hack to fall through */
54			fwrq->e = 0;
55			fwrq->m = c + 1;
56		}
57	}
58
59	if (fwrq->e > 0 || fwrq->m > 14 || fwrq->m < 1 ){
60		ret = -EOPNOTSUPP;
61		goto out;
62
63	}else { /* Set the channel */
64
65#ifdef ENABLE_DOT11D
66		if (!(GET_DOT11D_INFO(ieee)->channel_map)[fwrq->m]) {
67			ret = -EINVAL;
68			goto out;
69		}
70#endif
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 || ieee->iw_mode == IW_MODE_MASTER)
75			if(ieee->state == IEEE80211_LINKED){
76
77			ieee80211_stop_send_beacons(ieee);
78			ieee80211_start_send_beacons(ieee);
79			}
80	}
81
82	ret = 0;
83out:
84	up(&ieee->wx_sem);
85	return ret;
86}
87
88
89int ieee80211_wx_get_freq(struct ieee80211_device *ieee,
90			     struct iw_request_info *a,
91			     union iwreq_data *wrqu, char *b)
92{
93	struct iw_freq *fwrq = & wrqu->freq;
94
95	if (ieee->current_network.channel == 0)
96		return -1;
97	//NM 0.7.0 will not accept channel any more.
98	fwrq->m = ieee80211_wlan_frequencies[ieee->current_network.channel-1] * 100000;
99	fwrq->e = 1;
100//	fwrq->m = ieee->current_network.channel;
101//	fwrq->e = 0;
102
103	return 0;
104}
105
106int ieee80211_wx_get_wap(struct ieee80211_device *ieee,
107			    struct iw_request_info *info,
108			    union iwreq_data *wrqu, char *extra)
109{
110	unsigned long flags;
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 != IEEE80211_LINKED &&
120		ieee->state != IEEE80211_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}
132
133
134int ieee80211_wx_set_wap(struct ieee80211_device *ieee,
135			 struct iw_request_info *info,
136			 union iwreq_data *awrq,
137			 char *extra)
138{
139
140	int ret = 0;
141	u8 zero[] = {0,0,0,0,0,0};
142	unsigned long flags;
143
144	short ifup = ieee->proto_started;//dev->flags & IFF_UP;
145	struct sockaddr *temp = (struct sockaddr *)awrq;
146
147	ieee->sync_scan_hurryup = 1;
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 (ifup)
162		ieee80211_stop_protocol(ieee,true);
163
164	/* just to avoid to give inconsistent infos in the
165	 * get wx method. not really needed otherwise
166	 */
167	spin_lock_irqsave(&ieee->lock, flags);
168
169	memcpy(ieee->current_network.bssid, temp->sa_data, ETH_ALEN);
170	ieee->wap_set = memcmp(temp->sa_data, zero,ETH_ALEN)!=0;
171
172	spin_unlock_irqrestore(&ieee->lock, flags);
173
174	if (ifup)
175		ieee80211_start_protocol(ieee);
176out:
177	up(&ieee->wx_sem);
178	return ret;
179}
180
181 int ieee80211_wx_get_essid(struct ieee80211_device *ieee, struct iw_request_info *a,union iwreq_data *wrqu,char *b)
182{
183	int len,ret = 0;
184	unsigned long flags;
185
186	if (ieee->iw_mode == IW_MODE_MONITOR)
187		return -1;
188
189	/* We want avoid to give to the user inconsistent infos*/
190	spin_lock_irqsave(&ieee->lock, flags);
191
192	if (ieee->current_network.ssid[0] == '\0' ||
193		ieee->current_network.ssid_len == 0){
194		ret = -1;
195		goto out;
196	}
197
198	if (ieee->state != IEEE80211_LINKED &&
199		ieee->state != IEEE80211_LINKED_SCANNING &&
200		ieee->ssid_set == 0){
201		ret = -1;
202		goto out;
203	}
204	len = ieee->current_network.ssid_len;
205	wrqu->essid.length = len;
206	strncpy(b,ieee->current_network.ssid,len);
207	wrqu->essid.flags = 1;
208
209out:
210	spin_unlock_irqrestore(&ieee->lock, flags);
211
212	return ret;
213
214}
215
216int ieee80211_wx_set_rate(struct ieee80211_device *ieee,
217			     struct iw_request_info *info,
218			     union iwreq_data *wrqu, char *extra)
219{
220
221	u32 target_rate = wrqu->bitrate.value;
222
223	ieee->rate = target_rate/100000;
224	return 0;
225}
226
227
228
229int ieee80211_wx_get_rate(struct ieee80211_device *ieee,
230			     struct iw_request_info *info,
231			     union iwreq_data *wrqu, char *extra)
232{
233	u32 tmp_rate;
234	tmp_rate = TxCountToDataRate(ieee, ieee->softmac_stats.CurrentShowTxate);
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,true);
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
307#ifdef ENABLE_LPS
308	if (ieee->LeisurePSLeave) {
309		ieee->LeisurePSLeave(ieee->dev);
310	}
311
312	/* notify AP to be in PS mode */
313	ieee80211_sta_ps_send_null_frame(ieee, 1);
314	ieee80211_sta_ps_send_null_frame(ieee, 1);
315#endif
316
317	if (ieee->data_hard_stop)
318		ieee->data_hard_stop(ieee->dev);
319
320	ieee80211_stop_send_beacons(ieee);
321
322	ieee->state = IEEE80211_LINKED_SCANNING;
323	ieee->link_change(ieee->dev);
324	ieee->InitialGainHandler(ieee->dev,IG_Backup);
325	if (ieee->pHTInfo->bCurrentHTSupport && ieee->pHTInfo->bEnableHT && ieee->pHTInfo->bCurBW40MHz) {
326		b40M = 1;
327		chan_offset = ieee->pHTInfo->CurSTAExtChnlOffset;
328		bandwidth = (HT_CHANNEL_WIDTH)ieee->pHTInfo->bCurBW40MHz;
329		printk("Scan in 40M, force to 20M first:%d, %d\n", chan_offset, bandwidth);
330		ieee->SetBWModeHandler(ieee->dev, HT_CHANNEL_WIDTH_20, HT_EXTCHNL_OFFSET_NO_EXT);
331		}
332	ieee80211_start_scan_syncro(ieee);
333	if (b40M) {
334		printk("Scan in 20M, back to 40M\n");
335		if (chan_offset == HT_EXTCHNL_OFFSET_UPPER)
336			ieee->set_chan(ieee->dev, chan + 2);
337		else if (chan_offset == HT_EXTCHNL_OFFSET_LOWER)
338			ieee->set_chan(ieee->dev, chan - 2);
339		else
340			ieee->set_chan(ieee->dev, chan);
341		ieee->SetBWModeHandler(ieee->dev, bandwidth, chan_offset);
342	} else {
343		ieee->set_chan(ieee->dev, chan);
344	}
345
346	ieee->InitialGainHandler(ieee->dev,IG_Restore);
347	ieee->state = IEEE80211_LINKED;
348	ieee->link_change(ieee->dev);
349
350#ifdef ENABLE_LPS
351	/* Notify AP that I wake up again */
352	ieee80211_sta_ps_send_null_frame(ieee, 0);
353#endif
354
355	// To prevent the immediately calling watch_dog after scan.
356	if(ieee->LinkDetectInfo.NumRecvBcnInPeriod==0||ieee->LinkDetectInfo.NumRecvDataInPeriod==0 )
357	{
358		ieee->LinkDetectInfo.NumRecvBcnInPeriod = 1;
359		ieee->LinkDetectInfo.NumRecvDataInPeriod= 1;
360	}
361	if (ieee->data_hard_resume)
362		ieee->data_hard_resume(ieee->dev);
363
364	if(ieee->iw_mode == IW_MODE_ADHOC || ieee->iw_mode == IW_MODE_MASTER)
365		ieee80211_start_send_beacons(ieee);
366
367	count = 0;
368	up(&ieee->wx_sem);
369
370}
371
372int ieee80211_wx_set_scan(struct ieee80211_device *ieee, struct iw_request_info *a,
373			     union iwreq_data *wrqu, char *b)
374{
375	int ret = 0;
376
377	down(&ieee->wx_sem);
378
379	if (ieee->iw_mode == IW_MODE_MONITOR || !(ieee->proto_started)){
380		ret = -1;
381		goto out;
382	}
383
384	if ( ieee->state == IEEE80211_LINKED){
385		queue_work(ieee->wq, &ieee->wx_sync_scan_wq);
386		/* intentionally forget to up sem */
387		return 0;
388	}
389
390out:
391	up(&ieee->wx_sem);
392	return ret;
393}
394
395int ieee80211_wx_set_essid(struct ieee80211_device *ieee,
396			      struct iw_request_info *a,
397			      union iwreq_data *wrqu, char *extra)
398{
399
400	int ret=0,len;
401	short proto_started;
402	unsigned long flags;
403
404	ieee->sync_scan_hurryup = 1;
405	down(&ieee->wx_sem);
406
407	proto_started = ieee->proto_started;
408
409	if (wrqu->essid.length > IW_ESSID_MAX_SIZE){
410		ret= -E2BIG;
411		goto out;
412	}
413
414	if (ieee->iw_mode == IW_MODE_MONITOR){
415		ret= -1;
416		goto out;
417	}
418
419	if(proto_started){
420		ieee80211_stop_protocol(ieee,true);
421	}
422
423
424	/* this is just to be sure that the GET wx callback
425	 * has consisten infos. not needed otherwise
426	 */
427	spin_lock_irqsave(&ieee->lock, flags);
428
429	if (wrqu->essid.flags && wrqu->essid.length) {
430		//first flush current network.ssid
431		len = ((wrqu->essid.length-1) < IW_ESSID_MAX_SIZE) ? (wrqu->essid.length-1) : IW_ESSID_MAX_SIZE;
432		strncpy(ieee->current_network.ssid, extra, len+1);
433		ieee->current_network.ssid_len = len+1;
434		ieee->ssid_set = 1;
435	}
436	else{
437		ieee->ssid_set = 0;
438		ieee->current_network.ssid[0] = '\0';
439		ieee->current_network.ssid_len = 0;
440	}
441	spin_unlock_irqrestore(&ieee->lock, flags);
442
443	if (proto_started)
444		ieee80211_start_protocol(ieee);
445out:
446	up(&ieee->wx_sem);
447	return ret;
448}
449
450 int ieee80211_wx_get_mode(struct ieee80211_device *ieee, struct iw_request_info *a,
451			     union iwreq_data *wrqu, char *b)
452{
453
454	wrqu->mode = ieee->iw_mode;
455	return 0;
456}
457
458 int ieee80211_wx_set_rawtx(struct ieee80211_device *ieee,
459			       struct iw_request_info *info,
460			       union iwreq_data *wrqu, char *extra)
461{
462
463	int *parms = (int *)extra;
464	int enable = (parms[0] > 0);
465	short prev = ieee->raw_tx;
466
467	down(&ieee->wx_sem);
468
469	if(enable)
470		ieee->raw_tx = 1;
471	else
472		ieee->raw_tx = 0;
473
474	printk(KERN_INFO"raw TX is %s\n",
475	      ieee->raw_tx ? "enabled" : "disabled");
476
477	if(ieee->iw_mode == IW_MODE_MONITOR)
478	{
479		if(prev == 0 && ieee->raw_tx){
480			if (ieee->data_hard_resume)
481				ieee->data_hard_resume(ieee->dev);
482
483			netif_carrier_on(ieee->dev);
484		}
485
486		if(prev && ieee->raw_tx == 1)
487			netif_carrier_off(ieee->dev);
488	}
489
490	up(&ieee->wx_sem);
491
492	return 0;
493}
494
495int ieee80211_wx_get_name(struct ieee80211_device *ieee,
496			     struct iw_request_info *info,
497			     union iwreq_data *wrqu, char *extra)
498{
499	strcpy(wrqu->name, "802.11");
500	if(ieee->modulation & IEEE80211_CCK_MODULATION)
501		strcat(wrqu->name, "b");
502	if(ieee->modulation & IEEE80211_OFDM_MODULATION)
503		strcat(wrqu->name, "g");
504	if (ieee->mode & (IEEE_N_24G | IEEE_N_5G))
505		strcat(wrqu->name, "n");
506	return 0;
507}
508
509
510/* this is mostly stolen from hostap */
511int ieee80211_wx_set_power(struct ieee80211_device *ieee,
512				 struct iw_request_info *info,
513				 union iwreq_data *wrqu, char *extra)
514{
515	int ret = 0;
516	if(
517		(!ieee->sta_wake_up) ||
518	//	(!ieee->ps_request_tx_ack) ||
519		(!ieee->enter_sleep_state) ||
520		(!ieee->ps_is_queue_empty)){
521
522	//	printk("ERROR. PS mode is tryied to be use but driver missed a callback\n\n");
523
524		return -1;
525	}
526	down(&ieee->wx_sem);
527
528	if (wrqu->power.disabled){
529		ieee->ps = IEEE80211_PS_DISABLED;
530		goto exit;
531	}
532	if (wrqu->power.flags & IW_POWER_TIMEOUT) {
533		//ieee->ps_period = wrqu->power.value / 1000;
534		ieee->ps_timeout = wrqu->power.value / 1000;
535	}
536
537	if (wrqu->power.flags & IW_POWER_PERIOD) {
538
539		//ieee->ps_timeout = wrqu->power.value / 1000;
540		ieee->ps_period = wrqu->power.value / 1000;
541		//wrq->value / 1024;
542
543	}
544	switch (wrqu->power.flags & IW_POWER_MODE) {
545	case IW_POWER_UNICAST_R:
546		ieee->ps = IEEE80211_PS_UNICAST;
547		break;
548	case IW_POWER_MULTICAST_R:
549		ieee->ps = IEEE80211_PS_MBCAST;
550		break;
551	case IW_POWER_ALL_R:
552		ieee->ps = IEEE80211_PS_UNICAST | IEEE80211_PS_MBCAST;
553		break;
554
555	case IW_POWER_ON:
556	//	ieee->ps = IEEE80211_PS_DISABLED;
557		break;
558
559	default:
560		ret = -EINVAL;
561		goto exit;
562
563	}
564exit:
565	up(&ieee->wx_sem);
566	return ret;
567
568}
569
570/* this is stolen from hostap */
571int ieee80211_wx_get_power(struct ieee80211_device *ieee,
572				 struct iw_request_info *info,
573				 union iwreq_data *wrqu, char *extra)
574{
575	int ret =0;
576
577	down(&ieee->wx_sem);
578
579	if(ieee->ps == IEEE80211_PS_DISABLED){
580		wrqu->power.disabled = 1;
581		goto exit;
582	}
583
584	wrqu->power.disabled = 0;
585
586	if ((wrqu->power.flags & IW_POWER_TYPE) == IW_POWER_TIMEOUT) {
587		wrqu->power.flags = IW_POWER_TIMEOUT;
588		wrqu->power.value = ieee->ps_timeout * 1000;
589	} else {
590//		ret = -EOPNOTSUPP;
591//		goto exit;
592		wrqu->power.flags = IW_POWER_PERIOD;
593		wrqu->power.value = ieee->ps_period * 1000;
594//ieee->current_network.dtim_period * ieee->current_network.beacon_interval * 1024;
595	}
596
597       if ((ieee->ps & (IEEE80211_PS_MBCAST | IEEE80211_PS_UNICAST)) == (IEEE80211_PS_MBCAST | IEEE80211_PS_UNICAST))
598	   	wrqu->power.flags |= IW_POWER_ALL_R;
599	else if (ieee->ps & IEEE80211_PS_MBCAST)
600		wrqu->power.flags |= IW_POWER_MULTICAST_R;
601	else
602		wrqu->power.flags |= IW_POWER_UNICAST_R;
603
604exit:
605	up(&ieee->wx_sem);
606	return ret;
607
608}
609