Merge tag 'wireless-drivers-next-for-davem-2015-06-18' of git://git.kernel.org/pub/scm/linux/kernel/git/kvalo/wireless-drivers-next

Kalle Valo says:

====================
Major changes:

mwifiex:

* enhancements for AP mode: support verbose information in station
  dump command and also information about AP link.
* enable power save by default

brcmfmac:

* fix module reload issue for PCIe
* improving msgbuf protocol for PCIe devices
* rework .get_station() cfg80211 callback operation
* determine interface combinations upon device feature support

ath9k:

* ath9k_htc: add support of channel switch

wil6210:

* add modparam for bcast ring size
* support hidden SSID
* add per-MCS Rx stats
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
David S. Miller
2015-06-23 01:03:18 -07:00
86 changed files with 1741 additions and 903 deletions

View File

@@ -988,6 +988,7 @@ static void brcmf_sdiod_freezer_detach(struct brcmf_sdio_dev *sdiodev)
static int brcmf_sdiod_remove(struct brcmf_sdio_dev *sdiodev)
{
sdiodev->state = BRCMF_SDIOD_DOWN;
if (sdiodev->bus) {
brcmf_sdio_remove(sdiodev->bus);
sdiodev->bus = NULL;

View File

@@ -52,8 +52,6 @@
#define BRCMF_PNO_SCAN_COMPLETE 1
#define BRCMF_PNO_SCAN_INCOMPLETE 0
#define BRCMF_IFACE_MAX_CNT 3
#define WPA_OUI "\x00\x50\xF2" /* WPA OUI */
#define WPA_OUI_TYPE 1
#define RSN_OUI "\x00\x0F\xAC" /* RSN OUI */
@@ -2398,27 +2396,80 @@ brcmf_cfg80211_reconfigure_wep(struct brcmf_if *ifp)
brcmf_err("set wsec error (%d)\n", err);
}
static void brcmf_convert_sta_flags(u32 fw_sta_flags, struct station_info *si)
{
struct nl80211_sta_flag_update *sfu;
brcmf_dbg(TRACE, "flags %08x\n", fw_sta_flags);
si->filled |= BIT(NL80211_STA_INFO_STA_FLAGS);
sfu = &si->sta_flags;
sfu->mask = BIT(NL80211_STA_FLAG_WME) |
BIT(NL80211_STA_FLAG_AUTHENTICATED) |
BIT(NL80211_STA_FLAG_ASSOCIATED) |
BIT(NL80211_STA_FLAG_AUTHORIZED);
if (fw_sta_flags & BRCMF_STA_WME)
sfu->set |= BIT(NL80211_STA_FLAG_WME);
if (fw_sta_flags & BRCMF_STA_AUTHE)
sfu->set |= BIT(NL80211_STA_FLAG_AUTHENTICATED);
if (fw_sta_flags & BRCMF_STA_ASSOC)
sfu->set |= BIT(NL80211_STA_FLAG_ASSOCIATED);
if (fw_sta_flags & BRCMF_STA_AUTHO)
sfu->set |= BIT(NL80211_STA_FLAG_AUTHORIZED);
}
static void brcmf_fill_bss_param(struct brcmf_if *ifp, struct station_info *si)
{
struct {
__le32 len;
struct brcmf_bss_info_le bss_le;
} *buf;
u16 capability;
int err;
buf = kzalloc(WL_BSS_INFO_MAX, GFP_KERNEL);
if (!buf)
return;
buf->len = cpu_to_le32(WL_BSS_INFO_MAX);
err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BSS_INFO, buf,
WL_BSS_INFO_MAX);
if (err) {
brcmf_err("Failed to get bss info (%d)\n", err);
return;
}
si->filled |= BIT(NL80211_STA_INFO_BSS_PARAM);
si->bss_param.beacon_interval = le16_to_cpu(buf->bss_le.beacon_period);
si->bss_param.dtim_period = buf->bss_le.dtim_period;
capability = le16_to_cpu(buf->bss_le.capability);
if (capability & IEEE80211_HT_STBC_PARAM_DUAL_CTS_PROT)
si->bss_param.flags |= BSS_PARAM_FLAGS_CTS_PROT;
if (capability & WLAN_CAPABILITY_SHORT_PREAMBLE)
si->bss_param.flags |= BSS_PARAM_FLAGS_SHORT_PREAMBLE;
if (capability & WLAN_CAPABILITY_SHORT_SLOT_TIME)
si->bss_param.flags |= BSS_PARAM_FLAGS_SHORT_SLOT_TIME;
}
static s32
brcmf_cfg80211_get_station(struct wiphy *wiphy, struct net_device *ndev,
const u8 *mac, struct station_info *sinfo)
{
struct brcmf_if *ifp = netdev_priv(ndev);
struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
struct brcmf_scb_val_le scb_val;
int rssi;
s32 rate;
s32 err = 0;
u8 *bssid = profile->bssid;
struct brcmf_sta_info_le sta_info_le;
u32 beacon_period;
u32 dtim_period;
u32 sta_flags;
u32 is_tdls_peer;
brcmf_dbg(TRACE, "Enter, MAC %pM\n", mac);
if (!check_vif_up(ifp->vif))
return -EIO;
if (brcmf_is_apmode(ifp->vif)) {
memcpy(&sta_info_le, mac, ETH_ALEN);
memset(&sta_info_le, 0, sizeof(sta_info_le));
memcpy(&sta_info_le, mac, ETH_ALEN);
err = brcmf_fil_iovar_data_get(ifp, "tdls_sta_info",
&sta_info_le,
sizeof(sta_info_le));
is_tdls_peer = !err;
if (err) {
err = brcmf_fil_iovar_data_get(ifp, "sta_info",
&sta_info_le,
sizeof(sta_info_le));
@@ -2426,73 +2477,48 @@ brcmf_cfg80211_get_station(struct wiphy *wiphy, struct net_device *ndev,
brcmf_err("GET STA INFO failed, %d\n", err);
goto done;
}
sinfo->filled = BIT(NL80211_STA_INFO_INACTIVE_TIME);
sinfo->inactive_time = le32_to_cpu(sta_info_le.idle) * 1000;
if (le32_to_cpu(sta_info_le.flags) & BRCMF_STA_ASSOC) {
sinfo->filled |= BIT(NL80211_STA_INFO_CONNECTED_TIME);
sinfo->connected_time = le32_to_cpu(sta_info_le.in);
}
brcmf_dbg(TRACE, "STA idle time : %d ms, connected time :%d sec\n",
sinfo->inactive_time, sinfo->connected_time);
} else if (ifp->vif->wdev.iftype == NL80211_IFTYPE_STATION) {
if (memcmp(mac, bssid, ETH_ALEN)) {
brcmf_err("Wrong Mac address cfg_mac-%pM wl_bssid-%pM\n",
mac, bssid);
err = -ENOENT;
goto done;
}
/* Report the current tx rate */
err = brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_RATE, &rate);
if (err) {
brcmf_err("Could not get rate (%d)\n", err);
goto done;
} else {
}
brcmf_dbg(TRACE, "version %d\n", le16_to_cpu(sta_info_le.ver));
sinfo->filled = BIT(NL80211_STA_INFO_INACTIVE_TIME);
sinfo->inactive_time = le32_to_cpu(sta_info_le.idle) * 1000;
sta_flags = le32_to_cpu(sta_info_le.flags);
brcmf_convert_sta_flags(sta_flags, sinfo);
sinfo->sta_flags.mask |= BIT(NL80211_STA_FLAG_TDLS_PEER);
if (is_tdls_peer)
sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_TDLS_PEER);
else
sinfo->sta_flags.set &= ~BIT(NL80211_STA_FLAG_TDLS_PEER);
if (sta_flags & BRCMF_STA_ASSOC) {
sinfo->filled |= BIT(NL80211_STA_INFO_CONNECTED_TIME);
sinfo->connected_time = le32_to_cpu(sta_info_le.in);
brcmf_fill_bss_param(ifp, sinfo);
}
if (sta_flags & BRCMF_STA_SCBSTATS) {
sinfo->filled |= BIT(NL80211_STA_INFO_TX_FAILED);
sinfo->tx_failed = le32_to_cpu(sta_info_le.tx_failures);
sinfo->filled |= BIT(NL80211_STA_INFO_TX_PACKETS);
sinfo->tx_packets = le32_to_cpu(sta_info_le.tx_pkts);
sinfo->tx_packets += le32_to_cpu(sta_info_le.tx_mcast_pkts);
sinfo->filled |= BIT(NL80211_STA_INFO_RX_PACKETS);
sinfo->rx_packets = le32_to_cpu(sta_info_le.rx_ucast_pkts);
sinfo->rx_packets += le32_to_cpu(sta_info_le.rx_mcast_pkts);
if (sinfo->tx_packets) {
sinfo->filled |= BIT(NL80211_STA_INFO_TX_BITRATE);
sinfo->txrate.legacy = rate * 5;
brcmf_dbg(CONN, "Rate %d Mbps\n", rate / 2);
sinfo->txrate.legacy = le32_to_cpu(sta_info_le.tx_rate);
sinfo->txrate.legacy /= 100;
}
if (test_bit(BRCMF_VIF_STATUS_CONNECTED,
&ifp->vif->sme_state)) {
memset(&scb_val, 0, sizeof(scb_val));
err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_RSSI,
&scb_val, sizeof(scb_val));
if (err) {
brcmf_err("Could not get rssi (%d)\n", err);
goto done;
} else {
rssi = le32_to_cpu(scb_val.val);
sinfo->filled |= BIT(NL80211_STA_INFO_SIGNAL);
sinfo->signal = rssi;
brcmf_dbg(CONN, "RSSI %d dBm\n", rssi);
}
err = brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_BCNPRD,
&beacon_period);
if (err) {
brcmf_err("Could not get beacon period (%d)\n",
err);
goto done;
} else {
sinfo->bss_param.beacon_interval =
beacon_period;
brcmf_dbg(CONN, "Beacon peroid %d\n",
beacon_period);
}
err = brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_DTIMPRD,
&dtim_period);
if (err) {
brcmf_err("Could not get DTIM period (%d)\n",
err);
goto done;
} else {
sinfo->bss_param.dtim_period = dtim_period;
brcmf_dbg(CONN, "DTIM peroid %d\n",
dtim_period);
}
sinfo->filled |= BIT(NL80211_STA_INFO_BSS_PARAM);
if (sinfo->rx_packets) {
sinfo->filled |= BIT(NL80211_STA_INFO_RX_BITRATE);
sinfo->rxrate.legacy = le32_to_cpu(sta_info_le.rx_rate);
sinfo->rxrate.legacy /= 100;
}
} else
err = -EPERM;
if (le16_to_cpu(sta_info_le.ver) >= 4) {
sinfo->filled |= BIT(NL80211_STA_INFO_TX_BYTES);
sinfo->tx_bytes = le64_to_cpu(sta_info_le.tx_tot_bytes);
sinfo->filled |= BIT(NL80211_STA_INFO_RX_BYTES);
sinfo->rx_bytes = le64_to_cpu(sta_info_le.rx_tot_bytes);
}
}
done:
brcmf_dbg(TRACE, "Exit\n");
return err;
@@ -5640,53 +5666,6 @@ static int brcmf_setup_wiphybands(struct wiphy *wiphy)
return 0;
}
static const struct ieee80211_iface_limit brcmf_iface_limits_mbss[] = {
{
.max = 1,
.types = BIT(NL80211_IFTYPE_STATION) |
BIT(NL80211_IFTYPE_ADHOC)
},
{
.max = 4,
.types = BIT(NL80211_IFTYPE_AP)
},
{
.max = 1,
.types = BIT(NL80211_IFTYPE_P2P_CLIENT) |
BIT(NL80211_IFTYPE_P2P_GO)
},
{
.max = 1,
.types = BIT(NL80211_IFTYPE_P2P_DEVICE)
}
};
static const struct ieee80211_iface_limit brcmf_iface_limits_sbss[] = {
{
.max = 2,
.types = BIT(NL80211_IFTYPE_STATION) |
BIT(NL80211_IFTYPE_ADHOC) |
BIT(NL80211_IFTYPE_AP)
},
{
.max = 1,
.types = BIT(NL80211_IFTYPE_P2P_CLIENT) |
BIT(NL80211_IFTYPE_P2P_GO)
},
{
.max = 1,
.types = BIT(NL80211_IFTYPE_P2P_DEVICE)
}
};
static struct ieee80211_iface_combination brcmf_iface_combos[] = {
{
.max_interfaces = BRCMF_IFACE_MAX_CNT,
.num_different_channels = 1,
.n_limits = ARRAY_SIZE(brcmf_iface_limits_sbss),
.limits = brcmf_iface_limits_sbss,
}
};
static const struct ieee80211_txrx_stypes
brcmf_txrx_stypes[NUM_NL80211_IFTYPES] = {
[NL80211_IFTYPE_STATION] = {
@@ -5716,6 +5695,67 @@ brcmf_txrx_stypes[NUM_NL80211_IFTYPES] = {
}
};
static int brcmf_setup_ifmodes(struct wiphy *wiphy, struct brcmf_if *ifp)
{
struct ieee80211_iface_combination *combo = NULL;
struct ieee80211_iface_limit *limits = NULL;
int i = 0, max_iface_cnt;
combo = kzalloc(sizeof(*combo), GFP_KERNEL);
if (!combo)
goto err;
limits = kzalloc(sizeof(*limits) * 4, GFP_KERNEL);
if (!limits)
goto err;
wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
BIT(NL80211_IFTYPE_ADHOC) |
BIT(NL80211_IFTYPE_AP);
if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MCHAN))
combo->num_different_channels = 2;
else
combo->num_different_channels = 1;
if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MBSS)) {
limits[i].max = 1;
limits[i++].types = BIT(NL80211_IFTYPE_STATION);
limits[i].max = 4;
limits[i++].types = BIT(NL80211_IFTYPE_AP);
max_iface_cnt = 5;
} else {
limits[i].max = 2;
limits[i++].types = BIT(NL80211_IFTYPE_STATION) |
BIT(NL80211_IFTYPE_AP);
max_iface_cnt = 2;
}
if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_P2P)) {
wiphy->interface_modes |= BIT(NL80211_IFTYPE_P2P_CLIENT) |
BIT(NL80211_IFTYPE_P2P_GO) |
BIT(NL80211_IFTYPE_P2P_DEVICE);
limits[i].max = 1;
limits[i++].types = BIT(NL80211_IFTYPE_P2P_CLIENT) |
BIT(NL80211_IFTYPE_P2P_GO);
limits[i].max = 1;
limits[i++].types = BIT(NL80211_IFTYPE_P2P_DEVICE);
max_iface_cnt += 2;
}
combo->max_interfaces = max_iface_cnt;
combo->limits = limits;
combo->n_limits = i;
wiphy->iface_combinations = combo;
wiphy->n_iface_combinations = 1;
return 0;
err:
kfree(limits);
kfree(combo);
return -ENOMEM;
}
static void brcmf_wiphy_pno_params(struct wiphy *wiphy)
{
/* scheduled scan settings */
@@ -5746,7 +5786,6 @@ static void brcmf_wiphy_wowl_params(struct wiphy *wiphy)
static int brcmf_setup_wiphy(struct wiphy *wiphy, struct brcmf_if *ifp)
{
struct ieee80211_supported_band *band;
struct ieee80211_iface_combination ifc_combo;
__le32 bandlist[3];
u32 n_bands;
int err, i;
@@ -5754,24 +5793,11 @@ static int brcmf_setup_wiphy(struct wiphy *wiphy, struct brcmf_if *ifp)
wiphy->max_scan_ssids = WL_NUM_SCAN_MAX;
wiphy->max_scan_ie_len = BRCMF_SCAN_IE_LEN_MAX;
wiphy->max_num_pmkids = WL_NUM_PMKIDS_MAX;
wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
BIT(NL80211_IFTYPE_ADHOC) |
BIT(NL80211_IFTYPE_AP) |
BIT(NL80211_IFTYPE_P2P_CLIENT) |
BIT(NL80211_IFTYPE_P2P_GO) |
BIT(NL80211_IFTYPE_P2P_DEVICE);
/* need VSDB firmware feature for concurrent channels */
ifc_combo = brcmf_iface_combos[0];
if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MCHAN))
ifc_combo.num_different_channels = 2;
if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MBSS)) {
ifc_combo.n_limits = ARRAY_SIZE(brcmf_iface_limits_mbss),
ifc_combo.limits = brcmf_iface_limits_mbss;
}
wiphy->iface_combinations = kmemdup(&ifc_combo,
sizeof(ifc_combo),
GFP_KERNEL);
wiphy->n_iface_combinations = ARRAY_SIZE(brcmf_iface_combos);
err = brcmf_setup_ifmodes(wiphy, ifp);
if (err)
return err;
wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
wiphy->cipher_suites = __wl_cipher_suites;
wiphy->n_cipher_suites = ARRAY_SIZE(__wl_cipher_suites);
@@ -6036,6 +6062,8 @@ static void brcmf_free_wiphy(struct wiphy *wiphy)
if (!wiphy)
return;
if (wiphy->iface_combinations)
kfree(wiphy->iface_combinations->limits);
kfree(wiphy->iface_combinations);
if (wiphy->bands[IEEE80211_BAND_2GHZ]) {
kfree(wiphy->bands[IEEE80211_BAND_2GHZ]->channels);
@@ -6071,6 +6099,7 @@ struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr,
brcmf_err("Could not allocate wiphy device\n");
return NULL;
}
memcpy(wiphy->perm_addr, drvr->mac, ETH_ALEN);
set_wiphy_dev(wiphy, busdev);
cfg = wiphy_priv(wiphy);
@@ -6178,10 +6207,8 @@ void brcmf_cfg80211_detach(struct brcmf_cfg80211_info *cfg)
if (!cfg)
return;
WARN_ON(!list_empty(&cfg->vif_list));
wiphy_unregister(cfg->wiphy);
brcmf_btcoex_detach(cfg);
brcmf_p2p_detach(&cfg->p2p);
wiphy_unregister(cfg->wiphy);
wl_deinit_priv(cfg);
brcmf_free_wiphy(cfg->wiphy);
}

