2 * cfg80211 scan result handling
4 * Copyright 2008 Johannes Berg <johannes@sipsolutions.net>
6 #include <linux/kernel.h>
7 #include <linux/module.h>
8 #include <linux/netdevice.h>
9 #include <linux/wireless.h>
10 #include <linux/nl80211.h>
11 #include <linux/etherdevice.h>
13 #include <net/cfg80211.h>
14 #include <net/iw_handler.h>
18 #define IEEE80211_SCAN_RESULT_EXPIRE (10 * HZ)
20 void cfg80211_scan_done(struct cfg80211_scan_request *request, bool aborted)
22 struct net_device *dev;
23 #ifdef CONFIG_WIRELESS_EXT
24 union iwreq_data wrqu;
27 dev = dev_get_by_index(&init_net, request->ifidx);
31 WARN_ON(request != wiphy_to_dev(request->wiphy)->scan_req);
32 wiphy_to_dev(request->wiphy)->scan_req = NULL;
35 nl80211_send_scan_aborted(wiphy_to_dev(request->wiphy), dev);
37 nl80211_send_scan_done(wiphy_to_dev(request->wiphy), dev);
39 #ifdef CONFIG_WIRELESS_EXT
41 memset(&wrqu, 0, sizeof(wrqu));
43 wireless_send_event(dev, SIOCGIWSCAN, &wrqu, NULL);
52 EXPORT_SYMBOL(cfg80211_scan_done);
54 static void bss_release(struct kref *ref)
56 struct cfg80211_internal_bss *bss;
58 bss = container_of(ref, struct cfg80211_internal_bss, ref);
59 if (bss->pub.free_priv)
60 bss->pub.free_priv(&bss->pub);
64 /* must hold dev->bss_lock! */
65 void cfg80211_bss_age(struct cfg80211_registered_device *dev,
66 unsigned long age_secs)
68 struct cfg80211_internal_bss *bss;
69 unsigned long age_jiffies = msecs_to_jiffies(age_secs * MSEC_PER_SEC);
71 list_for_each_entry(bss, &dev->bss_list, list) {
72 bss->ts -= age_jiffies;
76 /* must hold dev->bss_lock! */
77 void cfg80211_bss_expire(struct cfg80211_registered_device *dev)
79 struct cfg80211_internal_bss *bss, *tmp;
82 list_for_each_entry_safe(bss, tmp, &dev->bss_list, list) {
84 !time_after(jiffies, bss->ts + IEEE80211_SCAN_RESULT_EXPIRE))
87 rb_erase(&bss->rbn, &dev->bss_tree);
88 kref_put(&bss->ref, bss_release);
93 dev->bss_generation++;
96 static u8 *find_ie(u8 num, u8 *ies, size_t len)
98 while (len > 2 && ies[0] != num) {
104 if (len < 2 + ies[1])
109 static int cmp_ies(u8 num, u8 *ies1, size_t len1, u8 *ies2, size_t len2)
111 const u8 *ie1 = find_ie(num, ies1, len1);
112 const u8 *ie2 = find_ie(num, ies2, len2);
120 r = memcmp(ie1 + 2, ie2 + 2, min(ie1[1], ie2[1]));
121 if (r == 0 && ie1[1] != ie2[1])
122 return ie2[1] - ie1[1];
126 static bool is_bss(struct cfg80211_bss *a,
128 const u8 *ssid, size_t ssid_len)
132 if (bssid && compare_ether_addr(a->bssid, bssid))
138 ssidie = find_ie(WLAN_EID_SSID,
139 a->information_elements,
140 a->len_information_elements);
143 if (ssidie[1] != ssid_len)
145 return memcmp(ssidie + 2, ssid, ssid_len) == 0;
148 static bool is_mesh(struct cfg80211_bss *a,
149 const u8 *meshid, size_t meshidlen,
154 if (!is_zero_ether_addr(a->bssid))
157 ie = find_ie(WLAN_EID_MESH_ID,
158 a->information_elements,
159 a->len_information_elements);
162 if (ie[1] != meshidlen)
164 if (memcmp(ie + 2, meshid, meshidlen))
167 ie = find_ie(WLAN_EID_MESH_CONFIG,
168 a->information_elements,
169 a->len_information_elements);
170 if (ie[1] != IEEE80211_MESH_CONFIG_LEN)
174 * Ignore mesh capability (last two bytes of the IE) when
175 * comparing since that may differ between stations taking
176 * part in the same mesh.
178 return memcmp(ie + 2, meshcfg, IEEE80211_MESH_CONFIG_LEN - 2) == 0;
181 static int cmp_bss(struct cfg80211_bss *a,
182 struct cfg80211_bss *b)
186 if (a->channel != b->channel)
187 return b->channel->center_freq - a->channel->center_freq;
189 r = memcmp(a->bssid, b->bssid, ETH_ALEN);
193 if (is_zero_ether_addr(a->bssid)) {
194 r = cmp_ies(WLAN_EID_MESH_ID,
195 a->information_elements,
196 a->len_information_elements,
197 b->information_elements,
198 b->len_information_elements);
201 return cmp_ies(WLAN_EID_MESH_CONFIG,
202 a->information_elements,
203 a->len_information_elements,
204 b->information_elements,
205 b->len_information_elements);
208 return cmp_ies(WLAN_EID_SSID,
209 a->information_elements,
210 a->len_information_elements,
211 b->information_elements,
212 b->len_information_elements);
215 struct cfg80211_bss *cfg80211_get_bss(struct wiphy *wiphy,
216 struct ieee80211_channel *channel,
218 const u8 *ssid, size_t ssid_len,
219 u16 capa_mask, u16 capa_val)
221 struct cfg80211_registered_device *dev = wiphy_to_dev(wiphy);
222 struct cfg80211_internal_bss *bss, *res = NULL;
224 spin_lock_bh(&dev->bss_lock);
226 list_for_each_entry(bss, &dev->bss_list, list) {
227 if ((bss->pub.capability & capa_mask) != capa_val)
229 if (channel && bss->pub.channel != channel)
231 if (is_bss(&bss->pub, bssid, ssid, ssid_len)) {
238 spin_unlock_bh(&dev->bss_lock);
243 EXPORT_SYMBOL(cfg80211_get_bss);
245 struct cfg80211_bss *cfg80211_get_mesh(struct wiphy *wiphy,
246 struct ieee80211_channel *channel,
247 const u8 *meshid, size_t meshidlen,
250 struct cfg80211_registered_device *dev = wiphy_to_dev(wiphy);
251 struct cfg80211_internal_bss *bss, *res = NULL;
253 spin_lock_bh(&dev->bss_lock);
255 list_for_each_entry(bss, &dev->bss_list, list) {
256 if (channel && bss->pub.channel != channel)
258 if (is_mesh(&bss->pub, meshid, meshidlen, meshcfg)) {
265 spin_unlock_bh(&dev->bss_lock);
270 EXPORT_SYMBOL(cfg80211_get_mesh);
273 static void rb_insert_bss(struct cfg80211_registered_device *dev,
274 struct cfg80211_internal_bss *bss)
276 struct rb_node **p = &dev->bss_tree.rb_node;
277 struct rb_node *parent = NULL;
278 struct cfg80211_internal_bss *tbss;
283 tbss = rb_entry(parent, struct cfg80211_internal_bss, rbn);
285 cmp = cmp_bss(&bss->pub, &tbss->pub);
288 /* will sort of leak this BSS */
298 rb_link_node(&bss->rbn, parent, p);
299 rb_insert_color(&bss->rbn, &dev->bss_tree);
302 static struct cfg80211_internal_bss *
303 rb_find_bss(struct cfg80211_registered_device *dev,
304 struct cfg80211_internal_bss *res)
306 struct rb_node *n = dev->bss_tree.rb_node;
307 struct cfg80211_internal_bss *bss;
311 bss = rb_entry(n, struct cfg80211_internal_bss, rbn);
312 r = cmp_bss(&res->pub, &bss->pub);
325 static struct cfg80211_internal_bss *
326 cfg80211_bss_update(struct cfg80211_registered_device *dev,
327 struct cfg80211_internal_bss *res,
330 struct cfg80211_internal_bss *found = NULL;
331 const u8 *meshid, *meshcfg;
334 * The reference to "res" is donated to this function.
337 if (WARN_ON(!res->pub.channel)) {
338 kref_put(&res->ref, bss_release);
344 if (is_zero_ether_addr(res->pub.bssid)) {
345 /* must be mesh, verify */
346 meshid = find_ie(WLAN_EID_MESH_ID, res->pub.information_elements,
347 res->pub.len_information_elements);
348 meshcfg = find_ie(WLAN_EID_MESH_CONFIG,
349 res->pub.information_elements,
350 res->pub.len_information_elements);
351 if (!meshid || !meshcfg ||
352 meshcfg[1] != IEEE80211_MESH_CONFIG_LEN) {
354 kref_put(&res->ref, bss_release);
359 spin_lock_bh(&dev->bss_lock);
361 found = rb_find_bss(dev, res);
363 if (found && overwrite) {
364 list_replace(&found->list, &res->list);
365 rb_replace_node(&found->rbn, &res->rbn,
367 /* XXX: workaround */
368 res->hold = found->hold;
369 kref_put(&found->ref, bss_release);
372 kref_get(&found->ref);
373 found->pub.beacon_interval = res->pub.beacon_interval;
374 found->pub.tsf = res->pub.tsf;
375 found->pub.signal = res->pub.signal;
376 found->pub.capability = res->pub.capability;
378 kref_put(&res->ref, bss_release);
380 /* this "consumes" the reference */
381 list_add_tail(&res->list, &dev->bss_list);
382 rb_insert_bss(dev, res);
386 dev->bss_generation++;
387 spin_unlock_bh(&dev->bss_lock);
389 kref_get(&found->ref);
393 struct cfg80211_bss *
394 cfg80211_inform_bss_frame(struct wiphy *wiphy,
395 struct ieee80211_channel *channel,
396 struct ieee80211_mgmt *mgmt, size_t len,
397 s32 signal, gfp_t gfp)
399 struct cfg80211_internal_bss *res;
400 size_t ielen = len - offsetof(struct ieee80211_mgmt,
401 u.probe_resp.variable);
403 size_t privsz = wiphy->bss_priv_size;
405 if (WARN_ON(wiphy->signal_type == NL80211_BSS_SIGNAL_UNSPEC &&
406 (signal < 0 || signal > 100)))
409 if (WARN_ON(!mgmt || !wiphy ||
410 len < offsetof(struct ieee80211_mgmt, u.probe_resp.variable)))
413 res = kzalloc(sizeof(*res) + privsz + ielen, gfp);
417 memcpy(res->pub.bssid, mgmt->bssid, ETH_ALEN);
418 res->pub.channel = channel;
419 res->pub.signal = signal;
420 res->pub.tsf = le64_to_cpu(mgmt->u.probe_resp.timestamp);
421 res->pub.beacon_interval = le16_to_cpu(mgmt->u.probe_resp.beacon_int);
422 res->pub.capability = le16_to_cpu(mgmt->u.probe_resp.capab_info);
423 /* point to after the private area */
424 res->pub.information_elements = (u8 *)res + sizeof(*res) + privsz;
425 memcpy(res->pub.information_elements, mgmt->u.probe_resp.variable, ielen);
426 res->pub.len_information_elements = ielen;
428 kref_init(&res->ref);
430 overwrite = ieee80211_is_probe_resp(mgmt->frame_control);
432 res = cfg80211_bss_update(wiphy_to_dev(wiphy), res, overwrite);
436 if (res->pub.capability & WLAN_CAPABILITY_ESS)
437 regulatory_hint_found_beacon(wiphy, channel, gfp);
439 /* cfg80211_bss_update gives us a referenced result */
442 EXPORT_SYMBOL(cfg80211_inform_bss_frame);
444 void cfg80211_put_bss(struct cfg80211_bss *pub)
446 struct cfg80211_internal_bss *bss;
451 bss = container_of(pub, struct cfg80211_internal_bss, pub);
452 kref_put(&bss->ref, bss_release);
454 EXPORT_SYMBOL(cfg80211_put_bss);
456 void cfg80211_unlink_bss(struct wiphy *wiphy, struct cfg80211_bss *pub)
458 struct cfg80211_registered_device *dev = wiphy_to_dev(wiphy);
459 struct cfg80211_internal_bss *bss;
464 bss = container_of(pub, struct cfg80211_internal_bss, pub);
466 spin_lock_bh(&dev->bss_lock);
468 list_del(&bss->list);
469 rb_erase(&bss->rbn, &dev->bss_tree);
471 spin_unlock_bh(&dev->bss_lock);
473 kref_put(&bss->ref, bss_release);
475 EXPORT_SYMBOL(cfg80211_unlink_bss);
477 void cfg80211_hold_bss(struct cfg80211_bss *pub)
479 struct cfg80211_internal_bss *bss;
484 bss = container_of(pub, struct cfg80211_internal_bss, pub);
487 EXPORT_SYMBOL(cfg80211_hold_bss);
489 void cfg80211_unhold_bss(struct cfg80211_bss *pub)
491 struct cfg80211_internal_bss *bss;
496 bss = container_of(pub, struct cfg80211_internal_bss, pub);
499 EXPORT_SYMBOL(cfg80211_unhold_bss);
501 #ifdef CONFIG_WIRELESS_EXT
502 int cfg80211_wext_siwscan(struct net_device *dev,
503 struct iw_request_info *info,
504 union iwreq_data *wrqu, char *extra)
506 struct cfg80211_registered_device *rdev;
508 struct iw_scan_req *wreq = NULL;
509 struct cfg80211_scan_request *creq;
510 int i, err, n_channels = 0;
511 enum ieee80211_band band;
513 if (!netif_running(dev))
516 rdev = cfg80211_get_dev_from_ifindex(dev->ifindex);
519 return PTR_ERR(rdev);
521 if (rdev->scan_req) {
526 wiphy = &rdev->wiphy;
528 for (band = 0; band < IEEE80211_NUM_BANDS; band++)
529 if (wiphy->bands[band])
530 n_channels += wiphy->bands[band]->n_channels;
532 creq = kzalloc(sizeof(*creq) + sizeof(struct cfg80211_ssid) +
533 n_channels * sizeof(void *),
541 creq->ifidx = dev->ifindex;
542 creq->ssids = (void *)(creq + 1);
543 creq->channels = (void *)(creq->ssids + 1);
544 creq->n_channels = n_channels;
549 for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
551 if (!wiphy->bands[band])
553 for (j = 0; j < wiphy->bands[band]->n_channels; j++) {
554 creq->channels[i] = &wiphy->bands[band]->channels[j];
559 /* translate scan request */
560 if (wrqu->data.length == sizeof(struct iw_scan_req)) {
561 wreq = (struct iw_scan_req *)extra;
563 if (wrqu->data.flags & IW_SCAN_THIS_ESSID) {
564 if (wreq->essid_len > IEEE80211_MAX_SSID_LEN)
566 memcpy(creq->ssids[0].ssid, wreq->essid, wreq->essid_len);
567 creq->ssids[0].ssid_len = wreq->essid_len;
569 if (wreq->scan_type == IW_SCAN_TYPE_PASSIVE)
573 rdev->scan_req = creq;
574 err = rdev->ops->scan(wiphy, dev, creq);
576 rdev->scan_req = NULL;
580 cfg80211_put_dev(rdev);
583 EXPORT_SYMBOL(cfg80211_wext_siwscan);
585 static void ieee80211_scan_add_ies(struct iw_request_info *info,
586 struct cfg80211_bss *bss,
587 char **current_ev, char *end_buf)
589 u8 *pos, *end, *next;
592 if (!bss->information_elements ||
593 !bss->len_information_elements)
597 * If needed, fragment the IEs buffer (at IE boundaries) into short
598 * enough fragments to fit into IW_GENERIC_IE_MAX octet messages.
600 pos = bss->information_elements;
601 end = pos + bss->len_information_elements;
603 while (end - pos > IW_GENERIC_IE_MAX) {
604 next = pos + 2 + pos[1];
605 while (next + 2 + next[1] - pos < IW_GENERIC_IE_MAX)
606 next = next + 2 + next[1];
608 memset(&iwe, 0, sizeof(iwe));
610 iwe.u.data.length = next - pos;
611 *current_ev = iwe_stream_add_point(info, *current_ev,
618 memset(&iwe, 0, sizeof(iwe));
620 iwe.u.data.length = end - pos;
621 *current_ev = iwe_stream_add_point(info, *current_ev,
626 static inline unsigned int elapsed_jiffies_msecs(unsigned long start)
628 unsigned long end = jiffies;
631 return jiffies_to_msecs(end - start);
633 return jiffies_to_msecs(end + (MAX_JIFFY_OFFSET - start) + 1);
637 ieee80211_bss(struct wiphy *wiphy, struct iw_request_info *info,
638 struct cfg80211_internal_bss *bss, char *current_ev,
643 u8 *ie = bss->pub.information_elements;
644 int rem = bss->pub.len_information_elements, i, sig;
647 memset(&iwe, 0, sizeof(iwe));
649 iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
650 memcpy(iwe.u.ap_addr.sa_data, bss->pub.bssid, ETH_ALEN);
651 current_ev = iwe_stream_add_event(info, current_ev, end_buf, &iwe,
654 memset(&iwe, 0, sizeof(iwe));
655 iwe.cmd = SIOCGIWFREQ;
656 iwe.u.freq.m = ieee80211_frequency_to_channel(bss->pub.channel->center_freq);
658 current_ev = iwe_stream_add_event(info, current_ev, end_buf, &iwe,
661 memset(&iwe, 0, sizeof(iwe));
662 iwe.cmd = SIOCGIWFREQ;
663 iwe.u.freq.m = bss->pub.channel->center_freq;
665 current_ev = iwe_stream_add_event(info, current_ev, end_buf, &iwe,
668 if (wiphy->signal_type != CFG80211_SIGNAL_TYPE_NONE) {
669 memset(&iwe, 0, sizeof(iwe));
671 iwe.u.qual.updated = IW_QUAL_LEVEL_UPDATED |
672 IW_QUAL_NOISE_INVALID |
673 IW_QUAL_QUAL_UPDATED;
674 switch (wiphy->signal_type) {
675 case CFG80211_SIGNAL_TYPE_MBM:
676 sig = bss->pub.signal / 100;
677 iwe.u.qual.level = sig;
678 iwe.u.qual.updated |= IW_QUAL_DBM;
679 if (sig < -110) /* rather bad */
681 else if (sig > -40) /* perfect */
683 /* will give a range of 0 .. 70 */
684 iwe.u.qual.qual = sig + 110;
686 case CFG80211_SIGNAL_TYPE_UNSPEC:
687 iwe.u.qual.level = bss->pub.signal;
688 /* will give range 0 .. 100 */
689 iwe.u.qual.qual = bss->pub.signal;
695 current_ev = iwe_stream_add_event(info, current_ev, end_buf,
696 &iwe, IW_EV_QUAL_LEN);
699 memset(&iwe, 0, sizeof(iwe));
700 iwe.cmd = SIOCGIWENCODE;
701 if (bss->pub.capability & WLAN_CAPABILITY_PRIVACY)
702 iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY;
704 iwe.u.data.flags = IW_ENCODE_DISABLED;
705 iwe.u.data.length = 0;
706 current_ev = iwe_stream_add_point(info, current_ev, end_buf,
716 memset(&iwe, 0, sizeof(iwe));
717 iwe.cmd = SIOCGIWESSID;
718 iwe.u.data.length = ie[1];
719 iwe.u.data.flags = 1;
720 current_ev = iwe_stream_add_point(info, current_ev, end_buf,
723 case WLAN_EID_MESH_ID:
724 memset(&iwe, 0, sizeof(iwe));
725 iwe.cmd = SIOCGIWESSID;
726 iwe.u.data.length = ie[1];
727 iwe.u.data.flags = 1;
728 current_ev = iwe_stream_add_point(info, current_ev, end_buf,
731 case WLAN_EID_MESH_CONFIG:
733 if (ie[1] != IEEE80211_MESH_CONFIG_LEN)
735 buf = kmalloc(50, GFP_ATOMIC);
739 memset(&iwe, 0, sizeof(iwe));
740 iwe.cmd = IWEVCUSTOM;
741 sprintf(buf, "Mesh network (version %d)", cfg[0]);
742 iwe.u.data.length = strlen(buf);
743 current_ev = iwe_stream_add_point(info, current_ev,
746 sprintf(buf, "Path Selection Protocol ID: "
747 "0x%02X%02X%02X%02X", cfg[1], cfg[2], cfg[3],
749 iwe.u.data.length = strlen(buf);
750 current_ev = iwe_stream_add_point(info, current_ev,
753 sprintf(buf, "Path Selection Metric ID: "
754 "0x%02X%02X%02X%02X", cfg[5], cfg[6], cfg[7],
756 iwe.u.data.length = strlen(buf);
757 current_ev = iwe_stream_add_point(info, current_ev,
760 sprintf(buf, "Congestion Control Mode ID: "
761 "0x%02X%02X%02X%02X", cfg[9], cfg[10],
763 iwe.u.data.length = strlen(buf);
764 current_ev = iwe_stream_add_point(info, current_ev,
767 sprintf(buf, "Channel Precedence: "
768 "0x%02X%02X%02X%02X", cfg[13], cfg[14],
770 iwe.u.data.length = strlen(buf);
771 current_ev = iwe_stream_add_point(info, current_ev,
776 case WLAN_EID_SUPP_RATES:
777 case WLAN_EID_EXT_SUPP_RATES:
778 /* display all supported rates in readable format */
779 p = current_ev + iwe_stream_lcp_len(info);
781 memset(&iwe, 0, sizeof(iwe));
782 iwe.cmd = SIOCGIWRATE;
783 /* Those two flags are ignored... */
784 iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0;
786 for (i = 0; i < ie[1]; i++) {
787 iwe.u.bitrate.value =
788 ((ie[i + 2] & 0x7f) * 500000);
789 p = iwe_stream_add_value(info, current_ev, p,
790 end_buf, &iwe, IW_EV_PARAM_LEN);
799 if (bss->pub.capability & (WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_IBSS)
801 memset(&iwe, 0, sizeof(iwe));
802 iwe.cmd = SIOCGIWMODE;
804 iwe.u.mode = IW_MODE_MESH;
805 else if (bss->pub.capability & WLAN_CAPABILITY_ESS)
806 iwe.u.mode = IW_MODE_MASTER;
808 iwe.u.mode = IW_MODE_ADHOC;
809 current_ev = iwe_stream_add_event(info, current_ev, end_buf,
810 &iwe, IW_EV_UINT_LEN);
813 buf = kmalloc(30, GFP_ATOMIC);
815 memset(&iwe, 0, sizeof(iwe));
816 iwe.cmd = IWEVCUSTOM;
817 sprintf(buf, "tsf=%016llx", (unsigned long long)(bss->pub.tsf));
818 iwe.u.data.length = strlen(buf);
819 current_ev = iwe_stream_add_point(info, current_ev, end_buf,
821 memset(&iwe, 0, sizeof(iwe));
822 iwe.cmd = IWEVCUSTOM;
823 sprintf(buf, " Last beacon: %ums ago",
824 elapsed_jiffies_msecs(bss->ts));
825 iwe.u.data.length = strlen(buf);
826 current_ev = iwe_stream_add_point(info, current_ev,
831 ieee80211_scan_add_ies(info, &bss->pub, ¤t_ev, end_buf);
837 static int ieee80211_scan_results(struct cfg80211_registered_device *dev,
838 struct iw_request_info *info,
839 char *buf, size_t len)
841 char *current_ev = buf;
842 char *end_buf = buf + len;
843 struct cfg80211_internal_bss *bss;
845 spin_lock_bh(&dev->bss_lock);
846 cfg80211_bss_expire(dev);
848 list_for_each_entry(bss, &dev->bss_list, list) {
849 if (buf + len - current_ev <= IW_EV_ADDR_LEN) {
850 spin_unlock_bh(&dev->bss_lock);
853 current_ev = ieee80211_bss(&dev->wiphy, info, bss,
854 current_ev, end_buf);
856 spin_unlock_bh(&dev->bss_lock);
857 return current_ev - buf;
861 int cfg80211_wext_giwscan(struct net_device *dev,
862 struct iw_request_info *info,
863 struct iw_point *data, char *extra)
865 struct cfg80211_registered_device *rdev;
868 if (!netif_running(dev))
871 rdev = cfg80211_get_dev_from_ifindex(dev->ifindex);
874 return PTR_ERR(rdev);
876 if (rdev->scan_req) {
881 res = ieee80211_scan_results(rdev, info, extra, data->length);
889 cfg80211_put_dev(rdev);
892 EXPORT_SYMBOL(cfg80211_wext_giwscan);