/* * Linux cfg80211 driver * * Copyright (C) 1999-2015, Broadcom Corporation * Copyright (C) 2019-2020, NVIDIA Corporation. All rights reserved. * * Portions contributed by Nvidia * Copyright (C) 2015-2020, NVIDIA Corporation. All rights reserved. * * Unless you and Broadcom execute a separate written software license * agreement governing use of this software, this software is licensed to you * under the terms of the GNU General Public License version 2 (the "GPL"), * available at http://www.broadcom.com/licenses/GPLv2.php, with the * following added to such license: * * As a special exception, the copyright holders of this software give you * permission to link this software with independent modules, and to copy and * distribute the resulting executable under terms of your choice, provided that * you also meet, for each linked independent module, the terms and conditions of * the license of that module. An independent module is a module which is not * derived from this software. The special exception does not apply to any * modifications of the software. * * Notwithstanding the above, under no circumstances may you combine this * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * * $Id: wl_cfg80211.c 534534 2015-02-13 11:46:03Z $ */ /* */ #include #include "dynamic.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef PNO_SUPPORT #include #endif /* PNO_SUPPORT */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef WL_NAN #include #endif /* WL_NAN */ #ifdef PROP_TXSTATUS #include #endif #if (LINUX_VERSION_CODE > KERNEL_VERSION(3, 13, 0)) || defined(WL_VENDOR_EXT_SUPPORT) #include #endif /* (LINUX_VERSION_CODE > KERNEL_VERSION(3, 13, 0)) || defined(WL_VENDOR_EXT_SUPPORT) */ #ifdef WL11U #if !defined(WL_ENABLE_P2P_IF) && !defined(WL_CFG80211_P2P_DEV_IF) #error You should enable 'WL_ENABLE_P2P_IF' or 'WL_CFG80211_P2P_DEV_IF' \ according to Kernel version and is supported only in Android-JB #endif /* !WL_ENABLE_P2P_IF && !WL_CFG80211_P2P_DEV_IF */ #endif /* WL11U */ #ifdef CONFIG_BCMDHD_CUSTOM_SYSFS_TEGRA #include "dhd_custom_sysfs_tegra.h" #include "dhd_custom_sysfs_tegra_scan.h" #include "dhd_custom_sysfs_tegra_stat.h" #endif #ifdef CONFIG_BCMDHD_CUSTOM_NET_BW_EST_TEGRA #include "dhd_custom_net_bw_est_tegra.h" #endif #define IW_WSEC_ENABLED(wsec) ((wsec) & (WEP_ENABLED | TKIP_ENABLED | AES_ENABLED)) static struct device *cfg80211_parent_dev = NULL; /* g_bcm_cfg should be static. Do not change */ static struct bcm_cfg80211 *g_bcm_cfg = NULL; u32 wl_dbg_level = WL_DBG_ERR; extern int op_mode; #ifdef WLAIBSS_MCHAN #define IBSS_IF_NAME "ibss%d" #endif /* WLAIBSS_MCHAN */ #ifdef VSDB /* sleep time to keep STA's connecting or connection for continuous af tx or finding a peer */ #define DEFAULT_SLEEP_TIME_VSDB 120 #define OFF_CHAN_TIME_THRESHOLD_MS 200 #define AF_RETRY_DELAY_TIME 40 /* if sta is connected or connecting, sleep for a while before retry af tx or finding a peer */ #define WL_AF_TX_KEEP_PRI_CONNECTION_VSDB(cfg) \ do { \ if (wl_get_drv_status(cfg, CONNECTED, bcmcfg_to_prmry_ndev(cfg)) || \ wl_get_drv_status(cfg, CONNECTING, bcmcfg_to_prmry_ndev(cfg))) { \ OSL_SLEEP(DEFAULT_SLEEP_TIME_VSDB); \ } \ } while (0) #else /* VSDB */ /* if not VSDB, do nothing */ #define WL_AF_TX_KEEP_PRI_CONNECTION_VSDB(cfg) #endif /* VSDB */ #ifdef WL_CFG80211_SYNC_GON #define WL_DRV_STATUS_SENDING_AF_FRM_EXT(cfg) \ (wl_get_drv_status_all(cfg, SENDING_ACT_FRM) || \ wl_get_drv_status_all(cfg, WAITING_NEXT_ACT_FRM_LISTEN)) #else #define WL_DRV_STATUS_SENDING_AF_FRM_EXT(cfg) wl_get_drv_status_all(cfg, SENDING_ACT_FRM) #endif /* WL_CFG80211_SYNC_GON */ #define WL_IS_P2P_DEV_EVENT(e) ((e->emsg.ifidx == 0) && \ (e->emsg.bsscfgidx == P2PAPI_BSSCFG_DEVICE)) #define COEX_DHCP #define WLAN_EID_SSID 0 #define CH_MIN_5G_CHANNEL 34 #define CH_MIN_2G_CHANNEL 1 enum rmc_event_type { RMC_EVENT_NONE, RMC_EVENT_LEADER_CHECK_FAIL }; /* This is to override regulatory domains defined in cfg80211 module (reg.c) * By default world regulatory domain defined in reg.c puts the flags NL80211_RRF_PASSIVE_SCAN * and NL80211_RRF_NO_IBSS for 5GHz channels (for 36..48 and 149..165). * With respect to these flags, wpa_supplicant doesn't start p2p operations on 5GHz channels. * All the chnages in world regulatory domain are to be done here. */ static const struct ieee80211_regdomain brcm_regdom = { .n_reg_rules = 4, .alpha2 = "99", .reg_rules = { /* IEEE 802.11b/g, channels 1..11 */ REG_RULE(2412-10, 2472+10, 40, 6, 20, 0), /* If any */ /* IEEE 802.11 channel 14 - Only JP enables * this and for 802.11b only */ REG_RULE(2484-10, 2484+10, 20, 6, 20, 0), /* IEEE 802.11a, channel 36..64 */ REG_RULE(5150-10, 5350+10, 40, 6, 20, 0), /* IEEE 802.11a, channel 100..165 */ REG_RULE(5470-10, 5850+10, 40, 6, 20, 0), } }; #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 0)) && \ (defined(WL_IFACE_COMB_NUM_CHANNELS) || defined(WL_CFG80211_P2P_DEV_IF)) /* * Possible interface combinations supported by driver * * ADHOC Mode - #ADHOC <= 1 on channels = 1 * SoftAP Mode - #AP <= 1 on channels = 1 * STA + P2P Mode - #STA <= 2, #{P2P-GO, P2P-client} <= 1, #P2P-device <= 1 * on channels = 2 */ static const struct ieee80211_iface_limit common_if_limits[] = { { .max = 1, .types = BIT(NL80211_IFTYPE_AP), }, { /* * During P2P-GO removal, P2P-GO is first changed to STA and later only * removed. So setting maximum possible number of STA interfaces according * to kernel version. * * less than linux-3.8 - max:3 (wlan0 + p2p0 + group removal of p2p-p2p0-x) * linux-3.8 and above - max:2 (wlan0 + group removal of p2p-wlan0-x) */ #ifdef WL_ENABLE_P2P_IF .max = 3, #else .max = 2, #endif /* WL_ENABLE_P2P_IF */ .types = BIT(NL80211_IFTYPE_STATION), }, { .max = 2, .types = BIT(NL80211_IFTYPE_P2P_GO) | BIT(NL80211_IFTYPE_P2P_CLIENT), }, #if defined(WL_CFG80211_P2P_DEV_IF) { .max = 1, .types = BIT(NL80211_IFTYPE_P2P_DEVICE), }, #endif /* WL_CFG80211_P2P_DEV_IF */ { .max = 1, .types = BIT(NL80211_IFTYPE_ADHOC), }, }; #ifdef BCM4330_CHIP #define NUM_DIFF_CHANNELS 1 #else #define NUM_DIFF_CHANNELS 2 #endif static const struct ieee80211_iface_combination common_iface_combinations[] = { { #ifndef CONFIG_BCMDHD_DISABLE_MCC .num_different_channels = 2, #else .num_different_channels = 1, #endif .max_interfaces = 4, .limits = common_if_limits, .n_limits = ARRAY_SIZE(common_if_limits), }, }; #endif /* LINUX_VER >= 3.0 && (WL_IFACE_COMB_NUM_CHANNELS || WL_CFG80211_P2P_DEV_IF) */ #if (LINUX_VERSION_CODE > KERNEL_VERSION(4, 6, 0)) #define IEEE80211_BAND_2GHZ NL80211_BAND_2GHZ #define IEEE80211_BAND_5GHZ NL80211_BAND_5GHZ #define IEEE80211_BAND_60GHZ NL80211_BAND_60GHZ #define IEEE80211_NUM_BANDS NUM_NL80211_BANDS #endif /* Data Element Definitions */ #define WPS_ID_CONFIG_METHODS 0x1008 #define WPS_ID_REQ_TYPE 0x103A #define WPS_ID_DEVICE_NAME 0x1011 #define WPS_ID_VERSION 0x104A #define WPS_ID_DEVICE_PWD_ID 0x1012 #define WPS_ID_REQ_DEV_TYPE 0x106A #define WPS_ID_SELECTED_REGISTRAR_CONFIG_METHODS 0x1053 #define WPS_ID_PRIM_DEV_TYPE 0x1054 /* Device Password ID */ #define DEV_PW_DEFAULT 0x0000 #define DEV_PW_USER_SPECIFIED 0x0001, #define DEV_PW_MACHINE_SPECIFIED 0x0002 #define DEV_PW_REKEY 0x0003 #define DEV_PW_PUSHBUTTON 0x0004 #define DEV_PW_REGISTRAR_SPECIFIED 0x0005 /* Config Methods */ #define WPS_CONFIG_USBA 0x0001 #define WPS_CONFIG_ETHERNET 0x0002 #define WPS_CONFIG_LABEL 0x0004 #define WPS_CONFIG_DISPLAY 0x0008 #define WPS_CONFIG_EXT_NFC_TOKEN 0x0010 #define WPS_CONFIG_INT_NFC_TOKEN 0x0020 #define WPS_CONFIG_NFC_INTERFACE 0x0040 #define WPS_CONFIG_PUSHBUTTON 0x0080 #define WPS_CONFIG_KEYPAD 0x0100 #define WPS_CONFIG_VIRT_PUSHBUTTON 0x0280 #define WPS_CONFIG_PHY_PUSHBUTTON 0x0480 #define WPS_CONFIG_VIRT_DISPLAY 0x2008 #define WPS_CONFIG_PHY_DISPLAY 0x4008 #ifdef MFP #define WL_AKM_SUITE_MFP_1X 0x000FAC05 #define WL_AKM_SUITE_MFP_PSK 0x000FAC06 #define WL_MFP_CAPABLE 0x1 #define WL_MFP_REQUIRED 0x2 #endif /* MFP */ #ifndef IBSS_COALESCE_ALLOWED #define IBSS_COALESCE_ALLOWED 0 #endif #ifndef IBSS_INITIAL_SCAN_ALLOWED #define IBSS_INITIAL_SCAN_ALLOWED 0 #endif #define CUSTOM_RETRY_MASK 0xff000000 /* Mask for retry counter of custom dwell time */ #define LONG_LISTEN_TIME 2000 /* * cfg80211_ops api/callback list */ static s32 wl_frame_get_mgmt(u16 fc, const struct ether_addr *da, const struct ether_addr *sa, const struct ether_addr *bssid, u8 **pheader, u32 *body_len, u8 *pbody); static s32 __wl_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev, struct cfg80211_scan_request *request, struct cfg80211_ssid *this_ssid); #if defined(WL_CFG80211_P2P_DEV_IF) static s32 wl_cfg80211_scan(struct wiphy *wiphy, struct cfg80211_scan_request *request); #else static s32 wl_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev, struct cfg80211_scan_request *request); #endif /* WL_CFG80211_P2P_DEV_IF */ static s32 wl_cfg80211_set_wiphy_params(struct wiphy *wiphy, u32 changed); #ifdef WLAIBSS_MCHAN static bcm_struct_cfgdev* bcm_cfg80211_add_ibss_if(struct wiphy *wiphy, char *name); static s32 bcm_cfg80211_del_ibss_if(struct wiphy *wiphy, bcm_struct_cfgdev *cfgdev); #endif /* WLAIBSS_MCHAN */ static s32 wl_cfg80211_join_ibss(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_ibss_params *params); static s32 wl_cfg80211_leave_ibss(struct wiphy *wiphy, struct net_device *dev); #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 16, 0)) static s32 wl_cfg80211_get_station(struct wiphy *wiphy, struct net_device *dev, const u8 *mac, struct station_info *sinfo); #else static s32 wl_cfg80211_get_station(struct wiphy *wiphy, struct net_device *dev, u8 *mac, struct station_info *sinfo); #endif static s32 wl_cfg80211_set_power_mgmt(struct wiphy *wiphy, struct net_device *dev, bool enabled, s32 timeout); static int wl_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_connect_params *sme); static s32 wl_cfg80211_disconnect(struct wiphy *wiphy, struct net_device *dev, u16 reason_code); #if defined(WL_CFG80211_P2P_DEV_IF) static s32 wl_cfg80211_set_tx_power(struct wiphy *wiphy, struct wireless_dev *wdev, enum nl80211_tx_power_setting type, s32 mbm); #else static s32 wl_cfg80211_set_tx_power(struct wiphy *wiphy, enum nl80211_tx_power_setting type, s32 dbm); #endif /* WL_CFG80211_P2P_DEV_IF */ #if defined(WL_CFG80211_P2P_DEV_IF) static s32 wl_cfg80211_get_tx_power(struct wiphy *wiphy, struct wireless_dev *wdev, s32 *dbm); #else static s32 wl_cfg80211_get_tx_power(struct wiphy *wiphy, s32 *dbm); #endif /* WL_CFG80211_P2P_DEV_IF */ static s32 wl_cfg80211_config_default_key(struct wiphy *wiphy, struct net_device *dev, u8 key_idx, bool unicast, bool multicast); static s32 wl_cfg80211_add_key(struct wiphy *wiphy, struct net_device *dev, u8 key_idx, bool pairwise, const u8 *mac_addr, struct key_params *params); static s32 wl_cfg80211_del_key(struct wiphy *wiphy, struct net_device *dev, u8 key_idx, bool pairwise, const u8 *mac_addr); static s32 wl_cfg80211_get_key(struct wiphy *wiphy, struct net_device *dev, u8 key_idx, bool pairwise, const u8 *mac_addr, void *cookie, void (*callback) (void *cookie, struct key_params *params)); static s32 wl_cfg80211_config_default_mgmt_key(struct wiphy *wiphy, struct net_device *dev, u8 key_idx); static s32 wl_cfg80211_resume(struct wiphy *wiphy); #if defined(WL_SUPPORT_BACKPORTED_KPATCHES) || (LINUX_VERSION_CODE >= KERNEL_VERSION(3, \ 2, 0)) static s32 wl_cfg80211_mgmt_tx_cancel_wait(struct wiphy *wiphy, bcm_struct_cfgdev *cfgdev, u64 cookie); #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0)) static s32 wl_cfg80211_del_station( struct wiphy *wiphy, struct net_device *ndev, struct station_del_parameters *params); #elif (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 16, 0)) static s32 wl_cfg80211_del_station(struct wiphy *wiphy, struct net_device *ndev, const u8* mac_addr); #else static s32 wl_cfg80211_del_station(struct wiphy *wiphy, struct net_device *ndev, u8* mac_addr); #endif #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 16, 0)) static s32 wl_cfg80211_change_station(struct wiphy *wiphy, struct net_device *dev, const u8 *mac, struct station_parameters *params); #else static s32 wl_cfg80211_change_station(struct wiphy *wiphy, struct net_device *dev, u8 *mac, struct station_parameters *params); #endif #endif /* WL_SUPPORT_BACKPORTED_KPATCHES || KERNEL_VER >= KERNEL_VERSION(3, 2, 0)) */ #if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 39)) static s32 wl_cfg80211_suspend(struct wiphy *wiphy, struct cfg80211_wowlan *wow); #else static s32 wl_cfg80211_suspend(struct wiphy *wiphy); #endif static s32 wl_cfg80211_set_pmksa(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_pmksa *pmksa); static s32 wl_cfg80211_del_pmksa(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_pmksa *pmksa); static s32 wl_cfg80211_flush_pmksa(struct wiphy *wiphy, struct net_device *dev); #ifdef P2PONEINT void wl_cfg80211_scan_abort(struct bcm_cfg80211 *cfg); #else static void wl_cfg80211_scan_abort(struct bcm_cfg80211 *cfg); #endif void wl_cfg80211_cancel_scan(struct bcm_cfg80211 *cfg); static s32 wl_notify_escan_complete(struct bcm_cfg80211 *cfg, struct net_device *ndev, bool aborted, bool fw_abort); #if (LINUX_VERSION_CODE > KERNEL_VERSION(3, 2, 0)) #if (defined(CONFIG_ARCH_MSM) && defined(TDLS_MGMT_VERSION2)) || (LINUX_VERSION_CODE < \ KERNEL_VERSION(3, 16, 0) && LINUX_VERSION_CODE >= KERNEL_VERSION(3, 15, 0)) static s32 wl_cfg80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev, u8 *peer, u8 action_code, u8 dialog_token, u16 status_code, u32 peer_capability, const u8 *data, size_t len); #elif ((LINUX_VERSION_CODE >= KERNEL_VERSION(3, 16, 0)) && \ (LINUX_VERSION_CODE < KERNEL_VERSION(3, 18, 0))) static s32 wl_cfg80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev, const u8 *peer, u8 action_code, u8 dialog_token, u16 status_code, u32 peer_capability, const u8 *data, size_t len); #elif (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 18, 0)) static s32 wl_cfg80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev, const u8 *peer, u8 action_code, u8 dialog_token, u16 status_code, u32 peer_capability, bool initiator, const u8 *data, size_t len); #else static s32 wl_cfg80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev, u8 *peer, u8 action_code, u8 dialog_token, u16 status_code, const u8 *data, size_t len); #endif /* CONFIG_ARCH_MSM && TDLS_MGMT_VERSION2 */ #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 16, 0)) static s32 wl_cfg80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev, const u8 *peer, enum nl80211_tdls_operation oper); #else static s32 wl_cfg80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev, u8 *peer, enum nl80211_tdls_operation oper); #endif #endif #ifdef WL_SCHED_SCAN #if (LINUX_VERSION_CODE > KERNEL_VERSION(4, 11, 0)) static int wl_cfg80211_sched_scan_stop(struct wiphy *wiphy, struct net_device *dev, u64 reqid); #else static int wl_cfg80211_sched_scan_stop(struct wiphy *wiphy, struct net_device *dev); #endif #endif #if defined(DUAL_STA) || defined(DUAL_STA_STATIC_IF) bcm_struct_cfgdev* wl_cfg80211_create_iface(struct wiphy *wiphy, enum nl80211_iftype iface_type, u8 *mac_addr, const char *name); s32 wl_cfg80211_del_iface(struct wiphy *wiphy, bcm_struct_cfgdev *cfgdev); #endif /* defined(DUAL_STA) || defined(DUAL_STA_STATIC_IF) */ chanspec_t wl_chspec_driver_to_host(chanspec_t chanspec); /* * event & event Q handlers for cfg80211 interfaces */ static s32 wl_create_event_handler(struct bcm_cfg80211 *cfg); static void wl_destroy_event_handler(struct bcm_cfg80211 *cfg); static s32 wl_event_handler(void *data); static void wl_init_eq(struct bcm_cfg80211 *cfg); static void wl_flush_eq(struct bcm_cfg80211 *cfg); static unsigned long wl_lock_eq(struct bcm_cfg80211 *cfg); static void wl_unlock_eq(struct bcm_cfg80211 *cfg, unsigned long flags); static void wl_init_eq_lock(struct bcm_cfg80211 *cfg); static void wl_init_event_handler(struct bcm_cfg80211 *cfg); static struct wl_event_q *wl_deq_event(struct bcm_cfg80211 *cfg); static s32 wl_enq_event(struct bcm_cfg80211 *cfg, struct net_device *ndev, u32 type, const wl_event_msg_t *msg, void *data); static void wl_put_event(struct wl_event_q *e); static void wl_wakeup_event(struct bcm_cfg80211 *cfg); static s32 wl_notify_connect_status_ap(struct bcm_cfg80211 *cfg, struct net_device *ndev, const wl_event_msg_t *e, void *data); static s32 wl_notify_connect_status(struct bcm_cfg80211 *cfg, bcm_struct_cfgdev *cfgdev, const wl_event_msg_t *e, void *data); static s32 wl_notify_roaming_status(struct bcm_cfg80211 *cfg, bcm_struct_cfgdev *cfgdev, const wl_event_msg_t *e, void *data); static s32 wl_notify_scan_status(struct bcm_cfg80211 *cfg, bcm_struct_cfgdev *cfgdev, const wl_event_msg_t *e, void *data); static s32 wl_bss_connect_done(struct bcm_cfg80211 *cfg, struct net_device *ndev, const wl_event_msg_t *e, void *data, bool completed); static s32 wl_bss_roaming_done(struct bcm_cfg80211 *cfg, struct net_device *ndev, const wl_event_msg_t *e, void *data); static s32 wl_notify_mic_status(struct bcm_cfg80211 *cfg, bcm_struct_cfgdev *cfgdev, const wl_event_msg_t *e, void *data); #ifdef BT_WIFI_HANDOVER static s32 wl_notify_bt_wifi_handover_req(struct bcm_cfg80211 *cfg, bcm_struct_cfgdev *cfgdev, const wl_event_msg_t *e, void *data); #endif /* BT_WIFI_HANDOVER */ #ifdef WL_SCHED_SCAN static s32 wl_notify_sched_scan_results(struct bcm_cfg80211 *cfg, struct net_device *ndev, const wl_event_msg_t *e, void *data); #endif /* WL_SCHED_SCAN */ #ifdef PNO_SUPPORT static s32 wl_notify_pfn_status(struct bcm_cfg80211 *cfg, bcm_struct_cfgdev *cfgdev, const wl_event_msg_t *e, void *data); #endif /* PNO_SUPPORT */ static s32 wl_notifier_change_state(struct bcm_cfg80211 *cfg, struct net_info *_net_info, enum wl_status state, bool set); #ifdef WLTDLS static s32 wl_tdls_event_handler(struct bcm_cfg80211 *cfg, bcm_struct_cfgdev *cfgdev, const wl_event_msg_t *e, void *data); #endif /* WLTDLS */ /* * register/deregister parent device */ static void wl_cfg80211_clear_parent_dev(void); /* * ioctl utilites */ /* * cfg80211 set_wiphy_params utilities */ static s32 wl_set_frag(struct net_device *dev, u32 frag_threshold); static s32 wl_set_rts(struct net_device *dev, u32 frag_threshold); static s32 wl_set_retry(struct net_device *dev, u32 retry, bool l); /* * cfg profile utilities */ static s32 wl_update_prof(struct bcm_cfg80211 *cfg, struct net_device *ndev, const wl_event_msg_t *e, const void *data, s32 item); static void *wl_read_prof(struct bcm_cfg80211 *cfg, struct net_device *ndev, s32 item); static void wl_init_prof(struct bcm_cfg80211 *cfg, struct net_device *ndev); /* * cfg80211 connect utilites */ static s32 wl_set_wpa_version(struct net_device *dev, struct cfg80211_connect_params *sme); static s32 wl_set_auth_type(struct net_device *dev, struct cfg80211_connect_params *sme); static s32 wl_set_set_cipher(struct net_device *dev, struct cfg80211_connect_params *sme); static s32 wl_set_key_mgmt(struct net_device *dev, struct cfg80211_connect_params *sme); static s32 wl_set_set_sharedkey(struct net_device *dev, struct cfg80211_connect_params *sme); static s32 wl_get_assoc_ies(struct bcm_cfg80211 *cfg, struct net_device *ndev); static void wl_ch_to_chanspec(int ch, struct wl_join_params *join_params, size_t *join_params_size); /* * information element utilities */ static void wl_rst_ie(struct bcm_cfg80211 *cfg); static __used s32 wl_add_ie(struct bcm_cfg80211 *cfg, u8 t, u8 l, u8 *v); static void wl_update_hidden_ap_ie(struct wl_bss_info *bi, u8 *ie_stream, u32 *ie_size, bool roam); static s32 wl_mrg_ie(struct bcm_cfg80211 *cfg, u8 *ie_stream, u16 ie_size); static s32 wl_cp_ie(struct bcm_cfg80211 *cfg, u8 *dst, u16 dst_size); static u32 wl_get_ielen(struct bcm_cfg80211 *cfg); #ifdef MFP static int wl_cfg80211_get_rsn_capa(bcm_tlv_t *wpa2ie, u8* capa); #endif #ifdef WL11U bcm_tlv_t * wl_cfg80211_find_interworking_ie(u8 *parse, u32 len); static s32 wl_cfg80211_add_iw_ie(struct bcm_cfg80211 *cfg, struct net_device *ndev, s32 bssidx, s32 pktflag, uint8 ie_id, uint8 *data, uint8 data_len); #endif /* WL11U */ static s32 wl_setup_wiphy(struct wireless_dev *wdev, struct device *dev, void *data); static void wl_free_wdev(struct bcm_cfg80211 *cfg); #ifdef CONFIG_CFG80211_INTERNAL_REGDB static int wl_cfg80211_reg_notifier(struct wiphy *wiphy, struct regulatory_request *request); #endif /* CONFIG_CFG80211_INTERNAL_REGDB */ static s32 wl_inform_bss(struct bcm_cfg80211 *cfg); static s32 wl_inform_single_bss(struct bcm_cfg80211 *cfg, struct wl_bss_info *bi, bool roam); static s32 wl_update_bss_info(struct bcm_cfg80211 *cfg, struct net_device *ndev, bool roam); #ifdef P2PONEINT chanspec_t wl_cfg80211_get_shared_freq(struct wiphy *wiphy); #else static chanspec_t wl_cfg80211_get_shared_freq(struct wiphy *wiphy); #endif s32 wl_cfg80211_channel_to_freq(u32 channel); static void wl_cfg80211_work_handler(struct work_struct *work); static s32 wl_add_keyext(struct wiphy *wiphy, struct net_device *dev, u8 key_idx, const u8 *mac_addr, struct key_params *params); /* * key indianess swap utilities */ static void swap_key_from_BE(struct wl_wsec_key *key); static void swap_key_to_BE(struct wl_wsec_key *key); /* * bcm_cfg80211 memory init/deinit utilities */ static s32 wl_init_priv_mem(struct bcm_cfg80211 *cfg); static void wl_deinit_priv_mem(struct bcm_cfg80211 *cfg); static void wl_delay(u32 ms); /* * ibss mode utilities */ static bool wl_is_ibssmode(struct bcm_cfg80211 *cfg, struct net_device *ndev); static __used bool wl_is_ibssstarter(struct bcm_cfg80211 *cfg); /* * link up/down , default configuration utilities */ static s32 __wl_cfg80211_up(struct bcm_cfg80211 *cfg); static s32 __wl_cfg80211_down(struct bcm_cfg80211 *cfg); static bool wl_is_linkdown(struct bcm_cfg80211 *cfg, const wl_event_msg_t *e); static bool wl_is_linkup(struct bcm_cfg80211 *cfg, const wl_event_msg_t *e, struct net_device *ndev); static bool wl_is_nonetwork(struct bcm_cfg80211 *cfg, const wl_event_msg_t *e); static void wl_link_up(struct bcm_cfg80211 *cfg); static void wl_link_down(struct bcm_cfg80211 *cfg); static s32 wl_config_ifmode(struct bcm_cfg80211 *cfg, struct net_device *ndev, s32 iftype); static void wl_init_conf(struct wl_conf *conf); static s32 wl_cfg80211_handle_ifdel(struct bcm_cfg80211 *cfg, wl_if_event_info *if_event_info, struct net_device* ndev); int wl_cfg80211_get_ioctl_version(void); /* * find most significant bit set */ static __used u32 wl_find_msb(u16 bit16); /* * rfkill support */ static int wl_setup_rfkill(struct bcm_cfg80211 *cfg, bool setup); static int wl_rfkill_set(void *data, bool blocked); #ifdef DEBUGFS_CFG80211 static s32 wl_setup_debugfs(struct bcm_cfg80211 *cfg); static s32 wl_free_debugfs(struct bcm_cfg80211 *cfg); #endif static wl_scan_params_t *wl_cfg80211_scan_alloc_params(int channel, int nprobes, int *out_params_size); static bool check_dev_role_integrity(struct bcm_cfg80211 *cfg, u32 dev_role); #ifdef WL_CFG80211_ACL /* ACL */ static int wl_cfg80211_set_mac_acl(struct wiphy *wiphy, struct net_device *cfgdev, const struct cfg80211_acl_data *acl); #endif /* WL_CFG80211_ACL */ /* * Some external functions, TODO: move them to dhd_linux.h */ int dhd_add_monitor(char *name, struct net_device **new_ndev); int dhd_del_monitor(struct net_device *ndev); int dhd_monitor_init(void *dhd_pub); int dhd_monitor_uninit(void); int dhd_start_xmit(struct sk_buff *skb, struct net_device *net); static int wl_cfg80211_delayed_roam(struct bcm_cfg80211 *cfg, struct net_device *ndev, const struct ether_addr *bssid); static int bw2cap[] = { 0, 0, WLC_BW_CAP_20MHZ, WLC_BW_CAP_40MHZ, WLC_BW_CAP_80MHZ, WLC_BW_CAP_160MHZ, WLC_BW_CAP_160MHZ }; #if (KERNEL_VERSION(4, 2, 0) <= LINUX_VERSION_CODE) || \ defined(CFG80211_DISCONNECTED_V2) #define CFG80211_DISCONNECTED(dev, reason, ie, len, loc_gen, gfp) \ cfg80211_disconnected(dev, reason, ie, len, loc_gen, gfp); #elif (KERNEL_VERSION(4, 2, 0) > LINUX_VERSION_CODE) #define CFG80211_DISCONNECTED(dev, reason, ie, len, loc_gen, gfp) \ cfg80211_disconnected(dev, reason, ie, len, gfp); #endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 2, 0)) */ #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 1, 0)) || \ defined(CFG80211_DISCONNECTED_V2) #define CFG80211_GET_BSS(wiphy, channel, bssid, ssid, ssid_len) \ cfg80211_get_bss(wiphy, channel, bssid, ssid, ssid_len, \ IEEE80211_BSS_TYPE_ESS, IEEE80211_PRIVACY_ANY); #else #define CFG80211_GET_BSS(wiphy, channel, bssid, ssid, ssid_len) \ cfg80211_get_bss(wiphy, channel, bssid, ssid, ssid_len, \ WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS); #endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 1, 0)) */ #define RETURN_EIO_IF_NOT_UP(wlpriv) \ do { \ struct net_device *checkSysUpNDev = bcmcfg_to_prmry_ndev(wlpriv); \ if (unlikely(!wl_get_drv_status(wlpriv, READY, checkSysUpNDev))) { \ WL_INFORM(("device is not ready\n")); \ return -EIO; \ } \ } while (0) #ifdef RSSI_OFFSET static s32 wl_rssi_offset(s32 rssi) { rssi += RSSI_OFFSET; if (rssi > 0) rssi = 0; return rssi; } #else #define wl_rssi_offset(x) x #endif #define IS_WPA_AKM(akm) ((akm) == RSN_AKM_NONE || \ (akm) == RSN_AKM_UNSPECIFIED || \ (akm) == RSN_AKM_PSK) extern int dhd_wait_pend8021x(struct net_device *dev); #ifdef PROP_TXSTATUS_VSDB extern int disable_proptx; #endif /* PROP_TXSTATUS_VSDB */ extern int passive_channel_skip; static s32 wl_csa_complete_ind(struct bcm_cfg80211 *cfg, bcm_struct_cfgdev *cfgdev, const wl_event_msg_t *e, void *data); #if ((LINUX_VERSION_CODE >= KERNEL_VERSION (3, 5, 0)) && (LINUX_VERSION_CODE <= (3, 7, \ 0))) struct chan_info { int freq; int chan_type; }; #endif #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 9, 0)) #define CFG80211_PUT_BSS(wiphy, bss) cfg80211_put_bss(wiphy, bss); #else #define CFG80211_PUT_BSS(wiphy, bss) cfg80211_put_bss(bss); #endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 9, 0) */ #if (WL_DBG_LEVEL > 0) #define WL_DBG_ESTR_MAX 50 static s8 wl_dbg_estr[][WL_DBG_ESTR_MAX] = { "SET_SSID", "JOIN", "START", "AUTH", "AUTH_IND", "DEAUTH", "DEAUTH_IND", "ASSOC", "ASSOC_IND", "REASSOC", "REASSOC_IND", "DISASSOC", "DISASSOC_IND", "QUIET_START", "QUIET_END", "BEACON_RX", "LINK", "MIC_ERROR", "NDIS_LINK", "ROAM", "TXFAIL", "PMKID_CACHE", "RETROGRADE_TSF", "PRUNE", "AUTOAUTH", "EAPOL_MSG", "SCAN_COMPLETE", "ADDTS_IND", "DELTS_IND", "BCNSENT_IND", "BCNRX_MSG", "BCNLOST_MSG", "ROAM_PREP", "PFN_NET_FOUND", "PFN_NET_LOST", "RESET_COMPLETE", "JOIN_START", "ROAM_START", "ASSOC_START", "IBSS_ASSOC", "RADIO", "PSM_WATCHDOG", "WLC_E_CCX_ASSOC_START", "WLC_E_CCX_ASSOC_ABORT", "PROBREQ_MSG", "SCAN_CONFIRM_IND", "PSK_SUP", "COUNTRY_CODE_CHANGED", "EXCEEDED_MEDIUM_TIME", "ICV_ERROR", "UNICAST_DECODE_ERROR", "MULTICAST_DECODE_ERROR", "TRACE", "WLC_E_BTA_HCI_EVENT", "IF", "WLC_E_P2P_DISC_LISTEN_COMPLETE", "RSSI", "PFN_SCAN_COMPLETE", "WLC_E_EXTLOG_MSG", "ACTION_FRAME", "ACTION_FRAME_COMPLETE", "WLC_E_PRE_ASSOC_IND", "WLC_E_PRE_REASSOC_IND", "WLC_E_CHANNEL_ADOPTED", "WLC_E_AP_STARTED", "WLC_E_DFS_AP_STOP", "WLC_E_DFS_AP_RESUME", "WLC_E_WAI_STA_EVENT", "WLC_E_WAI_MSG", "WLC_E_ESCAN_RESULT", "WLC_E_ACTION_FRAME_OFF_CHAN_COMPLETE", "WLC_E_PROBRESP_MSG", "WLC_E_P2P_PROBREQ_MSG", "WLC_E_DCS_REQUEST", "WLC_E_FIFO_CREDIT_MAP", "WLC_E_ACTION_FRAME_RX", "WLC_E_WAKE_EVENT", "WLC_E_RM_COMPLETE" }; #endif /* WL_DBG_LEVEL */ #define CHAN2G(_channel, _freq, _flags) { \ .band = IEEE80211_BAND_2GHZ, \ .center_freq = (_freq), \ .hw_value = (_channel), \ .flags = (_flags), \ .max_antenna_gain = 0, \ .max_power = 30, \ } #define CHAN5G(_channel, _flags) { \ .band = IEEE80211_BAND_5GHZ, \ .center_freq = 5000 + (5 * (_channel)), \ .hw_value = (_channel), \ .flags = (_flags), \ .max_antenna_gain = 0, \ .max_power = 30, \ } #define RATE_TO_BASE100KBPS(rate) (((rate) * 10) / 2) #define RATETAB_ENT(_rateid, _flags) \ { \ .bitrate = RATE_TO_BASE100KBPS(_rateid), \ .hw_value = (_rateid), \ .flags = (_flags), \ } static struct ieee80211_rate __wl_rates[] = { RATETAB_ENT(DOT11_RATE_1M, 0), RATETAB_ENT(DOT11_RATE_2M, IEEE80211_RATE_SHORT_PREAMBLE), RATETAB_ENT(DOT11_RATE_5M5, IEEE80211_RATE_SHORT_PREAMBLE), RATETAB_ENT(DOT11_RATE_11M, IEEE80211_RATE_SHORT_PREAMBLE), RATETAB_ENT(DOT11_RATE_6M, 0), RATETAB_ENT(DOT11_RATE_9M, 0), RATETAB_ENT(DOT11_RATE_12M, 0), RATETAB_ENT(DOT11_RATE_18M, 0), RATETAB_ENT(DOT11_RATE_24M, 0), RATETAB_ENT(DOT11_RATE_36M, 0), RATETAB_ENT(DOT11_RATE_48M, 0), RATETAB_ENT(DOT11_RATE_54M, 0) }; #define wl_a_rates (__wl_rates + 4) #define wl_a_rates_size 8 #define wl_g_rates (__wl_rates + 0) #define wl_g_rates_size 12 static struct ieee80211_channel __wl_2ghz_channels[] = { CHAN2G(1, 2412, 0), CHAN2G(2, 2417, 0), CHAN2G(3, 2422, 0), CHAN2G(4, 2427, 0), CHAN2G(5, 2432, 0), CHAN2G(6, 2437, 0), CHAN2G(7, 2442, 0), CHAN2G(8, 2447, 0), CHAN2G(9, 2452, 0), CHAN2G(10, 2457, 0), CHAN2G(11, 2462, 0), CHAN2G(12, 2467, 0), CHAN2G(13, 2472, 0), CHAN2G(14, 2484, 0) }; static struct ieee80211_channel __wl_5ghz_a_channels[] = { CHAN5G(34, 0), CHAN5G(36, 0), CHAN5G(38, 0), CHAN5G(40, 0), CHAN5G(42, 0), CHAN5G(44, 0), CHAN5G(46, 0), CHAN5G(48, 0), CHAN5G(52, 0), CHAN5G(56, 0), CHAN5G(60, 0), CHAN5G(64, 0), CHAN5G(100, 0), CHAN5G(104, 0), CHAN5G(108, 0), CHAN5G(112, 0), CHAN5G(116, 0), CHAN5G(120, 0), CHAN5G(124, 0), CHAN5G(128, 0), CHAN5G(132, 0), CHAN5G(136, 0), CHAN5G(140, 0), CHAN5G(144, 0), CHAN5G(149, 0), CHAN5G(153, 0), CHAN5G(157, 0), CHAN5G(161, 0), CHAN5G(165, 0) }; static struct ieee80211_supported_band __wl_band_2ghz = { .band = IEEE80211_BAND_2GHZ, .channels = __wl_2ghz_channels, .n_channels = ARRAY_SIZE(__wl_2ghz_channels), .bitrates = wl_g_rates, .n_bitrates = wl_g_rates_size }; static struct ieee80211_supported_band __wl_band_5ghz_a = { .band = IEEE80211_BAND_5GHZ, .channels = __wl_5ghz_a_channels, .n_channels = ARRAY_SIZE(__wl_5ghz_a_channels), .bitrates = wl_a_rates, .n_bitrates = wl_a_rates_size }; static const u32 __wl_cipher_suites[] = { WLAN_CIPHER_SUITE_WEP40, WLAN_CIPHER_SUITE_WEP104, WLAN_CIPHER_SUITE_TKIP, WLAN_CIPHER_SUITE_CCMP, WLAN_CIPHER_SUITE_AES_CMAC, }; #ifdef WL_SUPPORT_ACS /* * The firmware code required for this feature to work is currently under * BCMINTERNAL flag. In future if this is to enabled we need to bring the * required firmware code out of the BCMINTERNAL flag. */ struct wl_dump_survey { u32 obss; u32 ibss; u32 no_ctg; u32 no_pckt; u32 tx; u32 idle; }; #endif /* WL_SUPPORT_ACS */ #if defined(USE_DYNAMIC_MAXPKT_RXGLOM) static int maxrxpktglom = 0; #endif /* IOCtl version read from targeted driver */ static int ioctl_version; #ifdef DEBUGFS_CFG80211 #define S_SUBLOGLEVEL 20 static const struct { u32 log_level; char *sublogname; } sublogname_map[] = { {WL_DBG_ERR, "ERR"}, {WL_DBG_INFO, "INFO"}, {WL_DBG_DBG, "DBG"}, {WL_DBG_SCAN, "SCAN"}, {WL_DBG_TRACE, "TRACE"}, {WL_DBG_P2P_ACTION, "P2PACTION"} }; #endif /* watchdog work for disconnecting when fw is not associated for FW_ASSOC_WATCHDOG_TIME ms */ static struct fw_assoc_timeout_work fw_assoc_timeout; #define FW_ASSOC_WATCHDOG_TIME (5 * 1000) /* msec */ static void wl_add_remove_pm_enable_work(struct bcm_cfg80211 *cfg, bool add_remove, enum wl_handler_del_type type) { if (cfg == NULL) return; if (cfg->pm_enable_work_on && cfg->pwr_save) { if (add_remove) { schedule_delayed_work(&cfg->pm_enable_work, msecs_to_jiffies(WL_PM_ENABLE_TIMEOUT)); } else { cancel_delayed_work_sync(&cfg->pm_enable_work); switch (type) { case WL_HANDLER_MAINTAIN: schedule_delayed_work(&cfg->pm_enable_work, msecs_to_jiffies(WL_PM_ENABLE_TIMEOUT)); break; case WL_HANDLER_PEND: schedule_delayed_work(&cfg->pm_enable_work, msecs_to_jiffies(WL_PM_ENABLE_TIMEOUT*2)); break; case WL_HANDLER_DEL: default: cfg->pm_enable_work_on = false; break; } } } } /* Return a new chanspec given a legacy chanspec * Returns INVCHANSPEC on error */ static chanspec_t wl_chspec_from_legacy(chanspec_t legacy_chspec) { chanspec_t chspec; /* get the channel number */ chspec = LCHSPEC_CHANNEL(legacy_chspec); /* convert the band */ if (LCHSPEC_IS2G(legacy_chspec)) { chspec |= WL_CHANSPEC_BAND_2G; } else { chspec |= WL_CHANSPEC_BAND_5G; } /* convert the bw and sideband */ if (LCHSPEC_IS20(legacy_chspec)) { chspec |= WL_CHANSPEC_BW_20; } else { chspec |= WL_CHANSPEC_BW_40; if (LCHSPEC_CTL_SB(legacy_chspec) == WL_LCHANSPEC_CTL_SB_LOWER) { chspec |= WL_CHANSPEC_CTL_SB_L; } else { chspec |= WL_CHANSPEC_CTL_SB_U; } } if (wf_chspec_malformed(chspec)) { WL_ERR(("wl_chspec_from_legacy: output chanspec (0x%04X) malformed\n", chspec)); return INVCHANSPEC; } return chspec; } /* Return a legacy chanspec given a new chanspec * Returns INVCHANSPEC on error */ static chanspec_t wl_chspec_to_legacy(chanspec_t chspec) { chanspec_t lchspec; if (wf_chspec_malformed(chspec)) { WL_ERR(("wl_chspec_to_legacy: input chanspec (0x%04X) malformed\n", chspec)); return INVCHANSPEC; } /* get the channel number */ lchspec = CHSPEC_CHANNEL(chspec); /* convert the band */ if (CHSPEC_IS2G(chspec)) { lchspec |= WL_LCHANSPEC_BAND_2G; } else { lchspec |= WL_LCHANSPEC_BAND_5G; } /* convert the bw and sideband */ if (CHSPEC_IS20(chspec)) { lchspec |= WL_LCHANSPEC_BW_20; lchspec |= WL_LCHANSPEC_CTL_SB_NONE; } else if (CHSPEC_IS40(chspec)) { lchspec |= WL_LCHANSPEC_BW_40; if (CHSPEC_CTL_SB(chspec) == WL_CHANSPEC_CTL_SB_L) { lchspec |= WL_LCHANSPEC_CTL_SB_LOWER; } else { lchspec |= WL_LCHANSPEC_CTL_SB_UPPER; } } else { /* cannot express the bandwidth */ char chanbuf[CHANSPEC_STR_LEN]; WL_ERR(( "wl_chspec_to_legacy: unable to convert chanspec %s (0x%04X) " "to pre-11ac format\n", wf_chspec_ntoa(chspec, chanbuf), chspec)); return INVCHANSPEC; } return lchspec; } /* given a chanspec value, do the endian and chanspec version conversion to * a chanspec_t value * Returns INVCHANSPEC on error */ chanspec_t wl_chspec_host_to_driver(chanspec_t chanspec) { if (ioctl_version == 1) { chanspec = wl_chspec_to_legacy(chanspec); if (chanspec == INVCHANSPEC) { return chanspec; } } chanspec = htodchanspec(chanspec); return chanspec; } /* given a channel value, do the endian and chanspec version conversion to * a chanspec_t value * Returns INVCHANSPEC on error */ chanspec_t wl_ch_host_to_driver(u16 channel) { chanspec_t chanspec; chanspec = channel & WL_CHANSPEC_CHAN_MASK; if (channel <= CH_MAX_2G_CHANNEL) chanspec |= WL_CHANSPEC_BAND_2G; else chanspec |= WL_CHANSPEC_BAND_5G; chanspec |= WL_CHANSPEC_BW_20; chanspec |= WL_CHANSPEC_CTL_SB_NONE; return wl_chspec_host_to_driver(chanspec); } /* given a chanspec value from the driver, do the endian and chanspec version conversion to * a chanspec_t value * Returns INVCHANSPEC on error */ chanspec_t wl_chspec_driver_to_host(chanspec_t chanspec) { chanspec = dtohchanspec(chanspec); if (ioctl_version == 1) { chanspec = wl_chspec_from_legacy(chanspec); } return chanspec; } /* * convert ASCII string to MAC address (colon-delimited format) * eg: 00:11:22:33:44:55 */ int wl_cfg80211_ether_atoe(const char *a, struct ether_addr *n) { char *c = NULL; int count = 0; memset(n, 0, ETHER_ADDR_LEN); for (;;) { n->octet[count++] = (uint8)simple_strtoul(a, &c, 16); if (!*c++ || count == ETHER_ADDR_LEN) break; a = c; } return (count == ETHER_ADDR_LEN); } /* convert hex string buffer to binary */ int wl_cfg80211_hex_str_to_bin(unsigned char *data, int dlen, char *str) { int count, slen; int hvalue; char tmp[3] = {0}; char *ptr = str, *endp = NULL; if (!data || !str || !dlen) { WL_DBG((" passed buffer is empty \n")); return 0; } slen = strlen(str); if (dlen * 2 < slen) { WL_DBG((" destination buffer too short \n")); return 0; } if (slen % 2) { WL_DBG((" source buffer is of odd length \n")); return 0; } for (count = 0; count < slen; count += 2) { memcpy(tmp, ptr, 2); hvalue = simple_strtol(tmp, &endp, 16); if (*endp != '\0') { WL_DBG((" non hexadecimal character encountered \n")); return 0; } *data++ = (unsigned char)hvalue; ptr += 2; } return (slen / 2); } /* There isn't a lot of sense in it, but you can transmit anything you like */ static const struct ieee80211_txrx_stypes wl_cfg80211_default_mgmt_stypes[NUM_NL80211_IFTYPES] = { [NL80211_IFTYPE_ADHOC] = { .tx = 0xffff, .rx = BIT(IEEE80211_STYPE_ACTION >> 4) }, [NL80211_IFTYPE_STATION] = { .tx = 0xffff, .rx = BIT(IEEE80211_STYPE_ACTION >> 4) | BIT(IEEE80211_STYPE_PROBE_REQ >> 4) }, [NL80211_IFTYPE_AP] = { .tx = 0xffff, .rx = BIT(IEEE80211_STYPE_ASSOC_REQ >> 4) | BIT(IEEE80211_STYPE_REASSOC_REQ >> 4) | BIT(IEEE80211_STYPE_PROBE_REQ >> 4) | BIT(IEEE80211_STYPE_DISASSOC >> 4) | BIT(IEEE80211_STYPE_AUTH >> 4) | BIT(IEEE80211_STYPE_DEAUTH >> 4) | BIT(IEEE80211_STYPE_ACTION >> 4) }, [NL80211_IFTYPE_AP_VLAN] = { /* copy AP */ .tx = 0xffff, .rx = BIT(IEEE80211_STYPE_ASSOC_REQ >> 4) | BIT(IEEE80211_STYPE_REASSOC_REQ >> 4) | BIT(IEEE80211_STYPE_PROBE_REQ >> 4) | BIT(IEEE80211_STYPE_DISASSOC >> 4) | BIT(IEEE80211_STYPE_AUTH >> 4) | BIT(IEEE80211_STYPE_DEAUTH >> 4) | BIT(IEEE80211_STYPE_ACTION >> 4) }, [NL80211_IFTYPE_P2P_CLIENT] = { .tx = 0xffff, .rx = BIT(IEEE80211_STYPE_ACTION >> 4) | BIT(IEEE80211_STYPE_PROBE_REQ >> 4) }, [NL80211_IFTYPE_P2P_GO] = { .tx = 0xffff, .rx = BIT(IEEE80211_STYPE_ASSOC_REQ >> 4) | BIT(IEEE80211_STYPE_REASSOC_REQ >> 4) | BIT(IEEE80211_STYPE_PROBE_REQ >> 4) | BIT(IEEE80211_STYPE_DISASSOC >> 4) | BIT(IEEE80211_STYPE_AUTH >> 4) | BIT(IEEE80211_STYPE_DEAUTH >> 4) | BIT(IEEE80211_STYPE_ACTION >> 4) }, #if defined(WL_CFG80211_P2P_DEV_IF) [NL80211_IFTYPE_P2P_DEVICE] = { .tx = 0xffff, .rx = BIT(IEEE80211_STYPE_ACTION >> 4) | BIT(IEEE80211_STYPE_PROBE_REQ >> 4) }, #endif /* WL_CFG80211_P2P_DEV_IF */ }; static void swap_key_from_BE(struct wl_wsec_key *key) { key->index = htod32(key->index); key->len = htod32(key->len); key->algo = htod32(key->algo); key->flags = htod32(key->flags); key->rxiv.hi = htod32(key->rxiv.hi); key->rxiv.lo = htod16(key->rxiv.lo); key->iv_initialized = htod32(key->iv_initialized); } static void swap_key_to_BE(struct wl_wsec_key *key) { key->index = dtoh32(key->index); key->len = dtoh32(key->len); key->algo = dtoh32(key->algo); key->flags = dtoh32(key->flags); key->rxiv.hi = dtoh32(key->rxiv.hi); key->rxiv.lo = dtoh16(key->rxiv.lo); key->iv_initialized = dtoh32(key->iv_initialized); } /* Dump the contents of the encoded wps ie buffer and get pbc value */ static void wl_validate_wps_ie(char *wps_ie, s32 wps_ie_len, bool *pbc) { #define WPS_IE_FIXED_LEN 6 u16 len; u8 *subel = NULL; u16 subelt_id; u16 subelt_len; u16 val; u8 *valptr = (uint8*) &val; if (wps_ie == NULL || wps_ie_len < WPS_IE_FIXED_LEN) { WL_ERR(("invalid argument : NULL\n")); return; } len = (u16)wps_ie[TLV_LEN_OFF]; if (len > wps_ie_len) { WL_ERR(("invalid length len %d, wps ie len %d\n", len, wps_ie_len)); return; } WL_DBG(("wps_ie len=%d\n", len)); len -= 4; /* for the WPS IE's OUI, oui_type fields */ subel = wps_ie + WPS_IE_FIXED_LEN; while (len >= 4) { /* must have attr id, attr len fields */ valptr[0] = *subel++; valptr[1] = *subel++; subelt_id = HTON16(val); valptr[0] = *subel++; valptr[1] = *subel++; subelt_len = HTON16(val); len -= 4; /* for the attr id, attr len fields */ len -= subelt_len; /* for the remaining fields in this attribute */ WL_DBG((" subel=%p, subelt_id=0x%x subelt_len=%u\n", subel, subelt_id, subelt_len)); if (subelt_id == WPS_ID_VERSION) { WL_DBG((" attr WPS_ID_VERSION: %u\n", *subel)); } else if (subelt_id == WPS_ID_REQ_TYPE) { WL_DBG((" attr WPS_ID_REQ_TYPE: %u\n", *subel)); } else if (subelt_id == WPS_ID_CONFIG_METHODS) { valptr[0] = *subel; valptr[1] = *(subel + 1); WL_DBG((" attr WPS_ID_CONFIG_METHODS: %x\n", HTON16(val))); } else if (subelt_id == WPS_ID_DEVICE_NAME) { char devname[100]; size_t namelen = MIN(subelt_len, sizeof(devname)-1); memcpy(devname, subel, namelen); devname[namelen] = '\0'; WL_DBG((" attr WPS_ID_DEVICE_NAME: %s (len %u)\n", devname, subelt_len)); } else if (subelt_id == WPS_ID_DEVICE_PWD_ID) { valptr[0] = *subel; valptr[1] = *(subel + 1); WL_DBG((" attr WPS_ID_DEVICE_PWD_ID: %u\n", HTON16(val))); *pbc = (HTON16(val) == DEV_PW_PUSHBUTTON) ? true : false; } else if (subelt_id == WPS_ID_PRIM_DEV_TYPE) { valptr[0] = *subel; valptr[1] = *(subel + 1); WL_DBG((" attr WPS_ID_PRIM_DEV_TYPE: cat=%u \n", HTON16(val))); valptr[0] = *(subel + 6); valptr[1] = *(subel + 7); WL_DBG((" attr WPS_ID_PRIM_DEV_TYPE: subcat=%u\n", HTON16(val))); } else if (subelt_id == WPS_ID_REQ_DEV_TYPE) { valptr[0] = *subel; valptr[1] = *(subel + 1); WL_DBG((" attr WPS_ID_REQ_DEV_TYPE: cat=%u\n", HTON16(val))); valptr[0] = *(subel + 6); valptr[1] = *(subel + 7); WL_DBG((" attr WPS_ID_REQ_DEV_TYPE: subcat=%u\n", HTON16(val))); } else if (subelt_id == WPS_ID_SELECTED_REGISTRAR_CONFIG_METHODS) { valptr[0] = *subel; valptr[1] = *(subel + 1); WL_DBG((" attr WPS_ID_SELECTED_REGISTRAR_CONFIG_METHODS" ": cat=%u\n", HTON16(val))); } else { WL_DBG((" unknown attr 0x%x\n", subelt_id)); } subel += subelt_len; } } s32 wl_set_tx_power(struct net_device *dev, enum nl80211_tx_power_setting type, s32 dbm) { s32 err = 0; s32 disable = 0; s32 txpwrqdbm; struct bcm_cfg80211 *cfg = g_bcm_cfg; /* Make sure radio is off or on as far as software is concerned */ disable = WL_RADIO_SW_DISABLE << 16; disable = htod32(disable); err = wldev_ioctl(dev, WLC_SET_RADIO, &disable, sizeof(disable), true); if (unlikely(err)) { WL_ERR(("WLC_SET_RADIO error (%d)\n", err)); return err; } if (dbm > 0xffff) dbm = 0xffff; txpwrqdbm = dbm * 4; err = wldev_iovar_setbuf_bsscfg(dev, "qtxpower", (void *)&txpwrqdbm, sizeof(txpwrqdbm), cfg->ioctl_buf, WLC_IOCTL_SMLEN, 0, &cfg->ioctl_buf_sync); if (unlikely(err)) WL_ERR(("qtxpower error (%d)\n", err)); else WL_ERR(("dBm=%d, txpwrqdbm=0x%x\n", dbm, txpwrqdbm)); return err; } s32 wl_get_tx_power(struct net_device *dev, s32 *dbm) { s32 err = 0; s32 txpwrdbm; struct bcm_cfg80211 *cfg = g_bcm_cfg; err = wldev_iovar_getbuf_bsscfg(dev, "qtxpower", NULL, 0, cfg->ioctl_buf, WLC_IOCTL_SMLEN, 0, &cfg->ioctl_buf_sync); if (unlikely(err)) { WL_ERR(("error (%d)\n", err)); return err; } memcpy(&txpwrdbm, cfg->ioctl_buf, sizeof(txpwrdbm)); txpwrdbm = dtoh32(txpwrdbm); *dbm = (txpwrdbm & ~WL_TXPWR_OVERRIDE) / 4; WL_INFORM(("dBm=%d, txpwrdbm=0x%x\n", *dbm, txpwrdbm)); return err; } #ifdef P2PONEINT chanspec_t wl_cfg80211_get_shared_freq(struct wiphy *wiphy) #else static chanspec_t wl_cfg80211_get_shared_freq(struct wiphy *wiphy) #endif { chanspec_t chspec; int cur_band, err = 0; struct bcm_cfg80211 *cfg = wiphy_priv(wiphy); struct net_device *dev = bcmcfg_to_prmry_ndev(cfg); struct ether_addr bssid; struct wl_bss_info *bss = NULL; char *buf; memset(&bssid, 0, sizeof(bssid)); if ((err = wldev_ioctl(dev, WLC_GET_BSSID, &bssid, sizeof(bssid), false))) { /* STA interface is not associated. So start the new interface on a temp * channel . Later proper channel will be applied by the above framework * via set_channel (cfg80211 API). */ WL_DBG(("Not associated. Return a temp channel. \n")); err = wldev_ioctl(dev, WLC_GET_BAND, &cur_band, sizeof(int), false); if (unlikely(err)) { WL_ERR(("Get band failed\n")); return wl_ch_host_to_driver(WL_P2P_TEMP_CHAN); } if (cur_band == WLC_BAND_5G) return wl_ch_host_to_driver(WL_P2P_TEMP_CHAN_5G); else return wl_ch_host_to_driver(WL_P2P_TEMP_CHAN); } buf = kzalloc(WL_EXTRA_BUF_MAX, GFP_KERNEL); if (!buf) { WL_ERR(("buf alloc failed. use temp channel\n")); return wl_ch_host_to_driver(WL_P2P_TEMP_CHAN); } *(u32 *)buf = htod32(WL_EXTRA_BUF_MAX); err = wldev_ioctl(dev, WLC_GET_BSS_INFO, buf, WL_EXTRA_BUF_MAX, false); if (err) { WL_ERR(("Failed to get bss info, use temp channel\n")); chspec = wl_ch_host_to_driver(WL_P2P_TEMP_CHAN); } else { bss = (struct wl_bss_info *)(buf + 4); chspec = bss->chanspec; WL_DBG(("Valid BSS Found. chanspec:%d \n", chspec)); } kfree(buf); return chspec; } static bcm_struct_cfgdev * wl_cfg80211_add_monitor_if(char *name) { #if defined(WL_ENABLE_P2P_IF) || defined(WL_CFG80211_P2P_DEV_IF) WL_INFORM(("wl_cfg80211_add_monitor_if: No more support monitor interface\n")); return ERR_PTR(-EOPNOTSUPP); #else struct net_device* ndev = NULL; dhd_add_monitor(name, &ndev); WL_INFORM(("wl_cfg80211_add_monitor_if net device returned: 0x%p\n", ndev)); return ndev_to_cfgdev(ndev); #endif /* WL_ENABLE_P2P_IF || WL_CFG80211_P2P_DEV_IF */ } static bcm_struct_cfgdev * wl_cfg80211_add_virtual_iface(struct wiphy *wiphy, #if defined(WL_CFG80211_P2P_DEV_IF) const char *name, #else char *name, #endif /* WL_CFG80211_P2P_DEV_IF */ #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 1, 0)) unsigned char name_assign_type, #endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 1, 0)) */ enum nl80211_iftype type, #if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 11, 0)) u32 *flags, #endif struct vif_params *params) { s32 err; s32 timeout = -1; s32 wlif_type = -1; s32 mode = 0; s32 val = 0; s32 dhd_mode = 0; chanspec_t chspec; struct bcm_cfg80211 *cfg = wiphy_priv(wiphy); struct net_device *primary_ndev; struct net_device *new_ndev; struct ether_addr primary_mac; #ifdef PROP_TXSTATUS_VSDB #if defined(BCMSDIO) s32 up = 1; dhd_pub_t *dhd; bool enabled; #endif #endif /* PROP_TXSTATUS_VSDB */ #if defined(SUPPORT_AP_POWERSAVE) dhd_pub_t *dhd; #endif if (!cfg) return ERR_PTR(-EINVAL); #ifdef PROP_TXSTATUS_VSDB if (bcmdhd_prop_txstatus_vsdb) { #if defined(BCMSDIO) dhd = (dhd_pub_t *)(cfg->pub); #endif } #endif /* PROP_TXSTATUS_VSDB */ #if defined(SUPPORT_AP_POWERSAVE) dhd = (dhd_pub_t *)(cfg->pub); #endif /* Use primary I/F for sending cmds down to firmware */ primary_ndev = bcmcfg_to_prmry_ndev(cfg); if (unlikely(!wl_get_drv_status(cfg, READY, primary_ndev))) { WL_ERR(("device is not ready\n")); return ERR_PTR(-ENODEV); } if (wl_get_drv_status(cfg, CONNECTING, primary_ndev)) { WL_ERR(("Already AP connection going on\n")); return ERR_PTR(-EBUSY); } WL_DBG(("if name: %s, type: %d\n", name, type)); switch (type) { case NL80211_IFTYPE_ADHOC: #ifdef WLAIBSS_MCHAN return bcm_cfg80211_add_ibss_if(wiphy, (char *)name); #endif /* WLAIBSS_MCHAN */ case NL80211_IFTYPE_AP_VLAN: case NL80211_IFTYPE_WDS: case NL80211_IFTYPE_MESH_POINT: WL_ERR(("Unsupported interface type\n")); mode = WL_MODE_IBSS; return NULL; case NL80211_IFTYPE_MONITOR: return wl_cfg80211_add_monitor_if((char *)name); #if defined(WL_CFG80211_P2P_DEV_IF) case NL80211_IFTYPE_P2P_DEVICE: return wl_cfgp2p_add_p2p_disc_if(cfg); #endif /* WL_CFG80211_P2P_DEV_IF */ case NL80211_IFTYPE_STATION: #ifdef DUAL_STA #ifdef WLAIBSS_MCHAN if (cfg->ibss_cfgdev) { WL_ERR(("AIBSS is already operational. " " AIBSS & DUALSTA can't be used together \n")); return NULL; } #endif /* WLAIBSS_MCHAN */ if (!name) { WL_ERR(("Interface name not provided \n")); return NULL; } return wl_cfg80211_create_iface(cfg->wdev->wiphy, NL80211_IFTYPE_STATION, NULL, name); #endif /* DUAL_STA */ case NL80211_IFTYPE_P2P_CLIENT: wlif_type = WL_P2P_IF_CLIENT; mode = WL_MODE_BSS; break; case NL80211_IFTYPE_P2P_GO: case NL80211_IFTYPE_AP: wlif_type = WL_P2P_IF_GO; mode = WL_MODE_AP; break; default: WL_ERR(("Unsupported interface type\n")); return NULL; break; } if (!name) { WL_ERR(("name is NULL\n")); return NULL; } if (cfg->p2p_supported && (wlif_type != -1)) { ASSERT(cfg->p2p); /* ensure expectation of p2p initialization */ #ifdef PROP_TXSTATUS_VSDB if (bcmdhd_prop_txstatus_vsdb) { #if defined(BCMSDIO) if (!dhd) return ERR_PTR(-ENODEV); #endif } #endif /* PROP_TXSTATUS_VSDB */ if (!cfg->p2p) return ERR_PTR(-ENODEV); if (cfg->p2p && !cfg->p2p->on && strstr(name, WL_P2P_INTERFACE_PREFIX)) { p2p_on(cfg) = true; wl_cfgp2p_set_firm_p2p(cfg); wl_cfgp2p_init_discovery(cfg); get_primary_mac(cfg, &primary_mac); wl_cfgp2p_generate_bss_mac(&primary_mac, &cfg->p2p->dev_addr, &cfg->p2p->int_addr); } memset(cfg->p2p->vir_ifname, 0, IFNAMSIZ); strncpy(cfg->p2p->vir_ifname, name, IFNAMSIZ - 1); wl_cfg80211_scan_abort(cfg); #ifdef PROP_TXSTATUS_VSDB if (bcmdhd_prop_txstatus_vsdb) { #if defined(BCMSDIO) if (!cfg->wlfc_on && !disable_proptx) { dhd_wlfc_get_enable(dhd, &enabled); if (!enabled && dhd->op_mode != DHD_FLAG_HOSTAP_MODE && dhd->op_mode != DHD_FLAG_IBSS_MODE) { dhd_wlfc_init(dhd); err = wldev_ioctl(primary_ndev, WLC_UP, &up, sizeof(s32), true); if (err < 0) WL_ERR(("WLC_UP return err:%d\n", err)); } cfg->wlfc_on = true; } #endif } #endif /* PROP_TXSTATUS_VSDB */ /* In concurrency case, STA may be already associated in a particular channel. * so retrieve the current channel of primary interface and then start the virtual * interface on that. */ chspec = wl_cfg80211_get_shared_freq(wiphy); /* For P2P mode, use P2P-specific driver features to create the * bss: "cfg p2p_ifadd" */ wl_set_p2p_status(cfg, IF_ADDING); memset(&cfg->if_event_info, 0, sizeof(cfg->if_event_info)); if (wlif_type == WL_P2P_IF_GO) wldev_iovar_setint(primary_ndev, "mpc", 0); err = wl_cfgp2p_ifadd(cfg, &cfg->p2p->int_addr, htod32(wlif_type), chspec); if (unlikely(err)) { wl_clr_p2p_status(cfg, IF_ADDING); WL_ERR((" virtual iface add failed (%d) \n", err)); return ERR_PTR(-ENOMEM); } timeout = wait_event_interruptible_timeout(cfg->netif_change_event, (wl_get_p2p_status(cfg, IF_ADDING) == false), msecs_to_jiffies(MAX_WAIT_TIME)); if (timeout > 0 && !wl_get_p2p_status(cfg, IF_ADDING) && cfg->if_event_info.valid) { struct wireless_dev *vwdev; int pm_mode = PM_ENABLE; wl_if_event_info *event = &cfg->if_event_info; /* IF_ADD event has come back, we can proceed to to register * the new interface now, use the interface name provided by caller (thus * ignore the one from wlc) */ strncpy(cfg->if_event_info.name, name, IFNAMSIZ - 1); new_ndev = wl_cfg80211_allocate_if(cfg, event->ifidx, cfg->p2p->vir_ifname, event->mac, event->bssidx); if (new_ndev == NULL) goto fail; wl_to_p2p_bss_ndev(cfg, P2PAPI_BSSCFG_CONNECTION) = new_ndev; wl_to_p2p_bss_bssidx(cfg, P2PAPI_BSSCFG_CONNECTION) = event->bssidx; vwdev = kzalloc(sizeof(*vwdev), GFP_KERNEL); if (unlikely(!vwdev)) { WL_ERR(("Could not allocate wireless device\n")); goto fail; } vwdev->wiphy = cfg->wdev->wiphy; WL_INFORM(("virtual interface(%s) is created\n", cfg->p2p->vir_ifname)); vwdev->iftype = type; vwdev->netdev = new_ndev; new_ndev->ieee80211_ptr = vwdev; SET_NETDEV_DEV(new_ndev, wiphy_dev(vwdev->wiphy)); wl_set_drv_status(cfg, READY, new_ndev); cfg->p2p->vif_created = true; wl_set_mode_by_netdev(cfg, new_ndev, mode); if (wl_cfg80211_register_if(cfg, event->ifidx, new_ndev) != BCME_OK) { wl_cfg80211_remove_if(cfg, event->ifidx, new_ndev); goto fail; } wl_alloc_netinfo(cfg, new_ndev, vwdev, mode, pm_mode); val = 1; /* Disable firmware roaming for P2P interface */ wldev_iovar_setint(new_ndev, "roam_off", val); if (mode != WL_MODE_AP) wldev_iovar_setint(new_ndev, "buf_key_b4_m4", 1); DHD_NV_INFO((" virtual interface(%s) is " "created net attach done\n", cfg->p2p->vir_ifname)); #ifdef CONFIG_BCMDHD_CUSTOM_SYSFS_TEGRA TEGRA_SYSFS_HISTOGRAM_STAT_INC(ago_start); #endif if (mode == WL_MODE_AP) wl_set_drv_status(cfg, CONNECTED, new_ndev); #ifdef SUPPORT_AP_POWERSAVE if (mode == WL_MODE_AP) { dhd_set_ap_powersave(dhd, 0, TRUE); } #endif if (type == NL80211_IFTYPE_P2P_CLIENT) dhd_mode = DHD_FLAG_P2P_GC_MODE; else if (type == NL80211_IFTYPE_P2P_GO) dhd_mode = DHD_FLAG_P2P_GO_MODE; DNGL_FUNC(dhd_cfg80211_set_p2p_info, (cfg, dhd_mode)); /* reinitialize completion to clear previous count */ #if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 13, 0)) INIT_COMPLETION(cfg->iface_disable); #else init_completion(&cfg->iface_disable); #endif return ndev_to_cfgdev(new_ndev); } else { wl_clr_p2p_status(cfg, IF_ADDING); WL_ERR((" virtual interface(%s) is not created \n", cfg->p2p->vir_ifname)); WL_ERR(("left timeout : %d\n", timeout)); WL_ERR(("IF_ADDING status : %d\n", wl_get_p2p_status(cfg, IF_ADDING))); WL_ERR(("event valid : %d\n", cfg->if_event_info.valid)); wl_clr_p2p_status(cfg, GO_NEG_PHASE); wl_set_p2p_status(cfg, IF_DELETING); err = wl_cfgp2p_ifdel(cfg, &cfg->p2p->int_addr); if (err == BCME_OK) { timeout = wait_event_interruptible_timeout(cfg->netif_change_event, (wl_get_p2p_status(cfg, IF_DELETING) == false), msecs_to_jiffies(MAX_WAIT_TIME)); if (timeout > 0 && !wl_get_p2p_status(cfg, IF_DELETING) && cfg->if_event_info.valid) { WL_ERR(("IFDEL operation done\n")); } else { WL_ERR(("IFDEL didn't complete properly\n")); err = BCME_ERROR; } } if (err != BCME_OK) { struct net_device *ndev = bcmcfg_to_prmry_ndev(cfg); WL_ERR(("p2p_ifdel failed, error %d, sent HANG event to %s\n", err, ndev->name)); net_os_send_hang_message(ndev); } memset(cfg->p2p->vir_ifname, '\0', IFNAMSIZ); cfg->p2p->vif_created = false; #ifdef PROP_TXSTATUS_VSDB if (bcmdhd_prop_txstatus_vsdb) { #if defined(BCMSDIO) dhd_wlfc_get_enable(dhd, &enabled); if (enabled && cfg->wlfc_on && dhd->op_mode != DHD_FLAG_HOSTAP_MODE && dhd->op_mode != DHD_FLAG_IBSS_MODE) { dhd_wlfc_deinit(dhd); cfg->wlfc_on = false; } #endif } #endif /* PROP_TXSTATUS_VSDB */ } } fail: if (wlif_type == WL_P2P_IF_GO) wldev_iovar_setint(primary_ndev, "mpc", 1); return ERR_PTR(-ENODEV); } static s32 wl_cfg80211_del_virtual_iface(struct wiphy *wiphy, bcm_struct_cfgdev *cfgdev) { struct net_device *dev = NULL; struct ether_addr p2p_mac; struct bcm_cfg80211 *cfg = wiphy_priv(wiphy); s32 timeout = -1; s32 ret = 0; s32 index = -1; #ifdef CUSTOM_SET_CPUCORE dhd_pub_t *dhd = (dhd_pub_t *)(cfg->pub); #endif /* CUSTOM_SET_CPUCORE */ WL_DBG(("Enter\n")); #ifdef CUSTOM_SET_CPUCORE dhd->chan_isvht80 &= ~DHD_FLAG_P2P_MODE; if (!(dhd->chan_isvht80)) dhd_set_cpucore(dhd, FALSE); #endif /* CUSTOM_SET_CPUCORE */ if (!cfg) return -EINVAL; #if defined(WL_CFG80211_P2P_DEV_IF) if (cfgdev->iftype == NL80211_IFTYPE_P2P_DEVICE) { return wl_cfgp2p_del_p2p_disc_if(cfgdev, cfg); } #endif /* WL_CFG80211_P2P_DEV_IF */ dev = cfgdev_to_wlc_ndev(cfgdev, cfg); #ifdef WLAIBSS_MCHAN if (cfgdev == cfg->ibss_cfgdev) return bcm_cfg80211_del_ibss_if(wiphy, cfgdev); #endif /* WLAIBSS_MCHAN */ #ifdef DUAL_STA if (cfgdev == cfg->bss_cfgdev) return wl_cfg80211_del_iface(wiphy, cfgdev); #endif /* DUAL_STA */ if (wl_cfgp2p_find_idx(cfg, dev, &index) != BCME_OK) { WL_ERR(("Find p2p index from ndev(%p) failed\n", dev)); return BCME_ERROR; } if (cfg->p2p_supported) { memcpy(p2p_mac.octet, cfg->p2p->int_addr.octet, ETHER_ADDR_LEN); /* Clear GO_NEG_PHASE bit to take care of GO-NEG-FAIL cases */ WL_DBG(("P2P: GO_NEG_PHASE status cleared ")); wl_clr_p2p_status(cfg, GO_NEG_PHASE); if (cfg->p2p->vif_created) { if (wl_get_drv_status(cfg, SCANNING, dev)) { wl_notify_escan_complete(cfg, dev, true, true); } wldev_iovar_setint(dev, "mpc", 1); /* Delete pm_enable_work */ wl_add_remove_pm_enable_work(cfg, FALSE, WL_HANDLER_DEL); /* for GC */ if (wl_get_drv_status(cfg, DISCONNECTING, dev) && (wl_get_mode_by_netdev(cfg, dev) != WL_MODE_AP)) { WL_ERR(("Wait for Link Down event for GC !\n")); wait_for_completion_timeout (&cfg->iface_disable, msecs_to_jiffies(500)); } memset(&cfg->if_event_info, 0, sizeof(cfg->if_event_info)); wl_set_p2p_status(cfg, IF_DELETING); DNGL_FUNC(dhd_cfg80211_clean_p2p_info, (cfg)); /* for GO */ if (wl_get_mode_by_netdev(cfg, dev) == WL_MODE_AP) { wl_add_remove_eventmsg(dev, WLC_E_PROBREQ_MSG, false); /* disable interface before bsscfg free */ ret = wl_cfgp2p_ifdisable(cfg, &p2p_mac); /* if fw doesn't support "ifdis", do not wait for link down of ap mode */ if (ret == 0) { WL_ERR(("Wait for Link Down event for GO !!!\n")); wait_for_completion_timeout(&cfg->iface_disable, msecs_to_jiffies(500)); } else if (ret != BCME_UNSUPPORTED) { msleep(300); } } wl_cfgp2p_clear_management_ie(cfg, index); if (wl_get_mode_by_netdev(cfg, dev) != WL_MODE_AP) wldev_iovar_setint(dev, "buf_key_b4_m4", 0); /* delete interface after link down */ ret = wl_cfgp2p_ifdel(cfg, &p2p_mac); if (ret != BCME_OK) { struct net_device *ndev = bcmcfg_to_prmry_ndev(cfg); WL_ERR(("p2p_ifdel failed, error %d, sent HANG event to %s\n", ret, ndev->name)); #if defined(BCMDONGLEHOST) && defined(OEM_ANDROID) net_os_send_hang_message(ndev); #endif } else { /* Wait for IF_DEL operation to be finished */ timeout = wait_event_interruptible_timeout(cfg->netif_change_event, (wl_get_p2p_status(cfg, IF_DELETING) == false), msecs_to_jiffies(MAX_WAIT_TIME)); if (timeout > 0 && !wl_get_p2p_status(cfg, IF_DELETING) && cfg->if_event_info.valid) { WL_DBG(("IFDEL operation done\n")); wl_cfg80211_handle_ifdel(cfg, &cfg->if_event_info, dev); } else { WL_ERR(("IFDEL didn't complete properly\n")); } } ret = dhd_del_monitor(dev); if (wl_get_mode_by_netdev(cfg, dev) == WL_MODE_AP) { DHD_OS_WAKE_LOCK_CTRL_TIMEOUT_CANCEL((dhd_pub_t *)(cfg->pub)); } } } return ret; } static s32 wl_cfg80211_change_virtual_iface(struct wiphy *wiphy, struct net_device *ndev, enum nl80211_iftype type, #if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 11, 0)) u32 *flags, #endif struct vif_params *params) { s32 ap = 0; s32 infra = 0; s32 ibss = 0; s32 wlif_type; s32 mode = 0; s32 err = BCME_OK; chanspec_t chspec; struct bcm_cfg80211 *cfg = wiphy_priv(wiphy); dhd_pub_t *dhd = (dhd_pub_t *)(cfg->pub); WL_DBG(("Enter type %d\n", type)); if (!cfg) return -EINVAL; switch (type) { case NL80211_IFTYPE_MONITOR: case NL80211_IFTYPE_WDS: case NL80211_IFTYPE_MESH_POINT: ap = 1; WL_ERR(("type (%d) : currently we do not support this type\n", type)); break; case NL80211_IFTYPE_ADHOC: mode = WL_MODE_IBSS; ibss = 1; break; case NL80211_IFTYPE_STATION: case NL80211_IFTYPE_P2P_CLIENT: mode = WL_MODE_BSS; infra = 1; break; case NL80211_IFTYPE_AP: case NL80211_IFTYPE_AP_VLAN: case NL80211_IFTYPE_P2P_GO: mode = WL_MODE_AP; ap = 1; break; default: return -EINVAL; } if (!dhd) return -EINVAL; if (ap) { wl_set_mode_by_netdev(cfg, ndev, mode); if (cfg->p2p_supported && cfg->p2p->vif_created) { WL_DBG(("p2p_vif_created (%d) p2p_on (%d)\n", cfg->p2p->vif_created, p2p_on(cfg))); wldev_iovar_setint(ndev, "mpc", 0); wl_notify_escan_complete(cfg, ndev, true, true); /* In concurrency case, STA may be already associated in a particular * channel. so retrieve the current channel of primary interface and * then start the virtual interface on that. */ chspec = wl_cfg80211_get_shared_freq(wiphy); wlif_type = WL_P2P_IF_GO; WL_ERR(("%s : ap (%d), infra (%d), iftype: (%d)\n", ndev->name, ap, infra, type)); wl_set_p2p_status(cfg, IF_CHANGING); wl_clr_p2p_status(cfg, IF_CHANGED); wl_cfgp2p_ifchange(cfg, &cfg->p2p->int_addr, htod32(wlif_type), chspec); wait_event_interruptible_timeout(cfg->netif_change_event, (wl_get_p2p_status(cfg, IF_CHANGED) == true), msecs_to_jiffies(MAX_WAIT_TIME)); wl_set_mode_by_netdev(cfg, ndev, mode); dhd->op_mode &= ~DHD_FLAG_P2P_GC_MODE; dhd->op_mode |= DHD_FLAG_P2P_GO_MODE; wl_clr_p2p_status(cfg, IF_CHANGING); wl_clr_p2p_status(cfg, IF_CHANGED); if (mode == WL_MODE_AP) wl_set_drv_status(cfg, CONNECTED, ndev); #ifdef SUPPORT_AP_POWERSAVE dhd_set_ap_powersave(dhd, 0, TRUE); #endif } else if (ndev == bcmcfg_to_prmry_ndev(cfg) && !wl_get_drv_status(cfg, AP_CREATED, ndev)) { wl_set_drv_status(cfg, AP_CREATING, ndev); if (!cfg->ap_info && !(cfg->ap_info = kzalloc(sizeof(struct ap_info), GFP_KERNEL))) { WL_ERR(("struct ap_saved_ie allocation failed\n")); return -ENOMEM; } } else { WL_ERR(("Cannot change the interface for GO or SOFTAP\n")); return -EINVAL; } } else { WL_DBG(("Change_virtual_iface for transition from GO/AP to client/STA")); #ifdef SUPPORT_AP_POWERSAVE dhd_set_ap_powersave(dhd, 0, FALSE); #endif #ifdef P2PONEINT wl_set_mode_by_netdev(cfg, ndev, mode); if (cfg->p2p_supported && cfg->p2p->vif_created) { WL_DBG(("p2p_vif_created (%d) p2p_on (%d)\n", cfg->p2p->vif_created, p2p_on(cfg))); wldev_iovar_setint(ndev, "mpc", 0); wl_notify_escan_complete(cfg, ndev, true, true); /* In concurrency case, STA may be already associated in a particular * channel. so retrieve the current channel of primary interface and * then start the virtual interface on that. */ chspec = wl_cfg80211_get_shared_freq(wiphy); wlif_type = WL_P2P_IF_CLIENT; WL_ERR(("%s : ap (%d), infra (%d), iftype: (%d) chspec 0x%x \n", ndev->name, ap, infra, type, chspec)); wl_set_p2p_status(cfg, IF_CHANGING); wl_clr_p2p_status(cfg, IF_CHANGED); wl_cfgp2p_ifchange(cfg, &cfg->p2p->int_addr, htod32(wlif_type), chspec); wait_event_interruptible_timeout(cfg->netif_change_event, (wl_get_p2p_status(cfg, IF_CHANGED) == true), msecs_to_jiffies(MAX_WAIT_TIME)); wl_set_mode_by_netdev(cfg, ndev, mode); dhd->op_mode |= DHD_FLAG_P2P_GC_MODE; dhd->op_mode &= ~DHD_FLAG_P2P_GO_MODE; wl_clr_p2p_status(cfg, IF_CHANGING); wl_clr_p2p_status(cfg, IF_CHANGED); #define INIT_IE(IE_TYPE, BSS_TYPE) \ do { \ memset(wl_to_p2p_bss_saved_ie(cfg, BSS_TYPE).p2p_ ## IE_TYPE ## _ie, 0, \ sizeof(wl_to_p2p_bss_saved_ie(cfg, BSS_TYPE).p2p_ ## IE_TYPE ## _ie)); \ wl_to_p2p_bss_saved_ie(cfg, BSS_TYPE).p2p_ ## IE_TYPE ## _ie_len = 0; \ } while (0); INIT_IE(probe_req, P2PAPI_BSSCFG_CONNECTION); INIT_IE(probe_res, P2PAPI_BSSCFG_CONNECTION); INIT_IE(assoc_req, P2PAPI_BSSCFG_CONNECTION); INIT_IE(assoc_res, P2PAPI_BSSCFG_CONNECTION); INIT_IE(beacon, P2PAPI_BSSCFG_CONNECTION); } #endif /* P2PONEINT */ } if (ibss) { infra = 0; wl_set_mode_by_netdev(cfg, ndev, mode); err = wldev_ioctl(ndev, WLC_SET_INFRA, &infra, sizeof(s32), true); if (err < 0) { WL_ERR(("SET Adhoc error %d\n", err)); return -EINVAL; } } ndev->ieee80211_ptr->iftype = type; return 0; } s32 wl_cfg80211_notify_ifadd(int ifidx, char *name, uint8 *mac, uint8 bssidx) { bool ifadd_expected = FALSE; struct bcm_cfg80211 *cfg = g_bcm_cfg; /* P2P may send WLC_E_IF_ADD and/or WLC_E_IF_CHANGE during IF updating ("p2p_ifupd") * redirect the IF_ADD event to ifchange as it is not a real "new" interface */ if (wl_get_p2p_status(cfg, IF_CHANGING)) return wl_cfg80211_notify_ifchange(ifidx, name, mac, bssidx); /* Okay, we are expecting IF_ADD (as IF_ADDING is true) */ if (wl_get_p2p_status(cfg, IF_ADDING)) { ifadd_expected = TRUE; wl_clr_p2p_status(cfg, IF_ADDING); } else if (cfg->bss_pending_op) { ifadd_expected = TRUE; cfg->bss_pending_op = FALSE; } if (ifadd_expect