View File

@@ -223,8 +223,6 @@ void brcmf_commonring_write_cancel(struct brcmf_commonring *commonring,
void *brcmf_commonring_get_read_ptr(struct brcmf_commonring *commonring,
u16 *n_items)
{
void *ret_addr;
if (commonring->cr_update_wptr)
commonring->cr_update_wptr(commonring->cr_ctx);
@@ -235,19 +233,18 @@ void *brcmf_commonring_get_read_ptr(struct brcmf_commonring *commonring,
if (*n_items == 0)
return NULL;
ret_addr = commonring->buf_addr +
(commonring->r_ptr * commonring->item_len);
commonring->r_ptr += *n_items;
if (commonring->r_ptr == commonring->depth)
commonring->r_ptr = 0;
return ret_addr;
return commonring->buf_addr +
(commonring->r_ptr * commonring->item_len);
}
int brcmf_commonring_read_complete(struct brcmf_commonring *commonring)
int brcmf_commonring_read_complete(struct brcmf_commonring *commonring,
u16 n_items)
{
commonring->r_ptr += n_items;
if (commonring->r_ptr == commonring->depth)
commonring->r_ptr = 0;
if (commonring->cr_write_rptr)
return commonring->cr_write_rptr(commonring->cr_ctx);

View File

@@ -62,7 +62,8 @@ void brcmf_commonring_write_cancel(struct brcmf_commonring *commonring,
u16 n_items);
void *brcmf_commonring_get_read_ptr(struct brcmf_commonring *commonring,
u16 *n_items);
int brcmf_commonring_read_complete(struct brcmf_commonring *commonring);
int brcmf_commonring_read_complete(struct brcmf_commonring *commonring,
u16 n_items);
#define brcmf_commonring_n_items(commonring) (commonring->depth)
#define brcmf_commonring_len_item(commonring) (commonring->item_len)

View File

@@ -867,8 +867,6 @@ static void brcmf_del_if(struct brcmf_pub *drvr, s32 bssidx)
}
/* unregister will take care of freeing it */
unregister_netdev(ifp->ndev);
} else {
kfree(ifp);
}
}
@@ -1100,6 +1098,8 @@ void brcmf_detach(struct device *dev)
/* stop firmware event handling */
brcmf_fweh_detach(drvr);
if (drvr->config)
brcmf_p2p_detach(&drvr->config->p2p);
brcmf_bus_change_state(bus_if, BRCMF_BUS_DOWN);

