diff options
author | Johannes Berg <johannes@sipsolutions.net> | 2009-02-10 15:25:55 -0500 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2009-02-13 13:45:49 -0500 |
commit | 2a5193119269062608582418deba7af82844159a (patch) | |
tree | 1f2fe8cffbeb7530dce7fa708310f6fb29ab0dd8 /net/mac80211/scan.c | |
parent | 849b7967818995a32c3017542e33eb3155944368 (diff) |
cfg80211/nl80211: scanning (and mac80211 update to use it)
This patch adds basic scan capability to cfg80211/nl80211 and
changes mac80211 to use it. The BSS list that cfg80211 maintains
is made driver-accessible with a private area in each BSS struct,
but mac80211 doesn't yet use it. That's another large project.
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'net/mac80211/scan.c')
-rw-r--r-- | net/mac80211/scan.c | 356 |
1 files changed, 60 insertions, 296 deletions
diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c index eddca4e1e13c..c6b275b10cf9 100644 --- a/net/mac80211/scan.c +++ b/net/mac80211/scan.c | |||
@@ -13,6 +13,9 @@ | |||
13 | */ | 13 | */ |
14 | 14 | ||
15 | /* TODO: | 15 | /* TODO: |
16 | * figure out how to avoid that the "current BSS" expires | ||
17 | * clean up IBSS code (in MLME), see why it adds a BSS to the list | ||
18 | * use cfg80211's BSS handling (depends on IBSS TODO above) | ||
16 | * order BSS list by RSSI(?) ("quality of AP") | 19 | * order BSS list by RSSI(?) ("quality of AP") |
17 | * scan result table filtering (by capability (privacy, IBSS/BSS, WPA/RSN IE, | 20 | * scan result table filtering (by capability (privacy, IBSS/BSS, WPA/RSN IE, |
18 | * SSID) | 21 | * SSID) |
@@ -225,10 +228,26 @@ ieee80211_bss_info_update(struct ieee80211_local *local, | |||
225 | struct ieee80211_mgmt *mgmt, | 228 | struct ieee80211_mgmt *mgmt, |
226 | size_t len, | 229 | size_t len, |
227 | struct ieee802_11_elems *elems, | 230 | struct ieee802_11_elems *elems, |
228 | int freq, bool beacon) | 231 | struct ieee80211_channel *channel, |
232 | bool beacon) | ||
229 | { | 233 | { |
230 | struct ieee80211_bss *bss; | 234 | struct ieee80211_bss *bss; |
231 | int clen; | 235 | int clen, freq = channel->center_freq; |
236 | enum cfg80211_signal_type sigtype = CFG80211_SIGNAL_TYPE_NONE; | ||
237 | s32 signal = 0; | ||
238 | |||
239 | if (local->hw.flags & IEEE80211_HW_SIGNAL_DBM) { | ||
240 | sigtype = CFG80211_SIGNAL_TYPE_MBM; | ||
241 | signal = rx_status->signal * 100; | ||
242 | } else if (local->hw.flags & IEEE80211_HW_SIGNAL_UNSPEC) { | ||
243 | sigtype = CFG80211_SIGNAL_TYPE_UNSPEC; | ||
244 | signal = (rx_status->signal * 100) / local->hw.max_signal; | ||
245 | } | ||
246 | |||
247 | cfg80211_put_bss( | ||
248 | cfg80211_inform_bss_frame(local->hw.wiphy, channel, | ||
249 | mgmt, len, signal, sigtype, | ||
250 | GFP_ATOMIC)); | ||
232 | 251 | ||
233 | #ifdef CONFIG_MAC80211_MESH | 252 | #ifdef CONFIG_MAC80211_MESH |
234 | if (elems->mesh_config) | 253 | if (elems->mesh_config) |
@@ -401,7 +420,7 @@ ieee80211_scan_rx(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb, | |||
401 | 420 | ||
402 | bss = ieee80211_bss_info_update(sdata->local, rx_status, | 421 | bss = ieee80211_bss_info_update(sdata->local, rx_status, |
403 | mgmt, skb->len, &elems, | 422 | mgmt, skb->len, &elems, |
404 | freq, beacon); | 423 | channel, beacon); |
405 | if (bss) | 424 | if (bss) |
406 | ieee80211_rx_bss_put(sdata->local, bss); | 425 | ieee80211_rx_bss_put(sdata->local, bss); |
407 | 426 | ||
@@ -439,26 +458,22 @@ void ieee80211_send_nullfunc(struct ieee80211_local *local, | |||
439 | ieee80211_tx_skb(sdata, skb, 0); | 458 | ieee80211_tx_skb(sdata, skb, 0); |
440 | } | 459 | } |
441 | 460 | ||
442 | void ieee80211_scan_completed(struct ieee80211_hw *hw) | 461 | void ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted) |
443 | { | 462 | { |
444 | struct ieee80211_local *local = hw_to_local(hw); | 463 | struct ieee80211_local *local = hw_to_local(hw); |
445 | struct ieee80211_sub_if_data *sdata; | 464 | struct ieee80211_sub_if_data *sdata; |
446 | union iwreq_data wrqu; | ||
447 | 465 | ||
448 | if (WARN_ON(!local->hw_scanning && !local->sw_scanning)) | 466 | if (WARN_ON(!local->hw_scanning && !local->sw_scanning)) |
449 | return; | 467 | return; |
450 | 468 | ||
451 | local->last_scan_completed = jiffies; | 469 | if (WARN_ON(!local->scan_req)) |
452 | memset(&wrqu, 0, sizeof(wrqu)); | 470 | return; |
453 | 471 | ||
454 | /* | 472 | if (local->scan_req != &local->int_scan_req) |
455 | * local->scan_sdata could have been NULLed by the interface | 473 | cfg80211_scan_done(local->scan_req, aborted); |
456 | * down code in case we were scanning on an interface that is | 474 | local->scan_req = NULL; |
457 | * being taken down. | 475 | |
458 | */ | 476 | local->last_scan_completed = jiffies; |
459 | sdata = local->scan_sdata; | ||
460 | if (sdata) | ||
461 | wireless_send_event(sdata->dev, SIOCGIWSCAN, &wrqu, NULL); | ||
462 | 477 | ||
463 | if (local->hw_scanning) { | 478 | if (local->hw_scanning) { |
464 | local->hw_scanning = false; | 479 | local->hw_scanning = false; |
@@ -520,9 +535,8 @@ void ieee80211_scan_work(struct work_struct *work) | |||
520 | struct ieee80211_local *local = | 535 | struct ieee80211_local *local = |
521 | container_of(work, struct ieee80211_local, scan_work.work); | 536 | container_of(work, struct ieee80211_local, scan_work.work); |
522 | struct ieee80211_sub_if_data *sdata = local->scan_sdata; | 537 | struct ieee80211_sub_if_data *sdata = local->scan_sdata; |
523 | struct ieee80211_supported_band *sband; | ||
524 | struct ieee80211_channel *chan; | 538 | struct ieee80211_channel *chan; |
525 | int skip; | 539 | int skip, i; |
526 | unsigned long next_delay = 0; | 540 | unsigned long next_delay = 0; |
527 | 541 | ||
528 | /* | 542 | /* |
@@ -533,33 +547,13 @@ void ieee80211_scan_work(struct work_struct *work) | |||
533 | 547 | ||
534 | switch (local->scan_state) { | 548 | switch (local->scan_state) { |
535 | case SCAN_SET_CHANNEL: | 549 | case SCAN_SET_CHANNEL: |
536 | /* | ||
537 | * Get current scan band. scan_band may be IEEE80211_NUM_BANDS | ||
538 | * after we successfully scanned the last channel of the last | ||
539 | * band (and the last band is supported by the hw) | ||
540 | */ | ||
541 | if (local->scan_band < IEEE80211_NUM_BANDS) | ||
542 | sband = local->hw.wiphy->bands[local->scan_band]; | ||
543 | else | ||
544 | sband = NULL; | ||
545 | |||
546 | /* | ||
547 | * If we are at an unsupported band and have more bands | ||
548 | * left to scan, advance to the next supported one. | ||
549 | */ | ||
550 | while (!sband && local->scan_band < IEEE80211_NUM_BANDS - 1) { | ||
551 | local->scan_band++; | ||
552 | sband = local->hw.wiphy->bands[local->scan_band]; | ||
553 | local->scan_channel_idx = 0; | ||
554 | } | ||
555 | |||
556 | /* if no more bands/channels left, complete scan */ | 550 | /* if no more bands/channels left, complete scan */ |
557 | if (!sband || local->scan_channel_idx >= sband->n_channels) { | 551 | if (local->scan_channel_idx >= local->scan_req->n_channels) { |
558 | ieee80211_scan_completed(local_to_hw(local)); | 552 | ieee80211_scan_completed(local_to_hw(local), false); |
559 | return; | 553 | return; |
560 | } | 554 | } |
561 | skip = 0; | 555 | skip = 0; |
562 | chan = &sband->channels[local->scan_channel_idx]; | 556 | chan = local->scan_req->channels[local->scan_channel_idx]; |
563 | 557 | ||
564 | if (chan->flags & IEEE80211_CHAN_DISABLED || | 558 | if (chan->flags & IEEE80211_CHAN_DISABLED || |
565 | (sdata->vif.type == NL80211_IFTYPE_ADHOC && | 559 | (sdata->vif.type == NL80211_IFTYPE_ADHOC && |
@@ -575,15 +569,6 @@ void ieee80211_scan_work(struct work_struct *work) | |||
575 | 569 | ||
576 | /* advance state machine to next channel/band */ | 570 | /* advance state machine to next channel/band */ |
577 | local->scan_channel_idx++; | 571 | local->scan_channel_idx++; |
578 | if (local->scan_channel_idx >= sband->n_channels) { | ||
579 | /* | ||
580 | * scan_band may end up == IEEE80211_NUM_BANDS, but | ||
581 | * we'll catch that case above and complete the scan | ||
582 | * if that is the case. | ||
583 | */ | ||
584 | local->scan_band++; | ||
585 | local->scan_channel_idx = 0; | ||
586 | } | ||
587 | 572 | ||
588 | if (skip) | 573 | if (skip) |
589 | break; | 574 | break; |
@@ -596,10 +581,14 @@ void ieee80211_scan_work(struct work_struct *work) | |||
596 | next_delay = IEEE80211_PASSIVE_CHANNEL_TIME; | 581 | next_delay = IEEE80211_PASSIVE_CHANNEL_TIME; |
597 | local->scan_state = SCAN_SET_CHANNEL; | 582 | local->scan_state = SCAN_SET_CHANNEL; |
598 | 583 | ||
599 | if (local->scan_channel->flags & IEEE80211_CHAN_PASSIVE_SCAN) | 584 | if (local->scan_channel->flags & IEEE80211_CHAN_PASSIVE_SCAN || |
585 | !local->scan_req->n_ssids) | ||
600 | break; | 586 | break; |
601 | ieee80211_send_probe_req(sdata, NULL, local->scan_ssid, | 587 | for (i = 0; i < local->scan_req->n_ssids; i++) |
602 | local->scan_ssid_len); | 588 | ieee80211_send_probe_req( |
589 | sdata, NULL, | ||
590 | local->scan_req->ssids[i].ssid, | ||
591 | local->scan_req->ssids[i].ssid_len); | ||
603 | next_delay = IEEE80211_CHANNEL_TIME; | 592 | next_delay = IEEE80211_CHANNEL_TIME; |
604 | break; | 593 | break; |
605 | } | 594 | } |
@@ -610,14 +599,19 @@ void ieee80211_scan_work(struct work_struct *work) | |||
610 | 599 | ||
611 | 600 | ||
612 | int ieee80211_start_scan(struct ieee80211_sub_if_data *scan_sdata, | 601 | int ieee80211_start_scan(struct ieee80211_sub_if_data *scan_sdata, |
613 | u8 *ssid, size_t ssid_len) | 602 | struct cfg80211_scan_request *req) |
614 | { | 603 | { |
615 | struct ieee80211_local *local = scan_sdata->local; | 604 | struct ieee80211_local *local = scan_sdata->local; |
616 | struct ieee80211_sub_if_data *sdata; | 605 | struct ieee80211_sub_if_data *sdata; |
617 | 606 | ||
618 | if (ssid_len > IEEE80211_MAX_SSID_LEN) | 607 | if (!req) |
619 | return -EINVAL; | 608 | return -EINVAL; |
620 | 609 | ||
610 | if (local->scan_req && local->scan_req != req) | ||
611 | return -EBUSY; | ||
612 | |||
613 | local->scan_req = req; | ||
614 | |||
621 | /* MLME-SCAN.request (page 118) page 144 (11.1.3.1) | 615 | /* MLME-SCAN.request (page 118) page 144 (11.1.3.1) |
622 | * BSSType: INFRASTRUCTURE, INDEPENDENT, ANY_BSS | 616 | * BSSType: INFRASTRUCTURE, INDEPENDENT, ANY_BSS |
623 | * BSSID: MACAddress | 617 | * BSSID: MACAddress |
@@ -645,7 +639,7 @@ int ieee80211_start_scan(struct ieee80211_sub_if_data *scan_sdata, | |||
645 | int rc; | 639 | int rc; |
646 | 640 | ||
647 | local->hw_scanning = true; | 641 | local->hw_scanning = true; |
648 | rc = local->ops->hw_scan(local_to_hw(local), ssid, ssid_len); | 642 | rc = local->ops->hw_scan(local_to_hw(local), req); |
649 | if (rc) { | 643 | if (rc) { |
650 | local->hw_scanning = false; | 644 | local->hw_scanning = false; |
651 | return rc; | 645 | return rc; |
@@ -678,15 +672,10 @@ int ieee80211_start_scan(struct ieee80211_sub_if_data *scan_sdata, | |||
678 | } | 672 | } |
679 | mutex_unlock(&local->iflist_mtx); | 673 | mutex_unlock(&local->iflist_mtx); |
680 | 674 | ||
681 | if (ssid) { | ||
682 | local->scan_ssid_len = ssid_len; | ||
683 | memcpy(local->scan_ssid, ssid, ssid_len); | ||
684 | } else | ||
685 | local->scan_ssid_len = 0; | ||
686 | local->scan_state = SCAN_SET_CHANNEL; | 675 | local->scan_state = SCAN_SET_CHANNEL; |
687 | local->scan_channel_idx = 0; | 676 | local->scan_channel_idx = 0; |
688 | local->scan_band = IEEE80211_BAND_2GHZ; | ||
689 | local->scan_sdata = scan_sdata; | 677 | local->scan_sdata = scan_sdata; |
678 | local->scan_req = req; | ||
690 | 679 | ||
691 | netif_addr_lock_bh(local->mdev); | 680 | netif_addr_lock_bh(local->mdev); |
692 | local->filter_flags |= FIF_BCN_PRBRESP_PROMISC; | 681 | local->filter_flags |= FIF_BCN_PRBRESP_PROMISC; |
@@ -706,13 +695,21 @@ int ieee80211_start_scan(struct ieee80211_sub_if_data *scan_sdata, | |||
706 | 695 | ||
707 | 696 | ||
708 | int ieee80211_request_scan(struct ieee80211_sub_if_data *sdata, | 697 | int ieee80211_request_scan(struct ieee80211_sub_if_data *sdata, |
709 | u8 *ssid, size_t ssid_len) | 698 | struct cfg80211_scan_request *req) |
710 | { | 699 | { |
711 | struct ieee80211_local *local = sdata->local; | 700 | struct ieee80211_local *local = sdata->local; |
712 | struct ieee80211_if_sta *ifsta; | 701 | struct ieee80211_if_sta *ifsta; |
713 | 702 | ||
703 | if (!req) | ||
704 | return -EINVAL; | ||
705 | |||
706 | if (local->scan_req && local->scan_req != req) | ||
707 | return -EBUSY; | ||
708 | |||
709 | local->scan_req = req; | ||
710 | |||
714 | if (sdata->vif.type != NL80211_IFTYPE_STATION) | 711 | if (sdata->vif.type != NL80211_IFTYPE_STATION) |
715 | return ieee80211_start_scan(sdata, ssid, ssid_len); | 712 | return ieee80211_start_scan(sdata, req); |
716 | 713 | ||
717 | /* | 714 | /* |
718 | * STA has a state machine that might need to defer scanning | 715 | * STA has a state machine that might need to defer scanning |
@@ -727,241 +724,8 @@ int ieee80211_request_scan(struct ieee80211_sub_if_data *sdata, | |||
727 | } | 724 | } |
728 | 725 | ||
729 | ifsta = &sdata->u.sta; | 726 | ifsta = &sdata->u.sta; |
730 | |||
731 | ifsta->scan_ssid_len = ssid_len; | ||
732 | if (ssid_len) | ||
733 | memcpy(ifsta->scan_ssid, ssid, ssid_len); | ||
734 | set_bit(IEEE80211_STA_REQ_SCAN, &ifsta->request); | 727 | set_bit(IEEE80211_STA_REQ_SCAN, &ifsta->request); |
735 | queue_work(local->hw.workqueue, &ifsta->work); | 728 | queue_work(local->hw.workqueue, &ifsta->work); |
736 | 729 | ||
737 | return 0; | 730 | return 0; |
738 | } | 731 | } |
739 | |||
740 | |||
741 | static void ieee80211_scan_add_ies(struct iw_request_info *info, | ||
742 | struct ieee80211_bss *bss, | ||
743 | char **current_ev, char *end_buf) | ||
744 | { | ||
745 | u8 *pos, *end, *next; | ||
746 | struct iw_event iwe; | ||
747 | |||
748 | if (bss == NULL || bss->ies == NULL) | ||
749 | return; | ||
750 | |||
751 | /* | ||
752 | * If needed, fragment the IEs buffer (at IE boundaries) into short | ||
753 | * enough fragments to fit into IW_GENERIC_IE_MAX octet messages. | ||
754 | */ | ||
755 | pos = bss->ies; | ||
756 | end = pos + bss->ies_len; | ||
757 | |||
758 | while (end - pos > IW_GENERIC_IE_MAX) { | ||
759 | next = pos + 2 + pos[1]; | ||
760 | while (next + 2 + next[1] - pos < IW_GENERIC_IE_MAX) | ||
761 | next = next + 2 + next[1]; | ||
762 | |||
763 | memset(&iwe, 0, sizeof(iwe)); | ||
764 | iwe.cmd = IWEVGENIE; | ||
765 | iwe.u.data.length = next - pos; | ||
766 | *current_ev = iwe_stream_add_point(info, *current_ev, | ||
767 | end_buf, &iwe, pos); | ||
768 | |||
769 | pos = next; | ||
770 | } | ||
771 | |||
772 | if (end > pos) { | ||
773 | memset(&iwe, 0, sizeof(iwe)); | ||
774 | iwe.cmd = IWEVGENIE; | ||
775 | iwe.u.data.length = end - pos; | ||
776 | *current_ev = iwe_stream_add_point(info, *current_ev, | ||
777 | end_buf, &iwe, pos); | ||
778 | } | ||
779 | } | ||
780 | |||
781 | |||
782 | static char * | ||
783 | ieee80211_scan_result(struct ieee80211_local *local, | ||
784 | struct iw_request_info *info, | ||
785 | struct ieee80211_bss *bss, | ||
786 | char *current_ev, char *end_buf) | ||
787 | { | ||
788 | struct iw_event iwe; | ||
789 | char *buf; | ||
790 | |||
791 | if (time_after(jiffies, | ||
792 | bss->last_update + IEEE80211_SCAN_RESULT_EXPIRE)) | ||
793 | return current_ev; | ||
794 | |||
795 | memset(&iwe, 0, sizeof(iwe)); | ||
796 | iwe.cmd = SIOCGIWAP; | ||
797 | iwe.u.ap_addr.sa_family = ARPHRD_ETHER; | ||
798 | memcpy(iwe.u.ap_addr.sa_data, bss->bssid, ETH_ALEN); | ||
799 | current_ev = iwe_stream_add_event(info, current_ev, end_buf, &iwe, | ||
800 | IW_EV_ADDR_LEN); | ||
801 | |||
802 | memset(&iwe, 0, sizeof(iwe)); | ||
803 | iwe.cmd = SIOCGIWESSID; | ||
804 | if (bss_mesh_cfg(bss)) { | ||
805 | iwe.u.data.length = bss_mesh_id_len(bss); | ||
806 | iwe.u.data.flags = 1; | ||
807 | current_ev = iwe_stream_add_point(info, current_ev, end_buf, | ||
808 | &iwe, bss_mesh_id(bss)); | ||
809 | } else { | ||
810 | iwe.u.data.length = bss->ssid_len; | ||
811 | iwe.u.data.flags = 1; | ||
812 | current_ev = iwe_stream_add_point(info, current_ev, end_buf, | ||
813 | &iwe, bss->ssid); | ||
814 | } | ||
815 | |||
816 | if (bss->capability & (WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_IBSS) | ||
817 | || bss_mesh_cfg(bss)) { | ||
818 | memset(&iwe, 0, sizeof(iwe)); | ||
819 | iwe.cmd = SIOCGIWMODE; | ||
820 | if (bss_mesh_cfg(bss)) | ||
821 | iwe.u.mode = IW_MODE_MESH; | ||
822 | else if (bss->capability & WLAN_CAPABILITY_ESS) | ||
823 | iwe.u.mode = IW_MODE_MASTER; | ||
824 | else | ||
825 | iwe.u.mode = IW_MODE_ADHOC; | ||
826 | current_ev = iwe_stream_add_event(info, current_ev, end_buf, | ||
827 | &iwe, IW_EV_UINT_LEN); | ||
828 | } | ||
829 | |||
830 | memset(&iwe, 0, sizeof(iwe)); | ||
831 | iwe.cmd = SIOCGIWFREQ; | ||
832 | iwe.u.freq.m = ieee80211_frequency_to_channel(bss->freq); | ||
833 | iwe.u.freq.e = 0; | ||
834 | current_ev = iwe_stream_add_event(info, current_ev, end_buf, &iwe, | ||
835 | IW_EV_FREQ_LEN); | ||
836 | |||
837 | memset(&iwe, 0, sizeof(iwe)); | ||
838 | iwe.cmd = SIOCGIWFREQ; | ||
839 | iwe.u.freq.m = bss->freq; | ||
840 | iwe.u.freq.e = 6; | ||
841 | current_ev = iwe_stream_add_event(info, current_ev, end_buf, &iwe, | ||
842 | IW_EV_FREQ_LEN); | ||
843 | memset(&iwe, 0, sizeof(iwe)); | ||
844 | iwe.cmd = IWEVQUAL; | ||
845 | iwe.u.qual.qual = bss->qual; | ||
846 | iwe.u.qual.level = bss->signal; | ||
847 | iwe.u.qual.noise = bss->noise; | ||
848 | iwe.u.qual.updated = local->wstats_flags; | ||
849 | current_ev = iwe_stream_add_event(info, current_ev, end_buf, &iwe, | ||
850 | IW_EV_QUAL_LEN); | ||
851 | |||
852 | memset(&iwe, 0, sizeof(iwe)); | ||
853 | iwe.cmd = SIOCGIWENCODE; | ||
854 | if (bss->capability & WLAN_CAPABILITY_PRIVACY) | ||
855 | iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY; | ||
856 | else | ||
857 | iwe.u.data.flags = IW_ENCODE_DISABLED; | ||
858 | iwe.u.data.length = 0; | ||
859 | current_ev = iwe_stream_add_point(info, current_ev, end_buf, | ||
860 | &iwe, ""); | ||
861 | |||
862 | ieee80211_scan_add_ies(info, bss, ¤t_ev, end_buf); | ||
863 | |||
864 | if (bss->supp_rates_len > 0) { | ||
865 | /* display all supported rates in readable format */ | ||
866 | char *p = current_ev + iwe_stream_lcp_len(info); | ||
867 | int i; | ||
868 | |||
869 | memset(&iwe, 0, sizeof(iwe)); | ||
870 | iwe.cmd = SIOCGIWRATE; | ||
871 | /* Those two flags are ignored... */ | ||
872 | iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0; | ||
873 | |||
874 | for (i = 0; i < bss->supp_rates_len; i++) { | ||
875 | iwe.u.bitrate.value = ((bss->supp_rates[i] & | ||
876 | 0x7f) * 500000); | ||
877 | p = iwe_stream_add_value(info, current_ev, p, | ||
878 | end_buf, &iwe, IW_EV_PARAM_LEN); | ||
879 | } | ||
880 | current_ev = p; | ||
881 | } | ||
882 | |||
883 | buf = kmalloc(30, GFP_ATOMIC); | ||
884 | if (buf) { | ||
885 | memset(&iwe, 0, sizeof(iwe)); | ||
886 | iwe.cmd = IWEVCUSTOM; | ||
887 | sprintf(buf, "tsf=%016llx", (unsigned long long)(bss->timestamp)); | ||
888 | iwe.u.data.length = strlen(buf); | ||
889 | current_ev = iwe_stream_add_point(info, current_ev, end_buf, | ||
890 | &iwe, buf); | ||
891 | memset(&iwe, 0, sizeof(iwe)); | ||
892 | iwe.cmd = IWEVCUSTOM; | ||
893 | sprintf(buf, " Last beacon: %dms ago", | ||
894 | jiffies_to_msecs(jiffies - bss->last_update)); | ||
895 | iwe.u.data.length = strlen(buf); | ||
896 | current_ev = iwe_stream_add_point(info, current_ev, | ||
897 | end_buf, &iwe, buf); | ||
898 | kfree(buf); | ||
899 | } | ||
900 | |||
901 | if (bss_mesh_cfg(bss)) { | ||
902 | u8 *cfg = bss_mesh_cfg(bss); | ||
903 | buf = kmalloc(50, GFP_ATOMIC); | ||
904 | if (buf) { | ||
905 | memset(&iwe, 0, sizeof(iwe)); | ||
906 | iwe.cmd = IWEVCUSTOM; | ||
907 | sprintf(buf, "Mesh network (version %d)", cfg[0]); | ||
908 | iwe.u.data.length = strlen(buf); | ||
909 | current_ev = iwe_stream_add_point(info, current_ev, | ||
910 | end_buf, | ||
911 | &iwe, buf); | ||
912 | sprintf(buf, "Path Selection Protocol ID: " | ||
913 | "0x%02X%02X%02X%02X", cfg[1], cfg[2], cfg[3], | ||
914 | cfg[4]); | ||
915 | iwe.u.data.length = strlen(buf); | ||
916 | current_ev = iwe_stream_add_point(info, current_ev, | ||
917 | end_buf, | ||
918 | &iwe, buf); | ||
919 | sprintf(buf, "Path Selection Metric ID: " | ||
920 | "0x%02X%02X%02X%02X", cfg[5], cfg[6], cfg[7], | ||
921 | cfg[8]); | ||
922 | iwe.u.data.length = strlen(buf); | ||
923 | current_ev = iwe_stream_add_point(info, current_ev, | ||
924 | end_buf, | ||
925 | &iwe, buf); | ||
926 | sprintf(buf, "Congestion Control Mode ID: " | ||
927 | "0x%02X%02X%02X%02X", cfg[9], cfg[10], | ||
928 | cfg[11], cfg[12]); | ||
929 | iwe.u.data.length = strlen(buf); | ||
930 | current_ev = iwe_stream_add_point(info, current_ev, | ||
931 | end_buf, | ||
932 | &iwe, buf); | ||
933 | sprintf(buf, "Channel Precedence: " | ||
934 | "0x%02X%02X%02X%02X", cfg[13], cfg[14], | ||
935 | cfg[15], cfg[16]); | ||
936 | iwe.u.data.length = strlen(buf); | ||
937 | current_ev = iwe_stream_add_point(info, current_ev, | ||
938 | end_buf, | ||
939 | &iwe, buf); | ||
940 | kfree(buf); | ||
941 | } | ||
942 | } | ||
943 | |||
944 | return current_ev; | ||
945 | } | ||
946 | |||
947 | |||
948 | int ieee80211_scan_results(struct ieee80211_local *local, | ||
949 | struct iw_request_info *info, | ||
950 | char *buf, size_t len) | ||
951 | { | ||
952 | char *current_ev = buf; | ||
953 | char *end_buf = buf + len; | ||
954 | struct ieee80211_bss *bss; | ||
955 | |||
956 | spin_lock_bh(&local->bss_lock); | ||
957 | list_for_each_entry(bss, &local->bss_list, list) { | ||
958 | if (buf + len - current_ev <= IW_EV_ADDR_LEN) { | ||
959 | spin_unlock_bh(&local->bss_lock); | ||
960 | return -E2BIG; | ||
961 | } | ||
962 | current_ev = ieee80211_scan_result(local, info, bss, | ||
963 | current_ev, end_buf); | ||
964 | } | ||
965 | spin_unlock_bh(&local->bss_lock); | ||
966 | return current_ev - buf; | ||
967 | } | ||