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