diff options
Diffstat (limited to 'drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c')
-rw-r--r-- | drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c | 244 |
1 files changed, 195 insertions, 49 deletions
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c index 617199c0e5a0..dcde596c9eb9 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c @@ -625,6 +625,7 @@ struct wireless_dev *brcmf_ap_add_vif(struct wiphy *wiphy, const char *name, err = brcmf_net_attach(ifp, true); if (err) { brcmf_err("Registering netdevice failed\n"); + free_netdev(ifp->ndev); goto fail; } @@ -719,6 +720,8 @@ s32 brcmf_notify_escan_complete(struct brcmf_cfg80211_info *cfg, { struct brcmf_scan_params_le params_le; struct cfg80211_scan_request *scan_request; + u64 reqid; + u32 bucket; s32 err = 0; brcmf_dbg(SCAN, "Enter\n"); @@ -749,7 +752,7 @@ s32 brcmf_notify_escan_complete(struct brcmf_cfg80211_info *cfg, err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SCAN, ¶ms_le, sizeof(params_le)); if (err) - brcmf_err("Scan abort failed\n"); + brcmf_err("Scan abort failed\n"); } brcmf_scan_config_mpc(ifp, 1); @@ -758,11 +761,21 @@ s32 brcmf_notify_escan_complete(struct brcmf_cfg80211_info *cfg, * e-scan can be initiated internally * which takes precedence. */ - if (cfg->internal_escan) { - brcmf_dbg(SCAN, "scheduled scan completed\n"); - cfg->internal_escan = false; - if (!aborted) - cfg80211_sched_scan_results(cfg_to_wiphy(cfg), 0); + if (cfg->int_escan_map) { + brcmf_dbg(SCAN, "scheduled scan completed (%x)\n", + cfg->int_escan_map); + while (cfg->int_escan_map) { + bucket = __ffs(cfg->int_escan_map); + cfg->int_escan_map &= ~BIT(bucket); + reqid = brcmf_pno_find_reqid_by_bucket(cfg->pno, + bucket); + if (!aborted) { + brcmf_dbg(SCAN, "report results: reqid=%llu\n", + reqid); + cfg80211_sched_scan_results(cfg_to_wiphy(cfg), + reqid); + } + } } else if (scan_request) { struct cfg80211_scan_info info = { .aborted = aborted, @@ -1011,7 +1024,7 @@ static void brcmf_escan_prep(struct brcmf_cfg80211_info *cfg, if (!ssid_le.SSID_len) brcmf_dbg(SCAN, "%d: Broadcast scan\n", i); else - brcmf_dbg(SCAN, "%d: scan for %s size =%d\n", + brcmf_dbg(SCAN, "%d: scan for %.32s size=%d\n", i, ssid_le.SSID, ssid_le.SSID_len); memcpy(ptr, &ssid_le, sizeof(ssid_le)); ptr += sizeof(ssid_le); @@ -1344,6 +1357,27 @@ static u16 brcmf_map_fw_linkdown_reason(const struct brcmf_event_msg *e) return reason; } +static int brcmf_set_pmk(struct brcmf_if *ifp, const u8 *pmk_data, u16 pmk_len) +{ + struct brcmf_wsec_pmk_le pmk; + int i, err; + + /* convert to firmware key format */ + pmk.key_len = cpu_to_le16(pmk_len << 1); + pmk.flags = cpu_to_le16(BRCMF_WSEC_PASSPHRASE); + for (i = 0; i < pmk_len; i++) + snprintf(&pmk.key[2 * i], 3, "%02x", pmk_data[i]); + + /* store psk in firmware */ + err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_WSEC_PMK, + &pmk, sizeof(pmk)); + if (err < 0) + brcmf_err("failed to change PSK in firmware (len=%u)\n", + pmk_len); + + return err; +} + static void brcmf_link_down(struct brcmf_cfg80211_vif *vif, u16 reason) { struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(vif->wdev.wiphy); @@ -1366,6 +1400,10 @@ static void brcmf_link_down(struct brcmf_cfg80211_vif *vif, u16 reason) clear_bit(BRCMF_VIF_STATUS_CONNECTING, &vif->sme_state); clear_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status); brcmf_btcoex_set_mode(vif, BRCMF_BTCOEX_ENABLED, 0); + if (vif->profile.use_fwsup != BRCMF_PROFILE_FWSUP_NONE) { + brcmf_set_pmk(vif->ifp, NULL, 0); + vif->profile.use_fwsup = BRCMF_PROFILE_FWSUP_NONE; + } brcmf_dbg(TRACE, "Exit\n"); } @@ -1679,6 +1717,7 @@ static s32 brcmf_set_key_mgmt(struct net_device *ndev, struct cfg80211_connect_params *sme) { struct brcmf_if *ifp = netdev_priv(ndev); + struct brcmf_cfg80211_profile *profile = &ifp->vif->profile; s32 val; s32 err; const struct brcmf_tlv *rsn_ie; @@ -1689,6 +1728,8 @@ brcmf_set_key_mgmt(struct net_device *ndev, struct cfg80211_connect_params *sme) u32 mfp; u16 count; + profile->use_fwsup = BRCMF_PROFILE_FWSUP_NONE; + if (!sme->crypto.n_akm_suites) return 0; @@ -1701,6 +1742,8 @@ brcmf_set_key_mgmt(struct net_device *ndev, struct cfg80211_connect_params *sme) switch (sme->crypto.akm_suites[0]) { case WLAN_AKM_SUITE_8021X: val = WPA_AUTH_UNSPECIFIED; + if (sme->want_1x) + profile->use_fwsup = BRCMF_PROFILE_FWSUP_1X; break; case WLAN_AKM_SUITE_PSK: val = WPA_AUTH_PSK; @@ -1714,9 +1757,13 @@ brcmf_set_key_mgmt(struct net_device *ndev, struct cfg80211_connect_params *sme) switch (sme->crypto.akm_suites[0]) { case WLAN_AKM_SUITE_8021X: val = WPA2_AUTH_UNSPECIFIED; + if (sme->want_1x) + profile->use_fwsup = BRCMF_PROFILE_FWSUP_1X; break; case WLAN_AKM_SUITE_8021X_SHA256: val = WPA2_AUTH_1X_SHA256; + if (sme->want_1x) + profile->use_fwsup = BRCMF_PROFILE_FWSUP_1X; break; case WLAN_AKM_SUITE_PSK_SHA256: val = WPA2_AUTH_PSK_SHA256; @@ -1731,6 +1778,9 @@ brcmf_set_key_mgmt(struct net_device *ndev, struct cfg80211_connect_params *sme) } } + if (profile->use_fwsup == BRCMF_PROFILE_FWSUP_1X) + brcmf_dbg(INFO, "using 1X offload\n"); + if (!brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MFP)) goto skip_mfp_config; /* The MFP mode (1 or 2) needs to be determined, parse IEs. The @@ -1903,6 +1953,7 @@ brcmf_cfg80211_connect(struct wiphy *wiphy, struct net_device *ndev, { struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); struct brcmf_if *ifp = netdev_priv(ndev); + struct brcmf_cfg80211_profile *profile = &ifp->vif->profile; struct ieee80211_channel *chan = sme->channel; struct brcmf_join_params join_params; size_t join_params_size; @@ -1999,6 +2050,31 @@ brcmf_cfg80211_connect(struct wiphy *wiphy, struct net_device *ndev, goto done; } + if (sme->crypto.psk) { + if (WARN_ON(profile->use_fwsup != BRCMF_PROFILE_FWSUP_NONE)) { + err = -EINVAL; + goto done; + } + brcmf_dbg(INFO, "using PSK offload\n"); + profile->use_fwsup = BRCMF_PROFILE_FWSUP_PSK; + } + + if (profile->use_fwsup != BRCMF_PROFILE_FWSUP_NONE) { + /* enable firmware supplicant for this interface */ + err = brcmf_fil_iovar_int_set(ifp, "sup_wpa", 1); + if (err < 0) { + brcmf_err("failed to enable fw supplicant\n"); + goto done; + } + } + + if (profile->use_fwsup == BRCMF_PROFILE_FWSUP_PSK) { + err = brcmf_set_pmk(ifp, sme->crypto.psk, + BRCMF_WSEC_MAX_PSK_LEN); + if (err) + goto done; + } + /* Join with specific BSSID and cached SSID * If SSID is zero join based on BSSID only */ @@ -3011,7 +3087,7 @@ void brcmf_abort_scanning(struct brcmf_cfg80211_info *cfg) struct escan_info *escan = &cfg->escan_info; set_bit(BRCMF_SCAN_STATUS_ABORT, &cfg->scan_status); - if (cfg->internal_escan || cfg->scan_request) { + if (cfg->int_escan_map || cfg->scan_request) { escan->escan_state = WL_ESCAN_STATE_IDLE; brcmf_notify_escan_complete(cfg, escan->ifp, true, true); } @@ -3034,7 +3110,7 @@ static void brcmf_escan_timeout(unsigned long data) struct brcmf_cfg80211_info *cfg = (struct brcmf_cfg80211_info *)data; - if (cfg->internal_escan || cfg->scan_request) { + if (cfg->int_escan_map || cfg->scan_request) { brcmf_err("timer expired\n"); schedule_work(&cfg->escan_timeout_work); } @@ -3120,7 +3196,7 @@ brcmf_cfg80211_escan_handler(struct brcmf_if *ifp, if (brcmf_p2p_scan_finding_common_channel(cfg, bss_info_le)) goto exit; - if (!cfg->internal_escan && !cfg->scan_request) { + if (!cfg->int_escan_map && !cfg->scan_request) { brcmf_dbg(SCAN, "result without cfg80211 request\n"); goto exit; } @@ -3166,7 +3242,7 @@ brcmf_cfg80211_escan_handler(struct brcmf_if *ifp, cfg->escan_info.escan_state = WL_ESCAN_STATE_IDLE; if (brcmf_p2p_scan_finding_common_channel(cfg, NULL)) goto exit; - if (cfg->internal_escan || cfg->scan_request) { + if (cfg->int_escan_map || cfg->scan_request) { brcmf_inform_bss(cfg); aborted = status != BRCMF_E_STATUS_SUCCESS; brcmf_notify_escan_complete(cfg, ifp, aborted, false); @@ -3248,17 +3324,21 @@ static int brcmf_internal_escan_add_info(struct cfg80211_scan_request *req, return 0; } -static int brcmf_start_internal_escan(struct brcmf_if *ifp, +static int brcmf_start_internal_escan(struct brcmf_if *ifp, u32 fwmap, struct cfg80211_scan_request *request) { struct brcmf_cfg80211_info *cfg = ifp->drvr->config; int err; if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) { + if (cfg->int_escan_map) + brcmf_dbg(SCAN, "aborting internal scan: map=%u\n", + cfg->int_escan_map); /* Abort any on-going scan */ brcmf_abort_scanning(cfg); } + brcmf_dbg(SCAN, "start internal scan: map=%u\n", fwmap); set_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status); cfg->escan_info.run = brcmf_run_escan; err = brcmf_do_escan(ifp, request); @@ -3266,7 +3346,7 @@ static int brcmf_start_internal_escan(struct brcmf_if *ifp, clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status); return err; } - cfg->internal_escan = true; + cfg->int_escan_map = fwmap; return 0; } @@ -3308,6 +3388,7 @@ brcmf_notify_sched_scan_results(struct brcmf_if *ifp, struct wiphy *wiphy = cfg_to_wiphy(cfg); int i, err = 0; struct brcmf_pno_scanresults_le *pfn_result; + u32 bucket_map; u32 result_count; u32 status; u32 datalen; @@ -3352,6 +3433,7 @@ brcmf_notify_sched_scan_results(struct brcmf_if *ifp, goto out_err; } + bucket_map = 0; for (i = 0; i < result_count; i++) { netinfo = &netinfo_start[i]; @@ -3359,6 +3441,7 @@ brcmf_notify_sched_scan_results(struct brcmf_if *ifp, netinfo->SSID_len = IEEE80211_MAX_SSID_LEN; brcmf_dbg(SCAN, "SSID:%.32s Channel:%d\n", netinfo->SSID, netinfo->channel); + bucket_map |= brcmf_pno_get_bucket_map(cfg->pno, netinfo); err = brcmf_internal_escan_add_info(request, netinfo->SSID, netinfo->SSID_len, @@ -3367,7 +3450,10 @@ brcmf_notify_sched_scan_results(struct brcmf_if *ifp, goto out_err; } - err = brcmf_start_internal_escan(ifp, request); + if (!bucket_map) + goto free_req; + + err = brcmf_start_internal_escan(ifp, bucket_map, request); if (!err) goto free_req; @@ -3386,11 +3472,11 @@ brcmf_cfg80211_sched_scan_start(struct wiphy *wiphy, struct brcmf_if *ifp = netdev_priv(ndev); struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy); - brcmf_dbg(SCAN, "Enter n_match_sets:%d n_ssids:%d\n", + brcmf_dbg(SCAN, "Enter: n_match_sets=%d n_ssids=%d\n", req->n_match_sets, req->n_ssids); if (test_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status)) { - brcmf_err("Scanning suppressed: status (%lu)\n", + brcmf_err("Scanning suppressed: status=%lu\n", cfg->scan_status); return -EAGAIN; } @@ -3411,8 +3497,8 @@ static int brcmf_cfg80211_sched_scan_stop(struct wiphy *wiphy, struct brcmf_if *ifp = netdev_priv(ndev); brcmf_dbg(SCAN, "enter\n"); - brcmf_pno_clean(ifp); - if (cfg->internal_escan) + brcmf_pno_stop_sched_scan(ifp, reqid); + if (cfg->int_escan_map) brcmf_notify_escan_complete(cfg, ifp, true, true); return 0; } @@ -4674,9 +4760,6 @@ static int brcmf_cfg80211_stop_ap(struct wiphy *wiphy, struct net_device *ndev) err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_AP, 0); if (err < 0) brcmf_err("setting AP mode failed %d\n", err); - err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_INFRA, 0); - if (err < 0) - brcmf_err("setting INFRA mode failed %d\n", err); if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MBSS)) brcmf_fil_iovar_int_set(ifp, "mbss", 0); brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_REGULATORY, @@ -5131,6 +5214,34 @@ brcmf_cfg80211_set_rekey_data(struct wiphy *wiphy, struct net_device *ndev, } #endif +static int brcmf_cfg80211_set_pmk(struct wiphy *wiphy, struct net_device *dev, + const struct cfg80211_pmk_conf *conf) +{ + struct brcmf_if *ifp; + + brcmf_dbg(TRACE, "enter\n"); + + /* expect using firmware supplicant for 1X */ + ifp = netdev_priv(dev); + if (WARN_ON(ifp->vif->profile.use_fwsup != BRCMF_PROFILE_FWSUP_1X)) + return -EINVAL; + + return brcmf_set_pmk(ifp, conf->pmk, conf->pmk_len); +} + +static int brcmf_cfg80211_del_pmk(struct wiphy *wiphy, struct net_device *dev, + const u8 *aa) +{ + struct brcmf_if *ifp; + + brcmf_dbg(TRACE, "enter\n"); + ifp = netdev_priv(dev); + if (WARN_ON(ifp->vif->profile.use_fwsup != BRCMF_PROFILE_FWSUP_1X)) + return -EINVAL; + + return brcmf_set_pmk(ifp, NULL, 0); +} + static struct cfg80211_ops brcmf_cfg80211_ops = { .add_virtual_intf = brcmf_cfg80211_add_iface, .del_virtual_intf = brcmf_cfg80211_del_iface, @@ -5174,6 +5285,8 @@ static struct cfg80211_ops brcmf_cfg80211_ops = { .crit_proto_stop = brcmf_cfg80211_crit_proto_stop, .tdls_oper = brcmf_cfg80211_tdls_oper, .update_connect_params = brcmf_cfg80211_update_conn_params, + .set_pmk = brcmf_cfg80211_set_pmk, + .del_pmk = brcmf_cfg80211_del_pmk, }; struct brcmf_cfg80211_vif *brcmf_alloc_vif(struct brcmf_cfg80211_info *cfg, @@ -5227,16 +5340,31 @@ void brcmf_cfg80211_free_netdev(struct net_device *ndev) brcmf_free_vif(vif); } -static bool brcmf_is_linkup(const struct brcmf_event_msg *e) +static bool brcmf_is_linkup(struct brcmf_cfg80211_vif *vif, + const struct brcmf_event_msg *e) { u32 event = e->event_code; u32 status = e->status; + if (vif->profile.use_fwsup == BRCMF_PROFILE_FWSUP_PSK && + event == BRCMF_E_PSK_SUP && + status == BRCMF_E_STATUS_FWSUP_COMPLETED) + set_bit(BRCMF_VIF_STATUS_EAP_SUCCESS, &vif->sme_state); if (event == BRCMF_E_SET_SSID && status == BRCMF_E_STATUS_SUCCESS) { brcmf_dbg(CONN, "Processing set ssid\n"); - return true; + memcpy(vif->profile.bssid, e->addr, ETH_ALEN); + if (vif->profile.use_fwsup != BRCMF_PROFILE_FWSUP_PSK) + return true; + + set_bit(BRCMF_VIF_STATUS_ASSOC_SUCCESS, &vif->sme_state); } + if (test_bit(BRCMF_VIF_STATUS_EAP_SUCCESS, &vif->sme_state) && + test_bit(BRCMF_VIF_STATUS_ASSOC_SUCCESS, &vif->sme_state)) { + clear_bit(BRCMF_VIF_STATUS_EAP_SUCCESS, &vif->sme_state); + clear_bit(BRCMF_VIF_STATUS_ASSOC_SUCCESS, &vif->sme_state); + return true; + } return false; } @@ -5271,6 +5399,13 @@ static bool brcmf_is_nonetwork(struct brcmf_cfg80211_info *cfg, return true; } + if (event == BRCMF_E_PSK_SUP && + status != BRCMF_E_STATUS_FWSUP_COMPLETED) { + brcmf_dbg(CONN, "Processing failed supplicant state: %u\n", + status); + return true; + } + return false; } @@ -5421,27 +5556,28 @@ brcmf_bss_connect_done(struct brcmf_cfg80211_info *cfg, struct brcmf_if *ifp = netdev_priv(ndev); struct brcmf_cfg80211_profile *profile = &ifp->vif->profile; struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg); + struct cfg80211_connect_resp_params conn_params; brcmf_dbg(TRACE, "Enter\n"); if (test_and_clear_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state)) { + memset(&conn_params, 0, sizeof(conn_params)); if (completed) { brcmf_get_assoc_ies(cfg, ifp); - memcpy(profile->bssid, e->addr, ETH_ALEN); brcmf_update_bss_info(cfg, ifp); set_bit(BRCMF_VIF_STATUS_CONNECTED, &ifp->vif->sme_state); + conn_params.status = WLAN_STATUS_SUCCESS; + } else { + conn_params.status = WLAN_STATUS_AUTH_TIMEOUT; } - cfg80211_connect_result(ndev, - (u8 *)profile->bssid, - conn_info->req_ie, - conn_info->req_ie_len, - conn_info->resp_ie, - conn_info->resp_ie_len, - completed ? WLAN_STATUS_SUCCESS : - WLAN_STATUS_AUTH_TIMEOUT, - GFP_KERNEL); + conn_params.bssid = profile->bssid; + conn_params.req_ie = conn_info->req_ie; + conn_params.req_ie_len = conn_info->req_ie_len; + conn_params.resp_ie = conn_info->resp_ie; + conn_params.resp_ie_len = conn_info->resp_ie_len; + cfg80211_connect_done(ndev, &conn_params, GFP_KERNEL); brcmf_dbg(CONN, "Report connect result - connection %s\n", completed ? "succeeded" : "failed"); } @@ -5507,7 +5643,7 @@ brcmf_notify_connect_status(struct brcmf_if *ifp, if (brcmf_is_apmode(ifp->vif)) { err = brcmf_notify_connect_status_ap(cfg, ndev, e, data); - } else if (brcmf_is_linkup(e)) { + } else if (brcmf_is_linkup(ifp->vif, e)) { brcmf_dbg(CONN, "Linkup\n"); if (brcmf_is_ibssmode(ifp->vif)) { brcmf_inform_ibss(cfg, ndev, e->addr); @@ -5675,6 +5811,8 @@ static void brcmf_register_event_handlers(struct brcmf_cfg80211_info *cfg) brcmf_p2p_notify_action_tx_complete); brcmf_fweh_register(cfg->pub, BRCMF_E_ACTION_FRAME_OFF_CHAN_COMPLETE, brcmf_p2p_notify_action_tx_complete); + brcmf_fweh_register(cfg->pub, BRCMF_E_PSK_SUP, + brcmf_notify_connect_status); } static void brcmf_deinit_priv_mem(struct brcmf_cfg80211_info *cfg) @@ -6377,16 +6515,6 @@ err: return -ENOMEM; } -static void brcmf_wiphy_pno_params(struct wiphy *wiphy) -{ - /* scheduled scan settings */ - wiphy->max_sched_scan_reqs = 1; - wiphy->max_sched_scan_ssids = BRCMF_PNO_MAX_PFN_COUNT; - wiphy->max_match_sets = BRCMF_PNO_MAX_PFN_COUNT; - wiphy->max_sched_scan_ie_len = BRCMF_SCAN_IE_LEN_MAX; - wiphy->max_sched_scan_plan_interval = BRCMF_PNO_SCHED_SCAN_MAX_PERIOD; -} - #ifdef CONFIG_PM static const struct wiphy_wowlan_support brcmf_wowlan_support = { .flags = WIPHY_WOWLAN_MAGIC_PKT | WIPHY_WOWLAN_DISCONNECT, @@ -6433,6 +6561,7 @@ static int brcmf_setup_wiphy(struct wiphy *wiphy, struct brcmf_if *ifp) const struct ieee80211_iface_combination *combo; struct ieee80211_supported_band *band; u16 max_interfaces = 0; + bool gscan; __le32 bandlist[3]; u32 n_bands; int err, i; @@ -6480,11 +6609,18 @@ static int brcmf_setup_wiphy(struct wiphy *wiphy, struct brcmf_if *ifp) wiphy->flags |= WIPHY_FLAG_SUPPORTS_TDLS; if (!ifp->drvr->settings->roamoff) wiphy->flags |= WIPHY_FLAG_SUPPORTS_FW_ROAM; + if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_FWSUP)) { + wiphy_ext_feature_set(wiphy, + NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_PSK); + wiphy_ext_feature_set(wiphy, + NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_1X); + } wiphy->mgmt_stypes = brcmf_txrx_stypes; wiphy->max_remain_on_channel_duration = 5000; - if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_PNO)) - brcmf_wiphy_pno_params(wiphy); - + if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_PNO)) { + gscan = brcmf_feat_is_enabled(ifp, BRCMF_FEAT_GSCAN); + brcmf_pno_wiphy_params(wiphy, gscan); + } /* vendor commands/events support */ wiphy->vendor_commands = brcmf_vendor_cmds; wiphy->n_vendor_commands = BRCMF_VNDR_CMDS_LAST - 1; @@ -6766,7 +6902,7 @@ static void brcmf_cfg80211_reg_notifier(struct wiphy *wiphy, /* ignore non-ISO3166 country codes */ for (i = 0; i < sizeof(req->alpha2); i++) if (req->alpha2[i] < 'A' || req->alpha2[i] > 'Z') { - brcmf_err("not a ISO3166 code (0x%02x 0x%02x)\n", + brcmf_err("not an ISO3166 code (0x%02x 0x%02x)\n", req->alpha2[0], req->alpha2[1]); return; } @@ -6850,7 +6986,7 @@ struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr, wiphy = wiphy_new(ops, sizeof(struct brcmf_cfg80211_info)); if (!wiphy) { brcmf_err("Could not allocate wiphy device\n"); - return NULL; + goto ops_out; } memcpy(wiphy->perm_addr, drvr->mac, ETH_ALEN); set_wiphy_dev(wiphy, busdev); @@ -6951,6 +7087,13 @@ struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr, brcmf_p2p_detach(&cfg->p2p); goto wiphy_unreg_out; } + err = brcmf_pno_attach(cfg); + if (err) { + brcmf_err("PNO initialisation failed (%d)\n", err); + brcmf_btcoex_detach(cfg); + brcmf_p2p_detach(&cfg->p2p); + goto wiphy_unreg_out; + } if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_TDLS)) { err = brcmf_fil_iovar_int_set(ifp, "tdls_enable", 1); @@ -6983,6 +7126,7 @@ struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr, return cfg; detach: + brcmf_pno_detach(cfg); brcmf_btcoex_detach(cfg); brcmf_p2p_detach(&cfg->p2p); wiphy_unreg_out: @@ -6993,6 +7137,7 @@ priv_out: ifp->vif = NULL; wiphy_out: brcmf_free_wiphy(wiphy); +ops_out: kfree(ops); return NULL; } @@ -7002,6 +7147,7 @@ void brcmf_cfg80211_detach(struct brcmf_cfg80211_info *cfg) if (!cfg) return; + brcmf_pno_detach(cfg); brcmf_btcoex_detach(cfg); wiphy_unregister(cfg->wiphy); kfree(cfg->ops); |