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