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