View File

@@ -41,15 +41,6 @@ void brcmf_debugfs_exit(void)
root_folder = NULL;
}
static int brcmf_debugfs_chipinfo_read(struct seq_file *seq, void *data)
{
struct brcmf_bus *bus = dev_get_drvdata(seq->private);
seq_printf(seq, "chip: %x(%u) rev %u\n",
bus->chip, bus->chip, bus->chiprev);
return 0;
}
int brcmf_debugfs_attach(struct brcmf_pub *drvr)
{
struct device *dev = drvr->bus_if->dev;
@@ -58,7 +49,6 @@ int brcmf_debugfs_attach(struct brcmf_pub *drvr)
return -ENODEV;
drvr->dbgfs_dir = debugfs_create_dir(dev_name(dev), root_folder);
brcmf_debugfs_add_entry(drvr, "chipinfo", brcmf_debugfs_chipinfo_read);
return PTR_ERR_OR_ZERO(drvr->dbgfs_dir);
}
@@ -74,44 +64,12 @@ struct dentry *brcmf_debugfs_get_devdir(struct brcmf_pub *drvr)
return drvr->dbgfs_dir;
}
struct brcmf_debugfs_entry {
int (*read)(struct seq_file *seq, void *data);
struct brcmf_pub *drvr;
};
static int brcmf_debugfs_entry_open(struct inode *inode, struct file *f)
{
struct brcmf_debugfs_entry *entry = inode->i_private;
return single_open(f, entry->read, entry->drvr->bus_if->dev);
}
static const struct file_operations brcmf_debugfs_def_ops = {
.owner = THIS_MODULE,
.open = brcmf_debugfs_entry_open,
.release = single_release,
.read = seq_read,
.llseek = seq_lseek
};
int brcmf_debugfs_add_entry(struct brcmf_pub *drvr, const char *fn,
int (*read_fn)(struct seq_file *seq, void *data))
{
struct dentry *dentry = drvr->dbgfs_dir;
struct brcmf_debugfs_entry *entry;
struct dentry *e;
if (IS_ERR_OR_NULL(dentry))
return -ENOENT;
entry = devm_kzalloc(drvr->bus_if->dev, sizeof(*entry), GFP_KERNEL);
if (!entry)
return -ENOMEM;
entry->read = read_fn;
entry->drvr = drvr;
dentry = debugfs_create_file(fn, S_IRUGO, dentry, entry,
&brcmf_debugfs_def_ops);
return PTR_ERR_OR_ZERO(dentry);
e = debugfs_create_devm_seqfile(drvr->bus_if->dev, fn,
drvr->dbgfs_dir, read_fn);
return PTR_ERR_OR_ZERO(e);
}

