diff options
author | Hante Meuleman <meuleman@broadcom.com> | 2012-09-11 15:18:52 -0400 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2012-09-12 14:19:16 -0400 |
commit | e756af5b30b008f6ffcfebf8ad0b477f6f225b62 (patch) | |
tree | bb5da7c5310e4209e38152219adeb9ff8d7d6a75 | |
parent | d74a0b514de6996021573e5406af8d06da188794 (diff) |
brcmfmac: add e-scan support.
This patch adds e-scan support (currently i-scan is in use). E-scan
is a more powerful and memory efficient method for scanning. E-scan
will be the default scan method and eventually, i-scan support will
be removed. The scan methods do not make any difference to the end-user.
Reviewed-by: Arend Van Spriel <arend@broadcom.com>
Reviewed-by: Franky (Zhenhui) Lin <frankyl@broadcom.com>
Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com>
Signed-off-by: Hante Meuleman <meuleman@broadcom.com>
Signed-off-by: Arend van Spriel <arend@broadcom.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
-rw-r--r-- | drivers/net/wireless/brcm80211/Kconfig | 8 | ||||
-rw-r--r-- | drivers/net/wireless/brcm80211/brcmfmac/dhd.h | 22 | ||||
-rw-r--r-- | drivers/net/wireless/brcm80211/brcmfmac/dhd_common.c | 8 | ||||
-rw-r--r-- | drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c | 522 | ||||
-rw-r--r-- | drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.h | 25 |
5 files changed, 583 insertions, 2 deletions
diff --git a/drivers/net/wireless/brcm80211/Kconfig b/drivers/net/wireless/brcm80211/Kconfig index b480088b3dbe..c9d811eb6556 100644 --- a/drivers/net/wireless/brcm80211/Kconfig +++ b/drivers/net/wireless/brcm80211/Kconfig | |||
@@ -55,6 +55,14 @@ config BRCMFMAC_USB | |||
55 | IEEE802.11n embedded FullMAC WLAN driver. Say Y if you want to | 55 | IEEE802.11n embedded FullMAC WLAN driver. Say Y if you want to |
56 | use the driver for an USB wireless card. | 56 | use the driver for an USB wireless card. |
57 | 57 | ||
58 | config BRCMISCAN | ||
59 | bool "Broadcom I-Scan (OBSOLETE)" | ||
60 | depends on BRCMFMAC | ||
61 | ---help--- | ||
62 | This option enables the I-Scan method. By default fullmac uses the | ||
63 | new E-Scan method which uses less memory in firmware and gives no | ||
64 | limitation on the number of scan results. | ||
65 | |||
58 | config BRCMDBG | 66 | config BRCMDBG |
59 | bool "Broadcom driver debug functions" | 67 | bool "Broadcom driver debug functions" |
60 | depends on BRCMSMAC || BRCMFMAC | 68 | depends on BRCMSMAC || BRCMFMAC |
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd.h b/drivers/net/wireless/brcm80211/brcmfmac/dhd.h index 9e2fb5bc932f..4766d9f35696 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd.h | |||
@@ -130,6 +130,10 @@ | |||
130 | #define BRCMF_EVENT_MSG_FLUSHTXQ 0x02 | 130 | #define BRCMF_EVENT_MSG_FLUSHTXQ 0x02 |
131 | #define BRCMF_EVENT_MSG_GROUP 0x04 | 131 | #define BRCMF_EVENT_MSG_GROUP 0x04 |
132 | 132 | ||
133 | #define BRCMF_ESCAN_REQ_VERSION 1 | ||
134 | |||
135 | #define WLC_BSS_RSSI_ON_CHANNEL 0x0002 | ||
136 | |||
133 | struct brcmf_event_msg { | 137 | struct brcmf_event_msg { |
134 | __be16 version; | 138 | __be16 version; |
135 | __be16 flags; | 139 | __be16 flags; |
@@ -456,6 +460,24 @@ struct brcmf_scan_results_le { | |||
456 | __le32 count; | 460 | __le32 count; |
457 | }; | 461 | }; |
458 | 462 | ||
463 | struct brcmf_escan_params_le { | ||
464 | __le32 version; | ||
465 | __le16 action; | ||
466 | __le16 sync_id; | ||
467 | struct brcmf_scan_params_le params_le; | ||
468 | }; | ||
469 | |||
470 | struct brcmf_escan_result_le { | ||
471 | __le32 buflen; | ||
472 | __le32 version; | ||
473 | __le16 sync_id; | ||
474 | __le16 bss_count; | ||
475 | struct brcmf_bss_info_le bss_info_le; | ||
476 | }; | ||
477 | |||
478 | #define WL_ESCAN_RESULTS_FIXED_SIZE (sizeof(struct brcmf_escan_result_le) - \ | ||
479 | sizeof(struct brcmf_bss_info_le)) | ||
480 | |||
459 | /* used for association with a specific BSSID and chanspec list */ | 481 | /* used for association with a specific BSSID and chanspec list */ |
460 | struct brcmf_assoc_params_le { | 482 | struct brcmf_assoc_params_le { |
461 | /* 00:00:00:00:00:00: broadcast scan */ | 483 | /* 00:00:00:00:00:00: broadcast scan */ |
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_common.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_common.c index 2621dd3d7dcd..f6b862d77986 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_common.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_common.c | |||
@@ -205,7 +205,8 @@ brcmf_c_show_host_event(struct brcmf_event_msg *event, void *event_data) | |||
205 | BRCMF_E_ACTION_FRAME_COMPLETE, "ACTION FRAME TX COMPLETE"}, { | 205 | BRCMF_E_ACTION_FRAME_COMPLETE, "ACTION FRAME TX COMPLETE"}, { |
206 | BRCMF_E_IF, "IF"}, { | 206 | BRCMF_E_IF, "IF"}, { |
207 | BRCMF_E_RSSI, "RSSI"}, { | 207 | BRCMF_E_RSSI, "RSSI"}, { |
208 | BRCMF_E_PFN_SCAN_COMPLETE, "SCAN_COMPLETE"} | 208 | BRCMF_E_PFN_SCAN_COMPLETE, "SCAN_COMPLETE"}, { |
209 | BRCMF_E_ESCAN_RESULT, "ESCAN_RESULT"} | ||
209 | }; | 210 | }; |
210 | uint event_type, flags, auth_type, datalen; | 211 | uint event_type, flags, auth_type, datalen; |
211 | static u32 seqnum_prev; | 212 | static u32 seqnum_prev; |
@@ -350,6 +351,11 @@ brcmf_c_show_host_event(struct brcmf_event_msg *event, void *event_data) | |||
350 | brcmf_dbg(EVENT, "MACEVENT: %s\n", event_name); | 351 | brcmf_dbg(EVENT, "MACEVENT: %s\n", event_name); |
351 | break; | 352 | break; |
352 | 353 | ||
354 | case BRCMF_E_ESCAN_RESULT: | ||
355 | brcmf_dbg(EVENT, "MACEVENT: %s\n", event_name); | ||
356 | datalen = 0; | ||
357 | break; | ||
358 | |||
353 | case BRCMF_E_PFN_NET_FOUND: | 359 | case BRCMF_E_PFN_NET_FOUND: |
354 | case BRCMF_E_PFN_NET_LOST: | 360 | case BRCMF_E_PFN_NET_LOST: |
355 | case BRCMF_E_PFN_SCAN_COMPLETE: | 361 | case BRCMF_E_PFN_SCAN_COMPLETE: |
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c index 4a27ca03f5bc..65cf8f92cb3e 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c | |||
@@ -691,11 +691,342 @@ scan_out: | |||
691 | return err; | 691 | return err; |
692 | } | 692 | } |
693 | 693 | ||
694 | static void brcmf_escan_prep(struct brcmf_scan_params_le *params_le, | ||
695 | struct cfg80211_scan_request *request) | ||
696 | { | ||
697 | u32 n_ssids; | ||
698 | u32 n_channels; | ||
699 | s32 i; | ||
700 | s32 offset; | ||
701 | __le16 chanspec; | ||
702 | u16 channel; | ||
703 | struct ieee80211_channel *req_channel; | ||
704 | char *ptr; | ||
705 | struct brcmf_ssid ssid; | ||
706 | |||
707 | memcpy(params_le->bssid, ether_bcast, ETH_ALEN); | ||
708 | params_le->bss_type = DOT11_BSSTYPE_ANY; | ||
709 | params_le->scan_type = 0; | ||
710 | params_le->channel_num = 0; | ||
711 | params_le->nprobes = cpu_to_le32(-1); | ||
712 | params_le->active_time = cpu_to_le32(-1); | ||
713 | params_le->passive_time = cpu_to_le32(-1); | ||
714 | params_le->home_time = cpu_to_le32(-1); | ||
715 | memset(¶ms_le->ssid_le, 0, sizeof(params_le->ssid_le)); | ||
716 | |||
717 | /* if request is null exit so it will be all channel broadcast scan */ | ||
718 | if (!request) | ||
719 | return; | ||
720 | |||
721 | n_ssids = request->n_ssids; | ||
722 | n_channels = request->n_channels; | ||
723 | /* Copy channel array if applicable */ | ||
724 | WL_SCAN("### List of channelspecs to scan ### %d\n", n_channels); | ||
725 | if (n_channels > 0) { | ||
726 | for (i = 0; i < n_channels; i++) { | ||
727 | chanspec = 0; | ||
728 | req_channel = request->channels[i]; | ||
729 | channel = ieee80211_frequency_to_channel( | ||
730 | req_channel->center_freq); | ||
731 | if (req_channel->band == IEEE80211_BAND_2GHZ) | ||
732 | chanspec |= WL_CHANSPEC_BAND_2G; | ||
733 | else | ||
734 | chanspec |= WL_CHANSPEC_BAND_5G; | ||
735 | |||
736 | if (req_channel->flags & IEEE80211_CHAN_NO_HT40) { | ||
737 | chanspec |= WL_CHANSPEC_BW_20; | ||
738 | chanspec |= WL_CHANSPEC_CTL_SB_NONE; | ||
739 | } else { | ||
740 | chanspec |= WL_CHANSPEC_BW_40; | ||
741 | if (req_channel->flags & | ||
742 | IEEE80211_CHAN_NO_HT40PLUS) | ||
743 | chanspec |= WL_CHANSPEC_CTL_SB_LOWER; | ||
744 | else | ||
745 | chanspec |= WL_CHANSPEC_CTL_SB_UPPER; | ||
746 | } | ||
747 | |||
748 | params_le->channel_list[i] = | ||
749 | (channel & WL_CHANSPEC_CHAN_MASK) | | ||
750 | chanspec; | ||
751 | WL_SCAN("Chan : %d, Channel spec: %x\n", | ||
752 | channel, params_le->channel_list[i]); | ||
753 | params_le->channel_list[i] = | ||
754 | cpu_to_le16(params_le->channel_list[i]); | ||
755 | } | ||
756 | } else { | ||
757 | WL_SCAN("Scanning all channels\n"); | ||
758 | } | ||
759 | /* Copy ssid array if applicable */ | ||
760 | WL_SCAN("### List of SSIDs to scan ### %d\n", n_ssids); | ||
761 | if (n_ssids > 0) { | ||
762 | offset = offsetof(struct brcmf_scan_params_le, channel_list) + | ||
763 | n_channels * sizeof(u16); | ||
764 | offset = roundup(offset, sizeof(u32)); | ||
765 | ptr = (char *)params_le + offset; | ||
766 | for (i = 0; i < n_ssids; i++) { | ||
767 | memset(&ssid, 0, sizeof(ssid)); | ||
768 | ssid.SSID_len = cpu_to_le32(request->ssids[i].ssid_len); | ||
769 | memcpy(ssid.SSID, request->ssids[i].ssid, | ||
770 | request->ssids[i].ssid_len); | ||
771 | if (!ssid.SSID_len) | ||
772 | WL_SCAN("%d: Broadcast scan\n", i); | ||
773 | else | ||
774 | WL_SCAN("%d: scan for %s size =%d\n", i, | ||
775 | ssid.SSID, ssid.SSID_len); | ||
776 | memcpy(ptr, &ssid, sizeof(ssid)); | ||
777 | ptr += sizeof(ssid); | ||
778 | } | ||
779 | } else { | ||
780 | WL_SCAN("Broadcast scan %p\n", request->ssids); | ||
781 | if ((request->ssids) && request->ssids->ssid_len) { | ||
782 | WL_SCAN("SSID %s len=%d\n", params_le->ssid_le.SSID, | ||
783 | request->ssids->ssid_len); | ||
784 | params_le->ssid_le.SSID_len = | ||
785 | cpu_to_le32(request->ssids->ssid_len); | ||
786 | memcpy(¶ms_le->ssid_le.SSID, request->ssids->ssid, | ||
787 | request->ssids->ssid_len); | ||
788 | } | ||
789 | } | ||
790 | /* Adding mask to channel numbers */ | ||
791 | params_le->channel_num = | ||
792 | cpu_to_le32((n_ssids << BRCMF_SCAN_PARAMS_NSSID_SHIFT) | | ||
793 | (n_channels & BRCMF_SCAN_PARAMS_COUNT_MASK)); | ||
794 | } | ||
795 | |||
796 | static s32 | ||
797 | brcmf_notify_escan_complete(struct brcmf_cfg80211_priv *cfg_priv, | ||
798 | struct net_device *ndev, | ||
799 | bool aborted, bool fw_abort) | ||
800 | { | ||
801 | struct brcmf_scan_params_le params_le; | ||
802 | struct cfg80211_scan_request *scan_request; | ||
803 | s32 err = 0; | ||
804 | |||
805 | WL_SCAN("Enter\n"); | ||
806 | |||
807 | /* clear scan request, because the FW abort can cause a second call */ | ||
808 | /* to this functon and might cause a double cfg80211_scan_done */ | ||
809 | scan_request = cfg_priv->scan_request; | ||
810 | cfg_priv->scan_request = NULL; | ||
811 | |||
812 | if (timer_pending(&cfg_priv->escan_timeout)) | ||
813 | del_timer_sync(&cfg_priv->escan_timeout); | ||
814 | |||
815 | if (fw_abort) { | ||
816 | /* Do a scan abort to stop the driver's scan engine */ | ||
817 | WL_SCAN("ABORT scan in firmware\n"); | ||
818 | memset(¶ms_le, 0, sizeof(params_le)); | ||
819 | memcpy(params_le.bssid, ether_bcast, ETH_ALEN); | ||
820 | params_le.bss_type = DOT11_BSSTYPE_ANY; | ||
821 | params_le.scan_type = 0; | ||
822 | params_le.channel_num = cpu_to_le32(1); | ||
823 | params_le.nprobes = cpu_to_le32(1); | ||
824 | params_le.active_time = cpu_to_le32(-1); | ||
825 | params_le.passive_time = cpu_to_le32(-1); | ||
826 | params_le.home_time = cpu_to_le32(-1); | ||
827 | /* Scan is aborted by setting channel_list[0] to -1 */ | ||
828 | params_le.channel_list[0] = cpu_to_le16(-1); | ||
829 | /* E-Scan (or anyother type) can be aborted by SCAN */ | ||
830 | err = brcmf_exec_dcmd(ndev, BRCMF_C_SCAN, ¶ms_le, | ||
831 | sizeof(params_le)); | ||
832 | if (err) | ||
833 | WL_ERR("Scan abort failed\n"); | ||
834 | } | ||
835 | if (scan_request) { | ||
836 | WL_SCAN("ESCAN Completed scan: %s\n", | ||
837 | aborted ? "Aborted" : "Done"); | ||
838 | cfg80211_scan_done(scan_request, aborted); | ||
839 | brcmf_set_mpc(ndev, 1); | ||
840 | } | ||
841 | if (!test_and_clear_bit(WL_STATUS_SCANNING, &cfg_priv->status)) { | ||
842 | WL_ERR("Scan complete while device not scanning\n"); | ||
843 | return -EPERM; | ||
844 | } | ||
845 | |||
846 | return err; | ||
847 | } | ||
848 | |||
849 | static s32 | ||
850 | brcmf_run_escan(struct brcmf_cfg80211_priv *cfg_priv, struct net_device *ndev, | ||
851 | struct cfg80211_scan_request *request, u16 action) | ||
852 | { | ||
853 | s32 params_size = BRCMF_SCAN_PARAMS_FIXED_SIZE + | ||
854 | offsetof(struct brcmf_escan_params_le, params_le); | ||
855 | struct brcmf_escan_params_le *params; | ||
856 | s32 err = 0; | ||
857 | |||
858 | WL_SCAN("E-SCAN START\n"); | ||
859 | |||
860 | if (request != NULL) { | ||
861 | /* Allocate space for populating ssids in struct */ | ||
862 | params_size += sizeof(u32) * ((request->n_channels + 1) / 2); | ||
863 | |||
864 | /* Allocate space for populating ssids in struct */ | ||
865 | params_size += sizeof(struct brcmf_ssid) * request->n_ssids; | ||
866 | } | ||
867 | |||
868 | params = kzalloc(params_size, GFP_KERNEL); | ||
869 | if (!params) { | ||
870 | err = -ENOMEM; | ||
871 | goto exit; | ||
872 | } | ||
873 | BUG_ON(params_size + sizeof("escan") >= BRCMF_DCMD_MEDLEN); | ||
874 | brcmf_escan_prep(¶ms->params_le, request); | ||
875 | params->version = cpu_to_le32(BRCMF_ESCAN_REQ_VERSION); | ||
876 | params->action = cpu_to_le16(action); | ||
877 | params->sync_id = cpu_to_le16(0x1234); | ||
878 | |||
879 | err = brcmf_dev_iovar_setbuf(ndev, "escan", params, params_size, | ||
880 | cfg_priv->escan_ioctl_buf, BRCMF_DCMD_MEDLEN); | ||
881 | if (err) { | ||
882 | if (err == -EBUSY) | ||
883 | WL_INFO("system busy : escan canceled\n"); | ||
884 | else | ||
885 | WL_ERR("error (%d)\n", err); | ||
886 | } | ||
887 | |||
888 | kfree(params); | ||
889 | exit: | ||
890 | return err; | ||
891 | } | ||
892 | |||
893 | static s32 | ||
894 | brcmf_do_escan(struct brcmf_cfg80211_priv *cfg_priv, struct wiphy *wiphy, | ||
895 | struct net_device *ndev, struct cfg80211_scan_request *request) | ||
896 | { | ||
897 | s32 err; | ||
898 | __le32 passive_scan; | ||
899 | struct brcmf_scan_results *results; | ||
900 | |||
901 | WL_SCAN("Enter\n"); | ||
902 | cfg_priv->escan_info.ndev = ndev; | ||
903 | cfg_priv->escan_info.wiphy = wiphy; | ||
904 | cfg_priv->escan_info.escan_state = WL_ESCAN_STATE_SCANNING; | ||
905 | passive_scan = cfg_priv->active_scan ? 0 : cpu_to_le32(1); | ||
906 | err = brcmf_exec_dcmd(ndev, BRCMF_C_SET_PASSIVE_SCAN, | ||
907 | &passive_scan, sizeof(passive_scan)); | ||
908 | if (err) { | ||
909 | WL_ERR("error (%d)\n", err); | ||
910 | return err; | ||
911 | } | ||
912 | brcmf_set_mpc(ndev, 0); | ||
913 | results = (struct brcmf_scan_results *)cfg_priv->escan_info.escan_buf; | ||
914 | results->version = 0; | ||
915 | results->count = 0; | ||
916 | results->buflen = WL_ESCAN_RESULTS_FIXED_SIZE; | ||
917 | |||
918 | err = brcmf_run_escan(cfg_priv, ndev, request, WL_ESCAN_ACTION_START); | ||
919 | if (err) | ||
920 | brcmf_set_mpc(ndev, 1); | ||
921 | return err; | ||
922 | } | ||
923 | |||
924 | static s32 | ||
925 | brcmf_cfg80211_escan(struct wiphy *wiphy, struct net_device *ndev, | ||
926 | struct cfg80211_scan_request *request, | ||
927 | struct cfg80211_ssid *this_ssid) | ||
928 | { | ||
929 | struct brcmf_cfg80211_priv *cfg_priv = ndev_to_cfg(ndev); | ||
930 | struct cfg80211_ssid *ssids; | ||
931 | struct brcmf_cfg80211_scan_req *sr = cfg_priv->scan_req_int; | ||
932 | __le32 passive_scan; | ||
933 | bool escan_req; | ||
934 | bool spec_scan; | ||
935 | s32 err; | ||
936 | u32 SSID_len; | ||
937 | |||
938 | WL_SCAN("START ESCAN\n"); | ||
939 | |||
940 | if (test_bit(WL_STATUS_SCANNING, &cfg_priv->status)) { | ||
941 | WL_ERR("Scanning already : status (%lu)\n", cfg_priv->status); | ||
942 | return -EAGAIN; | ||
943 | } | ||
944 | if (test_bit(WL_STATUS_SCAN_ABORTING, &cfg_priv->status)) { | ||
945 | WL_ERR("Scanning being aborted : status (%lu)\n", | ||
946 | cfg_priv->status); | ||
947 | return -EAGAIN; | ||
948 | } | ||
949 | if (test_bit(WL_STATUS_CONNECTING, &cfg_priv->status)) { | ||
950 | WL_ERR("Connecting : status (%lu)\n", | ||
951 | cfg_priv->status); | ||
952 | return -EAGAIN; | ||
953 | } | ||
954 | |||
955 | /* Arm scan timeout timer */ | ||
956 | mod_timer(&cfg_priv->escan_timeout, jiffies + | ||
957 | WL_ESCAN_TIMER_INTERVAL_MS * HZ / 1000); | ||
958 | |||
959 | escan_req = false; | ||
960 | if (request) { | ||
961 | /* scan bss */ | ||
962 | ssids = request->ssids; | ||
963 | escan_req = true; | ||
964 | } else { | ||
965 | /* scan in ibss */ | ||
966 | /* we don't do escan in ibss */ | ||
967 | ssids = this_ssid; | ||
968 | } | ||
969 | |||
970 | cfg_priv->scan_request = request; | ||
971 | set_bit(WL_STATUS_SCANNING, &cfg_priv->status); | ||
972 | if (escan_req) { | ||
973 | err = brcmf_do_escan(cfg_priv, wiphy, ndev, request); | ||
974 | if (!err) | ||
975 | return err; | ||
976 | else | ||
977 | goto scan_out; | ||
978 | } else { | ||
979 | WL_SCAN("ssid \"%s\", ssid_len (%d)\n", | ||
980 | ssids->ssid, ssids->ssid_len); | ||
981 | memset(&sr->ssid_le, 0, sizeof(sr->ssid_le)); | ||
982 | SSID_len = min_t(u8, sizeof(sr->ssid_le.SSID), ssids->ssid_len); | ||
983 | sr->ssid_le.SSID_len = cpu_to_le32(0); | ||
984 | spec_scan = false; | ||
985 | if (SSID_len) { | ||
986 | memcpy(sr->ssid_le.SSID, ssids->ssid, SSID_len); | ||
987 | sr->ssid_le.SSID_len = cpu_to_le32(SSID_len); | ||
988 | spec_scan = true; | ||
989 | } else | ||
990 | WL_SCAN("Broadcast scan\n"); | ||
991 | |||
992 | passive_scan = cfg_priv->active_scan ? 0 : cpu_to_le32(1); | ||
993 | err = brcmf_exec_dcmd(ndev, BRCMF_C_SET_PASSIVE_SCAN, | ||
994 | &passive_scan, sizeof(passive_scan)); | ||
995 | if (err) { | ||
996 | WL_ERR("WLC_SET_PASSIVE_SCAN error (%d)\n", err); | ||
997 | goto scan_out; | ||
998 | } | ||
999 | brcmf_set_mpc(ndev, 0); | ||
1000 | err = brcmf_exec_dcmd(ndev, BRCMF_C_SCAN, &sr->ssid_le, | ||
1001 | sizeof(sr->ssid_le)); | ||
1002 | if (err) { | ||
1003 | if (err == -EBUSY) | ||
1004 | WL_INFO("BUSY: scan for \"%s\" canceled\n", | ||
1005 | sr->ssid_le.SSID); | ||
1006 | else | ||
1007 | WL_ERR("WLC_SCAN error (%d)\n", err); | ||
1008 | |||
1009 | brcmf_set_mpc(ndev, 1); | ||
1010 | goto scan_out; | ||
1011 | } | ||
1012 | } | ||
1013 | |||
1014 | return 0; | ||
1015 | |||
1016 | scan_out: | ||
1017 | clear_bit(WL_STATUS_SCANNING, &cfg_priv->status); | ||
1018 | if (timer_pending(&cfg_priv->escan_timeout)) | ||
1019 | del_timer_sync(&cfg_priv->escan_timeout); | ||
1020 | cfg_priv->scan_request = NULL; | ||
1021 | return err; | ||
1022 | } | ||
1023 | |||
694 | static s32 | 1024 | static s32 |
695 | brcmf_cfg80211_scan(struct wiphy *wiphy, | 1025 | brcmf_cfg80211_scan(struct wiphy *wiphy, |
696 | struct cfg80211_scan_request *request) | 1026 | struct cfg80211_scan_request *request) |
697 | { | 1027 | { |
698 | struct net_device *ndev = request->wdev->netdev; | 1028 | struct net_device *ndev = request->wdev->netdev; |
1029 | struct brcmf_cfg80211_priv *cfg_priv = ndev_to_cfg(ndev); | ||
699 | s32 err = 0; | 1030 | s32 err = 0; |
700 | 1031 | ||
701 | WL_TRACE("Enter\n"); | 1032 | WL_TRACE("Enter\n"); |
@@ -703,7 +1034,11 @@ brcmf_cfg80211_scan(struct wiphy *wiphy, | |||
703 | if (!check_sys_up(wiphy)) | 1034 | if (!check_sys_up(wiphy)) |
704 | return -EIO; | 1035 | return -EIO; |
705 | 1036 | ||
706 | err = brcmf_cfg80211_iscan(wiphy, ndev, request, NULL); | 1037 | if (cfg_priv->iscan_on) |
1038 | err = brcmf_cfg80211_iscan(wiphy, ndev, request, NULL); | ||
1039 | else if (cfg_priv->escan_on) | ||
1040 | err = brcmf_cfg80211_escan(wiphy, ndev, request, NULL); | ||
1041 | |||
707 | if (err) | 1042 | if (err) |
708 | WL_ERR("scan error (%d)\n", err); | 1043 | WL_ERR("scan error (%d)\n", err); |
709 | 1044 | ||
@@ -2472,6 +2807,175 @@ static s32 brcmf_init_iscan(struct brcmf_cfg80211_priv *cfg_priv) | |||
2472 | return err; | 2807 | return err; |
2473 | } | 2808 | } |
2474 | 2809 | ||
2810 | static void brcmf_cfg80211_escan_timeout_worker(struct work_struct *work) | ||
2811 | { | ||
2812 | struct brcmf_cfg80211_priv *cfg_priv = | ||
2813 | container_of(work, struct brcmf_cfg80211_priv, | ||
2814 | escan_timeout_work); | ||
2815 | |||
2816 | brcmf_notify_escan_complete(cfg_priv, | ||
2817 | cfg_priv->escan_info.ndev, true, true); | ||
2818 | } | ||
2819 | |||
2820 | static void brcmf_escan_timeout(unsigned long data) | ||
2821 | { | ||
2822 | struct brcmf_cfg80211_priv *cfg_priv = | ||
2823 | (struct brcmf_cfg80211_priv *)data; | ||
2824 | |||
2825 | if (cfg_priv->scan_request) { | ||
2826 | WL_ERR("timer expired\n"); | ||
2827 | if (cfg_priv->escan_on) | ||
2828 | schedule_work(&cfg_priv->escan_timeout_work); | ||
2829 | } | ||
2830 | } | ||
2831 | |||
2832 | static s32 | ||
2833 | brcmf_compare_update_same_bss(struct brcmf_bss_info_le *bss, | ||
2834 | struct brcmf_bss_info_le *bss_info_le) | ||
2835 | { | ||
2836 | if (!memcmp(&bss_info_le->BSSID, &bss->BSSID, ETH_ALEN) && | ||
2837 | (CHSPEC_BAND(le16_to_cpu(bss_info_le->chanspec)) == | ||
2838 | CHSPEC_BAND(le16_to_cpu(bss->chanspec))) && | ||
2839 | bss_info_le->SSID_len == bss->SSID_len && | ||
2840 | !memcmp(bss_info_le->SSID, bss->SSID, bss_info_le->SSID_len)) { | ||
2841 | if ((bss->flags & WLC_BSS_RSSI_ON_CHANNEL) == | ||
2842 | (bss_info_le->flags & WLC_BSS_RSSI_ON_CHANNEL)) { | ||
2843 | /* preserve max RSSI if the measurements are | ||
2844 | * both on-channel or both off-channel | ||
2845 | */ | ||
2846 | if (bss_info_le->RSSI > bss->RSSI) | ||
2847 | bss->RSSI = bss_info_le->RSSI; | ||
2848 | } else if ((bss->flags & WLC_BSS_RSSI_ON_CHANNEL) && | ||
2849 | (bss_info_le->flags & WLC_BSS_RSSI_ON_CHANNEL) == 0) { | ||
2850 | /* preserve the on-channel rssi measurement | ||
2851 | * if the new measurement is off channel | ||
2852 | */ | ||
2853 | bss->RSSI = bss_info_le->RSSI; | ||
2854 | bss->flags |= WLC_BSS_RSSI_ON_CHANNEL; | ||
2855 | } | ||
2856 | return 1; | ||
2857 | } | ||
2858 | return 0; | ||
2859 | } | ||
2860 | |||
2861 | static s32 | ||
2862 | brcmf_cfg80211_escan_handler(struct brcmf_cfg80211_priv *cfg_priv, | ||
2863 | struct net_device *ndev, | ||
2864 | const struct brcmf_event_msg *e, void *data) | ||
2865 | { | ||
2866 | s32 status; | ||
2867 | s32 err = 0; | ||
2868 | struct brcmf_escan_result_le *escan_result_le; | ||
2869 | struct brcmf_bss_info_le *bss_info_le; | ||
2870 | struct brcmf_bss_info_le *bss = NULL; | ||
2871 | u32 bi_length; | ||
2872 | struct brcmf_scan_results *list; | ||
2873 | u32 i; | ||
2874 | |||
2875 | status = be32_to_cpu(e->status); | ||
2876 | |||
2877 | if (!ndev || !cfg_priv->escan_on || | ||
2878 | !test_bit(WL_STATUS_SCANNING, &cfg_priv->status)) { | ||
2879 | WL_ERR("scan not ready ndev %p wl->escan_on %d drv_status %x\n", | ||
2880 | ndev, cfg_priv->escan_on, | ||
2881 | !test_bit(WL_STATUS_SCANNING, &cfg_priv->status)); | ||
2882 | return -EPERM; | ||
2883 | } | ||
2884 | |||
2885 | if (status == BRCMF_E_STATUS_PARTIAL) { | ||
2886 | WL_SCAN("ESCAN Partial result\n"); | ||
2887 | escan_result_le = (struct brcmf_escan_result_le *) data; | ||
2888 | if (!escan_result_le) { | ||
2889 | WL_ERR("Invalid escan result (NULL pointer)\n"); | ||
2890 | goto exit; | ||
2891 | } | ||
2892 | if (!cfg_priv->scan_request) { | ||
2893 | WL_SCAN("result without cfg80211 request\n"); | ||
2894 | goto exit; | ||
2895 | } | ||
2896 | |||
2897 | if (le16_to_cpu(escan_result_le->bss_count) != 1) { | ||
2898 | WL_ERR("Invalid bss_count %d: ignoring\n", | ||
2899 | escan_result_le->bss_count); | ||
2900 | goto exit; | ||
2901 | } | ||
2902 | bss_info_le = &escan_result_le->bss_info_le; | ||
2903 | |||
2904 | bi_length = le32_to_cpu(bss_info_le->length); | ||
2905 | if (bi_length != (le32_to_cpu(escan_result_le->buflen) - | ||
2906 | WL_ESCAN_RESULTS_FIXED_SIZE)) { | ||
2907 | WL_ERR("Invalid bss_info length %d: ignoring\n", | ||
2908 | bi_length); | ||
2909 | goto exit; | ||
2910 | } | ||
2911 | |||
2912 | if (!(cfg_to_wiphy(cfg_priv)->interface_modes & | ||
2913 | BIT(NL80211_IFTYPE_ADHOC))) { | ||
2914 | if (le16_to_cpu(bss_info_le->capability) & | ||
2915 | WLAN_CAPABILITY_IBSS) { | ||
2916 | WL_ERR("Ignoring IBSS result\n"); | ||
2917 | goto exit; | ||
2918 | } | ||
2919 | } | ||
2920 | |||
2921 | list = (struct brcmf_scan_results *) | ||
2922 | cfg_priv->escan_info.escan_buf; | ||
2923 | if (bi_length > WL_ESCAN_BUF_SIZE - list->buflen) { | ||
2924 | WL_ERR("Buffer is too small: ignoring\n"); | ||
2925 | goto exit; | ||
2926 | } | ||
2927 | |||
2928 | for (i = 0; i < list->count; i++) { | ||
2929 | bss = bss ? (struct brcmf_bss_info_le *) | ||
2930 | ((unsigned char *)bss + | ||
2931 | le32_to_cpu(bss->length)) : list->bss_info_le; | ||
2932 | if (brcmf_compare_update_same_bss(bss, bss_info_le)) | ||
2933 | goto exit; | ||
2934 | } | ||
2935 | memcpy(&(cfg_priv->escan_info.escan_buf[list->buflen]), | ||
2936 | bss_info_le, bi_length); | ||
2937 | list->version = le32_to_cpu(bss_info_le->version); | ||
2938 | list->buflen += bi_length; | ||
2939 | list->count++; | ||
2940 | } else { | ||
2941 | cfg_priv->escan_info.escan_state = WL_ESCAN_STATE_IDLE; | ||
2942 | if (cfg_priv->scan_request) { | ||
2943 | cfg_priv->bss_list = (struct brcmf_scan_results *) | ||
2944 | cfg_priv->escan_info.escan_buf; | ||
2945 | brcmf_inform_bss(cfg_priv); | ||
2946 | if (status == BRCMF_E_STATUS_SUCCESS) { | ||
2947 | WL_SCAN("ESCAN Completed\n"); | ||
2948 | brcmf_notify_escan_complete(cfg_priv, ndev, | ||
2949 | false, false); | ||
2950 | } else { | ||
2951 | WL_ERR("ESCAN Aborted, Event 0x%x\n", status); | ||
2952 | brcmf_notify_escan_complete(cfg_priv, ndev, | ||
2953 | true, false); | ||
2954 | } | ||
2955 | brcmf_set_mpc(ndev, 1); | ||
2956 | } else | ||
2957 | WL_ERR("Unexpected scan result 0x%x\n", status); | ||
2958 | } | ||
2959 | exit: | ||
2960 | return err; | ||
2961 | } | ||
2962 | |||
2963 | static void brcmf_init_escan(struct brcmf_cfg80211_priv *cfg_priv) | ||
2964 | { | ||
2965 | |||
2966 | if (cfg_priv->escan_on) { | ||
2967 | cfg_priv->el.handler[BRCMF_E_ESCAN_RESULT] = | ||
2968 | brcmf_cfg80211_escan_handler; | ||
2969 | cfg_priv->escan_info.escan_state = WL_ESCAN_STATE_IDLE; | ||
2970 | /* Init scan_timeout timer */ | ||
2971 | init_timer(&cfg_priv->escan_timeout); | ||
2972 | cfg_priv->escan_timeout.data = (unsigned long) cfg_priv; | ||
2973 | cfg_priv->escan_timeout.function = brcmf_escan_timeout; | ||
2974 | INIT_WORK(&cfg_priv->escan_timeout_work, | ||
2975 | brcmf_cfg80211_escan_timeout_worker); | ||
2976 | } | ||
2977 | } | ||
2978 | |||
2475 | static __always_inline void brcmf_delay(u32 ms) | 2979 | static __always_inline void brcmf_delay(u32 ms) |
2476 | { | 2980 | { |
2477 | if (ms < 1000 / HZ) { | 2981 | if (ms < 1000 / HZ) { |
@@ -3240,6 +3744,8 @@ static void brcmf_deinit_priv_mem(struct brcmf_cfg80211_priv *cfg_priv) | |||
3240 | cfg_priv->profile = NULL; | 3744 | cfg_priv->profile = NULL; |
3241 | kfree(cfg_priv->scan_req_int); | 3745 | kfree(cfg_priv->scan_req_int); |
3242 | cfg_priv->scan_req_int = NULL; | 3746 | cfg_priv->scan_req_int = NULL; |
3747 | kfree(cfg_priv->escan_ioctl_buf); | ||
3748 | cfg_priv->escan_ioctl_buf = NULL; | ||
3243 | kfree(cfg_priv->dcmd_buf); | 3749 | kfree(cfg_priv->dcmd_buf); |
3244 | cfg_priv->dcmd_buf = NULL; | 3750 | cfg_priv->dcmd_buf = NULL; |
3245 | kfree(cfg_priv->extra_buf); | 3751 | kfree(cfg_priv->extra_buf); |
@@ -3268,6 +3774,9 @@ static s32 brcmf_init_priv_mem(struct brcmf_cfg80211_priv *cfg_priv) | |||
3268 | GFP_KERNEL); | 3774 | GFP_KERNEL); |
3269 | if (!cfg_priv->scan_req_int) | 3775 | if (!cfg_priv->scan_req_int) |
3270 | goto init_priv_mem_out; | 3776 | goto init_priv_mem_out; |
3777 | cfg_priv->escan_ioctl_buf = kzalloc(BRCMF_DCMD_MEDLEN, GFP_KERNEL); | ||
3778 | if (!cfg_priv->escan_ioctl_buf) | ||
3779 | goto init_priv_mem_out; | ||
3271 | cfg_priv->dcmd_buf = kzalloc(WL_DCMD_LEN_MAX, GFP_KERNEL); | 3780 | cfg_priv->dcmd_buf = kzalloc(WL_DCMD_LEN_MAX, GFP_KERNEL); |
3272 | if (!cfg_priv->dcmd_buf) | 3781 | if (!cfg_priv->dcmd_buf) |
3273 | goto init_priv_mem_out; | 3782 | goto init_priv_mem_out; |
@@ -3404,8 +3913,17 @@ static s32 wl_init_priv(struct brcmf_cfg80211_priv *cfg_priv) | |||
3404 | 3913 | ||
3405 | cfg_priv->scan_request = NULL; | 3914 | cfg_priv->scan_request = NULL; |
3406 | cfg_priv->pwr_save = true; | 3915 | cfg_priv->pwr_save = true; |
3916 | #ifdef CONFIG_BRCMISCAN | ||
3407 | cfg_priv->iscan_on = true; /* iscan on & off switch. | 3917 | cfg_priv->iscan_on = true; /* iscan on & off switch. |
3408 | we enable iscan per default */ | 3918 | we enable iscan per default */ |
3919 | cfg_priv->escan_on = false; /* escan on & off switch. | ||
3920 | we disable escan per default */ | ||
3921 | #else | ||
3922 | cfg_priv->iscan_on = false; /* iscan on & off switch. | ||
3923 | we disable iscan per default */ | ||
3924 | cfg_priv->escan_on = true; /* escan on & off switch. | ||
3925 | we enable escan per default */ | ||
3926 | #endif | ||
3409 | cfg_priv->roam_on = true; /* roam on & off switch. | 3927 | cfg_priv->roam_on = true; /* roam on & off switch. |
3410 | we enable roam per default */ | 3928 | we enable roam per default */ |
3411 | 3929 | ||
@@ -3423,6 +3941,7 @@ static s32 wl_init_priv(struct brcmf_cfg80211_priv *cfg_priv) | |||
3423 | err = brcmf_init_iscan(cfg_priv); | 3941 | err = brcmf_init_iscan(cfg_priv); |
3424 | if (err) | 3942 | if (err) |
3425 | return err; | 3943 | return err; |
3944 | brcmf_init_escan(cfg_priv); | ||
3426 | brcmf_init_conf(cfg_priv->conf); | 3945 | brcmf_init_conf(cfg_priv->conf); |
3427 | brcmf_init_prof(cfg_priv->profile); | 3946 | brcmf_init_prof(cfg_priv->profile); |
3428 | brcmf_link_down(cfg_priv); | 3947 | brcmf_link_down(cfg_priv); |
@@ -3581,6 +4100,7 @@ static s32 brcmf_dongle_eventmsg(struct net_device *ndev) | |||
3581 | setbit(eventmask, BRCMF_E_TXFAIL); | 4100 | setbit(eventmask, BRCMF_E_TXFAIL); |
3582 | setbit(eventmask, BRCMF_E_JOIN_START); | 4101 | setbit(eventmask, BRCMF_E_JOIN_START); |
3583 | setbit(eventmask, BRCMF_E_SCAN_COMPLETE); | 4102 | setbit(eventmask, BRCMF_E_SCAN_COMPLETE); |
4103 | setbit(eventmask, BRCMF_E_ESCAN_RESULT); | ||
3584 | 4104 | ||
3585 | brcmf_c_mkiovar("event_msgs", eventmask, BRCMF_EVENTING_MASK_LEN, | 4105 | brcmf_c_mkiovar("event_msgs", eventmask, BRCMF_EVENTING_MASK_LEN, |
3586 | iovbuf, sizeof(iovbuf)); | 4106 | iovbuf, sizeof(iovbuf)); |
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.h b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.h index b5d9b36df3d0..3b2129738d30 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.h | |||
@@ -123,6 +123,13 @@ do { \ | |||
123 | #define WL_SCAN_UNASSOC_TIME 40 | 123 | #define WL_SCAN_UNASSOC_TIME 40 |
124 | #define WL_SCAN_PASSIVE_TIME 120 | 124 | #define WL_SCAN_PASSIVE_TIME 120 |
125 | 125 | ||
126 | #define WL_ESCAN_BUF_SIZE (1024 * 64) | ||
127 | #define WL_ESCAN_TIMER_INTERVAL_MS 8000 /* E-Scan timeout */ | ||
128 | |||
129 | #define WL_ESCAN_ACTION_START 1 | ||
130 | #define WL_ESCAN_ACTION_CONTINUE 2 | ||
131 | #define WL_ESCAN_ACTION_ABORT 3 | ||
132 | |||
126 | /* dongle status */ | 133 | /* dongle status */ |
127 | enum wl_status { | 134 | enum wl_status { |
128 | WL_STATUS_READY, | 135 | WL_STATUS_READY, |
@@ -275,6 +282,19 @@ struct brcmf_cfg80211_pmk_list { | |||
275 | struct pmkid foo[MAXPMKID - 1]; | 282 | struct pmkid foo[MAXPMKID - 1]; |
276 | }; | 283 | }; |
277 | 284 | ||
285 | /* dongle escan state */ | ||
286 | enum wl_escan_state { | ||
287 | WL_ESCAN_STATE_IDLE, | ||
288 | WL_ESCAN_STATE_SCANNING | ||
289 | }; | ||
290 | |||
291 | struct escan_info { | ||
292 | u32 escan_state; | ||
293 | u8 escan_buf[WL_ESCAN_BUF_SIZE]; | ||
294 | struct wiphy *wiphy; | ||
295 | struct net_device *ndev; | ||
296 | }; | ||
297 | |||
278 | /* dongle private data of cfg80211 interface */ | 298 | /* dongle private data of cfg80211 interface */ |
279 | struct brcmf_cfg80211_priv { | 299 | struct brcmf_cfg80211_priv { |
280 | struct wireless_dev *wdev; /* representing wl cfg80211 device */ | 300 | struct wireless_dev *wdev; /* representing wl cfg80211 device */ |
@@ -315,6 +335,11 @@ struct brcmf_cfg80211_priv { | |||
315 | u8 *dcmd_buf; /* dcmd buffer */ | 335 | u8 *dcmd_buf; /* dcmd buffer */ |
316 | u8 *extra_buf; /* maily to grab assoc information */ | 336 | u8 *extra_buf; /* maily to grab assoc information */ |
317 | struct dentry *debugfsdir; | 337 | struct dentry *debugfsdir; |
338 | bool escan_on; /* escan on/off switch */ | ||
339 | struct escan_info escan_info; /* escan information */ | ||
340 | struct timer_list escan_timeout; /* Timer for catch scan timeout */ | ||
341 | struct work_struct escan_timeout_work; /* scan timeout worker */ | ||
342 | u8 *escan_ioctl_buf; | ||
318 | u8 ci[0] __aligned(NETDEV_ALIGN); | 343 | u8 ci[0] __aligned(NETDEV_ALIGN); |
319 | }; | 344 | }; |
320 | 345 | ||