diff options
author | José Pekkarinen <jose.pekkarinen@nokia.com> | 2016-04-11 10:41:07 +0300 |
---|---|---|
committer | José Pekkarinen <jose.pekkarinen@nokia.com> | 2016-04-13 08:17:18 +0300 |
commit | e09b41010ba33a20a87472ee821fa407a5b8da36 (patch) | |
tree | d10dc367189862e7ca5c592f033dc3726e1df4e3 /kernel/drivers/net/wireless/brcm80211/brcmfmac | |
parent | f93b97fd65072de626c074dbe099a1fff05ce060 (diff) |
These changes are the raw update to linux-4.4.6-rt14. Kernel sources
are taken from kernel.org, and rt patch from the rt wiki download page.
During the rebasing, the following patch collided:
Force tick interrupt and get rid of softirq magic(I70131fb85).
Collisions have been removed because its logic was found on the
source already.
Change-Id: I7f57a4081d9deaa0d9ccfc41a6c8daccdee3b769
Signed-off-by: José Pekkarinen <jose.pekkarinen@nokia.com>
Diffstat (limited to 'kernel/drivers/net/wireless/brcm80211/brcmfmac')
35 files changed, 1964 insertions, 943 deletions
diff --git a/kernel/drivers/net/wireless/brcm80211/brcmfmac/bcdc.c b/kernel/drivers/net/wireless/brcm80211/brcmfmac/bcdc.c index 8e0e91c4a..288c84e7c 100644 --- a/kernel/drivers/net/wireless/brcm80211/brcmfmac/bcdc.c +++ b/kernel/drivers/net/wireless/brcm80211/brcmfmac/bcdc.c @@ -272,10 +272,11 @@ brcmf_proto_bcdc_hdrpush(struct brcmf_pub *drvr, int ifidx, u8 offset, } static int -brcmf_proto_bcdc_hdrpull(struct brcmf_pub *drvr, bool do_fws, u8 *ifidx, - struct sk_buff *pktbuf) +brcmf_proto_bcdc_hdrpull(struct brcmf_pub *drvr, bool do_fws, + struct sk_buff *pktbuf, struct brcmf_if **ifp) { struct brcmf_proto_bcdc_header *h; + struct brcmf_if *tmp_if; brcmf_dbg(BCDC, "Enter\n"); @@ -289,30 +290,21 @@ brcmf_proto_bcdc_hdrpull(struct brcmf_pub *drvr, bool do_fws, u8 *ifidx, trace_brcmf_bcdchdr(pktbuf->data); h = (struct brcmf_proto_bcdc_header *)(pktbuf->data); - *ifidx = BCDC_GET_IF_IDX(h); - if (*ifidx >= BRCMF_MAX_IFS) { - brcmf_err("rx data ifnum out of range (%d)\n", *ifidx); + tmp_if = brcmf_get_ifp(drvr, BCDC_GET_IF_IDX(h)); + if (!tmp_if) { + brcmf_dbg(INFO, "no matching ifp found\n"); return -EBADE; } - /* The ifidx is the idx to map to matching netdev/ifp. When receiving - * events this is easy because it contains the bssidx which maps - * 1-on-1 to the netdev/ifp. But for data frames the ifidx is rcvd. - * bssidx 1 is used for p2p0 and no data can be received or - * transmitted on it. Therefor bssidx is ifidx + 1 if ifidx > 0 - */ - if (*ifidx) - (*ifidx)++; - if (((h->flags & BCDC_FLAG_VER_MASK) >> BCDC_FLAG_VER_SHIFT) != BCDC_PROTO_VER) { brcmf_err("%s: non-BCDC packet received, flags 0x%x\n", - brcmf_ifname(drvr, *ifidx), h->flags); + brcmf_ifname(drvr, tmp_if->ifidx), h->flags); return -EBADE; } if (h->flags & BCDC_FLAG_SUM_GOOD) { brcmf_dbg(BCDC, "%s: BDC rcv, good checksum, flags 0x%x\n", - brcmf_ifname(drvr, *ifidx), h->flags); + brcmf_ifname(drvr, tmp_if->ifidx), h->flags); pktbuf->ip_summed = CHECKSUM_UNNECESSARY; } @@ -320,12 +312,14 @@ brcmf_proto_bcdc_hdrpull(struct brcmf_pub *drvr, bool do_fws, u8 *ifidx, skb_pull(pktbuf, BCDC_HEADER_LEN); if (do_fws) - brcmf_fws_hdrpull(drvr, *ifidx, h->data_offset << 2, pktbuf); + brcmf_fws_hdrpull(tmp_if, h->data_offset << 2, pktbuf); else skb_pull(pktbuf, h->data_offset << 2); if (pktbuf->len == 0) return -ENODATA; + + *ifp = tmp_if; return 0; } diff --git a/kernel/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c b/kernel/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c index 9b508bd3b..410a6645d 100644 --- a/kernel/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c +++ b/kernel/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c @@ -33,6 +33,7 @@ #include <linux/suspend.h> #include <linux/errno.h> #include <linux/module.h> +#include <linux/acpi.h> #include <net/cfg80211.h> #include <defs.h> @@ -987,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; @@ -1011,6 +1013,14 @@ static int brcmf_sdiod_remove(struct brcmf_sdio_dev *sdiodev) return 0; } +static void brcmf_sdiod_host_fixup(struct mmc_host *host) +{ + /* runtime-pm powers off the device */ + pm_runtime_forbid(host->parent); + /* avoid removal detection upon resume */ + host->caps |= MMC_CAP_NONREMOVABLE; +} + static int brcmf_sdiod_probe(struct brcmf_sdio_dev *sdiodev) { struct sdio_func *func; @@ -1076,7 +1086,7 @@ static int brcmf_sdiod_probe(struct brcmf_sdio_dev *sdiodev) ret = -ENODEV; goto out; } - pm_runtime_forbid(host->parent); + brcmf_sdiod_host_fixup(host); out: if (ret) brcmf_sdiod_remove(sdiodev); @@ -1108,12 +1118,25 @@ MODULE_DEVICE_TABLE(sdio, brcmf_sdmmc_ids); static struct brcmfmac_sdio_platform_data *brcmfmac_sdio_pdata; +static void brcmf_sdiod_acpi_set_power_manageable(struct device *dev, + int val) +{ +#if IS_ENABLED(CONFIG_ACPI) + struct acpi_device *adev; + + adev = ACPI_COMPANION(dev); + if (adev) + adev->flags.power_manageable = 0; +#endif +} + static int brcmf_ops_sdio_probe(struct sdio_func *func, const struct sdio_device_id *id) { int err; struct brcmf_sdio_dev *sdiodev; struct brcmf_bus *bus_if; + struct device *dev; brcmf_dbg(SDIO, "Enter\n"); brcmf_dbg(SDIO, "Class=%x\n", func->class); @@ -1121,6 +1144,10 @@ static int brcmf_ops_sdio_probe(struct sdio_func *func, brcmf_dbg(SDIO, "sdio device ID: 0x%04x\n", func->device); brcmf_dbg(SDIO, "Function#: %d\n", func->num); + dev = &func->dev; + /* prohibit ACPI power management for this device */ + brcmf_sdiod_acpi_set_power_manageable(dev, 0); + /* Consume func num 1 but dont do anything with it. */ if (func->num == 1) return 0; @@ -1246,15 +1273,15 @@ static int brcmf_ops_sdio_suspend(struct device *dev) brcmf_sdiod_freezer_on(sdiodev); brcmf_sdio_wd_timer(sdiodev->bus, 0); + sdio_flags = MMC_PM_KEEP_POWER; if (sdiodev->wowl_enabled) { - sdio_flags = MMC_PM_KEEP_POWER; if (sdiodev->pdata->oob_irq_supported) enable_irq_wake(sdiodev->pdata->oob_irq_nr); else - sdio_flags = MMC_PM_WAKE_SDIO_IRQ; - if (sdio_set_host_pm_flags(sdiodev->func[1], sdio_flags)) - brcmf_err("Failed to set pm_flags %x\n", sdio_flags); + sdio_flags |= MMC_PM_WAKE_SDIO_IRQ; } + if (sdio_set_host_pm_flags(sdiodev->func[1], sdio_flags)) + brcmf_err("Failed to set pm_flags %x\n", sdio_flags); return 0; } diff --git a/kernel/drivers/net/wireless/brcm80211/brcmfmac/btcoex.c b/kernel/drivers/net/wireless/brcm80211/brcmfmac/btcoex.c index 044516399..4e33f96b3 100644 --- a/kernel/drivers/net/wireless/brcm80211/brcmfmac/btcoex.c +++ b/kernel/drivers/net/wireless/brcm80211/brcmfmac/btcoex.c @@ -149,7 +149,7 @@ static s32 brcmf_btcoex_params_read(struct brcmf_if *ifp, u32 addr, u32 *data) static void brcmf_btcoex_boost_wifi(struct brcmf_btcoex_info *btci, bool trump_sco) { - struct brcmf_if *ifp = btci->cfg->pub->iflist[0]; + struct brcmf_if *ifp = brcmf_get_ifp(btci->cfg->pub, 0); if (trump_sco && !btci->saved_regs_part2) { /* this should reduce eSCO agressive @@ -468,7 +468,7 @@ int brcmf_btcoex_set_mode(struct brcmf_cfg80211_vif *vif, { struct brcmf_cfg80211_info *cfg = wiphy_priv(vif->wdev.wiphy); struct brcmf_btcoex_info *btci = cfg->btcoex; - struct brcmf_if *ifp = cfg->pub->iflist[0]; + struct brcmf_if *ifp = brcmf_get_ifp(cfg->pub, 0); switch (mode) { case BRCMF_BTCOEX_DISABLED: diff --git a/kernel/drivers/net/wireless/brcm80211/brcmfmac/bus.h b/kernel/drivers/net/wireless/brcm80211/brcmfmac/bus.h index 89e6a4dc1..230cad788 100644 --- a/kernel/drivers/net/wireless/brcm80211/brcmfmac/bus.h +++ b/kernel/drivers/net/wireless/brcm80211/brcmfmac/bus.h @@ -65,6 +65,8 @@ struct brcmf_bus_dcmd { * @rxctl: receive a control response message from dongle. * @gettxq: obtain a reference of bus transmit queue (optional). * @wowl_config: specify if dongle is configured for wowl when going to suspend + * @get_ramsize: obtain size of device memory. + * @get_memdump: obtain device memory dump in provided buffer. * * This structure provides an abstract interface towards the * bus specific driver. For control messages to common driver @@ -79,6 +81,8 @@ struct brcmf_bus_ops { int (*rxctl)(struct device *dev, unsigned char *msg, uint len); struct pktq * (*gettxq)(struct device *dev); void (*wowl_config)(struct device *dev, bool enabled); + size_t (*get_ramsize)(struct device *dev); + int (*get_memdump)(struct device *dev, void *data, size_t len); }; @@ -185,6 +189,23 @@ void brcmf_bus_wowl_config(struct brcmf_bus *bus, bool enabled) bus->ops->wowl_config(bus->dev, enabled); } +static inline size_t brcmf_bus_get_ramsize(struct brcmf_bus *bus) +{ + if (!bus->ops->get_ramsize) + return 0; + + return bus->ops->get_ramsize(bus->dev); +} + +static inline +int brcmf_bus_get_memdump(struct brcmf_bus *bus, void *data, size_t len) +{ + if (!bus->ops->get_memdump) + return -EOPNOTSUPP; + + return bus->ops->get_memdump(bus->dev, data, len); +} + /* * interface functions from common layer */ diff --git a/kernel/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c b/kernel/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c index 8a15ebbce..deb5f78dc 100644 --- a/kernel/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c +++ b/kernel/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c @@ -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 */ @@ -129,13 +127,47 @@ static struct ieee80211_rate __wl_rates[] = { RATETAB_ENT(BRCM_RATE_54M, 0), }; -#define wl_a_rates (__wl_rates + 4) -#define wl_a_rates_size 8 #define wl_g_rates (__wl_rates + 0) -#define wl_g_rates_size 12 +#define wl_g_rates_size ARRAY_SIZE(__wl_rates) +#define wl_a_rates (__wl_rates + 4) +#define wl_a_rates_size (wl_g_rates_size - 4) + +#define CHAN2G(_channel, _freq) { \ + .band = IEEE80211_BAND_2GHZ, \ + .center_freq = (_freq), \ + .hw_value = (_channel), \ + .flags = IEEE80211_CHAN_DISABLED, \ + .max_antenna_gain = 0, \ + .max_power = 30, \ +} + +#define CHAN5G(_channel) { \ + .band = IEEE80211_BAND_5GHZ, \ + .center_freq = 5000 + (5 * (_channel)), \ + .hw_value = (_channel), \ + .flags = IEEE80211_CHAN_DISABLED, \ + .max_antenna_gain = 0, \ + .max_power = 30, \ +} + +static struct ieee80211_channel __wl_2ghz_channels[] = { + CHAN2G(1, 2412), CHAN2G(2, 2417), CHAN2G(3, 2422), CHAN2G(4, 2427), + CHAN2G(5, 2432), CHAN2G(6, 2437), CHAN2G(7, 2442), CHAN2G(8, 2447), + CHAN2G(9, 2452), CHAN2G(10, 2457), CHAN2G(11, 2462), CHAN2G(12, 2467), + CHAN2G(13, 2472), CHAN2G(14, 2484) +}; + +static struct ieee80211_channel __wl_5ghz_channels[] = { + CHAN5G(34), CHAN5G(36), CHAN5G(38), CHAN5G(40), CHAN5G(42), + CHAN5G(44), CHAN5G(46), CHAN5G(48), CHAN5G(52), CHAN5G(56), + CHAN5G(60), CHAN5G(64), CHAN5G(100), CHAN5G(104), CHAN5G(108), + CHAN5G(112), CHAN5G(116), CHAN5G(120), CHAN5G(124), CHAN5G(128), + CHAN5G(132), CHAN5G(136), CHAN5G(140), CHAN5G(144), CHAN5G(149), + CHAN5G(153), CHAN5G(157), CHAN5G(161), CHAN5G(165) +}; /* Band templates duplicated per wiphy. The channel info - * is filled in after querying the device. + * above is added to the band during setup. */ static const struct ieee80211_supported_band __wl_band_2ghz = { .band = IEEE80211_BAND_2GHZ, @@ -143,7 +175,7 @@ static const struct ieee80211_supported_band __wl_band_2ghz = { .n_bitrates = wl_g_rates_size, }; -static const struct ieee80211_supported_band __wl_band_5ghz_a = { +static const struct ieee80211_supported_band __wl_band_5ghz = { .band = IEEE80211_BAND_5GHZ, .bitrates = wl_a_rates, .n_bitrates = wl_a_rates_size, @@ -204,89 +236,6 @@ static int brcmf_roamoff; module_param_named(roamoff, brcmf_roamoff, int, S_IRUSR); MODULE_PARM_DESC(roamoff, "do not use internal roaming engine"); -/* Quarter dBm units to mW - * Table starts at QDBM_OFFSET, so the first entry is mW for qdBm=153 - * Table is offset so the last entry is largest mW value that fits in - * a u16. - */ - -#define QDBM_OFFSET 153 /* Offset for first entry */ -#define QDBM_TABLE_LEN 40 /* Table size */ - -/* Smallest mW value that will round up to the first table entry, QDBM_OFFSET. - * Value is ( mW(QDBM_OFFSET - 1) + mW(QDBM_OFFSET) ) / 2 - */ -#define QDBM_TABLE_LOW_BOUND 6493 /* Low bound */ - -/* Largest mW value that will round down to the last table entry, - * QDBM_OFFSET + QDBM_TABLE_LEN-1. - * Value is ( mW(QDBM_OFFSET + QDBM_TABLE_LEN - 1) + - * mW(QDBM_OFFSET + QDBM_TABLE_LEN) ) / 2. - */ -#define QDBM_TABLE_HIGH_BOUND 64938 /* High bound */ - -static const u16 nqdBm_to_mW_map[QDBM_TABLE_LEN] = { -/* qdBm: +0 +1 +2 +3 +4 +5 +6 +7 */ -/* 153: */ 6683, 7079, 7499, 7943, 8414, 8913, 9441, 10000, -/* 161: */ 10593, 11220, 11885, 12589, 13335, 14125, 14962, 15849, -/* 169: */ 16788, 17783, 18836, 19953, 21135, 22387, 23714, 25119, -/* 177: */ 26607, 28184, 29854, 31623, 33497, 35481, 37584, 39811, -/* 185: */ 42170, 44668, 47315, 50119, 53088, 56234, 59566, 63096 -}; - -static u16 brcmf_qdbm_to_mw(u8 qdbm) -{ - uint factor = 1; - int idx = qdbm - QDBM_OFFSET; - - if (idx >= QDBM_TABLE_LEN) - /* clamp to max u16 mW value */ - return 0xFFFF; - - /* scale the qdBm index up to the range of the table 0-40 - * where an offset of 40 qdBm equals a factor of 10 mW. - */ - while (idx < 0) { - idx += 40; - factor *= 10; - } - - /* return the mW value scaled down to the correct factor of 10, - * adding in factor/2 to get proper rounding. - */ - return (nqdBm_to_mW_map[idx] + factor / 2) / factor; -} - -static u8 brcmf_mw_to_qdbm(u16 mw) -{ - u8 qdbm; - int offset; - uint mw_uint = mw; - uint boundary; - - /* handle boundary case */ - if (mw_uint <= 1) - return 0; - - offset = QDBM_OFFSET; - - /* move mw into the range of the table */ - while (mw_uint < QDBM_TABLE_LOW_BOUND) { - mw_uint *= 10; - offset -= 40; - } - - for (qdbm = 0; qdbm < QDBM_TABLE_LEN - 1; qdbm++) { - boundary = nqdBm_to_mW_map[qdbm] + (nqdBm_to_mW_map[qdbm + 1] - - nqdBm_to_mW_map[qdbm]) / 2; - if (mw_uint < boundary) - break; - } - - qdbm += (u8) offset; - - return qdbm; -} static u16 chandef_to_chanspec(struct brcmu_d11inf *d11inf, struct cfg80211_chan_def *ch) @@ -437,6 +386,36 @@ brcmf_find_wpsie(const u8 *parse, u32 len) return NULL; } +static int brcmf_vif_change_validate(struct brcmf_cfg80211_info *cfg, + struct brcmf_cfg80211_vif *vif, + enum nl80211_iftype new_type) +{ + int iftype_num[NUM_NL80211_IFTYPES]; + struct brcmf_cfg80211_vif *pos; + + memset(&iftype_num[0], 0, sizeof(iftype_num)); + list_for_each_entry(pos, &cfg->vif_list, list) + if (pos == vif) + iftype_num[new_type]++; + else + iftype_num[pos->wdev.iftype]++; + + return cfg80211_check_combinations(cfg->wiphy, 1, 0, iftype_num); +} + +static int brcmf_vif_add_validate(struct brcmf_cfg80211_info *cfg, + enum nl80211_iftype new_type) +{ + int iftype_num[NUM_NL80211_IFTYPES]; + struct brcmf_cfg80211_vif *pos; + + memset(&iftype_num[0], 0, sizeof(iftype_num)); + list_for_each_entry(pos, &cfg->vif_list, list) + iftype_num[pos->wdev.iftype]++; + + iftype_num[new_type]++; + return cfg80211_check_combinations(cfg->wiphy, 1, 0, iftype_num); +} static void convert_key_from_CPU(struct brcmf_wsec_key *key, struct brcmf_wsec_key_le *key_le) @@ -631,8 +610,14 @@ static struct wireless_dev *brcmf_cfg80211_add_iface(struct wiphy *wiphy, struct vif_params *params) { struct wireless_dev *wdev; + int err; brcmf_dbg(TRACE, "enter: %s type %d\n", name, type); + err = brcmf_vif_add_validate(wiphy_to_cfg(wiphy), type); + if (err) { + brcmf_err("iface validation failed: err=%d\n", err); + return ERR_PTR(err); + } switch (type) { case NL80211_IFTYPE_ADHOC: case NL80211_IFTYPE_STATION: @@ -791,8 +776,43 @@ brcmf_cfg80211_change_iface(struct wiphy *wiphy, struct net_device *ndev, s32 ap = 0; s32 err = 0; - brcmf_dbg(TRACE, "Enter, ndev=%p, type=%d\n", ndev, type); + brcmf_dbg(TRACE, "Enter, idx=%d, type=%d\n", ifp->bssidx, type); + /* WAR: There are a number of p2p interface related problems which + * need to be handled initially (before doing the validate). + * wpa_supplicant tends to do iface changes on p2p device/client/go + * which are not always possible/allowed. However we need to return + * OK otherwise the wpa_supplicant wont start. The situation differs + * on configuration and setup (p2pon=1 module param). The first check + * is to see if the request is a change to station for p2p iface. + */ + if ((type == NL80211_IFTYPE_STATION) && + ((vif->wdev.iftype == NL80211_IFTYPE_P2P_CLIENT) || + (vif->wdev.iftype == NL80211_IFTYPE_P2P_GO) || + (vif->wdev.iftype == NL80211_IFTYPE_P2P_DEVICE))) { + brcmf_dbg(TRACE, "Ignoring cmd for p2p if\n"); + /* Now depending on whether module param p2pon=1 was used the + * response needs to be either 0 or EOPNOTSUPP. The reason is + * that if p2pon=1 is used, but a newer supplicant is used then + * we should return an error, as this combination wont work. + * In other situations 0 is returned and supplicant will start + * normally. It will give a trace in cfg80211, but it is the + * only way to get it working. Unfortunately this will result + * in situation where we wont support new supplicant in + * combination with module param p2pon=1, but that is the way + * it is. If the user tries this then unloading of driver might + * fail/lock. + */ + if (cfg->p2p.p2pdev_dynamically) + return -EOPNOTSUPP; + else + return 0; + } + err = brcmf_vif_change_validate(wiphy_to_cfg(wiphy), vif, type); + if (err) { + brcmf_err("iface validation failed: err=%d\n", err); + return err; + } switch (type) { case NL80211_IFTYPE_MONITOR: case NL80211_IFTYPE_WDS: @@ -803,18 +823,6 @@ brcmf_cfg80211_change_iface(struct wiphy *wiphy, struct net_device *ndev, infra = 0; break; case NL80211_IFTYPE_STATION: - /* Ignore change for p2p IF. Unclear why supplicant does this */ - if ((vif->wdev.iftype == NL80211_IFTYPE_P2P_CLIENT) || - (vif->wdev.iftype == NL80211_IFTYPE_P2P_GO)) { - brcmf_dbg(TRACE, "Ignoring cmd for p2p if\n"); - /* WAR: It is unexpected to get a change of VIF for P2P - * IF, but it happens. The request can not be handled - * but returning EPERM causes a crash. Returning 0 - * without setting ieee80211_ptr->iftype causes trace - * (WARN_ON) but it works with wpa_supplicant - */ - return 0; - } infra = 1; break; case NL80211_IFTYPE_AP: @@ -832,7 +840,6 @@ brcmf_cfg80211_change_iface(struct wiphy *wiphy, struct net_device *ndev, err = brcmf_p2p_ifchange(cfg, BRCMF_FIL_P2P_IF_GO); } if (!err) { - set_bit(BRCMF_VIF_STATUS_AP_CREATING, &vif->sme_state); brcmf_dbg(INFO, "IF Type = AP\n"); } } else { @@ -1262,7 +1269,7 @@ static void brcmf_link_down(struct brcmf_cfg80211_vif *vif, u16 reason) } clear_bit(BRCMF_VIF_STATUS_CONNECTED, &vif->sme_state); cfg80211_disconnected(vif->wdev.netdev, reason, NULL, 0, - GFP_KERNEL); + true, GFP_KERNEL); } clear_bit(BRCMF_VIF_STATUS_CONNECTING, &vif->sme_state); @@ -1928,7 +1935,7 @@ brcmf_cfg80211_disconnect(struct wiphy *wiphy, struct net_device *ndev, clear_bit(BRCMF_VIF_STATUS_CONNECTED, &ifp->vif->sme_state); clear_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state); - cfg80211_disconnected(ndev, reason_code, NULL, 0, GFP_KERNEL); + cfg80211_disconnected(ndev, reason_code, NULL, 0, true, GFP_KERNEL); memcpy(&scbval.ea, &profile->bssid, ETH_ALEN); scbval.val = cpu_to_le32(reason_code); @@ -1945,16 +1952,14 @@ static s32 brcmf_cfg80211_set_tx_power(struct wiphy *wiphy, struct wireless_dev *wdev, enum nl80211_tx_power_setting type, s32 mbm) { - struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); struct net_device *ndev = cfg_to_ndev(cfg); struct brcmf_if *ifp = netdev_priv(ndev); - u16 txpwrmw; - s32 err = 0; - s32 disable = 0; - s32 dbm = MBM_TO_DBM(mbm); + s32 err; + s32 disable; + u32 qdbm = 127; - brcmf_dbg(TRACE, "Enter\n"); + brcmf_dbg(TRACE, "Enter %d %d\n", type, mbm); if (!check_vif_up(ifp->vif)) return -EIO; @@ -1963,12 +1968,20 @@ brcmf_cfg80211_set_tx_power(struct wiphy *wiphy, struct wireless_dev *wdev, break; case NL80211_TX_POWER_LIMITED: case NL80211_TX_POWER_FIXED: - if (dbm < 0) { + if (mbm < 0) { brcmf_err("TX_POWER_FIXED - dbm is negative\n"); err = -EINVAL; goto done; } + qdbm = MBM_TO_DBM(4 * mbm); + if (qdbm > 127) + qdbm = 127; + qdbm |= WL_TXPWR_OVERRIDE; break; + default: + brcmf_err("Unsupported type %d\n", type); + err = -EINVAL; + goto done; } /* Make sure radio is off or on as far as software is concerned */ disable = WL_RADIO_SW_DISABLE << 16; @@ -1976,52 +1989,44 @@ brcmf_cfg80211_set_tx_power(struct wiphy *wiphy, struct wireless_dev *wdev, if (err) brcmf_err("WLC_SET_RADIO error (%d)\n", err); - if (dbm > 0xffff) - txpwrmw = 0xffff; - else - txpwrmw = (u16) dbm; - err = brcmf_fil_iovar_int_set(ifp, "qtxpower", - (s32)brcmf_mw_to_qdbm(txpwrmw)); + err = brcmf_fil_iovar_int_set(ifp, "qtxpower", qdbm); if (err) brcmf_err("qtxpower error (%d)\n", err); - cfg->conf->tx_power = dbm; done: - brcmf_dbg(TRACE, "Exit\n"); + brcmf_dbg(TRACE, "Exit %d (qdbm)\n", qdbm & ~WL_TXPWR_OVERRIDE); return err; } -static s32 brcmf_cfg80211_get_tx_power(struct wiphy *wiphy, - struct wireless_dev *wdev, - s32 *dbm) +static s32 +brcmf_cfg80211_get_tx_power(struct wiphy *wiphy, struct wireless_dev *wdev, + s32 *dbm) { struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); - struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg)); - s32 txpwrdbm; - u8 result; - s32 err = 0; + struct net_device *ndev = cfg_to_ndev(cfg); + struct brcmf_if *ifp = netdev_priv(ndev); + s32 qdbm = 0; + s32 err; brcmf_dbg(TRACE, "Enter\n"); if (!check_vif_up(ifp->vif)) return -EIO; - err = brcmf_fil_iovar_int_get(ifp, "qtxpower", &txpwrdbm); + err = brcmf_fil_iovar_int_get(ifp, "qtxpower", &qdbm); if (err) { brcmf_err("error (%d)\n", err); goto done; } - - result = (u8) (txpwrdbm & ~WL_TXPWR_OVERRIDE); - *dbm = (s32) brcmf_qdbm_to_mw(result); + *dbm = (qdbm & ~WL_TXPWR_OVERRIDE) / 4; done: - brcmf_dbg(TRACE, "Exit\n"); + brcmf_dbg(TRACE, "Exit (0x%x %d)\n", qdbm, *dbm); return err; } static s32 brcmf_cfg80211_config_default_key(struct wiphy *wiphy, struct net_device *ndev, - u8 key_idx, bool unicast, bool multicast) + u8 key_idx, bool unicast, bool multicast) { struct brcmf_if *ifp = netdev_priv(ndev); u32 index; @@ -2364,27 +2369,83 @@ 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; + s32 total_rssi; + s32 count_rssi; + u32 i; 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)); @@ -2392,78 +2453,102 @@ 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, "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 = + le32_to_cpu(sta_info_le.tx_rate) / 100; } - 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; + if (sinfo->rx_packets) { + sinfo->filled |= BIT(NL80211_STA_INFO_RX_BITRATE); + sinfo->rxrate.legacy = + le32_to_cpu(sta_info_le.rx_rate) / 100; } - /* 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 { - sinfo->filled |= BIT(NL80211_STA_INFO_TX_BITRATE); - sinfo->txrate.legacy = rate * 5; - brcmf_dbg(CONN, "Rate %d Mbps\n", rate / 2); + 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); } - - 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); + total_rssi = 0; + count_rssi = 0; + for (i = 0; i < BRCMF_ANT_MAX; i++) { + if (sta_info_le.rssi[i]) { + sinfo->chain_signal_avg[count_rssi] = + sta_info_le.rssi[i]; + sinfo->chain_signal[count_rssi] = + sta_info_le.rssi[i]; + total_rssi += sta_info_le.rssi[i]; + count_rssi++; } - 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); } - } else - err = -EPERM; + if (count_rssi) { + sinfo->filled |= BIT(NL80211_STA_INFO_CHAIN_SIGNAL); + sinfo->chains = count_rssi; + + sinfo->filled |= BIT(NL80211_STA_INFO_SIGNAL); + total_rssi /= count_rssi; + sinfo->signal = total_rssi; + } + } done: brcmf_dbg(TRACE, "Exit\n"); return err; } +static int +brcmf_cfg80211_dump_station(struct wiphy *wiphy, struct net_device *ndev, + int idx, u8 *mac, struct station_info *sinfo) +{ + struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); + struct brcmf_if *ifp = netdev_priv(ndev); + s32 err; + + brcmf_dbg(TRACE, "Enter, idx %d\n", idx); + + if (idx == 0) { + cfg->assoclist.count = cpu_to_le32(BRCMF_MAX_ASSOCLIST); + err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_ASSOCLIST, + &cfg->assoclist, + sizeof(cfg->assoclist)); + if (err) { + brcmf_err("BRCMF_C_GET_ASSOCLIST unsupported, err=%d\n", + err); + cfg->assoclist.count = 0; + return -EOPNOTSUPP; + } + } + if (idx < le32_to_cpu(cfg->assoclist.count)) { + memcpy(mac, cfg->assoclist.mac[idx], ETH_ALEN); + return brcmf_cfg80211_get_station(wiphy, ndev, mac, sinfo); + } + return -ENOENT; +} + static s32 brcmf_cfg80211_set_power_mgmt(struct wiphy *wiphy, struct net_device *ndev, bool enabled, s32 timeout) @@ -4165,8 +4250,8 @@ brcmf_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *ndev, brcmf_dbg(TRACE, "GO mode configuration complete\n"); } - clear_bit(BRCMF_VIF_STATUS_AP_CREATING, &ifp->vif->sme_state); set_bit(BRCMF_VIF_STATUS_AP_CREATED, &ifp->vif->sme_state); + brcmf_net_setcarrier(ifp, true); exit: if ((err) && (!mbss)) { @@ -4230,8 +4315,8 @@ static int brcmf_cfg80211_stop_ap(struct wiphy *wiphy, struct net_device *ndev) } brcmf_set_mpc(ifp, 1); brcmf_configure_arp_offload(ifp, true); - set_bit(BRCMF_VIF_STATUS_AP_CREATING, &ifp->vif->sme_state); clear_bit(BRCMF_VIF_STATUS_AP_CREATED, &ifp->vif->sme_state); + brcmf_net_setcarrier(ifp, false); return err; } @@ -4563,6 +4648,7 @@ static struct cfg80211_ops wl_cfg80211_ops = { .join_ibss = brcmf_cfg80211_join_ibss, .leave_ibss = brcmf_cfg80211_leave_ibss, .get_station = brcmf_cfg80211_get_station, + .dump_station = brcmf_cfg80211_dump_station, .set_tx_power = brcmf_cfg80211_set_tx_power, .get_tx_power = brcmf_cfg80211_get_tx_power, .add_key = brcmf_cfg80211_add_key, @@ -4647,7 +4733,8 @@ void brcmf_cfg80211_free_netdev(struct net_device *ndev) ifp = netdev_priv(ndev); vif = ifp->vif; - brcmf_free_vif(vif); + if (vif) + brcmf_free_vif(vif); free_netdev(ndev); } @@ -4883,7 +4970,7 @@ brcmf_notify_connect_status_ap(struct brcmf_cfg80211_info *cfg, brcmf_dbg(CONN, "AP mode link down\n"); complete(&cfg->vif_disabled); if (ifp->vif->mbss) - brcmf_remove_interface(ifp->drvr, ifp->bssidx); + brcmf_remove_interface(ifp); return 0; } @@ -4939,6 +5026,7 @@ brcmf_notify_connect_status(struct brcmf_if *ifp, &ifp->vif->sme_state); } else brcmf_bss_connect_done(cfg, ndev, e, true); + brcmf_net_setcarrier(ifp, true); } else if (brcmf_is_linkdown(e)) { brcmf_dbg(CONN, "Linkdown\n"); if (!brcmf_is_ibssmode(ifp->vif)) { @@ -4948,6 +5036,7 @@ brcmf_notify_connect_status(struct brcmf_if *ifp, brcmf_init_prof(ndev_to_prof(ndev)); if (ndev != cfg_to_ndev(cfg)) complete(&cfg->vif_disabled); + brcmf_net_setcarrier(ifp, false); } else if (brcmf_is_nonetwork(cfg, e)) { if (brcmf_is_ibssmode(ifp->vif)) clear_bit(BRCMF_VIF_STATUS_CONNECTING, @@ -5253,40 +5342,6 @@ dongle_scantime_out: return err; } -/* Filter the list of channels received from firmware counting only - * the 20MHz channels. The wiphy band data only needs those which get - * flagged to indicate if they can take part in higher bandwidth. - */ -static void brcmf_count_20mhz_channels(struct brcmf_cfg80211_info *cfg, - struct brcmf_chanspec_list *chlist, - u32 chcnt[]) -{ - u32 total = le32_to_cpu(chlist->count); - struct brcmu_chan ch; - int i; - - for (i = 0; i < total; i++) { - ch.chspec = (u16)le32_to_cpu(chlist->element[i]); - cfg->d11inf.decchspec(&ch); - - /* Firmware gives a ordered list. We skip non-20MHz - * channels is 2G. For 5G we can abort upon reaching - * a non-20MHz channel in the list. - */ - if (ch.bw != BRCMU_CHAN_BW_20) { - if (ch.band == BRCMU_CHAN_BAND_5G) - break; - else - continue; - } - - if (ch.band == BRCMU_CHAN_BAND_2G) - chcnt[0] += 1; - else if (ch.band == BRCMU_CHAN_BAND_5G) - chcnt[1] += 1; - } -} - static void brcmf_update_bw40_channel_flag(struct ieee80211_channel *channel, struct brcmu_chan *ch) { @@ -5322,7 +5377,6 @@ static int brcmf_construct_chaninfo(struct brcmf_cfg80211_info *cfg, u32 i, j; u32 total; u32 chaninfo; - u32 chcnt[2] = { 0, 0 }; u32 index; pbuf = kzalloc(BRCMF_DCMD_MEDLEN, GFP_KERNEL); @@ -5339,42 +5393,15 @@ static int brcmf_construct_chaninfo(struct brcmf_cfg80211_info *cfg, goto fail_pbuf; } - brcmf_count_20mhz_channels(cfg, list, chcnt); wiphy = cfg_to_wiphy(cfg); - if (chcnt[0]) { - band = kmemdup(&__wl_band_2ghz, sizeof(__wl_band_2ghz), - GFP_KERNEL); - if (band == NULL) { - err = -ENOMEM; - goto fail_pbuf; - } - band->channels = kcalloc(chcnt[0], sizeof(*channel), - GFP_KERNEL); - if (band->channels == NULL) { - kfree(band); - err = -ENOMEM; - goto fail_pbuf; - } - band->n_channels = 0; - wiphy->bands[IEEE80211_BAND_2GHZ] = band; - } - if (chcnt[1]) { - band = kmemdup(&__wl_band_5ghz_a, sizeof(__wl_band_5ghz_a), - GFP_KERNEL); - if (band == NULL) { - err = -ENOMEM; - goto fail_band2g; - } - band->channels = kcalloc(chcnt[1], sizeof(*channel), - GFP_KERNEL); - if (band->channels == NULL) { - kfree(band); - err = -ENOMEM; - goto fail_band2g; - } - band->n_channels = 0; - wiphy->bands[IEEE80211_BAND_5GHZ] = band; - } + band = wiphy->bands[IEEE80211_BAND_2GHZ]; + if (band) + for (i = 0; i < band->n_channels; i++) + band->channels[i].flags = IEEE80211_CHAN_DISABLED; + band = wiphy->bands[IEEE80211_BAND_5GHZ]; + if (band) + for (i = 0; i < band->n_channels; i++) + band->channels[i].flags = IEEE80211_CHAN_DISABLED; total = le32_to_cpu(list->count); for (i = 0; i < total; i++) { @@ -5389,6 +5416,8 @@ static int brcmf_construct_chaninfo(struct brcmf_cfg80211_info *cfg, brcmf_err("Invalid channel Spec. 0x%x.\n", ch.chspec); continue; } + if (!band) + continue; if (!(bw_cap[band->band] & WLC_BW_40MHZ_BIT) && ch.bw == BRCMU_CHAN_BW_40) continue; @@ -5416,9 +5445,9 @@ static int brcmf_construct_chaninfo(struct brcmf_cfg80211_info *cfg, } else if (ch.bw == BRCMU_CHAN_BW_40) { brcmf_update_bw40_channel_flag(&channel[index], &ch); } else { - /* disable other bandwidths for now as mentioned - * order assure they are enabled for subsequent - * chanspecs. + /* enable the channel and disable other bandwidths + * for now as mentioned order assure they are enabled + * for subsequent chanspecs. */ channel[index].flags = IEEE80211_CHAN_NO_HT40 | IEEE80211_CHAN_NO_80MHZ; @@ -5437,16 +5466,8 @@ static int brcmf_construct_chaninfo(struct brcmf_cfg80211_info *cfg, IEEE80211_CHAN_NO_IR; } } - if (index == band->n_channels) - band->n_channels++; } - kfree(pbuf); - return 0; -fail_band2g: - kfree(wiphy->bands[IEEE80211_BAND_2GHZ]->channels); - kfree(wiphy->bands[IEEE80211_BAND_2GHZ]); - wiphy->bands[IEEE80211_BAND_2GHZ] = NULL; fail_pbuf: kfree(pbuf); return err; @@ -5674,53 +5695,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] = { @@ -5750,6 +5724,136 @@ brcmf_txrx_stypes[NUM_NL80211_IFTYPES] = { } }; +/** + * brcmf_setup_ifmodes() - determine interface modes and combinations. + * + * @wiphy: wiphy object. + * @ifp: interface object needed for feat module api. + * + * The interface modes and combinations are determined dynamically here + * based on firmware functionality. + * + * no p2p and no mbss: + * + * #STA <= 1, #AP <= 1, channels = 1, 2 total + * + * no p2p and mbss: + * + * #STA <= 1, #AP <= 1, channels = 1, 2 total + * #AP <= 4, matching BI, channels = 1, 4 total + * + * p2p, no mchan, and mbss: + * + * #STA <= 1, #P2P-DEV <= 1, #{P2P-CL, P2P-GO} <= 1, channels = 1, 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, mchan, and mbss: + * + * #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 + */ +static int brcmf_setup_ifmodes(struct wiphy *wiphy, struct brcmf_if *ifp) +{ + struct ieee80211_iface_combination *combo = NULL; + struct ieee80211_iface_limit *c0_limits = NULL; + struct ieee80211_iface_limit *p2p_limits = NULL; + struct ieee80211_iface_limit *mbss_limits = NULL; + bool mbss, p2p; + int i, c, n_combos; + + mbss = brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MBSS); + p2p = brcmf_feat_is_enabled(ifp, BRCMF_FEAT_P2P); + + n_combos = 1 + !!p2p + !!mbss; + combo = kcalloc(n_combos, sizeof(*combo), GFP_KERNEL); + if (!combo) + goto err; + + c0_limits = kcalloc(p2p ? 3 : 2, sizeof(*c0_limits), GFP_KERNEL); + if (!c0_limits) + goto err; + + if (p2p) { + p2p_limits = kcalloc(4, sizeof(*p2p_limits), GFP_KERNEL); + if (!p2p_limits) + goto err; + } + + if (mbss) { + mbss_limits = kcalloc(1, sizeof(*mbss_limits), GFP_KERNEL); + if (!mbss_limits) + goto err; + } + + wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | + BIT(NL80211_IFTYPE_ADHOC) | + BIT(NL80211_IFTYPE_AP); + + c = 0; + i = 0; + combo[c].num_different_channels = 1; + c0_limits[i].max = 1; + c0_limits[i++].types = BIT(NL80211_IFTYPE_STATION); + if (p2p) { + if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MCHAN)) + 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_P2P_DEVICE); + c0_limits[i].max = 1; + c0_limits[i++].types = BIT(NL80211_IFTYPE_P2P_CLIENT) | + BIT(NL80211_IFTYPE_P2P_GO); + } else { + c0_limits[i].max = 1; + c0_limits[i++].types = BIT(NL80211_IFTYPE_AP); + } + combo[c].max_interfaces = i; + combo[c].n_limits = i; + combo[c].limits = c0_limits; + + if (p2p) { + c++; + i = 0; + combo[c].num_different_channels = 1; + p2p_limits[i].max = 1; + p2p_limits[i++].types = BIT(NL80211_IFTYPE_STATION); + p2p_limits[i].max = 1; + p2p_limits[i++].types = BIT(NL80211_IFTYPE_AP); + p2p_limits[i].max = 1; + p2p_limits[i++].types = BIT(NL80211_IFTYPE_P2P_CLIENT); + p2p_limits[i].max = 1; + p2p_limits[i++].types = BIT(NL80211_IFTYPE_P2P_DEVICE); + combo[c].max_interfaces = i; + combo[c].n_limits = i; + combo[c].limits = p2p_limits; + } + + if (mbss) { + c++; + combo[c].beacon_int_infra_match = true; + combo[c].num_different_channels = 1; + mbss_limits[0].max = 4; + mbss_limits[0].types = BIT(NL80211_IFTYPE_AP); + combo[c].max_interfaces = 4; + combo[c].n_limits = 1; + combo[c].limits = mbss_limits; + } + wiphy->n_iface_combinations = n_combos; + wiphy->iface_combinations = combo; + return 0; + +err: + kfree(c0_limits); + kfree(p2p_limits); + kfree(mbss_limits); + kfree(combo); + return -ENOMEM; +} + static void brcmf_wiphy_pno_params(struct wiphy *wiphy) { /* scheduled scan settings */ @@ -5779,28 +5883,40 @@ static void brcmf_wiphy_wowl_params(struct wiphy *wiphy) static int brcmf_setup_wiphy(struct wiphy *wiphy, struct brcmf_if *ifp) { - struct ieee80211_iface_combination ifc_combo; + struct brcmf_pub *drvr = ifp->drvr; + const struct ieee80211_iface_combination *combo; + struct ieee80211_supported_band *band; + u16 max_interfaces = 0; + __le32 bandlist[3]; + u32 n_bands; + int err, i; + 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; + + for (i = 0, combo = wiphy->iface_combinations; + i < wiphy->n_iface_combinations; i++, combo++) { + max_interfaces = max(max_interfaces, combo->max_interfaces); + } + + for (i = 0; i < max_interfaces && i < ARRAY_SIZE(drvr->addresses); + i++) { + u8 *addr = drvr->addresses[i].addr; + + memcpy(addr, drvr->mac, ETH_ALEN); + if (i) { + addr[0] |= BIT(1); + addr[ETH_ALEN - 1] ^= i; + } + } + wiphy->addresses = drvr->addresses; + wiphy->n_addresses = i; + wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM; wiphy->cipher_suites = __wl_cipher_suites; wiphy->n_cipher_suites = ARRAY_SIZE(__wl_cipher_suites); @@ -5812,7 +5928,8 @@ static int brcmf_setup_wiphy(struct wiphy *wiphy, struct brcmf_if *ifp) wiphy->flags |= WIPHY_FLAG_SUPPORTS_FW_ROAM; wiphy->mgmt_stypes = brcmf_txrx_stypes; wiphy->max_remain_on_channel_duration = 5000; - brcmf_wiphy_pno_params(wiphy); + if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_PNO)) + brcmf_wiphy_pno_params(wiphy); /* vendor commands/events support */ wiphy->vendor_commands = brcmf_vendor_cmds; @@ -5821,7 +5938,52 @@ static int brcmf_setup_wiphy(struct wiphy *wiphy, struct brcmf_if *ifp) if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_WOWL)) brcmf_wiphy_wowl_params(wiphy); - return brcmf_setup_wiphybands(wiphy); + err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BANDLIST, &bandlist, + sizeof(bandlist)); + if (err) { + brcmf_err("could not obtain band info: err=%d\n", err); + return err; + } + /* first entry in bandlist is number of bands */ + n_bands = le32_to_cpu(bandlist[0]); + for (i = 1; i <= n_bands && i < ARRAY_SIZE(bandlist); i++) { + if (bandlist[i] == cpu_to_le32(WLC_BAND_2G)) { + band = kmemdup(&__wl_band_2ghz, sizeof(__wl_band_2ghz), + GFP_KERNEL); + if (!band) + return -ENOMEM; + + band->channels = kmemdup(&__wl_2ghz_channels, + sizeof(__wl_2ghz_channels), + GFP_KERNEL); + if (!band->channels) { + kfree(band); + return -ENOMEM; + } + + band->n_channels = ARRAY_SIZE(__wl_2ghz_channels); + wiphy->bands[IEEE80211_BAND_2GHZ] = band; + } + if (bandlist[i] == cpu_to_le32(WLC_BAND_5G)) { + band = kmemdup(&__wl_band_5ghz, sizeof(__wl_band_5ghz), + GFP_KERNEL); + if (!band) + return -ENOMEM; + + band->channels = kmemdup(&__wl_5ghz_channels, + sizeof(__wl_5ghz_channels), + GFP_KERNEL); + if (!band->channels) { + kfree(band); + return -ENOMEM; + } + + band->n_channels = ARRAY_SIZE(__wl_5ghz_channels); + wiphy->bands[IEEE80211_BAND_5GHZ] = band; + } + } + err = brcmf_setup_wiphybands(wiphy); + return err; } static s32 brcmf_config_dongle(struct brcmf_cfg80211_info *cfg) @@ -6007,11 +6169,24 @@ static void brcmf_cfg80211_reg_notifier(struct wiphy *wiphy, memset(&ccreq, 0, sizeof(ccreq)); ccreq.rev = cpu_to_le32(-1); memcpy(ccreq.ccode, req->alpha2, sizeof(req->alpha2)); - brcmf_fil_iovar_data_set(ifp, "country", &ccreq, sizeof(ccreq)); + if (brcmf_fil_iovar_data_set(ifp, "country", &ccreq, sizeof(ccreq))) { + brcmf_err("firmware rejected country setting\n"); + return; + } + brcmf_setup_wiphybands(wiphy); } static void brcmf_free_wiphy(struct wiphy *wiphy) { + int i; + + if (!wiphy) + return; + + if (wiphy->iface_combinations) { + for (i = 0; i < wiphy->n_iface_combinations; i++) + kfree(wiphy->iface_combinations[i].limits); + } kfree(wiphy->iface_combinations); if (wiphy->bands[IEEE80211_BAND_2GHZ]) { kfree(wiphy->bands[IEEE80211_BAND_2GHZ]->channels); @@ -6025,9 +6200,10 @@ static void brcmf_free_wiphy(struct wiphy *wiphy) } struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr, - struct device *busdev) + struct device *busdev, + bool p2pdev_forced) { - struct net_device *ndev = drvr->iflist[0]->ndev; + struct net_device *ndev = brcmf_get_ifp(drvr, 0)->ndev; struct brcmf_cfg80211_info *cfg; struct wiphy *wiphy; struct brcmf_cfg80211_vif *vif; @@ -6047,6 +6223,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); @@ -6115,8 +6292,19 @@ struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr, else *cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40; } + /* p2p might require that "if-events" get processed by fweh. So + * activate the already registered event handlers now and activate + * the rest when initialization has completed. drvr->config needs to + * be assigned before activating events. + */ + drvr->config = cfg; + err = brcmf_fweh_activate_events(ifp); + if (err) { + brcmf_err("FWEH activation failed (%d)\n", err); + goto wiphy_unreg_out; + } - err = brcmf_p2p_attach(cfg); + err = brcmf_p2p_attach(cfg, p2pdev_forced); if (err) { brcmf_err("P2P initilisation failed (%d)\n", err); goto wiphy_unreg_out; @@ -6137,6 +6325,13 @@ struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr, brcmf_notify_tdls_peer_event); } + /* (re-) activate FWEH event handling */ + err = brcmf_fweh_activate_events(ifp); + if (err) { + brcmf_err("FWEH activation failed (%d)\n", err); + goto wiphy_unreg_out; + } + return cfg; wiphy_unreg_out: @@ -6144,6 +6339,7 @@ wiphy_unreg_out: priv_out: wl_deinit_priv(cfg); brcmf_free_vif(vif); + ifp->vif = NULL; wiphy_out: brcmf_free_wiphy(wiphy); return NULL; @@ -6154,10 +6350,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); } diff --git a/kernel/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.h b/kernel/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.h index d9e6d01b2..6a878c8f8 100644 --- a/kernel/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.h +++ b/kernel/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.h @@ -143,7 +143,6 @@ struct brcmf_cfg80211_profile { * @BRCMF_VIF_STATUS_CONNECTING: connect/join in progress. * @BRCMF_VIF_STATUS_CONNECTED: connected/joined succesfully. * @BRCMF_VIF_STATUS_DISCONNECTING: disconnect/disable in progress. - * @BRCMF_VIF_STATUS_AP_CREATING: interface configured for AP operation. * @BRCMF_VIF_STATUS_AP_CREATED: AP operation started. */ enum brcmf_vif_status { @@ -151,7 +150,6 @@ enum brcmf_vif_status { BRCMF_VIF_STATUS_CONNECTING, BRCMF_VIF_STATUS_CONNECTED, BRCMF_VIF_STATUS_DISCONNECTING, - BRCMF_VIF_STATUS_AP_CREATING, BRCMF_VIF_STATUS_AP_CREATED }; @@ -407,6 +405,7 @@ struct brcmf_cfg80211_info { struct brcmu_d11inf d11inf; bool wowl_enabled; u32 pre_wowl_pmmode; + struct brcmf_assoclist_le assoclist; }; /** @@ -469,7 +468,8 @@ brcmf_cfg80211_connect_info *cfg_to_conn(struct brcmf_cfg80211_info *cfg) } struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr, - struct device *busdev); + struct device *busdev, + bool p2pdev_forced); void brcmf_cfg80211_detach(struct brcmf_cfg80211_info *cfg); s32 brcmf_cfg80211_up(struct net_device *ndev); s32 brcmf_cfg80211_down(struct net_device *ndev); diff --git a/kernel/drivers/net/wireless/brcm80211/brcmfmac/chip.c b/kernel/drivers/net/wireless/brcm80211/brcmfmac/chip.c index ab2fac8b2..f04833db2 100644 --- a/kernel/drivers/net/wireless/brcm80211/brcmfmac/chip.c +++ b/kernel/drivers/net/wireless/brcm80211/brcmfmac/chip.c @@ -101,6 +101,9 @@ /* ARM Cortex M3 core, ID 0x82a */ #define BCM4329_CORE_ARM_BASE 0x18002000 +/* Max possibly supported memory size (limited by IO mapped memory) */ +#define BRCMF_CHIP_MAX_MEMSIZE (4 * 1024 * 1024) + #define CORE_SB(base, field) \ (base + SBCONFIGOFF + offsetof(struct sbconfig, field)) #define SBCOREREV(sbidh) \ @@ -205,6 +208,7 @@ struct sbsocramregs { }; #define SOCRAMREGOFFS(_f) offsetof(struct sbsocramregs, _f) +#define SYSMEMREGOFFS(_f) offsetof(struct sbsocramregs, _f) #define ARMCR4_CAP (0x04) #define ARMCR4_BANKIDX (0x40) @@ -513,6 +517,9 @@ static int brcmf_chip_cores_check(struct brcmf_chip_priv *ci) case BCMA_CORE_ARM_CR4: cpu_found = true; break; + case BCMA_CORE_ARM_CA7: + cpu_found = true; + break; default: break; } @@ -611,6 +618,29 @@ static void brcmf_chip_socram_ramsize(struct brcmf_core_priv *sr, u32 *ramsize, } } +/** Return the SYS MEM size */ +static u32 brcmf_chip_sysmem_ramsize(struct brcmf_core_priv *sysmem) +{ + u32 memsize = 0; + u32 coreinfo; + u32 idx; + u32 nb; + u32 banksize; + + if (!brcmf_chip_iscoreup(&sysmem->pub)) + brcmf_chip_resetcore(&sysmem->pub, 0, 0, 0); + + coreinfo = brcmf_chip_core_read32(sysmem, SYSMEMREGOFFS(coreinfo)); + nb = (coreinfo & SRCI_SRNB_MASK) >> SRCI_SRNB_SHIFT; + + for (idx = 0; idx < nb; idx++) { + brcmf_chip_socram_banksize(sysmem, idx, &banksize); + memsize += banksize; + } + + return memsize; +} + /** Return the TCM-RAM size of the ARMCR4 core. */ static u32 brcmf_chip_tcm_ramsize(struct brcmf_core_priv *cr4) { @@ -644,13 +674,19 @@ static u32 brcmf_chip_tcm_rambase(struct brcmf_chip_priv *ci) return 0x198000; case BRCM_CC_4335_CHIP_ID: case BRCM_CC_4339_CHIP_ID: + case BRCM_CC_4350_CHIP_ID: case BRCM_CC_4354_CHIP_ID: case BRCM_CC_4356_CHIP_ID: case BRCM_CC_43567_CHIP_ID: case BRCM_CC_43569_CHIP_ID: case BRCM_CC_43570_CHIP_ID: + case BRCM_CC_4358_CHIP_ID: case BRCM_CC_43602_CHIP_ID: + case BRCM_CC_4371_CHIP_ID: return 0x180000; + case BRCM_CC_4365_CHIP_ID: + case BRCM_CC_4366_CHIP_ID: + return 0x200000; default: brcmf_err("unknown chip: %s\n", ci->pub.name); break; @@ -673,10 +709,28 @@ static int brcmf_chip_get_raminfo(struct brcmf_chip_priv *ci) return -EINVAL; } } else { - mem = brcmf_chip_get_core(&ci->pub, BCMA_CORE_INTERNAL_MEM); - mem_core = container_of(mem, struct brcmf_core_priv, pub); - brcmf_chip_socram_ramsize(mem_core, &ci->pub.ramsize, - &ci->pub.srsize); + mem = brcmf_chip_get_core(&ci->pub, BCMA_CORE_SYS_MEM); + if (mem) { + mem_core = container_of(mem, struct brcmf_core_priv, + pub); + ci->pub.ramsize = brcmf_chip_sysmem_ramsize(mem_core); + ci->pub.rambase = brcmf_chip_tcm_rambase(ci); + if (!ci->pub.rambase) { + brcmf_err("RAM base not provided with ARM CA7 core\n"); + return -EINVAL; + } + } else { + mem = brcmf_chip_get_core(&ci->pub, + BCMA_CORE_INTERNAL_MEM); + if (!mem) { + brcmf_err("No memory cores found\n"); + return -ENOMEM; + } + mem_core = container_of(mem, struct brcmf_core_priv, + pub); + brcmf_chip_socram_ramsize(mem_core, &ci->pub.ramsize, + &ci->pub.srsize); + } } brcmf_dbg(INFO, "RAM: base=0x%x size=%d (0x%x) sr=%d (0x%x)\n", ci->pub.rambase, ci->pub.ramsize, ci->pub.ramsize, @@ -686,6 +740,12 @@ static int brcmf_chip_get_raminfo(struct brcmf_chip_priv *ci) brcmf_err("RAM size is undetermined\n"); return -ENOMEM; } + + if (ci->pub.ramsize > BRCMF_CHIP_MAX_MEMSIZE) { + brcmf_err("RAM size is incorrect\n"); + return -ENOMEM; + } + return 0; } @@ -898,13 +958,22 @@ static int brcmf_chip_recognition(struct brcmf_chip_priv *ci) /* assure chip is passive for core access */ brcmf_chip_set_passive(&ci->pub); + + /* Call bus specific reset function now. Cores have been determined + * but further access may require a chip specific reset at this point. + */ + if (ci->ops->reset) { + ci->ops->reset(ci->ctx, &ci->pub); + brcmf_chip_set_passive(&ci->pub); + } + return brcmf_chip_get_raminfo(ci); } static void brcmf_chip_disable_arm(struct brcmf_chip_priv *chip, u16 id) { struct brcmf_core *core; - struct brcmf_core_priv *cr4; + struct brcmf_core_priv *cpu; u32 val; @@ -917,10 +986,11 @@ static void brcmf_chip_disable_arm(struct brcmf_chip_priv *chip, u16 id) brcmf_chip_coredisable(core, 0, 0); break; case BCMA_CORE_ARM_CR4: - cr4 = container_of(core, struct brcmf_core_priv, pub); + case BCMA_CORE_ARM_CA7: + cpu = container_of(core, struct brcmf_core_priv, pub); /* clear all IOCTL bits except HALT bit */ - val = chip->ops->read32(chip->ctx, cr4->wrapbase + BCMA_IOCTL); + val = chip->ops->read32(chip->ctx, cpu->wrapbase + BCMA_IOCTL); val &= ARMCR4_BCMA_IOCTL_CPUHALT; brcmf_chip_resetcore(core, val, ARMCR4_BCMA_IOCTL_CPUHALT, ARMCR4_BCMA_IOCTL_CPUHALT); @@ -1142,6 +1212,33 @@ static bool brcmf_chip_cr4_set_active(struct brcmf_chip_priv *chip, u32 rstvec) return true; } +static inline void +brcmf_chip_ca7_set_passive(struct brcmf_chip_priv *chip) +{ + struct brcmf_core *core; + + brcmf_chip_disable_arm(chip, BCMA_CORE_ARM_CA7); + + core = brcmf_chip_get_core(&chip->pub, BCMA_CORE_80211); + brcmf_chip_resetcore(core, D11_BCMA_IOCTL_PHYRESET | + D11_BCMA_IOCTL_PHYCLOCKEN, + D11_BCMA_IOCTL_PHYCLOCKEN, + D11_BCMA_IOCTL_PHYCLOCKEN); +} + +static bool brcmf_chip_ca7_set_active(struct brcmf_chip_priv *chip, u32 rstvec) +{ + struct brcmf_core *core; + + chip->ops->activate(chip->ctx, &chip->pub, rstvec); + + /* restore ARM */ + core = brcmf_chip_get_core(&chip->pub, BCMA_CORE_ARM_CA7); + brcmf_chip_resetcore(core, ARMCR4_BCMA_IOCTL_CPUHALT, 0, 0); + + return true; +} + void brcmf_chip_set_passive(struct brcmf_chip *pub) { struct brcmf_chip_priv *chip; @@ -1155,8 +1252,16 @@ void brcmf_chip_set_passive(struct brcmf_chip *pub) brcmf_chip_cr4_set_passive(chip); return; } - - brcmf_chip_cm3_set_passive(chip); + arm = brcmf_chip_get_core(pub, BCMA_CORE_ARM_CA7); + if (arm) { + brcmf_chip_ca7_set_passive(chip); + return; + } + arm = brcmf_chip_get_core(pub, BCMA_CORE_ARM_CM3); + if (arm) { + brcmf_chip_cm3_set_passive(chip); + return; + } } bool brcmf_chip_set_active(struct brcmf_chip *pub, u32 rstvec) @@ -1170,8 +1275,14 @@ bool brcmf_chip_set_active(struct brcmf_chip *pub, u32 rstvec) arm = brcmf_chip_get_core(pub, BCMA_CORE_ARM_CR4); if (arm) return brcmf_chip_cr4_set_active(chip, rstvec); + arm = brcmf_chip_get_core(pub, BCMA_CORE_ARM_CA7); + if (arm) + return brcmf_chip_ca7_set_active(chip, rstvec); + arm = brcmf_chip_get_core(pub, BCMA_CORE_ARM_CM3); + if (arm) + return brcmf_chip_cm3_set_active(chip); - return brcmf_chip_cm3_set_active(chip); + return false; } bool brcmf_chip_sr_capable(struct brcmf_chip *pub) diff --git a/kernel/drivers/net/wireless/brcm80211/brcmfmac/chip.h b/kernel/drivers/net/wireless/brcm80211/brcmfmac/chip.h index 60dcb38fc..f6b5feea2 100644 --- a/kernel/drivers/net/wireless/brcm80211/brcmfmac/chip.h +++ b/kernel/drivers/net/wireless/brcm80211/brcmfmac/chip.h @@ -73,6 +73,7 @@ struct brcmf_buscore_ops { u32 (*read32)(void *ctx, u32 addr); void (*write32)(void *ctx, u32 addr, u32 value); int (*prepare)(void *ctx); + int (*reset)(void *ctx, struct brcmf_chip *chip); int (*setup)(void *ctx, struct brcmf_chip *chip); void (*activate)(void *ctx, struct brcmf_chip *chip, u32 rstvec); }; diff --git a/kernel/drivers/net/wireless/brcm80211/brcmfmac/common.h b/kernel/drivers/net/wireless/brcm80211/brcmfmac/common.h index 0d39d80ce..21c7488b4 100644 --- a/kernel/drivers/net/wireless/brcm80211/brcmfmac/common.h +++ b/kernel/drivers/net/wireless/brcm80211/brcmfmac/common.h @@ -17,4 +17,7 @@ extern const u8 ALLFFMAC[ETH_ALEN]; +/* Sets dongle media info (drv_version, mac address). */ +int brcmf_c_preinit_dcmds(struct brcmf_if *ifp); + #endif /* BRCMFMAC_COMMON_H */ diff --git a/kernel/drivers/net/wireless/brcm80211/brcmfmac/commonring.c b/kernel/drivers/net/wireless/brcm80211/brcmfmac/commonring.c index 77656c711..7b0e52195 100644 --- a/kernel/drivers/net/wireless/brcm80211/brcmfmac/commonring.c +++ b/kernel/drivers/net/wireless/brcm80211/brcmfmac/commonring.c @@ -22,17 +22,6 @@ #include "core.h" #include "commonring.h" - -/* dma flushing needs implementation for mips and arm platforms. Should - * be put in util. Note, this is not real flushing. It is virtual non - * cached memory. Only write buffers should have to be drained. Though - * this may be different depending on platform...... - * SEE ALSO msgbuf.c - */ -#define brcmf_dma_flush(addr, len) -#define brcmf_dma_invalidate_cache(addr, len) - - void brcmf_commonring_register_cb(struct brcmf_commonring *commonring, int (*cr_ring_bell)(void *ctx), int (*cr_update_rptr)(void *ctx), @@ -206,14 +195,9 @@ int brcmf_commonring_write_complete(struct brcmf_commonring *commonring) address = commonring->buf_addr; address += (commonring->f_ptr * commonring->item_len); if (commonring->f_ptr > commonring->w_ptr) { - brcmf_dma_flush(address, - (commonring->depth - commonring->f_ptr) * - commonring->item_len); address = commonring->buf_addr; commonring->f_ptr = 0; } - brcmf_dma_flush(address, (commonring->w_ptr - commonring->f_ptr) * - commonring->item_len); commonring->f_ptr = commonring->w_ptr; @@ -239,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); @@ -251,21 +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; - - brcmf_dma_invalidate_cache(ret_addr, *n_ items * commonring->item_len); - - 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); diff --git a/kernel/drivers/net/wireless/brcm80211/brcmfmac/commonring.h b/kernel/drivers/net/wireless/brcm80211/brcmfmac/commonring.h index 3d404016a..b85033611 100644 --- a/kernel/drivers/net/wireless/brcm80211/brcmfmac/commonring.h +++ b/kernel/drivers/net/wireless/brcm80211/brcmfmac/commonring.h @@ -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) diff --git a/kernel/drivers/net/wireless/brcm80211/brcmfmac/core.c b/kernel/drivers/net/wireless/brcm80211/brcmfmac/core.c index f8f47dcfa..b5ab98ee1 100644 --- a/kernel/drivers/net/wireless/brcm80211/brcmfmac/core.c +++ b/kernel/drivers/net/wireless/brcm80211/brcmfmac/core.c @@ -33,6 +33,7 @@ #include "feature.h" #include "proto.h" #include "pcie.h" +#include "common.h" MODULE_AUTHOR("Broadcom Corporation"); MODULE_DESCRIPTION("Broadcom 802.11 wireless LAN fullmac driver."); @@ -53,6 +54,8 @@ MODULE_LICENSE("Dual BSD/GPL"); #define BRCMF_RXREORDER_EXPIDX_VALID 0x08 #define BRCMF_RXREORDER_NEW_HOLE 0x10 +#define BRCMF_BSSIDX_INVALID -1 + /* Error bits */ int brcmf_msg_level; module_param_named(debug, brcmf_msg_level, int, S_IRUSR | S_IWUSR); @@ -60,10 +63,8 @@ MODULE_PARM_DESC(debug, "level of debug output"); /* P2P0 enable */ static int brcmf_p2p_enable; -#ifdef CONFIG_BRCMDBG module_param_named(p2pon, brcmf_p2p_enable, int, 0); -MODULE_PARM_DESC(p2pon, "enable p2p management functionality"); -#endif +MODULE_PARM_DESC(p2pon, "enable legacy p2p management functionality"); char *brcmf_ifname(struct brcmf_pub *drvr, int ifidx) { @@ -83,6 +84,24 @@ char *brcmf_ifname(struct brcmf_pub *drvr, int ifidx) return "<if_none>"; } +struct brcmf_if *brcmf_get_ifp(struct brcmf_pub *drvr, int ifidx) +{ + struct brcmf_if *ifp; + s32 bssidx; + + if (ifidx < 0 || ifidx >= BRCMF_MAX_IFS) { + brcmf_err("ifidx %d out of range\n", ifidx); + return NULL; + } + + ifp = NULL; + bssidx = drvr->if2bss[ifidx]; + if (bssidx >= 0) + ifp = drvr->iflist[bssidx]; + + return ifp; +} + static void _brcmf_set_multicast_list(struct work_struct *work) { struct brcmf_if *ifp; @@ -520,17 +539,15 @@ void brcmf_rx_frame(struct device *dev, struct sk_buff *skb) struct brcmf_bus *bus_if = dev_get_drvdata(dev); struct brcmf_pub *drvr = bus_if->drvr; struct brcmf_skb_reorder_data *rd; - u8 ifidx; int ret; brcmf_dbg(DATA, "Enter: %s: rxp=%p\n", dev_name(dev), skb); /* process and remove protocol-specific header */ - ret = brcmf_proto_hdrpull(drvr, true, &ifidx, skb); - ifp = drvr->iflist[ifidx]; + ret = brcmf_proto_hdrpull(drvr, true, skb, &ifp); if (ret || !ifp || !ifp->ndev) { - if ((ret != -ENODATA) && ifp) + if (ret != -ENODATA && ifp) ifp->stats.rx_errors++; brcmu_pkt_buf_free_skb(skb); return; @@ -543,17 +560,11 @@ void brcmf_rx_frame(struct device *dev, struct sk_buff *skb) brcmf_netif_rx(ifp, skb); } -void brcmf_txfinalize(struct brcmf_pub *drvr, struct sk_buff *txp, u8 ifidx, - bool success) +void brcmf_txfinalize(struct brcmf_if *ifp, struct sk_buff *txp, bool success) { - struct brcmf_if *ifp; struct ethhdr *eh; u16 type; - ifp = drvr->iflist[ifidx]; - if (!ifp) - goto done; - eh = (struct ethhdr *)(txp->data); type = ntohs(eh->h_proto); @@ -565,7 +576,7 @@ void brcmf_txfinalize(struct brcmf_pub *drvr, struct sk_buff *txp, u8 ifidx, if (!success) ifp->stats.tx_errors++; -done: + brcmu_pkt_buf_free_skb(txp); } @@ -573,17 +584,17 @@ void brcmf_txcomplete(struct device *dev, struct sk_buff *txp, bool success) { struct brcmf_bus *bus_if = dev_get_drvdata(dev); struct brcmf_pub *drvr = bus_if->drvr; - u8 ifidx; + struct brcmf_if *ifp; /* await txstatus signal for firmware if active */ if (brcmf_fws_fc_active(drvr->fws)) { if (!success) brcmf_fws_bustxfail(drvr->fws, txp); } else { - if (brcmf_proto_hdrpull(drvr, false, &ifidx, txp)) + if (brcmf_proto_hdrpull(drvr, false, txp, &ifp)) brcmu_pkt_buf_free_skb(txp); else - brcmf_txfinalize(drvr, txp, ifidx, success); + brcmf_txfinalize(ifp, txp, success); } } @@ -624,8 +635,7 @@ static int brcmf_netdev_stop(struct net_device *ndev) brcmf_cfg80211_down(ndev); - /* Set state and stop OS transmissions */ - netif_stop_queue(ndev); + brcmf_net_setcarrier(ifp, false); return 0; } @@ -659,8 +669,8 @@ static int brcmf_netdev_open(struct net_device *ndev) return -EIO; } - /* Allow transmit calls */ - netif_start_queue(ndev); + /* Clear, carrier, set when connected or AP mode. */ + netif_carrier_off(ndev); return 0; } @@ -708,8 +718,6 @@ int brcmf_net_attach(struct brcmf_if *ifp, bool rtnl_locked) } brcmf_dbg(INFO, "%s: Broadcom Dongle Host Driver\n", ndev->name); - - ndev->destructor = brcmf_cfg80211_free_netdev; return 0; fail: @@ -719,6 +727,32 @@ fail: return -EBADE; } +static void brcmf_net_detach(struct net_device *ndev) +{ + if (ndev->reg_state == NETREG_REGISTERED) + unregister_netdev(ndev); + else + brcmf_cfg80211_free_netdev(ndev); +} + +void brcmf_net_setcarrier(struct brcmf_if *ifp, bool on) +{ + struct net_device *ndev; + + brcmf_dbg(TRACE, "Enter, idx=%d carrier=%d\n", ifp->bssidx, on); + + ndev = ifp->ndev; + brcmf_txflowblock_if(ifp, BRCMF_NETIF_STOP_REASON_DISCONNECTED, !on); + if (on) { + if (!netif_carrier_ok(ndev)) + netif_carrier_on(ndev); + + } else { + if (netif_carrier_ok(ndev)) + netif_carrier_off(ndev); + } +} + static int brcmf_net_p2p_open(struct net_device *ndev) { brcmf_dbg(TRACE, "Enter\n"); @@ -778,7 +812,7 @@ fail: } struct brcmf_if *brcmf_add_if(struct brcmf_pub *drvr, s32 bssidx, s32 ifidx, - char *name, u8 *mac_addr) + bool is_p2pdev, char *name, u8 *mac_addr) { struct brcmf_if *ifp; struct net_device *ndev; @@ -795,8 +829,7 @@ struct brcmf_if *brcmf_add_if(struct brcmf_pub *drvr, s32 bssidx, s32 ifidx, ifp->ndev->name); if (ifidx) { netif_stop_queue(ifp->ndev); - unregister_netdev(ifp->ndev); - free_netdev(ifp->ndev); + brcmf_net_detach(ifp->ndev); drvr->iflist[bssidx] = NULL; } else { brcmf_err("ignore IF event\n"); @@ -804,7 +837,7 @@ struct brcmf_if *brcmf_add_if(struct brcmf_pub *drvr, s32 bssidx, s32 ifidx, } } - if (!brcmf_p2p_enable && bssidx == 1) { + if (!brcmf_p2p_enable && is_p2pdev) { /* this is P2P_DEVICE interface */ brcmf_dbg(INFO, "allocate non-netdev interface\n"); ifp = kzalloc(sizeof(*ifp), GFP_KERNEL); @@ -813,13 +846,17 @@ struct brcmf_if *brcmf_add_if(struct brcmf_pub *drvr, s32 bssidx, s32 ifidx, } else { brcmf_dbg(INFO, "allocate netdev interface\n"); /* Allocate netdev, including space for private structure */ - ndev = alloc_netdev(sizeof(*ifp), name, NET_NAME_UNKNOWN, - ether_setup); + ndev = alloc_netdev(sizeof(*ifp), is_p2pdev ? "p2p%d" : name, + NET_NAME_UNKNOWN, ether_setup); if (!ndev) return ERR_PTR(-ENOMEM); + ndev->destructor = brcmf_cfg80211_free_netdev; ifp = netdev_priv(ndev); ifp->ndev = ndev; + /* store mapping ifidx to bssidx */ + if (drvr->if2bss[ifidx] == BRCMF_BSSIDX_INVALID) + drvr->if2bss[ifidx] = bssidx; } ifp->drvr = drvr; @@ -850,6 +887,8 @@ static void brcmf_del_if(struct brcmf_pub *drvr, s32 bssidx) return; } brcmf_dbg(TRACE, "Enter, idx=%d, ifidx=%d\n", bssidx, ifp->ifidx); + if (drvr->if2bss[ifp->ifidx] == bssidx) + drvr->if2bss[ifp->ifidx] = BRCMF_BSSIDX_INVALID; if (ifp->ndev) { if (bssidx == 0) { if (ifp->ndev->netdev_ops == &brcmf_netdev_ops_pri) { @@ -865,19 +904,28 @@ static void brcmf_del_if(struct brcmf_pub *drvr, s32 bssidx) cancel_work_sync(&ifp->setmacaddr_work); cancel_work_sync(&ifp->multicast_work); } - /* unregister will take care of freeing it */ - unregister_netdev(ifp->ndev); + brcmf_net_detach(ifp->ndev); } else { + /* Only p2p device interfaces which get dynamically created + * end up here. In this case the p2p module should be informed + * about the removal of the interface within the firmware. If + * not then p2p commands towards the firmware will cause some + * serious troublesome side effects. The p2p module will clean + * up the ifp if needed. + */ + brcmf_p2p_ifp_removed(ifp); kfree(ifp); } } -void brcmf_remove_interface(struct brcmf_pub *drvr, u32 bssidx) +void brcmf_remove_interface(struct brcmf_if *ifp) { - if (drvr->iflist[bssidx]) { - brcmf_fws_del_interface(drvr->iflist[bssidx]); - brcmf_del_if(drvr, bssidx); - } + if (!ifp || WARN_ON(ifp->drvr->iflist[ifp->bssidx] != ifp)) + return; + brcmf_dbg(TRACE, "Enter, bssidx=%d, ifidx=%d\n", ifp->bssidx, + ifp->ifidx); + brcmf_fws_del_interface(ifp); + brcmf_del_if(ifp->drvr, ifp->bssidx); } int brcmf_get_next_free_bsscfgidx(struct brcmf_pub *drvr) @@ -908,6 +956,7 @@ int brcmf_attach(struct device *dev) { struct brcmf_pub *drvr = NULL; int ret = 0; + int i; brcmf_dbg(TRACE, "Enter\n"); @@ -916,6 +965,9 @@ int brcmf_attach(struct device *dev) if (!drvr) return -ENOMEM; + for (i = 0; i < ARRAY_SIZE(drvr->if2bss); i++) + drvr->if2bss[i] = BRCMF_BSSIDX_INVALID; + mutex_init(&drvr->proto_block); /* Link to bus module */ @@ -923,8 +975,8 @@ int brcmf_attach(struct device *dev) drvr->bus_if = dev_get_drvdata(dev); drvr->bus_if->drvr = drvr; - /* create device debugfs folder */ - brcmf_debugfs_attach(drvr); + /* attach debug facilities */ + brcmf_debug_attach(drvr); /* Attach and link in the protocol */ ret = brcmf_proto_attach(drvr); @@ -983,16 +1035,11 @@ int brcmf_bus_start(struct device *dev) brcmf_dbg(TRACE, "\n"); /* add primary networking interface */ - ifp = brcmf_add_if(drvr, 0, 0, "wlan%d", NULL); + ifp = brcmf_add_if(drvr, 0, 0, false, "wlan%d", NULL); if (IS_ERR(ifp)) return PTR_ERR(ifp); - if (brcmf_p2p_enable) - p2p_ifp = brcmf_add_if(drvr, 1, 0, "p2p%d", NULL); - else - p2p_ifp = NULL; - if (IS_ERR(p2p_ifp)) - p2p_ifp = NULL; + p2p_ifp = NULL; /* signal bus ready */ brcmf_bus_change_state(bus_if, BRCMF_BUS_UP); @@ -1019,39 +1066,37 @@ int brcmf_bus_start(struct device *dev) brcmf_fws_add_interface(ifp); - drvr->config = brcmf_cfg80211_attach(drvr, bus_if->dev); + drvr->config = brcmf_cfg80211_attach(drvr, bus_if->dev, + brcmf_p2p_enable); if (drvr->config == NULL) { ret = -ENOMEM; goto fail; } - ret = brcmf_fweh_activate_events(ifp); - if (ret < 0) - goto fail; - ret = brcmf_net_attach(ifp, false); + + if ((!ret) && (brcmf_p2p_enable)) { + p2p_ifp = drvr->iflist[1]; + if (p2p_ifp) + ret = brcmf_net_p2p_attach(p2p_ifp); + } fail: if (ret < 0) { brcmf_err("failed: %d\n", ret); - brcmf_cfg80211_detach(drvr->config); + if (drvr->config) { + brcmf_cfg80211_detach(drvr->config); + drvr->config = NULL; + } if (drvr->fws) { brcmf_fws_del_interface(ifp); brcmf_fws_deinit(drvr); } - if (drvr->iflist[0]) { - free_netdev(ifp->ndev); - drvr->iflist[0] = NULL; - } - if (p2p_ifp) { - free_netdev(p2p_ifp->ndev); - drvr->iflist[1] = NULL; - } + if (ifp) + brcmf_net_detach(ifp->ndev); + if (p2p_ifp) + brcmf_net_detach(p2p_ifp->ndev); return ret; } - if ((brcmf_p2p_enable) && (p2p_ifp)) - if (brcmf_net_p2p_attach(p2p_ifp) < 0) - brcmf_p2p_enable = 0; - return 0; } @@ -1100,12 +1145,14 @@ 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); /* make sure primary interface removed last */ for (i = BRCMF_MAX_IFS-1; i > -1; i--) - brcmf_remove_interface(drvr, i); + brcmf_remove_interface(drvr->iflist[i]); brcmf_cfg80211_detach(drvr->config); @@ -1115,7 +1162,7 @@ void brcmf_detach(struct device *dev) brcmf_proto_detach(drvr); - brcmf_debugfs_detach(drvr); + brcmf_debug_detach(drvr); bus_if->drvr = NULL; kfree(drvr); } diff --git a/kernel/drivers/net/wireless/brcm80211/brcmfmac/core.h b/kernel/drivers/net/wireless/brcm80211/brcmfmac/core.h index fd74a9c6e..2f9101b2a 100644 --- a/kernel/drivers/net/wireless/brcm80211/brcmfmac/core.h +++ b/kernel/drivers/net/wireless/brcm80211/brcmfmac/core.h @@ -21,6 +21,7 @@ #ifndef BRCMFMAC_CORE_H #define BRCMFMAC_CORE_H +#include <net/cfg80211.h> #include "fweh.h" #define TOE_TX_CSUM_OL 0x00000001 @@ -118,7 +119,10 @@ struct brcmf_pub { /* Multicast data packets sent to dongle */ unsigned long tx_multicast; + struct mac_address addresses[BRCMF_MAX_IFS]; + struct brcmf_if *iflist[BRCMF_MAX_IFS]; + s32 if2bss[BRCMF_MAX_IFS]; struct mutex proto_block; unsigned char proto_buf[BRCMF_DCMD_MAXLEN]; @@ -150,10 +154,13 @@ struct brcmf_fws_mac_descriptor; * netif stopped due to firmware signalling flow control. * @BRCMF_NETIF_STOP_REASON_FLOW: * netif stopped due to flowring full. + * @BRCMF_NETIF_STOP_REASON_DISCONNECTED: + * netif stopped due to not being connected (STA mode). */ enum brcmf_netif_stop_reason { - BRCMF_NETIF_STOP_REASON_FWS_FC = 1, - BRCMF_NETIF_STOP_REASON_FLOW = 2 + BRCMF_NETIF_STOP_REASON_FWS_FC = BIT(0), + BRCMF_NETIF_STOP_REASON_FLOW = BIT(1), + BRCMF_NETIF_STOP_REASON_DISCONNECTED = BIT(2) }; /** @@ -199,19 +206,16 @@ int brcmf_netdev_wait_pend8021x(struct brcmf_if *ifp); /* Return pointer to interface name */ char *brcmf_ifname(struct brcmf_pub *drvr, int idx); - +struct brcmf_if *brcmf_get_ifp(struct brcmf_pub *drvr, int ifidx); int brcmf_net_attach(struct brcmf_if *ifp, bool rtnl_locked); struct brcmf_if *brcmf_add_if(struct brcmf_pub *drvr, s32 bssidx, s32 ifidx, - char *name, u8 *mac_addr); -void brcmf_remove_interface(struct brcmf_pub *drvr, u32 bssidx); + bool is_p2pdev, char *name, u8 *mac_addr); +void brcmf_remove_interface(struct brcmf_if *ifp); int brcmf_get_next_free_bsscfgidx(struct brcmf_pub *drvr); void brcmf_txflowblock_if(struct brcmf_if *ifp, enum brcmf_netif_stop_reason reason, bool state); -void brcmf_txfinalize(struct brcmf_pub *drvr, struct sk_buff *txp, u8 ifidx, - bool success); +void brcmf_txfinalize(struct brcmf_if *ifp, struct sk_buff *txp, bool success); void brcmf_netif_rx(struct brcmf_if *ifp, struct sk_buff *skb); - -/* Sets dongle media info (drv_version, mac address). */ -int brcmf_c_preinit_dcmds(struct brcmf_if *ifp); +void brcmf_net_setcarrier(struct brcmf_if *ifp, bool on); #endif /* BRCMFMAC_CORE_H */ diff --git a/kernel/drivers/net/wireless/brcm80211/brcmfmac/debug.c b/kernel/drivers/net/wireless/brcm80211/brcmfmac/debug.c index 9b473d50b..1299dccc7 100644 --- a/kernel/drivers/net/wireless/brcm80211/brcmfmac/debug.c +++ b/kernel/drivers/net/wireless/brcm80211/brcmfmac/debug.c @@ -16,15 +16,45 @@ #include <linux/debugfs.h> #include <linux/netdevice.h> #include <linux/module.h> +#include <linux/devcoredump.h> #include <brcmu_wifi.h> #include <brcmu_utils.h> #include "core.h" #include "bus.h" +#include "fweh.h" #include "debug.h" static struct dentry *root_folder; +static int brcmf_debug_create_memdump(struct brcmf_bus *bus, const void *data, + size_t len) +{ + void *dump; + size_t ramsize; + + ramsize = brcmf_bus_get_ramsize(bus); + if (ramsize) { + dump = vzalloc(len + ramsize); + if (!dump) + return -ENOMEM; + memcpy(dump, data, len); + brcmf_bus_get_memdump(bus, dump + len, ramsize); + dev_coredumpv(bus->dev, dump, len + ramsize, GFP_KERNEL); + } + return 0; +} + +static int brcmf_debug_psm_watchdog_notify(struct brcmf_if *ifp, + const struct brcmf_event_msg *evtmsg, + void *data) +{ + brcmf_dbg(TRACE, "enter: idx=%d\n", ifp->bssidx); + + return brcmf_debug_create_memdump(ifp->drvr->bus_if, data, + evtmsg->datalen); +} + void brcmf_debugfs_init(void) { root_folder = debugfs_create_dir(KBUILD_MODNAME, NULL); @@ -41,16 +71,7 @@ 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) +int brcmf_debug_attach(struct brcmf_pub *drvr) { struct device *dev = drvr->bus_if->dev; @@ -58,13 +79,18 @@ 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); + if (IS_ERR(drvr->dbgfs_dir)) + return PTR_ERR(drvr->dbgfs_dir); - return PTR_ERR_OR_ZERO(drvr->dbgfs_dir); + + return brcmf_fweh_register(drvr, BRCMF_E_PSM_WATCHDOG, + brcmf_debug_psm_watchdog_notify); } -void brcmf_debugfs_detach(struct brcmf_pub *drvr) +void brcmf_debug_detach(struct brcmf_pub *drvr) { + brcmf_fweh_unregister(drvr, BRCMF_E_PSM_WATCHDOG); + if (!IS_ERR_OR_NULL(drvr->dbgfs_dir)) debugfs_remove_recursive(drvr->dbgfs_dir); } @@ -74,44 +100,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; - - 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); + struct dentry *e; - 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); } diff --git a/kernel/drivers/net/wireless/brcm80211/brcmfmac/debug.h b/kernel/drivers/net/wireless/brcm80211/brcmfmac/debug.h index eb0b8c474..d0d9676f7 100644 --- a/kernel/drivers/net/wireless/brcm80211/brcmfmac/debug.h +++ b/kernel/drivers/net/wireless/brcm80211/brcmfmac/debug.h @@ -37,6 +37,7 @@ #define BRCMF_SDIO_VAL 0x00020000 #define BRCMF_MSGBUF_VAL 0x00040000 #define BRCMF_PCIE_VAL 0x00080000 +#define BRCMF_FWCON_VAL 0x00100000 /* set default print format */ #undef pr_fmt @@ -78,6 +79,7 @@ do { \ #define BRCMF_GLOM_ON() (brcmf_msg_level & BRCMF_GLOM_VAL) #define BRCMF_EVENT_ON() (brcmf_msg_level & BRCMF_EVENT_VAL) #define BRCMF_FIL_ON() (brcmf_msg_level & BRCMF_FIL_VAL) +#define BRCMF_FWCON_ON() (brcmf_msg_level & BRCMF_FWCON_VAL) #else /* defined(DEBUG) || defined(CONFIG_BRCM_TRACING) */ @@ -90,6 +92,7 @@ do { \ #define BRCMF_GLOM_ON() 0 #define BRCMF_EVENT_ON() 0 #define BRCMF_FIL_ON() 0 +#define BRCMF_FWCON_ON() 0 #endif /* defined(DEBUG) || defined(CONFIG_BRCM_TRACING) */ @@ -106,8 +109,8 @@ struct brcmf_pub; #ifdef DEBUG void brcmf_debugfs_init(void); void brcmf_debugfs_exit(void); -int brcmf_debugfs_attach(struct brcmf_pub *drvr); -void brcmf_debugfs_detach(struct brcmf_pub *drvr); +int brcmf_debug_attach(struct brcmf_pub *drvr); +void brcmf_debug_detach(struct brcmf_pub *drvr); struct dentry *brcmf_debugfs_get_devdir(struct brcmf_pub *drvr); int brcmf_debugfs_add_entry(struct brcmf_pub *drvr, const char *fn, int (*read_fn)(struct seq_file *seq, void *data)); @@ -118,11 +121,11 @@ static inline void brcmf_debugfs_init(void) static inline void brcmf_debugfs_exit(void) { } -static inline int brcmf_debugfs_attach(struct brcmf_pub *drvr) +static inline int brcmf_debug_attach(struct brcmf_pub *drvr) { return 0; } -static inline void brcmf_debugfs_detach(struct brcmf_pub *drvr) +static inline void brcmf_debug_detach(struct brcmf_pub *drvr) { } static inline diff --git a/kernel/drivers/net/wireless/brcm80211/brcmfmac/feature.c b/kernel/drivers/net/wireless/brcm80211/brcmfmac/feature.c index 7748a1ccf..44bb30636 100644 --- a/kernel/drivers/net/wireless/brcm80211/brcmfmac/feature.c +++ b/kernel/drivers/net/wireless/brcm80211/brcmfmac/feature.c @@ -15,6 +15,7 @@ */ #include <linux/netdevice.h> +#include <linux/module.h> #include <brcm_hw_ids.h> #include "core.h" @@ -23,6 +24,12 @@ #include "fwil.h" #include "feature.h" + +/* Module param feature_disable (global for all devices) */ +static int brcmf_feature_disable; +module_param_named(feature_disable, brcmf_feature_disable, int, 0); +MODULE_PARM_DESC(feature_disable, "Disable features"); + /* * expand feature list to array of feature strings. */ @@ -121,13 +128,21 @@ static void brcmf_feat_iovar_int_set(struct brcmf_if *ifp, void brcmf_feat_attach(struct brcmf_pub *drvr) { - struct brcmf_if *ifp = drvr->iflist[0]; + struct brcmf_if *ifp = brcmf_get_ifp(drvr, 0); brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_MCHAN, "mchan"); + brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_PNO, "pfn"); if (drvr->bus_if->wowl_supported) 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"); + + if (brcmf_feature_disable) { + brcmf_dbg(INFO, "Features: 0x%02x, disable: 0x%02x\n", + ifp->drvr->feat_flags, brcmf_feature_disable); + ifp->drvr->feat_flags &= ~brcmf_feature_disable; + } /* set chip related quirks */ switch (drvr->bus_if->chip) { diff --git a/kernel/drivers/net/wireless/brcm80211/brcmfmac/feature.h b/kernel/drivers/net/wireless/brcm80211/brcmfmac/feature.h index f5832e077..6b381f799 100644 --- a/kernel/drivers/net/wireless/brcm80211/brcmfmac/feature.h +++ b/kernel/drivers/net/wireless/brcm80211/brcmfmac/feature.h @@ -19,12 +19,18 @@ /* * Features: * + * MBSS: multiple BSSID support (eg. guest network in AP mode). * 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(WOWL) + BRCMF_FEAT_DEF(PNO) \ + BRCMF_FEAT_DEF(WOWL) \ + BRCMF_FEAT_DEF(P2P) /* * Quirks: * diff --git a/kernel/drivers/net/wireless/brcm80211/brcmfmac/firmware.c b/kernel/drivers/net/wireless/brcm80211/brcmfmac/firmware.c index 9cb99152a..4248f3c80 100644 --- a/kernel/drivers/net/wireless/brcm80211/brcmfmac/firmware.c +++ b/kernel/drivers/net/wireless/brcm80211/brcmfmac/firmware.c @@ -19,12 +19,17 @@ #include <linux/device.h> #include <linux/firmware.h> #include <linux/module.h> +#include <linux/bcm47xx_nvram.h> #include "debug.h" #include "firmware.h" +#define BRCMF_FW_MAX_NVRAM_SIZE 64000 +#define BRCMF_FW_NVRAM_DEVPATH_LEN 19 /* devpath0=pcie/1/4/ */ +#define BRCMF_FW_NVRAM_PCIEDEV_LEN 10 /* pcie/1/4/ + \0 */ + char brcmf_firmware_path[BRCMF_FW_PATH_LEN]; -module_param_string(firmware_path, brcmf_firmware_path, +module_param_string(alternative_fw_path, brcmf_firmware_path, BRCMF_FW_PATH_LEN, 0440); enum nvram_parser_state { @@ -39,25 +44,35 @@ 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. * @column: current column in line. * @pos: byte offset in input buffer. * @entry: start position of key,value entry. + * @multi_dev_v1: detect pcie multi device v1 (compressed). + * @multi_dev_v2: detect pcie multi device v2. */ struct nvram_parser { enum nvram_parser_state state; - const struct firmware *fwnv; + const u8 *data; u8 *nvram; u32 nvram_len; u32 line; u32 column; u32 pos; u32 entry; + bool multi_dev_v1; + bool multi_dev_v2; }; +/** + * is_nvram_char() - check if char is a valid one for NVRAM entry + * + * It accepts all printable ASCII chars except for '#' which opens a comment. + * Please note that ' ' (space) while accepted is not a valid key name char. + */ static bool is_nvram_char(char c) { /* comment marker excluded */ @@ -65,7 +80,7 @@ static bool is_nvram_char(char c) return false; /* key and value may have any other readable character */ - return (c > 0x20 && c < 0x7f); + return (c >= 0x20 && c < 0x7f); } static bool is_whitespace(char c) @@ -77,7 +92,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)) @@ -101,14 +116,18 @@ 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; - } else if (!is_nvram_char(c)) { + if (strncmp(&nvp->data[nvp->entry], "devpath", 7) == 0) + nvp->multi_dev_v1 = true; + 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", nvp->line, nvp->column); return COMMENT; @@ -127,12 +146,14 @@ 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; /* copy to output buffer */ memcpy(&nvp->nvram[nvp->nvram_len], skv, cplen); nvp->nvram_len += cplen; @@ -148,17 +169,20 @@ brcmf_nvram_handle_value(struct nvram_parser *nvp) static enum nvram_parser_state brcmf_nvram_handle_comment(struct nvram_parser *nvp) { - char *eol, *sol; - - sol = (char *)&nvp->fwnv->data[nvp->pos]; - eol = strchr(sol, '\n'); - if (eol == NULL) - return END; + char *eoc, *sol; + + sol = (char *)&nvp->data[nvp->pos]; + eoc = strchr(sol, '\n'); + if (!eoc) { + eoc = strchr(sol, '\0'); + if (!eoc) + return END; + } /* eat all moving to next line */ nvp->line++; nvp->column = 1; - nvp->pos += (eol - sol) + 1; + nvp->pos += (eoc - sol) + 1; return IDLE; } @@ -178,12 +202,20 @@ 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 (data_len > BRCMF_FW_MAX_NVRAM_SIZE) + size = BRCMF_FW_MAX_NVRAM_SIZE; + else + size = data_len; /* Alloc for extra 0 byte + roundup by 4 + length field */ - nvp->nvram = kzalloc(nv->size + 1 + 3 + sizeof(u32), GFP_KERNEL); + size += 1 + 3 + sizeof(u32); + nvp->nvram = kzalloc(size, GFP_KERNEL); if (!nvp->nvram) return -ENOMEM; @@ -192,26 +224,171 @@ static int brcmf_init_nvram_parser(struct nvram_parser *nvp, return 0; } +/* brcmf_fw_strip_multi_v1 :Some nvram files contain settings for multiple + * devices. Strip it down for one device, use domain_nr/bus_nr to determine + * which data is to be returned. v1 is the version where nvram is stored + * compressed and "devpath" maps to index for valid entries. + */ +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; + + u32 i, j; + bool found; + u8 *nvram; + u8 id; + + nvram = kzalloc(nvp->nvram_len + 1 + 3 + sizeof(u32), GFP_KERNEL); + if (!nvram) + goto fail; + + /* min length: devpath0=pcie/1/4/ + 0:x=y */ + if (nvp->nvram_len < BRCMF_FW_NVRAM_DEVPATH_LEN + 6) + goto fail; + + /* 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); + found = false; + i = 0; + while (i < nvp->nvram_len - BRCMF_FW_NVRAM_DEVPATH_LEN) { + /* 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], pci_path, pci_len) || + !strncmp(&nvp->nvram[i + 8], pcie_path, pcie_len))) { + id = nvp->nvram[i + 7] - '0'; + found = true; + break; + } + while (nvp->nvram[i] != 0) + i++; + i++; + } + if (!found) + goto fail; + + /* Now copy all valid entries, release old nvram and assign new one */ + i = 0; + j = 0; + while (i < nvp->nvram_len) { + if ((nvp->nvram[i] - '0' == id) && (nvp->nvram[i + 1] == ':')) { + i += 2; + while (nvp->nvram[i] != 0) { + nvram[j] = nvp->nvram[i]; + i++; + j++; + } + nvram[j] = 0; + j++; + } + while (nvp->nvram[i] != 0) + i++; + i++; + } + kfree(nvp->nvram); + nvp->nvram = nvram; + nvp->nvram_len = j; + return; + +fail: + kfree(nvram); + nvp->nvram_len = 0; +} + +/* brcmf_fw_strip_multi_v2 :Some nvram files contain settings for multiple + * devices. Strip it down for one device, use domain_nr/bus_nr to determine + * which data is to be returned. v2 is the version where nvram is stored + * uncompressed, all relevant valid entries are identified by + * pcie/domain_nr/bus_nr: + */ +static void brcmf_fw_strip_multi_v2(struct nvram_parser *nvp, u16 domain_nr, + u16 bus_nr) +{ + char prefix[BRCMF_FW_NVRAM_PCIEDEV_LEN]; + size_t len; + u32 i, j; + u8 *nvram; + + nvram = kzalloc(nvp->nvram_len + 1 + 3 + sizeof(u32), GFP_KERNEL); + if (!nvram) + goto fail; + + /* Copy all valid entries, release old nvram and assign new one. + * Valid entries are of type pcie/X/Y/ where X = domain_nr and + * Y = bus_nr. + */ + snprintf(prefix, sizeof(prefix), "pcie/%d/%d/", domain_nr, bus_nr); + len = strlen(prefix); + i = 0; + j = 0; + while (i < nvp->nvram_len - len) { + if (strncmp(&nvp->nvram[i], prefix, len) == 0) { + i += len; + while (nvp->nvram[i] != 0) { + nvram[j] = nvp->nvram[i]; + i++; + j++; + } + nvram[j] = 0; + j++; + } + while (nvp->nvram[i] != 0) + i++; + i++; + } + kfree(nvp->nvram); + nvp->nvram = nvram; + nvp->nvram_len = j; + return; +fail: + kfree(nvram); + nvp->nvram_len = 0; +} + /* brcmf_nvram_strip :Takes a buffer of "<var>=<value>\n" lines read from a fil * and ending in a NUL. Removes carriage returns, empty lines, comment lines, * 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) +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; } + if (nvp.multi_dev_v1) + brcmf_fw_strip_multi_v1(&nvp, domain_nr, bus_nr); + else if (nvp.multi_dev_v2) + brcmf_fw_strip_multi_v2(&nvp, domain_nr, bus_nr); + + if (nvp.nvram_len == 0) { + kfree(nvp.nvram); + return NULL; + } + pad = nvp.nvram_len; *new_length = roundup(nvp.nvram_len + 1, 4); while (pad != *new_length) { @@ -239,6 +416,8 @@ struct brcmf_fw { u16 flags; const struct firmware *code; const char *nvram_name; + u16 domain_nr; + u16 bus_nr; void (*done)(struct device *dev, const struct firmware *fw, void *nvram_image, u32 nvram_len); }; @@ -248,18 +427,33 @@ static void brcmf_fw_request_nvram_done(const struct firmware *fw, void *ctx) struct brcmf_fw *fwctx = ctx; u32 nvram_length = 0; void *nvram = NULL; + u8 *data = NULL; + size_t data_len; + bool raw_nvram; brcmf_dbg(TRACE, "enter: dev=%s\n", dev_name(fwctx->dev)); - if (!fw && !(fwctx->flags & BRCMF_FW_REQ_NV_OPTIONAL)) - goto fail; - - if (fw) { - nvram = brcmf_fw_nvram_strip(fw, &nvram_length); - release_firmware(fw); - if (!nvram && !(fwctx->flags & BRCMF_FW_REQ_NV_OPTIONAL)) + if (fw && fw->data) { + data = (u8 *)fw->data; + data_len = fw->size; + raw_nvram = false; + } else { + data = bcm47xx_nvram_get_contents(&data_len); + if (!data && !(fwctx->flags & BRCMF_FW_REQ_NV_OPTIONAL)) goto fail; + raw_nvram = true; } + if (data) + nvram = brcmf_fw_nvram_strip(data, data_len, &nvram_length, + fwctx->domain_nr, fwctx->bus_nr); + + if (raw_nvram) + bcm47xx_nvram_release_contents(data); + if (fw) + release_firmware(fw); + if (!nvram && !(fwctx->flags & BRCMF_FW_REQ_NV_OPTIONAL)) + goto fail; + fwctx->done(fwctx->dev, fwctx->code, nvram, nvram_length); kfree(fwctx); return; @@ -294,26 +488,21 @@ static void brcmf_fw_request_code_done(const struct firmware *fw, void *ctx) if (!ret) return; - /* when nvram is optional call .done() callback here */ - if (fwctx->flags & BRCMF_FW_REQ_NV_OPTIONAL) { - fwctx->done(fwctx->dev, fw, NULL, 0); - kfree(fwctx); - return; - } + brcmf_fw_request_nvram_done(NULL, fwctx); + return; - /* failed nvram request */ - release_firmware(fw); fail: brcmf_dbg(TRACE, "failed: dev=%s\n", dev_name(fwctx->dev)); device_release_driver(fwctx->dev); kfree(fwctx); } -int brcmf_fw_get_firmwares(struct device *dev, u16 flags, - const char *code, const char *nvram, - void (*fw_cb)(struct device *dev, - const struct firmware *fw, - void *nvram_image, u32 nvram_len)) +int brcmf_fw_get_firmwares_pcie(struct device *dev, u16 flags, + const char *code, const char *nvram, + void (*fw_cb)(struct device *dev, + const struct firmware *fw, + void *nvram_image, u32 nvram_len), + u16 domain_nr, u16 bus_nr) { struct brcmf_fw *fwctx; @@ -333,8 +522,21 @@ int brcmf_fw_get_firmwares(struct device *dev, u16 flags, fwctx->done = fw_cb; if (flags & BRCMF_FW_REQUEST_NVRAM) fwctx->nvram_name = nvram; + fwctx->domain_nr = domain_nr; + fwctx->bus_nr = bus_nr; return request_firmware_nowait(THIS_MODULE, true, code, dev, GFP_KERNEL, fwctx, brcmf_fw_request_code_done); } + +int brcmf_fw_get_firmwares(struct device *dev, u16 flags, + const char *code, const char *nvram, + void (*fw_cb)(struct device *dev, + const struct firmware *fw, + void *nvram_image, u32 nvram_len)) +{ + return brcmf_fw_get_firmwares_pcie(dev, flags, code, nvram, fw_cb, 0, + 0); +} + diff --git a/kernel/drivers/net/wireless/brcm80211/brcmfmac/firmware.h b/kernel/drivers/net/wireless/brcm80211/brcmfmac/firmware.h index 4d3482356..604dd48ab 100644 --- a/kernel/drivers/net/wireless/brcm80211/brcmfmac/firmware.h +++ b/kernel/drivers/net/wireless/brcm80211/brcmfmac/firmware.h @@ -32,6 +32,12 @@ void brcmf_fw_nvram_free(void *nvram); * fails it will not use the callback, but call device_release_driver() * instead which will call the driver .remove() callback. */ +int brcmf_fw_get_firmwares_pcie(struct device *dev, u16 flags, + const char *code, const char *nvram, + void (*fw_cb)(struct device *dev, + const struct firmware *fw, + void *nvram_image, u32 nvram_len), + u16 domain_nr, u16 bus_nr); int brcmf_fw_get_firmwares(struct device *dev, u16 flags, const char *code, const char *nvram, void (*fw_cb)(struct device *dev, diff --git a/kernel/drivers/net/wireless/brcm80211/brcmfmac/flowring.c b/kernel/drivers/net/wireless/brcm80211/brcmfmac/flowring.c index eb1325371..2ca783fa5 100644 --- a/kernel/drivers/net/wireless/brcm80211/brcmfmac/flowring.c +++ b/kernel/drivers/net/wireless/brcm80211/brcmfmac/flowring.c @@ -194,11 +194,15 @@ static void brcmf_flowring_block(struct brcmf_flowring *flow, u8 flowid, spin_lock_irqsave(&flow->block_lock, flags); ring = flow->rings[flowid]; + if (ring->blocked == blocked) { + spin_unlock_irqrestore(&flow->block_lock, flags); + return; + } ifidx = brcmf_flowring_ifidx_get(flow, flowid); currently_blocked = false; for (i = 0; i < flow->nrofrings; i++) { - if (flow->rings[i]) { + if ((flow->rings[i]) && (i != flowid)) { ring = flow->rings[i]; if ((ring->status == RING_OPEN) && (brcmf_flowring_ifidx_get(flow, i) == ifidx)) { @@ -209,15 +213,15 @@ static void brcmf_flowring_block(struct brcmf_flowring *flow, u8 flowid, } } } - ring->blocked = blocked; - if (currently_blocked == blocked) { + flow->rings[flowid]->blocked = blocked; + if (currently_blocked) { spin_unlock_irqrestore(&flow->block_lock, flags); return; } bus_if = dev_get_drvdata(flow->dev); drvr = bus_if->drvr; - ifp = drvr->iflist[ifidx]; + ifp = brcmf_get_ifp(drvr, ifidx); brcmf_txflowblock_if(ifp, BRCMF_NETIF_STOP_REASON_FLOW, blocked); spin_unlock_irqrestore(&flow->block_lock, flags); @@ -249,8 +253,8 @@ void brcmf_flowring_delete(struct brcmf_flowring *flow, u8 flowid) } -void brcmf_flowring_enqueue(struct brcmf_flowring *flow, u8 flowid, - struct sk_buff *skb) +u32 brcmf_flowring_enqueue(struct brcmf_flowring *flow, u8 flowid, + struct sk_buff *skb) { struct brcmf_flowring_ring *ring; @@ -271,6 +275,7 @@ void brcmf_flowring_enqueue(struct brcmf_flowring *flow, u8 flowid, if (skb_queue_len(&ring->skblist) < BRCMF_FLOWRING_LOW) brcmf_flowring_block(flow, flowid, false); } + return skb_queue_len(&ring->skblist); } diff --git a/kernel/drivers/net/wireless/brcm80211/brcmfmac/flowring.h b/kernel/drivers/net/wireless/brcm80211/brcmfmac/flowring.h index a34cd394c..95fd1c967 100644 --- a/kernel/drivers/net/wireless/brcm80211/brcmfmac/flowring.h +++ b/kernel/drivers/net/wireless/brcm80211/brcmfmac/flowring.h @@ -34,7 +34,7 @@ enum ring_status { }; struct brcmf_flowring_ring { - u8 hash_id; + u16 hash_id; bool blocked; enum ring_status status; struct sk_buff_head skblist; @@ -64,8 +64,8 @@ u32 brcmf_flowring_create(struct brcmf_flowring *flow, u8 da[ETH_ALEN], void brcmf_flowring_delete(struct brcmf_flowring *flow, u8 flowid); void brcmf_flowring_open(struct brcmf_flowring *flow, u8 flowid); u8 brcmf_flowring_tid(struct brcmf_flowring *flow, u8 flowid); -void brcmf_flowring_enqueue(struct brcmf_flowring *flow, u8 flowid, - struct sk_buff *skb); +u32 brcmf_flowring_enqueue(struct brcmf_flowring *flow, u8 flowid, + struct sk_buff *skb); struct sk_buff *brcmf_flowring_dequeue(struct brcmf_flowring *flow, u8 flowid); void brcmf_flowring_reinsert(struct brcmf_flowring *flow, u8 flowid, struct sk_buff *skb); diff --git a/kernel/drivers/net/wireless/brcm80211/brcmfmac/fweh.c b/kernel/drivers/net/wireless/brcm80211/brcmfmac/fweh.c index ec62492ff..3878b6f6c 100644 --- a/kernel/drivers/net/wireless/brcm80211/brcmfmac/fweh.c +++ b/kernel/drivers/net/wireless/brcm80211/brcmfmac/fweh.c @@ -179,25 +179,28 @@ static void brcmf_fweh_handle_if_event(struct brcmf_pub *drvr, { struct brcmf_if_event *ifevent = data; struct brcmf_if *ifp; + bool is_p2pdev; int err = 0; brcmf_dbg(EVENT, "action: %u idx: %u bsscfg: %u flags: %u role: %u\n", ifevent->action, ifevent->ifidx, ifevent->bssidx, ifevent->flags, ifevent->role); - /* The P2P Device interface event must not be ignored - * contrary to what firmware tells us. The only way to - * distinguish the P2P Device is by looking at the ifidx - * and bssidx received. + /* The P2P Device interface event must not be ignored contrary to what + * firmware tells us. Older firmware uses p2p noif, with sta role. + * This should be accepted when p2pdev_setup is ongoing. TDLS setup will + * use the same ifevent and should be ignored. */ - if (!(ifevent->ifidx == 0 && ifevent->bssidx == 1) && - (ifevent->flags & BRCMF_E_IF_FLAG_NOIF)) { + is_p2pdev = ((ifevent->flags & BRCMF_E_IF_FLAG_NOIF) && + (ifevent->role == BRCMF_E_IF_ROLE_P2P_CLIENT || + ((ifevent->role == BRCMF_E_IF_ROLE_STA) && + (drvr->fweh.p2pdev_setup_ongoing)))); + if (!is_p2pdev && (ifevent->flags & BRCMF_E_IF_FLAG_NOIF)) { brcmf_dbg(EVENT, "event can be ignored\n"); return; } if (ifevent->ifidx >= BRCMF_MAX_IFS) { - brcmf_err("invalid interface index: %u\n", - ifevent->ifidx); + brcmf_err("invalid interface index: %u\n", ifevent->ifidx); return; } @@ -207,10 +210,11 @@ static void brcmf_fweh_handle_if_event(struct brcmf_pub *drvr, brcmf_dbg(EVENT, "adding %s (%pM)\n", emsg->ifname, emsg->addr); ifp = brcmf_add_if(drvr, ifevent->bssidx, ifevent->ifidx, - emsg->ifname, emsg->addr); + is_p2pdev, emsg->ifname, emsg->addr); if (IS_ERR(ifp)) return; - brcmf_fws_add_interface(ifp); + if (!is_p2pdev) + brcmf_fws_add_interface(ifp); if (!drvr->fweh.evt_handler[BRCMF_E_IF]) if (brcmf_net_attach(ifp, false) < 0) return; @@ -222,7 +226,7 @@ static void brcmf_fweh_handle_if_event(struct brcmf_pub *drvr, err = brcmf_fweh_call_event_handler(ifp, emsg->event_code, emsg, data); if (ifp && ifevent->action == BRCMF_E_IF_DEL) - brcmf_remove_interface(drvr, ifevent->bssidx); + brcmf_remove_interface(ifp); } /** @@ -297,8 +301,7 @@ static void brcmf_fweh_event_worker(struct work_struct *work) goto event_free; } - if ((event->code == BRCMF_E_TDLS_PEER_EVENT) && - (emsg.bsscfgidx == 1)) + if (event->code == BRCMF_E_TDLS_PEER_EVENT) ifp = drvr->iflist[0]; else ifp = drvr->iflist[emsg.bsscfgidx]; @@ -315,6 +318,17 @@ event_free: } /** + * brcmf_fweh_p2pdev_setup() - P2P device setup ongoing (or not). + * + * @ifp: ifp on which setup is taking place or finished. + * @ongoing: p2p device setup in progress (or not). + */ +void brcmf_fweh_p2pdev_setup(struct brcmf_if *ifp, bool ongoing) +{ + ifp->drvr->fweh.p2pdev_setup_ongoing = ongoing; +} + +/** * brcmf_fweh_attach() - initialize firmware event handling. * * @drvr: driver information object. @@ -335,7 +349,7 @@ void brcmf_fweh_attach(struct brcmf_pub *drvr) void brcmf_fweh_detach(struct brcmf_pub *drvr) { struct brcmf_fweh_info *fweh = &drvr->fweh; - struct brcmf_if *ifp = drvr->iflist[0]; + struct brcmf_if *ifp = brcmf_get_ifp(drvr, 0); s8 eventmask[BRCMF_EVENTING_MASK_LEN]; if (ifp) { diff --git a/kernel/drivers/net/wireless/brcm80211/brcmfmac/fweh.h b/kernel/drivers/net/wireless/brcm80211/brcmfmac/fweh.h index cbf033f59..d9a942842 100644 --- a/kernel/drivers/net/wireless/brcm80211/brcmfmac/fweh.h +++ b/kernel/drivers/net/wireless/brcm80211/brcmfmac/fweh.h @@ -85,7 +85,6 @@ struct brcmf_event; BRCMF_ENUM_DEF(IF, 54) \ BRCMF_ENUM_DEF(P2P_DISC_LISTEN_COMPLETE, 55) \ BRCMF_ENUM_DEF(RSSI, 56) \ - BRCMF_ENUM_DEF(PFN_SCAN_COMPLETE, 57) \ BRCMF_ENUM_DEF(EXTLOG_MSG, 58) \ BRCMF_ENUM_DEF(ACTION_FRAME, 59) \ BRCMF_ENUM_DEF(ACTION_FRAME_COMPLETE, 60) \ @@ -103,8 +102,7 @@ struct brcmf_event; 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(PSTA_PRIMARY_INTF_IND, 128) + BRCMF_ENUM_DEF(BCMC_CREDIT_SUPPORT, 127) #define BRCMF_ENUM_DEF(id, val) \ BRCMF_E_##id = (val), @@ -112,7 +110,11 @@ struct brcmf_event; /* firmware event codes sent by the dongle */ enum brcmf_fweh_event_code { BRCMF_FWEH_EVENT_ENUM_DEFLIST - BRCMF_E_LAST + /* this determines event mask length which must match + * minimum length check in device firmware so it is + * hard-coded here. + */ + BRCMF_E_LAST = 139 }; #undef BRCMF_ENUM_DEF @@ -228,12 +230,14 @@ typedef int (*brcmf_fweh_handler_t)(struct brcmf_if *ifp, /** * struct brcmf_fweh_info - firmware event handling information. * + * @p2pdev_setup_ongoing: P2P device creation in progress. * @event_work: event worker. * @evt_q_lock: lock for event queue protection. * @event_q: event queue. * @evt_handler: registered event handlers. */ struct brcmf_fweh_info { + bool p2pdev_setup_ongoing; struct work_struct event_work; spinlock_t evt_q_lock; struct list_head event_q; @@ -253,6 +257,7 @@ void brcmf_fweh_unregister(struct brcmf_pub *drvr, int brcmf_fweh_activate_events(struct brcmf_if *ifp); void brcmf_fweh_process_event(struct brcmf_pub *drvr, struct brcmf_event *event_packet); +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) diff --git a/kernel/drivers/net/wireless/brcm80211/brcmfmac/fwil.h b/kernel/drivers/net/wireless/brcm80211/brcmfmac/fwil.h index 5434dcf64..b20fc0f82 100644 --- a/kernel/drivers/net/wireless/brcm80211/brcmfmac/fwil.h +++ b/kernel/drivers/net/wireless/brcm80211/brcmfmac/fwil.h @@ -72,6 +72,7 @@ #define BRCMF_C_GET_BSS_INFO 136 #define BRCMF_C_GET_BANDLIST 140 #define BRCMF_C_SET_SCB_TIMEOUT 158 +#define BRCMF_C_GET_ASSOCLIST 159 #define BRCMF_C_GET_PHYLIST 180 #define BRCMF_C_SET_SCAN_CHANNEL_TIME 185 #define BRCMF_C_SET_SCAN_UNASSOC_TIME 187 diff --git a/kernel/drivers/net/wireless/brcm80211/brcmfmac/fwil_types.h b/kernel/drivers/net/wireless/brcm80211/brcmfmac/fwil_types.h index 374920965..daa427b46 100644 --- a/kernel/drivers/net/wireless/brcm80211/brcmfmac/fwil_types.h +++ b/kernel/drivers/net/wireless/brcm80211/brcmfmac/fwil_types.h @@ -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,9 @@ #define BRCMF_WOWL_MAXPATTERNSIZE 128 #define BRCMF_COUNTRY_BUF_SZ 4 +#define BRCMF_ANT_MAX 4 + +#define BRCMF_MAX_ASSOCLIST 128 /* join preference types for join_pref iovar */ enum brcmf_join_pref_types { @@ -456,25 +463,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 { @@ -580,4 +623,15 @@ struct brcmf_rev_info_le { __le32 nvramrev; }; +/** + * struct brcmf_assoclist_le - request assoc list. + * + * @count: indicates number of stations. + * @mac: MAC addresses of stations. + */ +struct brcmf_assoclist_le { + __le32 count; + u8 mac[BRCMF_MAX_ASSOCLIST][ETH_ALEN]; +}; + #endif /* FWIL_TYPES_H_ */ diff --git a/kernel/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c b/kernel/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c index f0dda0ecd..086cac3f8 100644 --- a/kernel/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c +++ b/kernel/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c @@ -635,7 +635,7 @@ static int brcmf_fws_hanger_pushpkt(struct brcmf_fws_hanger *h, return 0; } -static int brcmf_fws_hanger_poppkt(struct brcmf_fws_hanger *h, +static inline int brcmf_fws_hanger_poppkt(struct brcmf_fws_hanger *h, u32 slot_id, struct sk_buff **pktout, bool remove_item) { @@ -972,7 +972,7 @@ static void brcmf_fws_flow_control_check(struct brcmf_fws_info *fws, struct pktq *pq, u8 if_id) { - struct brcmf_if *ifp = fws->drvr->iflist[!if_id ? 0 : if_id + 1]; + struct brcmf_if *ifp = brcmf_get_ifp(fws->drvr, if_id); if (WARN_ON(!ifp)) return; @@ -1398,7 +1398,7 @@ done: } static int brcmf_fws_txstatus_suppressed(struct brcmf_fws_info *fws, int fifo, - struct sk_buff *skb, u8 ifidx, + struct sk_buff *skb, u32 genbit, u16 seq) { struct brcmf_fws_mac_descriptor *entry = brcmf_skbcb(skb)->mac; @@ -1448,7 +1448,7 @@ brcmf_fws_txs_process(struct brcmf_fws_info *fws, u8 flags, u32 hslot, struct sk_buff *skb; struct brcmf_skbuff_cb *skcb; struct brcmf_fws_mac_descriptor *entry = NULL; - u8 ifidx; + struct brcmf_if *ifp; brcmf_dbg(DATA, "flags %d\n", flags); @@ -1497,15 +1497,16 @@ brcmf_fws_txs_process(struct brcmf_fws_info *fws, u8 flags, u32 hslot, } brcmf_fws_macdesc_return_req_credit(skb); - if (brcmf_proto_hdrpull(fws->drvr, false, &ifidx, skb)) { + ret = brcmf_proto_hdrpull(fws->drvr, false, skb, &ifp); + if (ret) { brcmu_pkt_buf_free_skb(skb); return -EINVAL; } if (!remove_from_hanger) - ret = brcmf_fws_txstatus_suppressed(fws, fifo, skb, ifidx, + ret = brcmf_fws_txstatus_suppressed(fws, fifo, skb, genbit, seq); if (remove_from_hanger || ret) - brcmf_txfinalize(fws->drvr, skb, ifidx, true); + brcmf_txfinalize(ifp, skb, true); return 0; } @@ -1615,11 +1616,10 @@ static int brcmf_fws_notify_bcmc_credit_support(struct brcmf_if *ifp, return 0; } -int brcmf_fws_hdrpull(struct brcmf_pub *drvr, int ifidx, s16 signal_len, - struct sk_buff *skb) +void brcmf_fws_hdrpull(struct brcmf_if *ifp, s16 siglen, struct sk_buff *skb) { struct brcmf_skb_reorder_data *rd; - struct brcmf_fws_info *fws = drvr->fws; + struct brcmf_fws_info *fws = ifp->drvr->fws; u8 *signal_data; s16 data_len; u8 type; @@ -1629,20 +1629,20 @@ int brcmf_fws_hdrpull(struct brcmf_pub *drvr, int ifidx, s16 signal_len, s32 err; brcmf_dbg(HDRS, "enter: ifidx %d, skblen %u, sig %d\n", - ifidx, skb->len, signal_len); + ifp->ifidx, skb->len, siglen); - WARN_ON(signal_len > skb->len); + WARN_ON(siglen > skb->len); - if (!signal_len) - return 0; + if (!siglen) + return; /* if flow control disabled, skip to packet data and leave */ if ((!fws) || (!fws->fw_signals)) { - skb_pull(skb, signal_len); - return 0; + skb_pull(skb, siglen); + return; } fws->stats.header_pulls++; - data_len = signal_len; + data_len = siglen; signal_data = skb->data; status = BRCMF_FWS_RET_OK_NOSCHEDULE; @@ -1730,14 +1730,12 @@ int brcmf_fws_hdrpull(struct brcmf_pub *drvr, int ifidx, s16 signal_len, /* signalling processing result does * not affect the actual ethernet packet. */ - skb_pull(skb, signal_len); + skb_pull(skb, siglen); /* this may be a signal-only packet */ if (skb->len == 0) fws->stats.header_only_pkt++; - - return 0; } static u8 brcmf_fws_precommit_skb(struct brcmf_fws_info *fws, int fifo, @@ -1848,7 +1846,7 @@ static int brcmf_fws_commit_skb(struct brcmf_fws_info *fws, int fifo, entry->transit_count--; if (entry->suppressed) entry->suppr_transit_count--; - brcmf_proto_hdrpull(fws->drvr, false, &ifidx, skb); + (void)brcmf_proto_hdrpull(fws->drvr, false, skb, NULL); goto rollback; } @@ -1904,7 +1902,7 @@ int brcmf_fws_process_skb(struct brcmf_if *ifp, struct sk_buff *skb) if (fws->avoid_queueing) { rc = brcmf_proto_txdata(drvr, ifp->ifidx, 0, skb); if (rc < 0) - brcmf_txfinalize(drvr, skb, ifp->ifidx, false); + brcmf_txfinalize(ifp, skb, false); return rc; } @@ -1928,7 +1926,7 @@ int brcmf_fws_process_skb(struct brcmf_if *ifp, struct sk_buff *skb) brcmf_fws_schedule_deq(fws); } else { brcmf_err("drop skb: no hanger slot\n"); - brcmf_txfinalize(drvr, skb, ifp->ifidx, false); + brcmf_txfinalize(ifp, skb, false); rc = -ENOMEM; } brcmf_fws_unlock(fws); @@ -2008,8 +2006,9 @@ static void brcmf_fws_dequeue_worker(struct work_struct *worker) ret = brcmf_proto_txdata(drvr, ifidx, 0, skb); brcmf_fws_lock(fws); if (ret < 0) - brcmf_txfinalize(drvr, skb, ifidx, - false); + brcmf_txfinalize(brcmf_get_ifp(drvr, + ifidx), + skb, false); if (fws->bus_flow_blocked) break; } @@ -2117,6 +2116,7 @@ static int brcmf_debugfs_fws_stats_read(struct seq_file *seq, void *data) int brcmf_fws_init(struct brcmf_pub *drvr) { struct brcmf_fws_info *fws; + struct brcmf_if *ifp; u32 tlv = BRCMF_FWS_FLAGS_RSSI_SIGNALS; int rc; u32 mode; @@ -2176,21 +2176,22 @@ int brcmf_fws_init(struct brcmf_pub *drvr) * continue. Set mode back to none indicating not enabled. */ fws->fw_signals = true; - if (brcmf_fil_iovar_int_set(drvr->iflist[0], "tlv", tlv)) { + ifp = brcmf_get_ifp(drvr, 0); + if (brcmf_fil_iovar_int_set(ifp, "tlv", tlv)) { brcmf_err("failed to set bdcv2 tlv signaling\n"); fws->fcmode = BRCMF_FWS_FCMODE_NONE; fws->fw_signals = false; } - if (brcmf_fil_iovar_int_set(drvr->iflist[0], "ampdu_hostreorder", 1)) + if (brcmf_fil_iovar_int_set(ifp, "ampdu_hostreorder", 1)) brcmf_dbg(INFO, "enabling AMPDU host-reorder failed\n"); /* Enable seq number reuse, if supported */ - if (brcmf_fil_iovar_int_get(drvr->iflist[0], "wlfc_mode", &mode) == 0) { + if (brcmf_fil_iovar_int_get(ifp, "wlfc_mode", &mode) == 0) { if (BRCMF_FWS_MODE_GET_REUSESEQ(mode)) { mode = 0; BRCMF_FWS_MODE_SET_REUSESEQ(mode, 1); - if (brcmf_fil_iovar_int_set(drvr->iflist[0], + if (brcmf_fil_iovar_int_set(ifp, "wlfc_mode", mode) == 0) { BRCMF_FWS_MODE_SET_REUSESEQ(fws->mode, 1); } diff --git a/kernel/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.h b/kernel/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.h index 9fc860910..a36bac17e 100644 --- a/kernel/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.h +++ b/kernel/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.h @@ -21,8 +21,7 @@ int brcmf_fws_init(struct brcmf_pub *drvr); void brcmf_fws_deinit(struct brcmf_pub *drvr); bool brcmf_fws_fc_active(struct brcmf_fws_info *fws); -int brcmf_fws_hdrpull(struct brcmf_pub *drvr, int ifidx, s16 signal_len, - struct sk_buff *skb); +void brcmf_fws_hdrpull(struct brcmf_if *ifp, s16 siglen, struct sk_buff *skb); int brcmf_fws_process_skb(struct brcmf_if *ifp, struct sk_buff *skb); void brcmf_fws_reset_interface(struct brcmf_if *ifp); diff --git a/kernel/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c b/kernel/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c index 65efb1468..44e618f9d 100644 --- a/kernel/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c +++ b/kernel/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c @@ -73,8 +73,10 @@ #define BRCMF_MSGBUF_TX_FLUSH_CNT1 32 #define BRCMF_MSGBUF_TX_FLUSH_CNT2 96 -#define BRCMF_MSGBUF_DELAY_TXWORKER_THRS 64 +#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; @@ -278,16 +280,6 @@ struct brcmf_msgbuf_pktids { struct brcmf_msgbuf_pktid *array; }; - -/* dma flushing needs implementation for mips and arm platforms. Should - * be put in util. Note, this is not real flushing. It is virtual non - * cached memory. Only write buffers should have to be drained. Though - * this may be different depending on platform...... - */ -#define brcmf_dma_flush(addr, len) -#define brcmf_dma_invalidate_cache(addr, len) - - static void brcmf_msgbuf_rxbuf_ioctlresp_post(struct brcmf_msgbuf *msgbuf); @@ -462,7 +454,6 @@ static int brcmf_msgbuf_tx_ioctl(struct brcmf_pub *drvr, int ifidx, memcpy(msgbuf->ioctbuf, buf, buf_len); else memset(msgbuf->ioctbuf, 0, buf_len); - brcmf_dma_flush(ioctl_buf, buf_len); err = brcmf_commonring_write_complete(commonring); brcmf_commonring_unlock(commonring); @@ -531,7 +522,7 @@ static int brcmf_msgbuf_set_dcmd(struct brcmf_pub *drvr, int ifidx, static int brcmf_msgbuf_hdrpull(struct brcmf_pub *drvr, bool do_fws, - u8 *ifidx, struct sk_buff *skb) + struct sk_buff *skb, struct brcmf_if **ifp) { return -ENODEV; } @@ -795,6 +786,8 @@ static int brcmf_msgbuf_txdata(struct brcmf_pub *drvr, int ifidx, struct brcmf_flowring *flow = msgbuf->flow; struct ethhdr *eh = (struct ethhdr *)(skb->data); u32 flowid; + u32 queue_count; + bool force; flowid = brcmf_flowring_lookup(flow, eh->h_dest, skb->priority, ifidx); if (flowid == BRCMF_FLOWRING_INVALID_ID) { @@ -802,8 +795,9 @@ static int brcmf_msgbuf_txdata(struct brcmf_pub *drvr, int ifidx, if (flowid == BRCMF_FLOWRING_INVALID_ID) return -ENOMEM; } - brcmf_flowring_enqueue(flow, flowid, skb); - brcmf_msgbuf_schedule_txdata(msgbuf, flowid, false); + queue_count = brcmf_flowring_enqueue(flow, flowid, skb); + force = ((queue_count % BRCMF_MSGBUF_TRICKLE_TXWORKER_THRS) == 0); + brcmf_msgbuf_schedule_txdata(msgbuf, flowid, force); return 0; } @@ -879,7 +873,8 @@ brcmf_msgbuf_process_txstatus(struct brcmf_msgbuf *msgbuf, void *buf) commonring = msgbuf->flowrings[flowid]; atomic_dec(&commonring->outstanding_tx); - brcmf_txfinalize(msgbuf->drvr, skb, tx_status->msg.ifidx, true); + brcmf_txfinalize(brcmf_get_ifp(msgbuf->drvr, tx_status->msg.ifidx), + skb, true); } @@ -1087,15 +1082,7 @@ brcmf_msgbuf_rx_skb(struct brcmf_msgbuf *msgbuf, struct sk_buff *skb, { struct brcmf_if *ifp; - /* The ifidx is the idx to map to matching netdev/ifp. When receiving - * events this is easy because it contains the bssidx which maps - * 1-on-1 to the netdev/ifp. But for data frames the ifidx is rcvd. - * bssidx 1 is used for p2p0 and no data can be received or - * transmitted on it. Therefor bssidx is ifidx + 1 if ifidx > 0 - */ - if (ifidx) - (ifidx)++; - ifp = msgbuf->drvr->iflist[ifidx]; + ifp = brcmf_get_ifp(msgbuf->drvr, ifidx); if (!ifp || !ifp->ndev) { brcmf_err("Received pkt for invalid ifidx %d\n", ifidx); brcmu_pkt_buf_free_skb(skb); @@ -1265,19 +1252,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; @@ -1358,6 +1353,60 @@ void brcmf_msgbuf_delete_flowring(struct brcmf_pub *drvr, u8 flowid) } } +#ifdef DEBUG +static int brcmf_msgbuf_stats_read(struct seq_file *seq, void *data) +{ + struct brcmf_bus *bus_if = dev_get_drvdata(seq->private); + struct brcmf_pub *drvr = bus_if->drvr; + struct brcmf_msgbuf *msgbuf = (struct brcmf_msgbuf *)drvr->proto->pd; + struct brcmf_commonring *commonring; + u16 i; + struct brcmf_flowring_ring *ring; + struct brcmf_flowring_hash *hash; + + commonring = msgbuf->commonrings[BRCMF_H2D_MSGRING_CONTROL_SUBMIT]; + seq_printf(seq, "h2d_ctl_submit: rp %4u, wp %4u, depth %4u\n", + commonring->r_ptr, commonring->w_ptr, commonring->depth); + commonring = msgbuf->commonrings[BRCMF_H2D_MSGRING_RXPOST_SUBMIT]; + seq_printf(seq, "h2d_rx_submit: rp %4u, wp %4u, depth %4u\n", + commonring->r_ptr, commonring->w_ptr, commonring->depth); + commonring = msgbuf->commonrings[BRCMF_D2H_MSGRING_CONTROL_COMPLETE]; + seq_printf(seq, "d2h_ctl_cmplt: rp %4u, wp %4u, depth %4u\n", + commonring->r_ptr, commonring->w_ptr, commonring->depth); + commonring = msgbuf->commonrings[BRCMF_D2H_MSGRING_TX_COMPLETE]; + seq_printf(seq, "d2h_tx_cmplt: rp %4u, wp %4u, depth %4u\n", + commonring->r_ptr, commonring->w_ptr, commonring->depth); + commonring = msgbuf->commonrings[BRCMF_D2H_MSGRING_RX_COMPLETE]; + seq_printf(seq, "d2h_rx_cmplt: rp %4u, wp %4u, depth %4u\n", + commonring->r_ptr, commonring->w_ptr, commonring->depth); + + seq_printf(seq, "\nh2d_flowrings: depth %u\n", + BRCMF_H2D_TXFLOWRING_MAX_ITEM); + seq_puts(seq, "Active flowrings:\n"); + hash = msgbuf->flow->hash; + for (i = 0; i < msgbuf->flow->nrofrings; i++) { + if (!msgbuf->flow->rings[i]) + continue; + ring = msgbuf->flow->rings[i]; + if (ring->status != RING_OPEN) + continue; + commonring = msgbuf->flowrings[i]; + hash = &msgbuf->flow->hash[ring->hash_id]; + seq_printf(seq, "id %3u: rp %4u, wp %4u, qlen %4u, blocked %u\n" + " ifidx %u, fifo %u, da %pM\n", + i, commonring->r_ptr, commonring->w_ptr, + skb_queue_len(&ring->skblist), ring->blocked, + hash->ifidx, hash->fifo, hash->mac); + } + + return 0; +} +#else +static int brcmf_msgbuf_stats_read(struct seq_file *seq, void *data) +{ + return 0; +} +#endif int brcmf_proto_msgbuf_attach(struct brcmf_pub *drvr) { @@ -1458,6 +1507,8 @@ int brcmf_proto_msgbuf_attach(struct brcmf_pub *drvr) spin_lock_init(&msgbuf->flowring_work_lock); INIT_LIST_HEAD(&msgbuf->work_queue); + brcmf_debugfs_add_entry(drvr, "msgbuf_stats", brcmf_msgbuf_stats_read); + return 0; fail: diff --git a/kernel/drivers/net/wireless/brcm80211/brcmfmac/of.c b/kernel/drivers/net/wireless/brcm80211/brcmfmac/of.c index c824570dd..03f35e0c5 100644 --- a/kernel/drivers/net/wireless/brcm80211/brcmfmac/of.c +++ b/kernel/drivers/net/wireless/brcm80211/brcmfmac/of.c @@ -39,10 +39,16 @@ void brcmf_of_probe(struct brcmf_sdio_dev *sdiodev) if (!sdiodev->pdata) return; + if (of_property_read_u32(np, "brcm,drive-strength", &val) == 0) + sdiodev->pdata->drive_strength = val; + + /* make sure there are interrupts defined in the node */ + if (!of_find_property(np, "interrupts", NULL)) + return; + irq = irq_of_parse_and_map(np, 0); if (!irq) { brcmf_err("interrupt could not be mapped\n"); - devm_kfree(dev, sdiodev->pdata); return; } irqf = irqd_get_trigger_type(irq_get_irq_data(irq)); @@ -50,7 +56,4 @@ void brcmf_of_probe(struct brcmf_sdio_dev *sdiodev) sdiodev->pdata->oob_irq_supported = true; sdiodev->pdata->oob_irq_nr = irq; sdiodev->pdata->oob_irq_flags = irqf; - - if (of_property_read_u32(np, "brcm,drive-strength", &val) == 0) - sdiodev->pdata->drive_strength = val; } diff --git a/kernel/drivers/net/wireless/brcm80211/brcmfmac/p2p.c b/kernel/drivers/net/wireless/brcm80211/brcmfmac/p2p.c index 710fbe570..d224b3dd7 100644 --- a/kernel/drivers/net/wireless/brcm80211/brcmfmac/p2p.c +++ b/kernel/drivers/net/wireless/brcm80211/brcmfmac/p2p.c @@ -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> @@ -1908,105 +1909,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. * * @p2p: P2P specific data. @@ -2182,11 +2084,13 @@ static struct wireless_dev *brcmf_p2p_create_p2pdev(struct brcmf_p2p_info *p2p, brcmf_p2p_set_firmware(pri_ifp, p2p->dev_addr); brcmf_cfg80211_arm_vif_event(p2p->cfg, p2p_vif); + brcmf_fweh_p2pdev_setup(pri_ifp, true); /* 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_fweh_p2pdev_setup(pri_ifp, false); brcmf_cfg80211_arm_vif_event(p2p->cfg, NULL); goto fail; } @@ -2195,6 +2099,7 @@ static struct wireless_dev *brcmf_p2p_create_p2pdev(struct brcmf_p2p_info *p2p, err = brcmf_cfg80211_wait_vif_event_timeout(p2p->cfg, BRCMF_E_IF_ADD, msecs_to_jiffies(1500)); brcmf_cfg80211_arm_vif_event(p2p->cfg, NULL); + brcmf_fweh_p2pdev_setup(pri_ifp, false); if (!err) { brcmf_err("timeout occurred\n"); err = -EIO; @@ -2229,19 +2134,6 @@ fail: } /** - * brcmf_p2p_delete_p2pdev() - delete P2P_DEVICE virtual interface. - * - * @vif: virtual interface object to delete. - */ -static void brcmf_p2p_delete_p2pdev(struct brcmf_p2p_info *p2p, - struct brcmf_cfg80211_vif *vif) -{ - cfg80211_unregister_wdev(&vif->wdev); - p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif = NULL; - brcmf_free_vif(vif); -} - -/** * brcmf_p2p_add_vif() - create a new P2P virtual interface. * * @wiphy: wiphy device of new interface. @@ -2352,6 +2244,7 @@ int brcmf_p2p_del_vif(struct wiphy *wiphy, struct wireless_dev *wdev) brcmf_dbg(TRACE, "delete P2P vif\n"); vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev); + brcmf_cfg80211_arm_vif_event(cfg, vif); switch (vif->wdev.iftype) { case NL80211_IFTYPE_P2P_CLIENT: if (test_bit(BRCMF_VIF_STATUS_DISCONNECTING, &vif->sme_state)) @@ -2364,8 +2257,10 @@ int brcmf_p2p_del_vif(struct wiphy *wiphy, struct wireless_dev *wdev) break; case NL80211_IFTYPE_P2P_DEVICE: - brcmf_p2p_delete_p2pdev(p2p, vif); - return 0; + if (!p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif) + return 0; + brcmf_p2p_cancel_remain_on_channel(vif->ifp); + brcmf_p2p_deinit_discovery(p2p); default: return -ENOTSUPP; } @@ -2377,10 +2272,11 @@ int brcmf_p2p_del_vif(struct wiphy *wiphy, struct wireless_dev *wdev) wait_for_completion_timeout(&cfg->vif_disabled, msecs_to_jiffies(500)); - brcmf_vif_clear_mgmt_ies(vif); - - brcmf_cfg80211_arm_vif_event(cfg, vif); - err = brcmf_p2p_release_p2p_if(vif); + err = 0; + if (vif->wdev.iftype != NL80211_IFTYPE_P2P_DEVICE) { + brcmf_vif_clear_mgmt_ies(vif); + err = brcmf_p2p_release_p2p_if(vif); + } if (!err) { /* wait for firmware event */ err = brcmf_cfg80211_wait_vif_event_timeout(cfg, BRCMF_E_IF_DEL, @@ -2390,12 +2286,31 @@ int brcmf_p2p_del_vif(struct wiphy *wiphy, struct wireless_dev *wdev) else err = 0; } + if (err) + brcmf_remove_interface(vif->ifp); + brcmf_cfg80211_arm_vif_event(cfg, NULL); - p2p->bss_idx[P2PAPI_BSSCFG_CONNECTION].vif = NULL; + if (vif->wdev.iftype != NL80211_IFTYPE_P2P_DEVICE) + p2p->bss_idx[P2PAPI_BSSCFG_CONNECTION].vif = NULL; return err; } +void brcmf_p2p_ifp_removed(struct brcmf_if *ifp) +{ + struct brcmf_cfg80211_info *cfg; + struct brcmf_cfg80211_vif *vif; + + brcmf_dbg(INFO, "P2P: device interface removed\n"); + vif = ifp->vif; + cfg = wdev_to_cfg(&vif->wdev); + cfg->p2p.bss_idx[P2PAPI_BSSCFG_DEVICE].vif = NULL; + rtnl_lock(); + cfg80211_unregister_wdev(&vif->wdev); + rtnl_unlock(); + brcmf_free_vif(vif); +} + int brcmf_p2p_start_device(struct wiphy *wiphy, struct wireless_dev *wdev) { struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); @@ -2419,9 +2334,68 @@ void brcmf_p2p_stop_device(struct wiphy *wiphy, struct wireless_dev *wdev) struct brcmf_cfg80211_vif *vif; vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev); - mutex_lock(&cfg->usr_sync); - (void)brcmf_p2p_deinit_discovery(p2p); - brcmf_abort_scanning(cfg); - clear_bit(BRCMF_VIF_STATUS_READY, &vif->sme_state); - mutex_unlock(&cfg->usr_sync); + /* This call can be result of the unregister_wdev call. In that case + * we dont want to do anything anymore. Just return. The config vif + * will have been cleared at this point. + */ + if (p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif == vif) { + mutex_lock(&cfg->usr_sync); + /* Set the discovery state to SCAN */ + (void)brcmf_p2p_set_discover_state(vif->ifp, + WL_P2P_DISC_ST_SCAN, 0, 0); + brcmf_abort_scanning(cfg); + 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. + * @p2pdev_forced: create p2p device interface at attach. + */ +s32 brcmf_p2p_attach(struct brcmf_cfg80211_info *cfg, bool p2pdev_forced) +{ + struct brcmf_p2p_info *p2p; + struct brcmf_if *pri_ifp; + s32 err = 0; + void *err_ptr; + + p2p = &cfg->p2p; + p2p->cfg = cfg; + + pri_ifp = brcmf_get_ifp(cfg->pub, 0); + p2p->bss_idx[P2PAPI_BSSCFG_PRIMARY].vif = pri_ifp->vif; + + if (p2pdev_forced) { + err_ptr = brcmf_p2p_create_p2pdev(p2p, NULL, NULL); + if (IS_ERR(err_ptr)) { + brcmf_err("P2P device creation failed.\n"); + err = PTR_ERR(err_ptr); + } + } else { + p2p->p2pdev_dynamically = true; + } + 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); + brcmf_remove_interface(vif->ifp); + } + /* just set it all to zero */ + memset(p2p, 0, sizeof(*p2p)); +} + diff --git a/kernel/drivers/net/wireless/brcm80211/brcmfmac/p2p.h b/kernel/drivers/net/wireless/brcm80211/brcmfmac/p2p.h index 872f382d9..5d4905902 100644 --- a/kernel/drivers/net/wireless/brcm80211/brcmfmac/p2p.h +++ b/kernel/drivers/net/wireless/brcm80211/brcmfmac/p2p.h @@ -124,6 +124,7 @@ struct afx_hdl { * @wait_next_af: thread synchronizing struct. * @gon_req_action: about to send go negotiation requets frame. * @block_gon_req_tx: drop tx go negotiation requets frame. + * @p2pdev_dynamically: is p2p device if created by module param or supplicant. */ struct brcmf_p2p_info { struct brcmf_cfg80211_info *cfg; @@ -144,9 +145,10 @@ struct brcmf_p2p_info { struct completion wait_next_af; bool gon_req_action; bool block_gon_req_tx; + bool p2pdev_dynamically; }; -s32 brcmf_p2p_attach(struct brcmf_cfg80211_info *cfg); +s32 brcmf_p2p_attach(struct brcmf_cfg80211_info *cfg, bool p2pdev_forced); void brcmf_p2p_detach(struct brcmf_p2p_info *p2p); struct wireless_dev *brcmf_p2p_add_vif(struct wiphy *wiphy, const char *name, unsigned char name_assign_type, @@ -155,6 +157,7 @@ struct wireless_dev *brcmf_p2p_add_vif(struct wiphy *wiphy, const char *name, int brcmf_p2p_del_vif(struct wiphy *wiphy, struct wireless_dev *wdev); int brcmf_p2p_ifchange(struct brcmf_cfg80211_info *cfg, enum brcmf_fil_p2p_if_types if_type); +void brcmf_p2p_ifp_removed(struct brcmf_if *ifp); int brcmf_p2p_start_device(struct wiphy *wiphy, struct wireless_dev *wdev); void brcmf_p2p_stop_device(struct wiphy *wiphy, struct wireless_dev *wdev); int brcmf_p2p_scan_prep(struct wiphy *wiphy, diff --git a/kernel/drivers/net/wireless/brcm80211/brcmfmac/pcie.c b/kernel/drivers/net/wireless/brcm80211/brcmfmac/pcie.c index 1831ecd08..83d804221 100644 --- a/kernel/drivers/net/wireless/brcm80211/brcmfmac/pcie.c +++ b/kernel/drivers/net/wireless/brcm80211/brcmfmac/pcie.c @@ -47,10 +47,20 @@ enum brcmf_pcie_state { #define BRCMF_PCIE_43602_FW_NAME "brcm/brcmfmac43602-pcie.bin" #define BRCMF_PCIE_43602_NVRAM_NAME "brcm/brcmfmac43602-pcie.txt" +#define BRCMF_PCIE_4350_FW_NAME "brcm/brcmfmac4350-pcie.bin" +#define BRCMF_PCIE_4350_NVRAM_NAME "brcm/brcmfmac4350-pcie.txt" #define BRCMF_PCIE_4356_FW_NAME "brcm/brcmfmac4356-pcie.bin" #define BRCMF_PCIE_4356_NVRAM_NAME "brcm/brcmfmac4356-pcie.txt" #define BRCMF_PCIE_43570_FW_NAME "brcm/brcmfmac43570-pcie.bin" #define BRCMF_PCIE_43570_NVRAM_NAME "brcm/brcmfmac43570-pcie.txt" +#define BRCMF_PCIE_4358_FW_NAME "brcm/brcmfmac4358-pcie.bin" +#define BRCMF_PCIE_4358_NVRAM_NAME "brcm/brcmfmac4358-pcie.txt" +#define BRCMF_PCIE_4365_FW_NAME "brcm/brcmfmac4365b-pcie.bin" +#define BRCMF_PCIE_4365_NVRAM_NAME "brcm/brcmfmac4365b-pcie.txt" +#define BRCMF_PCIE_4366_FW_NAME "brcm/brcmfmac4366b-pcie.bin" +#define BRCMF_PCIE_4366_NVRAM_NAME "brcm/brcmfmac4366b-pcie.txt" +#define BRCMF_PCIE_4371_FW_NAME "brcm/brcmfmac4371-pcie.bin" +#define BRCMF_PCIE_4371_NVRAM_NAME "brcm/brcmfmac4371-pcie.txt" #define BRCMF_PCIE_FW_UP_TIMEOUT 2000 /* msec */ @@ -72,6 +82,8 @@ enum brcmf_pcie_state { #define BRCMF_PCIE_REG_INTMASK 0x94 #define BRCMF_PCIE_REG_SBMBX 0x98 +#define BRCMF_PCIE_REG_LINK_STATUS_CTRL 0xBC + #define BRCMF_PCIE_PCIE2REG_INTMASK 0x24 #define BRCMF_PCIE_PCIE2REG_MAILBOXINT 0x48 #define BRCMF_PCIE_PCIE2REG_MAILBOXMASK 0x4C @@ -110,10 +122,11 @@ enum brcmf_pcie_state { BRCMF_PCIE_MB_INT_D2H3_DB0 | \ BRCMF_PCIE_MB_INT_D2H3_DB1) -#define BRCMF_PCIE_MIN_SHARED_VERSION 4 +#define BRCMF_PCIE_MIN_SHARED_VERSION 5 #define BRCMF_PCIE_MAX_SHARED_VERSION 5 #define BRCMF_PCIE_SHARED_VERSION_MASK 0x00FF -#define BRCMF_PCIE_SHARED_TXPUSH_SUPPORT 0x4000 +#define BRCMF_PCIE_SHARED_DMA_INDEX 0x10000 +#define BRCMF_PCIE_SHARED_DMA_2B_IDX 0x100000 #define BRCMF_PCIE_FLAGS_HTOD_SPLIT 0x4000 #define BRCMF_PCIE_FLAGS_DTOH_SPLIT 0x8000 @@ -145,6 +158,10 @@ enum brcmf_pcie_state { #define BRCMF_SHARED_RING_H2D_R_IDX_PTR_OFFSET 8 #define BRCMF_SHARED_RING_D2H_W_IDX_PTR_OFFSET 12 #define BRCMF_SHARED_RING_D2H_R_IDX_PTR_OFFSET 16 +#define BRCMF_SHARED_RING_H2D_WP_HADDR_OFFSET 20 +#define BRCMF_SHARED_RING_H2D_RP_HADDR_OFFSET 28 +#define BRCMF_SHARED_RING_D2H_WP_HADDR_OFFSET 36 +#define BRCMF_SHARED_RING_D2H_RP_HADDR_OFFSET 44 #define BRCMF_SHARED_RING_TCM_MEMLOC_OFFSET 0 #define BRCMF_SHARED_RING_MAX_SUB_QUEUES 52 @@ -185,10 +202,20 @@ enum brcmf_pcie_state { MODULE_FIRMWARE(BRCMF_PCIE_43602_FW_NAME); MODULE_FIRMWARE(BRCMF_PCIE_43602_NVRAM_NAME); +MODULE_FIRMWARE(BRCMF_PCIE_4350_FW_NAME); +MODULE_FIRMWARE(BRCMF_PCIE_4350_NVRAM_NAME); MODULE_FIRMWARE(BRCMF_PCIE_4356_FW_NAME); MODULE_FIRMWARE(BRCMF_PCIE_4356_NVRAM_NAME); MODULE_FIRMWARE(BRCMF_PCIE_43570_FW_NAME); MODULE_FIRMWARE(BRCMF_PCIE_43570_NVRAM_NAME); +MODULE_FIRMWARE(BRCMF_PCIE_4358_FW_NAME); +MODULE_FIRMWARE(BRCMF_PCIE_4358_NVRAM_NAME); +MODULE_FIRMWARE(BRCMF_PCIE_4365_FW_NAME); +MODULE_FIRMWARE(BRCMF_PCIE_4365_NVRAM_NAME); +MODULE_FIRMWARE(BRCMF_PCIE_4366_FW_NAME); +MODULE_FIRMWARE(BRCMF_PCIE_4366_NVRAM_NAME); +MODULE_FIRMWARE(BRCMF_PCIE_4371_FW_NAME); +MODULE_FIRMWARE(BRCMF_PCIE_4371_NVRAM_NAME); struct brcmf_pcie_console { @@ -244,6 +271,13 @@ struct brcmf_pciedev_info { bool mbdata_completed; bool irq_allocated; bool wowl_enabled; + u8 dma_idx_sz; + void *idxbuf; + u32 idxbuf_sz; + dma_addr_t idxbuf_dmahandle; + u16 (*read_ptr)(struct brcmf_pciedev_info *devinfo, u32 mem_offset); + void (*write_ptr)(struct brcmf_pciedev_info *devinfo, u32 mem_offset, + u16 value); }; struct brcmf_pcie_ringbuf { @@ -273,15 +307,6 @@ static const u32 brcmf_ring_itemsize[BRCMF_NROF_COMMON_MSGRINGS] = { }; -/* dma flushing needs implementation for mips and arm platforms. Should - * be put in util. Note, this is not real flushing. It is virtual non - * cached memory. Only write buffers should have to be drained. Though - * this may be different depending on platform...... - */ -#define brcmf_dma_flush(addr, len) -#define brcmf_dma_invalidate_cache(addr, len) - - static u32 brcmf_pcie_read_reg32(struct brcmf_pciedev_info *devinfo, u32 reg_offset) { @@ -329,6 +354,25 @@ brcmf_pcie_write_tcm16(struct brcmf_pciedev_info *devinfo, u32 mem_offset, } +static u16 +brcmf_pcie_read_idx(struct brcmf_pciedev_info *devinfo, u32 mem_offset) +{ + u16 *address = devinfo->idxbuf + mem_offset; + + return (*(address)); +} + + +static void +brcmf_pcie_write_idx(struct brcmf_pciedev_info *devinfo, u32 mem_offset, + u16 value) +{ + u16 *address = devinfo->idxbuf + mem_offset; + + *(address) = value; +} + + static u32 brcmf_pcie_read_tcm32(struct brcmf_pciedev_info *devinfo, u32 mem_offset) { @@ -408,6 +452,47 @@ brcmf_pcie_copy_mem_todev(struct brcmf_pciedev_info *devinfo, u32 mem_offset, } +static void +brcmf_pcie_copy_dev_tomem(struct brcmf_pciedev_info *devinfo, u32 mem_offset, + void *dstaddr, u32 len) +{ + void __iomem *address = devinfo->tcm + mem_offset; + __le32 *dst32; + __le16 *dst16; + u8 *dst8; + + if (((ulong)address & 4) || ((ulong)dstaddr & 4) || (len & 4)) { + if (((ulong)address & 2) || ((ulong)dstaddr & 2) || (len & 2)) { + dst8 = (u8 *)dstaddr; + while (len) { + *dst8 = ioread8(address); + address++; + dst8++; + len--; + } + } else { + len = len / 2; + dst16 = (__le16 *)dstaddr; + while (len) { + *dst16 = cpu_to_le16(ioread16(address)); + address += 2; + dst16++; + len--; + } + } + } else { + len = len / 4; + dst32 = (__le32 *)dstaddr; + while (len) { + *dst32 = cpu_to_le32(ioread32(address)); + address += 4; + dst32++; + len--; + } + } +} + + #define WRITECC32(devinfo, reg, value) brcmf_pcie_write_reg32(devinfo, \ CHIPCREGOFFS(reg), value) @@ -440,6 +525,7 @@ brcmf_pcie_select_core(struct brcmf_pciedev_info *devinfo, u16 coreid) static void brcmf_pcie_reset_device(struct brcmf_pciedev_info *devinfo) { + struct brcmf_core *core; u16 cfg_offset[] = { BRCMF_PCIE_CFGREG_STATUS_CMD, BRCMF_PCIE_CFGREG_PM_CSR, BRCMF_PCIE_CFGREG_MSI_CAP, @@ -458,32 +544,38 @@ static void brcmf_pcie_reset_device(struct brcmf_pciedev_info *devinfo) if (!devinfo->ci) return; + /* Disable ASPM */ brcmf_pcie_select_core(devinfo, BCMA_CORE_PCIE2); - brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_CONFIGADDR, - BRCMF_PCIE_CFGREG_LINK_STATUS_CTRL); - lsc = brcmf_pcie_read_reg32(devinfo, BRCMF_PCIE_PCIE2REG_CONFIGDATA); + pci_read_config_dword(devinfo->pdev, BRCMF_PCIE_REG_LINK_STATUS_CTRL, + &lsc); val = lsc & (~BRCMF_PCIE_LINK_STATUS_CTRL_ASPM_ENAB); - brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_CONFIGDATA, val); + pci_write_config_dword(devinfo->pdev, BRCMF_PCIE_REG_LINK_STATUS_CTRL, + val); + /* Watchdog reset */ brcmf_pcie_select_core(devinfo, BCMA_CORE_CHIPCOMMON); WRITECC32(devinfo, watchdog, 4); msleep(100); + /* Restore ASPM */ brcmf_pcie_select_core(devinfo, BCMA_CORE_PCIE2); - brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_CONFIGADDR, - BRCMF_PCIE_CFGREG_LINK_STATUS_CTRL); - brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_CONFIGDATA, lsc); - - brcmf_pcie_select_core(devinfo, BCMA_CORE_PCIE2); - for (i = 0; i < ARRAY_SIZE(cfg_offset); i++) { - brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_CONFIGADDR, - cfg_offset[i]); - val = brcmf_pcie_read_reg32(devinfo, - BRCMF_PCIE_PCIE2REG_CONFIGDATA); - brcmf_dbg(PCIE, "config offset 0x%04x, value 0x%04x\n", - cfg_offset[i], val); - brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_CONFIGDATA, - val); + pci_write_config_dword(devinfo->pdev, BRCMF_PCIE_REG_LINK_STATUS_CTRL, + lsc); + + core = brcmf_chip_get_core(devinfo->ci, BCMA_CORE_PCIE2); + if (core->rev <= 13) { + for (i = 0; i < ARRAY_SIZE(cfg_offset); i++) { + brcmf_pcie_write_reg32(devinfo, + BRCMF_PCIE_PCIE2REG_CONFIGADDR, + cfg_offset[i]); + val = brcmf_pcie_read_reg32(devinfo, + BRCMF_PCIE_PCIE2REG_CONFIGDATA); + brcmf_dbg(PCIE, "config offset 0x%04x, value 0x%04x\n", + cfg_offset[i], val); + brcmf_pcie_write_reg32(devinfo, + BRCMF_PCIE_PCIE2REG_CONFIGDATA, + val); + } } } @@ -493,8 +585,6 @@ static void brcmf_pcie_attach(struct brcmf_pciedev_info *devinfo) u32 config; brcmf_pcie_select_core(devinfo, BCMA_CORE_PCIE2); - if (brcmf_pcie_read_reg32(devinfo, BRCMF_PCIE_PCIE2REG_INTMASK) != 0) - brcmf_pcie_reset_device(devinfo); /* BAR1 window may not be sized properly */ brcmf_pcie_select_core(devinfo, BCMA_CORE_PCIE2); brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_CONFIGADDR, 0x4e0); @@ -618,7 +708,7 @@ static void brcmf_pcie_bus_console_init(struct brcmf_pciedev_info *devinfo) addr = console->base_addr + BRCMF_CONSOLE_BUFSIZE_OFFSET; console->bufsize = brcmf_pcie_read_tcm32(devinfo, addr); - brcmf_dbg(PCIE, "Console: base %x, buf %x, size %d\n", + brcmf_dbg(FWCON, "Console: base %x, buf %x, size %d\n", console->base_addr, console->buf_addr, console->bufsize); } @@ -630,6 +720,9 @@ static void brcmf_pcie_bus_console_read(struct brcmf_pciedev_info *devinfo) u8 ch; u32 newidx; + if (!BRCMF_FWCON_ON()) + return; + console = &devinfo->shared.console; addr = console->base_addr + BRCMF_CONSOLE_WRITEIDX_OFFSET; newidx = brcmf_pcie_read_tcm32(devinfo, addr); @@ -651,7 +744,7 @@ static void brcmf_pcie_bus_console_read(struct brcmf_pciedev_info *devinfo) } if (ch == '\n') { console->log_str[console->log_idx] = 0; - brcmf_dbg(PCIE, "CONSOLE: %s", console->log_str); + pr_debug("CONSOLE: %s", console->log_str); console->log_idx = 0; } } @@ -874,7 +967,7 @@ static int brcmf_pcie_ring_mb_write_rptr(void *ctx) brcmf_dbg(PCIE, "W r_ptr %d (%d), ring %d\n", commonring->r_ptr, commonring->w_ptr, ring->id); - brcmf_pcie_write_tcm16(devinfo, ring->r_idx_addr, commonring->r_ptr); + devinfo->write_ptr(devinfo, ring->r_idx_addr, commonring->r_ptr); return 0; } @@ -892,7 +985,7 @@ static int brcmf_pcie_ring_mb_write_wptr(void *ctx) brcmf_dbg(PCIE, "W w_ptr %d (%d), ring %d\n", commonring->w_ptr, commonring->r_ptr, ring->id); - brcmf_pcie_write_tcm16(devinfo, ring->w_idx_addr, commonring->w_ptr); + devinfo->write_ptr(devinfo, ring->w_idx_addr, commonring->w_ptr); return 0; } @@ -921,7 +1014,7 @@ static int brcmf_pcie_ring_mb_update_rptr(void *ctx) if (devinfo->state != BRCMFMAC_PCIE_STATE_UP) return -EIO; - commonring->r_ptr = brcmf_pcie_read_tcm16(devinfo, ring->r_idx_addr); + commonring->r_ptr = devinfo->read_ptr(devinfo, ring->r_idx_addr); brcmf_dbg(PCIE, "R r_ptr %d (%d), ring %d\n", commonring->r_ptr, commonring->w_ptr, ring->id); @@ -939,7 +1032,7 @@ static int brcmf_pcie_ring_mb_update_wptr(void *ctx) if (devinfo->state != BRCMFMAC_PCIE_STATE_UP) return -EIO; - commonring->w_ptr = brcmf_pcie_read_tcm16(devinfo, ring->w_idx_addr); + commonring->w_ptr = devinfo->read_ptr(devinfo, ring->w_idx_addr); brcmf_dbg(PCIE, "R w_ptr %d (%d), ring %d\n", commonring->w_ptr, commonring->r_ptr, ring->id); @@ -1044,6 +1137,13 @@ static void brcmf_pcie_release_ringbuffers(struct brcmf_pciedev_info *devinfo) } kfree(devinfo->shared.flowrings); devinfo->shared.flowrings = NULL; + if (devinfo->idxbuf) { + dma_free_coherent(&devinfo->pdev->dev, + devinfo->idxbuf_sz, + devinfo->idxbuf, + devinfo->idxbuf_dmahandle); + devinfo->idxbuf = NULL; + } } @@ -1059,19 +1159,72 @@ static int brcmf_pcie_init_ringbuffers(struct brcmf_pciedev_info *devinfo) u32 addr; u32 ring_mem_ptr; u32 i; + u64 address; + u32 bufsz; u16 max_sub_queues; + u8 idx_offset; ring_addr = devinfo->shared.ring_info_addr; brcmf_dbg(PCIE, "Base ring addr = 0x%08x\n", ring_addr); + addr = ring_addr + BRCMF_SHARED_RING_MAX_SUB_QUEUES; + max_sub_queues = brcmf_pcie_read_tcm16(devinfo, addr); + + if (devinfo->dma_idx_sz != 0) { + bufsz = (BRCMF_NROF_D2H_COMMON_MSGRINGS + max_sub_queues) * + devinfo->dma_idx_sz * 2; + devinfo->idxbuf = dma_alloc_coherent(&devinfo->pdev->dev, bufsz, + &devinfo->idxbuf_dmahandle, + GFP_KERNEL); + if (!devinfo->idxbuf) + devinfo->dma_idx_sz = 0; + } - addr = ring_addr + BRCMF_SHARED_RING_D2H_W_IDX_PTR_OFFSET; - d2h_w_idx_ptr = brcmf_pcie_read_tcm32(devinfo, addr); - addr = ring_addr + BRCMF_SHARED_RING_D2H_R_IDX_PTR_OFFSET; - d2h_r_idx_ptr = brcmf_pcie_read_tcm32(devinfo, addr); - addr = ring_addr + BRCMF_SHARED_RING_H2D_W_IDX_PTR_OFFSET; - h2d_w_idx_ptr = brcmf_pcie_read_tcm32(devinfo, addr); - addr = ring_addr + BRCMF_SHARED_RING_H2D_R_IDX_PTR_OFFSET; - h2d_r_idx_ptr = brcmf_pcie_read_tcm32(devinfo, addr); + if (devinfo->dma_idx_sz == 0) { + addr = ring_addr + BRCMF_SHARED_RING_D2H_W_IDX_PTR_OFFSET; + d2h_w_idx_ptr = brcmf_pcie_read_tcm32(devinfo, addr); + addr = ring_addr + BRCMF_SHARED_RING_D2H_R_IDX_PTR_OFFSET; + d2h_r_idx_ptr = brcmf_pcie_read_tcm32(devinfo, addr); + addr = ring_addr + BRCMF_SHARED_RING_H2D_W_IDX_PTR_OFFSET; + h2d_w_idx_ptr = brcmf_pcie_read_tcm32(devinfo, addr); + addr = ring_addr + BRCMF_SHARED_RING_H2D_R_IDX_PTR_OFFSET; + h2d_r_idx_ptr = brcmf_pcie_read_tcm32(devinfo, addr); + idx_offset = sizeof(u32); + devinfo->write_ptr = brcmf_pcie_write_tcm16; + devinfo->read_ptr = brcmf_pcie_read_tcm16; + brcmf_dbg(PCIE, "Using TCM indices\n"); + } else { + memset(devinfo->idxbuf, 0, bufsz); + devinfo->idxbuf_sz = bufsz; + idx_offset = devinfo->dma_idx_sz; + devinfo->write_ptr = brcmf_pcie_write_idx; + devinfo->read_ptr = brcmf_pcie_read_idx; + + h2d_w_idx_ptr = 0; + addr = ring_addr + BRCMF_SHARED_RING_H2D_WP_HADDR_OFFSET; + address = (u64)devinfo->idxbuf_dmahandle; + brcmf_pcie_write_tcm32(devinfo, addr, address & 0xffffffff); + brcmf_pcie_write_tcm32(devinfo, addr + 4, address >> 32); + + h2d_r_idx_ptr = h2d_w_idx_ptr + max_sub_queues * idx_offset; + addr = ring_addr + BRCMF_SHARED_RING_H2D_RP_HADDR_OFFSET; + address += max_sub_queues * idx_offset; + brcmf_pcie_write_tcm32(devinfo, addr, address & 0xffffffff); + brcmf_pcie_write_tcm32(devinfo, addr + 4, address >> 32); + + d2h_w_idx_ptr = h2d_r_idx_ptr + max_sub_queues * idx_offset; + addr = ring_addr + BRCMF_SHARED_RING_D2H_WP_HADDR_OFFSET; + address += max_sub_queues * idx_offset; + brcmf_pcie_write_tcm32(devinfo, addr, address & 0xffffffff); + brcmf_pcie_write_tcm32(devinfo, addr + 4, address >> 32); + + d2h_r_idx_ptr = d2h_w_idx_ptr + + BRCMF_NROF_D2H_COMMON_MSGRINGS * idx_offset; + addr = ring_addr + BRCMF_SHARED_RING_D2H_RP_HADDR_OFFSET; + address += BRCMF_NROF_D2H_COMMON_MSGRINGS * idx_offset; + brcmf_pcie_write_tcm32(devinfo, addr, address & 0xffffffff); + brcmf_pcie_write_tcm32(devinfo, addr + 4, address >> 32); + brcmf_dbg(PCIE, "Using host memory indices\n"); + } addr = ring_addr + BRCMF_SHARED_RING_TCM_MEMLOC_OFFSET; ring_mem_ptr = brcmf_pcie_read_tcm32(devinfo, addr); @@ -1085,8 +1238,8 @@ static int brcmf_pcie_init_ringbuffers(struct brcmf_pciedev_info *devinfo) ring->id = i; devinfo->shared.commonrings[i] = ring; - h2d_w_idx_ptr += sizeof(u32); - h2d_r_idx_ptr += sizeof(u32); + h2d_w_idx_ptr += idx_offset; + h2d_r_idx_ptr += idx_offset; ring_mem_ptr += BRCMF_RING_MEM_SZ; } @@ -1100,13 +1253,11 @@ static int brcmf_pcie_init_ringbuffers(struct brcmf_pciedev_info *devinfo) ring->id = i; devinfo->shared.commonrings[i] = ring; - d2h_w_idx_ptr += sizeof(u32); - d2h_r_idx_ptr += sizeof(u32); + d2h_w_idx_ptr += idx_offset; + d2h_r_idx_ptr += idx_offset; ring_mem_ptr += BRCMF_RING_MEM_SZ; } - addr = ring_addr + BRCMF_SHARED_RING_MAX_SUB_QUEUES; - max_sub_queues = brcmf_pcie_read_tcm16(devinfo, addr); devinfo->shared.nrof_flowrings = max_sub_queues - BRCMF_NROF_H2D_COMMON_MSGRINGS; rings = kcalloc(devinfo->shared.nrof_flowrings, sizeof(*ring), @@ -1130,15 +1281,15 @@ static int brcmf_pcie_init_ringbuffers(struct brcmf_pciedev_info *devinfo) ring); ring->w_idx_addr = h2d_w_idx_ptr; ring->r_idx_addr = h2d_r_idx_ptr; - h2d_w_idx_ptr += sizeof(u32); - h2d_r_idx_ptr += sizeof(u32); + h2d_w_idx_ptr += idx_offset; + h2d_r_idx_ptr += idx_offset; } devinfo->shared.flowrings = rings; return 0; fail: - brcmf_err("Allocating commonring buffers failed\n"); + brcmf_err("Allocating ring buffers failed\n"); brcmf_pcie_release_ringbuffers(devinfo); return -ENOMEM; } @@ -1171,7 +1322,6 @@ static int brcmf_pcie_init_scratchbuffers(struct brcmf_pciedev_info *devinfo) goto fail; memset(devinfo->shared.scratch, 0, BRCMF_DMA_D2H_SCRATCH_BUF_LEN); - brcmf_dma_flush(devinfo->shared.scratch, BRCMF_DMA_D2H_SCRATCH_BUF_LEN); addr = devinfo->shared.tcm_base_address + BRCMF_SHARED_DMA_SCRATCH_ADDR_OFFSET; @@ -1189,7 +1339,6 @@ static int brcmf_pcie_init_scratchbuffers(struct brcmf_pciedev_info *devinfo) goto fail; memset(devinfo->shared.ringupd, 0, BRCMF_DMA_D2H_RINGUPD_BUF_LEN); - brcmf_dma_flush(devinfo->shared.ringupd, BRCMF_DMA_D2H_RINGUPD_BUF_LEN); addr = devinfo->shared.tcm_base_address + BRCMF_SHARED_DMA_RINGUPD_ADDR_OFFSET; @@ -1248,12 +1397,36 @@ static void brcmf_pcie_wowl_config(struct device *dev, bool enabled) } +static size_t brcmf_pcie_get_ramsize(struct device *dev) +{ + struct brcmf_bus *bus_if = dev_get_drvdata(dev); + struct brcmf_pciedev *buspub = bus_if->bus_priv.pcie; + struct brcmf_pciedev_info *devinfo = buspub->devinfo; + + return devinfo->ci->ramsize - devinfo->ci->srsize; +} + + +static int brcmf_pcie_get_memdump(struct device *dev, void *data, size_t len) +{ + struct brcmf_bus *bus_if = dev_get_drvdata(dev); + struct brcmf_pciedev *buspub = bus_if->bus_priv.pcie; + struct brcmf_pciedev_info *devinfo = buspub->devinfo; + + brcmf_dbg(PCIE, "dump at 0x%08X: len=%zu\n", devinfo->ci->rambase, len); + brcmf_pcie_copy_dev_tomem(devinfo, devinfo->ci->rambase, data, len); + return 0; +} + + static struct brcmf_bus_ops brcmf_pcie_bus_ops = { .txdata = brcmf_pcie_tx, .stop = brcmf_pcie_down, .txctl = brcmf_pcie_tx_ctlpkt, .rxctl = brcmf_pcie_rx_ctlpkt, .wowl_config = brcmf_pcie_wowl_config, + .get_ramsize = brcmf_pcie_get_ramsize, + .get_memdump = brcmf_pcie_get_memdump, }; @@ -1276,10 +1449,13 @@ brcmf_pcie_init_share_ram_info(struct brcmf_pciedev_info *devinfo, brcmf_err("Unsupported PCIE version %d\n", version); return -EINVAL; } - if (shared->flags & BRCMF_PCIE_SHARED_TXPUSH_SUPPORT) { - brcmf_err("Unsupported legacy TX mode 0x%x\n", - shared->flags & BRCMF_PCIE_SHARED_TXPUSH_SUPPORT); - return -EINVAL; + + /* check firmware support dma indicies */ + if (shared->flags & BRCMF_PCIE_SHARED_DMA_INDEX) { + if (shared->flags & BRCMF_PCIE_SHARED_DMA_2B_IDX) + devinfo->dma_idx_sz = sizeof(u16); + else + devinfo->dma_idx_sz = sizeof(u32); } addr = sharedram_addr + BRCMF_SHARED_MAX_RXBUFPOST_OFFSET; @@ -1323,6 +1499,10 @@ static int brcmf_pcie_get_fwnames(struct brcmf_pciedev_info *devinfo) fw_name = BRCMF_PCIE_43602_FW_NAME; nvram_name = BRCMF_PCIE_43602_NVRAM_NAME; break; + case BRCM_CC_4350_CHIP_ID: + fw_name = BRCMF_PCIE_4350_FW_NAME; + nvram_name = BRCMF_PCIE_4350_NVRAM_NAME; + break; case BRCM_CC_4356_CHIP_ID: fw_name = BRCMF_PCIE_4356_FW_NAME; nvram_name = BRCMF_PCIE_4356_NVRAM_NAME; @@ -1333,6 +1513,22 @@ static int brcmf_pcie_get_fwnames(struct brcmf_pciedev_info *devinfo) fw_name = BRCMF_PCIE_43570_FW_NAME; nvram_name = BRCMF_PCIE_43570_NVRAM_NAME; break; + case BRCM_CC_4358_CHIP_ID: + fw_name = BRCMF_PCIE_4358_FW_NAME; + nvram_name = BRCMF_PCIE_4358_NVRAM_NAME; + break; + case BRCM_CC_4365_CHIP_ID: + fw_name = BRCMF_PCIE_4365_FW_NAME; + nvram_name = BRCMF_PCIE_4365_NVRAM_NAME; + break; + case BRCM_CC_4366_CHIP_ID: + fw_name = BRCMF_PCIE_4366_FW_NAME; + nvram_name = BRCMF_PCIE_4366_NVRAM_NAME; + break; + case BRCM_CC_4371_CHIP_ID: + fw_name = BRCMF_PCIE_4371_FW_NAME; + nvram_name = BRCMF_PCIE_4371_NVRAM_NAME; + break; default: brcmf_err("Unsupported chip 0x%04x\n", devinfo->ci->chip); return -ENODEV; @@ -1540,20 +1736,24 @@ static void brcmf_pcie_buscore_write32(void *ctx, u32 addr, u32 value) static int brcmf_pcie_buscoreprep(void *ctx) { + return brcmf_pcie_get_resource(ctx); +} + + +static int brcmf_pcie_buscore_reset(void *ctx, struct brcmf_chip *chip) +{ struct brcmf_pciedev_info *devinfo = (struct brcmf_pciedev_info *)ctx; - int err; + u32 val; - 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); - } + devinfo->ci = chip; + brcmf_pcie_reset_device(devinfo); - return err; + val = brcmf_pcie_read_reg32(devinfo, BRCMF_PCIE_PCIE2REG_MAILBOXINT); + if (val != 0xffffffff) + brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_MAILBOXINT, + val); + + return 0; } @@ -1568,6 +1768,7 @@ static void brcmf_pcie_buscore_activate(void *ctx, struct brcmf_chip *chip, static const struct brcmf_buscore_ops brcmf_pcie_buscore_ops = { .prepare = brcmf_pcie_buscoreprep, + .reset = brcmf_pcie_buscore_reset, .activate = brcmf_pcie_buscore_activate, .read32 = brcmf_pcie_buscore_read32, .write32 = brcmf_pcie_buscore_write32, @@ -1609,7 +1810,7 @@ static void brcmf_pcie_setup(struct device *dev, const struct firmware *fw, bus->msgbuf->commonrings[i] = &devinfo->shared.commonrings[i]->commonring; - flowrings = kcalloc(devinfo->shared.nrof_flowrings, sizeof(flowrings), + flowrings = kcalloc(devinfo->shared.nrof_flowrings, sizeof(*flowrings), GFP_KERNEL); if (!flowrings) goto fail; @@ -1641,8 +1842,13 @@ brcmf_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id) struct brcmf_pciedev_info *devinfo; struct brcmf_pciedev *pcie_bus_dev; struct brcmf_bus *bus; + u16 domain_nr; + u16 bus_nr; - brcmf_dbg(PCIE, "Enter %x:%x\n", pdev->vendor, pdev->device); + domain_nr = pci_domain_nr(pdev->bus) + 1; + bus_nr = pdev->bus->number; + brcmf_dbg(PCIE, "Enter %x:%x (%d/%d)\n", pdev->vendor, pdev->device, + domain_nr, bus_nr); ret = -ENOMEM; devinfo = kzalloc(sizeof(*devinfo), GFP_KERNEL); @@ -1691,10 +1897,10 @@ brcmf_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id) if (ret) goto fail_bus; - ret = brcmf_fw_get_firmwares(bus->dev, BRCMF_FW_REQUEST_NVRAM | - BRCMF_FW_REQ_NV_OPTIONAL, - devinfo->fw_name, devinfo->nvram_name, - brcmf_pcie_setup); + ret = brcmf_fw_get_firmwares_pcie(bus->dev, BRCMF_FW_REQUEST_NVRAM | + BRCMF_FW_REQ_NV_OPTIONAL, + devinfo->fw_name, devinfo->nvram_name, + brcmf_pcie_setup, domain_nr, bus_nr); if (ret == 0) return 0; fail_bus: @@ -1847,12 +2053,22 @@ cleanup: PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_NETWORK_OTHER << 8, 0xffff00, 0 } static struct pci_device_id brcmf_pcie_devid_table[] = { + BRCMF_PCIE_DEVICE(BRCM_PCIE_4350_DEVICE_ID), BRCMF_PCIE_DEVICE(BRCM_PCIE_4356_DEVICE_ID), BRCMF_PCIE_DEVICE(BRCM_PCIE_43567_DEVICE_ID), BRCMF_PCIE_DEVICE(BRCM_PCIE_43570_DEVICE_ID), + BRCMF_PCIE_DEVICE(BRCM_PCIE_4358_DEVICE_ID), BRCMF_PCIE_DEVICE(BRCM_PCIE_43602_DEVICE_ID), BRCMF_PCIE_DEVICE(BRCM_PCIE_43602_2G_DEVICE_ID), BRCMF_PCIE_DEVICE(BRCM_PCIE_43602_5G_DEVICE_ID), + BRCMF_PCIE_DEVICE(BRCM_PCIE_43602_RAW_DEVICE_ID), + BRCMF_PCIE_DEVICE(BRCM_PCIE_4365_DEVICE_ID), + BRCMF_PCIE_DEVICE(BRCM_PCIE_4365_2G_DEVICE_ID), + BRCMF_PCIE_DEVICE(BRCM_PCIE_4365_5G_DEVICE_ID), + BRCMF_PCIE_DEVICE(BRCM_PCIE_4366_DEVICE_ID), + BRCMF_PCIE_DEVICE(BRCM_PCIE_4366_2G_DEVICE_ID), + BRCMF_PCIE_DEVICE(BRCM_PCIE_4366_5G_DEVICE_ID), + BRCMF_PCIE_DEVICE(BRCM_PCIE_4371_DEVICE_ID), { /* end: all zeroes */ } }; diff --git a/kernel/drivers/net/wireless/brcm80211/brcmfmac/proto.h b/kernel/drivers/net/wireless/brcm80211/brcmfmac/proto.h index 971172ff6..d55119d36 100644 --- a/kernel/drivers/net/wireless/brcm80211/brcmfmac/proto.h +++ b/kernel/drivers/net/wireless/brcm80211/brcmfmac/proto.h @@ -24,8 +24,8 @@ enum proto_addr_mode { struct brcmf_proto { - int (*hdrpull)(struct brcmf_pub *drvr, bool do_fws, u8 *ifidx, - struct sk_buff *skb); + int (*hdrpull)(struct brcmf_pub *drvr, bool do_fws, + struct sk_buff *skb, struct brcmf_if **ifp); int (*query_dcmd)(struct brcmf_pub *drvr, int ifidx, uint cmd, void *buf, uint len); int (*set_dcmd)(struct brcmf_pub *drvr, int ifidx, uint cmd, void *buf, @@ -46,9 +46,19 @@ int brcmf_proto_attach(struct brcmf_pub *drvr); void brcmf_proto_detach(struct brcmf_pub *drvr); static inline int brcmf_proto_hdrpull(struct brcmf_pub *drvr, bool do_fws, - u8 *ifidx, struct sk_buff *skb) + struct sk_buff *skb, + struct brcmf_if **ifp) { - return drvr->proto->hdrpull(drvr, do_fws, ifidx, skb); + struct brcmf_if *tmp = NULL; + + /* assure protocol is always called with + * non-null initialized pointer. + */ + if (ifp) + *ifp = NULL; + else + ifp = &tmp; + return drvr->proto->hdrpull(drvr, do_fws, skb, ifp); } static inline int brcmf_proto_query_dcmd(struct brcmf_pub *drvr, int ifidx, uint cmd, void *buf, uint len) diff --git a/kernel/drivers/net/wireless/brcm80211/brcmfmac/sdio.c b/kernel/drivers/net/wireless/brcm80211/brcmfmac/sdio.c index ab0c89833..7e74ac3ad 100644 --- a/kernel/drivers/net/wireless/brcm80211/brcmfmac/sdio.c +++ b/kernel/drivers/net/wireless/brcm80211/brcmfmac/sdio.c @@ -15,6 +15,7 @@ */ #include <linux/types.h> +#include <linux/atomic.h> #include <linux/kernel.h> #include <linux/kthread.h> #include <linux/printk.h> @@ -123,6 +124,7 @@ struct rte_console { #define BRCMF_FIRSTREAD (1 << 6) +#define BRCMF_CONSOLE 10 /* watchdog interval to poll console */ /* SBSDIO_DEVICE_CTL */ @@ -601,6 +603,8 @@ static const struct sdiod_drive_str sdiod_drvstr_tab2_3v3[] = { #define BCM43241B0_NVRAM_NAME "brcm/brcmfmac43241b0-sdio.txt" #define BCM43241B4_FIRMWARE_NAME "brcm/brcmfmac43241b4-sdio.bin" #define BCM43241B4_NVRAM_NAME "brcm/brcmfmac43241b4-sdio.txt" +#define BCM43241B5_FIRMWARE_NAME "brcm/brcmfmac43241b5-sdio.bin" +#define BCM43241B5_NVRAM_NAME "brcm/brcmfmac43241b5-sdio.txt" #define BCM4329_FIRMWARE_NAME "brcm/brcmfmac4329-sdio.bin" #define BCM4329_NVRAM_NAME "brcm/brcmfmac4329-sdio.txt" #define BCM4330_FIRMWARE_NAME "brcm/brcmfmac4330-sdio.bin" @@ -628,6 +632,8 @@ MODULE_FIRMWARE(BCM43241B0_FIRMWARE_NAME); MODULE_FIRMWARE(BCM43241B0_NVRAM_NAME); MODULE_FIRMWARE(BCM43241B4_FIRMWARE_NAME); MODULE_FIRMWARE(BCM43241B4_NVRAM_NAME); +MODULE_FIRMWARE(BCM43241B5_FIRMWARE_NAME); +MODULE_FIRMWARE(BCM43241B5_NVRAM_NAME); MODULE_FIRMWARE(BCM4329_FIRMWARE_NAME); MODULE_FIRMWARE(BCM4329_NVRAM_NAME); MODULE_FIRMWARE(BCM4330_FIRMWARE_NAME); @@ -667,7 +673,8 @@ enum brcmf_firmware_type { static const struct brcmf_firmware_names brcmf_fwname_data[] = { { BRCM_CC_43143_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM43143) }, { BRCM_CC_43241_CHIP_ID, 0x0000001F, BRCMF_FIRMWARE_NVRAM(BCM43241B0) }, - { BRCM_CC_43241_CHIP_ID, 0xFFFFFFE0, BRCMF_FIRMWARE_NVRAM(BCM43241B4) }, + { BRCM_CC_43241_CHIP_ID, 0x00000020, BRCMF_FIRMWARE_NVRAM(BCM43241B4) }, + { BRCM_CC_43241_CHIP_ID, 0xFFFFFFC0, BRCMF_FIRMWARE_NVRAM(BCM43241B5) }, { BRCM_CC_4329_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4329) }, { BRCM_CC_4330_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4330) }, { BRCM_CC_4334_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4334) }, @@ -2559,15 +2566,6 @@ static inline void brcmf_sdio_clrintr(struct brcmf_sdio *bus) } } -static void atomic_orr(int val, atomic_t *v) -{ - int old_val; - - old_val = atomic_read(v); - while (atomic_cmpxchg(v, old_val, val | old_val) != old_val) - old_val = atomic_read(v); -} - static int brcmf_sdio_intr_rstatus(struct brcmf_sdio *bus) { struct brcmf_core *buscore; @@ -2590,7 +2588,7 @@ static int brcmf_sdio_intr_rstatus(struct brcmf_sdio *bus) if (val) { brcmf_sdiod_regwl(bus->sdiodev, addr, val, &ret); bus->sdcnt.f1regdata++; - atomic_orr(val, &bus->intstatus); + atomic_or(val, &bus->intstatus); } return ret; @@ -2707,7 +2705,7 @@ static void brcmf_sdio_dpc(struct brcmf_sdio *bus) /* Keep still-pending events for next scheduling */ if (intstatus) - atomic_orr(intstatus, &bus->intstatus); + atomic_or(intstatus, &bus->intstatus); brcmf_sdio_clrintr(bus); @@ -2815,6 +2813,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); @@ -2943,6 +2943,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; @@ -3204,6 +3206,8 @@ static void brcmf_sdio_debugfs_create(struct brcmf_sdio *bus) if (IS_ERR_OR_NULL(dentry)) return; + bus->console_interval = BRCMF_CONSOLE; + brcmf_debugfs_add_entry(drvr, "forensics", brcmf_sdio_forensic_read); brcmf_debugfs_add_entry(drvr, "counters", brcmf_debugfs_sdio_count_read); @@ -3233,6 +3237,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); @@ -3533,6 +3539,51 @@ done: return err; } +static size_t brcmf_sdio_bus_get_ramsize(struct device *dev) +{ + struct brcmf_bus *bus_if = dev_get_drvdata(dev); + struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio; + struct brcmf_sdio *bus = sdiodev->bus; + + return bus->ci->ramsize - bus->ci->srsize; +} + +static int brcmf_sdio_bus_get_memdump(struct device *dev, void *data, + size_t mem_size) +{ + struct brcmf_bus *bus_if = dev_get_drvdata(dev); + struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio; + struct brcmf_sdio *bus = sdiodev->bus; + int err; + int address; + int offset; + int len; + + brcmf_dbg(INFO, "dump at 0x%08x: size=%zu\n", bus->ci->rambase, + mem_size); + + address = bus->ci->rambase; + offset = err = 0; + sdio_claim_host(sdiodev->func[1]); + while (offset < mem_size) { + len = ((offset + MEMBLOCK) < mem_size) ? MEMBLOCK : + mem_size - offset; + err = brcmf_sdiod_ramrw(sdiodev, false, address, data, len); + if (err) { + brcmf_err("error %d on reading %d membytes at 0x%08x\n", + err, len, address); + goto done; + } + data += len; + offset += len; + address += len; + } + +done: + sdio_release_host(sdiodev->func[1]); + return err; +} + void brcmf_sdio_trigger_dpc(struct brcmf_sdio *bus) { if (!bus->dpc_triggered) { @@ -3550,10 +3601,6 @@ void brcmf_sdio_isr(struct brcmf_sdio *bus) return; } - if (bus->sdiodev->state != BRCMF_SDIOD_DATA) { - brcmf_err("bus is down. we have nothing to do\n"); - return; - } /* Count the interrupt call */ bus->sdcnt.intrcount++; if (in_interrupt()) @@ -3615,7 +3662,7 @@ static void brcmf_sdio_bus_watchdog(struct brcmf_sdio *bus) } #ifdef DEBUG /* Poll for console output periodically */ - if (bus->sdiodev->state == BRCMF_SDIOD_DATA && + if (bus->sdiodev->state == BRCMF_SDIOD_DATA && BRCMF_FWCON_ON() && bus->console_interval != 0) { bus->console.count += BRCMF_WD_POLL_MS; if (bus->console.count >= bus->console_interval) { @@ -3985,7 +4032,9 @@ static struct brcmf_bus_ops brcmf_sdio_bus_ops = { .txctl = brcmf_sdio_bus_txctl, .rxctl = brcmf_sdio_bus_rxctl, .gettxq = brcmf_sdio_bus_gettxq, - .wowl_config = brcmf_sdio_wowl_config + .wowl_config = brcmf_sdio_wowl_config, + .get_ramsize = brcmf_sdio_bus_get_ramsize, + .get_memdump = brcmf_sdio_bus_get_memdump, }; static void brcmf_sdio_firmware_callback(struct device *dev, diff --git a/kernel/drivers/net/wireless/brcm80211/brcmfmac/usb.c b/kernel/drivers/net/wireless/brcm80211/brcmfmac/usb.c index 5df6aa72c..689e64d00 100644 --- a/kernel/drivers/net/wireless/brcm80211/brcmfmac/usb.c +++ b/kernel/drivers/net/wireless/brcm80211/brcmfmac/usb.c @@ -144,6 +144,7 @@ struct brcmf_usbdev_info { struct usb_device *usbdev; struct device *dev; + struct mutex dev_init_lock; int ctl_in_pipe, ctl_out_pipe; struct urb *ctl_urb; /* URB for control endpoint */ @@ -1204,6 +1205,8 @@ static void brcmf_usb_probe_phase2(struct device *dev, int ret; brcmf_dbg(USB, "Start fw downloading\n"); + + devinfo = bus->bus_priv.usb->devinfo; ret = check_file(fw->data); if (ret < 0) { brcmf_err("invalid firmware\n"); @@ -1211,7 +1214,6 @@ static void brcmf_usb_probe_phase2(struct device *dev, goto error; } - devinfo = bus->bus_priv.usb->devinfo; devinfo->image = fw->data; devinfo->image_len = fw->size; @@ -1224,9 +1226,11 @@ static void brcmf_usb_probe_phase2(struct device *dev, if (ret) goto error; + mutex_unlock(&devinfo->dev_init_lock); return; error: brcmf_dbg(TRACE, "failed: dev=%s, err=%d\n", dev_name(dev), ret); + mutex_unlock(&devinfo->dev_init_lock); device_release_driver(dev); } @@ -1264,14 +1268,20 @@ static int brcmf_usb_probe_cb(struct brcmf_usbdev_info *devinfo) if (ret) goto fail; /* we are done */ + mutex_unlock(&devinfo->dev_init_lock); return 0; } bus->chip = bus_pub->devid; bus->chiprev = bus_pub->chiprev; /* request firmware here */ - brcmf_fw_get_firmwares(dev, 0, brcmf_usb_get_fwname(devinfo), NULL, - brcmf_usb_probe_phase2); + ret = brcmf_fw_get_firmwares(dev, 0, brcmf_usb_get_fwname(devinfo), + NULL, brcmf_usb_probe_phase2); + if (ret) { + brcmf_err("firmware request failed: %d\n", ret); + goto fail; + } + return 0; fail: @@ -1312,6 +1322,12 @@ 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); + usb_set_intfdata(intf, devinfo); /* Check that the device supports only one configuration */ @@ -1386,6 +1402,7 @@ brcmf_usb_probe(struct usb_interface *intf, const struct usb_device_id *id) return 0; fail: + mutex_unlock(&devinfo->dev_init_lock); kfree(devinfo); usb_set_intfdata(intf, NULL); return ret; @@ -1398,8 +1415,19 @@ brcmf_usb_disconnect(struct usb_interface *intf) brcmf_dbg(USB, "Enter\n"); devinfo = (struct brcmf_usbdev_info *)usb_get_intfdata(intf); - brcmf_usb_disconnect_cb(devinfo); - kfree(devinfo); + + if (devinfo) { + mutex_lock(&devinfo->dev_init_lock); + /* Make sure that devinfo still exists. Firmware probe routines + * may have released the device and cleared the intfdata. + */ + if (!usb_get_intfdata(intf)) + goto done; + + brcmf_usb_disconnect_cb(devinfo); + kfree(devinfo); + } +done: brcmf_dbg(USB, "Exit\n"); } |