View File

@@ -129,6 +129,7 @@ void brcmf_feat_attach(struct brcmf_pub *drvr)
brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_WOWL, "wowl");
if (drvr->bus_if->chip != BRCM_CC_43362_CHIP_ID)
brcmf_feat_iovar_int_set(ifp, BRCMF_FEAT_MBSS, "mbss", 0);
brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_P2P, "p2p");
/* set chip related quirks */
switch (drvr->bus_if->chip) {

View File

@@ -23,12 +23,14 @@
* MCHAN: multi-channel for concurrent P2P.
* PNO: preferred network offload.
* WOWL: Wake-On-WLAN.
* P2P: peer-to-peer
*/
#define BRCMF_FEAT_LIST \
BRCMF_FEAT_DEF(MBSS) \
BRCMF_FEAT_DEF(MCHAN) \
BRCMF_FEAT_DEF(PNO) \
BRCMF_FEAT_DEF(WOWL)
BRCMF_FEAT_DEF(WOWL) \
BRCMF_FEAT_DEF(P2P)
/*
* Quirks:
*

View File

@@ -43,7 +43,7 @@ enum nvram_parser_state {
* struct nvram_parser - internal info for parser.
*
* @state: current parser state.
* @fwnv: input buffer being parsed.
* @data: input buffer being parsed.
* @nvram: output buffer with parse result.
* @nvram_len: lenght of parse result.
* @line: current line.
@@ -55,7 +55,7 @@ enum nvram_parser_state {
*/
struct nvram_parser {
enum nvram_parser_state state;
const struct firmware *fwnv;
const u8 *data;
u8 *nvram;
u32 nvram_len;
u32 line;
@@ -91,7 +91,7 @@ static enum nvram_parser_state brcmf_nvram_handle_idle(struct nvram_parser *nvp)
{
char c;
c = nvp->fwnv->data[nvp->pos];
c = nvp->data[nvp->pos];
if (c == '\n')
return COMMENT;
if (is_whitespace(c))
@@ -115,16 +115,16 @@ static enum nvram_parser_state brcmf_nvram_handle_key(struct nvram_parser *nvp)
enum nvram_parser_state st = nvp->state;
char c;
c = nvp->fwnv->data[nvp->pos];
c = nvp->data[nvp->pos];
if (c == '=') {
/* ignore RAW1 by treating as comment */
if (strncmp(&nvp->fwnv->data[nvp->entry], "RAW1", 4) == 0)
if (strncmp(&nvp->data[nvp->entry], "RAW1", 4) == 0)
st = COMMENT;
else
st = VALUE;
if (strncmp(&nvp->fwnv->data[nvp->entry], "devpath", 7) == 0)
if (strncmp(&nvp->data[nvp->entry], "devpath", 7) == 0)
nvp->multi_dev_v1 = true;
if (strncmp(&nvp->fwnv->data[nvp->entry], "pcie/", 5) == 0)
if (strncmp(&nvp->data[nvp->entry], "pcie/", 5) == 0)
nvp->multi_dev_v2 = true;
} else if (!is_nvram_char(c) || c == ' ') {
brcmf_dbg(INFO, "warning: ln=%d:col=%d: '=' expected, skip invalid key entry\n",
@@ -145,11 +145,11 @@ brcmf_nvram_handle_value(struct nvram_parser *nvp)
char *ekv;
u32 cplen;
c = nvp->fwnv->data[nvp->pos];
c = nvp->data[nvp->pos];
if (!is_nvram_char(c)) {
/* key,value pair complete */
ekv = (u8 *)&nvp->fwnv->data[nvp->pos];
skv = (u8 *)&nvp->fwnv->data[nvp->entry];
ekv = (u8 *)&nvp->data[nvp->pos];
skv = (u8 *)&nvp->data[nvp->entry];
cplen = ekv - skv;
if (nvp->nvram_len + cplen + 1 >= BRCMF_FW_MAX_NVRAM_SIZE)
return END;
@@ -170,7 +170,7 @@ brcmf_nvram_handle_comment(struct nvram_parser *nvp)
{
char *eoc, *sol;
sol = (char *)&nvp->fwnv->data[nvp->pos];
sol = (char *)&nvp->data[nvp->pos];
eoc = strchr(sol, '\n');
if (!eoc) {
eoc = strchr(sol, '\0');
@@ -201,17 +201,17 @@ static enum nvram_parser_state
};
static int brcmf_init_nvram_parser(struct nvram_parser *nvp,
const struct firmware *nv)
const u8 *data, size_t data_len)
{
size_t size;
memset(nvp, 0, sizeof(*nvp));
nvp->fwnv = nv;
nvp->data = data;
/* Limit size to MAX_NVRAM_SIZE, some files contain lot of comment */
if (nv->size > BRCMF_FW_MAX_NVRAM_SIZE)
if (data_len > BRCMF_FW_MAX_NVRAM_SIZE)
size = BRCMF_FW_MAX_NVRAM_SIZE;
else
size = nv->size;
size = data_len;
/* Alloc for extra 0 byte + roundup by 4 + length field */
size += 1 + 3 + sizeof(u32);
nvp->nvram = kzalloc(size, GFP_KERNEL);
@@ -232,6 +232,8 @@ static void brcmf_fw_strip_multi_v1(struct nvram_parser *nvp, u16 domain_nr,
u16 bus_nr)
{
/* Device path with a leading '=' key-value separator */
char pci_path[] = "=pci/?/?";
size_t pci_len;
char pcie_path[] = "=pcie/?/?";
size_t pcie_len;
@@ -251,6 +253,9 @@ static void brcmf_fw_strip_multi_v1(struct nvram_parser *nvp, u16 domain_nr,
/* First search for the devpathX and see if it is the configuration
* for domain_nr/bus_nr. Search complete nvp
*/
snprintf(pci_path, sizeof(pci_path), "=pci/%d/%d", domain_nr,
bus_nr);
pci_len = strlen(pci_path);
snprintf(pcie_path, sizeof(pcie_path), "=pcie/%d/%d", domain_nr,
bus_nr);
pcie_len = strlen(pcie_path);
@@ -260,8 +265,9 @@ static void brcmf_fw_strip_multi_v1(struct nvram_parser *nvp, u16 domain_nr,
/* Format: devpathX=pcie/Y/Z/
* Y = domain_nr, Z = bus_nr, X = virtual ID
*/
if ((strncmp(&nvp->nvram[i], "devpath", 7) == 0) &&
(strncmp(&nvp->nvram[i + 8], pcie_path, pcie_len) == 0)) {
if (strncmp(&nvp->nvram[i], "devpath", 7) == 0 &&
(!strncmp(&nvp->nvram[i + 8], pci_path, pci_len) ||
!strncmp(&nvp->nvram[i + 8], pcie_path, pcie_len))) {
id = nvp->nvram[i + 7] - '0';
found = true;
break;
@@ -356,18 +362,18 @@ fail:
* and converts newlines to NULs. Shortens buffer as needed and pads with NULs.
* End of buffer is completed with token identifying length of buffer.
*/
static void *brcmf_fw_nvram_strip(const struct firmware *nv, u32 *new_length,
u16 domain_nr, u16 bus_nr)
static void *brcmf_fw_nvram_strip(const u8 *data, size_t data_len,
u32 *new_length, u16 domain_nr, u16 bus_nr)
{
struct nvram_parser nvp;
u32 pad;
u32 token;
__le32 token_le;
if (brcmf_init_nvram_parser(&nvp, nv) < 0)
if (brcmf_init_nvram_parser(&nvp, data, data_len) < 0)
return NULL;
while (nvp.pos < nv->size) {
while (nvp.pos < data_len) {
nvp.state = nv_parser_states[nvp.state](&nvp);
if (nvp.state == END)
break;
@@ -426,7 +432,7 @@ static void brcmf_fw_request_nvram_done(const struct firmware *fw, void *ctx)
goto fail;
if (fw) {
nvram = brcmf_fw_nvram_strip(fw, &nvram_length,
nvram = brcmf_fw_nvram_strip(fw->data, fw->size, &nvram_length,
fwctx->domain_nr, fwctx->bus_nr);
release_firmware(fw);
if (!nvram && !(fwctx->flags & BRCMF_FW_REQ_NV_OPTIONAL))

View File

@@ -32,7 +32,11 @@
#define BRCMF_BSS_INFO_VERSION 109 /* curr ver of brcmf_bss_info_le struct */
#define BRCMF_BSS_RSSI_ON_CHANNEL 0x0002
#define BRCMF_STA_ASSOC 0x10 /* Associated */
#define BRCMF_STA_WME 0x00000002 /* WMM association */
#define BRCMF_STA_AUTHE 0x00000008 /* Authenticated */
#define BRCMF_STA_ASSOC 0x00000010 /* Associated */
#define BRCMF_STA_AUTHO 0x00000020 /* Authorized */
#define BRCMF_STA_SCBSTATS 0x00004000 /* Per STA debug stats */
/* size of brcmf_scan_params not including variable length array */
#define BRCMF_SCAN_PARAMS_FIXED_SIZE 64
@@ -113,6 +117,7 @@
#define BRCMF_WOWL_MAXPATTERNSIZE 128
#define BRCMF_COUNTRY_BUF_SZ 4
#define BRCMF_ANT_MAX 4
/* join preference types for join_pref iovar */
enum brcmf_join_pref_types {
@@ -456,25 +461,61 @@ struct brcmf_channel_info_le {
};
struct brcmf_sta_info_le {
__le16 ver; /* version of this struct */
__le16 len; /* length in bytes of this structure */
__le16 cap; /* sta's advertised capabilities */
__le32 flags; /* flags defined below */
__le32 idle; /* time since data pkt rx'd from sta */
u8 ea[ETH_ALEN]; /* Station address */
__le32 count; /* # rates in this set */
u8 rates[BRCMF_MAXRATES_IN_SET]; /* rates in 500kbps units */
__le16 ver; /* version of this struct */
__le16 len; /* length in bytes of this structure */
__le16 cap; /* sta's advertised capabilities */
__le32 flags; /* flags defined below */
__le32 idle; /* time since data pkt rx'd from sta */
u8 ea[ETH_ALEN]; /* Station address */
__le32 count; /* # rates in this set */
u8 rates[BRCMF_MAXRATES_IN_SET]; /* rates in 500kbps units */
/* w/hi bit set if basic */
__le32 in; /* seconds elapsed since associated */
__le32 listen_interval_inms; /* Min Listen interval in ms for STA */
__le32 tx_pkts; /* # of packets transmitted */
__le32 tx_failures; /* # of packets failed */
__le32 rx_ucast_pkts; /* # of unicast packets received */
__le32 rx_mcast_pkts; /* # of multicast packets received */
__le32 tx_rate; /* Rate of last successful tx frame */
__le32 rx_rate; /* Rate of last successful rx frame */
__le32 rx_decrypt_succeeds; /* # of packet decrypted successfully */
__le32 rx_decrypt_failures; /* # of packet decrypted failed */
__le32 in; /* seconds elapsed since associated */
__le32 listen_interval_inms; /* Min Listen interval in ms for STA */
__le32 tx_pkts; /* # of packets transmitted */
__le32 tx_failures; /* # of packets failed */
__le32 rx_ucast_pkts; /* # of unicast packets received */
__le32 rx_mcast_pkts; /* # of multicast packets received */
__le32 tx_rate; /* Rate of last successful tx frame */
__le32 rx_rate; /* Rate of last successful rx frame */
__le32 rx_decrypt_succeeds; /* # of packet decrypted successfully */
__le32 rx_decrypt_failures; /* # of packet decrypted failed */
__le32 tx_tot_pkts; /* # of tx pkts (ucast + mcast) */
__le32 rx_tot_pkts; /* # of data packets recvd (uni + mcast) */
__le32 tx_mcast_pkts; /* # of mcast pkts txed */
__le64 tx_tot_bytes; /* data bytes txed (ucast + mcast) */
__le64 rx_tot_bytes; /* data bytes recvd (ucast + mcast) */
__le64 tx_ucast_bytes; /* data bytes txed (ucast) */
__le64 tx_mcast_bytes; /* # data bytes txed (mcast) */
__le64 rx_ucast_bytes; /* data bytes recvd (ucast) */
__le64 rx_mcast_bytes; /* data bytes recvd (mcast) */
s8 rssi[BRCMF_ANT_MAX]; /* per antenna rssi */
s8 nf[BRCMF_ANT_MAX]; /* per antenna noise floor */
__le16 aid; /* association ID */
__le16 ht_capabilities; /* advertised ht caps */
__le16 vht_flags; /* converted vht flags */
__le32 tx_pkts_retry_cnt; /* # of frames where a retry was
* exhausted.
*/
__le32 tx_pkts_retry_exhausted; /* # of user frames where a retry
* was exhausted
*/
s8 rx_lastpkt_rssi[BRCMF_ANT_MAX]; /* Per antenna RSSI of last
* received data frame.
*/
/* TX WLAN retry/failure statistics:
* Separated for host requested frames and locally generated frames.
* Include unicast frame only where the retries/failures can be counted.
*/
__le32 tx_pkts_total; /* # user frames sent successfully */
__le32 tx_pkts_retries; /* # user frames retries */
__le32 tx_pkts_fw_total; /* # FW generated sent successfully */
__le32 tx_pkts_fw_retries; /* # retries for FW generated frames */
__le32 tx_pkts_fw_retry_exhausted; /* # FW generated where a retry
* was exhausted
*/
__le32 rx_pkts_retried; /* # rx with retry bit set */
__le32 tx_rate_fallback; /* lowest fallback TX rate */
};
struct brcmf_chanspec_list {

View File

@@ -75,6 +75,8 @@
#define BRCMF_MSGBUF_DELAY_TXWORKER_THRS 96
#define BRCMF_MSGBUF_TRICKLE_TXWORKER_THRS 32
#define BRCMF_MSGBUF_UPDATE_RX_PTR_THRS 48
struct msgbuf_common_hdr {
u8 msgtype;
@@ -1257,19 +1259,27 @@ static void brcmf_msgbuf_process_rx(struct brcmf_msgbuf *msgbuf,
{
void *buf;
u16 count;
u16 processed;
again:
buf = brcmf_commonring_get_read_ptr(commonring, &count);
if (buf == NULL)
return;
processed = 0;
while (count) {
brcmf_msgbuf_process_msgtype(msgbuf,
buf + msgbuf->rx_dataoffset);
buf += brcmf_commonring_len_item(commonring);
processed++;
if (processed == BRCMF_MSGBUF_UPDATE_RX_PTR_THRS) {
brcmf_commonring_read_complete(commonring, processed);
processed = 0;
}
count--;
}
brcmf_commonring_read_complete(commonring);
if (processed)
brcmf_commonring_read_complete(commonring, processed);
if (commonring->r_ptr == 0)
goto again;

View File

@@ -16,6 +16,7 @@
#include <linux/slab.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/rtnetlink.h>
#include <net/cfg80211.h>
#include <brcmu_wifi.h>
@@ -1907,105 +1908,6 @@ s32 brcmf_p2p_notify_rx_mgmt_p2p_probereq(struct brcmf_if *ifp,
}
/**
* brcmf_p2p_attach() - attach for P2P.
*
* @cfg: driver private data for cfg80211 interface.
*/
s32 brcmf_p2p_attach(struct brcmf_cfg80211_info *cfg)
{
struct brcmf_if *pri_ifp;
struct brcmf_if *p2p_ifp;
struct brcmf_cfg80211_vif *p2p_vif;
struct brcmf_p2p_info *p2p;
struct brcmf_pub *drvr;
s32 bssidx;
s32 err = 0;
p2p = &cfg->p2p;
p2p->cfg = cfg;
drvr = cfg->pub;
pri_ifp = drvr->iflist[0];
p2p_ifp = drvr->iflist[1];
p2p->bss_idx[P2PAPI_BSSCFG_PRIMARY].vif = pri_ifp->vif;
if (p2p_ifp) {
p2p_vif = brcmf_alloc_vif(cfg, NL80211_IFTYPE_P2P_DEVICE,
false);
if (IS_ERR(p2p_vif)) {
brcmf_err("could not create discovery vif\n");
err = -ENOMEM;
goto exit;
}
p2p_vif->ifp = p2p_ifp;
p2p_ifp->vif = p2p_vif;
p2p_vif->wdev.netdev = p2p_ifp->ndev;
p2p_ifp->ndev->ieee80211_ptr = &p2p_vif->wdev;
SET_NETDEV_DEV(p2p_ifp->ndev, wiphy_dev(cfg->wiphy));
p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif = p2p_vif;
brcmf_p2p_generate_bss_mac(p2p, NULL);
memcpy(p2p_ifp->mac_addr, p2p->dev_addr, ETH_ALEN);
brcmf_p2p_set_firmware(pri_ifp, p2p->dev_addr);
/* Initialize P2P Discovery in the firmware */
err = brcmf_fil_iovar_int_set(pri_ifp, "p2p_disc", 1);
if (err < 0) {
brcmf_err("set p2p_disc error\n");
brcmf_free_vif(p2p_vif);
goto exit;
}
/* obtain bsscfg index for P2P discovery */
err = brcmf_fil_iovar_int_get(pri_ifp, "p2p_dev", &bssidx);
if (err < 0) {
brcmf_err("retrieving discover bsscfg index failed\n");
brcmf_free_vif(p2p_vif);
goto exit;
}
/* Verify that firmware uses same bssidx as driver !! */
if (p2p_ifp->bssidx != bssidx) {
brcmf_err("Incorrect bssidx=%d, compared to p2p_ifp->bssidx=%d\n",
bssidx, p2p_ifp->bssidx);
brcmf_free_vif(p2p_vif);
goto exit;
}
init_completion(&p2p->send_af_done);
INIT_WORK(&p2p->afx_hdl.afx_work, brcmf_p2p_afx_handler);
init_completion(&p2p->afx_hdl.act_frm_scan);
init_completion(&p2p->wait_next_af);
}
exit:
return err;
}
/**
* brcmf_p2p_detach() - detach P2P.
*
* @p2p: P2P specific data.
*/
void brcmf_p2p_detach(struct brcmf_p2p_info *p2p)
{
struct brcmf_cfg80211_vif *vif;
vif = p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif;
if (vif != NULL) {
brcmf_p2p_cancel_remain_on_channel(vif->ifp);
brcmf_p2p_deinit_discovery(p2p);
/* remove discovery interface */
brcmf_free_vif(vif);
p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif = NULL;
}
/* just set it all to zero */
memset(p2p, 0, sizeof(*p2p));
}
/**
* brcmf_p2p_get_current_chanspec() - Get current operation channel.
*
@@ -2238,6 +2140,7 @@ static void brcmf_p2p_delete_p2pdev(struct brcmf_p2p_info *p2p,
{
cfg80211_unregister_wdev(&vif->wdev);
p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif = NULL;
brcmf_remove_interface(vif->ifp->drvr, vif->ifp->bssidx);
brcmf_free_vif(vif);
}
@@ -2364,6 +2267,8 @@ int brcmf_p2p_del_vif(struct wiphy *wiphy, struct wireless_dev *wdev)
break;
case NL80211_IFTYPE_P2P_DEVICE:
brcmf_p2p_cancel_remain_on_channel(vif->ifp);
brcmf_p2p_deinit_discovery(p2p);
brcmf_p2p_delete_p2pdev(p2p, vif);
return 0;
default:
@@ -2425,3 +2330,103 @@ void brcmf_p2p_stop_device(struct wiphy *wiphy, struct wireless_dev *wdev)
clear_bit(BRCMF_VIF_STATUS_READY, &vif->sme_state);
mutex_unlock(&cfg->usr_sync);
}
/**
* brcmf_p2p_attach() - attach for P2P.
*
* @cfg: driver private data for cfg80211 interface.
*/
s32 brcmf_p2p_attach(struct brcmf_cfg80211_info *cfg)
{
struct brcmf_if *pri_ifp;
struct brcmf_if *p2p_ifp;
struct brcmf_cfg80211_vif *p2p_vif;
struct brcmf_p2p_info *p2p;
struct brcmf_pub *drvr;
s32 bssidx;
s32 err = 0;
p2p = &cfg->p2p;
p2p->cfg = cfg;
drvr = cfg->pub;
pri_ifp = drvr->iflist[0];
p2p_ifp = drvr->iflist[1];
p2p->bss_idx[P2PAPI_BSSCFG_PRIMARY].vif = pri_ifp->vif;
if (p2p_ifp) {
p2p_vif = brcmf_alloc_vif(cfg, NL80211_IFTYPE_P2P_DEVICE,
false);
if (IS_ERR(p2p_vif)) {
brcmf_err("could not create discovery vif\n");
err = -ENOMEM;
goto exit;
}
p2p_vif->ifp = p2p_ifp;
p2p_ifp->vif = p2p_vif;
p2p_vif->wdev.netdev = p2p_ifp->ndev;
p2p_ifp->ndev->ieee80211_ptr = &p2p_vif->wdev;
SET_NETDEV_DEV(p2p_ifp->ndev, wiphy_dev(cfg->wiphy));
p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif = p2p_vif;
brcmf_p2p_generate_bss_mac(p2p, NULL);
memcpy(p2p_ifp->mac_addr, p2p->dev_addr, ETH_ALEN);
brcmf_p2p_set_firmware(pri_ifp, p2p->dev_addr);
/* Initialize P2P Discovery in the firmware */
err = brcmf_fil_iovar_int_set(pri_ifp, "p2p_disc", 1);
if (err < 0) {
brcmf_err("set p2p_disc error\n");
brcmf_free_vif(p2p_vif);
goto exit;
}
/* obtain bsscfg index for P2P discovery */
err = brcmf_fil_iovar_int_get(pri_ifp, "p2p_dev", &bssidx);
if (err < 0) {
brcmf_err("retrieving discover bsscfg index failed\n");
brcmf_free_vif(p2p_vif);
goto exit;
}
/* Verify that firmware uses same bssidx as driver !! */
if (p2p_ifp->bssidx != bssidx) {
brcmf_err("Incorrect bssidx=%d, compared to p2p_ifp->bssidx=%d\n",
bssidx, p2p_ifp->bssidx);
brcmf_free_vif(p2p_vif);
goto exit;
}
init_completion(&p2p->send_af_done);
INIT_WORK(&p2p->afx_hdl.afx_work, brcmf_p2p_afx_handler);
init_completion(&p2p->afx_hdl.act_frm_scan);
init_completion(&p2p->wait_next_af);
}
exit:
return err;
}
/**
* brcmf_p2p_detach() - detach P2P.
*
* @p2p: P2P specific data.
*/
void brcmf_p2p_detach(struct brcmf_p2p_info *p2p)
{
struct brcmf_cfg80211_vif *vif;
vif = p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif;
if (vif != NULL) {
brcmf_p2p_cancel_remain_on_channel(vif->ifp);
brcmf_p2p_deinit_discovery(p2p);
/* remove discovery interface */
rtnl_lock();
brcmf_p2p_delete_p2pdev(p2p, vif);
rtnl_unlock();
}
/* just set it all to zero */
memset(p2p, 0, sizeof(*p2p));
}

View File

@@ -1629,20 +1629,7 @@ static void brcmf_pcie_buscore_write32(void *ctx, u32 addr, u32 value)
static int brcmf_pcie_buscoreprep(void *ctx)
{
struct brcmf_pciedev_info *devinfo = (struct brcmf_pciedev_info *)ctx;
int err;
err = brcmf_pcie_get_resource(devinfo);
if (err == 0) {
/* Set CC watchdog to reset all the cores on the chip to bring
* back dongle to a sane state.
*/
brcmf_pcie_buscore_write32(ctx, CORE_CC_REG(SI_ENUM_BASE,
watchdog), 4);
msleep(100);
}
return err;
return brcmf_pcie_get_resource(ctx);
}
@@ -1824,6 +1811,7 @@ brcmf_pcie_remove(struct pci_dev *pdev)
brcmf_pcie_intr_disable(devinfo);
brcmf_detach(&pdev->dev);
brcmf_pcie_reset_device(devinfo);
kfree(bus->bus_priv.pcie);
kfree(bus->msgbuf->flowrings);

View File

@@ -2820,6 +2820,8 @@ static int brcmf_sdio_bus_txdata(struct device *dev, struct sk_buff *pkt)
struct brcmf_sdio *bus = sdiodev->bus;
brcmf_dbg(TRACE, "Enter: pkt: data %p len %d\n", pkt->data, pkt->len);
if (sdiodev->state != BRCMF_SDIOD_DATA)
return -EIO;
/* Add space for the header */
skb_push(pkt, bus->tx_hdrlen);
@@ -2948,6 +2950,8 @@ brcmf_sdio_bus_txctl(struct device *dev, unsigned char *msg, uint msglen)
int ret;
brcmf_dbg(TRACE, "Enter\n");
if (sdiodev->state != BRCMF_SDIOD_DATA)
return -EIO;
/* Send from dpc */
bus->ctrl_frame_buf = msg;
@@ -3238,6 +3242,8 @@ brcmf_sdio_bus_rxctl(struct device *dev, unsigned char *msg, uint msglen)
struct brcmf_sdio *bus = sdiodev->bus;
brcmf_dbg(TRACE, "Enter\n");
if (sdiodev->state != BRCMF_SDIOD_DATA)
return -EIO;
/* Wait until control frame is available */
timeleft = brcmf_sdio_dcmd_resp_wait(bus, &bus->rxlen, &pending);