--- 015b411d0cea946887974e7a6d48e980940f7b6b +++ cb6da84506ec81d19c2e86f7decd3f8dfb8cf54c @@ -21,7 +21,7 @@ * 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 358102 2012-09-21 04:45:06Z $ + * $Id: wl_cfg80211.c 375020 2012-12-17 06:10:40Z $ */ #include @@ -58,6 +58,11 @@ #include #include #include +#include + +#ifdef PROP_TXSTATUS +#include +#endif #ifdef WL11U #ifndef WL_ENABLE_P2P_IF @@ -110,7 +115,7 @@ u32 wl_dbg_level = WL_DBG_ERR; #ifdef VSDB /* sleep time to keep STA's connecting or connection for continuous af tx or finding a peer */ -#define DEFAULT_SLEEP_TIME_VSDB 200 +#define DEFAULT_SLEEP_TIME_VSDB 200 #define OFF_CHAN_TIME_THRESHOLD_MS 200 /* if sta is connected or connecting, sleep for a while before retry af tx or finding a peer */ @@ -140,6 +145,10 @@ u32 wl_dbg_level = WL_DBG_ERR; #define DNGL_FUNC(func, parameters) func parameters; #define COEX_DHCP +#define WLAN_EID_SSID 0 +#define CH_MIN_5G_CHANNEL 34 +#define CH_MIN_2G_CHANNEL 1 + /* 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). @@ -255,6 +264,12 @@ static s32 wl_cfg80211_get_key(struct wi 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 0 || (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 2, 0)) +static s32 wl_cfg80211_mgmt_tx_cancel_wait(struct wiphy *wiphy, + struct net_device *dev, u64 cookie); +static s32 wl_cfg80211_del_station(struct wiphy *wiphy, + struct net_device *ndev, u8* mac_addr); +#endif #if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 39) static s32 wl_cfg80211_suspend(struct wiphy *wiphy, struct cfg80211_wowlan *wow); #else @@ -301,6 +316,15 @@ static s32 wl_bss_roaming_done(struct wl const wl_event_msg_t *e, void *data); static s32 wl_notify_mic_status(struct wl_priv *wl, struct net_device *ndev, const wl_event_msg_t *e, void *data); +#ifdef WL_SCHED_SCAN +static s32 +wl_notify_sched_scan_results(struct wl_priv *wl, 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 wl_priv *wl, struct net_device *ndev, + const wl_event_msg_t *e, void *data); +#endif /* PNO_SUPPORT */ static s32 wl_notifier_change_state(struct wl_priv *wl, struct net_info *_net_info, enum wl_status state, bool set); /* @@ -369,10 +393,9 @@ static s32 wl_setup_wiphy(struct wireles static void wl_free_wdev(struct wl_priv *wl); static s32 wl_inform_bss(struct wl_priv *wl); -static s32 wl_inform_single_bss(struct wl_priv *wl, struct wl_bss_info *bi); -static s32 wl_update_bss_info(struct wl_priv *wl, struct net_device *ndev); +static s32 wl_inform_single_bss(struct wl_priv *wl, struct wl_bss_info *bi, u8 is_roam_done); +static s32 wl_update_bss_info(struct wl_priv *wl, struct net_device *ndev, u8 is_roam_done); static chanspec_t wl_cfg80211_get_shared_freq(struct wiphy *wiphy); - static s32 wl_add_keyext(struct wiphy *wiphy, struct net_device *dev, u8 key_idx, const u8 *mac_addr, struct key_params *params); @@ -480,11 +503,16 @@ do { \ extern int dhd_wait_pend8021x(struct net_device *dev); -#ifdef PROP_TXSTATUS +#ifdef PROP_TXSTATUS_VSDB extern int disable_proptx; extern int dhd_wlfc_init(dhd_pub_t *dhd); extern void dhd_wlfc_deinit(dhd_pub_t *dhd); -#endif /* PROP_TXSTATUS */ +#endif /* PROP_TXSTATUS_VSDB */ + +#if defined(CUSTOMER_HW4) && defined(USE_DYNAMIC_F2_BLKSIZE) +extern int +dhdsdio_func_blocksize(dhd_pub_t *dhd, int function_num, int block_size); +#endif /* CUSTOMER_HW4 && USE_DYNAMIC_F2_BLKSIZE */ #if (WL_DBG_LEVEL > 0) #define WL_DBG_ESTR_MAX 50 @@ -958,7 +986,7 @@ static chanspec_t wl_cfg80211_get_shared else { bss = (struct wl_bss_info *) (wl->extra_buf + 4); chspec = bss->chanspec; - WL_DBG(("Valid BSS Found. chanspec:%d \n", bss->chanspec)); + WL_DBG(("Valid BSS Found. chanspec:%d \n", chspec)); } return chspec; } @@ -987,26 +1015,24 @@ wl_cfg80211_add_virtual_iface(struct wip s32 wlif_type = -1; s32 mode = 0; s32 val = 0; -#if defined(WL_ENABLE_P2P_IF) s32 dhd_mode = 0; -#endif /* (WL_ENABLE_P2P_IF) */ chanspec_t chspec; struct wl_priv *wl = wiphy_priv(wiphy); struct net_device *_ndev; struct ether_addr primary_mac; int (*net_attach)(void *dhdp, int ifidx); bool rollback_lock = false; -#ifdef PROP_TXSTATUS +#ifdef PROP_TXSTATUS_VSDB s32 up = 1; dhd_pub_t *dhd; -#endif /* PROP_TXSTATUS */ +#endif /* PROP_TXSTATUS_VSDB */ if (!wl) return ERR_PTR(-EINVAL); -#ifdef PROP_TXSTATUS +#ifdef PROP_TXSTATUS_VSDB dhd = (dhd_pub_t *)(wl->pub); -#endif /* PROP_TXSTATUS */ +#endif /* PROP_TXSTATUS_VSDB */ /* Use primary I/F for sending cmds down to firmware */ @@ -1077,11 +1103,11 @@ wl_cfg80211_add_virtual_iface(struct wip return ERR_PTR(-ENOMEM); } -#ifdef PROP_TXSTATUS +#ifdef PROP_TXSTATUS_VSDB if (!dhd) return ERR_PTR(-ENODEV); -#endif /* PROP_TXSTATUS */ - if (!wl->p2p || !wl->p2p->vir_ifname) +#endif /* PROP_TXSTATUS_VSDB */ + if (!wl->p2p) return ERR_PTR(-ENODEV); if (wl->p2p && !wl->p2p->on && strstr(name, WL_P2P_INTERFACE_PREFIX)) { @@ -1097,7 +1123,7 @@ wl_cfg80211_add_virtual_iface(struct wip strncpy(wl->p2p->vir_ifname, name, IFNAMSIZ - 1); wl_notify_escan_complete(wl, _ndev, true, true); -#ifdef PROP_TXSTATUS +#ifdef PROP_TXSTATUS_VSDB if (!wl->wlfc_on && !disable_proptx) { dhd->wlfc_enabled = true; dhd_wlfc_init(dhd); @@ -1106,7 +1132,7 @@ wl_cfg80211_add_virtual_iface(struct wip WL_ERR(("WLC_UP return err:%d\n", err)); wl->wlfc_on = true; } -#endif /* PROP_TXSTATUS */ +#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 @@ -1167,13 +1193,11 @@ wl_cfg80211_add_virtual_iface(struct wip "created net attach done\n", wl->p2p->vir_ifname)); if (mode == WL_MODE_AP) wl_set_drv_status(wl, CONNECTED, _ndev); -#if defined(WL_ENABLE_P2P_IF) if (type == NL80211_IFTYPE_P2P_CLIENT) - dhd_mode = P2P_GC_ENABLED; + dhd_mode = DHD_FLAG_P2P_GC_MODE; else if (type == NL80211_IFTYPE_P2P_GO) - dhd_mode = P2P_GO_ENABLED; + dhd_mode = DHD_FLAG_P2P_GO_MODE; DNGL_FUNC(dhd_cfg80211_set_p2p_info, (wl, dhd_mode)); -#endif /* (WL_ENABLE_P2P_IF) */ } else { /* put back the rtnl_lock again */ if (rollback_lock) @@ -1190,13 +1214,13 @@ wl_cfg80211_add_virtual_iface(struct wip WL_ERR((" virtual interface(%s) is not created \n", wl->p2p->vir_ifname)); memset(wl->p2p->vir_ifname, '\0', IFNAMSIZ); wl->p2p->vif_created = false; -#ifdef PROP_TXSTATUS +#ifdef PROP_TXSTATUS_VSDB if (dhd->wlfc_enabled && wl->wlfc_on) { dhd->wlfc_enabled = false; dhd_wlfc_deinit(dhd); wl->wlfc_on = false; } -#endif /* PROP_TXSTATUS */ +#endif /* PROP_TXSTATUS_VSDB */ } } fail: @@ -1242,9 +1266,7 @@ wl_cfg80211_del_virtual_iface(struct wip (&wl->iface_disable, msecs_to_jiffies(500)); } wl_set_p2p_status(wl, IF_DELETING); -#if defined(WL_ENABLE_P2P_IF) DNGL_FUNC(dhd_cfg80211_clean_p2p_info, (wl)); -#endif /* (WL_ENABLE_P2P_IF)) */ /* for GO */ if (wl_get_mode_by_netdev(wl, dev) == WL_MODE_AP) { @@ -1262,6 +1284,7 @@ wl_cfg80211_del_virtual_iface(struct wip msleep(300); } } + wl_cfgp2p_clear_management_ie(wl, wl_cfgp2p_find_idx(wl, dev)); /* delete interface after link down */ ret = wl_cfgp2p_ifdel(wl, &p2p_mac); /* Firmware could not delete the interface so we will not get WLC_E_IF @@ -1275,7 +1298,7 @@ wl_cfg80211_del_virtual_iface(struct wip struct net_device *ndev = wl_to_prmry_ndev(wl); WL_ERR(("Firmware returned an error (%d) from p2p_ifdel" "HANG Notification sent to %s\n", ret, ndev->name)); - wl_cfg80211_hang(ndev, WLAN_REASON_UNSPECIFIED); + net_os_send_hang_message(ndev); } /* Wait for IF_DEL operation to be finished in firmware */ timeout = wait_event_interruptible_timeout(wl->netif_change_event, @@ -1303,7 +1326,7 @@ wl_cfg80211_change_virtual_iface(struct s32 mode = 0; chanspec_t chspec; struct wl_priv *wl = wiphy_priv(wiphy); - + dhd_pub_t *dhd = (dhd_pub_t *)(wl->pub); WL_DBG(("Enter type %d\n", type)); switch (type) { case NL80211_IFTYPE_MONITOR: @@ -1330,7 +1353,8 @@ wl_cfg80211_change_virtual_iface(struct default: return -EINVAL; } - + if (!dhd) + return -EINVAL; if (ap) { wl_set_mode_by_netdev(wl, ndev, mode); if (wl->p2p_supported && wl->p2p->vif_created) { @@ -1355,6 +1379,8 @@ wl_cfg80211_change_virtual_iface(struct (wl_get_p2p_status(wl, IF_CHANGED) == true), msecs_to_jiffies(MAX_WAIT_TIME)); wl_set_mode_by_netdev(wl, ndev, mode); + dhd->op_mode &= ~DHD_FLAG_P2P_GC_MODE; + dhd->op_mode |= DHD_FLAG_P2P_GO_MODE; wl_clr_p2p_status(wl, IF_CHANGING); wl_clr_p2p_status(wl, IF_CHANGED); if (mode == WL_MODE_AP) @@ -1367,6 +1393,9 @@ wl_cfg80211_change_virtual_iface(struct WL_ERR(("struct ap_saved_ie allocation failed\n")); return -ENOMEM; } +#if defined(CUSTOMER_HW4) && defined(USE_DYNAMIC_F2_BLKSIZE) + dhdsdio_func_blocksize(dhd, 2, DYNAMIC_F2_BLKSIZE_FOR_NONLEGACY); +#endif /* CUSTOMER_HW4 && USE_DYNAMIC_F2_BLKSIZE */ } else { WL_ERR(("Cannot change the interface for GO or SOFTAP\n")); return -EINVAL; @@ -1425,9 +1454,9 @@ wl_cfg80211_ifdel_ops(struct net_device struct wl_priv *wl = wlcfg_drv_priv; bool rollback_lock = false; s32 index = 0; -#ifdef PROP_TXSTATUS +#ifdef PROP_TXSTATUS_VSDB dhd_pub_t *dhd = (dhd_pub_t *)(wl->pub); -#endif /* PROP_TXSTATUS */ +#endif /* PROP_TXSTATUS_VSDB */ if (!ndev || (strlen(ndev->name) == 0)) { WL_ERR(("net is NULL\n")); return 0; @@ -1454,18 +1483,17 @@ wl_cfg80211_ifdel_ops(struct net_device memset(wl->p2p->vir_ifname, '\0', IFNAMSIZ); index = wl_cfgp2p_find_idx(wl, ndev); wl_to_p2p_bss_ndev(wl, index) = NULL; - wl_to_p2p_bss_bssidx(wl, index) = 0; + wl_to_p2p_bss_bssidx(wl, index) = WL_INVALID; wl->p2p->vif_created = false; - wl_cfgp2p_clear_management_ie(wl, - index); + WL_DBG(("index : %d\n", index)); -#ifdef PROP_TXSTATUS +#ifdef PROP_TXSTATUS_VSDB if (dhd->wlfc_enabled && wl->wlfc_on) { dhd->wlfc_enabled = false; dhd_wlfc_deinit(dhd); wl->wlfc_on = false; } -#endif /* PROP_TXSTATUS */ +#endif /* PROP_TXSTATUS_VSDB */ wl_clr_drv_status(wl, CONNECTED, ndev); } /* Wake up any waiting thread */ @@ -1567,7 +1595,7 @@ static void wl_scan_prep(struct wl_scan_ u32 n_channels; u16 channel; chanspec_t chanspec; - s32 i = 0, offset; + s32 i = 0, j = 0, offset; char *ptr; wlc_ssid_t ssid; struct wl_priv *wl = wlcfg_drv_priv; @@ -1613,25 +1641,39 @@ static void wl_scan_prep(struct wl_scan_ (IEEE80211_CHAN_RADAR | IEEE80211_CHAN_PASSIVE_SCAN))) continue; - if (request->channels[i]->band == IEEE80211_BAND_2GHZ) + if (request->channels[i]->band == IEEE80211_BAND_2GHZ) { +#ifdef WL_HOST_BAND_MGMT + if (wl->curr_band == WLC_BAND_5G) { + WL_DBG(("In 5G only mode, omit 2G channel:%d\n", channel)); + continue; + } +#endif /* WL_HOST_BAND_MGMT */ chanspec |= WL_CHANSPEC_BAND_2G; - else + } else { +#ifdef WL_HOST_BAND_MGMT + if (wl->curr_band == WLC_BAND_2G) { + WL_DBG(("In 2G only mode, omit 5G channel:%d\n", channel)); + continue; + } +#endif /* WL_HOST_BAND_MGMT */ chanspec |= WL_CHANSPEC_BAND_5G; + } chanspec |= WL_CHANSPEC_BW_20; chanspec |= WL_CHANSPEC_CTL_SB_NONE; - params->channel_list[i] = channel; - params->channel_list[i] &= WL_CHANSPEC_CHAN_MASK; - params->channel_list[i] |= chanspec; + params->channel_list[j] = channel; + params->channel_list[j] &= WL_CHANSPEC_CHAN_MASK; + params->channel_list[j] |= chanspec; WL_SCAN(("Chan : %d, Channel spec: %x \n", - channel, params->channel_list[i])); - params->channel_list[i] = wl_chspec_host_to_driver(params->channel_list[i]); + channel, params->channel_list[j])); + params->channel_list[j] = wl_chspec_host_to_driver(params->channel_list[j]); + j++; } } else { WL_SCAN(("Scanning all channels\n")); } - n_channels = i; + n_channels = j; /* Copy ssid array if applicable */ WL_SCAN(("### List of SSIDs to scan ###\n")); if (n_ssids > 0) { @@ -1673,21 +1715,19 @@ wl_run_iscan(struct wl_iscan_ctrl *iscan struct wl_iscan_params *params = NULL; s32 err = 0; - if (request == NULL) { - err = -EINVAL; - goto done; - } - n_channels = request->n_channels; - n_ssids = request->n_ssids; - /* Allocate space for populating ssids in wl_iscan_params struct */ - if (n_channels % 2) - /* If n_channels is odd, add a padd of u16 */ - params_size += sizeof(u16) * (n_channels + 1); - else - params_size += sizeof(u16) * n_channels; + if (request != NULL) { + n_channels = request->n_channels; + n_ssids = request->n_ssids; + /* Allocate space for populating ssids in wl_iscan_params struct */ + if (n_channels % 2) + /* If n_channels is odd, add a padd of u16 */ + params_size += sizeof(u16) * (n_channels + 1); + else + params_size += sizeof(u16) * n_channels; - /* Allocate space for populating ssids in wl_iscan_params struct */ - params_size += sizeof(struct wlc_ssid) * n_ssids; + /* Allocate space for populating ssids in wl_iscan_params struct */ + params_size += sizeof(struct wlc_ssid) * n_ssids; + } params = (struct wl_iscan_params *)kzalloc(params_size, GFP_KERNEL); if (!params) { err = -ENOMEM; @@ -1731,7 +1771,7 @@ static s32 wl_do_iscan(struct wl_priv *w passive_scan = wl->active_scan ? 0 : 1; err = wldev_ioctl(ndev, WLC_SET_PASSIVE_SCAN, - &passive_scan, sizeof(passive_scan), false); + &passive_scan, sizeof(passive_scan), true); if (unlikely(err)) { WL_DBG(("error (%d)\n", err)); return err; @@ -1788,9 +1828,10 @@ wl_run_escan(struct wl_priv *wl, struct #ifdef USE_INITIAL_2G_SCAN bool is_first_init_2g_scan = false; #endif /* USE_INITIAL_2G_SCAN */ + WL_DBG(("Enter \n")); - if (!request || !wl) { + if (!wl) { err = -EINVAL; goto exit; } @@ -1798,6 +1839,9 @@ wl_run_escan(struct wl_priv *wl, struct /* LEGACY SCAN TRIGGER */ WL_SCAN((" LEGACY E-SCAN START\n")); + /* if scan request is not empty parse scan request paramters */ + if (request != NULL) { + #ifdef USE_INITIAL_2G_SCAN if (ndev == wl_to_prmry_ndev(wl) && g_first_broadcast_scan == true) { j = 0; @@ -1838,6 +1882,7 @@ wl_run_escan(struct wl_priv *wl, struct /* Allocate space for populating ssids in wl_iscan_params struct */ params_size += sizeof(struct wlc_ssid) * n_ssids; + } params = (wl_escan_params_t *) kzalloc(params_size, GFP_KERNEL); if (params == NULL) { err = -ENOMEM; @@ -1889,20 +1934,33 @@ wl_run_escan(struct wl_priv *wl, struct n_valid_chan = dtoh32(list->count); for (i = 0; i < num_chans; i++) { +#ifdef WL_HOST_BAND_MGMT + int channel_band = 0; +#endif /* WL_HOST_BAND_MGMT */ _freq = request->channels[i]->center_freq; channel = ieee80211_frequency_to_channel(_freq); - /* remove DFS channels */ - if (!(request->channels[i]->flags & +#ifdef WL_HOST_BAND_MGMT + channel_band = (channel > CH_MAX_2G_CHANNEL) ? + WLC_BAND_5G : WLC_BAND_2G; + if ((wl->curr_band != WLC_BAND_AUTO) && + (wl->curr_band != channel_band) && + !IS_P2P_SOCIAL_CHANNEL(channel)) + continue; +#endif /* WL_HOST_BAND_MGMT */ + + /* ignore DFS channels */ + if (request->channels[i]->flags & (IEEE80211_CHAN_RADAR - | IEEE80211_CHAN_PASSIVE_SCAN))) { - for (j = 0; j < n_valid_chan; j++) { - /* allows only supported channel on - * current reguatory - */ - if (channel == (dtoh32(list->element[j]))) - default_chan_list[n_nodfs++] = - channel; - } + | IEEE80211_CHAN_PASSIVE_SCAN)) + continue; + + for (j = 0; j < n_valid_chan; j++) { + /* allows only supported channel on + * current reguatory + */ + if (channel == (dtoh32(list->element[j]))) + default_chan_list[n_nodfs++] = + channel; } } @@ -1963,7 +2021,7 @@ wl_do_escan(struct wl_priv *wl, struct w wl->escan_info.escan_state = WL_ESCAN_STATE_SCANING; passive_scan = wl->active_scan ? 0 : 1; err = wldev_ioctl(ndev, WLC_SET_PASSIVE_SCAN, - &passive_scan, sizeof(passive_scan), false); + &passive_scan, sizeof(passive_scan), true); if (unlikely(err)) { WL_ERR(("error (%d)\n", err)); goto exit; @@ -2036,6 +2094,7 @@ __wl_cfg80211_scan(struct wiphy *wiphy, } #endif /* WL_CFG80211_VSDB_PRIORITIZE_SCAN_REQUEST */ + /* Arm scan timeout timer */ mod_timer(&wl->scan_timeout, jiffies + msecs_to_jiffies(WL_SCAN_TIMER_INTERVAL_MS)); iscan_req = false; @@ -2177,7 +2236,7 @@ __wl_cfg80211_scan(struct wiphy *wiphy, WL_SCAN(("sr->ssid.SSID_len (%d)\n", sr->ssid.SSID_len)); passive_scan = wl->active_scan ? 0 : 1; err = wldev_ioctl(ndev, WLC_SET_PASSIVE_SCAN, - &passive_scan, sizeof(passive_scan), false); + &passive_scan, sizeof(passive_scan), true); if (unlikely(err)) { WL_SCAN(("WLC_SET_PASSIVE_SCAN error (%d)\n", err)); goto scan_out; @@ -2229,7 +2288,7 @@ scan_out: if ((ret = wldev_ioctl(ndev, WLC_GET_BSSID, &bssid, ETHER_ADDR_LEN, false)) == 0) WL_ERR(("FW is connected with " MACDBG "/n", - STR_TO_MACD(bssid.octet))); + MAC2STRDBG(bssid.octet))); else WL_ERR(("GET BSSID failed with %d\n", ret)); @@ -2244,6 +2303,7 @@ scan_out: spin_lock_irqsave(&wl->cfgdrv_lock, flags); wl->scan_request = NULL; spin_unlock_irqrestore(&wl->cfgdrv_lock, flags); + return err; } @@ -2296,7 +2356,7 @@ static s32 wl_set_retry(struct net_devic u32 cmd = (l ? WLC_SET_LRL : WLC_SET_SRL); retry = htod32(retry); - err = wldev_ioctl(dev, cmd, &retry, sizeof(retry), false); + err = wldev_ioctl(dev, cmd, &retry, sizeof(retry), true); if (unlikely(err)) { WL_ERR(("cmd (%d) , error (%d)\n", cmd, err)); return err; @@ -2414,7 +2474,7 @@ wl_cfg80211_join_ibss(struct wiphy *wiph memset(&join_params.params.bssid, 0, ETHER_ADDR_LEN); err = wldev_ioctl(dev, WLC_SET_SSID, &join_params, - sizeof(join_params), false); + sizeof(join_params), true); if (unlikely(err)) { WL_ERR(("Error (%d)\n", err)); return err; @@ -2520,11 +2580,7 @@ wl_set_auth_type(struct net_device *dev, WL_DBG(("shared key\n")); break; case NL80211_AUTHTYPE_AUTOMATIC: -#ifdef USE_WEP_AUTH_SHARED_OPEN - val = WL_AUTH_SHARED_OPEN; -#else val = WL_AUTH_OPEN_SHARED; -#endif /* USE_WEP_AUTH_SHARED_OPEN */ WL_DBG(("automatic\n")); break; #ifdef BCMCCX @@ -2839,7 +2895,7 @@ wl_cfg80211_connect(struct wiphy *wiphy, wl_extjoin_params_t *ext_join_params; struct wl_join_params join_params; size_t join_params_size; -#ifdef ROAM_AP_ENV_DETECTION +#if defined(ROAM_ENABLE) && defined(ROAM_AP_ENV_DETECTION) dhd_pub_t *dhd = (dhd_pub_t *)(wl->pub); #endif /* ROAM_AP_ENV_DETECTION */ s32 err = 0; @@ -2897,7 +2953,7 @@ wl_cfg80211_connect(struct wiphy *wiphy, scbval.val = htod32(scbval.val); WL_DBG(("drv status CONNECTED is not set, but connected in FW!" MACDBG "/n", - STR_TO_MACD(bssid.octet))); + MAC2STRDBG(bssid.octet))); err = wldev_ioctl(dev, WLC_DISASSOC, &scbval, sizeof(scb_val_t), true); if (unlikely(err)) { @@ -2950,7 +3006,7 @@ wl_cfg80211_connect(struct wiphy *wiphy, return err; } } -#ifdef ROAM_AP_ENV_DETECTION +#if defined(ROAM_ENABLE) && defined(ROAM_AP_ENV_DETECTION) if (dhd->roam_env_detection && (wldev_iovar_setint(dev, "roam_env_detection", AP_ENV_DETECT_NOT_USED) == BCME_OK)) { s32 roam_trigger[2] = {WL_AUTO_ROAM_TRIGGER, WLC_BAND_ALL}; @@ -3047,12 +3103,13 @@ wl_cfg80211_connect(struct wiphy *wiphy, } ext_join_params->ssid.SSID_len = min(sizeof(ext_join_params->ssid.SSID), sme->ssid_len); memcpy(&ext_join_params->ssid.SSID, sme->ssid, ext_join_params->ssid.SSID_len); + wl_update_prof(wl, dev, NULL, &ext_join_params->ssid, WL_PROF_SSID); ext_join_params->ssid.SSID_len = htod32(ext_join_params->ssid.SSID_len); /* increate dwell time to receive probe response or detect Beacon * from target AP at a noisy air only during connect command */ - ext_join_params->scan.active_time = DHD_SCAN_ACTIVE_TIME*8; - ext_join_params->scan.passive_time = DHD_SCAN_PASSIVE_TIME*3; + ext_join_params->scan.active_time = WL_SCAN_JOIN_ACTIVE_DWELL_TIME_MS; + ext_join_params->scan.passive_time = WL_SCAN_JOIN_PASSIVE_DWELL_TIME_MS; /* Set up join scan parameters */ ext_join_params->scan.scan_type = -1; ext_join_params->scan.nprobes @@ -3635,7 +3692,7 @@ wl_cfg80211_get_station(struct wiphy *wi } if (memcmp(mac, curmacp, ETHER_ADDR_LEN)) { WL_ERR(("Wrong Mac address: "MACDBG" != "MACDBG"\n", - STR_TO_MACD(mac), STR_TO_MACD(curmacp))); + MAC2STRDBG(mac), MAC2STRDBG(curmacp))); } /* Report the current tx rate */ @@ -3686,6 +3743,23 @@ get_station_err: return err; } +/* Function to update sta power save mode for Kernel wifi stack */ +int wl_cfg80211_update_power_mode(struct net_device *dev) +{ + int pm = -1; + int err; + + err = wldev_ioctl(dev, WLC_GET_PM, &pm, sizeof(pm), false); + if (err || (pm == -1)) { + WL_ERR(("error (%d)\n", err)); + } else { + pm = (pm == PM_OFF) ? false : true; + WL_DBG(("%s: %d\n", __func__, pm)); + if (dev->ieee80211_ptr) + dev->ieee80211_ptr->ps = pm; + } + return err; +} static s32 wl_cfg80211_set_power_mgmt(struct wiphy *wiphy, struct net_device *dev, bool enabled, s32 timeout) @@ -3694,21 +3768,31 @@ wl_cfg80211_set_power_mgmt(struct wiphy s32 err = 0; struct wl_priv *wl = wiphy_priv(wiphy); struct net_info *_net_info = wl_get_netinfo_by_netdev(wl, dev); - +#if !defined(SUPPORT_PM2_ONLY) + dhd_pub_t *dhd = (dhd_pub_t *)(wl->pub); +#endif /* SUPPORT_PM2_ONLY */ CHECK_SYS_UP(wl); - if (wl->p2p_net == dev || _net_info == NULL) { + if (wl->p2p_net == dev || _net_info == NULL || wl->vsdb_mode) { return err; } + WL_DBG(("%s: Enter power save enabled %d\n", dev->name, enabled)); +#if !defined(SUPPORT_PM2_ONLY) + /* android has special hooks to change pm when kernel suspended */ + pm = enabled ? ((dhd->in_suspend) ? PM_MAX : PM_FAST) : PM_OFF; +#else pm = enabled ? PM_FAST : PM_OFF; - /* Do not enable the power save after assoc if it is p2p interface */ - if (_net_info->pm_block || wl->vsdb_mode) { - WL_DBG(("Do not enable the power save\n")); +#endif /* SUPPORT_PM2_ONLY */ + + if (_net_info->pm_block) { + /* Do not enable the power save if it is p2p interface or vsdb mode is set */ + WL_DBG(("%s:Do not enable the power save for pm_block %d or vsdb_mode %d\n", + dev->name, _net_info->pm_block, wl->vsdb_mode)); pm = PM_OFF; } pm = htod32(pm); - WL_DBG(("power save %s\n", (pm ? "enabled" : "disabled"))); + WL_DBG(("%s:power save %s\n", dev->name, (pm ? "enabled" : "disabled"))); err = wldev_ioctl(dev, WLC_SET_PM, &pm, sizeof(pm), true); if (unlikely(err)) { if (err == -ENODEV) @@ -3997,6 +4081,13 @@ wl_cfg80211_remain_on_channel(struct wip } else { ndev = dev; } + + if (!wl->p2p) { + WL_ERR(("wl->p2p is not initialized\n")); + err = BCME_ERROR; + goto exit; + } + #ifndef WL_CFG80211_VSDB_PRIORITIZE_SCAN_REQUEST if (wl_get_drv_status(wl, SCANNING, ndev)) { wl_notify_escan_complete(wl, ndev, true, true); @@ -4334,13 +4425,7 @@ wl_cfg80211_send_action_frame(struct wip #ifdef VSDB ulong off_chan_started_jiffies = 0; #endif - -#ifdef WL11U - if (!af_params || !action_frame || (!p2p_is_on(wl) && !wl->wl11u)) -#else - if (!af_params || !action_frame || !p2p_is_on(wl)) - return false; -#endif + dhd_pub_t *dhd = (dhd_pub_t *)(wl->pub); wl_cfgp2p_print_actframe(true, action_frame->data, action_frame->len); @@ -4384,7 +4469,7 @@ wl_cfg80211_send_action_frame(struct wip } else if (action_frame_len >= sizeof(wifi_p2psd_gas_pub_act_frame_t)) { /* service discovery process */ if (action == P2PSD_ACTION_ID_GAS_IREQ || - action == P2PSD_ACTION_ID_GAS_IREQ) { + action == P2PSD_ACTION_ID_GAS_CREQ) { /* configure service discovery query frame */ config_af_params.search_channel = true; @@ -4394,7 +4479,7 @@ wl_cfg80211_send_action_frame(struct wip af_params->dwell_time = WL_MED_DWELL_TIME; } else if (action == P2PSD_ACTION_ID_GAS_IRESP || - action == P2PSD_ACTION_ID_GAS_IRESP) { + action == P2PSD_ACTION_ID_GAS_CRESP) { /* configure service discovery response frame */ af_params->dwell_time = WL_MIN_DWELL_TIME; } else { @@ -4408,7 +4493,11 @@ wl_cfg80211_send_action_frame(struct wip /* do not configure anything. it will be sent with a default configuration */ } else { WL_DBG(("Unknown Frame: category 0x%x, action 0x%x\n", - category, action)); + category, action)); + if (dhd->op_mode & DHD_FLAG_HOSTAP_MODE) { + wl_clr_drv_status(wl, SENDING_ACT_FRM, dev); + return false; + } } /* To make sure to send successfully action frame, we have to turn off mpc */ @@ -4636,8 +4725,10 @@ wl_cfg80211_mgmt_tx(struct wiphy *wiphy, ieee80211_is_deauth(mgmt->frame_control)) { memcpy(scb_val.ea.octet, mgmt->da, ETH_ALEN); scb_val.val = mgmt->u.disassoc.reason_code; - wldev_ioctl(dev, WLC_SCB_DEAUTHENTICATE_FOR_REASON, &scb_val, + err = wldev_ioctl(dev, WLC_SCB_DEAUTHENTICATE_FOR_REASON, &scb_val, sizeof(scb_val_t), true); + if (err < 0) + WL_ERR(("WLC_SCB_DEAUTHENTICATE_FOR_REASON error %d\n", err)); WL_DBG(("Disconnect STA : %s scb_val.val %d\n", bcm_ether_ntoa((const struct ether_addr *)mgmt->da, eabuf), scb_val.val)); @@ -5509,6 +5600,53 @@ static s32 wl_cfg80211_hostapd_sec( return 0; } +#if 0 || (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 2, 0)) +static s32 +wl_cfg80211_del_station( + struct wiphy *wiphy, + struct net_device *ndev, + u8* mac_addr) +{ + struct net_device *dev; + struct wl_priv *wl = wiphy_priv(wiphy); + scb_val_t scb_val; + s8 eabuf[ETHER_ADDR_STR_LEN]; + + WL_DBG(("Entry\n")); + if (mac_addr == NULL) { + WL_DBG(("mac_addr is NULL ignore it\n")); + return 0; + } + + if (ndev == wl->p2p_net) { + dev = wl_to_prmry_ndev(wl); + } else { + dev = ndev; + } + + if (p2p_is_on(wl)) { + /* Suspend P2P discovery search-listen to prevent it from changing the + * channel. + */ + if ((wl_cfgp2p_discover_enable_search(wl, false)) < 0) { + WL_ERR(("Can not disable discovery mode\n")); + return -EFAULT; + } + } + + memcpy(scb_val.ea.octet, mac_addr, ETHER_ADDR_LEN); + scb_val.val = DOT11_RC_DEAUTH_LEAVING; + if (wldev_ioctl(dev, WLC_SCB_DEAUTHENTICATE_FOR_REASON, &scb_val, + sizeof(scb_val_t), true)) + WL_ERR(("WLC_SCB_DEAUTHENTICATE_FOR_REASON failed\n")); + WL_DBG(("Disconnect STA : %s scb_val.val %d\n", + bcm_ether_ntoa((const struct ether_addr *)mac_addr, eabuf), + scb_val.val)); + wl_delay(400); + return 0; +} +#endif + #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0) static s32 wl_cfg80211_start_ap( @@ -5638,49 +5776,6 @@ wl_cfg80211_stop_ap( exit: return err; } -static s32 -wl_cfg80211_del_station( - struct wiphy *wiphy, - struct net_device *ndev, - u8* mac_addr) -{ - struct net_device *dev; - struct wl_priv *wl = wiphy_priv(wiphy); - scb_val_t scb_val; - s8 eabuf[ETHER_ADDR_STR_LEN]; - - WL_DBG(("Entry\n")); - if (mac_addr == NULL) { - WL_DBG(("mac_addr is NULL ignore it\n")); - return 0; - } - - if (ndev == wl->p2p_net) { - dev = wl_to_prmry_ndev(wl); - } else { - dev = ndev; - } - - if (p2p_is_on(wl)) { - /* Suspend P2P discovery search-listen to prevent it from changing the - * channel. - */ - if ((wl_cfgp2p_discover_enable_search(wl, false)) < 0) { - WL_ERR(("Can not disable discovery mode\n")); - return -EFAULT; - } - } - - memcpy(scb_val.ea.octet, mac_addr, ETHER_ADDR_LEN); - scb_val.val = DOT11_RC_DEAUTH_LEAVING; - wldev_ioctl(dev, WLC_SCB_DEAUTHENTICATE_FOR_REASON, &scb_val, - sizeof(scb_val_t), true); - WL_DBG(("Disconnect STA : %s scb_val.val %d\n", - bcm_ether_ntoa((const struct ether_addr *)mac_addr, eabuf), - scb_val.val)); - wl_delay(400); - return 0; -} static s32 wl_cfg80211_change_beacon( @@ -5850,6 +5945,105 @@ fail: } #endif /* LINUX_VERSION_CODE < KERNEL_VERSION(3, 4, 0) */ +#ifdef WL_SCHED_SCAN +#define PNO_TIME 30 +#define PNO_REPEAT 4 +#define PNO_FREQ_EXPO_MAX 2 +int wl_cfg80211_sched_scan_start(struct wiphy *wiphy, + struct net_device *dev, + struct cfg80211_sched_scan_request *request) +{ + ushort pno_time = PNO_TIME; + int pno_repeat = PNO_REPEAT; + int pno_freq_expo_max = PNO_FREQ_EXPO_MAX; + wlc_ssid_t ssids_local[MAX_PFN_LIST_COUNT]; + struct wl_priv *wl = wiphy_priv(wiphy); + struct cfg80211_ssid *ssid = NULL; + int ssid_count = 0; + int i; + int ret = 0; + + WL_DBG(("Enter \n")); + WL_PNO((">>> SCHED SCAN START\n")); + WL_PNO(("Enter n_match_sets:%d n_ssids:%d \n", + request->n_match_sets, request->n_ssids)); + WL_PNO(("ssids:%d pno_time:%d pno_repeat:%d pno_freq:%d \n", + request->n_ssids, pno_time, pno_repeat, pno_freq_expo_max)); + + + if (!request || !request->n_ssids || !request->n_match_sets) { + WL_ERR(("Invalid sched scan req!! n_ssids:%d \n", request->n_ssids)); + return -EINVAL; + } + + memset(&ssids_local, 0, sizeof(ssids_local)); + + if (request->n_match_sets > 0) { + for (i = 0; i < request->n_match_sets; i++) { + ssid = &request->match_sets[i].ssid; + memcpy(ssids_local[i].SSID, ssid->ssid, ssid->ssid_len); + ssids_local[i].SSID_len = ssid->ssid_len; + WL_PNO((">>> PNO filter set for ssid (%s) \n", ssid->ssid)); + ssid_count++; + } + } + + if (request->n_ssids > 0) { + for (i = 0; i < request->n_ssids; i++) { + /* Active scan req for ssids */ + WL_PNO((">>> Active scan req for ssid (%s) \n", request->ssids[i].ssid)); + + /* match_set ssids is a supert set of n_ssid list, so we need + * not add these set seperately + */ + } + } + + if (ssid_count) { + if ((ret = dhd_dev_pno_set(dev, ssids_local, request->n_match_sets, + pno_time, pno_repeat, pno_freq_expo_max)) < 0) { + WL_ERR(("PNO setup failed!! ret=%d \n", ret)); + return -EINVAL; + } + + /* Enable the PNO */ + if (dhd_dev_pno_enable(dev, 1) < 0) { + WL_ERR(("PNO enable failed!! ret=%d \n", ret)); + return -EINVAL; + } + wl->sched_scan_req = request; + } else { + return -EINVAL; + } + + return 0; +} + +int wl_cfg80211_sched_scan_stop(struct wiphy *wiphy, struct net_device *dev) +{ + struct wl_priv *wl = wiphy_priv(wiphy); + + WL_DBG(("Enter \n")); + WL_PNO((">>> SCHED SCAN STOP\n")); + + if (dhd_dev_pno_enable(dev, 0) < 0) + WL_ERR(("PNO disable failed")); + + if (dhd_dev_pno_reset(dev) < 0) + WL_ERR(("PNO reset failed")); + + if (wl->scan_request && wl->sched_scan_running) { + WL_PNO((">>> Sched scan running. Aborting it..\n")); + wl_notify_escan_complete(wl, dev, true, true); + } + + wl->sched_scan_req = NULL; + wl->sched_scan_running = FALSE; + + return 0; +} +#endif /* WL_SCHED_SCAN */ + static struct cfg80211_ops wl_cfg80211_ops = { .add_virtual_intf = wl_cfg80211_add_virtual_iface, .del_virtual_intf = wl_cfg80211_del_virtual_iface, @@ -5887,8 +6081,17 @@ static struct cfg80211_ops wl_cfg80211_o .change_beacon = wl_cfg80211_change_beacon, .start_ap = wl_cfg80211_start_ap, .stop_ap = wl_cfg80211_stop_ap, - .del_station = wl_cfg80211_del_station, #endif /* LINUX_VERSION_CODE < KERNEL_VERSION(3, 4, 0) */ +#ifdef WL_SCHED_SCAN + .sched_scan_start = wl_cfg80211_sched_scan_start, + .sched_scan_stop = wl_cfg80211_sched_scan_stop, +#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3, 2, 0) */ +#if 0 || (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 2, 0)) +#ifndef CUSTOMER_HW4 + .del_station = wl_cfg80211_del_station, +#endif /* CUSTOMER_HW4 */ + .mgmt_tx_cancel_wait = wl_cfg80211_mgmt_tx_cancel_wait, +#endif }; s32 wl_mode_to_nl80211_iftype(s32 mode) @@ -5924,6 +6127,12 @@ static s32 wl_setup_wiphy(struct wireles /* Report how many SSIDs Driver can support per Scan request */ wdev->wiphy->max_scan_ssids = WL_SCAN_PARAMS_SSID_MAX; wdev->wiphy->max_num_pmkids = WL_NUM_PMKIDS_MAX; +#ifdef WL_SCHED_SCAN + wdev->wiphy->max_sched_scan_ssids = MAX_PFN_LIST_COUNT; + wdev->wiphy->max_match_sets = MAX_PFN_LIST_COUNT; + wdev->wiphy->max_sched_scan_ie_len = WL_SCAN_IE_LEN_MAX; + wdev->wiphy->flags |= WIPHY_FLAG_SUPPORTS_SCHED_SCAN; +#endif /* WL_SCHED_SCAN */ wdev->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) #if !(defined(WLP2P) && defined(WL_ENABLE_P2P_IF)) @@ -5965,7 +6174,7 @@ static s32 wl_setup_wiphy(struct wireles wdev->wiphy->flags |= WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL | WIPHY_FLAG_OFFCHAN_TX; #endif -#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0) +#if 0 || (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0)) /* From 3.4 kernel ownards AP_SME flag can be advertised * to remove the patch from supplicant */ @@ -6020,7 +6229,7 @@ static s32 wl_inform_bss(struct wl_priv #ifdef ROAM_CHANNEL_CACHE add_roam_cache(bi); #endif - err = wl_inform_single_bss(wl, bi); + err = wl_inform_single_bss(wl, bi, 0); if (unlikely(err)) break; } @@ -6030,7 +6239,7 @@ static s32 wl_inform_bss(struct wl_priv return err; } -static s32 wl_inform_single_bss(struct wl_priv *wl, struct wl_bss_info *bi) +static s32 wl_inform_single_bss(struct wl_priv *wl, struct wl_bss_info *bi, u8 is_roam_done) { struct wiphy *wiphy = wl_to_wiphy(wl); struct ieee80211_mgmt *mgmt; @@ -6045,6 +6254,7 @@ static s32 wl_inform_single_bss(struct w u32 freq; s32 err = 0; gfp_t aflags; + u8 *ie_offset = NULL; if (unlikely(dtoh32(bi->length) > WL_BSS_INFO_MAX)) { WL_DBG(("Beacon is larger than buffer. Discarding\n")); @@ -6085,7 +6295,36 @@ static s32 wl_inform_single_bss(struct w beacon_proberesp->capab_info = cpu_to_le16(bi->capability); wl_rst_ie(wl); - wl_mrg_ie(wl, ((u8 *) bi) + bi->ie_offset, bi->ie_length); + ie_offset = ((u8 *) bi) + bi->ie_offset; + + if (is_roam_done && ((int)(*(ie_offset)) == WLAN_EID_SSID && + ((int)(*(ie_offset+1)) == 0 || (int)(*(ie_offset+2)) == 0))) { + u8 *ie_new_offset = NULL; + uint8 ie_new_length; + + WL_ERR(("WAR trace: Changing the SSID Info, from beacon %d\n", + bi->flags & WL_BSS_FLAGS_FROM_BEACON)); + + ie_new_offset = (u8 *)kzalloc(WL_BSS_INFO_MAX, GFP_KERNEL); + if (ie_new_offset) { + *(ie_new_offset) = WLAN_EID_SSID; + *(ie_new_offset+1) = bi->SSID_len; + memcpy(ie_new_offset+2, bi->SSID, bi->SSID_len); + ie_new_length = bi->ie_length - *(ie_offset+1) + bi->SSID_len; + + /* Copy the remaining IE apart from SSID IE from bi */ + memcpy(ie_new_offset+2 + bi->SSID_len, + ie_offset+2 + *(ie_offset+1), + bi->ie_length - 2 - *(ie_offset+1)); + wl_mrg_ie(wl, ie_new_offset, ie_new_length); + kfree(ie_new_offset); + } else { + wl_mrg_ie(wl, ((u8 *) bi) + bi->ie_offset, bi->ie_length); + } + } else { + wl_mrg_ie(wl, ((u8 *) bi) + bi->ie_offset, bi->ie_length); + } + wl_cp_ie(wl, beacon_proberesp->variable, WL_BSS_INFO_MAX - offsetof(struct wl_cfg80211_bss_info, frame_buf)); notif_bss_info->frame_len = offsetof(struct ieee80211_mgmt, @@ -6233,16 +6472,17 @@ wl_notify_connect_status_ap(struct wl_pr struct station_info sinfo; #endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(3, 2, 0)) && !WL_CFG80211_STA_EVENT */ + WL_DBG(("event %d status %d reason %d\n", event, ntoh32(e->status), reason)); /* if link down, bsscfg is disabled. */ if (event == WLC_E_LINK && reason == WLC_E_LINK_BSSCFG_DIS && wl_get_p2p_status(wl, IF_DELETING) && (ndev != wl_to_prmry_ndev(wl))) { + wl_add_remove_eventmsg(ndev, WLC_E_PROBREQ_MSG, false); WL_INFO(("AP mode link down !! \n")); complete(&wl->iface_disable); return 0; } #if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 2, 0)) && !defined(WL_CFG80211_STA_EVENT) - body = kzalloc(len, GFP_KERNEL); WL_DBG(("Enter \n")); if (!len && (event == WLC_E_DEAUTH)) { len = 2; /* reason code field */ @@ -6355,6 +6595,9 @@ exit: } sinfo.assoc_req_ies = data; sinfo.assoc_req_ies_len = len; +#ifdef BCM4330_CHIP + cfg80211_del_sta(ndev, e->addr.octet, GFP_ATOMIC); +#endif cfg80211_new_sta(ndev, e->addr.octet, &sinfo, GFP_ATOMIC); } else if (event == WLC_E_DISASSOC_IND) { cfg80211_del_sta(ndev, e->addr.octet, GFP_ATOMIC); @@ -6414,7 +6657,7 @@ wl_notify_connect_status(struct wl_priv } else { if (!wl_get_drv_status(wl, DISCONNECTING, ndev)) { printk("wl_bss_connect_done succeeded with " MACDBG "\n", - STR_TO_MACD((u8*)(&e->addr))); + MAC2STRDBG((u8*)(&e->addr))); wl_bss_connect_done(wl, ndev, e, data, true); WL_DBG(("joined in BSS network \"%s\"\n", ((struct wlc_ssid *) @@ -6445,11 +6688,11 @@ wl_notify_connect_status(struct wl_priv printk("link down if %s may call cfg80211_disconnected. " "event : %d, reason=%d from " MACDBG "\n", ndev->name, event, ntoh32(e->reason), - STR_TO_MACD((u8*)(&e->addr))); + MAC2STRDBG((u8*)(&e->addr))); if (memcmp(curbssid, &e->addr, ETHER_ADDR_LEN) != 0) { WL_ERR(("BSSID of event is not the connected BSSID" "(ignore it) cur: " MACDBG " event: " MACDBG"\n", - STR_TO_MACD(curbssid), STR_TO_MACD((u8*)(&e->addr)))); + MAC2STRDBG(curbssid), MAC2STRDBG((u8*)(&e->addr)))); return 0; } wl_clr_drv_status(wl, CONNECTED, ndev); @@ -6645,7 +6888,7 @@ static void wl_ch_to_chanspec(int ch, st } } -static s32 wl_update_bss_info(struct wl_priv *wl, struct net_device *ndev) +static s32 wl_update_bss_info(struct wl_priv *wl, struct net_device *ndev, u8 is_roam_done) { struct cfg80211_bss *bss; struct wl_bss_info *bi; @@ -6655,6 +6898,7 @@ static s32 wl_update_bss_info(struct wl_ s32 dtim_period; size_t ie_len; u8 *ie; + u8 *ssidie; u8 *curbssid; s32 err = 0; struct wiphy *wiphy; @@ -6685,7 +6929,14 @@ static s32 wl_update_bss_info(struct wl_ err = -EIO; goto update_bss_info_out; } - err = wl_inform_single_bss(wl, bi); + + ie = ((u8 *)bi) + bi->ie_offset; + ie_len = bi->ie_length; + ssidie = (u8 *)cfg80211_find_ie(WLAN_EID_SSID, ie, ie_len); + if (ssidie && ssidie[1] == bi->SSID_len && !ssidie[2] && bi->SSID[0]) + memcpy(ssidie + 2, bi->SSID, bi->SSID_len); + + err = wl_inform_single_bss(wl, bi, is_roam_done); if (unlikely(err)) goto update_bss_info_out; @@ -6736,10 +6987,10 @@ wl_bss_roaming_done(struct wl_priv *wl, wl_get_assoc_ies(wl, ndev); wl_update_prof(wl, ndev, NULL, (void *)(e->addr.octet), WL_PROF_BSSID); curbssid = wl_read_prof(wl, ndev, WL_PROF_BSSID); - wl_update_bss_info(wl, ndev); + wl_update_bss_info(wl, ndev, 1); wl_update_pmklist(ndev, wl->pmk_list, err); printk("wl_bss_roaming_done succeeded to " MACDBG "\n", - STR_TO_MACD((u8*)(&e->addr))); + MAC2STRDBG((u8*)(&e->addr))); cfg80211_roamed(ndev, @@ -6764,19 +7015,20 @@ wl_bss_connect_done(struct wl_priv *wl, { struct wl_connect_info *conn_info = wl_to_conn(wl); struct wl_security *sec = wl_read_prof(wl, ndev, WL_PROF_SEC); -#ifdef ROAM_AP_ENV_DETECTION +#if defined(ROAM_ENABLE) && defined(ROAM_AP_ENV_DETECTION) dhd_pub_t *dhd = (dhd_pub_t *)(wl->pub); #endif /* ROAM_AP_ENV_DETECTION */ s32 err = 0; u8 *curbssid = wl_read_prof(wl, ndev, WL_PROF_BSSID); if (!sec) { WL_ERR(("sec is NULL\n")); + return -ENODEV; } WL_DBG((" enter\n")); #ifdef ESCAN_RESULT_PATCH if (wl_get_drv_status(wl, CONNECTED, ndev)) { if (memcmp(curbssid, connect_req_bssid, ETHER_ADDR_LEN) == 0) { - WL_ERR((" Connected event of connected device e=%d s=%d, ignore it\n", + WL_DBG((" Connected event of connected device e=%d s=%d, ignore it\n", ntoh32(e->event_type), ntoh32(e->status))); return err; } @@ -6803,10 +7055,10 @@ wl_bss_connect_done(struct wl_priv *wl, wl_get_assoc_ies(wl, ndev); wl_update_prof(wl, ndev, NULL, (void *)(e->addr.octet), WL_PROF_BSSID); curbssid = wl_read_prof(wl, ndev, WL_PROF_BSSID); - wl_update_bss_info(wl, ndev); + wl_update_bss_info(wl, ndev, 0); wl_update_pmklist(ndev, wl->pmk_list, err); wl_set_drv_status(wl, CONNECTED, ndev); -#ifdef ROAM_AP_ENV_DETECTION +#if defined(ROAM_ENABLE) && defined(ROAM_AP_ENV_DETECTION) if (dhd->roam_env_detection) wldev_iovar_setint(ndev, "roam_env_detection", AP_ENV_INDETERMINATE); @@ -6851,6 +7103,30 @@ wl_notify_mic_status(struct wl_priv *wl, return 0; } +#ifdef PNO_SUPPORT +static s32 +wl_notify_pfn_status(struct wl_priv *wl, struct net_device *ndev, + const wl_event_msg_t *e, void *data) +{ + WL_ERR((">>> PNO Event\n")); + +#ifndef WL_SCHED_SCAN +#ifndef CUSTOMER_HW4 + mutex_lock(&wl->usr_sync); + /* TODO: Use cfg80211_sched_scan_results(wiphy); */ + cfg80211_disconnected(ndev, 0, NULL, 0, GFP_KERNEL); + mutex_unlock(&wl->usr_sync); +#endif /* !CUSTOMER_HW4 */ +#else + /* If cfg80211 scheduled scan is supported, report the pno results via sched + * scan results + */ + wl_notify_sched_scan_results(wl, ndev, e, data); +#endif /* WL_SCHED_SCAN */ + return 0; +} +#endif /* PNO_SUPPORT */ + static s32 wl_notify_scan_status(struct wl_priv *wl, struct net_device *ndev, const wl_event_msg_t *e, void *data) @@ -7122,7 +7398,9 @@ wl_notify_rx_mgmt_frame(struct wl_priv * wldev_iovar_getbuf_bsscfg(dev, "cur_etheraddr", NULL, 0, wl->ioctl_buf, WLC_IOCTL_SMLEN, bsscfgidx, &wl->ioctl_buf_sync); - wldev_ioctl(dev, WLC_GET_BSSID, &bssid, ETHER_ADDR_LEN, false); + err = wldev_ioctl(dev, WLC_GET_BSSID, &bssid, ETHER_ADDR_LEN, false); + if (err < 0) + WL_ERR(("WLC_GET_BSSID error %d\n", err)); memcpy(da.octet, wl->ioctl_buf, ETHER_ADDR_LEN); err = wl_frame_get_mgmt(FC_ACTION, &da, &e->addr, &bssid, &mgmt_frame, &mgmt_frame_len, @@ -7144,6 +7422,7 @@ wl_notify_rx_mgmt_frame(struct wl_priv * (void) p2p_act_frm; } else if (wl_cfgp2p_is_gas_action(&mgmt_frame[DOT11_MGMT_HDR_LEN], mgmt_frame_len - DOT11_MGMT_HDR_LEN)) { + sd_act_frm = (wifi_p2psd_gas_pub_act_frame_t *) (&mgmt_frame[DOT11_MGMT_HDR_LEN]); if (sd_act_frm && wl_get_drv_status_all(wl, WAITING_NEXT_ACT_FRM)) { @@ -7275,6 +7554,122 @@ exit: return 0; } +#ifdef WL_SCHED_SCAN +/* If target scan is not reliable, set the below define to "1" to do a + * full escan + */ +#define FULL_ESCAN_ON_PFN_NET_FOUND 0 +static s32 +wl_notify_sched_scan_results(struct wl_priv *wl, struct net_device *ndev, + const wl_event_msg_t *e, void *data) +{ + wl_pfn_net_info_t *netinfo, *pnetinfo; + struct cfg80211_scan_request request; + struct wiphy *wiphy = wl_to_wiphy(wl); + int err = 0; + struct cfg80211_ssid ssid[MAX_PFN_LIST_COUNT]; + struct ieee80211_channel *channel = NULL; + int channel_req = 0; + int band = 0; + struct wl_pfn_scanresults *pfn_result = (struct wl_pfn_scanresults *)data; + + WL_DBG(("Enter\n")); + + if (e->event_type == WLC_E_PFN_NET_LOST) { + WL_PNO(("PFN NET LOST event. Do Nothing \n")); + return 0; + } + WL_PNO((">>> PFN NET FOUND event. count:%d \n", pfn_result->count)); + if (pfn_result->count > 0) { + int i; + + memset(&request, 0x00, sizeof(struct cfg80211_scan_request)); + memset(&ssid, 0x00, sizeof(ssid)); + request.wiphy = wiphy; + + pnetinfo = (wl_pfn_net_info_t *)(data + sizeof(wl_pfn_scanresults_t) + - sizeof(wl_pfn_net_info_t)); + channel = (struct ieee80211_channel *)kzalloc( + (sizeof(struct ieee80211_channel) * MAX_PFN_LIST_COUNT), + GFP_KERNEL); + if (!channel) { + WL_ERR(("No memory")); + err = -ENOMEM; + goto out_err; + } + + for (i = 0; i < pfn_result->count; i++) { + netinfo = &pnetinfo[i]; + if (!netinfo) { + WL_ERR(("Invalid netinfo ptr. index:%d", i)); + err = -EINVAL; + goto out_err; + } + WL_PNO((">>> SSID:%s Channel:%d \n", + netinfo->pfnsubnet.SSID, netinfo->pfnsubnet.channel)); + /* PFN result doesn't have all the info which are required by the supplicant + * (For e.g IEs) Do a target Escan so that sched scan results are reported + * via wl_inform_single_bss in the required format. Escan does require the + * scan request in the form of cfg80211_scan_request. For timebeing, create + * cfg80211_scan_request one out of the received PNO event. + */ + memcpy(ssid[i].ssid, netinfo->pfnsubnet.SSID, + netinfo->pfnsubnet.SSID_len); + ssid[i].ssid_len = netinfo->pfnsubnet.SSID_len; + request.n_ssids++; + + channel_req = netinfo->pfnsubnet.channel; + band = (channel_req <= CH_MAX_2G_CHANNEL) ? NL80211_BAND_2GHZ + : NL80211_BAND_5GHZ; + channel[i].center_freq = ieee80211_channel_to_frequency(channel_req, band); + channel[i].band = band; + channel[i].flags |= IEEE80211_CHAN_NO_HT40; + request.channels[i] = &channel[i]; + request.n_channels++; + } + + /* assign parsed ssid array */ + if (request.n_ssids) + request.ssids = &ssid[0]; + + if (wl_get_drv_status_all(wl, SCANNING)) { + /* Abort any on-going scan */ + wl_notify_escan_complete(wl, ndev, true, true); + } + + if (wl_get_p2p_status(wl, DISCOVERY_ON)) { + WL_PNO((">>> P2P discovery was ON. Disabling it\n")); + err = wl_cfgp2p_discover_enable_search(wl, false); + if (unlikely(err)) { + wl_clr_drv_status(wl, SCANNING, ndev); + goto out_err; + } + } + + wl_set_drv_status(wl, SCANNING, ndev); +#if FULL_ESCAN_ON_PFN_NET_FOUND + WL_PNO((">>> Doing Full ESCAN on PNO event\n")); + err = wl_do_escan(wl, wiphy, ndev, NULL); +#else + WL_PNO((">>> Doing targeted ESCAN on PNO event\n")); + err = wl_do_escan(wl, wiphy, ndev, &request); +#endif + if (err) { + wl_clr_drv_status(wl, SCANNING, ndev); + goto out_err; + } + wl->sched_scan_running = TRUE; + } + else { + WL_ERR(("FALSE PNO Event. (pfn_count == 0) \n")); + } +out_err: + if (channel) + kfree(channel); + return err; +} +#endif /* WL_SCHED_SCAN */ + static void wl_init_conf(struct wl_conf *conf) { WL_DBG(("Enter \n")); @@ -7317,7 +7712,9 @@ static void wl_init_event_handler(struct wl->evt_handler[WLC_E_P2P_DISC_LISTEN_COMPLETE] = wl_cfgp2p_listen_complete; wl->evt_handler[WLC_E_ACTION_FRAME_COMPLETE] = wl_cfgp2p_action_tx_complete; wl->evt_handler[WLC_E_ACTION_FRAME_OFF_CHAN_COMPLETE] = wl_cfgp2p_action_tx_complete; - +#ifdef PNO_SUPPORT + wl->evt_handler[WLC_E_PFN_NET_FOUND] = wl_notify_pfn_status; +#endif /* PNO_SUPPORT */ } static s32 wl_init_priv_mem(struct wl_priv *wl) @@ -7785,13 +8182,13 @@ static s32 wl_notify_escan_complete(stru ndev, wl_to_prmry_ndev(wl), wl->p2p_net)); dev = ndev; } - if (fw_abort) { + if (fw_abort && !in_atomic()) { /* Our scan params only need space for 1 channel and 0 ssids */ params = wl_cfg80211_scan_alloc_params(-1, 0, ¶ms_size); if (params == NULL) { WL_ERR(("scan params allocation failed \n")); err = -ENOMEM; - } else if (!in_atomic()) { + } else { /* Do a scan abort to stop the driver's scan engine */ err = wldev_ioctl(dev, WLC_SCAN, params, params_size, true); if (err < 0) { @@ -7818,6 +8215,17 @@ static s32 wl_notify_escan_complete(stru } #endif /* ESCAN_RESULT_PATCH */ spin_lock_irqsave(&wl->cfgdrv_lock, flags); +#ifdef WL_SCHED_SCAN + if (wl->sched_scan_req && !wl->scan_request) { + WL_PNO((">>> REPORTING SCHED SCAN RESULTS \n")); + if (aborted) + cfg80211_sched_scan_stopped(wl->sched_scan_req->wiphy); + else + cfg80211_sched_scan_results(wl->sched_scan_req->wiphy); + wl->sched_scan_running = FALSE; + wl->sched_scan_req = NULL; + } +#endif /* WL_SCHED_SCAN */ if (likely(wl->scan_request)) { cfg80211_scan_done(wl->scan_request, aborted); wl->scan_request = NULL; @@ -7860,7 +8268,8 @@ static s32 wl_escan_handler(struct wl_pr } if (!ndev || !wl->escan_on || - !wl_get_drv_status(wl, SCANNING, ndev)) { + (!wl_get_drv_status(wl, SCANNING, ndev) && + !wl->sched_scan_running)) { #if defined(DUAL_ESCAN_RESULT_BUFFER) WL_ERR(("escan is not ready ndev %p wl->escan_on %d" " drv_status 0x%x e_type %d e_states %d\n", @@ -7919,7 +8328,7 @@ static s32 wl_escan_handler(struct wl_pr s32 channel = CHSPEC_CHANNEL( wl_chspec_driver_to_host(bi->chanspec)); WL_DBG(("ACTION FRAME SCAN : Peer " MACDBG " found, channel : %d\n", - STR_TO_MACD(wl->afx_hdl->tx_dst_addr.octet), channel)); + MAC2STRDBG(wl->afx_hdl->tx_dst_addr.octet), channel)); wl_clr_p2p_status(wl, SCANNING); wl->afx_hdl->peer_chan = channel; complete(&wl->act_frm_scan); @@ -7940,6 +8349,10 @@ static s32 wl_escan_handler(struct wl_pr #else if (p2p_is_on(wl) && p2p_scan(wl)) { #endif +#ifdef WL_HOST_BAND_MGMT + s32 channel = 0; + s32 channel_band = 0; +#endif /* WL_HOST_BAND_MGMT */ /* p2p scan && allow only probe response */ if (bi->flags & WL_BSS_FLAGS_FROM_BEACON) goto exit; @@ -7949,6 +8362,20 @@ static s32 wl_escan_handler(struct wl_pr " response/beacon\n")); goto exit; } +#ifdef WL_HOST_BAND_MGMT + channel = CHSPEC_CHANNEL(wl_chspec_driver_to_host(bi->chanspec)); + channel_band = (channel > CH_MAX_2G_CHANNEL) ? + WLC_BAND_5G : WLC_BAND_2G; + + + if ((wl->curr_band == WLC_BAND_5G) && + (channel_band == WLC_BAND_2G)) { + /* Avoid sending the GO results in band conflict */ + if (wl_cfgp2p_retreive_p2pattrib(p2p_ie, + P2P_SEID_GROUP_ID) != NULL) + goto exit; + } +#endif /* WL_HOST_BAND_MGMT */ } for (i = 0; i < list->count; i++) { bss = bss ? (wl_bss_info_t *)((uintptr)bss + dtoh32(bss->length)) @@ -7969,7 +8396,7 @@ static s32 wl_escan_handler(struct wl_pr WL_DBG(("%s("MACDBG"), i=%d prev: RSSI %d" " flags 0x%x, new: RSSI %d flags 0x%x\n", - bss->SSID, STR_TO_MACD(bi->BSSID.octet), i, + bss->SSID, MAC2STRDBG(bi->BSSID.octet), i, bss->RSSI, bss->flags, bi->RSSI, bi->flags)); if ((bss->flags & WL_BSS_FLAGS_RSSI_ONCHANNEL) == @@ -7979,7 +8406,7 @@ static s32 wl_escan_handler(struct wl_pr */ WL_SCAN(("%s("MACDBG"), same onchan" ", RSSI: prev %d new %d\n", - bss->SSID, STR_TO_MACD(bi->BSSID.octet), + bss->SSID, MAC2STRDBG(bi->BSSID.octet), bss->RSSI, bi->RSSI)); bi->RSSI = MAX(bss->RSSI, bi->RSSI); } else if ((bss->flags & WL_BSS_FLAGS_RSSI_ONCHANNEL) && @@ -7989,7 +8416,7 @@ static s32 wl_escan_handler(struct wl_pr */ WL_SCAN(("%s("MACDBG"), prev onchan" ", RSSI: prev %d new %d\n", - bss->SSID, STR_TO_MACD(bi->BSSID.octet), + bss->SSID, MAC2STRDBG(bi->BSSID.octet), bss->RSSI, bi->RSSI)); bi->RSSI = bss->RSSI; bi->flags |= WL_BSS_FLAGS_RSSI_ONCHANNEL; @@ -8001,7 +8428,7 @@ static s32 wl_escan_handler(struct wl_pr " is occured(bcast:%d->probresp%d)\n", bss->ie_length, bi->ie_length)); WL_DBG(("%s("MACDBG"), replacement!(%d -> %d)\n", - bss->SSID, STR_TO_MACD(bi->BSSID.octet), + bss->SSID, MAC2STRDBG(bi->BSSID.octet), prev_len, bi_length)); if (list->buflen - prev_len + bi_length @@ -8060,7 +8487,7 @@ static s32 wl_escan_handler(struct wl_pr wl_clr_drv_status(wl, SCANNING, wl->afx_hdl->dev); if (wl->afx_hdl->peer_chan == WL_INVALID) complete(&wl->act_frm_scan); - } else if (likely(wl->scan_request)) { + } else if ((likely(wl->scan_request)) || (wl->sched_scan_running)) { WL_INFO(("ESCAN COMPLETED\n")); #if defined(DUAL_ESCAN_RESULT_BUFFER) wl->bss_list = (wl_scan_results_t *) @@ -8089,7 +8516,7 @@ static s32 wl_escan_handler(struct wl_pr wl_clr_p2p_status(wl, SCANNING); if (wl->afx_hdl->peer_chan == WL_INVALID) complete(&wl->act_frm_scan); - } else if (likely(wl->scan_request)) { + } else if ((likely(wl->scan_request)) || (wl->sched_scan_running)) { WL_INFO(("ESCAN ABORTED\n")); #if defined(DUAL_ESCAN_RESULT_BUFFER) wl->bss_list = (wl_scan_results_t *) @@ -8125,7 +8552,7 @@ static s32 wl_escan_handler(struct wl_pr wl_clr_drv_status(wl, SCANNING, wl->afx_hdl->dev); if (wl->afx_hdl->peer_chan == WL_INVALID) complete(&wl->act_frm_scan); - } else if (likely(wl->scan_request)) { + } else if ((likely(wl->scan_request)) || (wl->sched_scan_running)) { #if defined(DUAL_ESCAN_RESULT_BUFFER) wl->bss_list = (wl_scan_results_t *) wl->escan_info.escan_buf[(wl->escan_info.cur_sync_id+1)%2]; @@ -8143,147 +8570,164 @@ exit: mutex_unlock(&wl->usr_sync); return err; } +static void wl_cfg80211_concurrent_roam(struct wl_priv *wl, int enable) +{ + u32 connected_cnt = wl_get_drv_status_all(wl, CONNECTED); + struct net_info *iter, *next; + int err; + + if (!wl->roamoff_on_concurrent) + return; + if (enable && connected_cnt > 1) { + for_each_ndev(wl, iter, next) { + /* Save the current roam setting */ + if ((err = wldev_iovar_getint(iter->ndev, "roam_off", + (s32 *)&iter->roam_off)) != BCME_OK) { + WL_ERR(("%s:Failed to get current roam setting err %d\n", + iter->ndev->name, err)); + continue; + } + if ((err = wldev_iovar_setint(iter->ndev, "roam_off", 1)) != BCME_OK) { + WL_ERR((" %s:failed to set roam_off : %d\n", + iter->ndev->name, err)); + } + } + } + else if (!enable) { + for_each_ndev(wl, iter, next) { + if (iter->roam_off != WL_INVALID) { + if ((err = wldev_iovar_setint(iter->ndev, "roam_off", + iter->roam_off)) == BCME_OK) + iter->roam_off = WL_INVALID; + else { + WL_ERR((" %s:failed to set roam_off : %d\n", + iter->ndev->name, err)); + } + } + } + } + return; +} + +static void wl_cfg80211_determine_vsdb_mode(struct wl_priv *wl) +{ +#ifdef CUSTOMER_HW4 + u32 connected_cnt = wl_get_drv_status_all(wl, CONNECTED); + if (connected_cnt > 1) { + wl->vsdb_mode = true; + } else { + wl->vsdb_mode = false; + } + return; +#else + struct net_info *iter, *next; + u32 chan = 0; + u32 chanspec = 0; + u32 prev_chan = 0; + u32 connected_cnt = wl_get_drv_status_all(wl, CONNECTED); + wl->vsdb_mode = false; + + if (connected_cnt <= 1) { + return; + } + for_each_ndev(wl, iter, next) { + chanspec = 0; + chan = 0; + if (wl_get_drv_status(wl, CONNECTED, iter->ndev)) { + if (wldev_iovar_getint(iter->ndev, "chanspec", + (s32 *)&chanspec) == BCME_OK) { + chan = CHSPEC_CHANNEL(chanspec); + if (CHSPEC_IS40(chanspec)) { + if (CHSPEC_SB_UPPER(chanspec)) + chan += CH_10MHZ_APART; + else + chan -= CH_10MHZ_APART; + } + wl_update_prof(wl, iter->ndev, NULL, + &chan, WL_PROF_CHAN); + } + if (!prev_chan && chan) + prev_chan = chan; + else if (prev_chan && (prev_chan != chan)) + wl->vsdb_mode = true; + } + } + return; +#endif /* CUSTOMER_HW4 */ +} static s32 wl_notifier_change_state(struct wl_priv *wl, struct net_info *_net_info, enum wl_status state, bool set) { s32 pm = PM_FAST; s32 err = BCME_OK; u32 chan = 0; - u32 chanspec = 0; - u32 prev_chan = 0; - u32 connected_cnt = 0; struct net_info *iter, *next; struct net_device *primary_dev = wl_to_prmry_ndev(wl); - if (set) { /* set */ - switch (state) { - case WL_STATUS_CONNECTED: { - if ((connected_cnt = wl_get_drv_status_all(wl, CONNECTED)) > 1) { - pm = PM_OFF; - WL_INFO(("Do not enable the power save for VSDB mode\n")); - } else if (_net_info->pm_block) { - pm = PM_OFF; - } else { - pm = PM_FAST; - } + WL_DBG(("Enter state %d set %d _net_info->pm_restore %d iface %s\n", + state, set, _net_info->pm_restore, _net_info->ndev->name)); + + if (state != WL_STATUS_CONNECTED) + return 0; + + if (set) { + wl_cfg80211_concurrent_roam(wl, 1); + + if (wl_get_mode_by_netdev(wl, _net_info->ndev) == WL_MODE_AP) { + + if (wl_add_remove_eventmsg(primary_dev, WLC_E_P2P_PROBREQ_MSG, false)) + WL_ERR((" failed to unset WLC_E_P2P_PROPREQ_MSG\n")); + } + wl_cfg80211_determine_vsdb_mode(wl); + if (wl->vsdb_mode || _net_info->pm_block) { + pm = PM_OFF; for_each_ndev(wl, iter, next) { - if ((connected_cnt == 1) && (iter->ndev != _net_info->ndev)) + if (iter->pm_restore) continue; - chanspec = 0; - chan = 0; - if (wl_get_drv_status(wl, CONNECTED, iter->ndev)) { - if (wldev_iovar_getint(iter->ndev, "chanspec", - (s32 *)&chanspec) == BCME_OK) { - chan = CHSPEC_CHANNEL(chanspec); - if (CHSPEC_IS40(chanspec)) { - if (CHSPEC_SB_UPPER(chanspec)) - chan += CH_10MHZ_APART; - else - chan -= CH_10MHZ_APART; - } - wl_update_prof(wl, iter->ndev, NULL, - &chan, WL_PROF_CHAN); - } - if ((wl_get_mode_by_netdev(wl, iter->ndev) - == WL_MODE_BSS)) { - pm = htod32(pm); - WL_DBG(("power save %s\n", - (pm ? "enabled" : "disabled"))); - err = wldev_ioctl(iter->ndev, WLC_SET_PM, - &pm, sizeof(pm), true); - if (unlikely(err)) { - if (err == -ENODEV) - WL_DBG(("net_device" - " is not ready\n")); - else - WL_ERR(("error" - " (%d)\n", err)); - break; - } - } - if (connected_cnt > 1) { - if (!prev_chan && chan) - prev_chan = chan; - else if (prev_chan && (prev_chan != chan)) { - wl->vsdb_mode = true; - } - if (wl->roamoff_on_concurrent) { - if ((err = wldev_iovar_getint(iter->ndev, - "roam_off", (s32 *)&iter->roam_off)) - == BCME_OK) { - if ((err = - wldev_iovar_setint(iter->ndev, - "roam_off", 1)) != - BCME_OK) { - WL_ERR((" failed to set " - "roam_off : %d\n", err)); - } - } else - WL_ERR(("failed to get" - " roam_off : %d\n", err)); - } - } + /* Save the current power mode */ + err = wldev_ioctl(iter->ndev, WLC_GET_PM, &iter->pm, + sizeof(iter->pm), false); + WL_DBG(("%s:power save %s\n", iter->ndev->name, + iter->pm ? "enabled" : "disabled")); + if (!err && iter->pm) { + iter->pm_restore = true; } + } - if (wl_get_mode_by_netdev(wl, _net_info->ndev) == WL_MODE_AP) { - if (wl_add_remove_eventmsg(primary_dev, - WLC_E_P2P_PROBREQ_MSG, false)) - WL_ERR((" failed to unset" - " WLC_E_P2P_PROPREQ_MSG\n")); - } - break; - } - default: - break; - } - } else { /* clear */ - switch (state) { - case WL_STATUS_CONNECTED: { - chan = 0; - /* clear chan information when the net device is disconnected */ - wl_update_prof(wl, _net_info->ndev, NULL, &chan, WL_PROF_CHAN); - if (wl_get_drv_status_all(wl, CONNECTED) == 1) { - wl->vsdb_mode = false; - for_each_ndev(wl, iter, next) { - if (wl_get_drv_status(wl, CONNECTED, iter->ndev) && - (wl_get_mode_by_netdev(wl, iter->ndev) - == WL_MODE_BSS)) { - if (wl_get_netinfo_by_netdev(wl, - iter->ndev)->pm_block) - pm = PM_OFF; - else - pm = PM_FAST; - pm = htod32(pm); - WL_DBG(("power save %s\n", - (pm ? "enabled" : "disabled"))); - err = wldev_ioctl(iter->ndev, - WLC_SET_PM, &pm, sizeof(pm), true); - if (unlikely(err)) { - if (err == -ENODEV) - WL_DBG(("net_device" - " is not ready\n")); - else - WL_ERR(("error" - " (%d)\n", err)); - break; - } - } + for_each_ndev(wl, iter, next) { + if ((err = wldev_ioctl(iter->ndev, WLC_SET_PM, &pm, + sizeof(pm), true)) != 0) { + if (err == -ENODEV) + WL_DBG(("%s:netdev not ready\n", iter->ndev->name)); + else + WL_ERR(("%s:error (%d)\n", iter->ndev->name, err)); + iter->ndev->ieee80211_ptr->ps = false; } } - if (wl->roamoff_on_concurrent) { - for_each_ndev(wl, iter, next) { - if ((iter->roam_off != WL_INVALID) && - ((err = wldev_iovar_setint(iter->ndev, "roam_off", - iter->roam_off)) == BCME_OK)) { - iter->roam_off = WL_INVALID; - } else if (err) - WL_ERR((" failed to set roam_off : %d\n", err)); + } + } + else { /* clear */ + chan = 0; + /* clear chan information when the net device is disconnected */ + wl_update_prof(wl, _net_info->ndev, NULL, &chan, WL_PROF_CHAN); + wl_cfg80211_determine_vsdb_mode(wl); + for_each_ndev(wl, iter, next) { + if (iter->pm_restore && iter->pm) { + WL_DBG(("%s:restoring power save %s\n", + iter->ndev->name, (iter->pm ? "enabled" : "disabled"))); + err = wldev_ioctl(iter->ndev, + WLC_SET_PM, &iter->pm, sizeof(iter->pm), true); + if (unlikely(err)) { + if (err == -ENODEV) + WL_DBG(("%s:netdev not ready\n", iter->ndev->name)); + else + WL_ERR(("%s:error(%d)\n", iter->ndev->name, err)); + break; } + iter->pm_restore = 0; + iter->ndev->ieee80211_ptr->ps = true; } - break; - } - default: - break; } + wl_cfg80211_concurrent_roam(wl, 0); } return err; } @@ -8425,7 +8869,11 @@ s32 wl_cfg80211_attach_post(struct net_d return -ENODEV; } wl = wlcfg_drv_priv; - if (wl && !wl_get_drv_status(wl, READY, ndev)) { + if (unlikely(!wl)) { + WL_ERR(("wl is invaild\n")); + return -EINVAL; + } + if (!wl_get_drv_status(wl, READY, ndev)) { if (wl->wdev && wl_cfgp2p_supported(wl, ndev)) { #if !defined(WL_ENABLE_P2P_IF) @@ -8443,7 +8891,7 @@ s32 wl_cfg80211_attach_post(struct net_d wl->p2p_net->dev_addr[0] |= 0x02; WL_ERR(("%s: p2p_dev_addr="MACDBG "\n", wl->p2p_net->name, - STR_TO_MACD(wl->p2p_net->dev_addr))); + MAC2STRDBG(wl->p2p_net->dev_addr))); } else { WL_ERR(("p2p_net not yet populated." " Couldn't update the MAC Address for p2p0 \n")); @@ -8453,8 +8901,7 @@ s32 wl_cfg80211_attach_post(struct net_d wl->p2p_supported = true; } - } else - return -ENODEV; + } wl_set_drv_status(wl, READY, ndev); fail: return err; @@ -8551,8 +8998,9 @@ void wl_cfg80211_detach(void *para) wl_setup_rfkill(wl, FALSE); if (wl->p2p_supported) { - WL_ERR(("wl_cfgp2p_down() is not called yet\n")); - wl_cfgp2p_down(wl); + if (timer_pending(&wl->p2p->listen_timer)) + del_timer_sync(&wl->p2p->listen_timer); + wl_cfgp2p_deinit_priv(wl); } #if defined(WLP2P) && defined(WL_ENABLE_P2P_IF) @@ -8663,10 +9111,6 @@ wl_cfg80211_event(struct net_device *nde u32 event_type = ntoh32(e->event_type); struct wl_priv *wl = wlcfg_drv_priv; -#if defined(CUSTOMER_HW4) && defined(PNO_SUPPORT) && defined(CONFIG_HAS_WAKELOCK) - int pno_wakelock_timeout = 10; /* 10 second */ -#endif - #if (WL_DBG_LEVEL > 0) s8 *estr = (event_type <= sizeof(wl_dbg_estr) / WL_DBG_ESTR_MAX - 1) ? wl_dbg_estr[event_type] : (s8 *) "Unknown"; @@ -8674,9 +9118,6 @@ wl_cfg80211_event(struct net_device *nde #endif /* (WL_DBG_LEVEL > 0) */ if (event_type == WLC_E_PFN_NET_FOUND) { -#if defined(CUSTOMER_HW4) && defined(PNO_SUPPORT) && defined(CONFIG_HAS_WAKELOCK) - net_os_wake_lock_timeout_for_pno(ndev, pno_wakelock_timeout); -#endif WL_DBG((" PNOEVENT: PNO_NET_FOUND\n")); } else if (event_type == WLC_E_PFN_NET_LOST) { @@ -8815,7 +9256,8 @@ s32 wl_add_remove_eventmsg(struct net_de s8 eventmask[WL_EVENTING_MASK_LEN]; s32 err = 0; - + if (!ndev) + return -ENODEV; /* Setup event_msgs */ bcm_mkiovar("event_msgs", NULL, 0, iovbuf, sizeof(iovbuf)); @@ -8891,13 +9333,14 @@ static int wl_construct_reginfo(struct w else channel -= CH_10MHZ_APART; } - if (CHSPEC_IS2G(c) && channel <= CH_MAX_2G_CHANNEL) { + if (CHSPEC_IS2G(c) && (channel >= CH_MIN_2G_CHANNEL) && + (channel <= CH_MAX_2G_CHANNEL)) { band_chan_arr = __wl_2ghz_channels; array_size = ARRAYSIZE(__wl_2ghz_channels); n_cnt = &n_2g; band = IEEE80211_BAND_2GHZ; ht40_allowed = (bw_cap == WLC_N_BW_40ALL)? true : false; - } else if (CHSPEC_IS5G(c) && channel > CH_MAX_2G_CHANNEL) { + } else if (CHSPEC_IS5G(c) && channel >= CH_MIN_5G_CHANNEL) { band_chan_arr = __wl_5ghz_a_channels; array_size = ARRAYSIZE(__wl_5ghz_a_channels); n_cnt = &n_5g; @@ -8907,6 +9350,8 @@ static int wl_construct_reginfo(struct w WL_ERR(("Invalid channel Sepc. 0x%x.\n", c)); continue; } + if (!ht40_allowed && CHSPEC_IS40(c)) + continue; for (j = 0; (j < *n_cnt && (*n_cnt < array_size)); j++) { if (band_chan_arr[j].hw_value == channel) { update = true; @@ -8989,6 +9434,8 @@ s32 wl_update_wiphybands(struct wl_priv bool rollback_lock = false; s32 bw_cap = 0; s32 cur_band = -1; + struct ieee80211_supported_band *bands[IEEE80211_NUM_BANDS]={NULL,}; + if (wl == NULL) { wl = wlcfg_drv_priv; mutex_lock(&wl->usr_sync); @@ -9000,9 +9447,12 @@ s32 wl_update_wiphybands(struct wl_priv err = wldev_ioctl(dev, WLC_GET_BANDLIST, bandlist, sizeof(bandlist), false); if (unlikely(err)) { - WL_ERR(("error real bandlist (%d)\n", err)); + WL_ERR(("error read bandlist (%d)\n", err)); goto end_bands; } + + wiphy = wl_to_wiphy(wl); + err = wldev_ioctl(dev, WLC_GET_BAND, &cur_band, sizeof(s32), false); if (unlikely(err)) { @@ -9028,37 +9478,42 @@ s32 wl_update_wiphybands(struct wl_priv goto end_bands; err = 0; } - wiphy = wl_to_wiphy(wl); + nband = bandlist[0]; - wiphy->bands[IEEE80211_BAND_5GHZ] = NULL; - wiphy->bands[IEEE80211_BAND_2GHZ] = NULL; + for (i = 1; i <= nband && i < ARRAYSIZE(bandlist); i++) { index = -1; if (bandlist[i] == WLC_BAND_5G && __wl_band_5ghz_a.n_channels > 0) { - wiphy->bands[IEEE80211_BAND_5GHZ] = + bands[IEEE80211_BAND_5GHZ] = &__wl_band_5ghz_a; index = IEEE80211_BAND_5GHZ; if (bw_cap == WLC_N_BW_40ALL || bw_cap == WLC_N_BW_20IN2G_40IN5G) - wiphy->bands[index]->ht_cap.cap |= IEEE80211_HT_CAP_SGI_40; + bands[index]->ht_cap.cap |= IEEE80211_HT_CAP_SGI_40; } else if (bandlist[i] == WLC_BAND_2G && __wl_band_2ghz.n_channels > 0) { - wiphy->bands[IEEE80211_BAND_2GHZ] = + bands[IEEE80211_BAND_2GHZ] = &__wl_band_2ghz; index = IEEE80211_BAND_2GHZ; if (bw_cap == WLC_N_BW_40ALL) - wiphy->bands[index]->ht_cap.cap |= IEEE80211_HT_CAP_SGI_40; + bands[index]->ht_cap.cap |= IEEE80211_HT_CAP_SGI_40; } if ((index >= 0) && nmode) { - wiphy->bands[index]->ht_cap.cap |= + bands[index]->ht_cap.cap |= (IEEE80211_HT_CAP_SGI_20 | IEEE80211_HT_CAP_DSSSCCK40); - wiphy->bands[index]->ht_cap.ht_supported = TRUE; - wiphy->bands[index]->ht_cap.ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K; - wiphy->bands[index]->ht_cap.ampdu_density = IEEE80211_HT_MPDU_DENSITY_16; + bands[index]->ht_cap.ht_supported = TRUE; + bands[index]->ht_cap.ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K; + bands[index]->ht_cap.ampdu_density = IEEE80211_HT_MPDU_DENSITY_16; + /* An HT shall support all EQM rates for one spatial stream */ + bands[index]->ht_cap.mcs.rx_mask[0] = 0xff; } } + wiphy->bands[IEEE80211_BAND_2GHZ] = bands[IEEE80211_BAND_2GHZ]; + wiphy->bands[IEEE80211_BAND_5GHZ] = bands[IEEE80211_BAND_5GHZ]; + + wiphy_apply_custom_regulatory(wiphy, &brcm_regdom); end_bands: if (rollback_lock) @@ -9089,6 +9544,15 @@ static s32 __wl_cfg80211_up(struct wl_pr err = dhd_monitor_init(wl->pub); err = wl_invoke_iscan(wl); + +#ifdef WL_HOST_BAND_MGMT + /* By default the curr_band is initialized to BAND_AUTO */ + if (wl_cfg80211_set_band(ndev, WLC_BAND_AUTO) < 0) { + WL_ERR(("roam_band set failed\n")); + err = -1; + } +#endif /* WL_HOST_BAND_MGMT */ + wl_set_drv_status(wl, READY, ndev); return err; } @@ -9099,18 +9563,16 @@ static s32 __wl_cfg80211_down(struct wl_ unsigned long flags; struct net_info *iter, *next; struct net_device *ndev = wl_to_prmry_ndev(wl); -#ifdef WL_ENABLE_P2P_IF -#if !defined(CUSTOMER_HW4) struct net_device *p2p_net = wl->p2p_net; -#endif /* !defined(CUSTOMER_HW4) */ -#endif /* WL_ENABLE_P2P_IF */ WL_DBG(("In\n")); /* Check if cfg80211 interface is already down */ if (!wl_get_drv_status(wl, READY, ndev)) return err; /* it is even not ready */ + for_each_ndev(wl, iter, next) wl_set_drv_status(wl, SCAN_ABORTING, iter->ndev); + wl_term_iscan(wl); spin_lock_irqsave(&wl->cfgdrv_lock, flags); if (wl->scan_request) { @@ -9131,14 +9593,12 @@ static s32 __wl_cfg80211_down(struct wl_ } wl_to_prmry_ndev(wl)->ieee80211_ptr->iftype = NL80211_IFTYPE_STATION; -#ifdef WL_ENABLE_P2P_IF -#if !defined(CUSTOMER_HW4) - if ((p2p_net) && (p2p_net->flags & IFF_UP)) { - /* p2p0 interface is still UP. Bring it down */ - p2p_net->flags &= ~IFF_UP; +#if defined(WL_CFG80211) && defined(SUPPORT_DEEP_SLEEP) + if (!sleep_never) { + if (p2p_net) + dev_close(p2p_net); } #endif -#endif /* WL_ENABLE_P2P_IF */ DNGL_FUNC(dhd_cfg80211_down, (wl)); wl_flush_eq(wl); wl_link_down(wl); @@ -9155,7 +9615,6 @@ s32 wl_cfg80211_up(void *para) s32 err = 0; int val = 1; dhd_pub_t *dhd; - (void)para; WL_DBG(("In\n")); wl = wlcfg_drv_priv; @@ -9176,14 +9635,15 @@ s32 wl_cfg80211_up(void *para) mutex_lock(&wl->usr_sync); dhd = (dhd_pub_t *)(wl->pub); - if ((dhd->op_mode & HOSTAPD_MASK) != HOSTAPD_MASK) { - wl_cfg80211_attach_post(wl_to_prmry_ndev(wl)); + if (!(dhd->op_mode & DHD_FLAG_HOSTAP_MODE)) { + err = wl_cfg80211_attach_post(wl_to_prmry_ndev(wl)); + if (unlikely(err)) + return err; } err = __wl_cfg80211_up(wl); - if (err) + if (unlikely(err)) WL_ERR(("__wl_cfg80211_up failed\n")); mutex_unlock(&wl->usr_sync); - return err; } @@ -9237,6 +9697,7 @@ static void *wl_read_prof(struct wl_priv break; case WL_PROF_SSID: rptr = &profile->ssid; + break; case WL_PROF_CHAN: rptr = &profile->channel; break; @@ -9287,6 +9748,7 @@ wl_update_prof(struct wl_priv *wl, struc break; case WL_PROF_CHAN: profile->channel = *(u32*)data; + break; default: err = -EOPNOTSUPP; break; @@ -9624,6 +10086,20 @@ void wl_cfg80211_enable_trace(bool set, else wl_dbg_level |= (WL_DBG_LEVEL & level); } +#if 0 || (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 2, 0)) +static s32 +wl_cfg80211_mgmt_tx_cancel_wait(struct wiphy *wiphy, + struct net_device *dev, u64 cookie) +{ + /* CFG80211 checks for tx_cancel_wait callback when ATTR_DURATION + * is passed with CMD_FRAME. This callback is supposed to cancel + * the OFFCHANNEL Wait. Since we are already taking care of that + * with the tx_mgmt logic, do nothing here. + */ + + return 0; +} +#endif /* WL_SUPPORT_BACKPORTED_PATCHES || KERNEL >= 3.2.0 */ #ifdef WL11U bcm_tlv_t * @@ -9717,3 +10193,29 @@ wl_cfg80211_add_iw_ie(struct wl_priv *wl return err; } #endif /* WL11U */ + +#ifdef WL_HOST_BAND_MGMT +s32 +wl_cfg80211_set_band(struct net_device *ndev, int band) +{ + struct wl_priv *wl = wlcfg_drv_priv; + int ret = 0; + char ioctl_buf[50]; + + if ((band < WLC_BAND_AUTO) || (band > WLC_BAND_2G)) { + WL_ERR(("Invalid band\n")); + return -EINVAL; + } + + if ((ret = wldev_iovar_setbuf(ndev, "roam_band", &band, + sizeof(int), ioctl_buf, sizeof(ioctl_buf), NULL)) < 0) { + WL_ERR(("seting roam_band failed code=%d\n", ret)); + return ret; + } + + WL_DBG(("Setting band to %d\n", band)); + wl->curr_band = band; + + return 0; +} +#endif /* WL_HOST_BAND_MGMT */