1
0
Fork 0

brcmfmac: merge in latest driver from Cypress (including required wifi core module updates)

pull/10/head
Steinar Bakkemo 2020-10-07 12:08:41 +02:00
parent 9b0d59aedb
commit a808e2e497
40 changed files with 1967 additions and 300 deletions

View File

@ -20,7 +20,6 @@ config BRCMSMAC
config BRCMFMAC
tristate "Broadcom FullMAC WLAN driver"
depends on CFG80211
select WIRELESS_EXT
select BRCMUTIL
---help---
This module adds support for wireless adapters based on Broadcom

View File

@ -47,6 +47,7 @@
#include "sdio.h"
#include "core.h"
#include "common.h"
#include "cfg80211.h"
#define SDIOH_API_ACCESS_RETRY_LIMIT 2
@ -55,6 +56,7 @@
#define SDIO_FUNC1_BLOCKSIZE 64
#define SDIO_FUNC2_BLOCKSIZE 512
#define SDIO_4373_FUNC2_BLOCKSIZE 256
#define SDIO_4359_FUNC2_BLOCKSIZE 256
/* Maximum milliseconds to wait for F2 to come up */
#define SDIO_WAIT_F2RDY 3000
@ -1052,8 +1054,15 @@ static int brcmf_sdiod_probe(struct brcmf_sdio_dev *sdiodev)
goto out;
}
if (sdiodev->func[0]->device == SDIO_DEVICE_ID_CYPRESS_4373) {
switch (sdiodev->func[0]->device) {
case SDIO_DEVICE_ID_CYPRESS_4373:
f2_blksz = SDIO_4373_FUNC2_BLOCKSIZE;
break;
case SDIO_DEVICE_ID_BROADCOM_4359:
f2_blksz = SDIO_4359_FUNC2_BLOCKSIZE;
break;
default:
break;
}
ret = sdio_set_block_size(sdiodev->func[2], f2_blksz);
@ -1110,12 +1119,12 @@ static const struct sdio_device_id brcmf_sdmmc_ids[] = {
BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_43364),
BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4335_4339),
BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4339),
BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_43428),
BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_43430),
BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4345),
BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_43455),
BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4354),
BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4356),
BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4359),
BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_CYPRESS_4373),
BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_CYPRESS_43012),
{ /* end: all zeroes */ }
@ -1184,6 +1193,7 @@ static int brcmf_ops_sdio_probe(struct sdio_func *func,
dev_set_drvdata(&func->dev, bus_if);
dev_set_drvdata(&sdiodev->func[1]->dev, bus_if);
sdiodev->dev = &sdiodev->func[1]->dev;
dev_set_drvdata(&sdiodev->func[2]->dev, bus_if);
brcmf_sdiod_change_state(sdiodev, BRCMF_SDIOD_DOWN);
@ -1200,6 +1210,7 @@ static int brcmf_ops_sdio_probe(struct sdio_func *func,
fail:
dev_set_drvdata(&func->dev, NULL);
dev_set_drvdata(&sdiodev->func[1]->dev, NULL);
dev_set_drvdata(&sdiodev->func[2]->dev, NULL);
kfree(sdiodev->func[0]);
kfree(sdiodev);
kfree(bus_if);
@ -1256,14 +1267,26 @@ static int brcmf_ops_sdio_suspend(struct device *dev)
struct brcmf_bus *bus_if;
struct brcmf_sdio_dev *sdiodev;
mmc_pm_flag_t sdio_flags;
struct brcmf_cfg80211_info *config;
int retry = BRCMF_PM_WAIT_MAXRETRY;
func = container_of(dev, struct sdio_func, dev);
bus_if = dev_get_drvdata(dev);
config = bus_if->drvr->config;
brcmf_dbg(SDIO, "Enter: F%d\n", func->num);
while (retry &&
config->pm_state == BRCMF_CFG80211_PM_STATE_SUSPENDING) {
usleep_range(10000, 20000);
retry--;
}
if (!retry && config->pm_state == BRCMF_CFG80211_PM_STATE_SUSPENDING)
brcmf_err("timed out wait for cfg80211 suspended\n");
if (func->num != SDIO_FUNC_1)
return 0;
bus_if = dev_get_drvdata(dev);
sdiodev = bus_if->bus_priv.sdio;
brcmf_sdiod_freezer_on(sdiodev);

View File

@ -160,6 +160,7 @@ struct brcmf_bus {
const struct brcmf_bus_ops *ops;
struct brcmf_bus_msgbuf *msgbuf;
bool allow_skborphan;
};
/*
@ -256,6 +257,7 @@ void brcmf_bus_change_state(struct brcmf_bus *bus, enum brcmf_bus_state state);
int brcmf_bus_started(struct device *dev);
s32 brcmf_iovar_data_set(struct device *dev, char *name, void *data, u32 len);
void brcmf_bus_add_txhdrlen(struct device *dev, uint len);
int brcmf_fwlog_attach(struct device *dev);
#ifdef CONFIG_BRCMFMAC_SDIO
void brcmf_sdio_exit(void);

View File

@ -33,6 +33,7 @@
#include "p2p.h"
#include "btcoex.h"
#include "pno.h"
#include "fwsignal.h"
#include "cfg80211.h"
#include "feature.h"
#include "fwil.h"
@ -1367,6 +1368,30 @@ static int brcmf_set_pmk(struct brcmf_if *ifp, const u8 *pmk_data, u16 pmk_len)
return err;
}
static int brcmf_set_sae_password(struct brcmf_if *ifp, const u8 *pwd_data,
u16 pwd_len)
{
struct brcmf_wsec_sae_pwd_le sae_pwd;
int err;
if (pwd_len > BRCMF_WSEC_MAX_SAE_PASSWORD_LEN) {
brcmf_err("sae_password must be less than %d\n",
BRCMF_WSEC_MAX_SAE_PASSWORD_LEN);
return -EINVAL;
}
sae_pwd.key_len = cpu_to_le16(pwd_len);
memcpy(sae_pwd.key, pwd_data, pwd_len);
err = brcmf_fil_iovar_data_set(ifp, "sae_password", &sae_pwd,
sizeof(sae_pwd));
if (err < 0)
brcmf_err("failed to set SAE password in firmware (len=%u)\n",
pwd_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);
@ -1581,6 +1606,8 @@ static s32 brcmf_set_wpa_version(struct net_device *ndev,
val = WPA_AUTH_PSK | WPA_AUTH_UNSPECIFIED;
else if (sme->crypto.wpa_versions & NL80211_WPA_VERSION_2)
val = WPA2_AUTH_PSK | WPA2_AUTH_UNSPECIFIED;
else if (sme->crypto.wpa_versions & NL80211_WPA_VERSION_3)
val = WPA3_AUTH_SAE_PSK;
else
val = WPA_AUTH_DISABLED;
brcmf_dbg(CONN, "setting wpa_auth to 0x%0x\n", val);
@ -1611,6 +1638,10 @@ static s32 brcmf_set_auth_type(struct net_device *ndev,
val = 1;
brcmf_dbg(CONN, "shared key\n");
break;
case NL80211_AUTHTYPE_SAE:
val = 3;
brcmf_dbg(CONN, "SAE authentication\n");
break;
default:
val = 2;
brcmf_dbg(CONN, "automatic, auth type (%d)\n", sme->auth_type);
@ -1718,6 +1749,7 @@ brcmf_set_key_mgmt(struct net_device *ndev, struct cfg80211_connect_params *sme)
u16 count;
profile->use_fwsup = BRCMF_PROFILE_FWSUP_NONE;
profile->is_ft = false;
if (!sme->crypto.n_akm_suites)
return 0;
@ -1762,11 +1794,24 @@ brcmf_set_key_mgmt(struct net_device *ndev, struct cfg80211_connect_params *sme)
break;
case WLAN_AKM_SUITE_FT_8021X:
val = WPA2_AUTH_UNSPECIFIED | WPA2_AUTH_FT;
profile->is_ft = true;
if (sme->want_1x)
profile->use_fwsup = BRCMF_PROFILE_FWSUP_1X;
break;
case WLAN_AKM_SUITE_FT_PSK:
val = WPA2_AUTH_PSK | WPA2_AUTH_FT;
profile->is_ft = true;
break;
default:
brcmf_err("invalid cipher group (%d)\n",
sme->crypto.cipher_group);
return -EINVAL;
}
} else if (val & WPA3_AUTH_SAE_PSK) {
switch (sme->crypto.akm_suites[0]) {
case WLAN_AKM_SUITE_SAE:
val = WPA3_AUTH_SAE_PSK;
profile->use_fwsup = BRCMF_PROFILE_FWSUP_SAE;
break;
default:
brcmf_err("invalid cipher group (%d)\n",
@ -1777,6 +1822,8 @@ 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");
else if (profile->use_fwsup == BRCMF_PROFILE_FWSUP_SAE)
brcmf_dbg(INFO, "using SAE offload\n");
if (!brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MFP))
goto skip_mfp_config;
@ -1842,7 +1889,8 @@ brcmf_set_sharedkey(struct net_device *ndev,
brcmf_dbg(CONN, "wpa_versions 0x%x cipher_pairwise 0x%x\n",
sec->wpa_versions, sec->cipher_pairwise);
if (sec->wpa_versions & (NL80211_WPA_VERSION_1 | NL80211_WPA_VERSION_2))
if (sec->wpa_versions & (NL80211_WPA_VERSION_1 | NL80211_WPA_VERSION_2 |
NL80211_WPA_VERSION_3))
return 0;
if (!(sec->cipher_pairwise &
@ -2047,7 +2095,8 @@ brcmf_cfg80211_connect(struct wiphy *wiphy, struct net_device *ndev,
goto done;
}
if (sme->crypto.psk) {
if (sme->crypto.psk &&
profile->use_fwsup != BRCMF_PROFILE_FWSUP_SAE) {
if (WARN_ON(profile->use_fwsup != BRCMF_PROFILE_FWSUP_NONE)) {
err = -EINVAL;
goto done;
@ -2065,12 +2114,23 @@ brcmf_cfg80211_connect(struct wiphy *wiphy, struct net_device *ndev,
}
}
if (profile->use_fwsup == BRCMF_PROFILE_FWSUP_PSK) {
if (profile->use_fwsup == BRCMF_PROFILE_FWSUP_PSK)
err = brcmf_set_pmk(ifp, sme->crypto.psk,
BRCMF_WSEC_MAX_PSK_LEN);
if (err)
else if (profile->use_fwsup == BRCMF_PROFILE_FWSUP_SAE) {
/* clean up user-space RSNE */
if (brcmf_fil_iovar_data_set(ifp, "wpaie", NULL, 0)) {
brcmf_err("failed to clean up user-space RSNE\n");
goto done;
}
err = brcmf_set_sae_password(ifp, sme->crypto.sae_pwd,
sme->crypto.sae_pwd_len);
if (!err && sme->crypto.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
@ -2825,7 +2885,7 @@ brcmf_cfg80211_set_power_mgmt(struct wiphy *wiphy, struct net_device *ndev,
goto done;
}
pm = enabled ? PM_FAST : PM_OFF;
pm = enabled ? ifp->drvr->settings->default_pm : PM_OFF;
/* Do not enable the power save after assoc if it is a p2p interface */
if (ifp->vif->wdev.iftype == NL80211_IFTYPE_P2P_CLIENT) {
brcmf_dbg(INFO, "Do not enable power save for P2P clients\n");
@ -2863,7 +2923,7 @@ static s32 brcmf_inform_single_bss(struct brcmf_cfg80211_info *cfg,
if (le32_to_cpu(bi->length) > WL_BSS_INFO_MAX) {
brcmf_err("Bss info is larger than buffer. Discarding\n");
return 0;
return -EINVAL;
}
if (!bi->ctl_ch) {
@ -3589,6 +3649,8 @@ brcmf_wowl_nd_results(struct brcmf_if *ifp, const struct brcmf_event_msg *e,
}
netinfo = brcmf_get_netinfo_array(pfn_result);
if (netinfo->SSID_len > IEEE80211_MAX_SSID_LEN)
netinfo->SSID_len = IEEE80211_MAX_SSID_LEN;
memcpy(cfg->wowl.nd->ssid.ssid, netinfo->SSID, netinfo->SSID_len);
cfg->wowl.nd->ssid.ssid_len = netinfo->SSID_len;
cfg->wowl.nd->n_channels = 1;
@ -3689,10 +3751,24 @@ static s32 brcmf_cfg80211_resume(struct wiphy *wiphy)
struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
struct net_device *ndev = cfg_to_ndev(cfg);
struct brcmf_if *ifp = netdev_priv(ndev);
struct brcmf_pub *drvr = ifp->drvr;
struct brcmf_bus *bus_if = drvr->bus_if;
struct brcmf_cfg80211_info *config = drvr->config;
int retry = BRCMF_PM_WAIT_MAXRETRY;
brcmf_dbg(TRACE, "Enter\n");
config->pm_state = BRCMF_CFG80211_PM_STATE_RESUMING;
if (cfg->wowl.active) {
/* wait for bus resumed */
while (retry && bus_if->state != BRCMF_BUS_UP) {
usleep_range(10000, 20000);
retry--;
}
if (!retry && bus_if->state != BRCMF_BUS_UP)
brcmf_err("timed out wait for bus resume\n");
brcmf_report_wowl_wakeind(wiphy, ifp);
brcmf_fil_iovar_int_set(ifp, "wowl_clear", 0);
brcmf_config_wowl_pattern(ifp, "clr", NULL, 0, NULL, 0);
@ -3713,6 +3789,7 @@ static s32 brcmf_cfg80211_resume(struct wiphy *wiphy)
brcmf_pktfilter_enable(ifp->ndev, false);
}
config->pm_state = BRCMF_CFG80211_PM_STATE_RESUMED;
return 0;
}
@ -3782,9 +3859,12 @@ static s32 brcmf_cfg80211_suspend(struct wiphy *wiphy,
struct net_device *ndev = cfg_to_ndev(cfg);
struct brcmf_if *ifp = netdev_priv(ndev);
struct brcmf_cfg80211_vif *vif;
struct brcmf_cfg80211_info *config = ifp->drvr->config;
brcmf_dbg(TRACE, "Enter\n");
config->pm_state = BRCMF_CFG80211_PM_STATE_SUSPENDING;
/* if the primary net_device is not READY there is nothing
* we can do but pray resume goes smoothly.
*/
@ -3820,15 +3900,19 @@ static s32 brcmf_cfg80211_suspend(struct wiphy *wiphy,
brcmf_set_mpc(ifp, 1);
} else {
/* Configure WOWL parameters */
if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_WOWL))
/* Configure WOWL parameters */
brcmf_configure_wowl(cfg, ifp, wowl);
}
exit:
brcmf_dbg(TRACE, "Exit\n");
/* set cfg80211 pm state to cfg80211 suspended state */
config->pm_state = BRCMF_CFG80211_PM_STATE_SUSPENDED;
/* clear any scanning activity */
cfg->scan_status = 0;
brcmf_dbg(TRACE, "Exit\n");
return 0;
}
@ -4361,6 +4445,11 @@ s32 brcmf_vif_set_mgmt_ie(struct brcmf_cfg80211_vif *vif, s32 pktflag,
mgmt_ie_len = &saved_ie->assoc_req_ie_len;
mgmt_ie_buf_len = sizeof(saved_ie->assoc_req_ie);
break;
case BRCMF_VNDR_IE_ASSOCRSP_FLAG:
mgmt_ie_buf = saved_ie->assoc_res_ie;
mgmt_ie_len = &saved_ie->assoc_res_ie_len;
mgmt_ie_buf_len = sizeof(saved_ie->assoc_res_ie);
break;
default:
err = -EPERM;
brcmf_err("not suitable type\n");
@ -4510,6 +4599,15 @@ brcmf_config_ap_mgmt_ie(struct brcmf_cfg80211_vif *vif,
else
brcmf_dbg(TRACE, "Applied Vndr IEs for Probe Resp\n");
/* Set Assoc Response IEs to FW */
err = brcmf_vif_set_mgmt_ie(vif, BRCMF_VNDR_IE_ASSOCRSP_FLAG,
beacon->assocresp_ies,
beacon->assocresp_ies_len);
if (err)
brcmf_err("Set Assoc Resp IE Failed\n");
else
brcmf_dbg(TRACE, "Applied Vndr IEs for Assoc Resp\n");
return err;
}
@ -5342,6 +5440,7 @@ struct brcmf_cfg80211_vif *brcmf_alloc_vif(struct brcmf_cfg80211_info *cfg,
struct brcmf_cfg80211_vif *vif_walk;
struct brcmf_cfg80211_vif *vif;
bool mbss;
struct brcmf_if *ifp = brcmf_get_ifp(cfg->pub, 0);
brcmf_dbg(TRACE, "allocating virtual interface (size=%zu)\n",
sizeof(*vif));
@ -5354,7 +5453,8 @@ struct brcmf_cfg80211_vif *brcmf_alloc_vif(struct brcmf_cfg80211_info *cfg,
brcmf_init_prof(&vif->profile);
if (type == NL80211_IFTYPE_AP) {
if (type == NL80211_IFTYPE_AP &&
brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MBSS)) {
mbss = false;
list_for_each_entry(vif_walk, &cfg->vif_list, list) {
if (vif_walk->wdev.iftype == NL80211_IFTYPE_AP) {
@ -5393,25 +5493,28 @@ static bool brcmf_is_linkup(struct brcmf_cfg80211_vif *vif,
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)
if (event == BRCMF_E_PSK_SUP &&
status == BRCMF_E_STATUS_FWSUP_COMPLETED) {
set_bit(BRCMF_VIF_STATUS_EAP_SUCCESS, &vif->sme_state);
if (vif->profile.use_fwsup == BRCMF_PROFILE_FWSUP_1X)
return true;
}
if (event == BRCMF_E_SET_SSID && status == BRCMF_E_STATUS_SUCCESS) {
brcmf_dbg(CONN, "Processing set ssid\n");
memcpy(vif->profile.bssid, e->addr, ETH_ALEN);
if (vif->profile.use_fwsup != BRCMF_PROFILE_FWSUP_PSK)
if (vif->profile.use_fwsup != BRCMF_PROFILE_FWSUP_PSK &&
vif->profile.use_fwsup != BRCMF_PROFILE_FWSUP_SAE)
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);
test_and_clear_bit(BRCMF_VIF_STATUS_ASSOC_SUCCESS,
&vif->sme_state))
return true;
}
return false;
}
@ -5504,6 +5607,25 @@ u8 brcmf_map_prio_to_aci(void *config, u8 prio)
return prio;
}
static void brcmf_init_wmm_prio(u8 *priority)
{
/* Initialize AC priority array to default
* 802.1d priority as per following table:
* 802.1d prio 0,3 maps to BE
* 802.1d prio 1,2 maps to BK
* 802.1d prio 4,5 maps to VI
* 802.1d prio 6,7 maps to VO
*/
priority[0] = BRCMF_FWS_FIFO_AC_BE;
priority[3] = BRCMF_FWS_FIFO_AC_BE;
priority[1] = BRCMF_FWS_FIFO_AC_BK;
priority[2] = BRCMF_FWS_FIFO_AC_BK;
priority[4] = BRCMF_FWS_FIFO_AC_VI;
priority[5] = BRCMF_FWS_FIFO_AC_VI;
priority[6] = BRCMF_FWS_FIFO_AC_VO;
priority[7] = BRCMF_FWS_FIFO_AC_VO;
}
static void brcmf_wifi_prioritize_acparams(const
struct brcmf_cfg80211_edcf_acparam *acp, u8 *priority)
{
@ -5556,22 +5678,30 @@ static void brcmf_wifi_prioritize_acparams(const
* Use ACI prio to get the new priority value for
* each 802.1d traffic type, in this range.
*/
if (!(aci_prio[AC_BE] == aci_prio[AC_BK] &&
aci_prio[AC_BK] == aci_prio[AC_VI] &&
aci_prio[AC_VI] == aci_prio[AC_VO])) {
/* 802.1d 0,3 maps to BE */
priority[0] = aci_prio[AC_BE];
priority[3] = aci_prio[AC_BE];
/* 802.1d 0,3 maps to BE */
priority[0] = aci_prio[AC_BE];
priority[3] = aci_prio[AC_BE];
/* 802.1d 1,2 maps to BK */
priority[1] = aci_prio[AC_BK];
priority[2] = aci_prio[AC_BK];
/* 802.1d 1,2 maps to BK */
priority[1] = aci_prio[AC_BK];
priority[2] = aci_prio[AC_BK];
/* 802.1d 4,5 maps to VO */
priority[4] = aci_prio[AC_VI];
priority[5] = aci_prio[AC_VI];
/* 802.1d 4,5 maps to VO */
priority[4] = aci_prio[AC_VI];
priority[5] = aci_prio[AC_VI];
/* 802.1d 6,7 maps to VO */
priority[6] = aci_prio[AC_VO];
priority[7] = aci_prio[AC_VO];
/* 802.1d 6,7 maps to VO */
priority[6] = aci_prio[AC_VO];
priority[7] = aci_prio[AC_VO];
} else {
/* Initialize to default priority */
brcmf_init_wmm_prio(priority);
}
brcmf_dbg(CONN, "Adj prio BE 0->%d, BK 1->%d, BK 2->%d, BE 3->%d\n",
priority[0], priority[1], priority[2], priority[3]);
@ -5651,6 +5781,47 @@ static s32 brcmf_get_assoc_ies(struct brcmf_cfg80211_info *cfg,
return err;
}
static bool
brcmf_has_pmkid(const u8 *parse, u32 len)
{
const struct brcmf_tlv *rsn_ie;
const u8 *ie;
u32 ie_len;
u32 offset;
u16 count;
rsn_ie = brcmf_parse_tlvs(parse, len, WLAN_EID_RSN);
if (!rsn_ie)
goto done;
ie = (const u8 *)rsn_ie;
ie_len = rsn_ie->len + TLV_HDR_LEN;
/* Skip group data cipher suite */
offset = TLV_HDR_LEN + WPA_IE_VERSION_LEN + WPA_IE_MIN_OUI_LEN;
if (offset + WPA_IE_SUITE_COUNT_LEN >= ie_len)
goto done;
/* Skip pairwise cipher suite(s) */
count = ie[offset] + (ie[offset + 1] << 8);
offset += WPA_IE_SUITE_COUNT_LEN + (count * WPA_IE_MIN_OUI_LEN);
if (offset + WPA_IE_SUITE_COUNT_LEN >= ie_len)
goto done;
/* Skip auth key management suite(s) */
count = ie[offset] + (ie[offset + 1] << 8);
offset += WPA_IE_SUITE_COUNT_LEN + (count * WPA_IE_MIN_OUI_LEN);
if (offset + RSN_CAP_LEN >= ie_len)
goto done;
/* Skip rsn capabilities */
offset += RSN_CAP_LEN;
if (offset + RSN_PMKID_COUNT_LEN > ie_len)
goto done;
/* Extract PMKID count */
count = ie[offset] + (ie[offset + 1] << 8);
if (count)
return true;
done:
return false;
}
static s32
brcmf_bss_roaming_done(struct brcmf_cfg80211_info *cfg,
struct net_device *ndev,
@ -5711,6 +5882,11 @@ done:
roam_info.resp_ie = conn_info->resp_ie;
roam_info.resp_ie_len = conn_info->resp_ie_len;
if (profile->use_fwsup == BRCMF_PROFILE_FWSUP_1X &&
(brcmf_has_pmkid(roam_info.req_ie, roam_info.req_ie_len) ||
profile->is_ft))
roam_info.authorized = true;
cfg80211_roamed(ndev, &roam_info, GFP_KERNEL);
brcmf_dbg(CONN, "Report roaming result\n");
@ -5748,10 +5924,22 @@ brcmf_bss_connect_done(struct brcmf_cfg80211_info *cfg,
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;
if (profile->use_fwsup == BRCMF_PROFILE_FWSUP_1X &&
brcmf_has_pmkid(conn_params.req_ie, conn_params.req_ie_len))
conn_params.authorized = true;
cfg80211_connect_done(ndev, &conn_params, GFP_KERNEL);
brcmf_dbg(CONN, "Report connect result - connection %s\n",
completed ? "succeeded" : "failed");
}
if (test_and_clear_bit(BRCMF_VIF_STATUS_EAP_SUCCESS,
&ifp->vif->sme_state) &&
profile->use_fwsup == BRCMF_PROFILE_FWSUP_1X) {
cfg80211_port_authorized(ndev, profile->bssid, GFP_KERNEL);
brcmf_dbg(CONN, "Report port authorized\n");
}
brcmf_dbg(TRACE, "Exit\n");
return 0;
}
@ -5951,25 +6139,6 @@ static void brcmf_init_conf(struct brcmf_cfg80211_conf *conf)
conf->retry_long = (u32)-1;
}
static void brcmf_init_wmm_prio(u8 *priority)
{
/* Initialize AC priority array to default
* 802.1d priority as per following table:
* 802.1d prio 0,3 maps to BE
* 802.1d prio 1,2 maps to BK
* 802.1d prio 4,5 maps to VI
* 802.1d prio 6,7 maps to VO
*/
priority[0] = AC_BE;
priority[3] = AC_BE;
priority[1] = AC_BK;
priority[2] = AC_BK;
priority[4] = AC_VI;
priority[5] = AC_VI;
priority[6] = AC_VO;
priority[7] = AC_VO;
}
static void brcmf_register_event_handlers(struct brcmf_cfg80211_info *cfg)
{
brcmf_fweh_register(cfg->pub, BRCMF_E_LINK,
@ -6581,6 +6750,16 @@ brcmf_txrx_stypes[NUM_NL80211_IFTYPES] = {
.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)
}
};
@ -6602,6 +6781,9 @@ brcmf_txrx_stypes[NUM_NL80211_IFTYPES] = {
* #STA <= 1, #AP <= 1, channels = 1, 2 total
* #AP <= 4, matching BI, channels = 1, 4 total
*
* no p2p and rsdb:
* #STA <= 1, #AP <= 2, channels = 2, 3 total
*
* p2p, no mchan, and mbss:
*
* #STA <= 1, #P2P-DEV <= 1, #{P2P-CL, P2P-GO} <= 1, channels = 1, 3 total
@ -6613,6 +6795,16 @@ brcmf_txrx_stypes[NUM_NL80211_IFTYPES] = {
* #STA <= 1, #P2P-DEV <= 1, #{P2P-CL, P2P-GO} <= 1, channels = 2, 3 total
* #STA <= 1, #P2P-DEV <= 1, #AP <= 1, #P2P-CL <= 1, channels = 1, 4 total
* #AP <= 4, matching BI, channels = 1, 4 total
*
* p2p, rsdb, and no mbss:
* #STA <= 1, #P2P-DEV <= 1, #{P2P-CL, P2P-GO} <= 2, AP <= 2,
* channels = 2, 4 total
*
* p2p, rsdb, mbss
* #STA <= 1, #P2P-DEV <= 1, #{P2P-CL, P2P-GO} <= 2, AP <= 2,
* channels = 2, 4 total
* #AP <= 4, matching BI, channels = 1, 4 total
*
*/
static int brcmf_setup_ifmodes(struct wiphy *wiphy, struct brcmf_if *ifp)
{
@ -6620,13 +6812,14 @@ static int brcmf_setup_ifmodes(struct wiphy *wiphy, struct brcmf_if *ifp)
struct ieee80211_iface_limit *c0_limits = NULL;
struct ieee80211_iface_limit *p2p_limits = NULL;
struct ieee80211_iface_limit *mbss_limits = NULL;
bool mbss, p2p;
bool mbss, p2p, rsdb;
int i, c, n_combos;
mbss = brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MBSS);
p2p = brcmf_feat_is_enabled(ifp, BRCMF_FEAT_P2P);
rsdb = brcmf_feat_is_enabled(ifp, BRCMF_FEAT_RSDB);
n_combos = 1 + !!p2p + !!mbss;
n_combos = 1 + !!(p2p && !rsdb) + !!mbss;
combo = kcalloc(n_combos, sizeof(*combo), GFP_KERNEL);
if (!combo)
goto err;
@ -6637,16 +6830,36 @@ static int brcmf_setup_ifmodes(struct wiphy *wiphy, struct brcmf_if *ifp)
c = 0;
i = 0;
c0_limits = kcalloc(p2p ? 3 : 2, sizeof(*c0_limits), GFP_KERNEL);
if (p2p && rsdb)
c0_limits = kcalloc(4, sizeof(*c0_limits), GFP_KERNEL);
else if (p2p)
c0_limits = kcalloc(3, sizeof(*c0_limits), GFP_KERNEL);
else
c0_limits = kcalloc(2, sizeof(*c0_limits), GFP_KERNEL);
if (!c0_limits)
goto err;
c0_limits[i].max = 1;
c0_limits[i++].types = BIT(NL80211_IFTYPE_STATION);
if (p2p) {
if (p2p && rsdb) {
combo[c].num_different_channels = 2;
wiphy->interface_modes |= BIT(NL80211_IFTYPE_P2P_CLIENT) |
BIT(NL80211_IFTYPE_P2P_GO) |
BIT(NL80211_IFTYPE_P2P_DEVICE);
c0_limits[i].max = 1;
c0_limits[i++].types = BIT(NL80211_IFTYPE_STATION);
c0_limits[i].max = 1;
c0_limits[i++].types = BIT(NL80211_IFTYPE_P2P_DEVICE);
c0_limits[i].max = 2;
c0_limits[i++].types = BIT(NL80211_IFTYPE_P2P_CLIENT) |
BIT(NL80211_IFTYPE_P2P_GO);
c0_limits[i].max = 2;
c0_limits[i++].types = BIT(NL80211_IFTYPE_AP);
combo[c].max_interfaces = 4;
} else if (p2p) {
if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MCHAN))
combo[c].num_different_channels = 2;
else
combo[c].num_different_channels = 1;
c0_limits[i].max = 1;
c0_limits[i++].types = BIT(NL80211_IFTYPE_STATION);
wiphy->interface_modes |= BIT(NL80211_IFTYPE_P2P_CLIENT) |
BIT(NL80211_IFTYPE_P2P_GO) |
BIT(NL80211_IFTYPE_P2P_DEVICE);
@ -6655,16 +6868,26 @@ static int brcmf_setup_ifmodes(struct wiphy *wiphy, struct brcmf_if *ifp)
c0_limits[i].max = 1;
c0_limits[i++].types = BIT(NL80211_IFTYPE_P2P_CLIENT) |
BIT(NL80211_IFTYPE_P2P_GO);
combo[c].max_interfaces = i;
} else if (rsdb) {
combo[c].num_different_channels = 2;
c0_limits[i].max = 1;
c0_limits[i++].types = BIT(NL80211_IFTYPE_STATION);
c0_limits[i].max = 2;
c0_limits[i++].types = BIT(NL80211_IFTYPE_AP);
combo[c].max_interfaces = 3;
} else {
combo[c].num_different_channels = 1;
c0_limits[i].max = 1;
c0_limits[i++].types = BIT(NL80211_IFTYPE_STATION);
c0_limits[i].max = 1;
c0_limits[i++].types = BIT(NL80211_IFTYPE_AP);
combo[c].max_interfaces = i;
}
combo[c].max_interfaces = i;
combo[c].n_limits = i;
combo[c].limits = c0_limits;
if (p2p) {
if (p2p && !rsdb) {
c++;
i = 0;
p2p_limits = kcalloc(4, sizeof(*p2p_limits), GFP_KERNEL);
@ -6776,6 +6999,7 @@ static void brcmf_wiphy_wowl_params(struct wiphy *wiphy, struct brcmf_if *ifp)
static int brcmf_setup_wiphy(struct wiphy *wiphy, struct brcmf_if *ifp)
{
struct brcmf_pub *drvr = ifp->drvr;
struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
const struct ieee80211_iface_combination *combo;
struct ieee80211_supported_band *band;
u16 max_interfaces = 0;
@ -6833,6 +7057,9 @@ static int brcmf_setup_wiphy(struct wiphy *wiphy, struct brcmf_if *ifp)
NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_PSK);
wiphy_ext_feature_set(wiphy,
NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_1X);
if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_SAE))
wiphy_ext_feature_set(wiphy,
NL80211_EXT_FEATURE_SAE_OFFLOAD);
}
wiphy->mgmt_stypes = brcmf_txrx_stypes;
wiphy->max_remain_on_channel_duration = 5000;
@ -6843,6 +7070,10 @@ static int brcmf_setup_wiphy(struct wiphy *wiphy, struct brcmf_if *ifp)
/* vendor commands/events support */
wiphy->vendor_commands = brcmf_vendor_cmds;
wiphy->n_vendor_commands = BRCMF_VNDR_CMDS_LAST - 1;
wiphy->vendor_events = brcmf_vendor_events;
wiphy->n_vendor_events = BRCMF_VNDR_EVTS_LAST;
brcmf_fweh_register(cfg->pub, BRCMF_E_PHY_TEMP,
brcmf_wiphy_phy_temp_evt_handler);
brcmf_wiphy_wowl_params(wiphy, ifp);
err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BANDLIST, &bandlist,
@ -6916,7 +7147,7 @@ static s32 brcmf_config_dongle(struct brcmf_cfg80211_info *cfg)
brcmf_dongle_scantime(ifp);
power_mode = cfg->pwr_save ? PM_FAST : PM_OFF;
power_mode = cfg->pwr_save ? ifp->drvr->settings->default_pm : PM_OFF;
err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM, power_mode);
if (err)
goto default_conf_out;
@ -6941,6 +7172,13 @@ static s32 brcmf_config_dongle(struct brcmf_cfg80211_info *cfg)
brcmf_configure_arp_nd_offload(ifp, true);
if (ifp->drvr->settings->frameburst) {
err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_FAKEFRAG, 1);
if (err)
brcmf_info("setting frameburst mode failed\n");
brcmf_dbg(INFO, "frameburst mode enabled\n");
}
cfg->dongle_up = true;
default_conf_out:
@ -7222,6 +7460,7 @@ struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr,
cfg->wiphy = wiphy;
cfg->ops = ops;
cfg->pub = drvr;
cfg->pm_state = BRCMF_CFG80211_PM_STATE_RESUMED;
init_vif_event(&cfg->vif_event);
INIT_LIST_HEAD(&cfg->vif_list);

View File

@ -103,6 +103,8 @@
#define BRCMF_VIF_EVENT_TIMEOUT msecs_to_jiffies(1500)
#define BRCMF_PM_WAIT_MAXRETRY 100
/* cfg80211 wowlan definitions */
#define WL_WOWLAN_MAX_PATTERNS 8
#define WL_WOWLAN_MIN_PATTERN_LEN 1
@ -142,7 +144,8 @@ struct brcmf_cfg80211_security {
enum brcmf_profile_fwsup {
BRCMF_PROFILE_FWSUP_NONE,
BRCMF_PROFILE_FWSUP_PSK,
BRCMF_PROFILE_FWSUP_1X
BRCMF_PROFILE_FWSUP_1X,
BRCMF_PROFILE_FWSUP_SAE
};
/**
@ -157,6 +160,7 @@ struct brcmf_cfg80211_profile {
struct brcmf_cfg80211_security sec;
struct brcmf_wsec_key key[BRCMF_MAX_DEFAULT_KEYS];
enum brcmf_profile_fwsup use_fwsup;
bool is_ft;
};
/**
@ -180,25 +184,36 @@ enum brcmf_vif_status {
BRCMF_VIF_STATUS_ASSOC_SUCCESS,
};
enum brcmf_cfg80211_pm_state {
BRCMF_CFG80211_PM_STATE_RESUMED,
BRCMF_CFG80211_PM_STATE_RESUMING,
BRCMF_CFG80211_PM_STATE_SUSPENDED,
BRCMF_CFG80211_PM_STATE_SUSPENDING,
};
/**
* struct vif_saved_ie - holds saved IEs for a virtual interface.
*
* @probe_req_ie: IE info for probe request.
* @probe_res_ie: IE info for probe response.
* @beacon_ie: IE info for beacon frame.
* @assoc_res_ie: IE info for association response frame.
* @probe_req_ie_len: IE info length for probe request.
* @probe_res_ie_len: IE info length for probe response.
* @beacon_ie_len: IE info length for beacon frame.
* @assoc_res_ie_len: IE info length for association response frame.
*/
struct vif_saved_ie {
u8 probe_req_ie[IE_MAX_LEN];
u8 probe_res_ie[IE_MAX_LEN];
u8 beacon_ie[IE_MAX_LEN];
u8 assoc_req_ie[IE_MAX_LEN];
u8 assoc_res_ie[IE_MAX_LEN];
u32 probe_req_ie_len;
u32 probe_res_ie_len;
u32 beacon_ie_len;
u32 assoc_req_ie_len;
u32 assoc_res_ie_len;
};
/**
@ -366,6 +381,7 @@ struct brcmf_cfg80211_info {
struct brcmf_cfg80211_wowl wowl;
struct brcmf_pno_info *pno;
u8 ac_priority[MAX_8021D_PRIO];
u8 pm_state;
};
/**

View File

@ -224,6 +224,24 @@ struct sbsocramregs {
#define ARMCR4_BSZ_MASK 0x3f
#define ARMCR4_BSZ_MULT 8192
/* Minimum PMU resource mask for 43012C0 */
#define CY_43012_PMU_MIN_RES_MASK 0xF8BFE77
/* PMU STATUS mask for 43012C0 */
#define CY_43012_PMU_STATUS_MASK 0x1AC
/* PMU CONTROL EXT mask for 43012C0 */
#define CY_43012_PMU_CONTROL_EXT_MASK 0x11
/* PMU Watchdog Counter Tick value for 43012C0 */
#define CY_43012_PMU_WATCHDOG_TICK_VAL 0x04
/* PMU Watchdog Counter Tick value for 4373 */
#define CY_4373_PMU_WATCHDOG_TICK_VAL 0x04
/* Minimum PMU resource mask for 4373 */
#define CY_4373_PMU_MIN_RES_MASK 0xFCAFF7F
struct brcmf_core_priv {
struct brcmf_core pub;
u32 wrapbase;
@ -444,11 +462,25 @@ static void brcmf_chip_ai_resetcore(struct brcmf_core_priv *core, u32 prereset,
{
struct brcmf_chip_priv *ci;
int count;
struct brcmf_core *d11core2 = NULL;
struct brcmf_core_priv *d11priv2 = NULL;
ci = core->chip;
/* special handle two D11 cores reset */
if (core->pub.id == BCMA_CORE_80211) {
d11core2 = brcmf_chip_get_d11core(&ci->pub, 1);
if (d11core2) {
brcmf_dbg(INFO, "found two d11 cores, reset both\n");
d11priv2 = container_of(d11core2, struct brcmf_core_priv,
pub);
}
}
/* must disable first to work for arbitrary current core state */
brcmf_chip_ai_coredisable(core, prereset, reset);
if (d11priv2)
brcmf_chip_ai_coredisable(d11priv2, prereset, reset);
count = 0;
while (ci->ops->read32(ci->ctx, core->wrapbase + BCMA_RESET_CTL) &
@ -460,9 +492,30 @@ static void brcmf_chip_ai_resetcore(struct brcmf_core_priv *core, u32 prereset,
usleep_range(40, 60);
}
if (d11priv2) {
count = 0;
while (ci->ops->read32(ci->ctx,
d11priv2->wrapbase + BCMA_RESET_CTL) &
BCMA_RESET_CTL_RESET) {
ci->ops->write32(ci->ctx,
d11priv2->wrapbase + BCMA_RESET_CTL,
0);
count++;
if (count > 50)
break;
usleep_range(40, 60);
}
}
ci->ops->write32(ci->ctx, core->wrapbase + BCMA_IOCTL,
postreset | BCMA_IOCTL_CLK);
ci->ops->read32(ci->ctx, core->wrapbase + BCMA_IOCTL);
if (d11priv2) {
ci->ops->write32(ci->ctx, d11priv2->wrapbase + BCMA_IOCTL,
postreset | BCMA_IOCTL_CLK);
ci->ops->read32(ci->ctx, d11priv2->wrapbase + BCMA_IOCTL);
}
}
static char *brcmf_chip_name(uint chipid, char *buf, uint len)
@ -785,7 +838,7 @@ static int brcmf_chip_dmp_get_regaddr(struct brcmf_chip_priv *ci, u32 *eromaddr,
u32 *regbase, u32 *wrapbase)
{
u8 desc;
u32 val;
u32 val, szdesc;
u8 mpnum = 0;
u8 stype, sztype, wraptype;
@ -831,14 +884,15 @@ static int brcmf_chip_dmp_get_regaddr(struct brcmf_chip_priv *ci, u32 *eromaddr,
/* next size descriptor can be skipped */
if (sztype == DMP_SLAVE_SIZE_DESC) {
val = brcmf_chip_dmp_get_desc(ci, eromaddr, NULL);
szdesc = brcmf_chip_dmp_get_desc(ci, eromaddr, NULL);
/* skip upper size descriptor if present */
if (val & DMP_DESC_ADDRSIZE_GT32)
if (szdesc & DMP_DESC_ADDRSIZE_GT32)
brcmf_chip_dmp_get_desc(ci, eromaddr, NULL);
}
/* only look for 4K register regions */
if (sztype != DMP_SLAVE_SIZE_4K)
/* look for 4K or 8K register regions */
if (sztype != DMP_SLAVE_SIZE_4K &&
sztype != DMP_SLAVE_SIZE_8K)
continue;
stype = (val & DMP_SLAVE_TYPE) >> DMP_SLAVE_TYPE_S;
@ -895,7 +949,8 @@ int brcmf_chip_dmp_erom_scan(struct brcmf_chip_priv *ci)
/* need core with ports */
if (nmw + nsw == 0 &&
id != BCMA_CORE_PMU)
id != BCMA_CORE_PMU &&
id != BCMA_CORE_GCI)
continue;
/* try to obtain register address info */
@ -1119,6 +1174,21 @@ void brcmf_chip_detach(struct brcmf_chip *pub)
kfree(chip);
}
struct brcmf_core *brcmf_chip_get_d11core(struct brcmf_chip *pub, u8 unit)
{
struct brcmf_chip_priv *chip;
struct brcmf_core_priv *core;
chip = container_of(pub, struct brcmf_chip_priv, pub);
list_for_each_entry(core, &chip->cores, list) {
if (core->pub.id == BCMA_CORE_80211) {
if (unit-- == 0)
return &core->pub;
}
}
return NULL;
}
struct brcmf_core *brcmf_chip_get_core(struct brcmf_chip *pub, u16 coreid)
{
struct brcmf_chip_priv *chip;
@ -1161,6 +1231,14 @@ struct brcmf_core *brcmf_chip_get_pmu(struct brcmf_chip *pub)
return cc;
}
struct brcmf_core *brcmf_chip_get_gci(struct brcmf_chip *pub)
{
struct brcmf_core *gci;
gci = brcmf_chip_get_core(pub, BCMA_CORE_GCI);
return gci;
}
bool brcmf_chip_iscoreup(struct brcmf_core *pub)
{
struct brcmf_core_priv *core;
@ -1368,6 +1446,7 @@ bool brcmf_chip_sr_capable(struct brcmf_chip *pub)
addr = CORE_CC_REG(base, sr_control0);
reg = chip->ops->read32(chip->ctx, addr);
return (reg & sr_eng_en) != 0;
case BRCM_CC_4359_CHIP_ID:
case CY_CC_43012_CHIP_ID:
addr = CORE_CC_REG(pmu->base, retention_ctl);
reg = chip->ops->read32(chip->ctx, addr);
@ -1385,3 +1464,151 @@ bool brcmf_chip_sr_capable(struct brcmf_chip *pub)
PMU_RCTL_LOGIC_DISABLE_MASK)) == 0;
}
}
void brcmf_chip_reset_pmu_regs(struct brcmf_chip *pub)
{
struct brcmf_chip_priv *chip;
u32 addr;
u32 base;
brcmf_dbg(TRACE, "Enter\n");
chip = container_of(pub, struct brcmf_chip_priv, pub);
base = brcmf_chip_get_pmu(pub)->base;
switch (pub->chip) {
case CY_CC_43012_CHIP_ID:
/* SW scratch */
addr = CORE_CC_REG(base, swscratch);
chip->ops->write32(chip->ctx, addr, 0);
/* PMU status */
addr = CORE_CC_REG(base, pmustatus);
chip->ops->write32(chip->ctx, addr,
CY_43012_PMU_STATUS_MASK);
/* PMU control ext */
addr = CORE_CC_REG(base, pmucontrol_ext);
chip->ops->write32(chip->ctx, addr,
CY_43012_PMU_CONTROL_EXT_MASK);
break;
default:
brcmf_err("Unsupported chip id\n");
break;
}
}
void brcmf_chip_set_default_min_res_mask(struct brcmf_chip *pub)
{
struct brcmf_chip_priv *chip;
u32 addr;
u32 base;
brcmf_dbg(TRACE, "Enter\n");
chip = container_of(pub, struct brcmf_chip_priv, pub);
base = brcmf_chip_get_pmu(pub)->base;
switch (pub->chip) {
case CY_CC_43012_CHIP_ID:
addr = CORE_CC_REG(base, min_res_mask);
chip->ops->write32(chip->ctx, addr,
CY_43012_PMU_MIN_RES_MASK);
break;
default:
brcmf_err("Unsupported chip id\n");
break;
}
}
void brcmf_chip_ulp_reset_lhl_regs(struct brcmf_chip *pub)
{
struct brcmf_chip_priv *chip;
u32 base;
u32 addr;
brcmf_dbg(TRACE, "Enter\n");
chip = container_of(pub, struct brcmf_chip_priv, pub);
base = brcmf_chip_get_gci(pub)->base;
/* LHL Top Level Power Sequence Control */
addr = CORE_GCI_REG(base, lhl_top_pwrseq_ctl_adr);
chip->ops->write32(chip->ctx, addr, 0);
/* GPIO Interrupt Enable0 */
addr = CORE_GCI_REG(base, gpio_int_en_port_adr[0]);
chip->ops->write32(chip->ctx, addr, 0);
/* GPIO Interrupt Status0 */
addr = CORE_GCI_REG(base, gpio_int_st_port_adr[0]);
chip->ops->write32(chip->ctx, addr, ~0);
/* WL ARM Timer0 Interrupt Mask */
addr = CORE_GCI_REG(base, lhl_wl_armtim0_intrp_adr);
chip->ops->write32(chip->ctx, addr, 0);
/* WL ARM Timer0 Interrupt Status */
addr = CORE_GCI_REG(base, lhl_wl_armtim0_st_adr);
chip->ops->write32(chip->ctx, addr, ~0);
/* WL ARM Timer */
addr = CORE_GCI_REG(base, lhl_wl_armtim0_adr);
chip->ops->write32(chip->ctx, addr, 0);
/* WL MAC Timer0 Interrupt Mask */
addr = CORE_GCI_REG(base, lhl_wl_mactim0_intrp_adr);
chip->ops->write32(chip->ctx, addr, 0);
/* WL MAC Timer0 Interrupt Status */
addr = CORE_GCI_REG(base, lhl_wl_mactim0_st_adr);
chip->ops->write32(chip->ctx, addr, ~0);
/* WL MAC TimerInt0 */
addr = CORE_GCI_REG(base, lhl_wl_mactim_int0_adr);
chip->ops->write32(chip->ctx, addr, 0x0);
}
void brcmf_chip_reset_watchdog(struct brcmf_chip *pub)
{
struct brcmf_chip_priv *chip;
u32 base;
u32 addr;
brcmf_dbg(TRACE, "Enter\n");
chip = container_of(pub, struct brcmf_chip_priv, pub);
base = brcmf_chip_get_pmu(pub)->base;
switch (pub->chip) {
case CY_CC_43012_CHIP_ID:
addr = CORE_CC_REG(base, min_res_mask);
chip->ops->write32(chip->ctx, addr,
CY_43012_PMU_MIN_RES_MASK);
/* Watchdog res mask */
addr = CORE_CC_REG(base, watchdog_res_mask);
chip->ops->write32(chip->ctx, addr,
CY_43012_PMU_MIN_RES_MASK);
/* PMU watchdog */
addr = CORE_CC_REG(base, pmuwatchdog);
chip->ops->write32(chip->ctx, addr,
CY_43012_PMU_WATCHDOG_TICK_VAL);
break;
case CY_CC_4373_CHIP_ID:
addr = CORE_CC_REG(base, min_res_mask);
chip->ops->write32(chip->ctx, addr,
CY_4373_PMU_MIN_RES_MASK);
addr = CORE_CC_REG(base, watchdog_res_mask);
chip->ops->write32(chip->ctx, addr,
CY_4373_PMU_MIN_RES_MASK);
addr = CORE_CC_REG(base, pmuwatchdog);
chip->ops->write32(chip->ctx, addr,
CY_4373_PMU_WATCHDOG_TICK_VAL);
mdelay(100);
break;
default:
break;
}
}

View File

@ -21,6 +21,9 @@
#define CORE_CC_REG(base, field) \
(base + offsetof(struct chipcregs, field))
#define CORE_GCI_REG(base, field) \
(base + offsetof(struct chipgciregs, field))
/**
* struct brcmf_chip - chip level information.
*
@ -93,5 +96,10 @@ void brcmf_chip_resetcore(struct brcmf_core *core, u32 prereset, u32 reset,
void brcmf_chip_set_passive(struct brcmf_chip *ci);
bool brcmf_chip_set_active(struct brcmf_chip *ci, u32 rstvec);
bool brcmf_chip_sr_capable(struct brcmf_chip *pub);
void brcmf_chip_reset_watchdog(struct brcmf_chip *pub);
void brcmf_chip_ulp_reset_lhl_regs(struct brcmf_chip *pub);
void brcmf_chip_reset_pmu_regs(struct brcmf_chip *pub);
void brcmf_chip_set_default_min_res_mask(struct brcmf_chip *pub);
struct brcmf_core *brcmf_chip_get_d11core(struct brcmf_chip *pub, u8 unit);
#endif /* BRCMF_AXIDMP_H */

View File

@ -30,6 +30,9 @@
#include "common.h"
#include "of.h"
#include "firmware.h"
#include "fweh.h"
#include <brcm_hw_ids.h>
#include "defs.h"
MODULE_AUTHOR("Broadcom Corporation");
MODULE_DESCRIPTION("Broadcom 802.11 wireless LAN fullmac driver.");
@ -50,7 +53,8 @@ module_param_named(txglomsz, brcmf_sdiod_txglomsz, int, 0);
MODULE_PARM_DESC(txglomsz, "Maximum tx packet chain size [SDIO]");
/* Debug level configuration. See debug.h for bits, sysfs modifiable */
int brcmf_msg_level = 0x0fffffff;
int brcmf_msg_level = BRCMF_DATA_VAL | BRCMF_CTL_VAL | BRCMF_HDRS_VAL | BRCMF_BYTES_VAL | BRCMF_GLOM_VAL | BRCMF_EVENT_VAL | BRCMF_FIL_VAL | BRCMF_FWCON_VAL | BRCMF_SCAN_VAL;
module_param_named(debug, brcmf_msg_level, int, S_IRUSR | S_IWUSR);
MODULE_PARM_DESC(debug, "Level of debug output");
@ -83,6 +87,14 @@ static int brcmf_sdio_wq_highpri;
module_param_named(sdio_wq_highpri, brcmf_sdio_wq_highpri, int, 0);
MODULE_PARM_DESC(sdio_wq_highpri, "SDIO workqueue is set to high priority");
static int brcmf_frameburst;
module_param_named(frameburst, brcmf_frameburst, int, 0);
MODULE_PARM_DESC(frameburst, "Enable firmware frameburst feature");
static int brcmf_max_pm;
module_param_named(max_pm, brcmf_max_pm, int, 0);
MODULE_PARM_DESC(max_pm, "Use max power management mode by default");
#ifdef DEBUG
/* always succeed brcmf_bus_started() */
static int brcmf_ignore_probe_fail;
@ -247,6 +259,9 @@ int brcmf_c_preinit_dcmds(struct brcmf_if *ifp)
char *clmver;
char *ptr;
s32 err;
struct eventmsgs_ext *eventmask_msg = NULL;
u8 msglen;
struct brcmf_bus *bus = ifp->drvr->bus_if;
/* retrieve mac addresses */
err = brcmf_fil_iovar_data_get(ifp, "cur_etheraddr", ifp->mac_addr,
@ -353,6 +368,41 @@ int brcmf_c_preinit_dcmds(struct brcmf_if *ifp)
goto done;
}
/* Enable event_msg_ext specific to 43012 chip */
if (bus->chip == CY_CC_43012_CHIP_ID) {
/* Program event_msg_ext to support event larger than 128 */
msglen = (roundup(BRCMF_E_LAST, NBBY) / NBBY) +
EVENTMSGS_EXT_STRUCT_SIZE;
/* Allocate buffer for eventmask_msg */
eventmask_msg = kzalloc(msglen, GFP_KERNEL);
if (!eventmask_msg) {
err = -ENOMEM;
goto done;
}
/* Read the current programmed event_msgs_ext */
eventmask_msg->ver = EVENTMSGS_VER;
eventmask_msg->len = roundup(BRCMF_E_LAST, NBBY) / NBBY;
err = brcmf_fil_iovar_data_get(ifp, "event_msgs_ext",
eventmask_msg,
msglen);
/* Enable ULP event */
brcmf_dbg(EVENT, "enable event ULP\n");
setbit(eventmask_msg->mask, BRCMF_E_ULP);
/* Write updated Event mask */
eventmask_msg->ver = EVENTMSGS_VER;
eventmask_msg->command = EVENTMSGS_SET_MASK;
eventmask_msg->len = (roundup(BRCMF_E_LAST, NBBY) / NBBY);
err = brcmf_fil_iovar_data_set(ifp, "event_msgs_ext",
eventmask_msg, msglen);
if (err) {
brcmf_err("Set event_msgs_ext error (%d)\n", err);
goto done;
}
}
/* Setup default scan channel time */
err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_SCAN_CHANNEL_TIME,
BRCMF_DEFAULT_SCAN_CHANNEL_TIME);
@ -457,6 +507,8 @@ struct brcmf_mp_device *brcmf_get_module_param(struct device *dev,
settings->roamoff = !!brcmf_roamoff;
settings->eap_restrict = !!brcmf_eap_restrict;
settings->sdio_wq_highpri = !!brcmf_sdio_wq_highpri;
settings->frameburst = !!brcmf_frameburst;
settings->default_pm = !!brcmf_max_pm ? PM_MAX : PM_FAST;
#ifdef DEBUG
settings->ignore_probe_fail = !!brcmf_ignore_probe_fail;
#endif

View File

@ -51,6 +51,8 @@ extern struct brcmf_mp_global_t brcmf_mp_global;
* @roamoff: Firmware roaming off?
* @eap_restrict: Not allow data tx/rx until 802.1X auth succeeds
* @sdio_wq_highpri: Tasks submitted to SDIO workqueue will run immediately.
* @frameburst: Firmware frame burst mode.
* @default_pm: default power management (PM) mode.
* @ignore_probe_fail: Ignore probe failure.
* @country_codes: If available, pointer to struct for translating country codes
* @bus: Bus specific platform data. Only SDIO at the mmoment.
@ -63,6 +65,8 @@ struct brcmf_mp_device {
bool eap_restrict;
int sdio_dpc_prio;
bool sdio_wq_highpri;
bool frameburst;
int default_pm;
bool ignore_probe_fail;
struct brcmfmac_pd_cc *country_codes;
union {

View File

@ -344,7 +344,8 @@ void brcmf_rx_frame(struct device *dev, struct sk_buff *skb, bool handle_event)
} else {
/* Process special event packets */
if (handle_event)
brcmf_fweh_process_skb(ifp->drvr, skb);
brcmf_fweh_process_skb(ifp->drvr, skb,
BCMILCP_SUBTYPE_VENDOR_LONG);
brcmf_netif_rx(ifp, skb);
}
@ -361,7 +362,7 @@ void brcmf_rx_event(struct device *dev, struct sk_buff *skb)
if (brcmf_rx_hdrpull(drvr, skb, &ifp))
return;
brcmf_fweh_process_skb(ifp->drvr, skb);
brcmf_fweh_process_skb(ifp->drvr, skb, 0);
brcmu_pkt_buf_free_skb(skb);
}
@ -370,6 +371,11 @@ void brcmf_txfinalize(struct brcmf_if *ifp, struct sk_buff *txp, bool success)
struct ethhdr *eh;
u16 type;
if (!ifp) {
brcmu_pkt_buf_free_skb(txp);
return;
}
eh = (struct ethhdr *)(txp->data);
type = ntohs(eh->h_proto);
@ -923,6 +929,14 @@ fail:
return ret;
}
int brcmf_fwlog_attach(struct device *dev)
{
struct brcmf_bus *bus_if = dev_get_drvdata(dev);
struct brcmf_pub *drvr = bus_if->drvr;
return brcmf_debug_fwlog_init(drvr);
}
static int brcmf_revinfo_read(struct seq_file *s, void *data)
{
struct brcmf_bus *bus_if = dev_get_drvdata(s->private);
@ -1132,8 +1146,10 @@ int brcmf_netdev_wait_pend8021x(struct brcmf_if *ifp)
!brcmf_get_pend_8021x_cnt(ifp),
MAX_WAIT_FOR_8021X_TX);
if (!err)
if (!err) {
brcmf_err("Timed out waiting for no pending 802.1x packets\n");
atomic_set(&ifp->pend_8021x_cnt, 0);
}
return !err;
}
@ -1338,6 +1354,7 @@ brcmf_pktfilter_add_remove(struct net_device *ndev, int filter_num, bool add)
drvr->pkt_filter[filter_num].id = 0;
drvr->pkt_filter[filter_num].enable = 0;
}
failed:
if (ret)

View File

@ -27,6 +27,82 @@
static struct dentry *root_folder;
static int
brcmf_debug_msgtrace_seqchk(u32 *prev, u32 cur)
{
if ((cur == 0 && *prev == 0xFFFFFFFF) || ((cur - *prev) == 1)) {
goto done;
} else if (cur == *prev) {
brcmf_dbg(FWCON, "duplicate trace\n");
return -1;
} else if (cur > *prev) {
brcmf_dbg(FWCON, "lost %d packets\n", cur - *prev);
} else {
brcmf_dbg(FWCON, "seq out of order, host %d, dongle %d\n",
*prev, cur);
}
done:
*prev = cur;
return 0;
}
static int
brcmf_debug_msg_parser(void *event_data)
{
int err = 0;
struct msgtrace_hdr *hdr;
char *data, *s;
static u32 seqnum_prev;
hdr = (struct msgtrace_hdr *)event_data;
data = (char *)event_data + MSGTRACE_HDRLEN;
/* There are 2 bytes available at the end of data */
data[ntohs(hdr->len)] = '\0';
if (ntohl(hdr->discarded_bytes) || ntohl(hdr->discarded_printf)) {
brcmf_dbg(FWCON, "Discarded_bytes %d discarded_printf %d\n",
ntohl(hdr->discarded_bytes),
ntohl(hdr->discarded_printf));
}
err = brcmf_debug_msgtrace_seqchk(&seqnum_prev, ntohl(hdr->seqnum));
if (err)
return err;
while (*data != '\0' && (s = strstr(data, "\n")) != NULL) {
*s = '\0';
brcmf_dbg(FWCON, "[FWLOG] %s\n", data);
data = s + 1;
}
if (*data)
brcmf_dbg(FWCON, "[FWLOG] %s", data);
return err;
}
static int
brcmf_debug_trace_parser(struct brcmf_if *ifp,
const struct brcmf_event_msg *evtmsg,
void *event_data)
{
int err = 0;
struct msgtrace_hdr *hdr;
hdr = (struct msgtrace_hdr *)event_data;
if (hdr->version != MSGTRACE_VERSION) {
brcmf_dbg(FWCON, "trace version mismatch host %d dngl %d\n",
MSGTRACE_VERSION, hdr->version);
err = -EPROTO;
return err;
}
if (hdr->trace_type == MSGTRACE_HDR_TYPE_MSG)
err = brcmf_debug_msg_parser(event_data);
return err;
}
int brcmf_debug_create_memdump(struct brcmf_bus *bus, const void *data,
size_t len)
{
@ -42,7 +118,8 @@ int brcmf_debug_create_memdump(struct brcmf_bus *bus, const void *data,
if (!dump)
return -ENOMEM;
memcpy(dump, data, len);
if (data && len > 0)
memcpy(dump, data, len);
err = brcmf_bus_get_memdump(bus, dump + len, ramsize);
if (err) {
vfree(dump);
@ -81,6 +158,12 @@ int brcmf_debug_attach(struct brcmf_pub *drvr)
return PTR_ERR_OR_ZERO(drvr->dbgfs_dir);
}
int brcmf_debug_fwlog_init(struct brcmf_pub *drvr)
{
return brcmf_fweh_register(drvr, BRCMF_E_TRACE,
brcmf_debug_trace_parser);
}
void brcmf_debug_detach(struct brcmf_pub *drvr)
{
brcmf_fweh_unregister(drvr, BRCMF_E_PSM_WATCHDOG);

View File

@ -17,13 +17,6 @@
#ifndef BRCMFMAC_DEBUG_H
#define BRCMFMAC_DEBUG_H
/* SBA HACK !! */
#define CONFIG_BRCMDBG
#define DEBUG
#include <linux/net.h> /* net_ratelimit() */
/* message levels */
@ -47,6 +40,7 @@
#define BRCMF_MSGBUF_VAL 0x00040000
#define BRCMF_PCIE_VAL 0x00080000
#define BRCMF_FWCON_VAL 0x00100000
#define BRCMF_ULP_VAL 0x00200000
/* set default print format */
#undef pr_fmt
@ -108,6 +102,10 @@ do { \
#endif /* defined(DEBUG) || defined(CONFIG_BRCM_TRACING) */
#define MSGTRACE_VERSION 1
#define MSGTRACE_HDR_TYPE_MSG 0
#define MSGTRACE_HDR_TYPE_LOG 1
#define brcmf_dbg_hex_dump(test, data, len, fmt, ...) \
do { \
trace_brcmf_hexdump((void *)data, len); \
@ -129,6 +127,7 @@ int brcmf_debugfs_add_entry(struct brcmf_pub *drvr, const char *fn,
int (*read_fn)(struct seq_file *seq, void *data));
int brcmf_debug_create_memdump(struct brcmf_bus *bus, const void *data,
size_t len);
int brcmf_debug_fwlog_init(struct brcmf_pub *drvr);
#else
static inline void brcmf_debugfs_init(void)
{
@ -155,6 +154,25 @@ int brcmf_debug_create_memdump(struct brcmf_bus *bus, const void *data,
{
return 0;
}
static inline
int brcmf_debug_fwlog_init(struct brcmf_pub *drvr)
{
return 0;
}
#endif
/* Message trace header */
struct msgtrace_hdr {
u8 version;
u8 trace_type;
u16 len; /* Len of the trace */
u32 seqnum; /* Sequence number of message */
/* Number of discarded bytes because of trace overflow */
u32 discarded_bytes;
/* Number of discarded printf because of trace overflow */
u32 discarded_printf;
};
#define MSGTRACE_HDRLEN sizeof(struct msgtrace_hdr)
#endif /* BRCMFMAC_DEBUG_H */

View File

@ -48,6 +48,7 @@ static const struct brcmf_feat_fwcap brcmf_fwcap_map[] = {
{ BRCMF_FEAT_MBSS, "mbss" },
{ BRCMF_FEAT_MCHAN, "mchan" },
{ BRCMF_FEAT_P2P, "p2p" },
{ BRCMF_FEAT_SAE, "sae" },
};
#ifdef DEBUG

View File

@ -33,6 +33,7 @@
* MFP: 802.11w Management Frame Protection.
* GSCAN: enhanced scan offload feature.
* FWSUP: Firmware supplicant.
* SAE: simultaneous authentication of equals
*/
#define BRCMF_FEAT_LIST \
BRCMF_FEAT_DEF(MBSS) \
@ -48,7 +49,8 @@
BRCMF_FEAT_DEF(WOWL_ARP_ND) \
BRCMF_FEAT_DEF(MFP) \
BRCMF_FEAT_DEF(GSCAN) \
BRCMF_FEAT_DEF(FWSUP)
BRCMF_FEAT_DEF(FWSUP) \
BRCMF_FEAT_DEF(SAE)
/*
* Quirks:

View File

@ -101,7 +101,9 @@ struct brcmf_cfg80211_info;
BRCMF_ENUM_DEF(FIFO_CREDIT_MAP, 74) \
BRCMF_ENUM_DEF(ACTION_FRAME_RX, 75) \
BRCMF_ENUM_DEF(TDLS_PEER_EVENT, 92) \
BRCMF_ENUM_DEF(BCMC_CREDIT_SUPPORT, 127)
BRCMF_ENUM_DEF(PHY_TEMP, 111) \
BRCMF_ENUM_DEF(BCMC_CREDIT_SUPPORT, 127) \
BRCMF_ENUM_DEF(ULP, 146)
#define BRCMF_ENUM_DEF(id, val) \
BRCMF_E_##id = (val),
@ -113,7 +115,7 @@ enum brcmf_fweh_event_code {
* minimum length check in device firmware so it is
* hard-coded here.
*/
BRCMF_E_LAST = 139
BRCMF_E_LAST = 147
};
#undef BRCMF_ENUM_DEF
@ -211,7 +213,7 @@ enum brcmf_fweh_event_code {
*/
#define BRCM_OUI "\x00\x10\x18"
#define BCMILCP_BCM_SUBTYPE_EVENT 1
#define BCMILCP_SUBTYPE_VENDOR_LONG 32769
/**
* struct brcm_ethhdr - broadcom specific ether header.
@ -294,6 +296,28 @@ struct brcmf_if_event {
u8 role;
};
enum event_msgs_ext_command {
EVENTMSGS_NONE = 0,
EVENTMSGS_SET_BIT = 1,
EVENTMSGS_RESET_BIT = 2,
EVENTMSGS_SET_MASK = 3
};
#define EVENTMSGS_VER 1
#define EVENTMSGS_EXT_STRUCT_SIZE offsetof(struct eventmsgs_ext, mask[0])
/* len- for SET it would be mask size from the application to the firmware */
/* for GET it would be actual firmware mask size */
/* maxgetsize - is only used for GET. indicate max mask size that the */
/* application can read from the firmware */
struct eventmsgs_ext {
u8 ver;
u8 command;
u8 len;
u8 maxgetsize;
u8 mask[1];
};
typedef int (*brcmf_fweh_handler_t)(struct brcmf_if *ifp,
const struct brcmf_event_msg *evtmsg,
void *data);
@ -334,10 +358,10 @@ void brcmf_fweh_process_event(struct brcmf_pub *drvr,
void brcmf_fweh_p2pdev_setup(struct brcmf_if *ifp, bool ongoing);
static inline void brcmf_fweh_process_skb(struct brcmf_pub *drvr,
struct sk_buff *skb)
struct sk_buff *skb, u16 stype)
{
struct brcmf_event *event_packet;
u16 usr_stype;
u16 subtype, usr_stype;
/* only process events when protocol matches */
if (skb->protocol != cpu_to_be16(ETH_P_LINK_CTL))
@ -346,8 +370,16 @@ static inline void brcmf_fweh_process_skb(struct brcmf_pub *drvr,
if ((skb->len + ETH_HLEN) < sizeof(*event_packet))
return;
/* check for BRCM oui match */
event_packet = (struct brcmf_event *)skb_mac_header(skb);
/* check subtype if needed */
if (unlikely(stype)) {
subtype = get_unaligned_be16(&event_packet->hdr.subtype);
if (subtype != stype)
return;
}
/* check for BRCM oui match */
if (memcmp(BRCM_OUI, &event_packet->hdr.oui[0],
sizeof(event_packet->hdr.oui)))
return;

View File

@ -80,6 +80,7 @@
#define BRCMF_C_SCB_DEAUTHENTICATE_FOR_REASON 201
#define BRCMF_C_SET_ASSOC_PREFER 205
#define BRCMF_C_GET_VALID_CHANNELS 217
#define BRCMF_C_SET_FAKEFRAG 219
#define BRCMF_C_GET_KEY_PRIMARY 235
#define BRCMF_C_SET_KEY_PRIMARY 236
#define BRCMF_C_SET_SCAN_PASSIVE_TIME 258

View File

@ -30,7 +30,7 @@
#define BRCMF_ARP_OL_PEER_AUTO_REPLY 0x00000008
#define BRCMF_BSS_INFO_VERSION 109 /* curr ver of brcmf_bss_info_le struct */
#define BRCMF_BSS_RSSI_ON_CHANNEL 0x0002
#define BRCMF_BSS_RSSI_ON_CHANNEL 0x0004
#define BRCMF_STA_WME 0x00000002 /* WMM association */
#define BRCMF_STA_AUTHE 0x00000008 /* Authenticated */
@ -53,6 +53,8 @@
#define BRCMF_WSEC_MAX_PSK_LEN 32
#define BRCMF_WSEC_PASSPHRASE BIT(0)
#define BRCMF_WSEC_MAX_SAE_PASSWORD_LEN 128
/* primary (ie tx) key */
#define BRCMF_PRIMARY_KEY (1 << 1)
#define DOT11_BSSTYPE_ANY 2
@ -125,7 +127,7 @@
/* Link Down indication in WoWL mode: */
#define BRCMF_WOWL_LINKDOWN (1 << 31)
#define BRCMF_WOWL_MAXPATTERNS 8
#define BRCMF_WOWL_MAXPATTERNS 16
#define BRCMF_WOWL_MAXPATTERNSIZE 128
enum {
@ -519,6 +521,17 @@ struct brcmf_wsec_pmk_le {
u8 key[2 * BRCMF_WSEC_MAX_PSK_LEN + 1];
};
/**
* struct brcmf_wsec_sae_pwd_le - firmware SAE password material.
*
* @key_len: number of octets in key materials.
* @key: SAE password material.
*/
struct brcmf_wsec_sae_pwd_le {
__le16 key_len;
u8 key[BRCMF_WSEC_MAX_SAE_PASSWORD_LEN];
};
/* Used to get specific STA parameters */
struct brcmf_scb_val_le {
__le32 val;

View File

@ -322,28 +322,6 @@ struct brcmf_skbuff_cb {
/* How long to defer borrowing in jiffies */
#define BRCMF_FWS_BORROW_DEFER_PERIOD (HZ / 10)
/**
* enum brcmf_fws_fifo - fifo indices used by dongle firmware.
*
* @BRCMF_FWS_FIFO_FIRST: first fifo, ie. background.
* @BRCMF_FWS_FIFO_AC_BK: fifo for background traffic.
* @BRCMF_FWS_FIFO_AC_BE: fifo for best-effort traffic.
* @BRCMF_FWS_FIFO_AC_VI: fifo for video traffic.
* @BRCMF_FWS_FIFO_AC_VO: fifo for voice traffic.
* @BRCMF_FWS_FIFO_BCMC: fifo for broadcast/multicast (AP only).
* @BRCMF_FWS_FIFO_ATIM: fifo for ATIM (AP only).
* @BRCMF_FWS_FIFO_COUNT: number of fifos.
*/
enum brcmf_fws_fifo {
BRCMF_FWS_FIFO_FIRST,
BRCMF_FWS_FIFO_AC_BK = BRCMF_FWS_FIFO_FIRST,
BRCMF_FWS_FIFO_AC_BE,
BRCMF_FWS_FIFO_AC_VI,
BRCMF_FWS_FIFO_AC_VO,
BRCMF_FWS_FIFO_BCMC,
BRCMF_FWS_FIFO_ATIM,
BRCMF_FWS_FIFO_COUNT
};
/**
* enum brcmf_fws_txstatus - txstatus flag values.
@ -416,6 +394,7 @@ struct brcmf_fws_mac_descriptor {
};
#define BRCMF_FWS_HANGER_MAXITEMS 3072
#define BRCMF_BORROW_RATIO 3
/**
* enum brcmf_fws_hanger_item_state - state of hanger item.
@ -512,7 +491,8 @@ struct brcmf_fws_info {
u32 fifo_enqpkt[BRCMF_FWS_FIFO_COUNT];
int fifo_credit[BRCMF_FWS_FIFO_COUNT];
int init_fifo_credit[BRCMF_FWS_FIFO_COUNT];
int credits_borrowed[BRCMF_FWS_FIFO_AC_VO + 1];
int credits_borrowed[BRCMF_FWS_FIFO_AC_VO + 1]
[BRCMF_FWS_FIFO_AC_VO + 1];
int deq_node_pos[BRCMF_FWS_FIFO_COUNT];
u32 fifo_credit_map;
u32 fifo_delay_map;
@ -521,6 +501,7 @@ struct brcmf_fws_info {
bool creditmap_received;
u8 mode;
bool avoid_queueing;
int fifo_init_credit[BRCMF_FWS_FIFO_COUNT];
};
/*
@ -583,15 +564,21 @@ static bool brcmf_fws_ifidx_match(struct sk_buff *skb, void *arg)
static void brcmf_fws_psq_flush(struct brcmf_fws_info *fws, struct pktq *q,
int ifidx)
{
struct brcmf_fws_hanger_item *hi;
bool (*matchfn)(struct sk_buff *, void *) = NULL;
struct sk_buff *skb;
int prec;
u32 hslot;
if (ifidx != -1)
matchfn = brcmf_fws_ifidx_match;
for (prec = 0; prec < q->num_prec; prec++) {
skb = brcmu_pktq_pdeq_match(q, prec, matchfn, &ifidx);
while (skb) {
hslot = brcmf_skb_htod_tag_get_field(skb, HSLOT);
hi = &fws->hanger.items[hslot];
WARN_ON(skb != hi->pkt);
hi->state = BRCMF_FWS_HANGER_ITEM_STATE_FREE;
brcmu_pkt_buf_free_skb(skb);
skb = brcmu_pktq_pdeq_match(q, prec, matchfn, &ifidx);
}
@ -1214,13 +1201,11 @@ static void brcmf_fws_return_credits(struct brcmf_fws_info *fws,
if (!credits)
return;
fws->fifo_credit_map |= 1 << fifo;
if ((fifo == BRCMF_FWS_FIFO_AC_BE) &&
(fws->credits_borrowed[0])) {
if (fifo > BRCMF_FWS_FIFO_AC_BK &&
fifo <= BRCMF_FWS_FIFO_AC_VO) {
for (lender_ac = BRCMF_FWS_FIFO_AC_VO; lender_ac >= 0;
lender_ac--) {
borrowed = &fws->credits_borrowed[lender_ac];
borrowed = &fws->credits_borrowed[fifo][lender_ac];
if (*borrowed) {
fws->fifo_credit_map |= (1 << lender_ac);
fifo_credit = &fws->fifo_credit[lender_ac];
@ -1237,7 +1222,11 @@ static void brcmf_fws_return_credits(struct brcmf_fws_info *fws,
}
}
fws->fifo_credit[fifo] += credits;
if (credits) {
fws->fifo_credit[fifo] += credits;
fws->fifo_credit_map |= 1 << fifo;
}
if (fws->fifo_credit[fifo] > fws->init_fifo_credit[fifo])
fws->fifo_credit[fifo] = fws->init_fifo_credit[fifo];
@ -1635,6 +1624,7 @@ static int brcmf_fws_notify_credit_map(struct brcmf_if *ifp,
if (fws->fifo_credit[i] < 0)
brcmf_err("fifo_credit[%d] value is negative(%d)\n",
i, fws->fifo_credit[i]);
fws->fifo_init_credit[i] = fws->fifo_credit[i];
}
brcmf_fws_schedule_deq(fws);
brcmf_fws_unlock(fws);
@ -1868,6 +1858,9 @@ void brcmf_fws_hdrpull(struct brcmf_if *ifp, s16 siglen, struct sk_buff *skb)
WARN_ON(siglen > skb->len);
if (siglen > skb->len)
siglen = skb->len;
if (!siglen)
return;
/* if flow control disabled, skip to packet data and leave */
@ -2029,27 +2022,31 @@ static void brcmf_fws_rollback_toq(struct brcmf_fws_info *fws,
}
}
static int brcmf_fws_borrow_credit(struct brcmf_fws_info *fws)
static int brcmf_fws_borrow_credit(struct brcmf_fws_info *fws,
int highest_lender_ac, int borrower_ac,
bool borrow_all)
{
int lender_ac;
int lender_ac, borrow_limit = 0;
if (time_after(fws->borrow_defer_timestamp, jiffies)) {
fws->fifo_credit_map &= ~(1 << BRCMF_FWS_FIFO_AC_BE);
return -ENAVAIL;
}
for (lender_ac = 0; lender_ac <= highest_lender_ac; lender_ac++) {
for (lender_ac = 0; lender_ac <= BRCMF_FWS_FIFO_AC_VO; lender_ac++) {
if (fws->fifo_credit[lender_ac] > 0) {
fws->credits_borrowed[lender_ac]++;
if (!borrow_all)
borrow_limit =
fws->fifo_credit[lender_ac] / BRCMF_BORROW_RATIO;
else
borrow_limit = 0;
if (fws->fifo_credit[lender_ac] > borrow_limit) {
fws->credits_borrowed[borrower_ac][lender_ac]++;
fws->fifo_credit[lender_ac]--;
if (fws->fifo_credit[lender_ac] == 0)
fws->fifo_credit_map &= ~(1 << lender_ac);
fws->fifo_credit_map |= (1 << BRCMF_FWS_FIFO_AC_BE);
fws->fifo_credit_map |= (1 << borrower_ac);
brcmf_dbg(DATA, "borrow credit from: %d\n", lender_ac);
return 0;
}
}
fws->fifo_credit_map &= ~(1 << BRCMF_FWS_FIFO_AC_BE);
fws->fifo_credit_map &= ~(1 << borrower_ac);
return -ENAVAIL;
}
@ -2149,8 +2146,7 @@ int brcmf_fws_process_skb(struct brcmf_if *ifp, struct sk_buff *skb)
brcmf_fws_enq(fws, BRCMF_FWS_SKBSTATE_DELAYED, fifo, skb);
brcmf_fws_schedule_deq(fws);
} else {
brcmf_err("drop skb: no hanger slot\n");
brcmf_txfinalize(ifp, skb, false);
brcmf_err("no hanger slot available\n");
rc = -ENOMEM;
}
brcmf_fws_unlock(fws);
@ -2196,12 +2192,43 @@ void brcmf_fws_del_interface(struct brcmf_if *ifp)
brcmf_fws_lock(fws);
ifp->fws_desc = NULL;
brcmf_fws_macdesc_cleanup(fws, entry, ifp->ifidx);
brcmf_dbg(TRACE, "deleting %s\n", entry->name);
brcmf_fws_macdesc_deinit(entry);
brcmf_fws_cleanup(fws, ifp->ifidx);
brcmf_fws_unlock(fws);
}
static bool brcmf_fws_ismultistream(struct brcmf_fws_info *fws)
{
bool ret = false;
u8 credit_usage = 0;
/* Check only for BE, VI and VO traffic */
u32 delay_map = fws->fifo_delay_map &
((1 << BRCMF_FWS_FIFO_AC_BE) |
(1 << BRCMF_FWS_FIFO_AC_VI) |
(1 << BRCMF_FWS_FIFO_AC_VO));
if (hweight_long(delay_map) > 1) {
ret = true;
} else {
if (fws->fifo_credit[BRCMF_FWS_FIFO_AC_BE] <
fws->fifo_init_credit[BRCMF_FWS_FIFO_AC_BE])
credit_usage++;
if (fws->fifo_credit[BRCMF_FWS_FIFO_AC_VI] <
fws->fifo_init_credit[BRCMF_FWS_FIFO_AC_VI])
credit_usage++;
if (fws->fifo_credit[BRCMF_FWS_FIFO_AC_VO] <
fws->fifo_init_credit[BRCMF_FWS_FIFO_AC_VO])
credit_usage++;
if (credit_usage > 1)
ret = true;
}
return ret;
}
static void brcmf_fws_dequeue_worker(struct work_struct *worker)
{
struct brcmf_fws_info *fws;
@ -2215,6 +2242,11 @@ static void brcmf_fws_dequeue_worker(struct work_struct *worker)
fws = container_of(worker, struct brcmf_fws_info, fws_dequeue_work);
drvr = fws->drvr;
if (brcmf_fws_ismultistream(fws))
drvr->bus_if->allow_skborphan = false;
else
drvr->bus_if->allow_skborphan = true;
brcmf_fws_lock(fws);
for (fifo = BRCMF_FWS_FIFO_BCMC; fifo >= 0 && !fws->bus_flow_blocked;
fifo--) {
@ -2239,9 +2271,10 @@ static void brcmf_fws_dequeue_worker(struct work_struct *worker)
}
continue;
}
while ((fws->fifo_credit[fifo] > 0) ||
while ((fws->fifo_credit[fifo]) ||
((!fws->bcmc_credit_check) &&
(fifo == BRCMF_FWS_FIFO_BCMC))) {
(fifo == BRCMF_FWS_FIFO_BCMC))) {
skb = brcmf_fws_deq(fws, fifo);
if (!skb)
break;
@ -2251,10 +2284,14 @@ static void brcmf_fws_dequeue_worker(struct work_struct *worker)
if (fws->bus_flow_blocked)
break;
}
if ((fifo == BRCMF_FWS_FIFO_AC_BE) &&
(fws->fifo_credit[fifo] <= 0) &&
(!fws->bus_flow_blocked)) {
while (brcmf_fws_borrow_credit(fws) == 0) {
if (fifo >= BRCMF_FWS_FIFO_AC_BE &&
fifo <= BRCMF_FWS_FIFO_AC_VO &&
fws->fifo_credit[fifo] == 0 &&
!fws->bus_flow_blocked) {
while (brcmf_fws_borrow_credit(fws,
fifo - 1, fifo,
false) == 0) {
skb = brcmf_fws_deq(fws, fifo);
if (!skb) {
brcmf_fws_return_credits(fws, fifo, 1);
@ -2345,7 +2382,7 @@ struct brcmf_fws_info *brcmf_fws_attach(struct brcmf_pub *drvr)
struct brcmf_if *ifp;
u32 tlv = BRCMF_FWS_FLAGS_RSSI_SIGNALS;
int rc;
u32 mode = 0;
u32 mode;
fws = kzalloc(sizeof(*fws), GFP_KERNEL);
if (!fws) {

View File

@ -18,6 +18,29 @@
#ifndef FWSIGNAL_H_
#define FWSIGNAL_H_
/**
* enum brcmf_fws_fifo - fifo indices used by dongle firmware.
*
* @BRCMF_FWS_FIFO_FIRST: first fifo, ie. background.
* @BRCMF_FWS_FIFO_AC_BK: fifo for background traffic.
* @BRCMF_FWS_FIFO_AC_BE: fifo for best-effort traffic.
* @BRCMF_FWS_FIFO_AC_VI: fifo for video traffic.
* @BRCMF_FWS_FIFO_AC_VO: fifo for voice traffic.
* @BRCMF_FWS_FIFO_BCMC: fifo for broadcast/multicast (AP only).
* @BRCMF_FWS_FIFO_ATIM: fifo for ATIM (AP only).
* @BRCMF_FWS_FIFO_COUNT: number of fifos.
*/
enum brcmf_fws_fifo {
BRCMF_FWS_FIFO_FIRST,
BRCMF_FWS_FIFO_AC_BK = BRCMF_FWS_FIFO_FIRST,
BRCMF_FWS_FIFO_AC_BE,
BRCMF_FWS_FIFO_AC_VI,
BRCMF_FWS_FIFO_AC_VO,
BRCMF_FWS_FIFO_BCMC,
BRCMF_FWS_FIFO_ATIM,
BRCMF_FWS_FIFO_COUNT
};
struct brcmf_fws_info *brcmf_fws_attach(struct brcmf_pub *drvr);
void brcmf_fws_detach(struct brcmf_fws_info *fws);
bool brcmf_fws_queue_skbs(struct brcmf_fws_info *fws);

View File

@ -78,6 +78,7 @@
#define BRCMF_MSGBUF_TRICKLE_TXWORKER_THRS 32
#define BRCMF_MSGBUF_UPDATE_RX_PTR_THRS 48
#define BRCMF_MAX_TXSTATUS_WAIT_RETRIES 10
struct msgbuf_common_hdr {
u8 msgtype;
@ -330,7 +331,7 @@ brcmf_msgbuf_alloc_pktid(struct device *dev,
do {
(*idx)++;
if (*idx == pktids->array_size)
*idx = 0;
*idx = 1;
if (array[*idx].allocated.counter == 0)
if (atomic_cmpxchg(&array[*idx].allocated, 0, 1) == 0)
break;
@ -793,8 +794,12 @@ static int brcmf_msgbuf_tx_queue_data(struct brcmf_pub *drvr, int ifidx,
flowid = brcmf_flowring_lookup(flow, eh->h_dest, skb->priority, ifidx);
if (flowid == BRCMF_FLOWRING_INVALID_ID) {
flowid = brcmf_msgbuf_flowring_create(msgbuf, ifidx, skb);
if (flowid == BRCMF_FLOWRING_INVALID_ID)
if (flowid == BRCMF_FLOWRING_INVALID_ID) {
return -ENOMEM;
} else {
brcmf_flowring_enqueue(flow, flowid, skb);
return 0;
}
}
queue_count = brcmf_flowring_enqueue(flow, flowid, skb);
force = ((queue_count % BRCMF_MSGBUF_TRICKLE_TXWORKER_THRS) == 0);
@ -1112,7 +1117,7 @@ static void brcmf_msgbuf_process_event(struct brcmf_msgbuf *msgbuf, void *buf)
skb->protocol = eth_type_trans(skb, ifp->ndev);
brcmf_fweh_process_skb(ifp->drvr, skb);
brcmf_fweh_process_skb(ifp->drvr, skb, 0);
exit:
brcmu_pkt_buf_free_skb(skb);
@ -1324,9 +1329,25 @@ void brcmf_msgbuf_delete_flowring(struct brcmf_pub *drvr, u16 flowid)
struct brcmf_msgbuf *msgbuf = (struct brcmf_msgbuf *)drvr->proto->pd;
struct msgbuf_tx_flowring_delete_req *delete;
struct brcmf_commonring *commonring;
struct brcmf_commonring *commonring_del;
void *ret_ptr;
u8 ifidx;
int err;
int retry = BRCMF_MAX_TXSTATUS_WAIT_RETRIES;
/* wait for commonring txflow finished */
commonring_del = msgbuf->flowrings[flowid];
brcmf_commonring_lock(commonring_del);
while (retry && atomic_read(&commonring_del->outstanding_tx)) {
usleep_range(5000, 10000);
retry--;
}
brcmf_commonring_unlock(commonring_del);
if (!retry && atomic_read(&commonring_del->outstanding_tx)) {
brcmf_err("timed out waiting for txstatus\n");
atomic_set(&commonring_del->outstanding_tx, 0);
}
commonring = msgbuf->commonrings[BRCMF_H2D_MSGRING_CONTROL_SUBMIT];
brcmf_commonring_lock(commonring);

View File

@ -28,6 +28,7 @@
#include "fwil_types.h"
#include "p2p.h"
#include "cfg80211.h"
#include "feature.h"
/* parameters used for p2p escan */
#define P2PAPI_SCAN_NPROBES 1
@ -70,12 +71,13 @@
#define P2P_AF_MIN_DWELL_TIME 100
#define P2P_AF_MED_DWELL_TIME 400
#define P2P_AF_LONG_DWELL_TIME 1000
#define P2P_AF_TX_MAX_RETRY 1
#define P2P_AF_TX_MAX_RETRY 5
#define P2P_AF_MAX_WAIT_TIME msecs_to_jiffies(2000)
#define P2P_INVALID_CHANNEL -1
#define P2P_CHANNEL_SYNC_RETRY 5
#define P2P_AF_FRM_SCAN_MAX_WAIT msecs_to_jiffies(450)
#define P2P_DEFAULT_SLEEP_TIME_VSDB 200
#define P2P_AF_RETRY_DELAY_TIME 40
/* WiFi P2P Public Action Frame OUI Subtypes */
#define P2P_PAF_GON_REQ 0 /* Group Owner Negotiation Req */
@ -103,6 +105,9 @@
#define P2PSD_ACTION_ID_GAS_CRESP 0x0d /* GAS Comback Response AF */
#define BRCMF_P2P_DISABLE_TIMEOUT msecs_to_jiffies(500)
/* Mask for retry counter of custom dwell time */
#define CUSTOM_RETRY_MASK 0xff000000
/**
* struct brcmf_p2p_disc_st_le - set discovery state in firmware.
*
@ -498,9 +503,13 @@ static void brcmf_p2p_generate_bss_mac(struct brcmf_p2p_info *p2p, u8 *dev_addr)
* BSSCFGs need to simultaneously co-exist, then this address must be
* different from the P2P Device Address, but also locally administered.
*/
memcpy(p2p->int_addr, p2p->dev_addr, ETH_ALEN);
p2p->int_addr[0] |= 0x02;
p2p->int_addr[4] ^= 0x80;
memcpy(p2p->conn_int_addr, p2p->dev_addr, ETH_ALEN);
p2p->conn_int_addr[0] |= 0x02;
p2p->conn_int_addr[4] ^= 0x80;
memcpy(p2p->conn2_int_addr, p2p->dev_addr, ETH_ALEN);
p2p->conn2_int_addr[0] |= 0x02;
p2p->conn2_int_addr[4] ^= 0x90;
}
/**
@ -1667,6 +1676,17 @@ static s32 brcmf_p2p_pub_af_tx(struct brcmf_cfg80211_info *cfg,
return err;
}
static bool brcmf_p2p_check_dwell_overflow(s32 requested_dwell,
unsigned long dwell_jiffies)
{
if ((requested_dwell & CUSTOM_RETRY_MASK) &&
(jiffies_to_msecs(jiffies - dwell_jiffies) >
(requested_dwell & ~CUSTOM_RETRY_MASK))) {
brcmf_err("Action frame TX retry time over dwell time!\n");
return true;
}
return false;
}
/**
* brcmf_p2p_send_action_frame() - send action frame .
*
@ -1690,6 +1710,10 @@ bool brcmf_p2p_send_action_frame(struct brcmf_cfg80211_info *cfg,
s32 tx_retry;
s32 extra_listen_time;
uint delta_ms;
unsigned long dwell_jiffies = 0;
bool dwell_overflow = false;
s32 requested_dwell = af_params->dwell_time;
action_frame = &af_params->action_frame;
action_frame_len = le16_to_cpu(action_frame->len);
@ -1801,12 +1825,21 @@ bool brcmf_p2p_send_action_frame(struct brcmf_cfg80211_info *cfg,
/* update channel */
af_params->channel = cpu_to_le32(afx_hdl->peer_chan);
}
dwell_jiffies = jiffies;
dwell_overflow = brcmf_p2p_check_dwell_overflow(requested_dwell,
dwell_jiffies);
tx_retry = 0;
while (!p2p->block_gon_req_tx &&
(ack == false) && (tx_retry < P2P_AF_TX_MAX_RETRY)) {
(!ack) && (tx_retry < P2P_AF_TX_MAX_RETRY) &&
!dwell_overflow) {
if (af_params->channel)
msleep(P2P_AF_RETRY_DELAY_TIME);
ack = !brcmf_p2p_tx_action_frame(p2p, af_params);
tx_retry++;
dwell_overflow = brcmf_p2p_check_dwell_overflow(requested_dwell,
dwell_jiffies);
}
if (ack == false) {
brcmf_err("Failed to send Action Frame(retry %d)\n", tx_retry);
@ -2019,7 +2052,7 @@ int brcmf_p2p_ifchange(struct brcmf_cfg80211_info *cfg,
if_request.type = cpu_to_le16((u16)if_type);
if_request.chspec = cpu_to_le16(chanspec);
memcpy(if_request.addr, p2p->int_addr, sizeof(if_request.addr));
memcpy(if_request.addr, p2p->conn_int_addr, sizeof(if_request.addr));
brcmf_cfg80211_arm_vif_event(cfg, vif);
err = brcmf_fil_iovar_data_set(vif->ifp, "p2p_ifupd", &if_request,
@ -2165,6 +2198,27 @@ fail:
return ERR_PTR(err);
}
int brcmf_p2p_get_conn_idx(struct brcmf_cfg80211_info *cfg)
{
int i;
struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
if (!ifp)
return -ENODEV;
for (i = P2PAPI_BSSCFG_CONNECTION; i < P2PAPI_BSSCFG_MAX; i++) {
if (!cfg->p2p.bss_idx[i].vif) {
if (i == P2PAPI_BSSCFG_CONNECTION2 &&
!(brcmf_feat_is_enabled(ifp, BRCMF_FEAT_RSDB))) {
brcmf_err("Multi p2p not supported");
return -EIO;
}
return i;
}
}
return -EIO;
}
/**
* brcmf_p2p_add_vif() - create a new P2P virtual interface.
*
@ -2183,7 +2237,9 @@ struct wireless_dev *brcmf_p2p_add_vif(struct wiphy *wiphy, const char *name,
struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
struct brcmf_cfg80211_vif *vif;
enum brcmf_fil_p2p_if_types iftype;
int err;
int err = 0;
int connidx;
u8 *p2p_intf_addr;
if (brcmf_cfg80211_vif_event_armed(cfg))
return ERR_PTR(-EBUSY);
@ -2209,9 +2265,21 @@ struct wireless_dev *brcmf_p2p_add_vif(struct wiphy *wiphy, const char *name,
return (struct wireless_dev *)vif;
brcmf_cfg80211_arm_vif_event(cfg, vif);
err = brcmf_p2p_request_p2p_if(&cfg->p2p, ifp, cfg->p2p.int_addr,
iftype);
connidx = brcmf_p2p_get_conn_idx(cfg);
if (connidx == P2PAPI_BSSCFG_CONNECTION)
p2p_intf_addr = cfg->p2p.conn_int_addr;
else if (connidx == P2PAPI_BSSCFG_CONNECTION2)
p2p_intf_addr = cfg->p2p.conn2_int_addr;
else
err = -EINVAL;
if (!err)
err = brcmf_p2p_request_p2p_if(&cfg->p2p, ifp,
p2p_intf_addr, iftype);
if (err) {
brcmf_err("request p2p interface failed\n");
brcmf_cfg80211_arm_vif_event(cfg, NULL);
goto fail;
}
@ -2243,7 +2311,7 @@ struct wireless_dev *brcmf_p2p_add_vif(struct wiphy *wiphy, const char *name,
goto fail;
}
cfg->p2p.bss_idx[P2PAPI_BSSCFG_CONNECTION].vif = vif;
cfg->p2p.bss_idx[connidx].vif = vif;
/* Disable firmware roaming for P2P interface */
brcmf_fil_iovar_int_set(ifp, "roam_off", 1);
if (iftype == BRCMF_FIL_P2P_IF_GO) {

View File

@ -25,13 +25,15 @@ struct brcmf_cfg80211_info;
*
* @P2PAPI_BSSCFG_PRIMARY: maps to driver's primary bsscfg.
* @P2PAPI_BSSCFG_DEVICE: maps to driver's P2P device discovery bsscfg.
* @P2PAPI_BSSCFG_CONNECTION: maps to driver's P2P connection bsscfg.
* @P2PAPI_BSSCFG_CONNECTION: maps to driver's 1st P2P connection bsscfg.
* @P2PAPI_BSSCFG_CONNECTION2: maps to driver's 2nd P2P connection bsscfg.
* @P2PAPI_BSSCFG_MAX: used for range checking.
*/
enum p2p_bss_type {
P2PAPI_BSSCFG_PRIMARY, /* maps to driver's primary bsscfg */
P2PAPI_BSSCFG_DEVICE, /* maps to driver's P2P device discovery bsscfg */
P2PAPI_BSSCFG_CONNECTION, /* maps to driver's P2P connection bsscfg */
P2PAPI_BSSCFG_CONNECTION, /* driver's 1st P2P connection bsscfg */
P2PAPI_BSSCFG_CONNECTION2, /* driver's 2nd P2P connection bsscfg */
P2PAPI_BSSCFG_MAX
};
@ -130,7 +132,8 @@ struct brcmf_p2p_info {
struct brcmf_cfg80211_info *cfg;
unsigned long status;
u8 dev_addr[ETH_ALEN];
u8 int_addr[ETH_ALEN];
u8 conn_int_addr[ETH_ALEN];
u8 conn2_int_addr[ETH_ALEN];
struct p2p_bss bss_idx[P2PAPI_BSSCFG_MAX];
struct timer_list listen_timer;
u8 listen_channel;

View File

@ -39,6 +39,7 @@
#include "chip.h"
#include "core.h"
#include "common.h"
#include "cfg80211.h"
enum brcmf_pcie_state {
@ -78,7 +79,7 @@ static struct brcmf_firmware_mapping brcmf_pcie_fwnames[] = {
BRCMF_FW_NVRAM_ENTRY(BRCM_CC_4371_CHIP_ID, 0xFFFFFFFF, 4371),
};
#define BRCMF_PCIE_FW_UP_TIMEOUT 2000 /* msec */
#define BRCMF_PCIE_FW_UP_TIMEOUT 5000 /* msec */
#define BRCMF_PCIE_REG_MAP_SIZE (32 * 1024)
@ -671,7 +672,6 @@ brcmf_pcie_send_mb_data(struct brcmf_pciedev_info *devinfo, u32 htod_mb_data)
brcmf_pcie_write_tcm32(devinfo, addr, htod_mb_data);
pci_write_config_dword(devinfo->pdev, BRCMF_PCIE_REG_SBMBX, 1);
pci_write_config_dword(devinfo->pdev, BRCMF_PCIE_REG_SBMBX, 1);
return 0;
}
@ -1888,11 +1888,22 @@ static int brcmf_pcie_pm_enter_D3(struct device *dev)
{
struct brcmf_pciedev_info *devinfo;
struct brcmf_bus *bus;
struct brcmf_cfg80211_info *config;
int retry = BRCMF_PM_WAIT_MAXRETRY;
brcmf_dbg(PCIE, "Enter\n");
bus = dev_get_drvdata(dev);
devinfo = bus->bus_priv.pcie->devinfo;
config = bus->drvr->config;
while (retry &&
config->pm_state == BRCMF_CFG80211_PM_STATE_SUSPENDING) {
usleep_range(10000, 20000);
retry--;
}
if (!retry && config->pm_state == BRCMF_CFG80211_PM_STATE_SUSPENDING)
brcmf_err("timed out wait for cfg80211 suspended\n");
brcmf_bus_change_state(bus, BRCMF_BUS_DOWN);
@ -1972,6 +1983,7 @@ static const struct dev_pm_ops brcmf_pciedrvr_pm = {
static const struct pci_device_id brcmf_pcie_devid_table[] = {
BRCMF_PCIE_DEVICE(BRCM_PCIE_4350_DEVICE_ID),
BRCMF_PCIE_DEVICE(BRCM_PCIE_4354_RAW_DEVICE_ID),
BRCMF_PCIE_DEVICE_SUB(BRCM_PCIE_4355_DEVICE_ID,BRCM_PCIE_VENDOR_ID_BROADCOM,BRCM_PCIE_4355_DEVICE_ID),
BRCMF_PCIE_DEVICE(BRCM_PCIE_4356_DEVICE_ID),
BRCMF_PCIE_DEVICE(BRCM_PCIE_43567_DEVICE_ID),

View File

@ -53,10 +53,17 @@
#define DEFAULT_F2_WATERMARK 0x8
#define CY_4373_F2_WATERMARK 0x40
#define CY_4373_F1_MESBUSYCTRL (CY_4373_F2_WATERMARK | SBSDIO_MESBUSYCTRL_ENAB)
#define CY_43012_F2_WATERMARK 0x60
#define CY_4339_F2_WATERMARK 48
#define CY_43455_F2_WATERMARK 0x60
#define CY_43455_MES_WATERMARK 0x50
#define CY_43455_MESBUSYCTRL (CY_43455_MES_WATERMARK | \
SBSDIO_MESBUSYCTRL_ENAB)
#define CY_4339_F2_WATERMARK 48
#define CY_4339_MES_WATERMARK 80
#define CY_4339_MESBUSYCTRL (CY_4339_MES_WATERMARK | SBSDIO_MESBUSYCTRL_ENAB)
#define CY_4359_F2_WATERMARK 0x40
#define CY_4359_F1_MESBUSYCTRL (CY_4359_F2_WATERMARK | SBSDIO_MESBUSYCTRL_ENAB)
#ifdef DEBUG
#define BRCMF_TRAP_INFO_SIZE 80
@ -270,10 +277,11 @@ struct rte_console {
#define I_HMB_HOST_INT I_HMB_SW3 /* Miscellaneous Interrupt */
/* tohostmailboxdata */
#define HMB_DATA_NAKHANDLED 1 /* retransmit NAK'd frame */
#define HMB_DATA_DEVREADY 2 /* talk to host after enable */
#define HMB_DATA_FC 4 /* per prio flowcontrol update flag */
#define HMB_DATA_FWREADY 8 /* fw ready for protocol activity */
#define HMB_DATA_NAKHANDLED 0x0001 /* retransmit NAK'd frame */
#define HMB_DATA_DEVREADY 0x0002 /* talk to host after enable */
#define HMB_DATA_FC 0x0004 /* per prio flowcontrol update flag */
#define HMB_DATA_FWREADY 0x0008 /* fw ready for protocol activity */
#define HMB_DATA_FWHALT 0x0010 /* firmware halted */
#define HMB_DATA_FCDATA_MASK 0xff000000
#define HMB_DATA_FCDATA_SHIFT 24
@ -329,6 +337,10 @@ struct rte_console {
static void brcmf_sdio_firmware_callback(struct device *dev, int err,
const struct firmware *code,
void *nvram, u32 nvram_len);
static int brcmf_sdio_f2_ready(struct brcmf_sdio *bus);
static int brcmf_ulp_event_notify(struct brcmf_if *ifp,
const struct brcmf_event_msg *evtmsg,
void *data);
#ifdef DEBUG
/* Device console log buffer state */
@ -623,6 +635,7 @@ BRCMF_FW_NVRAM_DEF(43430A1, "brcmfmac43430-sdio.bin", "brcmfmac43430-sdio.txt");
BRCMF_FW_NVRAM_DEF(43455, "brcmfmac43455-sdio.bin", "brcmfmac43455-sdio.txt");
BRCMF_FW_NVRAM_DEF(4354, "brcmfmac4354-sdio.bin", "brcmfmac4354-sdio.txt");
BRCMF_FW_NVRAM_DEF(4356, "brcmfmac4356-sdio.bin", "brcmfmac4356-sdio.txt");
BRCMF_FW_NVRAM_DEF(4359, "brcmfmac4359-sdio.bin", "brcmfmac4359-sdio.txt");
BRCMF_FW_NVRAM_DEF(4373, "brcmfmac4373-sdio.bin", "brcmfmac4373-sdio.txt");
BRCMF_FW_NVRAM_DEF(43012, "brcmfmac43012-sdio.bin", "brcmfmac43012-sdio.txt");
@ -644,6 +657,7 @@ static struct brcmf_firmware_mapping brcmf_sdio_fwnames[] = {
BRCMF_FW_NVRAM_ENTRY(BRCM_CC_4345_CHIP_ID, 0xFFFFFFC0, 43455),
BRCMF_FW_NVRAM_ENTRY(BRCM_CC_4354_CHIP_ID, 0xFFFFFFFF, 4354),
BRCMF_FW_NVRAM_ENTRY(BRCM_CC_4356_CHIP_ID, 0xFFFFFFFF, 4356),
BRCMF_FW_NVRAM_ENTRY(BRCM_CC_4359_CHIP_ID, 0xFFFFFFFF, 4359),
BRCMF_FW_NVRAM_ENTRY(CY_CC_4373_CHIP_ID, 0xFFFFFFFF, 4373),
BRCMF_FW_NVRAM_ENTRY(CY_CC_43012_CHIP_ID, 0xFFFFFFFF, 43012)
};
@ -787,6 +801,7 @@ static int brcmf_sdio_htclk(struct brcmf_sdio *bus, bool on, bool pendok)
clkreq =
bus->alp_only ? SBSDIO_ALP_AVAIL_REQ : SBSDIO_HT_AVAIL_REQ;
printk("[---- SBA ----] Sending SBSDIO_FUNC1_CHIPCLKCSR/SBSDIO_HT_AVAIL_REQ ..\n");
brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR,
clkreq, &err);
if (err) {
@ -1092,7 +1107,7 @@ static void brcmf_sdio_get_console_addr(struct brcmf_sdio *bus)
}
#endif /* DEBUG */
static u32 brcmf_sdio_hostmail(struct brcmf_sdio *bus)
static u32 brcmf_sdio_hostmail(struct brcmf_sdio *bus, u32 *hmbd)
{
u32 intstatus = 0;
u32 hmb_data;
@ -1110,6 +1125,10 @@ static u32 brcmf_sdio_hostmail(struct brcmf_sdio *bus)
offsetof(struct sdpcmd_regs, tosbmailbox));
bus->sdcnt.f1regdata += 2;
/* dongle indicates the firmware has halted/crashed */
if (hmb_data & HMB_DATA_FWHALT)
brcmf_err("mailbox indicates firmware halted\n");
/* Dongle recomposed rx frames, accept them again */
if (hmb_data & HMB_DATA_NAKHANDLED) {
brcmf_dbg(SDIO, "Dongle reports NAK handled, expect rtx of %d\n",
@ -1167,9 +1186,13 @@ static u32 brcmf_sdio_hostmail(struct brcmf_sdio *bus)
HMB_DATA_NAKHANDLED |
HMB_DATA_FC |
HMB_DATA_FWREADY |
HMB_DATA_FWHALT |
HMB_DATA_FCDATA_MASK | HMB_DATA_VERSION_MASK))
brcmf_err("Unknown mailbox data content: 0x%02x\n",
hmb_data);
/* Populate hmb_data if argument is passed for DS1 check later */
if (hmbd)
*hmbd = hmb_data;
return intstatus;
}
@ -2560,23 +2583,21 @@ brcmf_sdio_ulp_preinit(struct device *dev)
struct brcmf_bus *bus_if = dev_get_drvdata(dev);
struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio;
struct brcmf_if *ifp = bus_if->drvr->iflist[0];
s32 err = 0;
brcmf_dbg(TRACE, "Enter\n");
brcmf_dbg(ULP, "Enter\n");
/* Query ulp_sdioctrl iovar to get the ULP related SHM offsets */
err = brcmf_fil_iovar_data_get(ifp, "ulp_sdioctrl", &sdiodev->shm_ulp,
sizeof(sdiodev->shm_ulp));
if (err)
brcmf_err("ulp_sdioctrl iovar returned err = %d\n", err);
brcmf_fil_iovar_data_get(ifp, "ulp_sdioctrl",
&sdiodev->fmac_ulp.ulp_shm_offset,
sizeof(sdiodev->fmac_ulp.ulp_shm_offset));
sdiodev->ulp = false;
brcmf_dbg(TRACE, "m_ulp_ctrl_sdio[%x] m_ulp_wakeevt_ind [%x]\n",
M_DS1_CTRL_SDIO(sdiodev->shm_ulp),
M_WAKEEVENT_IND(sdiodev->shm_ulp));
brcmf_dbg(TRACE, "m_ulp_wakeind [%x]\n",
M_ULP_WAKE_IND(sdiodev->shm_ulp));
brcmf_dbg(ULP, "m_ulp_ctrl_sdio[%x] m_ulp_wakeevt_ind [%x]\n",
M_DS1_CTRL_SDIO(sdiodev->fmac_ulp),
M_WAKEEVENT_IND(sdiodev->fmac_ulp));
brcmf_dbg(ULP, "m_ulp_wakeind [%x]\n",
M_ULP_WAKE_IND(sdiodev->fmac_ulp));
}
/* Reinitialize ARM because In DS1 mode ARM got off */
@ -2604,41 +2625,68 @@ brcmf_sdio_ulp_reinit_fw(struct brcmf_sdio *bus)
/* Check if device is in DS1 mode and handshake with ULP UCODE */
static bool
brcmf_sdio_ulp_pre_redownload_check(struct brcmf_sdio *bus)
brcmf_sdio_ulp_pre_redownload_check(struct brcmf_sdio *bus, u32 hmb_data)
{
int err = 0;
u32 value = 0;
u32 val32, ulp_wake_ind, wowl_wake_ind;
int reg_addr;
unsigned long timeout;
struct brcmf_ulp *fmac_ulp = &bus->sdiodev->fmac_ulp;
int i = 0;
/* If any host mail box data is present, ignore DS1 exit sequence */
if (hmb_data)
return false;
/* Skip if DS1 Exit is already in progress
* This can happen if firmware download is taking more time
*/
if (fmac_ulp->ulp_state == FMAC_ULP_TRIGGERED)
return false;
value = brcmf_sdiod_regrb(bus->sdiodev, SDIO_CCCR_IOEx, &err);
if (value == SDIO_FUNC_ENABLE_1) {
brcmf_dbg(SDIO, "GOT THE INTERRUPT FROM UCODE\n");
brcmf_dbg(ULP, "GOT THE INTERRUPT FROM UCODE\n");
bus->sdiodev->ulp = true;
ulp_wake_ind = D11SHM_RD(bus->sdiodev, M_ULP_WAKE_IND(
bus->sdiodev->shm_ulp), &err) >> 16;
wowl_wake_ind = D11SHM_RD(bus->sdiodev, M_WAKEEVENT_IND(
bus->sdiodev->shm_ulp), &err) >> 16;
fmac_ulp->ulp_state = FMAC_ULP_TRIGGERED;
ulp_wake_ind = D11SHM_RDW(bus->sdiodev, M_ULP_WAKE_IND(
bus->sdiodev->fmac_ulp), &err);
wowl_wake_ind = D11SHM_RDW(bus->sdiodev, M_WAKEEVENT_IND(
bus->sdiodev->fmac_ulp), &err);
brcmf_dbg(SDIO, "wowl_wake_ind: 0x%08x, ulp_wake_ind: 0x%08x\n",
wowl_wake_ind, ulp_wake_ind);
brcmf_dbg(ULP, "wowl_wake_ind: 0x%08x, ulp_wake_ind: 0x%08x state %s\n",
wowl_wake_ind, ulp_wake_ind, (fmac_ulp->ulp_state) ?
"DS1 Exit Triggered" : "IDLE State");
if (wowl_wake_ind || ulp_wake_ind) {
/* TX wake Don't do anything.
/* RX wake Don't do anything.
* Just bail out and re-download firmware.
*/
/* Print out PHY TX error block when bit 9 set */
if ((ulp_wake_ind & C_DS1_PHY_TXERR) &&
M_DS1_PHYTX_ERR_BLK(bus->sdiodev->fmac_ulp)) {
brcmf_err("Dump PHY TX Error SHM Locations\n");
for (i = 0; i < PHYTX_ERR_BLK_SIZE; i++) {
pr_err("0x%x", D11SHM_RDW(bus->sdiodev,
(M_DS1_PHYTX_ERR_BLK
(bus->sdiodev->fmac_ulp) +
(i * 2)), &err));
}
brcmf_err("\n");
}
} else {
/* RX wake negotiate with MAC */
brcmf_dbg(SDIO, "M_DS1_CTRL_SDIO: 0x%08x\n",
(u32)D11SHM_RD(bus->sdiodev,
M_DS1_CTRL_SDIO(bus->sdiodev->shm_ulp),
/* TX wake negotiate with MAC */
brcmf_dbg(ULP, "M_DS1_CTRL_SDIO: 0x%08x\n",
(u32)D11SHM_RDW(bus->sdiodev,
M_DS1_CTRL_SDIO(bus->sdiodev->fmac_ulp),
&err));
val32 = D11SHM_RD(bus->sdiodev, M_DS1_CTRL_SDIO(
bus->sdiodev->fmac_ulp), &err);
D11SHM_WR(bus->sdiodev, M_DS1_CTRL_SDIO(
bus->sdiodev->shm_ulp),
C_DS1_CTRL_SDIO_DS1_EXIT |
C_DS1_CTRL_REQ_VALID,
bus->sdiodev->fmac_ulp), val32,
(C_DS1_CTRL_SDIO_DS1_EXIT |
C_DS1_CTRL_REQ_VALID),
&err);
val32 = D11REG_RD(bus->sdiodev,
D11_MACCONTROL_REG, &err);
@ -2647,37 +2695,38 @@ brcmf_sdio_ulp_pre_redownload_check(struct brcmf_sdio *bus)
D11_MACCONTROL_REG, val32, &err);
/* Poll for PROC_DONE to be set by ucode */
value = D11SHM_RD(bus->sdiodev,
M_DS1_CTRL_SDIO(
bus->sdiodev->shm_ulp), &err);
value = D11SHM_RDW(bus->sdiodev,
M_DS1_CTRL_SDIO(
bus->sdiodev->fmac_ulp), &err);
/* Wait here (polling) for C_DS1_CTRL_PROC_DONE */
timeout = jiffies + ULP_HUDI_PROC_DONE_TIME;
while (!(value & C_DS1_CTRL_PROC_DONE)) {
value = D11SHM_RD(bus->sdiodev,
M_DS1_CTRL_SDIO(
bus->sdiodev->shm_ulp), &err);
value = D11SHM_RDW(bus->sdiodev,
M_DS1_CTRL_SDIO
(bus->sdiodev->fmac_ulp),
&err);
if (time_after(jiffies, timeout))
break;
usleep_range(1000, 2000);
}
brcmf_dbg(SDIO, "M_DS1_CTRL_SDIO: 0x%08x\n",
(u32)D11SHM_RD(bus->sdiodev,
brcmf_dbg(ULP, "M_DS1_CTRL_SDIO: 0x%08x\n",
(u32)D11SHM_RDW(bus->sdiodev,
M_DS1_CTRL_SDIO(
bus->sdiodev->shm_ulp), &err));
value = D11SHM_RD(bus->sdiodev,
M_DS1_CTRL_SDIO(
bus->sdiodev->shm_ulp), &err);
bus->sdiodev->fmac_ulp), &err));
value = D11SHM_RDW(bus->sdiodev,
M_DS1_CTRL_SDIO(
bus->sdiodev->fmac_ulp), &err);
if (!(value & C_DS1_CTRL_PROC_DONE)) {
brcmf_err("%s: timeout Failed to enter DS1 Exit state!\n",
__func__);
return false;
}
}
ulp_wake_ind = D11SHM_RD(bus->sdiodev, M_ULP_WAKE_IND(
bus->sdiodev->shm_ulp), &err) >> 16;
wowl_wake_ind = D11SHM_RD(bus->sdiodev, M_WAKEEVENT_IND(
bus->sdiodev->shm_ulp), &err) >> 16;
brcmf_dbg(SDIO, "wowl_wake_ind: 0x%08x, ulp_wake_ind: 0x%08x\n",
ulp_wake_ind = D11SHM_RDW(bus->sdiodev, M_ULP_WAKE_IND(
bus->sdiodev->fmac_ulp), &err);
wowl_wake_ind = D11SHM_RDW(bus->sdiodev, M_WAKEEVENT_IND(
bus->sdiodev->fmac_ulp), &err);
brcmf_dbg(ULP, "wowl_wake_ind: 0x%08x, ulp_wake_ind: 0x%08x\n",
wowl_wake_ind, ulp_wake_ind);
reg_addr = CORE_CC_REG(
brcmf_chip_get_pmu(bus->ci)->base, min_res_mask);
@ -2762,9 +2811,10 @@ static void brcmf_sdio_dpc(struct brcmf_sdio *bus)
/* Handle host mailbox indication */
if (intstatus & I_HMB_HOST_INT) {
u32 hmb_data = 0;
intstatus &= ~I_HMB_HOST_INT;
intstatus |= brcmf_sdio_hostmail(bus);
if (brcmf_sdio_ulp_pre_redownload_check(bus))
intstatus |= brcmf_sdio_hostmail(bus, &hmb_data);
if (brcmf_sdio_ulp_pre_redownload_check(bus, hmb_data))
brcmf_sdio_ulp_reinit_fw(bus);
}
@ -2810,13 +2860,14 @@ static void brcmf_sdio_dpc(struct brcmf_sdio *bus)
brcmf_sdio_clrintr(bus);
if (bus->ctrl_frame_stat && (bus->clkstate == CLK_AVAIL) &&
data_ok(bus)) {
data_ok(bus) && brcmf_sdio_f2_ready(bus)) {
sdio_claim_host(bus->sdiodev->func[1]);
if (bus->ctrl_frame_stat) {
err = brcmf_sdio_tx_ctrlframe(bus, bus->ctrl_frame_buf,
err = brcmf_sdio_tx_ctrlframe(bus,
bus->ctrl_frame_buf,
bus->ctrl_frame_len);
bus->ctrl_frame_err = err;
wmb();
wmb(); /* drain writebuffer */
bus->ctrl_frame_stat = false;
}
sdio_release_host(bus->sdiodev->func[1]);
@ -3532,8 +3583,10 @@ static void brcmf_sdio_sr_init(struct brcmf_sdio *bus)
}
if (bus->ci->chip == CY_CC_43012_CHIP_ID ||
bus->ci->chip == CY_CC_4373_CHIP_ID ||
bus->ci->chip == BRCM_CC_4339_CHIP_ID ||
bus->ci->chip == BRCM_CC_4354_CHIP_ID ||
bus->ci->chip == BRCM_CC_4356_CHIP_ID ||
bus->ci->chip == BRCM_CC_4345_CHIP_ID) {
cardcap = SDIO_CCCR_BRCM_CARDCAP_CMD_NODEC;
} else {
@ -3807,7 +3860,11 @@ static void brcmf_sdio_bus_watchdog(struct brcmf_sdio *bus)
if (bus->idlecount > bus->idletime) {
brcmf_dbg(SDIO, "idle\n");
sdio_claim_host(bus->sdiodev->func[1]);
brcmf_sdio_wd_timer(bus, false);
#ifdef DEBUG
if (!BRCMF_FWCON_ON() ||
bus->console_interval == 0)
#endif
brcmf_sdio_wd_timer(bus, false);
bus->idlecount = 0;
brcmf_sdio_bus_sleep(bus, true, false);
sdio_release_host(bus->sdiodev->func[1]);
@ -4219,7 +4276,7 @@ static void brcmf_sdio_firmware_callback(struct device *dev, int err,
u8 saveclk;
u8 devctl;
brcmf_dbg(TRACE, "Enter: dev=%s, err=%d\n", dev_name(dev), err);
brcmf_dbg(ULP, "Enter: dev=%s, err=%d\n", dev_name(dev), err);
bus_if = dev_get_drvdata(dev);
sdiodev = bus_if->bus_priv.sdio;
if (err)
@ -4287,6 +4344,7 @@ static void brcmf_sdio_firmware_callback(struct device *dev, int err,
devctl = brcmf_sdiod_regrb(sdiodev, SBSDIO_DEVICE_CTL, &err);
devctl |= SBSDIO_DEVCTL_F2WM_ENAB;
brcmf_sdiod_regwb(sdiodev, SBSDIO_DEVICE_CTL, devctl, &err);
brcmf_sdiod_regwb(sdiodev, SBSDIO_FUNC1_MESBUSYCTRL, CY_4373_F1_MESBUSYCTRL, &err);
break;
case SDIO_DEVICE_ID_CYPRESS_43012:
brcmf_dbg(INFO, "set F2 watermark to 0x%x*4 bytes for 43012\n",
@ -4299,6 +4357,22 @@ static void brcmf_sdio_firmware_callback(struct device *dev, int err,
devctl |= SBSDIO_DEVCTL_F2WM_ENAB;
brcmf_sdiod_regwb(sdiodev,
SBSDIO_DEVICE_CTL, devctl, &err);
/* Register for ULP events */
brcmf_fweh_register(bus_if->drvr, BRCMF_E_ULP,
brcmf_ulp_event_notify);
break;
case SDIO_DEVICE_ID_BROADCOM_43455:
brcmf_dbg(INFO, "set F2 watermark to 0x%x*4 bytes for 43455\n",
CY_43455_F2_WATERMARK);
brcmf_sdiod_regwb(sdiodev, SBSDIO_WATERMARK,
CY_43455_F2_WATERMARK, &err);
devctl = brcmf_sdiod_regrb(sdiodev, SBSDIO_DEVICE_CTL,
&err);
devctl |= SBSDIO_DEVCTL_F2WM_ENAB;
brcmf_sdiod_regwb(sdiodev, SBSDIO_DEVICE_CTL, devctl,
&err);
brcmf_sdiod_regwb(sdiodev, SBSDIO_FUNC1_MESBUSYCTRL,
CY_43455_MESBUSYCTRL, &err);
break;
case SDIO_DEVICE_ID_BROADCOM_4339:
brcmf_sdiod_regwb(sdiodev,
@ -4312,6 +4386,19 @@ static void brcmf_sdio_firmware_callback(struct device *dev, int err,
brcmf_sdiod_regwb(sdiodev,
SBSDIO_FUNC1_MESBUSYCTRL, CY_4339_MESBUSYCTRL, &err);
break;
case SDIO_DEVICE_ID_BROADCOM_4359:
brcmf_dbg(INFO, "set F2 watermark to 0x%x*4 bytes\n",
CY_4359_F2_WATERMARK);
brcmf_sdiod_regwb(sdiodev, SBSDIO_WATERMARK,
CY_4359_F2_WATERMARK, &err);
devctl = brcmf_sdiod_regrb(sdiodev, SBSDIO_DEVICE_CTL,
&err);
devctl |= SBSDIO_DEVCTL_F2WM_ENAB;
brcmf_sdiod_regwb(sdiodev, SBSDIO_DEVICE_CTL, devctl,
&err);
brcmf_sdiod_regwb(sdiodev, SBSDIO_FUNC1_MESBUSYCTRL,
CY_4359_F1_MESBUSYCTRL, &err);
break;
default:
brcmf_sdiod_regwb(sdiodev, SBSDIO_WATERMARK, DEFAULT_F2_WATERMARK, &err);
break;
@ -4355,6 +4442,12 @@ static void brcmf_sdio_firmware_callback(struct device *dev, int err,
brcmf_err("dongle is not responding\n");
goto fail;
}
} else {
/* For ULP, after firmware redownload complete
* set ULP state to IDLE
*/
if (bus->sdiodev->fmac_ulp.ulp_state == FMAC_ULP_TRIGGERED)
bus->sdiodev->fmac_ulp.ulp_state = FMAC_ULP_IDLE;
}
return;
@ -4373,9 +4466,6 @@ struct brcmf_sdio *brcmf_sdio_probe(struct brcmf_sdio_dev *sdiodev)
struct brcmf_sdio *bus;
struct workqueue_struct *wq;
printk("[--- SBA ---] brcmf_sdio_probe ENTER !!\n");
brcmf_dbg(TRACE, "Enter\n");
/* Allocate private bus interface state */
@ -4547,7 +4637,17 @@ void brcmf_sdio_remove(struct brcmf_sdio *bus)
* necessary cores.
*/
msleep(20);
brcmf_chip_set_passive(bus->ci);
if (bus->sdiodev->fmac_ulp.ulp_state ==
FMAC_ULP_ENTRY_RECV) {
brcmf_chip_ulp_reset_lhl_regs(bus->ci);
brcmf_chip_reset_pmu_regs(bus->ci);
} else {
brcmf_chip_set_passive(bus->ci);
}
/* Reset the PMU, backplane and all the
* cores by using the PMUWatchdogCounter.
*/
brcmf_chip_reset_watchdog(bus->ci);
brcmf_sdio_clkctl(bus, CLK_NONE, false);
sdio_release_host(bus->sdiodev->func[1]);
}
@ -4603,3 +4703,41 @@ int brcmf_sdio_sleep(struct brcmf_sdio *bus, bool sleep)
return ret;
}
/* Check F2 Ready bit before sending data to Firmware */
static int
brcmf_sdio_f2_ready(struct brcmf_sdio *bus)
{
int ret = -1;
int iordy_status = 0;
sdio_claim_host(bus->sdiodev->func[1]);
/* Read the status of IOR2 */
iordy_status = brcmf_sdiod_regrb(bus->sdiodev,
SDIO_CCCR_IORx,
NULL);
sdio_release_host(bus->sdiodev->func[1]);
ret = iordy_status & SDIO_FUNC_ENABLE_2;
return ret;
}
static int brcmf_ulp_event_notify(struct brcmf_if *ifp,
const struct brcmf_event_msg *evtmsg,
void *data)
{
int err = 0;
struct brcmf_bus *bus_if = ifp->drvr->bus_if;
struct brcmf_sdio_dev *sdiodev;
struct brcmf_sdio *bus;
struct brcmf_ulp_event *ulp_event = (struct brcmf_ulp_event *)data;
sdiodev = bus_if->bus_priv.sdio;
bus = sdiodev->bus;
brcmf_dbg(ULP, "Chip went to DS1 state : action %d\n",
ulp_event->ulp_dongle_action);
if (ulp_event->ulp_dongle_action == FMAC_ULP_ENTRY)
bus->sdiodev->fmac_ulp.ulp_state = FMAC_ULP_ENTRY_RECV;
return err;
}

View File

@ -109,6 +109,7 @@
#define SBSDIO_FUNC1_MESBUSYCTRL 0x1001D
/* Enable busy capability for MES access */
#define SBSDIO_MESBUSYCTRL_ENAB 0x80
/* Sdio Core Rev 12 */
#define SBSDIO_FUNC1_WAKEUPCTRL 0x1001E
#define SBSDIO_FUNC1_WCTRL_ALPWAIT_MASK 0x1
@ -184,12 +185,30 @@ struct ulp_shm_info {
u32 m_ulp_ctrl_sdio;
u32 m_ulp_wakeevt_ind;
u32 m_ulp_wakeind;
u32 m_ulp_phytxblk;
};
struct fmac_ulp {
/* FMAC ULP state machine */
#define FMAC_ULP_IDLE (0)
#define FMAC_ULP_ENTRY_RECV (1)
#define FMAC_ULP_TRIGGERED (2)
/* BRCMF_E_ULP event data */
#define FMAC_ULP_EVENT_VERSION 1
#define FMAC_ULP_DISABLE_CONSOLE 1 /* Disable console */
#define FMAC_ULP_UCODE_DOWNLOAD 2 /* Download ULP ucode file */
#define FMAC_ULP_ENTRY 3 /* Inform ulp entry to Host */
struct brcmf_ulp {
uint ulp_state;
struct ulp_shm_info ulp_shm_offset;
};
struct brcmf_ulp_event {
u16 version;
u16 ulp_dongle_action;
};
struct brcmf_sdio_dev {
struct sdio_func *func[SDIO_MAX_FUNCS];
u8 num_funcs; /* Supported funcs on client */
@ -214,7 +233,7 @@ struct brcmf_sdio_dev {
bool wowl_enabled;
enum brcmf_sdiod_state state;
struct brcmf_sdiod_freezer *freezer;
struct fmac_ulp shm_ulp;
struct brcmf_ulp fmac_ulp;
bool ulp;
};
@ -395,6 +414,7 @@ void brcmf_sdio_trigger_dpc(struct brcmf_sdio *bus);
#define M_DS1_CTRL_SDIO(ptr) ((ptr).ulp_shm_offset.m_ulp_ctrl_sdio)
#define M_WAKEEVENT_IND(ptr) ((ptr).ulp_shm_offset.m_ulp_wakeevt_ind)
#define M_ULP_WAKE_IND(ptr) ((ptr).ulp_shm_offset.m_ulp_wakeind)
#define M_DS1_PHYTX_ERR_BLK(ptr) ((ptr).ulp_shm_offset.m_ulp_phytxblk)
#define D11_BASE_ADDR 0x18001000
#define D11_AXI_BASE_ADDR 0xE8000000
@ -408,30 +428,61 @@ void brcmf_sdio_trigger_dpc(struct brcmf_sdio *bus);
#define D11_MACCONTROL_REG D11REG_ADDR(0x120)
#define D11_MACCONTROL_REG_WAKE 0x4000000
/* Following are the offsets in M_DRVR_UCODE_IF_PTR block. Start address of
* M_DRVR_UCODE_IF_PTR block is present in M_DRVR_UCODE_IF_PTR.
*/
/* HUDI Sequence SHM bits */
#define C_DS1_CTRL_SDIO_DS1_SLEEP 0x1
#define C_DS1_CTRL_SDIO_MAC_ON 0x2
#define C_DS1_CTRL_SDIO_RADIO_PHY_ON 0x4
#define C_DS1_CTRL_SDIO_DS1_EXIT 0x8
#define C_DS1_CTRL_PROC_DONE 0x100
#define C_DS1_CTRL_REQ_VALID 0x200
/* M_ULP_WAKE_IND bits */
#define ULP_WAKE_IND_WATCHDOG_EXP 0x1
#define ULP_WAKE_IND_FCBS_ERROR 0x2
#define ULP_WAKE_IND_RE_TRANSMIT_ERR 0x4
#define ULP_WAKE_IND_HOST_WKUP 0x8
#define ULP_WAKE_IND_INVALID_FCBS_BLK 0x10
/* M_ULP_WAKEIND bits */
#define C_WATCHDOG_EXPIRY BIT(0)
#define C_FCBS_ERROR BIT(1)
#define C_RETX_FAILURE BIT(2)
#define C_HOST_WAKEUP BIT(3)
#define C_INVALID_FCBS_BLOCK BIT(4)
#define C_HUDI_DS1_EXIT BIT(5)
#define C_LOB_SLEEP BIT(6)
#define C_DS1_PHY_TXERR BIT(9)
#define C_DS1_WAKE_TIMER BIT(10)
#define C_DS1_CTRL_SDIO_DS1_SLEEP 0x1
#define C_DS1_CTRL_SDIO_MAC_ON 0x2
#define C_DS1_CTRL_SDIO_RADIO_PHY_ON 0x4
#define C_DS1_CTRL_SDIO_DS1_EXIT 0x8
#define C_DS1_CTRL_PROC_DONE 0x100
#define C_DS1_CTRL_REQ_VALID 0x200
#define D11SHM_WR(sdh, offset, val, ret) \
brcmf_sdiod_regwl(sdh, D11SHM_ADDR(offset), val, ret)
#define PHYTX_ERR_BLK_SIZE 18
#define D11SHM_FIRST2BYTE_MASK 0xFFFF0000
#define D11SHM_SECOND2BYTE_MASK 0x0000FFFF
#define D11SHM_2BYTE_SHIFT 16
#define D11SHM_RD(sdh, offset, ret) \
brcmf_sdiod_regrl(sdh, D11SHM_ADDR(offset), ret)
/* SHM Read is motified based on SHM 4 byte alignment as SHM size is 2 bytes and
* 2 byte is currently not working on FMAC
* If SHM address is not 4 byte aligned, then right shift by 16
* otherwise, mask the first two MSB bytes
* Suppose data in address 7260 is 0x440002 and it is 4 byte aligned
* Correct SHM value is 0x2 for this SHM offset and next SHM value is 0x44
*/
#define D11SHM_RDW(sdh, offset, ret) \
((offset % 4) ? \
(brcmf_sdiod_regrl(sdh, D11SHM_ADDR(offset), ret) \
>> D11SHM_2BYTE_SHIFT) : \
(brcmf_sdiod_regrl(sdh, D11SHM_ADDR(offset), ret) \
& D11SHM_SECOND2BYTE_MASK))
/* SHM is of size 2 bytes, 4 bytes write will overwrite other SHM's
* First read 4 bytes and then clear the required two bytes based on
* 4 byte alignment, then update the required value and write the
* 4 byte value now
*/
#define D11SHM_WR(sdh, offset, val, mask, ret) \
do { \
if ((offset) % 4) \
val = (val & D11SHM_SECOND2BYTE_MASK) | \
((mask) << D11SHM_2BYTE_SHIFT); \
else \
val = (mask) | (val & D11SHM_FIRST2BYTE_MASK); \
brcmf_sdiod_regwl(sdh, D11SHM_ADDR(offset), val, ret); \
} while (0)
#define D11REG_WR(sdh, addr, val, ret) \
brcmf_sdiod_regwl(sdh, addr, val, ret)

View File

@ -30,6 +30,7 @@
#include "core.h"
#include "common.h"
#include "bcdc.h"
#include "cfg80211.h"
#define IOCTL_RESP_TIMEOUT msecs_to_jiffies(2000)
@ -103,7 +104,6 @@ static struct brcmf_firmware_mapping brcmf_usb_fwnames[] = {
*/
#define DL_IMAGE_TOOBIG 7 /* firmware image too big */
struct trx_header_le {
__le32 magic; /* "HDR0" */
__le32 len; /* Length of file including header */
@ -160,7 +160,7 @@ struct brcmf_usbdev_info {
struct usb_device *usbdev;
struct device *dev;
struct mutex dev_init_lock;
struct completion fw_downloaded;
int ctl_in_pipe, ctl_out_pipe;
struct urb *ctl_urb; /* URB for control endpoint */
@ -175,7 +175,6 @@ struct brcmf_usbdev_info {
struct urb *bulk_urb; /* used for FW download */
bool wowl_enabled;
struct brcmf_mp_device *settings;
};
@ -323,27 +322,44 @@ static int brcmf_usb_tx_ctlpkt(struct device *dev, u8 *buf, u32 len)
int err = 0;
int timeout = 0;
struct brcmf_usbdev_info *devinfo = brcmf_usb_get_businfo(dev);
struct usb_interface *intf = to_usb_interface(dev);
brcmf_dbg(USB, "Enter\n");
if (devinfo->bus_pub.state != BRCMFMAC_USB_STATE_UP)
return -EIO;
if (test_and_set_bit(0, &devinfo->ctl_op))
return -EIO;
err = usb_autopm_get_interface(intf);
if (err)
goto out;
if (devinfo->bus_pub.state != BRCMFMAC_USB_STATE_UP) {
err = -EIO;
goto fail;
}
if (test_and_set_bit(0, &devinfo->ctl_op)) {
err = -EIO;
goto fail;
}
devinfo->ctl_completed = false;
err = brcmf_usb_send_ctl(devinfo, buf, len);
if (err) {
brcmf_err("fail %d bytes: %d\n", err, len);
clear_bit(0, &devinfo->ctl_op);
return err;
goto fail;
}
timeout = brcmf_usb_ioctl_resp_wait(devinfo);
clear_bit(0, &devinfo->ctl_op);
if (!timeout) {
brcmf_err("Txctl wait timed out\n");
usb_kill_urb(devinfo->ctl_urb);
clear_bit(0, &devinfo->ctl_op);
err = -EIO;
goto fail;
}
clear_bit(0, &devinfo->ctl_op);
fail:
usb_autopm_put_interface(intf);
out:
return err;
}
@ -352,32 +368,48 @@ static int brcmf_usb_rx_ctlpkt(struct device *dev, u8 *buf, u32 len)
int err = 0;
int timeout = 0;
struct brcmf_usbdev_info *devinfo = brcmf_usb_get_businfo(dev);
struct usb_interface *intf = to_usb_interface(dev);
brcmf_dbg(USB, "Enter\n");
if (devinfo->bus_pub.state != BRCMFMAC_USB_STATE_UP)
return -EIO;
if (test_and_set_bit(0, &devinfo->ctl_op))
return -EIO;
err = usb_autopm_get_interface(intf);
if (err)
goto out;
if (devinfo->bus_pub.state != BRCMFMAC_USB_STATE_UP) {
err = -EIO;
goto fail;
}
if (test_and_set_bit(0, &devinfo->ctl_op)) {
err = -EIO;
goto fail;
}
devinfo->ctl_completed = false;
err = brcmf_usb_recv_ctl(devinfo, buf, len);
if (err) {
brcmf_err("fail %d bytes: %d\n", err, len);
clear_bit(0, &devinfo->ctl_op);
return err;
goto fail;
}
timeout = brcmf_usb_ioctl_resp_wait(devinfo);
err = devinfo->ctl_urb_status;
clear_bit(0, &devinfo->ctl_op);
if (!timeout) {
brcmf_err("rxctl wait timed out\n");
usb_kill_urb(devinfo->ctl_urb);
clear_bit(0, &devinfo->ctl_op);
err = -EIO;
goto fail;
}
clear_bit(0, &devinfo->ctl_op);
fail:
usb_autopm_put_interface(intf);
if (!err)
return devinfo->ctl_urb_actual_length;
else
return err;
out:
return err;
}
static struct brcmf_usbreq *brcmf_usb_deq(struct brcmf_usbdev_info *devinfo,
@ -515,10 +547,12 @@ static void brcmf_usb_rx_complete(struct urb *urb)
return;
}
if (devinfo->bus_pub.state == BRCMFMAC_USB_STATE_UP) {
if (devinfo->bus_pub.state == BRCMFMAC_USB_STATE_UP ||
devinfo->bus_pub.state == BRCMFMAC_USB_STATE_SLEEP) {
skb_put(skb, urb->actual_length);
brcmf_rx_frame(devinfo->dev, skb, true);
brcmf_usb_rx_refill(devinfo, req);
usb_mark_last_busy(urb->dev);
} else {
brcmu_pkt_buf_free_skb(skb);
brcmf_usb_enq(devinfo, &devinfo->rx_freeq, req, NULL);
@ -604,6 +638,11 @@ static int brcmf_usb_tx(struct device *dev, struct sk_buff *skb)
struct brcmf_usbreq *req;
int ret;
unsigned long flags;
struct usb_interface *intf = to_usb_interface(dev);
ret = usb_autopm_get_interface(intf);
if (ret)
goto out;
brcmf_dbg(USB, "Enter, skb=%p\n", skb);
if (devinfo->bus_pub.state != BRCMFMAC_USB_STATE_UP) {
@ -619,6 +658,8 @@ static int brcmf_usb_tx(struct device *dev, struct sk_buff *skb)
goto fail;
}
if (devinfo->bus_pub.bus->allow_skborphan)
skb_orphan(skb);
req->skb = skb;
req->devinfo = devinfo;
usb_fill_bulk_urb(req->urb, devinfo->usbdev, devinfo->tx_pipe,
@ -642,9 +683,10 @@ static int brcmf_usb_tx(struct device *dev, struct sk_buff *skb)
devinfo->tx_flowblock = true;
}
spin_unlock_irqrestore(&devinfo->tx_flowblock_lock, flags);
return 0;
fail:
usb_autopm_put_interface(intf);
out:
return ret;
}
@ -1002,20 +1044,32 @@ static int
brcmf_usb_fw_download(struct brcmf_usbdev_info *devinfo)
{
int err;
struct usb_interface *intf;
brcmf_dbg(USB, "Enter\n");
if (devinfo == NULL)
return -ENODEV;
if (!devinfo) {
err = -ENODEV;
goto out;
}
if (!devinfo->image) {
brcmf_err("No firmware!\n");
return -ENOENT;
err = -ENOENT;
goto out;
}
intf = to_usb_interface(devinfo->dev);
err = usb_autopm_get_interface(intf);
if (err)
goto out;
err = brcmf_usb_dlstart(devinfo,
(u8 *)devinfo->image, devinfo->image_len);
if (err == 0)
err = brcmf_usb_dlrun(devinfo);
usb_autopm_put_interface(intf);
out:
return err;
}
@ -1116,18 +1170,6 @@ error:
return NULL;
}
static void brcmf_usb_wowl_config(struct device *dev, bool enabled)
{
struct brcmf_usbdev_info *devinfo = brcmf_usb_get_businfo(dev);
brcmf_dbg(USB, "Configuring WOWL, enabled=%d\n", enabled);
devinfo->wowl_enabled = enabled;
if (enabled)
device_set_wakeup_enable(devinfo->dev, true);
else
device_set_wakeup_enable(devinfo->dev, false);
}
static int brcmf_usb_get_fwname(struct device *dev, u32 chip, u32 chiprev,
u8 *fw_name)
{
@ -1150,7 +1192,6 @@ static const struct brcmf_bus_ops brcmf_usb_bus_ops = {
.stop = brcmf_usb_down,
.txctl = brcmf_usb_tx_ctlpkt,
.rxctl = brcmf_usb_rx_ctlpkt,
.wowl_config = brcmf_usb_wowl_config,
.get_fwname = brcmf_usb_get_fwname,
};
@ -1165,6 +1206,12 @@ static int brcmf_usb_bus_setup(struct brcmf_usbdev_info *devinfo)
return ret;
}
if (BRCMF_FWCON_ON()) {
ret = brcmf_fwlog_attach(devinfo->dev);
if (ret)
goto fail;
}
ret = brcmf_usb_up(devinfo->dev);
if (ret)
goto fail;
@ -1210,11 +1257,11 @@ static void brcmf_usb_probe_phase2(struct device *dev, int ret,
if (ret)
goto error;
mutex_unlock(&devinfo->dev_init_lock);
complete(&devinfo->fw_downloaded);
return;
error:
brcmf_dbg(TRACE, "failed: dev=%s, err=%d\n", dev_name(dev), ret);
mutex_unlock(&devinfo->dev_init_lock);
complete(&devinfo->fw_downloaded);
device_release_driver(dev);
}
@ -1243,6 +1290,7 @@ static int brcmf_usb_probe_cb(struct brcmf_usbdev_info *devinfo)
bus->ops = &brcmf_usb_bus_ops;
bus->proto_type = BRCMF_PROTO_BCDC;
bus->always_use_fws_queue = true;
bus->allow_skborphan = true;
#ifdef CONFIG_PM
bus->wowl_supported = true;
#endif
@ -1260,7 +1308,7 @@ static int brcmf_usb_probe_cb(struct brcmf_usbdev_info *devinfo)
if (ret)
goto fail;
/* we are done */
mutex_unlock(&devinfo->dev_init_lock);
complete(&devinfo->fw_downloaded);
return 0;
}
bus->chip = bus_pub->devid;
@ -1321,14 +1369,14 @@ brcmf_usb_probe(struct usb_interface *intf, const struct usb_device_id *id)
devinfo->usbdev = usb;
devinfo->dev = &usb->dev;
/* Take an init lock, to protect for disconnect while still loading.
* Necessary because of the asynchronous firmware load construction
*/
mutex_init(&devinfo->dev_init_lock);
mutex_lock(&devinfo->dev_init_lock);
/* Initialize fw downloaded completion and mark it as not complete */
init_completion(&devinfo->fw_downloaded);
usb_set_intfdata(intf, devinfo);
intf->needs_remote_wakeup = 1;
/* Check that the device supports only one configuration */
if (usb->descriptor.bNumConfigurations != 1) {
brcmf_err("Number of configurations: %d not supported\n",
@ -1403,7 +1451,7 @@ brcmf_usb_probe(struct usb_interface *intf, const struct usb_device_id *id)
return 0;
fail:
mutex_unlock(&devinfo->dev_init_lock);
complete(&devinfo->fw_downloaded);
kfree(devinfo);
usb_set_intfdata(intf, NULL);
return ret;
@ -1418,7 +1466,7 @@ brcmf_usb_disconnect(struct usb_interface *intf)
devinfo = (struct brcmf_usbdev_info *)usb_get_intfdata(intf);
if (devinfo) {
mutex_lock(&devinfo->dev_init_lock);
wait_for_completion(&devinfo->fw_downloaded);
/* Make sure that devinfo still exists. Firmware probe routines
* may have released the device and cleared the intfdata.
*/
@ -1439,13 +1487,25 @@ static int brcmf_usb_suspend(struct usb_interface *intf, pm_message_t state)
{
struct usb_device *usb = interface_to_usbdev(intf);
struct brcmf_usbdev_info *devinfo = brcmf_usb_get_businfo(&usb->dev);
struct brcmf_bus *bus;
struct brcmf_cfg80211_info *config;
int retry = BRCMF_PM_WAIT_MAXRETRY;
brcmf_dbg(USB, "Enter\n");
bus = devinfo->bus_pub.bus;
config = bus->drvr->config;
while (retry &&
config->pm_state == BRCMF_CFG80211_PM_STATE_SUSPENDING) {
usleep_range(10000, 20000);
retry--;
}
if (!retry && config->pm_state == BRCMF_CFG80211_PM_STATE_SUSPENDING)
brcmf_err("timed out wait for cfg80211 suspended\n");
devinfo->bus_pub.state = BRCMFMAC_USB_STATE_SLEEP;
if (devinfo->wowl_enabled)
brcmf_cancel_all_urbs(devinfo);
else
brcmf_detach(&usb->dev);
brcmf_cancel_all_urbs(devinfo);
device_set_wakeup_enable(devinfo->dev, true);
return 0;
}
@ -1458,11 +1518,9 @@ static int brcmf_usb_resume(struct usb_interface *intf)
struct brcmf_usbdev_info *devinfo = brcmf_usb_get_businfo(&usb->dev);
brcmf_dbg(USB, "Enter\n");
if (!devinfo->wowl_enabled)
return brcmf_usb_bus_setup(devinfo);
devinfo->bus_pub.state = BRCMFMAC_USB_STATE_UP;
brcmf_usb_rx_fill_all(devinfo);
device_set_wakeup_enable(devinfo->dev, false);
return 0;
}
@ -1510,6 +1568,7 @@ static struct usb_driver brcmf_usbdrvr = {
.suspend = brcmf_usb_suspend,
.resume = brcmf_usb_resume,
.reset_resume = brcmf_usb_reset_resume,
.supports_autosuspend = true,
.disable_hub_initiated_lpm = 1,
};

View File

@ -118,6 +118,56 @@ exit:
return ret;
}
s32
brcmf_wiphy_phy_temp_evt_handler(struct brcmf_if *ifp,
const struct brcmf_event_msg *e, void *data)
{
struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
struct wiphy *wiphy = cfg_to_wiphy(cfg);
struct sk_buff *skb;
struct nlattr *phy_temp_data;
u32 version, temp, tempdelta;
struct brcmf_phy_temp_evt *phy_temp_evt;
phy_temp_evt = (struct brcmf_phy_temp_evt *)data;
version = le32_to_cpu(phy_temp_evt->version);
temp = le32_to_cpu(phy_temp_evt->temp);
tempdelta = le32_to_cpu(phy_temp_evt->tempdelta);
skb = cfg80211_vendor_event_alloc(wiphy, NULL,
sizeof(*phy_temp_evt),
BRCMF_VNDR_EVTS_PHY_TEMP,
GFP_KERNEL);
if (!skb) {
brcmf_dbg(EVENT, "NO MEM: can't allocate skb for vendor PHY_TEMP_EVENT\n");
return -ENOMEM;
}
phy_temp_data = nla_nest_start(skb, NL80211_ATTR_VENDOR_EVENTS);
if (!phy_temp_data) {
nla_nest_cancel(skb, phy_temp_data);
kfree_skb(skb);
brcmf_dbg(EVENT, "skb could not nest vendor attributes\n");
return -EMSGSIZE;
}
if (nla_put_u32(skb, BRCMF_NLATTR_VERS, version) ||
nla_put_u32(skb, BRCMF_NLATTR_PHY_TEMP, temp) ||
nla_put_u32(skb, BRCMF_NLATTR_PHY_TEMPDELTA, tempdelta)) {
kfree_skb(skb);
brcmf_dbg(EVENT, "NO ROOM in skb for vendor PHY_TEMP_EVENT\n");
return -EMSGSIZE;
}
nla_nest_end(skb, phy_temp_data);
cfg80211_vendor_event(skb, GFP_KERNEL);
return 0;
}
const struct wiphy_vendor_command brcmf_vendor_cmds[] = {
{
{
@ -129,3 +179,10 @@ const struct wiphy_vendor_command brcmf_vendor_cmds[] = {
.doit = brcmf_cfg80211_vndr_cmds_dcmd_handler
},
};
const struct nl80211_vendor_cmd_info brcmf_vendor_events[] = {
{
.vendor_id = BROADCOM_OUI,
.subcmd = BRCMF_VNDR_EVTS_PHY_TEMP,
},
};

View File

@ -25,6 +25,11 @@ enum brcmf_vndr_cmds {
BRCMF_VNDR_CMDS_LAST
};
enum brcmf_vndr_evts {
BRCMF_VNDR_EVTS_PHY_TEMP,
BRCMF_VNDR_EVTS_LAST
};
/**
* enum brcmf_nlattrs - nl80211 message attributes
*
@ -36,11 +41,21 @@ enum brcmf_nlattrs {
BRCMF_NLATTR_LEN,
BRCMF_NLATTR_DATA,
BRCMF_NLATTR_VERS,
BRCMF_NLATTR_PHY_TEMP,
BRCMF_NLATTR_PHY_TEMPDELTA,
__BRCMF_NLATTR_AFTER_LAST,
BRCMF_NLATTR_MAX = __BRCMF_NLATTR_AFTER_LAST - 1
};
/* structure of event sent up by firmware: is this the right place for it? */
struct brcmf_phy_temp_evt {
__le32 version;
__le32 temp;
__le32 tempdelta;
} __packed;
/**
* struct brcmf_vndr_dcmd_hdr - message header for cfg80211 vendor command dcmd
* support
@ -60,5 +75,9 @@ struct brcmf_vndr_dcmd_hdr {
};
extern const struct wiphy_vendor_command brcmf_vendor_cmds[];
extern const struct nl80211_vendor_cmd_info brcmf_vendor_events[];
s32 brcmf_wiphy_phy_temp_evt_handler(struct brcmf_if *ifp,
const struct brcmf_event_msg *e,
void *data);
#endif /* _vendor_h_ */

View File

@ -74,6 +74,7 @@
/* PCIE Device IDs */
#define BRCM_PCIE_4350_DEVICE_ID 0x43a3
#define BRCM_PCIE_4354_DEVICE_ID 0x43df
#define BRCM_PCIE_4354_RAW_DEVICE_ID 0x4354
#define BRCM_PCIE_4355_DEVICE_ID 0x4355
#define BRCM_PCIE_4356_DEVICE_ID 0x43ec
#define BRCM_PCIE_43567_DEVICE_ID 0x43d3

View File

@ -240,6 +240,8 @@ static inline bool ac_bitmap_tst(u8 bitmap, int prec)
#define WPA2_AUTH_FT 0x4000 /* Fast BSS Transition */
#define WPA2_AUTH_PSK_SHA256 0x8000 /* PSK with SHA256 key derivation */
#define WPA3_AUTH_SAE_PSK 0x40000 /* SAE with 4-way handshake */
#define DOT11_DEFAULT_RTS_LEN 2347
#define DOT11_DEFAULT_FRAG_LEN 2346

View File

@ -225,8 +225,197 @@ struct chipcregs {
u32 PAD[3];
u32 retention_grpidx; /* 0x680 */
u32 retention_grpctl; /* 0x684 */
u32 PAD[94];
u16 sromotp[768];
u32 mac_res_req_timer; /* 0x688 */
u32 mac_res_req_mask; /* 0x68c */
u32 PAD[18];
u32 pmucontrol_ext; /* 0x6d8 */
u32 slowclkperiod; /* 0x6dc */
u32 PAD[8];
u32 pmuintmask0; /* 0x700 */
u32 pmuintmask1; /* 0x704 */
u32 PAD[14];
u32 pmuintstatus; /* 0x740 */
u32 extwakeupstatus; /* 0x744 */
u32 watchdog_res_mask; /* 0x748 */
u32 swscratch; /* 0x750 */
u32 PAD[3];
u32 extwakemask[2]; /* 0x760-0x764 */
u32 PAD[2];
u32 extwakereqmask[2]; /* 0x770-0x774 */
u32 PAD[2];
u32 pmuintctrl0; /* 0x780 */
u32 pmuintctrl1; /* 0x784 */
u32 PAD[2];
u32 extwakectrl[2]; /* 0x790 */
};
#define CHIPGCIREGOFFS(field) offsetof(struct chipgciregs, field)
struct chipgciregs {
u32 gci_corecaps0; /* 0x000 */
u32 gci_corecaps1; /* 0x004 */
u32 gci_corecaps2; /* 0x008 */
u32 gci_corectrl; /* 0x00c */
u32 gci_corestat; /* 0x010 */
u32 gci_intstat; /* 0x014 */
u32 gci_intmask; /* 0x018 */
u32 gci_wakemask; /* 0x01c */
u32 gci_levelintstat; /* 0x020 */
u32 gci_eventintstat; /* 0x024 */
u32 gci_wakelevelintstat; /* 0x028 */
u32 gci_wakeeventintstat; /* 0x02c */
u32 semaphoreintstatus; /* 0x030 */
u32 semaphoreintmask; /* 0x034 */
u32 semaphorerequest; /* 0x038 */
u32 semaphorereserve; /* 0x03c */
u32 gci_indirect_addr; /* 0x040 */
u32 gci_gpioctl; /* 0x044 */
u32 gci_gpiostatus; /* 0x048 */
u32 gci_gpiomask; /* 0x04c */
u32 eventsummary; /* 0x050 */
u32 gci_miscctl; /* 0x054 */
u32 gci_gpiointmask; /* 0x058 */
u32 gci_gpiowakemask; /* 0x05c */
u32 gci_input[32]; /* 0x060 */
u32 gci_event[32]; /* 0x0e0 */
u32 gci_output[4]; /* 0x160 */
u32 gci_control_0; /* 0x170 */
u32 gci_control_1; /* 0x174 */
u32 gci_intpolreg; /* 0x178 */
u32 gci_levelintmask; /* 0x17c */
u32 gci_eventintmask; /* 0x180 */
u32 wakelevelintmask; /* 0x184 */
u32 wakeeventintmask; /* 0x188 */
u32 hwmask; /* 0x18c */
u32 PAD;
u32 gci_inbandeventintmask; /* 0x194 */
u32 PAD;
u32 gci_inbandeventstatus; /* 0x19c */
u32 gci_seciauxtx; /* 0x1a0 */
u32 gci_seciauxrx; /* 0x1a4 */
u32 gci_secitx_datatag; /* 0x1a8 */
u32 gci_secirx_datatag; /* 0x1ac */
u32 gci_secitx_datamask; /* 0x1b0 */
u32 gci_seciusef0tx_reg; /* 0x1b4 */
u32 gci_secif0tx_offset; /* 0x1b8 */
u32 gci_secif0rx_offset; /* 0x1bc */
u32 gci_secif1tx_offset; /* 0x1c0 */
u32 gci_rxfifo_common_ctrl; /* 0x1c4 */
u32 gci_rxfifoctrl; /* 0x1c8 */
u32 gci_hw_sema_status; /* 0x1cc */
u32 gci_seciuartescval; /* 0x1d0 */
u32 gic_seciuartautobaudctr; /* 0x1d4 */
u32 gci_secififolevel; /* 0x1d8 */
u32 gci_seciuartdata; /* 0x1dc */
u32 gci_secibauddiv; /* 0x1e0 */
u32 gci_secifcr; /* 0x1e4 */
u32 gci_secilcr; /* 0x1e8 */
u32 gci_secimcr; /* 0x1ec */
u32 gci_secilsr; /* 0x1f0 */
u32 gci_secimsr; /* 0x1f4 */
u32 gci_baudadj; /* 0x1f8 */
u32 gci_inbandintmask; /* 0x1fc */
u32 gci_chipctrl; /* 0x200 */
u32 gci_chipsts; /* 0x204 */
u32 gci_gpioout; /* 0x208 */
u32 gci_gpioout_read; /* 0x20C */
u32 gci_mpwaketx; /* 0x210 */
u32 gci_mpwakedetect; /* 0x214 */
u32 gci_seciin_ctrl; /* 0x218 */
u32 gci_seciout_ctrl; /* 0x21C */
u32 gci_seciin_auxfifo_en; /* 0x220 */
u32 gci_seciout_txen_txbr; /* 0x224 */
u32 gci_seciin_rxbrstatus; /* 0x228 */
u32 gci_seciin_rxerrstatus; /* 0x22C */
u32 gci_seciin_fcstatus; /* 0x230 */
u32 gci_seciout_txstatus; /* 0x234 */
u32 gci_seciout_txbrstatus; /* 0x238 */
u32 wlan_mem_info; /* 0x23C */
u32 wlan_bankxinfo; /* 0x240 */
u32 bt_smem_select; /* 0x244 */
u32 bt_smem_stby; /* 0x248 */
u32 bt_smem_status; /* 0x24C */
u32 wlan_bankxactivepda; /* 0x250 */
u32 wlan_bankxsleeppda; /* 0x254 */
u32 wlan_bankxkill; /* 0x258 */
u32 PAD[41];
u32 gci_chipid; /* 0x300 */
u32 PAD[3];
u32 otpstatus; /* 0x310 */
u32 otpcontrol; /* 0x314 */
u32 otpprog; /* 0x318 */
u32 otplayout; /* 0x31c */
u32 otplayoutextension; /* 0x320 */
u32 otpcontrol1; /* 0x324 */
u32 otpprogdata; /* 0x328 */
u32 PAD[52];
u32 otpECCstatus; /* 0x3FC */
u32 PAD[512];
u32 lhl_core_capab_adr; /* 0xC00 */
u32 lhl_main_ctl_adr; /* 0xC04 */
u32 lhl_pmu_ctl_adr; /* 0xC08 */
u32 lhl_extlpo_ctl_adr; /* 0xC0C */
u32 lpo_ctl_adr; /* 0xC10 */
u32 lhl_lpo2_ctl_adr; /* 0xC14 */
u32 lhl_osc32k_ctl_adr; /* 0xC18 */
u32 lhl_clk_status_adr; /* 0xC1C */
u32 lhl_clk_det_ctl_adr; /* 0xC20 */
u32 lhl_clk_sel_adr; /* 0xC24 */
u32 hidoff_cnt_adr[2]; /* 0xC28-0xC2C */
u32 lhl_autoclk_ctl_adr; /* 0xC30 */
u32 PAD;
u32 lhl_hibtim_adr; /* 0xC38 */
u32 lhl_wl_ilp_val_adr; /* 0xC3C */
u32 lhl_wl_armtim0_intrp_adr; /* 0xC40 */
u32 lhl_wl_armtim0_st_adr; /* 0xC44 */
u32 lhl_wl_armtim0_adr; /* 0xC48 */
u32 PAD[9];
u32 lhl_wl_mactim0_intrp_adr; /* 0xC70 */
u32 lhl_wl_mactim0_st_adr; /* 0xC74 */
u32 lhl_wl_mactim_int0_adr; /* 0xC78 */
u32 lhl_wl_mactim_frac0_adr; /* 0xC7C */
u32 lhl_wl_mactim1_intrp_adr; /* 0xC80 */
u32 lhl_wl_mactim1_st_adr; /* 0xC84 */
u32 lhl_wl_mactim_int1_adr; /* 0xC88 */
u32 lhl_wl_mactim_frac1_adr; /* 0xC8C */
u32 PAD[8];
u32 gpio_int_en_port_adr[4]; /* 0xCB0-0xCBC */
u32 gpio_int_st_port_adr[4]; /* 0xCC0-0xCCC */
u32 gpio_ctrl_iocfg_p_adr[64]; /* 0xCD0-0xDCC */
u32 gpio_gctrl_iocfg_p0_p39_adr; /* 0xDD0 */
u32 gpio_gdsctrl_iocfg_p0_p25_p30_p39_adr; /* 0xDD4 */
u32 gpio_gdsctrl_iocfg_p26_p29_adr; /* 0xDD8 */
u32 PAD[8];
u32 lhl_gpio_din0_adr; /* 0xDFC */
u32 lhl_gpio_din1_adr; /* 0xE00 */
u32 lhl_wkup_status_adr; /* 0xE04 */
u32 lhl_ctl_adr; /* 0xE08 */
u32 lhl_adc_ctl_adr; /* 0xE0C */
u32 lhl_qdxyz_in_dly_adr; /* 0xE10 */
u32 lhl_optctl_adr; /* 0xE14 */
u32 lhl_optct2_adr; /* 0xE18 */
u32 lhl_scanp_cntr_init_val_adr; /* 0xE1C */
u32 lhl_opt_togg_val_adr[6]; /* 0xE20-0xE34 */
u32 lhl_optx_smp_val_adr; /* 0xE38 */
u32 lhl_opty_smp_val_adr; /* 0xE3C */
u32 lhl_optz_smp_val_adr; /* 0xE40 */
u32 lhl_hidoff_keepstate_adr[3]; /* 0xE44-0xE4C */
u32 lhl_bt_slmboot_ctl0_adr[4]; /* 0xE50-0xE5C */
u32 lhl_wl_fw_ctl; /* 0xE60 */
u32 lhl_wl_hw_ctl_adr[2]; /* 0xE64-0xE68 */
u32 lhl_bt_hw_ctl_adr; /* 0xE6C */
u32 lhl_top_pwrseq_en_adr; /* 0xE70 */
u32 lhl_top_pwrdn_ctl_adr; /* 0xE74 */
u32 lhl_top_pwrup_ctl_adr; /* 0xE78 */
u32 lhl_top_pwrseq_ctl_adr; /* 0xE7C */
u32 lhl_top_pwrdn2_ctl_adr; /* 0xE80 */
u32 lhl_top_pwrup2_ctl_adr; /* 0xE84 */
u32 wpt_regon_intrp_cfg_adr; /* 0xE88 */
u32 bt_regon_intrp_cfg_adr; /* 0xE8C */
u32 wl_regon_intrp_cfg_adr; /* 0xE90 */
u32 regon_intrp_st_adr; /* 0xE94 */
u32 regon_intrp_en_adr; /* 0xE98 */
};
/* chipid */

View File

@ -2112,6 +2112,7 @@ enum ieee80211_key_len {
#define FILS_ERP_MAX_RRK_LEN 64
#define PMK_MAX_LEN 48
#define SAE_PASSWORD_MAX_LEN 128
/* Public action codes (IEEE Std 802.11-2016, 9.6.8.1, Table 9-307) */
enum ieee80211_pub_actioncode {

View File

@ -42,6 +42,7 @@
#define SDIO_DEVICE_ID_BROADCOM_43455 0xa9bf
#define SDIO_DEVICE_ID_BROADCOM_4354 0x4354
#define SDIO_DEVICE_ID_BROADCOM_4356 0x4356
#define SDIO_DEVICE_ID_BROADCOM_4359 0x4355
#define SDIO_DEVICE_ID_CYPRESS_4373 0x4373
#define SDIO_DEVICE_ID_CYPRESS_43012 43012

View File

@ -650,6 +650,9 @@ struct survey_info {
* CFG80211_MAX_WEP_KEYS WEP keys
* @wep_tx_key: key index (0..3) of the default TX static WEP key
* @psk: PSK (for devices supporting 4-way-handshake offload)
* @sae_pwd: password for SAE authentication (for devices supporting SAE
* offload)
* @sae_pwd_len: length of SAE password (for devices supporting SAE offload)
*/
struct cfg80211_crypto_settings {
u32 wpa_versions;
@ -664,6 +667,8 @@ struct cfg80211_crypto_settings {
struct key_params *wep_keys;
int wep_tx_key;
const u8 *psk;
const u8 *sae_pwd;
u16 sae_pwd_len;
};
/**
@ -5309,6 +5314,8 @@ static inline void cfg80211_testmode_event(struct sk_buff *skb, gfp_t gfp)
* not known. This value is used only if @status < 0 to indicate that the
* failure is due to a timeout and not due to explicit rejection by the AP.
* This value is ignored in other cases (@status >= 0).
* @authorized: Indicates whether the connection is ready to transport
* data packets.
*/
struct cfg80211_connect_resp_params {
int status;
@ -5326,6 +5333,7 @@ struct cfg80211_connect_resp_params {
size_t pmk_len;
const u8 *pmkid;
enum nl80211_timeout_reason timeout_reason;
bool authorized;
};
/**
@ -5506,6 +5514,23 @@ struct cfg80211_roam_info {
void cfg80211_roamed(struct net_device *dev, struct cfg80211_roam_info *info,
gfp_t gfp);
/**
* cfg80211_port_authorized - notify cfg80211 of successful security association
*
* @dev: network device
* @bssid: the BSSID of the AP
* @gfp: allocation flags
*
* This function should be called by a driver that supports 4 way handshake
* offload after a security association was successfully established (i.e.,
* the 4 way handshake was completed successfully). The call to this function
* should be preceded with a call to cfg80211_connect_result(),
* cfg80211_connect_done(), cfg80211_connect_bss() or cfg80211_roamed() to
* indicate the 802.11 association.
*/
void cfg80211_port_authorized(struct net_device *dev, const u8 *bssid,
gfp_t gfp);
/**
* cfg80211_disconnected - notify cfg80211 that connection was dropped
*

View File

@ -231,6 +231,15 @@
* use in a FILS shared key connection with PMKSA caching.
*/
/**
* DOC: SAE authentication offload
*
* By setting @NL80211_EXT_FEATURE_SAE_OFFLOAD flag drivers can indicate they
* support offloading SAE authentication for WPA3-Personal networks. In
* %NL80211_CMD_CONNECT the password for SAE should be specified using
* %NL80211_ATTR_SAE_PASSWORD.
*/
/**
* enum nl80211_commands - supported nl80211 commands
*
@ -569,13 +578,14 @@
* authentication/association or not receiving a response from the AP.
* Non-zero %NL80211_ATTR_STATUS_CODE value is indicated in that case as
* well to remain backwards compatible.
* @NL80211_CMD_ROAM: notifcation indicating the card/driver roamed by itself.
* When the driver roamed in a network that requires 802.1X authentication,
* %NL80211_ATTR_PORT_AUTHORIZED should be set if the 802.1X authentication
* was done by the driver or if roaming was done using Fast Transition
* protocol (in which case 802.1X authentication is not needed). If
* %NL80211_ATTR_PORT_AUTHORIZED is not set, user space is responsible for
* the 802.1X authentication.
* When establishing a security association, drivers that support 4 way
* handshake offload should send %NL80211_CMD_PORT_AUTHORIZED event when
* the 4 way handshake is completed successfully.
* @NL80211_CMD_ROAM: Notification indicating the card/driver roamed by itself.
* When a security association was established with the new AP (e.g. if
* the FT protocol was used for roaming or the driver completed the 4 way
* handshake), this event should be followed by an
* %NL80211_CMD_PORT_AUTHORIZED event.
* @NL80211_CMD_DISCONNECT: drop a given connection; also used to notify
* userspace that a connection was dropped by the AP or due to other
* reasons, for this the %NL80211_ATTR_DISCONNECTED_BY_AP and
@ -982,6 +992,12 @@
* @NL80211_CMD_DEL_PMK: For offloaded 4-Way handshake, delete the previously
* configured PMK for the authenticator address identified by
* &NL80211_ATTR_MAC.
* @NL80211_CMD_PORT_AUTHORIZED: An event that indicates that the 4 way
* handshake was completed successfully by the driver. The BSSID is
* specified with &NL80211_ATTR_MAC. Drivers that support 4 way handshake
* offload should send this event after indicating 802.11 association with
* &NL80211_CMD_CONNECT or &NL80211_CMD_ROAM. If the 4 way handshake failed
* &NL80211_CMD_DISCONNECT should be indicated instead.
*
* @NL80211_CMD_MAX: highest used command number
* @__NL80211_CMD_AFTER_LAST: internal use
@ -1185,6 +1201,8 @@ enum nl80211_commands {
NL80211_CMD_SET_PMK,
NL80211_CMD_DEL_PMK,
NL80211_CMD_PORT_AUTHORIZED,
/* add new commands above here */
/* used to define NL80211_CMD_MAX below */
@ -2139,6 +2157,36 @@ enum nl80211_commands {
* the driver or is not needed (because roaming used the Fast Transition
* protocol).
*
* @NL80211_ATTR_EXTERNAL_AUTH_ACTION: Identify the requested external
* authentication operation (u32 attribute with an
* &enum nl80211_external_auth_action value). This is used with the
* %NL80211_CMD_EXTERNAL_AUTH request event.
* @NL80211_ATTR_EXTERNAL_AUTH_SUPPORT: Flag attribute indicating that the user
* space supports external authentication. This attribute shall be used
* only with %NL80211_CMD_CONNECT request. The driver may offload
* authentication processing to user space if this capability is indicated
* in NL80211_CMD_CONNECT requests from the user space.
*
* @NL80211_ATTR_NSS: Station's New/updated RX_NSS value notified using this
* u8 attribute. This is used with %NL80211_CMD_STA_OPMODE_CHANGED.
*
* @NL80211_ATTR_TXQ_STATS: TXQ statistics (nested attribute, see &enum
* nl80211_txq_stats)
* @NL80211_ATTR_TXQ_LIMIT: Total packet limit for the TXQ queues for this phy.
* The smaller of this and the memory limit is enforced.
* @NL80211_ATTR_TXQ_MEMORY_LIMIT: Total memory memory limit (in bytes) for the
* TXQ queues for this phy. The smaller of this and the packet limit is
* enforced.
* @NL80211_ATTR_TXQ_QUANTUM: TXQ scheduler quantum (bytes). Number of bytes
* a flow is assigned on each round of the DRR scheduler.
* @NL80211_ATTR_HE_CAPABILITY: HE Capability information element (from
* association request when used with NL80211_CMD_NEW_STATION). Can be set
* only if %NL80211_STA_FLAG_WME is set.
*
* @NL80211_ATTR_SAE_PASSWORD: attribute for passing SAE password material. It
* is used with %NL80211_CMD_CONNECT to provide password for offloading
* SAE authentication for WPA3-Personal networks.
*
* @NUM_NL80211_ATTR: total number of nl80211_attrs available
* @NL80211_ATTR_MAX: highest attribute number currently defined
* @__NL80211_ATTR_AFTER_LAST: internal use
@ -2565,6 +2613,23 @@ enum nl80211_attrs {
NL80211_ATTR_PMKR0_NAME,
NL80211_ATTR_PORT_AUTHORIZED,
NL80211_ATTR_EXTERNAL_AUTH_ACTION,
NL80211_ATTR_EXTERNAL_AUTH_SUPPORT,
NL80211_ATTR_NSS,
NL80211_ATTR_ACK_SIGNAL,
NL80211_ATTR_CONTROL_PORT_OVER_NL80211,
NL80211_ATTR_TXQ_STATS,
NL80211_ATTR_TXQ_LIMIT,
NL80211_ATTR_TXQ_MEMORY_LIMIT,
NL80211_ATTR_TXQ_QUANTUM,
NL80211_ATTR_HE_CAPABILITY,
NL80211_ATTR_SAE_PASSWORD,
/* add attributes here, update the policy in nl80211.c */
__NL80211_ATTR_AFTER_LAST,
@ -3958,6 +4023,7 @@ enum nl80211_mfp {
enum nl80211_wpa_versions {
NL80211_WPA_VERSION_1 = 1 << 0,
NL80211_WPA_VERSION_2 = 1 << 1,
NL80211_WPA_VERSION_3 = 1 << 2,
};
/**
@ -4916,6 +4982,40 @@ enum nl80211_feature_flags {
* handshake with 802.1X in station mode (will pass EAP frames to the host
* and accept the set_pmk/del_pmk commands), doing it in the host might not
* be supported.
* @NL80211_EXT_FEATURE_FILS_MAX_CHANNEL_TIME: Driver is capable of overriding
* the max channel attribute in the FILS request params IE with the
* actual dwell time.
* @NL80211_EXT_FEATURE_ACCEPT_BCAST_PROBE_RESP: Driver accepts broadcast probe
* response
* @NL80211_EXT_FEATURE_OCE_PROBE_REQ_HIGH_TX_RATE: Driver supports sending
* the first probe request in each channel at rate of at least 5.5Mbps.
* @NL80211_EXT_FEATURE_OCE_PROBE_REQ_DEFERRAL_SUPPRESSION: Driver supports
* probe request tx deferral and suppression
* @NL80211_EXT_FEATURE_MFP_OPTIONAL: Driver supports the %NL80211_MFP_OPTIONAL
* value in %NL80211_ATTR_USE_MFP.
* @NL80211_EXT_FEATURE_LOW_SPAN_SCAN: Driver supports low span scan.
* @NL80211_EXT_FEATURE_LOW_POWER_SCAN: Driver supports low power scan.
* @NL80211_EXT_FEATURE_HIGH_ACCURACY_SCAN: Driver supports high accuracy scan.
* @NL80211_EXT_FEATURE_DFS_OFFLOAD: HW/driver will offload DFS actions.
* Device or driver will do all DFS-related actions by itself,
* informing user-space about CAC progress, radar detection event,
* channel change triggered by radar detection event.
* No need to start CAC from user-space, no need to react to
* "radar detected" event.
* @NL80211_EXT_FEATURE_CONTROL_PORT_OVER_NL80211: Driver supports sending and
* receiving control port frames over nl80211 instead of the netdevice.
* @NL80211_EXT_FEATURE_DATA_ACK_SIGNAL_SUPPORT: This Driver support data ack
* rssi if firmware support, this flag is to intimate about ack rssi
* support to nl80211.
* @NL80211_EXT_FEATURE_TXQS: Driver supports FQ-CoDel-enabled intermediate
* TXQs.
* @NL80211_EXT_FEATURE_SCAN_RANDOM_SN: Driver/device supports randomizing the
* SN in probe request frames if requested by %NL80211_SCAN_FLAG_RANDOM_SN.
* @NL80211_EXT_FEATURE_SCAN_MIN_PREQ_CONTENT: Driver/device can omit all data
* except for supported rates from the probe request content if requested
* by the %NL80211_SCAN_FLAG_MIN_PREQ_CONTENT flag.
* @NL80211_EXT_FEATURE_SAE_OFFLOAD: Device wants to do SAE authentication in
* station mode (SAE password is passed as part of the connect command).
*
* @NUM_NL80211_EXT_FEATURES: number of extended features.
* @MAX_NL80211_EXT_FEATURES: highest extended feature index.
@ -4938,6 +5038,21 @@ enum nl80211_ext_feature_index {
NL80211_EXT_FEATURE_FILS_SK_OFFLOAD,
NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_PSK,
NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_1X,
NL80211_EXT_FEATURE_FILS_MAX_CHANNEL_TIME,
NL80211_EXT_FEATURE_ACCEPT_BCAST_PROBE_RESP,
NL80211_EXT_FEATURE_OCE_PROBE_REQ_HIGH_TX_RATE,
NL80211_EXT_FEATURE_OCE_PROBE_REQ_DEFERRAL_SUPPRESSION,
NL80211_EXT_FEATURE_MFP_OPTIONAL,
NL80211_EXT_FEATURE_LOW_SPAN_SCAN,
NL80211_EXT_FEATURE_LOW_POWER_SCAN,
NL80211_EXT_FEATURE_HIGH_ACCURACY_SCAN,
NL80211_EXT_FEATURE_DFS_OFFLOAD,
NL80211_EXT_FEATURE_CONTROL_PORT_OVER_NL80211,
NL80211_EXT_FEATURE_DATA_ACK_SIGNAL_SUPPORT,
NL80211_EXT_FEATURE_TXQS,
NL80211_EXT_FEATURE_SCAN_RANDOM_SN,
NL80211_EXT_FEATURE_SCAN_MIN_PREQ_CONTENT,
NL80211_EXT_FEATURE_SAE_OFFLOAD,
/* add new features before the definition below */
NUM_NL80211_EXT_FEATURES,

View File

@ -217,6 +217,7 @@ enum cfg80211_event_type {
EVENT_DISCONNECTED,
EVENT_IBSS_JOINED,
EVENT_STOPPED,
EVENT_PORT_AUTHORIZED,
};
struct cfg80211_event {
@ -236,6 +237,9 @@ struct cfg80211_event {
u8 bssid[ETH_ALEN];
struct ieee80211_channel *channel;
} ij;
struct {
u8 bssid[ETH_ALEN];
} pa;
};
};
@ -386,6 +390,7 @@ int cfg80211_disconnect(struct cfg80211_registered_device *rdev,
bool wextev);
void __cfg80211_roamed(struct wireless_dev *wdev,
struct cfg80211_roam_info *info);
void __cfg80211_port_authorized(struct wireless_dev *wdev, const u8 *bssid);
int cfg80211_mgd_wext_connect(struct cfg80211_registered_device *rdev,
struct wireless_dev *wdev);
void cfg80211_autodisconnect_wk(struct work_struct *work);

View File

@ -421,6 +421,8 @@ static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = {
[NL80211_ATTR_FILS_CACHE_ID] = { .len = 2 },
[NL80211_ATTR_PMK] = { .type = NLA_BINARY, .len = PMK_MAX_LEN },
[NL80211_ATTR_SCHED_SCAN_MULTI] = { .type = NLA_FLAG },
[NL80211_ATTR_SAE_PASSWORD] = { .type = NLA_BINARY,
.len = SAE_PASSWORD_MAX_LEN },
};
/* policy for the key attributes */
@ -3866,6 +3868,13 @@ static bool nl80211_valid_auth_type(struct cfg80211_registered_device *rdev,
/* SAE not supported yet */
if (auth_type == NL80211_AUTHTYPE_SAE)
return false;
if (!(rdev->wiphy.features & NL80211_FEATURE_SAE) &&
!wiphy_ext_feature_isset(&rdev->wiphy,
NL80211_EXT_FEATURE_SAE_OFFLOAD) &&
auth_type == NL80211_AUTHTYPE_SAE)
return false;
/* FILS with SK PFS or PK not supported yet */
if (auth_type == NL80211_AUTHTYPE_FILS_SK_PFS ||
auth_type == NL80211_AUTHTYPE_FILS_PK)
@ -7966,7 +7975,8 @@ static int nl80211_dump_survey(struct sk_buff *skb, struct netlink_callback *cb)
static bool nl80211_valid_wpa_versions(u32 wpa_versions)
{
return !(wpa_versions & ~(NL80211_WPA_VERSION_1 |
NL80211_WPA_VERSION_2));
NL80211_WPA_VERSION_2 |
NL80211_WPA_VERSION_3));
}
static int nl80211_authenticate(struct sk_buff *skb, struct genl_info *info)
@ -8180,6 +8190,16 @@ static int nl80211_crypto_settings(struct cfg80211_registered_device *rdev,
settings->psk = nla_data(info->attrs[NL80211_ATTR_PMK]);
}
if (info->attrs[NL80211_ATTR_SAE_PASSWORD]) {
if (!wiphy_ext_feature_isset(&rdev->wiphy,
NL80211_EXT_FEATURE_SAE_OFFLOAD))
return -EINVAL;
settings->sae_pwd =
nla_data(info->attrs[NL80211_ATTR_SAE_PASSWORD]);
settings->sae_pwd_len =
nla_len(info->attrs[NL80211_ATTR_SAE_PASSWORD]);
}
return 0;
}
@ -13756,6 +13776,8 @@ void nl80211_send_connect_result(struct cfg80211_registered_device *rdev,
(nla_put_flag(msg, NL80211_ATTR_TIMED_OUT) ||
nla_put_u32(msg, NL80211_ATTR_TIMEOUT_REASON,
cr->timeout_reason))) ||
(cr->authorized &&
nla_put_flag(msg, NL80211_ATTR_PORT_AUTHORIZED)) ||
(cr->req_ie &&
nla_put(msg, NL80211_ATTR_REQ_IE, cr->req_ie_len, cr->req_ie)) ||
(cr->resp_ie &&
@ -13827,6 +13849,37 @@ void nl80211_send_roamed(struct cfg80211_registered_device *rdev,
nlmsg_free(msg);
}
void nl80211_send_port_authorized(struct cfg80211_registered_device *rdev,
struct net_device *netdev, const u8 *bssid)
{
struct sk_buff *msg;
void *hdr;
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
if (!msg)
return;
hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_PORT_AUTHORIZED);
if (!hdr) {
nlmsg_free(msg);
return;
}
if (nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex) ||
nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid))
goto nla_put_failure;
genlmsg_end(msg, hdr);
genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
NL80211_MCGRP_MLME, GFP_KERNEL);
return;
nla_put_failure:
genlmsg_cancel(msg, hdr);
nlmsg_free(msg);
}
void nl80211_send_disconnected(struct cfg80211_registered_device *rdev,
struct net_device *netdev, u16 reason,
const u8 *ie, size_t ie_len, bool from_ap)

View File

@ -59,6 +59,8 @@ void nl80211_send_connect_result(struct cfg80211_registered_device *rdev,
void nl80211_send_roamed(struct cfg80211_registered_device *rdev,
struct net_device *netdev,
struct cfg80211_roam_info *info, gfp_t gfp);
void nl80211_send_port_authorized(struct cfg80211_registered_device *rdev,
struct net_device *netdev, const u8 *bssid);
void nl80211_send_disconnected(struct cfg80211_registered_device *rdev,
struct net_device *netdev, u16 reason,
const u8 *ie, size_t ie_len, bool from_ap);

View File

@ -857,6 +857,7 @@ void cfg80211_connect_done(struct net_device *dev,
ev->cr.bss = params->bss;
ev->cr.status = params->status;
ev->cr.timeout_reason = params->timeout_reason;
ev->cr.authorized = params->authorized;
spin_lock_irqsave(&wdev->event_lock, flags);
list_add_tail(&ev->list, &wdev->event_list);
@ -965,6 +966,50 @@ void cfg80211_roamed(struct net_device *dev, struct cfg80211_roam_info *info,
}
EXPORT_SYMBOL(cfg80211_roamed);
void __cfg80211_port_authorized(struct wireless_dev *wdev, const u8 *bssid)
{
ASSERT_WDEV_LOCK(wdev);
if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION))
return;
if (WARN_ON(!wdev->current_bss) ||
WARN_ON(!ether_addr_equal(wdev->current_bss->pub.bssid, bssid)))
return;
nl80211_send_port_authorized(wiphy_to_rdev(wdev->wiphy), wdev->netdev,
bssid);
}
void cfg80211_port_authorized(struct net_device *dev, const u8 *bssid,
gfp_t gfp)
{
struct wireless_dev *wdev = dev->ieee80211_ptr;
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
struct cfg80211_event *ev;
unsigned long flags;
if (WARN_ON(!bssid))
return;
ev = kzalloc(sizeof(*ev), gfp);
if (!ev)
return;
ev->type = EVENT_PORT_AUTHORIZED;
memcpy(ev->pa.bssid, bssid, ETH_ALEN);
/*
* Use the wdev event list so that if there are pending
* connected/roamed events, they will be reported first.
*/
spin_lock_irqsave(&wdev->event_lock, flags);
list_add_tail(&ev->list, &wdev->event_list);
spin_unlock_irqrestore(&wdev->event_lock, flags);
queue_work(cfg80211_wq, &rdev->event_work);
}
EXPORT_SYMBOL(cfg80211_port_authorized);
void __cfg80211_disconnected(struct net_device *dev, const u8 *ie,
size_t ie_len, u16 reason, bool from_ap)
{

View File

@ -964,6 +964,9 @@ void cfg80211_process_wdev_events(struct wireless_dev *wdev)
case EVENT_STOPPED:
__cfg80211_leave(wiphy_to_rdev(wdev->wiphy), wdev);
break;
case EVENT_PORT_AUTHORIZED:
__cfg80211_port_authorized(wdev, ev->pa.bssid);
break;
}
wdev_unlock(wdev);