From 89c771e5a62b856f4705f189892c489190edaec1 Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Fri, 10 Oct 2014 20:52:40 +0300 Subject: cfg80211: Convert del_station() callback to use a param struct This makes it easier to add new parameters for the del_station calls without having to modify all drivers that use this. Signed-off-by: Jouni Malinen Signed-off-by: Johannes Berg --- drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/net/wireless/brcm80211') diff --git a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c index 28fa25b509db..1a2e062d3bfd 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c @@ -3998,24 +3998,24 @@ brcmf_cfg80211_change_beacon(struct wiphy *wiphy, struct net_device *ndev, static int brcmf_cfg80211_del_station(struct wiphy *wiphy, struct net_device *ndev, - const u8 *mac) + struct station_del_parameters *params) { struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); struct brcmf_scb_val_le scbval; struct brcmf_if *ifp = netdev_priv(ndev); s32 err; - if (!mac) + if (!params->mac) return -EFAULT; - brcmf_dbg(TRACE, "Enter %pM\n", mac); + brcmf_dbg(TRACE, "Enter %pM\n", params->mac); if (ifp->vif == cfg->p2p.bss_idx[P2PAPI_BSSCFG_DEVICE].vif) ifp = cfg->p2p.bss_idx[P2PAPI_BSSCFG_PRIMARY].vif->ifp; if (!check_vif_up(ifp->vif)) return -EIO; - memcpy(&scbval.ea, mac, ETH_ALEN); + memcpy(&scbval.ea, params->mac, ETH_ALEN); scbval.val = cpu_to_le32(WLAN_REASON_DEAUTH_LEAVING); err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SCB_DEAUTHENTICATE_FOR_REASON, &scbval, sizeof(scbval)); -- cgit v1.2.3 From 244b124c6a43eebaa746780d8390b93ec1519c8f Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Tue, 28 Oct 2014 14:56:04 +0100 Subject: brcmfmac: Add wowl support for USB devices. This patch adds wowl support for USB bus devices. This feature requires FW which has support for wowl built in. Reviewed-by: Arend Van Spriel Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Daniel (Deognyoun) Kim Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/usb.c | 49 +++++++++++++++++++++------ 1 file changed, 39 insertions(+), 10 deletions(-) (limited to 'drivers/net/wireless/brcm80211') diff --git a/drivers/net/wireless/brcm80211/brcmfmac/usb.c b/drivers/net/wireless/brcm80211/brcmfmac/usb.c index dc135915470d..e533000787f3 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/usb.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/usb.c @@ -93,6 +93,8 @@ struct brcmf_usbdev_info { u8 ifnum; struct urb *bulk_urb; /* used for FW download */ + + bool wowl_enabled; }; static void brcmf_usb_rx_refill(struct brcmf_usbdev_info *devinfo, @@ -600,6 +602,16 @@ static int brcmf_usb_up(struct device *dev) return 0; } +static void brcmf_cancel_all_urbs(struct brcmf_usbdev_info *devinfo) +{ + if (devinfo->ctl_urb) + usb_kill_urb(devinfo->ctl_urb); + if (devinfo->bulk_urb) + usb_kill_urb(devinfo->bulk_urb); + brcmf_usb_free_q(&devinfo->tx_postq, true); + brcmf_usb_free_q(&devinfo->rx_postq, true); +} + static void brcmf_usb_down(struct device *dev) { struct brcmf_usbdev_info *devinfo = brcmf_usb_get_businfo(dev); @@ -613,14 +625,7 @@ static void brcmf_usb_down(struct device *dev) brcmf_usb_state_change(devinfo, BRCMFMAC_USB_STATE_DOWN); - if (devinfo->ctl_urb) - usb_kill_urb(devinfo->ctl_urb); - - if (devinfo->bulk_urb) - usb_kill_urb(devinfo->bulk_urb); - brcmf_usb_free_q(&devinfo->tx_postq, true); - - brcmf_usb_free_q(&devinfo->rx_postq, true); + brcmf_cancel_all_urbs(devinfo); } static void @@ -1094,11 +1099,24 @@ error: return NULL; } +static void brcmf_usb_wowl_config(struct device *dev, bool enabled) +{ + struct brcmf_usbdev_info *devinfo = brcmf_usb_get_businfo(dev); + + brcmf_dbg(USB, "Configuring WOWL, enabled=%d\n", enabled); + devinfo->wowl_enabled = enabled; + if (enabled) + device_set_wakeup_enable(devinfo->dev, true); + else + device_set_wakeup_enable(devinfo->dev, false); +} + static struct brcmf_bus_ops brcmf_usb_bus_ops = { .txdata = brcmf_usb_tx, .stop = brcmf_usb_down, .txctl = brcmf_usb_tx_ctlpkt, .rxctl = brcmf_usb_rx_ctlpkt, + .wowl_config = brcmf_usb_wowl_config, }; static int brcmf_usb_bus_setup(struct brcmf_usbdev_info *devinfo) @@ -1186,6 +1204,9 @@ static int brcmf_usb_probe_cb(struct brcmf_usbdev_info *devinfo) bus->ops = &brcmf_usb_bus_ops; bus->proto_type = BRCMF_PROTO_BCDC; bus->always_use_fws_queue = true; +#ifdef CONFIG_PM + bus->wowl_supported = true; +#endif if (!brcmf_usb_dlneeded(devinfo)) { ret = brcmf_usb_bus_setup(devinfo); @@ -1339,7 +1360,10 @@ static int brcmf_usb_suspend(struct usb_interface *intf, pm_message_t state) brcmf_dbg(USB, "Enter\n"); devinfo->bus_pub.state = BRCMFMAC_USB_STATE_SLEEP; - brcmf_detach(&usb->dev); + if (devinfo->wowl_enabled) + brcmf_cancel_all_urbs(devinfo); + else + brcmf_detach(&usb->dev); return 0; } @@ -1352,7 +1376,12 @@ static int brcmf_usb_resume(struct usb_interface *intf) struct brcmf_usbdev_info *devinfo = brcmf_usb_get_businfo(&usb->dev); brcmf_dbg(USB, "Enter\n"); - return brcmf_usb_bus_setup(devinfo); + if (!devinfo->wowl_enabled) + return brcmf_usb_bus_setup(devinfo); + + devinfo->bus_pub.state = BRCMFMAC_USB_STATE_UP; + brcmf_usb_rx_fill_all(devinfo); + return 0; } static int brcmf_usb_reset_resume(struct usb_interface *intf) -- cgit v1.2.3 From 330b4e4be937bf0ef126e01323f2756645b92c06 Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Tue, 28 Oct 2014 14:56:05 +0100 Subject: brcmfmac: Add wowl support for SDIO devices. This patch adds wowl support for SDIO bus devices. This feature requires FW which has support for wowl built in. Reviewed-by: Arend Van Spriel Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c | 45 +++++++++++++++------- drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c | 1 + .../net/wireless/brcm80211/brcmfmac/sdio_host.h | 2 + 3 files changed, 34 insertions(+), 14 deletions(-) (limited to 'drivers/net/wireless/brcm80211') diff --git a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c index 8dbd5dbb78fd..79b2c7e3f7e7 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c @@ -1064,6 +1064,16 @@ static int brcmf_ops_sdio_probe(struct sdio_func *func, if (!sdiodev->pdata) brcmf_of_probe(sdiodev); +#ifdef CONFIG_PM_SLEEP + /* wowl can be supported when KEEP_POWER is true and (WAKE_SDIO_IRQ + * is true or when platform data OOB irq is true). + */ + if ((sdio_get_host_pm_caps(sdiodev->func[1]) & MMC_PM_KEEP_POWER) && + ((sdio_get_host_pm_caps(sdiodev->func[1]) & MMC_PM_WAKE_SDIO_IRQ) || + (sdiodev->pdata->oob_irq_supported))) + bus_if->wowl_supported = true; +#endif + atomic_set(&sdiodev->suspend, false); init_waitqueue_head(&sdiodev->request_word_wait); init_waitqueue_head(&sdiodev->request_buffer_wait); @@ -1116,34 +1126,39 @@ static void brcmf_ops_sdio_remove(struct sdio_func *func) brcmf_dbg(SDIO, "Exit\n"); } +void brcmf_sdio_wowl_config(struct device *dev, bool enabled) +{ + struct brcmf_bus *bus_if = dev_get_drvdata(dev); + struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio; + + brcmf_dbg(SDIO, "Configuring WOWL, enabled=%d\n", enabled); + sdiodev->wowl_enabled = enabled; +} + #ifdef CONFIG_PM_SLEEP static int brcmf_ops_sdio_suspend(struct device *dev) { - mmc_pm_flag_t sdio_flags; struct brcmf_bus *bus_if = dev_get_drvdata(dev); struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio; - int ret = 0; + mmc_pm_flag_t sdio_flags; brcmf_dbg(SDIO, "Enter\n"); - sdio_flags = sdio_get_host_pm_caps(sdiodev->func[1]); - if (!(sdio_flags & MMC_PM_KEEP_POWER)) { - brcmf_err("Host can't keep power while suspended\n"); - return -EINVAL; - } - atomic_set(&sdiodev->suspend, true); - ret = sdio_set_host_pm_flags(sdiodev->func[1], MMC_PM_KEEP_POWER); - if (ret) { - brcmf_err("Failed to set pm_flags\n"); - atomic_set(&sdiodev->suspend, false); - return ret; + 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); } brcmf_sdio_wd_timer(sdiodev->bus, 0); - return ret; + return 0; } static int brcmf_ops_sdio_resume(struct device *dev) @@ -1152,6 +1167,8 @@ static int brcmf_ops_sdio_resume(struct device *dev) struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio; brcmf_dbg(SDIO, "Enter\n"); + if (sdiodev->pdata->oob_irq_supported) + disable_irq_wake(sdiodev->pdata->oob_irq_nr); brcmf_sdio_wd_timer(sdiodev->bus, BRCMF_WD_POLL_MS); atomic_set(&sdiodev->suspend, false); return 0; diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c index f55f625fd06b..079187c51170 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c @@ -3949,6 +3949,7 @@ 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 }; static void brcmf_sdio_firmware_callback(struct device *dev, diff --git a/drivers/net/wireless/brcm80211/brcmfmac/sdio_host.h b/drivers/net/wireless/brcm80211/brcmfmac/sdio_host.h index f2d06cae366a..262aedfeaa30 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/sdio_host.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/sdio_host.h @@ -186,6 +186,7 @@ struct brcmf_sdio_dev { struct sg_table sgtable; char fw_name[BRCMF_FW_PATH_LEN + BRCMF_FW_NAME_LEN]; char nvram_name[BRCMF_FW_PATH_LEN + BRCMF_FW_NAME_LEN]; + bool wowl_enabled; }; /* sdio core registers */ @@ -334,5 +335,6 @@ void brcmf_sdio_remove(struct brcmf_sdio *bus); void brcmf_sdio_isr(struct brcmf_sdio *bus); void brcmf_sdio_wd_timer(struct brcmf_sdio *bus, uint wdtick); +void brcmf_sdio_wowl_config(struct device *dev, bool enabled); #endif /* _BRCM_SDH_H_ */ -- cgit v1.2.3 From b9a82f892e2a143788d6a4e9fa121837ae1c9a8d Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Tue, 28 Oct 2014 14:56:06 +0100 Subject: brcmfmac: Add wowl patterns support. Reviewed-by: Arend Van Spriel Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Daniel (Deognyoun) Kim Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- .../net/wireless/brcm80211/brcmfmac/fwil_types.h | 89 +++++++++++++++------- .../net/wireless/brcm80211/brcmfmac/wl_cfg80211.c | 66 ++++++++++++++-- 2 files changed, 120 insertions(+), 35 deletions(-) (limited to 'drivers/net/wireless/brcm80211') diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fwil_types.h b/drivers/net/wireless/brcm80211/brcmfmac/fwil_types.h index 5ff5cd0bb032..ba64b292f7a5 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/fwil_types.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/fwil_types.h @@ -55,59 +55,63 @@ /* WOWL bits */ /* Wakeup on Magic packet: */ -#define WL_WOWL_MAGIC (1 << 0) +#define BRCMF_WOWL_MAGIC (1 << 0) /* Wakeup on Netpattern */ -#define WL_WOWL_NET (1 << 1) +#define BRCMF_WOWL_NET (1 << 1) /* Wakeup on loss-of-link due to Disassoc/Deauth: */ -#define WL_WOWL_DIS (1 << 2) +#define BRCMF_WOWL_DIS (1 << 2) /* Wakeup on retrograde TSF: */ -#define WL_WOWL_RETR (1 << 3) +#define BRCMF_WOWL_RETR (1 << 3) /* Wakeup on loss of beacon: */ -#define WL_WOWL_BCN (1 << 4) +#define BRCMF_WOWL_BCN (1 << 4) /* Wakeup after test: */ -#define WL_WOWL_TST (1 << 5) +#define BRCMF_WOWL_TST (1 << 5) /* Wakeup after PTK refresh: */ -#define WL_WOWL_M1 (1 << 6) +#define BRCMF_WOWL_M1 (1 << 6) /* Wakeup after receipt of EAP-Identity Req: */ -#define WL_WOWL_EAPID (1 << 7) +#define BRCMF_WOWL_EAPID (1 << 7) /* Wakeind via PME(0) or GPIO(1): */ -#define WL_WOWL_PME_GPIO (1 << 8) +#define BRCMF_WOWL_PME_GPIO (1 << 8) /* need tkip phase 1 key to be updated by the driver: */ -#define WL_WOWL_NEEDTKIP1 (1 << 9) +#define BRCMF_WOWL_NEEDTKIP1 (1 << 9) /* enable wakeup if GTK fails: */ -#define WL_WOWL_GTK_FAILURE (1 << 10) +#define BRCMF_WOWL_GTK_FAILURE (1 << 10) /* support extended magic packets: */ -#define WL_WOWL_EXTMAGPAT (1 << 11) +#define BRCMF_WOWL_EXTMAGPAT (1 << 11) /* support ARP/NS/keepalive offloading: */ -#define WL_WOWL_ARPOFFLOAD (1 << 12) +#define BRCMF_WOWL_ARPOFFLOAD (1 << 12) /* read protocol version for EAPOL frames: */ -#define WL_WOWL_WPA2 (1 << 13) +#define BRCMF_WOWL_WPA2 (1 << 13) /* If the bit is set, use key rotaton: */ -#define WL_WOWL_KEYROT (1 << 14) +#define BRCMF_WOWL_KEYROT (1 << 14) /* If the bit is set, frm received was bcast frame: */ -#define WL_WOWL_BCAST (1 << 15) +#define BRCMF_WOWL_BCAST (1 << 15) /* If the bit is set, scan offload is enabled: */ -#define WL_WOWL_SCANOL (1 << 16) +#define BRCMF_WOWL_SCANOL (1 << 16) /* Wakeup on tcpkeep alive timeout: */ -#define WL_WOWL_TCPKEEP_TIME (1 << 17) +#define BRCMF_WOWL_TCPKEEP_TIME (1 << 17) /* Wakeup on mDNS Conflict Resolution: */ -#define WL_WOWL_MDNS_CONFLICT (1 << 18) +#define BRCMF_WOWL_MDNS_CONFLICT (1 << 18) /* Wakeup on mDNS Service Connect: */ -#define WL_WOWL_MDNS_SERVICE (1 << 19) +#define BRCMF_WOWL_MDNS_SERVICE (1 << 19) /* tcp keepalive got data: */ -#define WL_WOWL_TCPKEEP_DATA (1 << 20) +#define BRCMF_WOWL_TCPKEEP_DATA (1 << 20) /* Firmware died in wowl mode: */ -#define WL_WOWL_FW_HALT (1 << 21) +#define BRCMF_WOWL_FW_HALT (1 << 21) /* Enable detection of radio button changes: */ -#define WL_WOWL_ENAB_HWRADIO (1 << 22) +#define BRCMF_WOWL_ENAB_HWRADIO (1 << 22) /* Offloads detected MIC failure(s): */ -#define WL_WOWL_MIC_FAIL (1 << 23) +#define BRCMF_WOWL_MIC_FAIL (1 << 23) /* Wakeup in Unassociated state (Net/Magic Pattern): */ -#define WL_WOWL_UNASSOC (1 << 24) +#define BRCMF_WOWL_UNASSOC (1 << 24) /* Wakeup if received matched secured pattern: */ -#define WL_WOWL_SECURE (1 << 25) +#define BRCMF_WOWL_SECURE (1 << 25) /* Link Down indication in WoWL mode: */ -#define WL_WOWL_LINKDOWN (1 << 31) +#define BRCMF_WOWL_LINKDOWN (1 << 31) + +#define BRCMF_WOWL_MAXPATTERNS 8 +#define BRCMF_WOWL_MAXPATTERNSIZE 128 + /* join preference types for join_pref iovar */ enum brcmf_join_pref_types { @@ -124,6 +128,12 @@ enum brcmf_fil_p2p_if_types { BRCMF_FIL_P2P_IF_DEV, }; +enum brcmf_wowl_pattern_type { + BRCMF_WOWL_PATTERN_TYPE_BITMAP = 0, + BRCMF_WOWL_PATTERN_TYPE_ARP, + BRCMF_WOWL_PATTERN_TYPE_NA +}; + struct brcmf_fil_p2p_if_le { u8 addr[ETH_ALEN]; __le16 type; @@ -484,4 +494,29 @@ struct brcmf_rx_mgmt_data { __be32 rate; }; +/** + * struct brcmf_fil_wowl_pattern_le - wowl pattern configuration struct. + * + * @cmd: "add", "del" or "clr". + * @masksize: Size of the mask in #of bytes + * @offset: Pattern byte offset in packet + * @patternoffset: Offset of start of pattern. Starting from field masksize. + * @patternsize: Size of the pattern itself in #of bytes + * @id: id + * @reasonsize: Size of the wakeup reason code + * @type: Type of pattern (enum brcmf_wowl_pattern_type) + */ +struct brcmf_fil_wowl_pattern_le { + u8 cmd[4]; + __le32 masksize; + __le32 offset; + __le32 patternoffset; + __le32 patternsize; + __le32 id; + __le32 reasonsize; + __le32 type; + /* u8 mask[] - Mask follows the structure above */ + /* u8 pattern[] - Pattern follows the mask is at 'patternoffset' */ +}; + #endif /* FWIL_TYPES_H_ */ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c index 28fa25b509db..fcadbe3c38d6 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c @@ -2779,6 +2779,44 @@ static __always_inline void brcmf_delay(u32 ms) } } +static s32 brcmf_config_wowl_pattern(struct brcmf_if *ifp, u8 cmd[4], + u8 *pattern, u32 patternsize, u8 *mask, + u32 packet_offset) +{ + struct brcmf_fil_wowl_pattern_le *filter; + u32 masksize; + u32 patternoffset; + u8 *buf; + u32 bufsize; + s32 ret; + + masksize = (patternsize + 7) / 8; + patternoffset = sizeof(*filter) - sizeof(filter->cmd) + masksize; + + bufsize = sizeof(*filter) + patternsize + masksize; + buf = kzalloc(bufsize, GFP_KERNEL); + if (!buf) + return -ENOMEM; + filter = (struct brcmf_fil_wowl_pattern_le *)buf; + + memcpy(filter->cmd, cmd, 4); + filter->masksize = cpu_to_le32(masksize); + filter->offset = cpu_to_le32(packet_offset); + filter->patternoffset = cpu_to_le32(patternoffset); + filter->patternsize = cpu_to_le32(patternsize); + filter->type = cpu_to_le32(BRCMF_WOWL_PATTERN_TYPE_BITMAP); + + if ((mask) && (masksize)) + memcpy(buf + sizeof(*filter), mask, masksize); + if ((pattern) && (patternsize)) + memcpy(buf + sizeof(*filter) + masksize, pattern, patternsize); + + ret = brcmf_fil_iovar_data_set(ifp, "wowl_pattern", buf, bufsize); + + kfree(buf); + return ret; +} + static s32 brcmf_cfg80211_resume(struct wiphy *wiphy) { struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); @@ -2788,10 +2826,11 @@ static s32 brcmf_cfg80211_resume(struct wiphy *wiphy) brcmf_dbg(TRACE, "Enter\n"); if (cfg->wowl_enabled) { + brcmf_configure_arp_offload(ifp, true); brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM, cfg->pre_wowl_pmmode); - brcmf_fil_iovar_data_set(ifp, "wowl_pattern", "clr", 4); brcmf_fil_iovar_int_set(ifp, "wowl_clear", 0); + brcmf_config_wowl_pattern(ifp, "clr", NULL, 0, NULL, 0); cfg->wowl_enabled = false; } return 0; @@ -2802,21 +2841,29 @@ static void brcmf_configure_wowl(struct brcmf_cfg80211_info *cfg, struct cfg80211_wowlan *wowl) { u32 wowl_config; + u32 i; brcmf_dbg(TRACE, "Suspend, wowl config.\n"); + brcmf_configure_arp_offload(ifp, false); brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_PM, &cfg->pre_wowl_pmmode); brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM, PM_MAX); wowl_config = 0; if (wowl->disconnect) - wowl_config |= WL_WOWL_DIS | WL_WOWL_BCN | WL_WOWL_RETR; - /* Note: if "wowl" target and not "wowlpf" then wowl_bcn_loss - * should be configured. This paramater is not supported by - * wowlpf. - */ + wowl_config = BRCMF_WOWL_DIS | BRCMF_WOWL_BCN | BRCMF_WOWL_RETR; if (wowl->magic_pkt) - wowl_config |= WL_WOWL_MAGIC; + wowl_config |= BRCMF_WOWL_MAGIC; + if ((wowl->patterns) && (wowl->n_patterns)) { + wowl_config |= BRCMF_WOWL_NET; + for (i = 0; i < wowl->n_patterns; i++) { + brcmf_config_wowl_pattern(ifp, "add", + (u8 *)wowl->patterns[i].pattern, + wowl->patterns[i].pattern_len, + (u8 *)wowl->patterns[i].mask, + wowl->patterns[i].pkt_offset); + } + } brcmf_fil_iovar_int_set(ifp, "wowl", wowl_config); brcmf_fil_iovar_int_set(ifp, "wowl_activate", 1); brcmf_bus_wowl_config(cfg->pub->bus_if, true); @@ -5440,10 +5487,13 @@ static void brcmf_wiphy_pno_params(struct wiphy *wiphy) wiphy->flags |= WIPHY_FLAG_SUPPORTS_SCHED_SCAN; } - #ifdef CONFIG_PM static const struct wiphy_wowlan_support brcmf_wowlan_support = { .flags = WIPHY_WOWLAN_MAGIC_PKT | WIPHY_WOWLAN_DISCONNECT, + .n_patterns = BRCMF_WOWL_MAXPATTERNS, + .pattern_max_len = BRCMF_WOWL_MAXPATTERNSIZE, + .pattern_min_len = 1, + .max_pkt_offset = 1500, }; #endif -- cgit v1.2.3 From a9a2808f05322fe152672b7e7d09759446947da8 Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Tue, 28 Oct 2014 14:56:07 +0100 Subject: brcmfmac: show firmware error as string in debug message Showing the firmware error allows to quickly give a clue what went wrong and directly look in the firmware code that gave us back the error. Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Hante Meuleman Reviewed-by: Daniel (Deognyoun) Kim Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/fwil.c | 73 +++++++++++++++++++++++++- 1 file changed, 72 insertions(+), 1 deletion(-) (limited to 'drivers/net/wireless/brcm80211') diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fwil.c b/drivers/net/wireless/brcm80211/brcmfmac/fwil.c index ded328f80cd1..42da73e26142 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/fwil.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/fwil.c @@ -32,6 +32,76 @@ #define MAX_HEX_DUMP_LEN 64 +#ifdef DEBUG +static const char * const brcmf_fil_errstr[] = { + "BCME_OK", + "BCME_ERROR", + "BCME_BADARG", + "BCME_BADOPTION", + "BCME_NOTUP", + "BCME_NOTDOWN", + "BCME_NOTAP", + "BCME_NOTSTA", + "BCME_BADKEYIDX", + "BCME_RADIOOFF", + "BCME_NOTBANDLOCKED", + "BCME_NOCLK", + "BCME_BADRATESET", + "BCME_BADBAND", + "BCME_BUFTOOSHORT", + "BCME_BUFTOOLONG", + "BCME_BUSY", + "BCME_NOTASSOCIATED", + "BCME_BADSSIDLEN", + "BCME_OUTOFRANGECHAN", + "BCME_BADCHAN", + "BCME_BADADDR", + "BCME_NORESOURCE", + "BCME_UNSUPPORTED", + "BCME_BADLEN", + "BCME_NOTREADY", + "BCME_EPERM", + "BCME_NOMEM", + "BCME_ASSOCIATED", + "BCME_RANGE", + "BCME_NOTFOUND", + "BCME_WME_NOT_ENABLED", + "BCME_TSPEC_NOTFOUND", + "BCME_ACM_NOTSUPPORTED", + "BCME_NOT_WME_ASSOCIATION", + "BCME_SDIO_ERROR", + "BCME_DONGLE_DOWN", + "BCME_VERSION", + "BCME_TXFAIL", + "BCME_RXFAIL", + "BCME_NODEVICE", + "BCME_NMODE_DISABLED", + "BCME_NONRESIDENT", + "BCME_SCANREJECT", + "BCME_USAGE_ERROR", + "BCME_IOCTL_ERROR", + "BCME_SERIAL_PORT_ERR", + "BCME_DISABLED", + "BCME_DECERR", + "BCME_ENCERR", + "BCME_MICERR", + "BCME_REPLAY", + "BCME_IE_NOTFOUND", +}; + +static const char *brcmf_fil_get_errstr(u32 err) +{ + if (err >= ARRAY_SIZE(brcmf_fil_errstr)) + return "(unknown)"; + + return brcmf_fil_errstr[err]; +} +#else +static const char *brcmf_fil_get_errstr(u32 err) +{ + return ""; +} +#endif /* DEBUG */ static s32 brcmf_fil_cmd_data(struct brcmf_if *ifp, u32 cmd, void *data, u32 len, bool set) @@ -54,7 +124,8 @@ brcmf_fil_cmd_data(struct brcmf_if *ifp, u32 cmd, void *data, u32 len, bool set) if (err >= 0) err = 0; else - brcmf_dbg(FIL, "Failed err=%d\n", err); + brcmf_dbg(FIL, "Failed: %s (%d)\n", + brcmf_fil_get_errstr((u32)(-err)), err); return err; } -- cgit v1.2.3 From a3e53bbfd91c155d06bb168ad7a830cdf21b500c Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Tue, 28 Oct 2014 14:56:08 +0100 Subject: brcmfmac: remove unused defintion The define EBRCMF_UNSUPPORTED is not used in the source file so this patch removes it. Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Hante Meuleman Reviewed-by: Daniel (Deognyoun) Kim Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/feature.c | 5 ----- 1 file changed, 5 deletions(-) (limited to 'drivers/net/wireless/brcm80211') diff --git a/drivers/net/wireless/brcm80211/brcmfmac/feature.c b/drivers/net/wireless/brcm80211/brcmfmac/feature.c index aed53acef456..4eb6a41e72c7 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/feature.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/feature.c @@ -23,11 +23,6 @@ #include "fwil.h" #include "feature.h" -/* - * firmware error code received if iovar is unsupported. - */ -#define EBRCMF_FEAT_UNSUPPORTED 23 - /* * expand feature list to array of feature strings. */ -- cgit v1.2.3 From 9c6476668025e76a8365be783f2649fe3259b91c Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Tue, 28 Oct 2014 14:56:09 +0100 Subject: brcmfmac: do not use firmware error code in driver Passing the firmware error codes up the driver may be mapped to linux error numbers which may impact proper fault analysis. So better pass up a generic failure code, ie. -EBADE and only show firmware error code in FIL debug message. Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Hante Meuleman Reviewed-by: Daniel (Deognyoun) Kim Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/fwil.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) (limited to 'drivers/net/wireless/brcm80211') diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fwil.c b/drivers/net/wireless/brcm80211/brcmfmac/fwil.c index 42da73e26142..dccbdc1aae6d 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/fwil.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/fwil.c @@ -122,12 +122,11 @@ brcmf_fil_cmd_data(struct brcmf_if *ifp, u32 cmd, void *data, u32 len, bool set) err = brcmf_proto_query_dcmd(drvr, ifp->ifidx, cmd, data, len); if (err >= 0) - err = 0; - else - brcmf_dbg(FIL, "Failed: %s (%d)\n", - brcmf_fil_get_errstr((u32)(-err)), err); + return 0; - return err; + brcmf_dbg(FIL, "Failed: %s (%d)\n", + brcmf_fil_get_errstr((u32)(-err)), err); + return -EBADE; } s32 -- cgit v1.2.3 From ac83d0b0aae152385ae998c65fc03ea790d8c025 Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Tue, 28 Oct 2014 14:56:10 +0100 Subject: brcmfmac: (clean) Remove usb_rdl.h as it is not needed. Reviewed-by: Arend Van Spriel Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Daniel (Deognyoun) Kim Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/usb.c | 88 ++++++++++++++++++----- drivers/net/wireless/brcm80211/brcmfmac/usb_rdl.h | 75 ------------------- 2 files changed, 69 insertions(+), 94 deletions(-) delete mode 100644 drivers/net/wireless/brcm80211/brcmfmac/usb_rdl.h (limited to 'drivers/net/wireless/brcm80211') diff --git a/drivers/net/wireless/brcm80211/brcmfmac/usb.c b/drivers/net/wireless/brcm80211/brcmfmac/usb.c index e533000787f3..3f12b606839e 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/usb.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/usb.c @@ -27,9 +27,9 @@ #include #include "firmware.h" -#include "usb_rdl.h" #include "usb.h" + #define IOCTL_RESP_TIMEOUT 2000 #define BRCMF_USB_RESET_GETVER_SPINWAIT 100 /* in unit of ms */ @@ -49,6 +49,71 @@ #define BRCMF_USB_43242_FW_NAME "brcm/brcmfmac43242a.bin" #define BRCMF_USB_43569_FW_NAME "brcm/brcmfmac43569.bin" +#define TRX_MAGIC 0x30524448 /* "HDR0" */ +#define TRX_MAX_OFFSET 3 /* Max number of file offsets */ +#define TRX_UNCOMP_IMAGE 0x20 /* Trx holds uncompressed img */ +#define TRX_RDL_CHUNK 1500 /* size of each dl transfer */ +#define TRX_OFFSETS_DLFWLEN_IDX 0 + +/* Control messages: bRequest values */ +#define DL_GETSTATE 0 /* returns the rdl_state_t struct */ +#define DL_CHECK_CRC 1 /* currently unused */ +#define DL_GO 2 /* execute downloaded image */ +#define DL_START 3 /* initialize dl state */ +#define DL_REBOOT 4 /* reboot the device in 2 seconds */ +#define DL_GETVER 5 /* returns the bootrom_id_t struct */ +#define DL_GO_PROTECTED 6 /* execute the downloaded code and set reset + * event to occur in 2 seconds. It is the + * responsibility of the downloaded code to + * clear this event + */ +#define DL_EXEC 7 /* jump to a supplied address */ +#define DL_RESETCFG 8 /* To support single enum on dongle + * - Not used by bootloader + */ +#define DL_DEFER_RESP_OK 9 /* Potentially defer the response to setup + * if resp unavailable + */ + +/* states */ +#define DL_WAITING 0 /* waiting to rx first pkt */ +#define DL_READY 1 /* hdr was good, waiting for more of the + * compressed image + */ +#define DL_BAD_HDR 2 /* hdr was corrupted */ +#define DL_BAD_CRC 3 /* compressed image was corrupted */ +#define DL_RUNNABLE 4 /* download was successful,waiting for go cmd */ +#define DL_START_FAIL 5 /* failed to initialize correctly */ +#define DL_NVRAM_TOOBIG 6 /* host specified nvram data exceeds DL_NVRAM + * value + */ +#define DL_IMAGE_TOOBIG 7 /* firmware image too big */ + + +struct trx_header_le { + __le32 magic; /* "HDR0" */ + __le32 len; /* Length of file including header */ + __le32 crc32; /* CRC from flag_version to end of file */ + __le32 flag_version; /* 0:15 flags, 16:31 version */ + __le32 offsets[TRX_MAX_OFFSET]; /* Offsets of partitions from start of + * header + */ +}; + +struct rdl_state_le { + __le32 state; + __le32 bytes; +}; + +struct bootrom_id_le { + __le32 chip; /* Chip id */ + __le32 chiprev; /* Chip rev */ + __le32 ramsize; /* Size of RAM */ + __le32 remapbase; /* Current remap base address */ + __le32 boardtype; /* Type of board */ + __le32 boardrev; /* Board revision */ +}; + struct brcmf_usb_image { struct list_head list; s8 *fwname; @@ -788,7 +853,7 @@ brcmf_usb_dl_writeimage(struct brcmf_usbdev_info *devinfo, u8 *fw, int fwlen) brcmf_dbg(USB, "Enter, fw %p, len %d\n", fw, fwlen); - bulkchunk = kmalloc(RDL_CHUNK, GFP_ATOMIC); + bulkchunk = kmalloc(TRX_RDL_CHUNK, GFP_ATOMIC); if (bulkchunk == NULL) { err = -ENOMEM; goto fail; @@ -815,10 +880,10 @@ brcmf_usb_dl_writeimage(struct brcmf_usbdev_info *devinfo, u8 *fw, int fwlen) /* Wait until the usb device reports it received all * the bytes we sent */ if ((rdlbytes == sent) && (rdlbytes != dllen)) { - if ((dllen-sent) < RDL_CHUNK) + if ((dllen-sent) < TRX_RDL_CHUNK) sendlen = dllen-sent; else - sendlen = RDL_CHUNK; + sendlen = TRX_RDL_CHUNK; /* simply avoid having to send a ZLP by ensuring we * never have an even @@ -983,21 +1048,6 @@ static void brcmf_usb_detach(struct brcmf_usbdev_info *devinfo) kfree(devinfo->rx_reqs); } -#define TRX_MAGIC 0x30524448 /* "HDR0" */ -#define TRX_VERSION 1 /* Version 1 */ -#define TRX_MAX_LEN 0x3B0000 /* Max length */ -#define TRX_NO_HEADER 1 /* Do not write TRX header */ -#define TRX_MAX_OFFSET 3 /* Max number of individual files */ -#define TRX_UNCOMP_IMAGE 0x20 /* Trx contains uncompressed image */ - -struct trx_header_le { - __le32 magic; /* "HDR0" */ - __le32 len; /* Length of file including header */ - __le32 crc32; /* CRC from flag_version to end of file */ - __le32 flag_version; /* 0:15 flags, 16:31 version */ - __le32 offsets[TRX_MAX_OFFSET]; /* Offsets of partitions from start of - * header */ -}; static int check_file(const u8 *headers) { diff --git a/drivers/net/wireless/brcm80211/brcmfmac/usb_rdl.h b/drivers/net/wireless/brcm80211/brcmfmac/usb_rdl.h deleted file mode 100644 index 0a35c51c3da2..000000000000 --- a/drivers/net/wireless/brcm80211/brcmfmac/usb_rdl.h +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright (c) 2011 Broadcom Corporation - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY - * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION - * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN - * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef _USB_RDL_H -#define _USB_RDL_H - -/* Control messages: bRequest values */ -#define DL_GETSTATE 0 /* returns the rdl_state_t struct */ -#define DL_CHECK_CRC 1 /* currently unused */ -#define DL_GO 2 /* execute downloaded image */ -#define DL_START 3 /* initialize dl state */ -#define DL_REBOOT 4 /* reboot the device in 2 seconds */ -#define DL_GETVER 5 /* returns the bootrom_id_t struct */ -#define DL_GO_PROTECTED 6 /* execute the downloaded code and set reset - * event to occur in 2 seconds. It is the - * responsibility of the downloaded code to - * clear this event - */ -#define DL_EXEC 7 /* jump to a supplied address */ -#define DL_RESETCFG 8 /* To support single enum on dongle - * - Not used by bootloader - */ -#define DL_DEFER_RESP_OK 9 /* Potentially defer the response to setup - * if resp unavailable - */ - -/* states */ -#define DL_WAITING 0 /* waiting to rx first pkt */ -#define DL_READY 1 /* hdr was good, waiting for more of the - * compressed image */ -#define DL_BAD_HDR 2 /* hdr was corrupted */ -#define DL_BAD_CRC 3 /* compressed image was corrupted */ -#define DL_RUNNABLE 4 /* download was successful,waiting for go cmd */ -#define DL_START_FAIL 5 /* failed to initialize correctly */ -#define DL_NVRAM_TOOBIG 6 /* host specified nvram data exceeds DL_NVRAM - * value */ -#define DL_IMAGE_TOOBIG 7 /* download image too big (exceeds DATA_START - * for rdl) */ - -struct rdl_state_le { - __le32 state; - __le32 bytes; -}; - -struct bootrom_id_le { - __le32 chip; /* Chip id */ - __le32 chiprev; /* Chip rev */ - __le32 ramsize; /* Size of RAM */ - __le32 remapbase; /* Current remap base address */ - __le32 boardtype; /* Type of board */ - __le32 boardrev; /* Board revision */ -}; - -#define RDL_CHUNK 1500 /* size of each dl transfer */ - -#define TRX_OFFSETS_DLFWLEN_IDX 0 -#define TRX_OFFSETS_JUMPTO_IDX 1 -#define TRX_OFFSETS_NVM_LEN_IDX 2 - -#define TRX_OFFSETS_DLBASE_IDX 0 - -#endif /* _USB_RDL_H */ -- cgit v1.2.3 From d3c80372e00c792cbde42fa1abda3b8b08575d59 Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Tue, 28 Oct 2014 14:56:11 +0100 Subject: brcmfmac: (clean) Remove packet filter configuration. Packet filters got configured but never used. Reviewed-by: Franky Lin Reviewed-by: Arend Van Spriel Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- .../net/wireless/brcm80211/brcmfmac/dhd_common.c | 166 --------------------- 1 file changed, 166 deletions(-) (limited to 'drivers/net/wireless/brcm80211') diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_common.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_common.c index d991f8e3d9ec..6028fc466575 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_common.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_common.c @@ -26,11 +26,9 @@ #include "fwil_types.h" #include "tracepoint.h" -#define PKTFILTER_BUF_SIZE 128 #define BRCMF_DEFAULT_BCN_TIMEOUT 3 #define BRCMF_DEFAULT_SCAN_CHANNEL_TIME 40 #define BRCMF_DEFAULT_SCAN_UNASSOC_TIME 40 -#define BRCMF_DEFAULT_PACKET_FILTER "100 0 0 0 0x01 0x00" /* boost value for RSSI_DELTA in preferred join selection */ #define BRCMF_JOIN_PREF_RSSI_BOOST 8 @@ -86,165 +84,6 @@ bool brcmf_c_prec_enq(struct device *dev, struct pktq *q, return p != NULL; } -/* Convert user's input in hex pattern to byte-size mask */ -static int brcmf_c_pattern_atoh(char *src, char *dst) -{ - int i; - if (strncmp(src, "0x", 2) != 0 && strncmp(src, "0X", 2) != 0) { - brcmf_err("Mask invalid format. Needs to start with 0x\n"); - return -EINVAL; - } - src = src + 2; /* Skip past 0x */ - if (strlen(src) % 2 != 0) { - brcmf_err("Mask invalid format. Length must be even.\n"); - return -EINVAL; - } - for (i = 0; *src != '\0'; i++) { - unsigned long res; - char num[3]; - strncpy(num, src, 2); - num[2] = '\0'; - if (kstrtoul(num, 16, &res)) - return -EINVAL; - dst[i] = (u8)res; - src += 2; - } - return i; -} - -static void -brcmf_c_pktfilter_offload_enable(struct brcmf_if *ifp, char *arg, int enable, - int master_mode) -{ - unsigned long res; - char *argv; - char *arg_save = NULL, *arg_org = NULL; - s32 err; - struct brcmf_pkt_filter_enable_le enable_parm; - - arg_save = kstrdup(arg, GFP_ATOMIC); - if (!arg_save) - goto fail; - - arg_org = arg_save; - - argv = strsep(&arg_save, " "); - - if (argv == NULL) { - brcmf_err("No args provided\n"); - goto fail; - } - - /* Parse packet filter id. */ - enable_parm.id = 0; - if (!kstrtoul(argv, 0, &res)) - enable_parm.id = cpu_to_le32((u32)res); - - /* Enable/disable the specified filter. */ - enable_parm.enable = cpu_to_le32(enable); - - err = brcmf_fil_iovar_data_set(ifp, "pkt_filter_enable", &enable_parm, - sizeof(enable_parm)); - if (err) - brcmf_err("Set pkt_filter_enable error (%d)\n", err); - - /* Control the master mode */ - err = brcmf_fil_iovar_int_set(ifp, "pkt_filter_mode", master_mode); - if (err) - brcmf_err("Set pkt_filter_mode error (%d)\n", err); - -fail: - kfree(arg_org); -} - -static void brcmf_c_pktfilter_offload_set(struct brcmf_if *ifp, char *arg) -{ - struct brcmf_pkt_filter_le *pkt_filter; - unsigned long res; - int buf_len; - s32 err; - u32 mask_size; - u32 pattern_size; - char *argv[8], *buf = NULL; - int i = 0; - char *arg_save = NULL, *arg_org = NULL; - - arg_save = kstrdup(arg, GFP_ATOMIC); - if (!arg_save) - goto fail; - - arg_org = arg_save; - - buf = kmalloc(PKTFILTER_BUF_SIZE, GFP_ATOMIC); - if (!buf) - goto fail; - - argv[i] = strsep(&arg_save, " "); - while (argv[i]) { - i++; - if (i >= 8) { - brcmf_err("Too many parameters\n"); - goto fail; - } - argv[i] = strsep(&arg_save, " "); - } - - if (i != 6) { - brcmf_err("Not enough args provided %d\n", i); - goto fail; - } - - pkt_filter = (struct brcmf_pkt_filter_le *)buf; - - /* Parse packet filter id. */ - pkt_filter->id = 0; - if (!kstrtoul(argv[0], 0, &res)) - pkt_filter->id = cpu_to_le32((u32)res); - - /* Parse filter polarity. */ - pkt_filter->negate_match = 0; - if (!kstrtoul(argv[1], 0, &res)) - pkt_filter->negate_match = cpu_to_le32((u32)res); - - /* Parse filter type. */ - pkt_filter->type = 0; - if (!kstrtoul(argv[2], 0, &res)) - pkt_filter->type = cpu_to_le32((u32)res); - - /* Parse pattern filter offset. */ - pkt_filter->u.pattern.offset = 0; - if (!kstrtoul(argv[3], 0, &res)) - pkt_filter->u.pattern.offset = cpu_to_le32((u32)res); - - /* Parse pattern filter mask. */ - mask_size = brcmf_c_pattern_atoh(argv[4], - (char *)pkt_filter->u.pattern.mask_and_pattern); - - /* Parse pattern filter pattern. */ - pattern_size = brcmf_c_pattern_atoh(argv[5], - (char *)&pkt_filter->u.pattern.mask_and_pattern[mask_size]); - - if (mask_size != pattern_size) { - brcmf_err("Mask and pattern not the same size\n"); - goto fail; - } - - pkt_filter->u.pattern.size_bytes = cpu_to_le32(mask_size); - buf_len = offsetof(struct brcmf_pkt_filter_le, - u.pattern.mask_and_pattern); - buf_len += mask_size + pattern_size; - - err = brcmf_fil_iovar_data_set(ifp, "pkt_filter_add", pkt_filter, - buf_len); - if (err) - brcmf_err("Set pkt_filter_add error (%d)\n", err); - -fail: - kfree(arg_org); - - kfree(buf); -} - int brcmf_c_preinit_dcmds(struct brcmf_if *ifp) { s8 eventmask[BRCMF_EVENTING_MASK_LEN]; @@ -356,11 +195,6 @@ int brcmf_c_preinit_dcmds(struct brcmf_if *ifp) goto done; } - /* Setup packet filter */ - brcmf_c_pktfilter_offload_set(ifp, BRCMF_DEFAULT_PACKET_FILTER); - brcmf_c_pktfilter_offload_enable(ifp, BRCMF_DEFAULT_PACKET_FILTER, - 0, true); - /* do bus specific preinit here */ err = brcmf_bus_preinit(ifp->drvr->bus_if); done: -- cgit v1.2.3 From 4a1c61508b4830a3c36257a0995646d7b22b29a9 Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Tue, 28 Oct 2014 14:56:12 +0100 Subject: brcmfmac: (clean) Move tracepoint related function. __brcmf_err is a tracepoint specific function. Move it to tracepoint.c. Reviewed-by: Arend Van Spriel Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/dhd_common.c | 15 --------------- drivers/net/wireless/brcm80211/brcmfmac/tracepoint.c | 15 +++++++++++++++ 2 files changed, 15 insertions(+), 15 deletions(-) (limited to 'drivers/net/wireless/brcm80211') diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_common.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_common.c index 6028fc466575..98bc2226a119 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_common.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_common.c @@ -201,21 +201,6 @@ done: return err; } -#ifdef CONFIG_BRCM_TRACING -void __brcmf_err(const char *func, const char *fmt, ...) -{ - struct va_format vaf = { - .fmt = fmt, - }; - va_list args; - - va_start(args, fmt); - vaf.va = &args; - pr_err("%s: %pV", func, &vaf); - trace_brcmf_err(func, &vaf); - va_end(args); -} -#endif #if defined(CONFIG_BRCM_TRACING) || defined(CONFIG_BRCMDBG) void __brcmf_dbg(u32 level, const char *func, const char *fmt, ...) { diff --git a/drivers/net/wireless/brcm80211/brcmfmac/tracepoint.c b/drivers/net/wireless/brcm80211/brcmfmac/tracepoint.c index b505db48c60d..a10f35c5eb3d 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/tracepoint.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/tracepoint.c @@ -19,4 +19,19 @@ #ifndef __CHECKER__ #define CREATE_TRACE_POINTS #include "tracepoint.h" + +void __brcmf_err(const char *func, const char *fmt, ...) +{ + struct va_format vaf = { + .fmt = fmt, + }; + va_list args; + + va_start(args, fmt); + vaf.va = &args; + pr_err("%s: %pV", func, &vaf); + trace_brcmf_err(func, &vaf); + va_end(args); +} + #endif -- cgit v1.2.3 From a8e8ed3446a32a2323e70bc1cef05c5119d893e0 Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Tue, 28 Oct 2014 14:56:13 +0100 Subject: brcmfmac: (clean) Rename files dhd_dbg to debug Reviewed-by: Arend Van Spriel Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/Makefile | 2 +- drivers/net/wireless/brcm80211/brcmfmac/bcdc.c | 2 +- drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c | 2 +- drivers/net/wireless/brcm80211/brcmfmac/btcoex.c | 2 +- drivers/net/wireless/brcm80211/brcmfmac/chip.c | 2 +- drivers/net/wireless/brcm80211/brcmfmac/debug.c | 117 ++++++++++++++++++ drivers/net/wireless/brcm80211/brcmfmac/debug.h | 136 +++++++++++++++++++++ drivers/net/wireless/brcm80211/brcmfmac/dhd_bus.h | 2 +- .../net/wireless/brcm80211/brcmfmac/dhd_common.c | 2 +- drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.c | 117 ------------------ drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.h | 136 --------------------- .../net/wireless/brcm80211/brcmfmac/dhd_linux.c | 2 +- drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c | 2 +- drivers/net/wireless/brcm80211/brcmfmac/feature.c | 2 +- drivers/net/wireless/brcm80211/brcmfmac/firmware.c | 2 +- drivers/net/wireless/brcm80211/brcmfmac/flowring.c | 2 +- drivers/net/wireless/brcm80211/brcmfmac/fweh.c | 2 +- drivers/net/wireless/brcm80211/brcmfmac/fwil.c | 2 +- drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c | 2 +- drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c | 2 +- drivers/net/wireless/brcm80211/brcmfmac/of.c | 2 +- drivers/net/wireless/brcm80211/brcmfmac/p2p.c | 2 +- drivers/net/wireless/brcm80211/brcmfmac/pcie.c | 2 +- drivers/net/wireless/brcm80211/brcmfmac/proto.c | 2 +- drivers/net/wireless/brcm80211/brcmfmac/usb.c | 2 +- drivers/net/wireless/brcm80211/brcmfmac/vendor.c | 2 +- .../net/wireless/brcm80211/brcmfmac/wl_cfg80211.c | 2 +- 27 files changed, 276 insertions(+), 276 deletions(-) create mode 100644 drivers/net/wireless/brcm80211/brcmfmac/debug.c create mode 100644 drivers/net/wireless/brcm80211/brcmfmac/debug.h delete mode 100644 drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.c delete mode 100644 drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.h (limited to 'drivers/net/wireless/brcm80211') diff --git a/drivers/net/wireless/brcm80211/brcmfmac/Makefile b/drivers/net/wireless/brcm80211/brcmfmac/Makefile index 90a977fe9a64..1da9042c8aba 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/Makefile +++ b/drivers/net/wireless/brcm80211/brcmfmac/Makefile @@ -50,7 +50,7 @@ brcmfmac-$(CONFIG_BRCMFMAC_USB) += \ brcmfmac-$(CONFIG_BRCMFMAC_PCIE) += \ pcie.o brcmfmac-$(CONFIG_BRCMDBG) += \ - dhd_dbg.o + debug.o brcmfmac-$(CONFIG_BRCM_TRACING) += \ tracepoint.o brcmfmac-$(CONFIG_OF) += \ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/bcdc.c b/drivers/net/wireless/brcm80211/brcmfmac/bcdc.c index a159ff3427de..11d3dfae8bac 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/bcdc.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/bcdc.c @@ -28,7 +28,7 @@ #include "dhd.h" #include "dhd_bus.h" #include "fwsignal.h" -#include "dhd_dbg.h" +#include "debug.h" #include "tracepoint.h" #include "proto.h" #include "bcdc.h" diff --git a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c index 79b2c7e3f7e7..8c0dfea68eeb 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c @@ -42,7 +42,7 @@ #include #include "chip.h" #include "dhd_bus.h" -#include "dhd_dbg.h" +#include "debug.h" #include "sdio_host.h" #include "of.h" diff --git a/drivers/net/wireless/brcm80211/brcmfmac/btcoex.c b/drivers/net/wireless/brcm80211/brcmfmac/btcoex.c index a29ac4977b3a..a2f7e2ccfc84 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/btcoex.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/btcoex.c @@ -21,7 +21,7 @@ #include #include #include -#include +#include "debug.h" #include "fwil.h" #include "fwil_types.h" #include "btcoex.h" diff --git a/drivers/net/wireless/brcm80211/brcmfmac/chip.c b/drivers/net/wireless/brcm80211/brcmfmac/chip.c index 95efde868db8..ddae0b5e56ec 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/chip.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/chip.c @@ -25,7 +25,7 @@ #include #include #include -#include "dhd_dbg.h" +#include "debug.h" #include "chip.h" /* SOC Interconnect types (aka chip types) */ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/debug.c b/drivers/net/wireless/brcm80211/brcmfmac/debug.c new file mode 100644 index 000000000000..27e51085d33a --- /dev/null +++ b/drivers/net/wireless/brcm80211/brcmfmac/debug.c @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2012 Broadcom Corporation + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +#include +#include +#include + +#include +#include +#include "dhd.h" +#include "dhd_bus.h" +#include "debug.h" + +static struct dentry *root_folder; + +void brcmf_debugfs_init(void) +{ + root_folder = debugfs_create_dir(KBUILD_MODNAME, NULL); + if (IS_ERR(root_folder)) + root_folder = NULL; +} + +void brcmf_debugfs_exit(void) +{ + if (!root_folder) + return; + + debugfs_remove_recursive(root_folder); + root_folder = NULL; +} + +static int brcmf_debugfs_chipinfo_read(struct seq_file *seq, void *data) +{ + struct brcmf_bus *bus = dev_get_drvdata(seq->private); + + seq_printf(seq, "chip: %x(%u) rev %u\n", + bus->chip, bus->chip, bus->chiprev); + return 0; +} + +int brcmf_debugfs_attach(struct brcmf_pub *drvr) +{ + struct device *dev = drvr->bus_if->dev; + + if (!root_folder) + return -ENODEV; + + drvr->dbgfs_dir = debugfs_create_dir(dev_name(dev), root_folder); + brcmf_debugfs_add_entry(drvr, "chipinfo", brcmf_debugfs_chipinfo_read); + + return PTR_ERR_OR_ZERO(drvr->dbgfs_dir); +} + +void brcmf_debugfs_detach(struct brcmf_pub *drvr) +{ + if (!IS_ERR_OR_NULL(drvr->dbgfs_dir)) + debugfs_remove_recursive(drvr->dbgfs_dir); +} + +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); + + return PTR_ERR_OR_ZERO(dentry); +} diff --git a/drivers/net/wireless/brcm80211/brcmfmac/debug.h b/drivers/net/wireless/brcm80211/brcmfmac/debug.h new file mode 100644 index 000000000000..eb0b8c47479d --- /dev/null +++ b/drivers/net/wireless/brcm80211/brcmfmac/debug.h @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2010 Broadcom Corporation + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef BRCMFMAC_DEBUG_H +#define BRCMFMAC_DEBUG_H + +/* message levels */ +#define BRCMF_TRACE_VAL 0x00000002 +#define BRCMF_INFO_VAL 0x00000004 +#define BRCMF_DATA_VAL 0x00000008 +#define BRCMF_CTL_VAL 0x00000010 +#define BRCMF_TIMER_VAL 0x00000020 +#define BRCMF_HDRS_VAL 0x00000040 +#define BRCMF_BYTES_VAL 0x00000080 +#define BRCMF_INTR_VAL 0x00000100 +#define BRCMF_GLOM_VAL 0x00000200 +#define BRCMF_EVENT_VAL 0x00000400 +#define BRCMF_BTA_VAL 0x00000800 +#define BRCMF_FIL_VAL 0x00001000 +#define BRCMF_USB_VAL 0x00002000 +#define BRCMF_SCAN_VAL 0x00004000 +#define BRCMF_CONN_VAL 0x00008000 +#define BRCMF_BCDC_VAL 0x00010000 +#define BRCMF_SDIO_VAL 0x00020000 +#define BRCMF_MSGBUF_VAL 0x00040000 +#define BRCMF_PCIE_VAL 0x00080000 + +/* set default print format */ +#undef pr_fmt +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +/* Macro for error messages. net_ratelimit() is used when driver + * debugging is not selected. When debugging the driver error + * messages are as important as other tracing or even more so. + */ +#ifndef CONFIG_BRCM_TRACING +#ifdef CONFIG_BRCMDBG +#define brcmf_err(fmt, ...) pr_err("%s: " fmt, __func__, ##__VA_ARGS__) +#else +#define brcmf_err(fmt, ...) \ + do { \ + if (net_ratelimit()) \ + pr_err("%s: " fmt, __func__, ##__VA_ARGS__); \ + } while (0) +#endif +#else +__printf(2, 3) +void __brcmf_err(const char *func, const char *fmt, ...); +#define brcmf_err(fmt, ...) \ + __brcmf_err(__func__, fmt, ##__VA_ARGS__) +#endif + +#if defined(DEBUG) || defined(CONFIG_BRCM_TRACING) +__printf(3, 4) +void __brcmf_dbg(u32 level, const char *func, const char *fmt, ...); +#define brcmf_dbg(level, fmt, ...) \ +do { \ + __brcmf_dbg(BRCMF_##level##_VAL, __func__, \ + fmt, ##__VA_ARGS__); \ +} while (0) +#define BRCMF_DATA_ON() (brcmf_msg_level & BRCMF_DATA_VAL) +#define BRCMF_CTL_ON() (brcmf_msg_level & BRCMF_CTL_VAL) +#define BRCMF_HDRS_ON() (brcmf_msg_level & BRCMF_HDRS_VAL) +#define BRCMF_BYTES_ON() (brcmf_msg_level & BRCMF_BYTES_VAL) +#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) + +#else /* defined(DEBUG) || defined(CONFIG_BRCM_TRACING) */ + +#define brcmf_dbg(level, fmt, ...) no_printk(fmt, ##__VA_ARGS__) + +#define BRCMF_DATA_ON() 0 +#define BRCMF_CTL_ON() 0 +#define BRCMF_HDRS_ON() 0 +#define BRCMF_BYTES_ON() 0 +#define BRCMF_GLOM_ON() 0 +#define BRCMF_EVENT_ON() 0 +#define BRCMF_FIL_ON() 0 + +#endif /* defined(DEBUG) || defined(CONFIG_BRCM_TRACING) */ + +#define brcmf_dbg_hex_dump(test, data, len, fmt, ...) \ +do { \ + trace_brcmf_hexdump((void *)data, len); \ + if (test) \ + brcmu_dbg_hex_dump(data, len, fmt, ##__VA_ARGS__); \ +} while (0) + +extern int brcmf_msg_level; + +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); +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)); +#else +static inline void brcmf_debugfs_init(void) +{ +} +static inline void brcmf_debugfs_exit(void) +{ +} +static inline int brcmf_debugfs_attach(struct brcmf_pub *drvr) +{ + return 0; +} +static inline void brcmf_debugfs_detach(struct brcmf_pub *drvr) +{ +} +static inline +int brcmf_debugfs_add_entry(struct brcmf_pub *drvr, const char *fn, + int (*read_fn)(struct seq_file *seq, void *data)) +{ + return 0; +} +#endif + +#endif /* BRCMFMAC_DEBUG_H */ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_bus.h b/drivers/net/wireless/brcm80211/brcmfmac/dhd_bus.h index 80e73a1262be..0421cfea9a25 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_bus.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_bus.h @@ -17,7 +17,7 @@ #ifndef _BRCMF_BUS_H_ #define _BRCMF_BUS_H_ -#include "dhd_dbg.h" +#include "debug.h" /* IDs of the 6 default common rings of msgbuf protocol */ #define BRCMF_H2D_MSGRING_CONTROL_SUBMIT 0 diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_common.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_common.c index 98bc2226a119..78ada880506f 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_common.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_common.c @@ -21,7 +21,7 @@ #include #include "dhd.h" #include "dhd_bus.h" -#include "dhd_dbg.h" +#include "debug.h" #include "fwil.h" #include "fwil_types.h" #include "tracepoint.h" diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.c deleted file mode 100644 index be9f4f829192..000000000000 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.c +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Copyright (c) 2012 Broadcom Corporation - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY - * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION - * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN - * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ -#include -#include -#include - -#include -#include -#include "dhd.h" -#include "dhd_bus.h" -#include "dhd_dbg.h" - -static struct dentry *root_folder; - -void brcmf_debugfs_init(void) -{ - root_folder = debugfs_create_dir(KBUILD_MODNAME, NULL); - if (IS_ERR(root_folder)) - root_folder = NULL; -} - -void brcmf_debugfs_exit(void) -{ - if (!root_folder) - return; - - debugfs_remove_recursive(root_folder); - root_folder = NULL; -} - -static int brcmf_debugfs_chipinfo_read(struct seq_file *seq, void *data) -{ - struct brcmf_bus *bus = dev_get_drvdata(seq->private); - - seq_printf(seq, "chip: %x(%u) rev %u\n", - bus->chip, bus->chip, bus->chiprev); - return 0; -} - -int brcmf_debugfs_attach(struct brcmf_pub *drvr) -{ - struct device *dev = drvr->bus_if->dev; - - if (!root_folder) - return -ENODEV; - - drvr->dbgfs_dir = debugfs_create_dir(dev_name(dev), root_folder); - brcmf_debugfs_add_entry(drvr, "chipinfo", brcmf_debugfs_chipinfo_read); - - return PTR_ERR_OR_ZERO(drvr->dbgfs_dir); -} - -void brcmf_debugfs_detach(struct brcmf_pub *drvr) -{ - if (!IS_ERR_OR_NULL(drvr->dbgfs_dir)) - debugfs_remove_recursive(drvr->dbgfs_dir); -} - -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); - - return PTR_ERR_OR_ZERO(dentry); -} diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.h b/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.h deleted file mode 100644 index dec40d316c82..000000000000 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.h +++ /dev/null @@ -1,136 +0,0 @@ -/* - * Copyright (c) 2010 Broadcom Corporation - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY - * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION - * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN - * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef _BRCMF_DBG_H_ -#define _BRCMF_DBG_H_ - -/* message levels */ -#define BRCMF_TRACE_VAL 0x00000002 -#define BRCMF_INFO_VAL 0x00000004 -#define BRCMF_DATA_VAL 0x00000008 -#define BRCMF_CTL_VAL 0x00000010 -#define BRCMF_TIMER_VAL 0x00000020 -#define BRCMF_HDRS_VAL 0x00000040 -#define BRCMF_BYTES_VAL 0x00000080 -#define BRCMF_INTR_VAL 0x00000100 -#define BRCMF_GLOM_VAL 0x00000200 -#define BRCMF_EVENT_VAL 0x00000400 -#define BRCMF_BTA_VAL 0x00000800 -#define BRCMF_FIL_VAL 0x00001000 -#define BRCMF_USB_VAL 0x00002000 -#define BRCMF_SCAN_VAL 0x00004000 -#define BRCMF_CONN_VAL 0x00008000 -#define BRCMF_BCDC_VAL 0x00010000 -#define BRCMF_SDIO_VAL 0x00020000 -#define BRCMF_MSGBUF_VAL 0x00040000 -#define BRCMF_PCIE_VAL 0x00080000 - -/* set default print format */ -#undef pr_fmt -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -/* Macro for error messages. net_ratelimit() is used when driver - * debugging is not selected. When debugging the driver error - * messages are as important as other tracing or even more so. - */ -#ifndef CONFIG_BRCM_TRACING -#ifdef CONFIG_BRCMDBG -#define brcmf_err(fmt, ...) pr_err("%s: " fmt, __func__, ##__VA_ARGS__) -#else -#define brcmf_err(fmt, ...) \ - do { \ - if (net_ratelimit()) \ - pr_err("%s: " fmt, __func__, ##__VA_ARGS__); \ - } while (0) -#endif -#else -__printf(2, 3) -void __brcmf_err(const char *func, const char *fmt, ...); -#define brcmf_err(fmt, ...) \ - __brcmf_err(__func__, fmt, ##__VA_ARGS__) -#endif - -#if defined(DEBUG) || defined(CONFIG_BRCM_TRACING) -__printf(3, 4) -void __brcmf_dbg(u32 level, const char *func, const char *fmt, ...); -#define brcmf_dbg(level, fmt, ...) \ -do { \ - __brcmf_dbg(BRCMF_##level##_VAL, __func__, \ - fmt, ##__VA_ARGS__); \ -} while (0) -#define BRCMF_DATA_ON() (brcmf_msg_level & BRCMF_DATA_VAL) -#define BRCMF_CTL_ON() (brcmf_msg_level & BRCMF_CTL_VAL) -#define BRCMF_HDRS_ON() (brcmf_msg_level & BRCMF_HDRS_VAL) -#define BRCMF_BYTES_ON() (brcmf_msg_level & BRCMF_BYTES_VAL) -#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) - -#else /* defined(DEBUG) || defined(CONFIG_BRCM_TRACING) */ - -#define brcmf_dbg(level, fmt, ...) no_printk(fmt, ##__VA_ARGS__) - -#define BRCMF_DATA_ON() 0 -#define BRCMF_CTL_ON() 0 -#define BRCMF_HDRS_ON() 0 -#define BRCMF_BYTES_ON() 0 -#define BRCMF_GLOM_ON() 0 -#define BRCMF_EVENT_ON() 0 -#define BRCMF_FIL_ON() 0 - -#endif /* defined(DEBUG) || defined(CONFIG_BRCM_TRACING) */ - -#define brcmf_dbg_hex_dump(test, data, len, fmt, ...) \ -do { \ - trace_brcmf_hexdump((void *)data, len); \ - if (test) \ - brcmu_dbg_hex_dump(data, len, fmt, ##__VA_ARGS__); \ -} while (0) - -extern int brcmf_msg_level; - -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); -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)); -#else -static inline void brcmf_debugfs_init(void) -{ -} -static inline void brcmf_debugfs_exit(void) -{ -} -static inline int brcmf_debugfs_attach(struct brcmf_pub *drvr) -{ - return 0; -} -static inline void brcmf_debugfs_detach(struct brcmf_pub *drvr) -{ -} -static inline -int brcmf_debugfs_add_entry(struct brcmf_pub *drvr, const char *fn, - int (*read_fn)(struct seq_file *seq, void *data)) -{ - return 0; -} -#endif - -#endif /* _BRCMF_DBG_H_ */ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c index fb1043908a23..2522e684c66e 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c @@ -24,7 +24,7 @@ #include "dhd.h" #include "dhd_bus.h" -#include "dhd_dbg.h" +#include "debug.h" #include "fwil_types.h" #include "p2p.h" #include "wl_cfg80211.h" diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c index 079187c51170..4fc2f523af06 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c @@ -97,7 +97,7 @@ struct rte_console { #include #include "dhd_bus.h" -#include "dhd_dbg.h" +#include "debug.h" #include "tracepoint.h" #define TXQLEN 2048 /* bulk tx queue length */ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/feature.c b/drivers/net/wireless/brcm80211/brcmfmac/feature.c index 4eb6a41e72c7..86b854e83f71 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/feature.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/feature.c @@ -19,7 +19,7 @@ #include #include "dhd.h" #include "dhd_bus.h" -#include "dhd_dbg.h" +#include "debug.h" #include "fwil.h" #include "feature.h" diff --git a/drivers/net/wireless/brcm80211/brcmfmac/firmware.c b/drivers/net/wireless/brcm80211/brcmfmac/firmware.c index 8ea9f283d2b8..0f157f151282 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/firmware.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/firmware.c @@ -20,7 +20,7 @@ #include #include -#include "dhd_dbg.h" +#include "debug.h" #include "firmware.h" char brcmf_firmware_path[BRCMF_FW_PATH_LEN]; diff --git a/drivers/net/wireless/brcm80211/brcmfmac/flowring.c b/drivers/net/wireless/brcm80211/brcmfmac/flowring.c index 1faa929f5fff..15a2cc56baf0 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/flowring.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/flowring.c @@ -20,7 +20,7 @@ #include #include "dhd.h" -#include "dhd_dbg.h" +#include "debug.h" #include "dhd_bus.h" #include "proto.h" #include "flowring.h" diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fweh.c b/drivers/net/wireless/brcm80211/brcmfmac/fweh.c index 44fc85f68f7a..ce98ce55685b 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/fweh.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/fweh.c @@ -19,7 +19,7 @@ #include "brcmu_utils.h" #include "dhd.h" -#include "dhd_dbg.h" +#include "debug.h" #include "tracepoint.h" #include "fwsignal.h" #include "fweh.h" diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fwil.c b/drivers/net/wireless/brcm80211/brcmfmac/fwil.c index dccbdc1aae6d..424146c226aa 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/fwil.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/fwil.c @@ -24,7 +24,7 @@ #include #include "dhd.h" #include "dhd_bus.h" -#include "dhd_dbg.h" +#include "debug.h" #include "tracepoint.h" #include "fwil.h" #include "proto.h" diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c index 183f08d7fc8c..5a3cf644af2f 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c @@ -27,7 +27,7 @@ #include #include #include "dhd.h" -#include "dhd_dbg.h" +#include "debug.h" #include "dhd_bus.h" #include "fwil.h" #include "fwil_types.h" diff --git a/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c b/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c index 11cc051f97cd..87924d32d540 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c @@ -25,7 +25,7 @@ #include #include "dhd.h" -#include "dhd_dbg.h" +#include "debug.h" #include "proto.h" #include "msgbuf.h" #include "commonring.h" diff --git a/drivers/net/wireless/brcm80211/brcmfmac/of.c b/drivers/net/wireless/brcm80211/brcmfmac/of.c index f05f5270fec1..875060c54a00 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/of.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/of.c @@ -21,7 +21,7 @@ #include #include -#include "dhd_dbg.h" +#include "debug.h" #include "sdio_host.h" void brcmf_of_probe(struct brcmf_sdio_dev *sdiodev) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/p2p.c b/drivers/net/wireless/brcm80211/brcmfmac/p2p.c index d54c58a32faa..7459c252c1dd 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/p2p.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/p2p.c @@ -22,7 +22,7 @@ #include #include #include -#include +#include "debug.h" #include "fwil.h" #include "fwil_types.h" #include "p2p.h" diff --git a/drivers/net/wireless/brcm80211/brcmfmac/pcie.c b/drivers/net/wireless/brcm80211/brcmfmac/pcie.c index 8c0632ec9f7a..d8fe7abd96c8 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/pcie.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/pcie.c @@ -30,7 +30,7 @@ #include #include -#include "dhd_dbg.h" +#include "debug.h" #include "dhd_bus.h" #include "commonring.h" #include "msgbuf.h" diff --git a/drivers/net/wireless/brcm80211/brcmfmac/proto.c b/drivers/net/wireless/brcm80211/brcmfmac/proto.c index 62b940723339..f96b9326fe3d 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/proto.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/proto.c @@ -22,7 +22,7 @@ #include #include "dhd.h" #include "dhd_bus.h" -#include "dhd_dbg.h" +#include "debug.h" #include "proto.h" #include "bcdc.h" #include "msgbuf.h" diff --git a/drivers/net/wireless/brcm80211/brcmfmac/usb.c b/drivers/net/wireless/brcm80211/brcmfmac/usb.c index 3f12b606839e..4eb9b2c158b3 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/usb.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/usb.c @@ -24,8 +24,8 @@ #include #include #include -#include +#include "debug.h" #include "firmware.h" #include "usb.h" diff --git a/drivers/net/wireless/brcm80211/brcmfmac/vendor.c b/drivers/net/wireless/brcm80211/brcmfmac/vendor.c index 5960d827508c..f204c83cd61f 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/vendor.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/vendor.c @@ -22,7 +22,7 @@ #include "fwil_types.h" #include "dhd.h" #include "p2p.h" -#include "dhd_dbg.h" +#include "debug.h" #include "wl_cfg80211.h" #include "vendor.h" #include "fwil.h" diff --git a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c index fcadbe3c38d6..50bc6a0b9062 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c @@ -27,7 +27,7 @@ #include #include #include "dhd.h" -#include "dhd_dbg.h" +#include "debug.h" #include "tracepoint.h" #include "fwil_types.h" #include "p2p.h" -- cgit v1.2.3 From d14f78b990ec4d66e9509bc42a6fce2d217880ec Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Tue, 28 Oct 2014 14:56:14 +0100 Subject: brcmfmac: (clean) Rename dhd_bus.h in bus.h Reviewed-by: Arend Van Spriel Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/bcdc.c | 2 +- drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c | 2 +- drivers/net/wireless/brcm80211/brcmfmac/bus.h | 243 ++++++++++++++++++++ drivers/net/wireless/brcm80211/brcmfmac/debug.c | 2 +- drivers/net/wireless/brcm80211/brcmfmac/dhd_bus.h | 244 --------------------- .../net/wireless/brcm80211/brcmfmac/dhd_common.c | 2 +- .../net/wireless/brcm80211/brcmfmac/dhd_linux.c | 2 +- drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c | 2 +- drivers/net/wireless/brcm80211/brcmfmac/feature.c | 2 +- drivers/net/wireless/brcm80211/brcmfmac/flowring.c | 2 +- drivers/net/wireless/brcm80211/brcmfmac/fwil.c | 2 +- drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c | 2 +- drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c | 2 +- drivers/net/wireless/brcm80211/brcmfmac/pcie.c | 2 +- drivers/net/wireless/brcm80211/brcmfmac/proto.c | 2 +- drivers/net/wireless/brcm80211/brcmfmac/usb.c | 3 +- .../net/wireless/brcm80211/brcmfmac/wl_cfg80211.c | 2 +- 17 files changed, 258 insertions(+), 260 deletions(-) create mode 100644 drivers/net/wireless/brcm80211/brcmfmac/bus.h delete mode 100644 drivers/net/wireless/brcm80211/brcmfmac/dhd_bus.h (limited to 'drivers/net/wireless/brcm80211') diff --git a/drivers/net/wireless/brcm80211/brcmfmac/bcdc.c b/drivers/net/wireless/brcm80211/brcmfmac/bcdc.c index 11d3dfae8bac..d5a2d94ffde4 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/bcdc.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/bcdc.c @@ -26,7 +26,7 @@ #include #include "dhd.h" -#include "dhd_bus.h" +#include "bus.h" #include "fwsignal.h" #include "debug.h" #include "tracepoint.h" diff --git a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c index 8c0dfea68eeb..969bdd655c7b 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c @@ -41,7 +41,7 @@ #include #include #include "chip.h" -#include "dhd_bus.h" +#include "bus.h" #include "debug.h" #include "sdio_host.h" #include "of.h" diff --git a/drivers/net/wireless/brcm80211/brcmfmac/bus.h b/drivers/net/wireless/brcm80211/brcmfmac/bus.h new file mode 100644 index 000000000000..ef344e47218a --- /dev/null +++ b/drivers/net/wireless/brcm80211/brcmfmac/bus.h @@ -0,0 +1,243 @@ +/* + * Copyright (c) 2010 Broadcom Corporation + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef BRCMFMAC_BUS_H +#define BRCMFMAC_BUS_H + +#include "debug.h" + +/* IDs of the 6 default common rings of msgbuf protocol */ +#define BRCMF_H2D_MSGRING_CONTROL_SUBMIT 0 +#define BRCMF_H2D_MSGRING_RXPOST_SUBMIT 1 +#define BRCMF_D2H_MSGRING_CONTROL_COMPLETE 2 +#define BRCMF_D2H_MSGRING_TX_COMPLETE 3 +#define BRCMF_D2H_MSGRING_RX_COMPLETE 4 + +#define BRCMF_NROF_H2D_COMMON_MSGRINGS 2 +#define BRCMF_NROF_D2H_COMMON_MSGRINGS 3 +#define BRCMF_NROF_COMMON_MSGRINGS (BRCMF_NROF_H2D_COMMON_MSGRINGS + \ + BRCMF_NROF_D2H_COMMON_MSGRINGS) + +/* The level of bus communication with the dongle */ +enum brcmf_bus_state { + BRCMF_BUS_UNKNOWN, /* Not determined yet */ + BRCMF_BUS_NOMEDIUM, /* No medium access to dongle */ + BRCMF_BUS_DOWN, /* Not ready for frame transfers */ + BRCMF_BUS_LOAD, /* Download access only (CPU reset) */ + BRCMF_BUS_DATA /* Ready for frame transfers */ +}; + +/* The level of bus communication with the dongle */ +enum brcmf_bus_protocol_type { + BRCMF_PROTO_BCDC, + BRCMF_PROTO_MSGBUF +}; + +struct brcmf_bus_dcmd { + char *name; + char *param; + int param_len; + struct list_head list; +}; + +/** + * struct brcmf_bus_ops - bus callback operations. + * + * @preinit: execute bus/device specific dongle init commands (optional). + * @init: prepare for communication with dongle. + * @stop: clear pending frames, disable data flow. + * @txdata: send a data frame to the dongle. When the data + * has been transferred, the common driver must be + * notified using brcmf_txcomplete(). The common + * driver calls this function with interrupts + * disabled. + * @txctl: transmit a control request message to dongle. + * @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 + * + * This structure provides an abstract interface towards the + * bus specific driver. For control messages to common driver + * will assure there is only one active transaction. Unless + * indicated otherwise these callbacks are mandatory. + */ +struct brcmf_bus_ops { + int (*preinit)(struct device *dev); + void (*stop)(struct device *dev); + int (*txdata)(struct device *dev, struct sk_buff *skb); + int (*txctl)(struct device *dev, unsigned char *msg, uint len); + int (*rxctl)(struct device *dev, unsigned char *msg, uint len); + struct pktq * (*gettxq)(struct device *dev); + void (*wowl_config)(struct device *dev, bool enabled); +}; + + +/** + * struct brcmf_bus_msgbuf - bus ringbuf if in case of msgbuf. + * + * @commonrings: commonrings which are always there. + * @flowrings: commonrings which are dynamically created and destroyed for data. + * @rx_dataoffset: if set then all rx data has this this offset. + * @max_rxbufpost: maximum number of buffers to post for rx. + * @nrof_flowrings: number of flowrings. + */ +struct brcmf_bus_msgbuf { + struct brcmf_commonring *commonrings[BRCMF_NROF_COMMON_MSGRINGS]; + struct brcmf_commonring **flowrings; + u32 rx_dataoffset; + u32 max_rxbufpost; + u32 nrof_flowrings; +}; + + +/** + * struct brcmf_bus - interface structure between common and bus layer + * + * @bus_priv: pointer to private bus device. + * @proto_type: protocol type, bcdc or msgbuf + * @dev: device pointer of bus device. + * @drvr: public driver information. + * @state: operational state of the bus interface. + * @maxctl: maximum size for rxctl request message. + * @tx_realloc: number of tx packets realloced for headroom. + * @dstats: dongle-based statistical data. + * @dcmd_list: bus/device specific dongle initialization commands. + * @chip: device identifier of the dongle chip. + * @wowl_supported: is wowl supported by bus driver. + * @chiprev: revision of the dongle chip. + */ +struct brcmf_bus { + union { + struct brcmf_sdio_dev *sdio; + struct brcmf_usbdev *usb; + struct brcmf_pciedev *pcie; + } bus_priv; + enum brcmf_bus_protocol_type proto_type; + struct device *dev; + struct brcmf_pub *drvr; + enum brcmf_bus_state state; + uint maxctl; + unsigned long tx_realloc; + u32 chip; + u32 chiprev; + bool always_use_fws_queue; + bool wowl_supported; + + struct brcmf_bus_ops *ops; + struct brcmf_bus_msgbuf *msgbuf; +}; + +/* + * callback wrappers + */ +static inline int brcmf_bus_preinit(struct brcmf_bus *bus) +{ + if (!bus->ops->preinit) + return 0; + return bus->ops->preinit(bus->dev); +} + +static inline void brcmf_bus_stop(struct brcmf_bus *bus) +{ + bus->ops->stop(bus->dev); +} + +static inline int brcmf_bus_txdata(struct brcmf_bus *bus, struct sk_buff *skb) +{ + return bus->ops->txdata(bus->dev, skb); +} + +static inline +int brcmf_bus_txctl(struct brcmf_bus *bus, unsigned char *msg, uint len) +{ + return bus->ops->txctl(bus->dev, msg, len); +} + +static inline +int brcmf_bus_rxctl(struct brcmf_bus *bus, unsigned char *msg, uint len) +{ + return bus->ops->rxctl(bus->dev, msg, len); +} + +static inline +struct pktq *brcmf_bus_gettxq(struct brcmf_bus *bus) +{ + if (!bus->ops->gettxq) + return ERR_PTR(-ENOENT); + + return bus->ops->gettxq(bus->dev); +} + +static inline +void brcmf_bus_wowl_config(struct brcmf_bus *bus, bool enabled) +{ + if (bus->ops->wowl_config) + bus->ops->wowl_config(bus->dev, enabled); +} + +static inline bool brcmf_bus_ready(struct brcmf_bus *bus) +{ + return bus->state == BRCMF_BUS_LOAD || bus->state == BRCMF_BUS_DATA; +} + +static inline void brcmf_bus_change_state(struct brcmf_bus *bus, + enum brcmf_bus_state new_state) +{ + /* NOMEDIUM is permanent */ + if (bus->state == BRCMF_BUS_NOMEDIUM) + return; + + brcmf_dbg(TRACE, "%d -> %d\n", bus->state, new_state); + bus->state = new_state; +} + +/* + * interface functions from common layer + */ + +bool brcmf_c_prec_enq(struct device *dev, struct pktq *q, struct sk_buff *pkt, + int prec); + +/* Receive frame for delivery to OS. Callee disposes of rxp. */ +void brcmf_rx_frame(struct device *dev, struct sk_buff *rxp); + +/* Indication from bus module regarding presence/insertion of dongle. */ +int brcmf_attach(struct device *dev); +/* Indication from bus module regarding removal/absence of dongle */ +void brcmf_detach(struct device *dev); +/* Indication from bus module that dongle should be reset */ +void brcmf_dev_reset(struct device *dev); +/* Indication from bus module to change flow-control state */ +void brcmf_txflowblock(struct device *dev, bool state); + +/* Notify the bus has transferred the tx packet to firmware */ +void brcmf_txcomplete(struct device *dev, struct sk_buff *txp, bool success); + +int brcmf_bus_start(struct device *dev); +s32 brcmf_iovar_data_set(struct device *dev, char *name, void *data, u32 len); +void brcmf_bus_add_txhdrlen(struct device *dev, uint len); + +#ifdef CONFIG_BRCMFMAC_SDIO +void brcmf_sdio_exit(void); +void brcmf_sdio_init(void); +void brcmf_sdio_register(void); +#endif +#ifdef CONFIG_BRCMFMAC_USB +void brcmf_usb_exit(void); +void brcmf_usb_register(void); +#endif + +#endif /* BRCMFMAC_BUS_H */ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/debug.c b/drivers/net/wireless/brcm80211/brcmfmac/debug.c index 27e51085d33a..340b10447ab9 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/debug.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/debug.c @@ -20,7 +20,7 @@ #include #include #include "dhd.h" -#include "dhd_bus.h" +#include "bus.h" #include "debug.h" static struct dentry *root_folder; diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_bus.h b/drivers/net/wireless/brcm80211/brcmfmac/dhd_bus.h deleted file mode 100644 index 0421cfea9a25..000000000000 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_bus.h +++ /dev/null @@ -1,244 +0,0 @@ -/* - * Copyright (c) 2010 Broadcom Corporation - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY - * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION - * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN - * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef _BRCMF_BUS_H_ -#define _BRCMF_BUS_H_ - -#include "debug.h" - -/* IDs of the 6 default common rings of msgbuf protocol */ -#define BRCMF_H2D_MSGRING_CONTROL_SUBMIT 0 -#define BRCMF_H2D_MSGRING_RXPOST_SUBMIT 1 -#define BRCMF_D2H_MSGRING_CONTROL_COMPLETE 2 -#define BRCMF_D2H_MSGRING_TX_COMPLETE 3 -#define BRCMF_D2H_MSGRING_RX_COMPLETE 4 - -#define BRCMF_NROF_H2D_COMMON_MSGRINGS 2 -#define BRCMF_NROF_D2H_COMMON_MSGRINGS 3 -#define BRCMF_NROF_COMMON_MSGRINGS (BRCMF_NROF_H2D_COMMON_MSGRINGS + \ - BRCMF_NROF_D2H_COMMON_MSGRINGS) - -/* The level of bus communication with the dongle */ -enum brcmf_bus_state { - BRCMF_BUS_UNKNOWN, /* Not determined yet */ - BRCMF_BUS_NOMEDIUM, /* No medium access to dongle */ - BRCMF_BUS_DOWN, /* Not ready for frame transfers */ - BRCMF_BUS_LOAD, /* Download access only (CPU reset) */ - BRCMF_BUS_DATA /* Ready for frame transfers */ -}; - -/* The level of bus communication with the dongle */ -enum brcmf_bus_protocol_type { - BRCMF_PROTO_BCDC, - BRCMF_PROTO_MSGBUF -}; - -struct brcmf_bus_dcmd { - char *name; - char *param; - int param_len; - struct list_head list; -}; - -/** - * struct brcmf_bus_ops - bus callback operations. - * - * @preinit: execute bus/device specific dongle init commands (optional). - * @init: prepare for communication with dongle. - * @stop: clear pending frames, disable data flow. - * @txdata: send a data frame to the dongle. When the data - * has been transferred, the common driver must be - * notified using brcmf_txcomplete(). The common - * driver calls this function with interrupts - * disabled. - * @txctl: transmit a control request message to dongle. - * @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 - * - * This structure provides an abstract interface towards the - * bus specific driver. For control messages to common driver - * will assure there is only one active transaction. Unless - * indicated otherwise these callbacks are mandatory. - */ -struct brcmf_bus_ops { - int (*preinit)(struct device *dev); - void (*stop)(struct device *dev); - int (*txdata)(struct device *dev, struct sk_buff *skb); - int (*txctl)(struct device *dev, unsigned char *msg, uint len); - int (*rxctl)(struct device *dev, unsigned char *msg, uint len); - struct pktq * (*gettxq)(struct device *dev); - void (*wowl_config)(struct device *dev, bool enabled); -}; - - -/** - * struct brcmf_bus_msgbuf - bus ringbuf if in case of msgbuf. - * - * @commonrings: commonrings which are always there. - * @flowrings: commonrings which are dynamically created and destroyed for data. - * @rx_dataoffset: if set then all rx data has this this offset. - * @max_rxbufpost: maximum number of buffers to post for rx. - * @nrof_flowrings: number of flowrings. - */ -struct brcmf_bus_msgbuf { - struct brcmf_commonring *commonrings[BRCMF_NROF_COMMON_MSGRINGS]; - struct brcmf_commonring **flowrings; - u32 rx_dataoffset; - u32 max_rxbufpost; - u32 nrof_flowrings; -}; - - -/** - * struct brcmf_bus - interface structure between common and bus layer - * - * @bus_priv: pointer to private bus device. - * @proto_type: protocol type, bcdc or msgbuf - * @dev: device pointer of bus device. - * @drvr: public driver information. - * @state: operational state of the bus interface. - * @maxctl: maximum size for rxctl request message. - * @tx_realloc: number of tx packets realloced for headroom. - * @dstats: dongle-based statistical data. - * @dcmd_list: bus/device specific dongle initialization commands. - * @chip: device identifier of the dongle chip. - * @wowl_supported: is wowl supported by bus driver. - * @chiprev: revision of the dongle chip. - */ -struct brcmf_bus { - union { - struct brcmf_sdio_dev *sdio; - struct brcmf_usbdev *usb; - struct brcmf_pciedev *pcie; - } bus_priv; - enum brcmf_bus_protocol_type proto_type; - struct device *dev; - struct brcmf_pub *drvr; - enum brcmf_bus_state state; - uint maxctl; - unsigned long tx_realloc; - u32 chip; - u32 chiprev; - bool always_use_fws_queue; - bool wowl_supported; - - struct brcmf_bus_ops *ops; - struct brcmf_bus_msgbuf *msgbuf; -}; - -/* - * callback wrappers - */ -static inline int brcmf_bus_preinit(struct brcmf_bus *bus) -{ - if (!bus->ops->preinit) - return 0; - return bus->ops->preinit(bus->dev); -} - -static inline void brcmf_bus_stop(struct brcmf_bus *bus) -{ - bus->ops->stop(bus->dev); -} - -static inline int brcmf_bus_txdata(struct brcmf_bus *bus, struct sk_buff *skb) -{ - return bus->ops->txdata(bus->dev, skb); -} - -static inline -int brcmf_bus_txctl(struct brcmf_bus *bus, unsigned char *msg, uint len) -{ - return bus->ops->txctl(bus->dev, msg, len); -} - -static inline -int brcmf_bus_rxctl(struct brcmf_bus *bus, unsigned char *msg, uint len) -{ - return bus->ops->rxctl(bus->dev, msg, len); -} - -static inline -struct pktq *brcmf_bus_gettxq(struct brcmf_bus *bus) -{ - if (!bus->ops->gettxq) - return ERR_PTR(-ENOENT); - - return bus->ops->gettxq(bus->dev); -} - -static inline -void brcmf_bus_wowl_config(struct brcmf_bus *bus, bool enabled) -{ - if (bus->ops->wowl_config) - bus->ops->wowl_config(bus->dev, enabled); -} - -static inline bool brcmf_bus_ready(struct brcmf_bus *bus) -{ - return bus->state == BRCMF_BUS_LOAD || bus->state == BRCMF_BUS_DATA; -} - -static inline void brcmf_bus_change_state(struct brcmf_bus *bus, - enum brcmf_bus_state new_state) -{ - /* NOMEDIUM is permanent */ - if (bus->state == BRCMF_BUS_NOMEDIUM) - return; - - brcmf_dbg(TRACE, "%d -> %d\n", bus->state, new_state); - bus->state = new_state; -} - -/* - * interface functions from common layer - */ - -bool brcmf_c_prec_enq(struct device *dev, struct pktq *q, struct sk_buff *pkt, - int prec); - -/* Receive frame for delivery to OS. Callee disposes of rxp. */ -void brcmf_rx_frame(struct device *dev, struct sk_buff *rxp); - -/* Indication from bus module regarding presence/insertion of dongle. */ -int brcmf_attach(struct device *dev); -/* Indication from bus module regarding removal/absence of dongle */ -void brcmf_detach(struct device *dev); -/* Indication from bus module that dongle should be reset */ -void brcmf_dev_reset(struct device *dev); -/* Indication from bus module to change flow-control state */ -void brcmf_txflowblock(struct device *dev, bool state); - -/* Notify the bus has transferred the tx packet to firmware */ -void brcmf_txcomplete(struct device *dev, struct sk_buff *txp, bool success); - -int brcmf_bus_start(struct device *dev); -s32 brcmf_iovar_data_set(struct device *dev, char *name, void *data, - u32 len); -void brcmf_bus_add_txhdrlen(struct device *dev, uint len); - -#ifdef CONFIG_BRCMFMAC_SDIO -void brcmf_sdio_exit(void); -void brcmf_sdio_init(void); -void brcmf_sdio_register(void); -#endif -#ifdef CONFIG_BRCMFMAC_USB -void brcmf_usb_exit(void); -void brcmf_usb_register(void); -#endif - -#endif /* _BRCMF_BUS_H_ */ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_common.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_common.c index 78ada880506f..7723994136a1 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_common.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_common.c @@ -20,7 +20,7 @@ #include #include #include "dhd.h" -#include "dhd_bus.h" +#include "bus.h" #include "debug.h" #include "fwil.h" #include "fwil_types.h" diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c index 2522e684c66e..8dbf2c155b5b 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c @@ -23,7 +23,7 @@ #include #include "dhd.h" -#include "dhd_bus.h" +#include "bus.h" #include "debug.h" #include "fwil_types.h" #include "p2p.h" diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c index 4fc2f523af06..224e922d139a 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c @@ -96,7 +96,7 @@ struct rte_console { #endif /* DEBUG */ #include -#include "dhd_bus.h" +#include "bus.h" #include "debug.h" #include "tracepoint.h" diff --git a/drivers/net/wireless/brcm80211/brcmfmac/feature.c b/drivers/net/wireless/brcm80211/brcmfmac/feature.c index 86b854e83f71..d4be4c416912 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/feature.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/feature.c @@ -18,7 +18,7 @@ #include #include "dhd.h" -#include "dhd_bus.h" +#include "bus.h" #include "debug.h" #include "fwil.h" #include "feature.h" diff --git a/drivers/net/wireless/brcm80211/brcmfmac/flowring.c b/drivers/net/wireless/brcm80211/brcmfmac/flowring.c index 15a2cc56baf0..e9cb550f5c5a 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/flowring.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/flowring.c @@ -21,7 +21,7 @@ #include "dhd.h" #include "debug.h" -#include "dhd_bus.h" +#include "bus.h" #include "proto.h" #include "flowring.h" #include "msgbuf.h" diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fwil.c b/drivers/net/wireless/brcm80211/brcmfmac/fwil.c index 424146c226aa..70db41f51b7b 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/fwil.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/fwil.c @@ -23,7 +23,7 @@ #include #include #include "dhd.h" -#include "dhd_bus.h" +#include "bus.h" #include "debug.h" #include "tracepoint.h" #include "fwil.h" diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c index 5a3cf644af2f..dbd52ccbde56 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c @@ -28,7 +28,7 @@ #include #include "dhd.h" #include "debug.h" -#include "dhd_bus.h" +#include "bus.h" #include "fwil.h" #include "fwil_types.h" #include "fweh.h" diff --git a/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c b/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c index 87924d32d540..18e1770b8d8c 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c @@ -30,7 +30,7 @@ #include "msgbuf.h" #include "commonring.h" #include "flowring.h" -#include "dhd_bus.h" +#include "bus.h" #include "tracepoint.h" diff --git a/drivers/net/wireless/brcm80211/brcmfmac/pcie.c b/drivers/net/wireless/brcm80211/brcmfmac/pcie.c index d8fe7abd96c8..b0ae7993e2e8 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/pcie.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/pcie.c @@ -31,7 +31,7 @@ #include #include "debug.h" -#include "dhd_bus.h" +#include "bus.h" #include "commonring.h" #include "msgbuf.h" #include "pcie.h" diff --git a/drivers/net/wireless/brcm80211/brcmfmac/proto.c b/drivers/net/wireless/brcm80211/brcmfmac/proto.c index f96b9326fe3d..5e83b2bee9d8 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/proto.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/proto.c @@ -21,7 +21,7 @@ #include #include "dhd.h" -#include "dhd_bus.h" +#include "bus.h" #include "debug.h" #include "proto.h" #include "bcdc.h" diff --git a/drivers/net/wireless/brcm80211/brcmfmac/usb.c b/drivers/net/wireless/brcm80211/brcmfmac/usb.c index 4eb9b2c158b3..5265aa70b094 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/usb.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/usb.c @@ -23,8 +23,7 @@ #include #include #include -#include - +#include "bus.h" #include "debug.h" #include "firmware.h" #include "usb.h" diff --git a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c index 50bc6a0b9062..2560dff4151e 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c @@ -37,7 +37,7 @@ #include "fwil.h" #include "proto.h" #include "vendor.h" -#include "dhd_bus.h" +#include "bus.h" #define BRCMF_SCAN_IE_LEN_MAX 2048 #define BRCMF_PNO_VERSION 2 -- cgit v1.2.3 From 76b5a96d1df10535c7e2e7f876f2230e243aa7b8 Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Tue, 28 Oct 2014 14:56:15 +0100 Subject: brcmfmac: (clean) Rename dhd_common.c in common.c Reviewed-by: Arend Van Spriel Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/Makefile | 2 +- drivers/net/wireless/brcm80211/brcmfmac/common.c | 219 +++++++++++++++++++++ .../net/wireless/brcm80211/brcmfmac/dhd_common.c | 219 --------------------- 3 files changed, 220 insertions(+), 220 deletions(-) create mode 100644 drivers/net/wireless/brcm80211/brcmfmac/common.c delete mode 100644 drivers/net/wireless/brcm80211/brcmfmac/dhd_common.c (limited to 'drivers/net/wireless/brcm80211') diff --git a/drivers/net/wireless/brcm80211/brcmfmac/Makefile b/drivers/net/wireless/brcm80211/brcmfmac/Makefile index 1da9042c8aba..f6e35c95b829 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/Makefile +++ b/drivers/net/wireless/brcm80211/brcmfmac/Makefile @@ -30,7 +30,7 @@ brcmfmac-objs += \ fwsignal.o \ p2p.o \ proto.o \ - dhd_common.o \ + common.o \ dhd_linux.o \ firmware.o \ feature.o \ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/common.c b/drivers/net/wireless/brcm80211/brcmfmac/common.c new file mode 100644 index 000000000000..75642e46c37a --- /dev/null +++ b/drivers/net/wireless/brcm80211/brcmfmac/common.c @@ -0,0 +1,219 @@ +/* + * Copyright (c) 2010 Broadcom Corporation + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include "dhd.h" +#include "bus.h" +#include "debug.h" +#include "fwil.h" +#include "fwil_types.h" +#include "tracepoint.h" + +#define BRCMF_DEFAULT_BCN_TIMEOUT 3 +#define BRCMF_DEFAULT_SCAN_CHANNEL_TIME 40 +#define BRCMF_DEFAULT_SCAN_UNASSOC_TIME 40 + +/* boost value for RSSI_DELTA in preferred join selection */ +#define BRCMF_JOIN_PREF_RSSI_BOOST 8 + + +bool brcmf_c_prec_enq(struct device *dev, struct pktq *q, + struct sk_buff *pkt, int prec) +{ + struct sk_buff *p; + int eprec = -1; /* precedence to evict from */ + bool discard_oldest; + struct brcmf_bus *bus_if = dev_get_drvdata(dev); + struct brcmf_pub *drvr = bus_if->drvr; + + /* Fast case, precedence queue is not full and we are also not + * exceeding total queue length + */ + if (!pktq_pfull(q, prec) && !pktq_full(q)) { + brcmu_pktq_penq(q, prec, pkt); + return true; + } + + /* Determine precedence from which to evict packet, if any */ + if (pktq_pfull(q, prec)) { + eprec = prec; + } else if (pktq_full(q)) { + p = brcmu_pktq_peek_tail(q, &eprec); + if (eprec > prec) + return false; + } + + /* Evict if needed */ + if (eprec >= 0) { + /* Detect queueing to unconfigured precedence */ + discard_oldest = ac_bitmap_tst(drvr->wme_dp, eprec); + if (eprec == prec && !discard_oldest) + return false; /* refuse newer (incoming) packet */ + /* Evict packet according to discard policy */ + p = discard_oldest ? brcmu_pktq_pdeq(q, eprec) : + brcmu_pktq_pdeq_tail(q, eprec); + if (p == NULL) + brcmf_err("brcmu_pktq_penq() failed, oldest %d\n", + discard_oldest); + + brcmu_pkt_buf_free_skb(p); + } + + /* Enqueue */ + p = brcmu_pktq_penq(q, prec, pkt); + if (p == NULL) + brcmf_err("brcmu_pktq_penq() failed\n"); + + return p != NULL; +} + +int brcmf_c_preinit_dcmds(struct brcmf_if *ifp) +{ + s8 eventmask[BRCMF_EVENTING_MASK_LEN]; + u8 buf[BRCMF_DCMD_SMLEN]; + struct brcmf_join_pref_params join_pref_params[2]; + char *ptr; + s32 err; + + /* retreive mac address */ + err = brcmf_fil_iovar_data_get(ifp, "cur_etheraddr", ifp->mac_addr, + sizeof(ifp->mac_addr)); + if (err < 0) { + brcmf_err("Retreiving cur_etheraddr failed, %d\n", + err); + goto done; + } + memcpy(ifp->drvr->mac, ifp->mac_addr, sizeof(ifp->drvr->mac)); + + /* query for 'ver' to get version info from firmware */ + memset(buf, 0, sizeof(buf)); + strcpy(buf, "ver"); + err = brcmf_fil_iovar_data_get(ifp, "ver", buf, sizeof(buf)); + if (err < 0) { + brcmf_err("Retreiving version information failed, %d\n", + err); + goto done; + } + ptr = (char *)buf; + strsep(&ptr, "\n"); + + /* Print fw version info */ + brcmf_err("Firmware version = %s\n", buf); + + /* locate firmware version number for ethtool */ + ptr = strrchr(buf, ' ') + 1; + strlcpy(ifp->drvr->fwver, ptr, sizeof(ifp->drvr->fwver)); + + /* set mpc */ + err = brcmf_fil_iovar_int_set(ifp, "mpc", 1); + if (err) { + brcmf_err("failed setting mpc\n"); + goto done; + } + + /* + * Setup timeout if Beacons are lost and roam is off to report + * link down + */ + err = brcmf_fil_iovar_int_set(ifp, "bcn_timeout", + BRCMF_DEFAULT_BCN_TIMEOUT); + if (err) { + brcmf_err("bcn_timeout error (%d)\n", err); + goto done; + } + + /* Enable/Disable build-in roaming to allowed ext supplicant to take + * of romaing + */ + err = brcmf_fil_iovar_int_set(ifp, "roam_off", 1); + if (err) { + brcmf_err("roam_off error (%d)\n", err); + goto done; + } + + /* Setup join_pref to select target by RSSI(with boost on 5GHz) */ + join_pref_params[0].type = BRCMF_JOIN_PREF_RSSI_DELTA; + join_pref_params[0].len = 2; + join_pref_params[0].rssi_gain = BRCMF_JOIN_PREF_RSSI_BOOST; + join_pref_params[0].band = WLC_BAND_5G; + join_pref_params[1].type = BRCMF_JOIN_PREF_RSSI; + join_pref_params[1].len = 2; + join_pref_params[1].rssi_gain = 0; + join_pref_params[1].band = 0; + err = brcmf_fil_iovar_data_set(ifp, "join_pref", join_pref_params, + sizeof(join_pref_params)); + if (err) + brcmf_err("Set join_pref error (%d)\n", err); + + /* Setup event_msgs, enable E_IF */ + err = brcmf_fil_iovar_data_get(ifp, "event_msgs", eventmask, + BRCMF_EVENTING_MASK_LEN); + if (err) { + brcmf_err("Get event_msgs error (%d)\n", err); + goto done; + } + setbit(eventmask, BRCMF_E_IF); + err = brcmf_fil_iovar_data_set(ifp, "event_msgs", eventmask, + BRCMF_EVENTING_MASK_LEN); + if (err) { + brcmf_err("Set event_msgs error (%d)\n", err); + goto done; + } + + /* Setup default scan channel time */ + err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_SCAN_CHANNEL_TIME, + BRCMF_DEFAULT_SCAN_CHANNEL_TIME); + if (err) { + brcmf_err("BRCMF_C_SET_SCAN_CHANNEL_TIME error (%d)\n", + err); + goto done; + } + + /* Setup default scan unassoc time */ + err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_SCAN_UNASSOC_TIME, + BRCMF_DEFAULT_SCAN_UNASSOC_TIME); + if (err) { + brcmf_err("BRCMF_C_SET_SCAN_UNASSOC_TIME error (%d)\n", + err); + goto done; + } + + /* do bus specific preinit here */ + err = brcmf_bus_preinit(ifp->drvr->bus_if); +done: + return err; +} + +#if defined(CONFIG_BRCM_TRACING) || defined(CONFIG_BRCMDBG) +void __brcmf_dbg(u32 level, const char *func, const char *fmt, ...) +{ + struct va_format vaf = { + .fmt = fmt, + }; + va_list args; + + va_start(args, fmt); + vaf.va = &args; + if (brcmf_msg_level & level) + pr_debug("%s %pV", func, &vaf); + trace_brcmf_dbg(level, func, &vaf); + va_end(args); +} +#endif diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_common.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_common.c deleted file mode 100644 index 7723994136a1..000000000000 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_common.c +++ /dev/null @@ -1,219 +0,0 @@ -/* - * Copyright (c) 2010 Broadcom Corporation - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY - * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION - * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN - * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include -#include -#include -#include -#include -#include "dhd.h" -#include "bus.h" -#include "debug.h" -#include "fwil.h" -#include "fwil_types.h" -#include "tracepoint.h" - -#define BRCMF_DEFAULT_BCN_TIMEOUT 3 -#define BRCMF_DEFAULT_SCAN_CHANNEL_TIME 40 -#define BRCMF_DEFAULT_SCAN_UNASSOC_TIME 40 - -/* boost value for RSSI_DELTA in preferred join selection */ -#define BRCMF_JOIN_PREF_RSSI_BOOST 8 - - -bool brcmf_c_prec_enq(struct device *dev, struct pktq *q, - struct sk_buff *pkt, int prec) -{ - struct sk_buff *p; - int eprec = -1; /* precedence to evict from */ - bool discard_oldest; - struct brcmf_bus *bus_if = dev_get_drvdata(dev); - struct brcmf_pub *drvr = bus_if->drvr; - - /* Fast case, precedence queue is not full and we are also not - * exceeding total queue length - */ - if (!pktq_pfull(q, prec) && !pktq_full(q)) { - brcmu_pktq_penq(q, prec, pkt); - return true; - } - - /* Determine precedence from which to evict packet, if any */ - if (pktq_pfull(q, prec)) - eprec = prec; - else if (pktq_full(q)) { - p = brcmu_pktq_peek_tail(q, &eprec); - if (eprec > prec) - return false; - } - - /* Evict if needed */ - if (eprec >= 0) { - /* Detect queueing to unconfigured precedence */ - discard_oldest = ac_bitmap_tst(drvr->wme_dp, eprec); - if (eprec == prec && !discard_oldest) - return false; /* refuse newer (incoming) packet */ - /* Evict packet according to discard policy */ - p = discard_oldest ? brcmu_pktq_pdeq(q, eprec) : - brcmu_pktq_pdeq_tail(q, eprec); - if (p == NULL) - brcmf_err("brcmu_pktq_penq() failed, oldest %d\n", - discard_oldest); - - brcmu_pkt_buf_free_skb(p); - } - - /* Enqueue */ - p = brcmu_pktq_penq(q, prec, pkt); - if (p == NULL) - brcmf_err("brcmu_pktq_penq() failed\n"); - - return p != NULL; -} - -int brcmf_c_preinit_dcmds(struct brcmf_if *ifp) -{ - s8 eventmask[BRCMF_EVENTING_MASK_LEN]; - u8 buf[BRCMF_DCMD_SMLEN]; - struct brcmf_join_pref_params join_pref_params[2]; - char *ptr; - s32 err; - - /* retreive mac address */ - err = brcmf_fil_iovar_data_get(ifp, "cur_etheraddr", ifp->mac_addr, - sizeof(ifp->mac_addr)); - if (err < 0) { - brcmf_err("Retreiving cur_etheraddr failed, %d\n", - err); - goto done; - } - memcpy(ifp->drvr->mac, ifp->mac_addr, sizeof(ifp->drvr->mac)); - - /* query for 'ver' to get version info from firmware */ - memset(buf, 0, sizeof(buf)); - strcpy(buf, "ver"); - err = brcmf_fil_iovar_data_get(ifp, "ver", buf, sizeof(buf)); - if (err < 0) { - brcmf_err("Retreiving version information failed, %d\n", - err); - goto done; - } - ptr = (char *)buf; - strsep(&ptr, "\n"); - - /* Print fw version info */ - brcmf_err("Firmware version = %s\n", buf); - - /* locate firmware version number for ethtool */ - ptr = strrchr(buf, ' ') + 1; - strlcpy(ifp->drvr->fwver, ptr, sizeof(ifp->drvr->fwver)); - - /* set mpc */ - err = brcmf_fil_iovar_int_set(ifp, "mpc", 1); - if (err) { - brcmf_err("failed setting mpc\n"); - goto done; - } - - /* - * Setup timeout if Beacons are lost and roam is off to report - * link down - */ - err = brcmf_fil_iovar_int_set(ifp, "bcn_timeout", - BRCMF_DEFAULT_BCN_TIMEOUT); - if (err) { - brcmf_err("bcn_timeout error (%d)\n", err); - goto done; - } - - /* Enable/Disable build-in roaming to allowed ext supplicant to take - * of romaing - */ - err = brcmf_fil_iovar_int_set(ifp, "roam_off", 1); - if (err) { - brcmf_err("roam_off error (%d)\n", err); - goto done; - } - - /* Setup join_pref to select target by RSSI(with boost on 5GHz) */ - join_pref_params[0].type = BRCMF_JOIN_PREF_RSSI_DELTA; - join_pref_params[0].len = 2; - join_pref_params[0].rssi_gain = BRCMF_JOIN_PREF_RSSI_BOOST; - join_pref_params[0].band = WLC_BAND_5G; - join_pref_params[1].type = BRCMF_JOIN_PREF_RSSI; - join_pref_params[1].len = 2; - join_pref_params[1].rssi_gain = 0; - join_pref_params[1].band = 0; - err = brcmf_fil_iovar_data_set(ifp, "join_pref", join_pref_params, - sizeof(join_pref_params)); - if (err) - brcmf_err("Set join_pref error (%d)\n", err); - - /* Setup event_msgs, enable E_IF */ - err = brcmf_fil_iovar_data_get(ifp, "event_msgs", eventmask, - BRCMF_EVENTING_MASK_LEN); - if (err) { - brcmf_err("Get event_msgs error (%d)\n", err); - goto done; - } - setbit(eventmask, BRCMF_E_IF); - err = brcmf_fil_iovar_data_set(ifp, "event_msgs", eventmask, - BRCMF_EVENTING_MASK_LEN); - if (err) { - brcmf_err("Set event_msgs error (%d)\n", err); - goto done; - } - - /* Setup default scan channel time */ - err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_SCAN_CHANNEL_TIME, - BRCMF_DEFAULT_SCAN_CHANNEL_TIME); - if (err) { - brcmf_err("BRCMF_C_SET_SCAN_CHANNEL_TIME error (%d)\n", - err); - goto done; - } - - /* Setup default scan unassoc time */ - err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_SCAN_UNASSOC_TIME, - BRCMF_DEFAULT_SCAN_UNASSOC_TIME); - if (err) { - brcmf_err("BRCMF_C_SET_SCAN_UNASSOC_TIME error (%d)\n", - err); - goto done; - } - - /* do bus specific preinit here */ - err = brcmf_bus_preinit(ifp->drvr->bus_if); -done: - return err; -} - -#if defined(CONFIG_BRCM_TRACING) || defined(CONFIG_BRCMDBG) -void __brcmf_dbg(u32 level, const char *func, const char *fmt, ...) -{ - struct va_format vaf = { - .fmt = fmt, - }; - va_list args; - - va_start(args, fmt); - vaf.va = &args; - if (brcmf_msg_level & level) - pr_debug("%s %pV", func, &vaf); - trace_brcmf_dbg(level, func, &vaf); - va_end(args); -} -#endif -- cgit v1.2.3 From bfe8197582f369dea55e1ef431a51d0b227704c8 Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Tue, 28 Oct 2014 14:56:16 +0100 Subject: brcmfmac: (clean) Rename files wl_cfg80211 to cfg80211 Reviewed-by: Arend Van Spriel Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/Makefile | 2 +- drivers/net/wireless/brcm80211/brcmfmac/btcoex.c | 2 +- drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c | 5863 ++++++++++++++++++++ drivers/net/wireless/brcm80211/brcmfmac/cfg80211.h | 497 ++ .../net/wireless/brcm80211/brcmfmac/dhd_linux.c | 2 +- drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c | 2 +- drivers/net/wireless/brcm80211/brcmfmac/p2p.c | 2 +- drivers/net/wireless/brcm80211/brcmfmac/vendor.c | 2 +- .../net/wireless/brcm80211/brcmfmac/wl_cfg80211.c | 5862 ------------------- .../net/wireless/brcm80211/brcmfmac/wl_cfg80211.h | 496 -- 10 files changed, 6366 insertions(+), 6364 deletions(-) create mode 100644 drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c create mode 100644 drivers/net/wireless/brcm80211/brcmfmac/cfg80211.h delete mode 100644 drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c delete mode 100644 drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.h (limited to 'drivers/net/wireless/brcm80211') diff --git a/drivers/net/wireless/brcm80211/brcmfmac/Makefile b/drivers/net/wireless/brcm80211/brcmfmac/Makefile index f6e35c95b829..35b680550446 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/Makefile +++ b/drivers/net/wireless/brcm80211/brcmfmac/Makefile @@ -23,7 +23,7 @@ ccflags-y += -D__CHECK_ENDIAN__ obj-$(CONFIG_BRCMFMAC) += brcmfmac.o brcmfmac-objs += \ - wl_cfg80211.o \ + cfg80211.o \ chip.o \ fwil.o \ fweh.o \ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/btcoex.c b/drivers/net/wireless/brcm80211/brcmfmac/btcoex.c index a2f7e2ccfc84..e324414fcf6d 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/btcoex.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/btcoex.c @@ -26,7 +26,7 @@ #include "fwil_types.h" #include "btcoex.h" #include "p2p.h" -#include "wl_cfg80211.h" +#include "cfg80211.h" /* T1 start SCO/eSCO priority suppression */ #define BRCMF_BTCOEX_OPPR_WIN_TIME 2000 diff --git a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c new file mode 100644 index 000000000000..0390e57613cd --- /dev/null +++ b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c @@ -0,0 +1,5863 @@ +/* + * Copyright (c) 2010 Broadcom Corporation + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* Toplevel file. Relies on dhd_linux.c to send commands to the dongle. */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include "dhd.h" +#include "debug.h" +#include "tracepoint.h" +#include "fwil_types.h" +#include "p2p.h" +#include "btcoex.h" +#include "cfg80211.h" +#include "feature.h" +#include "fwil.h" +#include "proto.h" +#include "vendor.h" +#include "bus.h" + +#define BRCMF_SCAN_IE_LEN_MAX 2048 +#define BRCMF_PNO_VERSION 2 +#define BRCMF_PNO_TIME 30 +#define BRCMF_PNO_REPEAT 4 +#define BRCMF_PNO_FREQ_EXPO_MAX 3 +#define BRCMF_PNO_MAX_PFN_COUNT 16 +#define BRCMF_PNO_ENABLE_ADAPTSCAN_BIT 6 +#define BRCMF_PNO_HIDDEN_BIT 2 +#define BRCMF_PNO_WPA_AUTH_ANY 0xFFFFFFFF +#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 */ +#define WME_OUI_TYPE 2 +#define WPS_OUI_TYPE 4 + +#define VS_IE_FIXED_HDR_LEN 6 +#define WPA_IE_VERSION_LEN 2 +#define WPA_IE_MIN_OUI_LEN 4 +#define WPA_IE_SUITE_COUNT_LEN 2 + +#define WPA_CIPHER_NONE 0 /* None */ +#define WPA_CIPHER_WEP_40 1 /* WEP (40-bit) */ +#define WPA_CIPHER_TKIP 2 /* TKIP: default for WPA */ +#define WPA_CIPHER_AES_CCM 4 /* AES (CCM) */ +#define WPA_CIPHER_WEP_104 5 /* WEP (104-bit) */ + +#define RSN_AKM_NONE 0 /* None (IBSS) */ +#define RSN_AKM_UNSPECIFIED 1 /* Over 802.1x */ +#define RSN_AKM_PSK 2 /* Pre-shared Key */ +#define RSN_CAP_LEN 2 /* Length of RSN capabilities */ +#define RSN_CAP_PTK_REPLAY_CNTR_MASK 0x000C + +#define VNDR_IE_CMD_LEN 4 /* length of the set command + * string :"add", "del" (+ NUL) + */ +#define VNDR_IE_COUNT_OFFSET 4 +#define VNDR_IE_PKTFLAG_OFFSET 8 +#define VNDR_IE_VSIE_OFFSET 12 +#define VNDR_IE_HDR_SIZE 12 +#define VNDR_IE_PARSE_LIMIT 5 + +#define DOT11_MGMT_HDR_LEN 24 /* d11 management header len */ +#define DOT11_BCN_PRB_FIXED_LEN 12 /* beacon/probe fixed length */ + +#define BRCMF_SCAN_JOIN_ACTIVE_DWELL_TIME_MS 320 +#define BRCMF_SCAN_JOIN_PASSIVE_DWELL_TIME_MS 400 +#define BRCMF_SCAN_JOIN_PROBE_INTERVAL_MS 20 + +#define BRCMF_ASSOC_PARAMS_FIXED_SIZE \ + (sizeof(struct brcmf_assoc_params_le) - sizeof(u16)) + +static bool check_vif_up(struct brcmf_cfg80211_vif *vif) +{ + if (!test_bit(BRCMF_VIF_STATUS_READY, &vif->sme_state)) { + brcmf_dbg(INFO, "device is not ready : status (%lu)\n", + vif->sme_state); + return false; + } + return true; +} + +#define RATE_TO_BASE100KBPS(rate) (((rate) * 10) / 2) +#define RATETAB_ENT(_rateid, _flags) \ + { \ + .bitrate = RATE_TO_BASE100KBPS(_rateid), \ + .hw_value = (_rateid), \ + .flags = (_flags), \ + } + +static struct ieee80211_rate __wl_rates[] = { + RATETAB_ENT(BRCM_RATE_1M, 0), + RATETAB_ENT(BRCM_RATE_2M, IEEE80211_RATE_SHORT_PREAMBLE), + RATETAB_ENT(BRCM_RATE_5M5, IEEE80211_RATE_SHORT_PREAMBLE), + RATETAB_ENT(BRCM_RATE_11M, IEEE80211_RATE_SHORT_PREAMBLE), + RATETAB_ENT(BRCM_RATE_6M, 0), + RATETAB_ENT(BRCM_RATE_9M, 0), + RATETAB_ENT(BRCM_RATE_12M, 0), + RATETAB_ENT(BRCM_RATE_18M, 0), + RATETAB_ENT(BRCM_RATE_24M, 0), + RATETAB_ENT(BRCM_RATE_36M, 0), + RATETAB_ENT(BRCM_RATE_48M, 0), + 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 + +/* Band templates duplicated per wiphy. The channel info + * is filled in after querying the device. + */ +static const struct ieee80211_supported_band __wl_band_2ghz = { + .band = IEEE80211_BAND_2GHZ, + .bitrates = wl_g_rates, + .n_bitrates = wl_g_rates_size, +}; + +static const struct ieee80211_supported_band __wl_band_5ghz_a = { + .band = IEEE80211_BAND_5GHZ, + .bitrates = wl_a_rates, + .n_bitrates = wl_a_rates_size, +}; + +/* This is to override regulatory domains defined in cfg80211 module (reg.c) + * By default world regulatory domain defined in reg.c puts the flags + * NL80211_RRF_NO_IR for 5GHz channels (for * 36..48 and 149..165). + * With respect to these flags, wpa_supplicant doesn't * start p2p + * operations on 5GHz channels. All the changes in world regulatory + * domain are to be done here. + */ +static const struct ieee80211_regdomain brcmf_regdom = { + .n_reg_rules = 4, + .alpha2 = "99", + .reg_rules = { + /* IEEE 802.11b/g, channels 1..11 */ + REG_RULE(2412-10, 2472+10, 40, 6, 20, 0), + /* If any */ + /* IEEE 802.11 channel 14 - Only JP enables + * this and for 802.11b only + */ + REG_RULE(2484-10, 2484+10, 20, 6, 20, 0), + /* IEEE 802.11a, channel 36..64 */ + REG_RULE(5150-10, 5350+10, 80, 6, 20, 0), + /* IEEE 802.11a, channel 100..165 */ + REG_RULE(5470-10, 5850+10, 80, 6, 20, 0), } +}; + +static const u32 __wl_cipher_suites[] = { + WLAN_CIPHER_SUITE_WEP40, + WLAN_CIPHER_SUITE_WEP104, + WLAN_CIPHER_SUITE_TKIP, + WLAN_CIPHER_SUITE_CCMP, + WLAN_CIPHER_SUITE_AES_CMAC, +}; + +/* Vendor specific ie. id = 221, oui and type defines exact ie */ +struct brcmf_vs_tlv { + u8 id; + u8 len; + u8 oui[3]; + u8 oui_type; +}; + +struct parsed_vndr_ie_info { + u8 *ie_ptr; + u32 ie_len; /* total length including id & length field */ + struct brcmf_vs_tlv vndrie; +}; + +struct parsed_vndr_ies { + u32 count; + struct parsed_vndr_ie_info ie_info[VNDR_IE_PARSE_LIMIT]; +}; + +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) +{ + struct brcmu_chan ch_inf; + s32 primary_offset; + + brcmf_dbg(TRACE, "chandef: control %d center %d width %d\n", + ch->chan->center_freq, ch->center_freq1, ch->width); + ch_inf.chnum = ieee80211_frequency_to_channel(ch->center_freq1); + primary_offset = ch->center_freq1 - ch->chan->center_freq; + switch (ch->width) { + case NL80211_CHAN_WIDTH_20: + ch_inf.bw = BRCMU_CHAN_BW_20; + WARN_ON(primary_offset != 0); + break; + case NL80211_CHAN_WIDTH_40: + ch_inf.bw = BRCMU_CHAN_BW_40; + if (primary_offset < 0) + ch_inf.sb = BRCMU_CHAN_SB_U; + else + ch_inf.sb = BRCMU_CHAN_SB_L; + break; + case NL80211_CHAN_WIDTH_80: + ch_inf.bw = BRCMU_CHAN_BW_80; + if (primary_offset < 0) { + if (primary_offset < -CH_10MHZ_APART) + ch_inf.sb = BRCMU_CHAN_SB_UU; + else + ch_inf.sb = BRCMU_CHAN_SB_UL; + } else { + if (primary_offset > CH_10MHZ_APART) + ch_inf.sb = BRCMU_CHAN_SB_LL; + else + ch_inf.sb = BRCMU_CHAN_SB_LU; + } + break; + default: + WARN_ON_ONCE(1); + } + switch (ch->chan->band) { + case IEEE80211_BAND_2GHZ: + ch_inf.band = BRCMU_CHAN_BAND_2G; + break; + case IEEE80211_BAND_5GHZ: + ch_inf.band = BRCMU_CHAN_BAND_5G; + break; + default: + WARN_ON_ONCE(1); + } + d11inf->encchspec(&ch_inf); + + return ch_inf.chspec; +} + +u16 channel_to_chanspec(struct brcmu_d11inf *d11inf, + struct ieee80211_channel *ch) +{ + struct brcmu_chan ch_inf; + + ch_inf.chnum = ieee80211_frequency_to_channel(ch->center_freq); + ch_inf.bw = BRCMU_CHAN_BW_20; + d11inf->encchspec(&ch_inf); + + return ch_inf.chspec; +} + +/* Traverse a string of 1-byte tag/1-byte length/variable-length value + * triples, returning a pointer to the substring whose first element + * matches tag + */ +const struct brcmf_tlv * +brcmf_parse_tlvs(const void *buf, int buflen, uint key) +{ + const struct brcmf_tlv *elt = buf; + int totlen = buflen; + + /* find tagged parameter */ + while (totlen >= TLV_HDR_LEN) { + int len = elt->len; + + /* validate remaining totlen */ + if ((elt->id == key) && (totlen >= (len + TLV_HDR_LEN))) + return elt; + + elt = (struct brcmf_tlv *)((u8 *)elt + (len + TLV_HDR_LEN)); + totlen -= (len + TLV_HDR_LEN); + } + + return NULL; +} + +/* Is any of the tlvs the expected entry? If + * not update the tlvs buffer pointer/length. + */ +static bool +brcmf_tlv_has_ie(const u8 *ie, const u8 **tlvs, u32 *tlvs_len, + const u8 *oui, u32 oui_len, u8 type) +{ + /* If the contents match the OUI and the type */ + if (ie[TLV_LEN_OFF] >= oui_len + 1 && + !memcmp(&ie[TLV_BODY_OFF], oui, oui_len) && + type == ie[TLV_BODY_OFF + oui_len]) { + return true; + } + + if (tlvs == NULL) + return false; + /* point to the next ie */ + ie += ie[TLV_LEN_OFF] + TLV_HDR_LEN; + /* calculate the length of the rest of the buffer */ + *tlvs_len -= (int)(ie - *tlvs); + /* update the pointer to the start of the buffer */ + *tlvs = ie; + + return false; +} + +static struct brcmf_vs_tlv * +brcmf_find_wpaie(const u8 *parse, u32 len) +{ + const struct brcmf_tlv *ie; + + while ((ie = brcmf_parse_tlvs(parse, len, WLAN_EID_VENDOR_SPECIFIC))) { + if (brcmf_tlv_has_ie((const u8 *)ie, &parse, &len, + WPA_OUI, TLV_OUI_LEN, WPA_OUI_TYPE)) + return (struct brcmf_vs_tlv *)ie; + } + return NULL; +} + +static struct brcmf_vs_tlv * +brcmf_find_wpsie(const u8 *parse, u32 len) +{ + const struct brcmf_tlv *ie; + + while ((ie = brcmf_parse_tlvs(parse, len, WLAN_EID_VENDOR_SPECIFIC))) { + if (brcmf_tlv_has_ie((u8 *)ie, &parse, &len, + WPA_OUI, TLV_OUI_LEN, WPS_OUI_TYPE)) + return (struct brcmf_vs_tlv *)ie; + } + return NULL; +} + + +static void convert_key_from_CPU(struct brcmf_wsec_key *key, + struct brcmf_wsec_key_le *key_le) +{ + key_le->index = cpu_to_le32(key->index); + key_le->len = cpu_to_le32(key->len); + key_le->algo = cpu_to_le32(key->algo); + key_le->flags = cpu_to_le32(key->flags); + key_le->rxiv.hi = cpu_to_le32(key->rxiv.hi); + key_le->rxiv.lo = cpu_to_le16(key->rxiv.lo); + key_le->iv_initialized = cpu_to_le32(key->iv_initialized); + memcpy(key_le->data, key->data, sizeof(key->data)); + memcpy(key_le->ea, key->ea, sizeof(key->ea)); +} + +static int +send_key_to_dongle(struct net_device *ndev, struct brcmf_wsec_key *key) +{ + int err; + struct brcmf_wsec_key_le key_le; + + convert_key_from_CPU(key, &key_le); + + brcmf_netdev_wait_pend8021x(ndev); + + err = brcmf_fil_bsscfg_data_set(netdev_priv(ndev), "wsec_key", &key_le, + sizeof(key_le)); + + if (err) + brcmf_err("wsec_key error (%d)\n", err); + return err; +} + +static s32 +brcmf_configure_arp_offload(struct brcmf_if *ifp, bool enable) +{ + s32 err; + u32 mode; + + if (enable) + mode = BRCMF_ARP_OL_AGENT | BRCMF_ARP_OL_PEER_AUTO_REPLY; + else + mode = 0; + + /* Try to set and enable ARP offload feature, this may fail, then it */ + /* is simply not supported and err 0 will be returned */ + err = brcmf_fil_iovar_int_set(ifp, "arp_ol", mode); + if (err) { + brcmf_dbg(TRACE, "failed to set ARP offload mode to 0x%x, err = %d\n", + mode, err); + err = 0; + } else { + err = brcmf_fil_iovar_int_set(ifp, "arpoe", enable); + if (err) { + brcmf_dbg(TRACE, "failed to configure (%d) ARP offload err = %d\n", + enable, err); + err = 0; + } else + brcmf_dbg(TRACE, "successfully configured (%d) ARP offload to 0x%x\n", + enable, mode); + } + + return err; +} + +static void +brcmf_cfg80211_update_proto_addr_mode(struct wireless_dev *wdev) +{ + struct brcmf_cfg80211_vif *vif; + struct brcmf_if *ifp; + + vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev); + ifp = vif->ifp; + + if ((wdev->iftype == NL80211_IFTYPE_ADHOC) || + (wdev->iftype == NL80211_IFTYPE_AP) || + (wdev->iftype == NL80211_IFTYPE_P2P_GO)) + brcmf_proto_configure_addr_mode(ifp->drvr, ifp->ifidx, + ADDR_DIRECT); + else + brcmf_proto_configure_addr_mode(ifp->drvr, ifp->ifidx, + ADDR_INDIRECT); +} + +static bool brcmf_is_apmode(struct brcmf_cfg80211_vif *vif) +{ + enum nl80211_iftype iftype; + + iftype = vif->wdev.iftype; + return iftype == NL80211_IFTYPE_AP || iftype == NL80211_IFTYPE_P2P_GO; +} + +static bool brcmf_is_ibssmode(struct brcmf_cfg80211_vif *vif) +{ + return vif->wdev.iftype == NL80211_IFTYPE_ADHOC; +} + +static struct wireless_dev *brcmf_cfg80211_add_iface(struct wiphy *wiphy, + const char *name, + enum nl80211_iftype type, + u32 *flags, + struct vif_params *params) +{ + struct wireless_dev *wdev; + + brcmf_dbg(TRACE, "enter: %s type %d\n", name, type); + switch (type) { + case NL80211_IFTYPE_ADHOC: + case NL80211_IFTYPE_STATION: + case NL80211_IFTYPE_AP: + case NL80211_IFTYPE_AP_VLAN: + case NL80211_IFTYPE_WDS: + case NL80211_IFTYPE_MONITOR: + case NL80211_IFTYPE_MESH_POINT: + return ERR_PTR(-EOPNOTSUPP); + case NL80211_IFTYPE_P2P_CLIENT: + case NL80211_IFTYPE_P2P_GO: + case NL80211_IFTYPE_P2P_DEVICE: + wdev = brcmf_p2p_add_vif(wiphy, name, type, flags, params); + if (!IS_ERR(wdev)) + brcmf_cfg80211_update_proto_addr_mode(wdev); + return wdev; + case NL80211_IFTYPE_UNSPECIFIED: + default: + return ERR_PTR(-EINVAL); + } +} + +static void brcmf_scan_config_mpc(struct brcmf_if *ifp, int mpc) +{ + if (brcmf_feat_is_quirk_enabled(ifp, BRCMF_FEAT_QUIRK_NEED_MPC)) + brcmf_set_mpc(ifp, mpc); +} + +void brcmf_set_mpc(struct brcmf_if *ifp, int mpc) +{ + s32 err = 0; + + if (check_vif_up(ifp->vif)) { + err = brcmf_fil_iovar_int_set(ifp, "mpc", mpc); + if (err) { + brcmf_err("fail to set mpc\n"); + return; + } + brcmf_dbg(INFO, "MPC : %d\n", mpc); + } +} + +s32 brcmf_notify_escan_complete(struct brcmf_cfg80211_info *cfg, + struct brcmf_if *ifp, bool aborted, + bool fw_abort) +{ + struct brcmf_scan_params_le params_le; + struct cfg80211_scan_request *scan_request; + s32 err = 0; + + brcmf_dbg(SCAN, "Enter\n"); + + /* clear scan request, because the FW abort can cause a second call */ + /* to this functon and might cause a double cfg80211_scan_done */ + scan_request = cfg->scan_request; + cfg->scan_request = NULL; + + if (timer_pending(&cfg->escan_timeout)) + del_timer_sync(&cfg->escan_timeout); + + if (fw_abort) { + /* Do a scan abort to stop the driver's scan engine */ + brcmf_dbg(SCAN, "ABORT scan in firmware\n"); + memset(¶ms_le, 0, sizeof(params_le)); + memset(params_le.bssid, 0xFF, ETH_ALEN); + params_le.bss_type = DOT11_BSSTYPE_ANY; + params_le.scan_type = 0; + params_le.channel_num = cpu_to_le32(1); + params_le.nprobes = cpu_to_le32(1); + params_le.active_time = cpu_to_le32(-1); + params_le.passive_time = cpu_to_le32(-1); + params_le.home_time = cpu_to_le32(-1); + /* Scan is aborted by setting channel_list[0] to -1 */ + params_le.channel_list[0] = cpu_to_le16(-1); + /* E-Scan (or anyother type) can be aborted by SCAN */ + err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SCAN, + ¶ms_le, sizeof(params_le)); + if (err) + brcmf_err("Scan abort failed\n"); + } + + brcmf_scan_config_mpc(ifp, 1); + + /* + * e-scan can be initiated by scheduled scan + * which takes precedence. + */ + if (cfg->sched_escan) { + brcmf_dbg(SCAN, "scheduled scan completed\n"); + cfg->sched_escan = false; + if (!aborted) + cfg80211_sched_scan_results(cfg_to_wiphy(cfg)); + } else if (scan_request) { + brcmf_dbg(SCAN, "ESCAN Completed scan: %s\n", + aborted ? "Aborted" : "Done"); + cfg80211_scan_done(scan_request, aborted); + } + if (!test_and_clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) + brcmf_dbg(SCAN, "Scan complete, probably P2P scan\n"); + + return err; +} + +static +int brcmf_cfg80211_del_iface(struct wiphy *wiphy, struct wireless_dev *wdev) +{ + struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy); + struct net_device *ndev = wdev->netdev; + + /* vif event pending in firmware */ + if (brcmf_cfg80211_vif_event_armed(cfg)) + return -EBUSY; + + if (ndev) { + if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status) && + cfg->escan_info.ifp == netdev_priv(ndev)) + brcmf_notify_escan_complete(cfg, netdev_priv(ndev), + true, true); + + brcmf_fil_iovar_int_set(netdev_priv(ndev), "mpc", 1); + } + + switch (wdev->iftype) { + case NL80211_IFTYPE_ADHOC: + case NL80211_IFTYPE_STATION: + case NL80211_IFTYPE_AP: + case NL80211_IFTYPE_AP_VLAN: + case NL80211_IFTYPE_WDS: + case NL80211_IFTYPE_MONITOR: + case NL80211_IFTYPE_MESH_POINT: + return -EOPNOTSUPP; + case NL80211_IFTYPE_P2P_CLIENT: + case NL80211_IFTYPE_P2P_GO: + case NL80211_IFTYPE_P2P_DEVICE: + return brcmf_p2p_del_vif(wiphy, wdev); + case NL80211_IFTYPE_UNSPECIFIED: + default: + return -EINVAL; + } + return -EOPNOTSUPP; +} + +static s32 +brcmf_cfg80211_change_iface(struct wiphy *wiphy, struct net_device *ndev, + enum nl80211_iftype type, u32 *flags, + struct vif_params *params) +{ + struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy); + struct brcmf_if *ifp = netdev_priv(ndev); + struct brcmf_cfg80211_vif *vif = ifp->vif; + s32 infra = 0; + s32 ap = 0; + s32 err = 0; + + brcmf_dbg(TRACE, "Enter, ndev=%p, type=%d\n", ndev, type); + + switch (type) { + case NL80211_IFTYPE_MONITOR: + case NL80211_IFTYPE_WDS: + brcmf_err("type (%d) : currently we do not support this type\n", + type); + return -EOPNOTSUPP; + case NL80211_IFTYPE_ADHOC: + 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: + case NL80211_IFTYPE_P2P_GO: + ap = 1; + break; + default: + err = -EINVAL; + goto done; + } + + if (ap) { + if (type == NL80211_IFTYPE_P2P_GO) { + brcmf_dbg(INFO, "IF Type = P2P GO\n"); + 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 { + err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_INFRA, infra); + if (err) { + brcmf_err("WLC_SET_INFRA error (%d)\n", err); + err = -EAGAIN; + goto done; + } + brcmf_dbg(INFO, "IF Type = %s\n", brcmf_is_ibssmode(vif) ? + "Adhoc" : "Infra"); + } + ndev->ieee80211_ptr->iftype = type; + + brcmf_cfg80211_update_proto_addr_mode(&vif->wdev); + +done: + brcmf_dbg(TRACE, "Exit\n"); + + return err; +} + +static void brcmf_escan_prep(struct brcmf_cfg80211_info *cfg, + struct brcmf_scan_params_le *params_le, + struct cfg80211_scan_request *request) +{ + u32 n_ssids; + u32 n_channels; + s32 i; + s32 offset; + u16 chanspec; + char *ptr; + struct brcmf_ssid_le ssid_le; + + memset(params_le->bssid, 0xFF, ETH_ALEN); + params_le->bss_type = DOT11_BSSTYPE_ANY; + params_le->scan_type = 0; + params_le->channel_num = 0; + params_le->nprobes = cpu_to_le32(-1); + params_le->active_time = cpu_to_le32(-1); + params_le->passive_time = cpu_to_le32(-1); + params_le->home_time = cpu_to_le32(-1); + memset(¶ms_le->ssid_le, 0, sizeof(params_le->ssid_le)); + + /* if request is null exit so it will be all channel broadcast scan */ + if (!request) + return; + + n_ssids = request->n_ssids; + n_channels = request->n_channels; + /* Copy channel array if applicable */ + brcmf_dbg(SCAN, "### List of channelspecs to scan ### %d\n", + n_channels); + if (n_channels > 0) { + for (i = 0; i < n_channels; i++) { + chanspec = channel_to_chanspec(&cfg->d11inf, + request->channels[i]); + brcmf_dbg(SCAN, "Chan : %d, Channel spec: %x\n", + request->channels[i]->hw_value, chanspec); + params_le->channel_list[i] = cpu_to_le16(chanspec); + } + } else { + brcmf_dbg(SCAN, "Scanning all channels\n"); + } + /* Copy ssid array if applicable */ + brcmf_dbg(SCAN, "### List of SSIDs to scan ### %d\n", n_ssids); + if (n_ssids > 0) { + offset = offsetof(struct brcmf_scan_params_le, channel_list) + + n_channels * sizeof(u16); + offset = roundup(offset, sizeof(u32)); + ptr = (char *)params_le + offset; + for (i = 0; i < n_ssids; i++) { + memset(&ssid_le, 0, sizeof(ssid_le)); + ssid_le.SSID_len = + cpu_to_le32(request->ssids[i].ssid_len); + memcpy(ssid_le.SSID, request->ssids[i].ssid, + request->ssids[i].ssid_len); + if (!ssid_le.SSID_len) + brcmf_dbg(SCAN, "%d: Broadcast scan\n", i); + else + brcmf_dbg(SCAN, "%d: scan for %s size =%d\n", + i, ssid_le.SSID, ssid_le.SSID_len); + memcpy(ptr, &ssid_le, sizeof(ssid_le)); + ptr += sizeof(ssid_le); + } + } else { + brcmf_dbg(SCAN, "Broadcast scan %p\n", request->ssids); + if ((request->ssids) && request->ssids->ssid_len) { + brcmf_dbg(SCAN, "SSID %s len=%d\n", + params_le->ssid_le.SSID, + request->ssids->ssid_len); + params_le->ssid_le.SSID_len = + cpu_to_le32(request->ssids->ssid_len); + memcpy(¶ms_le->ssid_le.SSID, request->ssids->ssid, + request->ssids->ssid_len); + } + } + /* Adding mask to channel numbers */ + params_le->channel_num = + cpu_to_le32((n_ssids << BRCMF_SCAN_PARAMS_NSSID_SHIFT) | + (n_channels & BRCMF_SCAN_PARAMS_COUNT_MASK)); +} + +static s32 +brcmf_run_escan(struct brcmf_cfg80211_info *cfg, struct brcmf_if *ifp, + struct cfg80211_scan_request *request, u16 action) +{ + s32 params_size = BRCMF_SCAN_PARAMS_FIXED_SIZE + + offsetof(struct brcmf_escan_params_le, params_le); + struct brcmf_escan_params_le *params; + s32 err = 0; + + brcmf_dbg(SCAN, "E-SCAN START\n"); + + if (request != NULL) { + /* Allocate space for populating ssids in struct */ + params_size += sizeof(u32) * ((request->n_channels + 1) / 2); + + /* Allocate space for populating ssids in struct */ + params_size += sizeof(struct brcmf_ssid) * request->n_ssids; + } + + params = kzalloc(params_size, GFP_KERNEL); + if (!params) { + err = -ENOMEM; + goto exit; + } + BUG_ON(params_size + sizeof("escan") >= BRCMF_DCMD_MEDLEN); + brcmf_escan_prep(cfg, ¶ms->params_le, request); + params->version = cpu_to_le32(BRCMF_ESCAN_REQ_VERSION); + params->action = cpu_to_le16(action); + params->sync_id = cpu_to_le16(0x1234); + + err = brcmf_fil_iovar_data_set(ifp, "escan", params, params_size); + if (err) { + if (err == -EBUSY) + brcmf_dbg(INFO, "system busy : escan canceled\n"); + else + brcmf_err("error (%d)\n", err); + } + + kfree(params); +exit: + return err; +} + +static s32 +brcmf_do_escan(struct brcmf_cfg80211_info *cfg, struct wiphy *wiphy, + struct brcmf_if *ifp, struct cfg80211_scan_request *request) +{ + s32 err; + u32 passive_scan; + struct brcmf_scan_results *results; + struct escan_info *escan = &cfg->escan_info; + + brcmf_dbg(SCAN, "Enter\n"); + escan->ifp = ifp; + escan->wiphy = wiphy; + escan->escan_state = WL_ESCAN_STATE_SCANNING; + passive_scan = cfg->active_scan ? 0 : 1; + err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PASSIVE_SCAN, + passive_scan); + if (err) { + brcmf_err("error (%d)\n", err); + return err; + } + brcmf_scan_config_mpc(ifp, 0); + results = (struct brcmf_scan_results *)cfg->escan_info.escan_buf; + results->version = 0; + results->count = 0; + results->buflen = WL_ESCAN_RESULTS_FIXED_SIZE; + + err = escan->run(cfg, ifp, request, WL_ESCAN_ACTION_START); + if (err) + brcmf_scan_config_mpc(ifp, 1); + return err; +} + +static s32 +brcmf_cfg80211_escan(struct wiphy *wiphy, struct brcmf_cfg80211_vif *vif, + struct cfg80211_scan_request *request, + struct cfg80211_ssid *this_ssid) +{ + struct brcmf_if *ifp = vif->ifp; + struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); + struct cfg80211_ssid *ssids; + struct brcmf_cfg80211_scan_req *sr = &cfg->scan_req_int; + u32 passive_scan; + bool escan_req; + bool spec_scan; + s32 err; + u32 SSID_len; + + brcmf_dbg(SCAN, "START ESCAN\n"); + + if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) { + brcmf_err("Scanning already: status (%lu)\n", cfg->scan_status); + return -EAGAIN; + } + if (test_bit(BRCMF_SCAN_STATUS_ABORT, &cfg->scan_status)) { + brcmf_err("Scanning being aborted: status (%lu)\n", + cfg->scan_status); + return -EAGAIN; + } + if (test_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status)) { + brcmf_err("Scanning suppressed: status (%lu)\n", + cfg->scan_status); + return -EAGAIN; + } + if (test_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state)) { + brcmf_err("Connecting: status (%lu)\n", ifp->vif->sme_state); + return -EAGAIN; + } + + /* If scan req comes for p2p0, send it over primary I/F */ + if (vif == cfg->p2p.bss_idx[P2PAPI_BSSCFG_DEVICE].vif) + vif = cfg->p2p.bss_idx[P2PAPI_BSSCFG_PRIMARY].vif; + + /* Arm scan timeout timer */ + mod_timer(&cfg->escan_timeout, jiffies + + WL_ESCAN_TIMER_INTERVAL_MS * HZ / 1000); + + escan_req = false; + if (request) { + /* scan bss */ + ssids = request->ssids; + escan_req = true; + } else { + /* scan in ibss */ + /* we don't do escan in ibss */ + ssids = this_ssid; + } + + cfg->scan_request = request; + set_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status); + if (escan_req) { + cfg->escan_info.run = brcmf_run_escan; + err = brcmf_p2p_scan_prep(wiphy, request, vif); + if (err) + goto scan_out; + + err = brcmf_do_escan(cfg, wiphy, vif->ifp, request); + if (err) + goto scan_out; + } else { + brcmf_dbg(SCAN, "ssid \"%s\", ssid_len (%d)\n", + ssids->ssid, ssids->ssid_len); + memset(&sr->ssid_le, 0, sizeof(sr->ssid_le)); + SSID_len = min_t(u8, sizeof(sr->ssid_le.SSID), ssids->ssid_len); + sr->ssid_le.SSID_len = cpu_to_le32(0); + spec_scan = false; + if (SSID_len) { + memcpy(sr->ssid_le.SSID, ssids->ssid, SSID_len); + sr->ssid_le.SSID_len = cpu_to_le32(SSID_len); + spec_scan = true; + } else + brcmf_dbg(SCAN, "Broadcast scan\n"); + + passive_scan = cfg->active_scan ? 0 : 1; + err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PASSIVE_SCAN, + passive_scan); + if (err) { + brcmf_err("WLC_SET_PASSIVE_SCAN error (%d)\n", err); + goto scan_out; + } + brcmf_scan_config_mpc(ifp, 0); + err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SCAN, + &sr->ssid_le, sizeof(sr->ssid_le)); + if (err) { + if (err == -EBUSY) + brcmf_dbg(INFO, "BUSY: scan for \"%s\" canceled\n", + sr->ssid_le.SSID); + else + brcmf_err("WLC_SCAN error (%d)\n", err); + + brcmf_scan_config_mpc(ifp, 1); + goto scan_out; + } + } + + return 0; + +scan_out: + clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status); + if (timer_pending(&cfg->escan_timeout)) + del_timer_sync(&cfg->escan_timeout); + cfg->scan_request = NULL; + return err; +} + +static s32 +brcmf_cfg80211_scan(struct wiphy *wiphy, struct cfg80211_scan_request *request) +{ + struct brcmf_cfg80211_vif *vif; + s32 err = 0; + + brcmf_dbg(TRACE, "Enter\n"); + vif = container_of(request->wdev, struct brcmf_cfg80211_vif, wdev); + if (!check_vif_up(vif)) + return -EIO; + + err = brcmf_cfg80211_escan(wiphy, vif, request, NULL); + + if (err) + brcmf_err("scan error (%d)\n", err); + + brcmf_dbg(TRACE, "Exit\n"); + return err; +} + +static s32 brcmf_set_rts(struct net_device *ndev, u32 rts_threshold) +{ + s32 err = 0; + + err = brcmf_fil_iovar_int_set(netdev_priv(ndev), "rtsthresh", + rts_threshold); + if (err) + brcmf_err("Error (%d)\n", err); + + return err; +} + +static s32 brcmf_set_frag(struct net_device *ndev, u32 frag_threshold) +{ + s32 err = 0; + + err = brcmf_fil_iovar_int_set(netdev_priv(ndev), "fragthresh", + frag_threshold); + if (err) + brcmf_err("Error (%d)\n", err); + + return err; +} + +static s32 brcmf_set_retry(struct net_device *ndev, u32 retry, bool l) +{ + s32 err = 0; + u32 cmd = (l ? BRCMF_C_SET_LRL : BRCMF_C_SET_SRL); + + err = brcmf_fil_cmd_int_set(netdev_priv(ndev), cmd, retry); + if (err) { + brcmf_err("cmd (%d) , error (%d)\n", cmd, err); + return err; + } + return err; +} + +static s32 brcmf_cfg80211_set_wiphy_params(struct wiphy *wiphy, u32 changed) +{ + struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); + struct net_device *ndev = cfg_to_ndev(cfg); + struct brcmf_if *ifp = netdev_priv(ndev); + s32 err = 0; + + brcmf_dbg(TRACE, "Enter\n"); + if (!check_vif_up(ifp->vif)) + return -EIO; + + if (changed & WIPHY_PARAM_RTS_THRESHOLD && + (cfg->conf->rts_threshold != wiphy->rts_threshold)) { + cfg->conf->rts_threshold = wiphy->rts_threshold; + err = brcmf_set_rts(ndev, cfg->conf->rts_threshold); + if (!err) + goto done; + } + if (changed & WIPHY_PARAM_FRAG_THRESHOLD && + (cfg->conf->frag_threshold != wiphy->frag_threshold)) { + cfg->conf->frag_threshold = wiphy->frag_threshold; + err = brcmf_set_frag(ndev, cfg->conf->frag_threshold); + if (!err) + goto done; + } + if (changed & WIPHY_PARAM_RETRY_LONG + && (cfg->conf->retry_long != wiphy->retry_long)) { + cfg->conf->retry_long = wiphy->retry_long; + err = brcmf_set_retry(ndev, cfg->conf->retry_long, true); + if (!err) + goto done; + } + if (changed & WIPHY_PARAM_RETRY_SHORT + && (cfg->conf->retry_short != wiphy->retry_short)) { + cfg->conf->retry_short = wiphy->retry_short; + err = brcmf_set_retry(ndev, cfg->conf->retry_short, false); + if (!err) + goto done; + } + +done: + brcmf_dbg(TRACE, "Exit\n"); + return err; +} + +static void brcmf_init_prof(struct brcmf_cfg80211_profile *prof) +{ + memset(prof, 0, sizeof(*prof)); +} + +static void brcmf_link_down(struct brcmf_cfg80211_vif *vif) +{ + struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(vif->wdev.wiphy); + s32 err = 0; + + brcmf_dbg(TRACE, "Enter\n"); + + if (test_bit(BRCMF_VIF_STATUS_CONNECTED, &vif->sme_state)) { + brcmf_dbg(INFO, "Call WLC_DISASSOC to stop excess roaming\n "); + err = brcmf_fil_cmd_data_set(vif->ifp, + BRCMF_C_DISASSOC, NULL, 0); + if (err) { + brcmf_err("WLC_DISASSOC failed (%d)\n", err); + } + clear_bit(BRCMF_VIF_STATUS_CONNECTED, &vif->sme_state); + cfg80211_disconnected(vif->wdev.netdev, 0, NULL, 0, GFP_KERNEL); + + } + clear_bit(BRCMF_VIF_STATUS_CONNECTING, &vif->sme_state); + clear_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status); + brcmf_btcoex_set_mode(vif, BRCMF_BTCOEX_ENABLED, 0); + brcmf_dbg(TRACE, "Exit\n"); +} + +static s32 +brcmf_cfg80211_join_ibss(struct wiphy *wiphy, struct net_device *ndev, + struct cfg80211_ibss_params *params) +{ + struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); + struct brcmf_if *ifp = netdev_priv(ndev); + struct brcmf_cfg80211_profile *profile = &ifp->vif->profile; + struct brcmf_join_params join_params; + size_t join_params_size = 0; + s32 err = 0; + s32 wsec = 0; + s32 bcnprd; + u16 chanspec; + + brcmf_dbg(TRACE, "Enter\n"); + if (!check_vif_up(ifp->vif)) + return -EIO; + + if (params->ssid) + brcmf_dbg(CONN, "SSID: %s\n", params->ssid); + else { + brcmf_dbg(CONN, "SSID: NULL, Not supported\n"); + return -EOPNOTSUPP; + } + + set_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state); + + if (params->bssid) + brcmf_dbg(CONN, "BSSID: %pM\n", params->bssid); + else + brcmf_dbg(CONN, "No BSSID specified\n"); + + if (params->chandef.chan) + brcmf_dbg(CONN, "channel: %d\n", + params->chandef.chan->center_freq); + else + brcmf_dbg(CONN, "no channel specified\n"); + + if (params->channel_fixed) + brcmf_dbg(CONN, "fixed channel required\n"); + else + brcmf_dbg(CONN, "no fixed channel required\n"); + + if (params->ie && params->ie_len) + brcmf_dbg(CONN, "ie len: %d\n", params->ie_len); + else + brcmf_dbg(CONN, "no ie specified\n"); + + if (params->beacon_interval) + brcmf_dbg(CONN, "beacon interval: %d\n", + params->beacon_interval); + else + brcmf_dbg(CONN, "no beacon interval specified\n"); + + if (params->basic_rates) + brcmf_dbg(CONN, "basic rates: %08X\n", params->basic_rates); + else + brcmf_dbg(CONN, "no basic rates specified\n"); + + if (params->privacy) + brcmf_dbg(CONN, "privacy required\n"); + else + brcmf_dbg(CONN, "no privacy required\n"); + + /* Configure Privacy for starter */ + if (params->privacy) + wsec |= WEP_ENABLED; + + err = brcmf_fil_iovar_int_set(ifp, "wsec", wsec); + if (err) { + brcmf_err("wsec failed (%d)\n", err); + goto done; + } + + /* Configure Beacon Interval for starter */ + if (params->beacon_interval) + bcnprd = params->beacon_interval; + else + bcnprd = 100; + + err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_BCNPRD, bcnprd); + if (err) { + brcmf_err("WLC_SET_BCNPRD failed (%d)\n", err); + goto done; + } + + /* Configure required join parameter */ + memset(&join_params, 0, sizeof(struct brcmf_join_params)); + + /* SSID */ + profile->ssid.SSID_len = min_t(u32, params->ssid_len, 32); + memcpy(profile->ssid.SSID, params->ssid, profile->ssid.SSID_len); + memcpy(join_params.ssid_le.SSID, params->ssid, profile->ssid.SSID_len); + join_params.ssid_le.SSID_len = cpu_to_le32(profile->ssid.SSID_len); + join_params_size = sizeof(join_params.ssid_le); + + /* BSSID */ + if (params->bssid) { + memcpy(join_params.params_le.bssid, params->bssid, ETH_ALEN); + join_params_size = sizeof(join_params.ssid_le) + + BRCMF_ASSOC_PARAMS_FIXED_SIZE; + memcpy(profile->bssid, params->bssid, ETH_ALEN); + } else { + memset(join_params.params_le.bssid, 0xFF, ETH_ALEN); + memset(profile->bssid, 0, ETH_ALEN); + } + + /* Channel */ + if (params->chandef.chan) { + u32 target_channel; + + cfg->channel = + ieee80211_frequency_to_channel( + params->chandef.chan->center_freq); + if (params->channel_fixed) { + /* adding chanspec */ + chanspec = chandef_to_chanspec(&cfg->d11inf, + ¶ms->chandef); + join_params.params_le.chanspec_list[0] = + cpu_to_le16(chanspec); + join_params.params_le.chanspec_num = cpu_to_le32(1); + join_params_size += sizeof(join_params.params_le); + } + + /* set channel for starter */ + target_channel = cfg->channel; + err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_CHANNEL, + target_channel); + if (err) { + brcmf_err("WLC_SET_CHANNEL failed (%d)\n", err); + goto done; + } + } else + cfg->channel = 0; + + cfg->ibss_starter = false; + + + err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SSID, + &join_params, join_params_size); + if (err) { + brcmf_err("WLC_SET_SSID failed (%d)\n", err); + goto done; + } + +done: + if (err) + clear_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state); + brcmf_dbg(TRACE, "Exit\n"); + return err; +} + +static s32 +brcmf_cfg80211_leave_ibss(struct wiphy *wiphy, struct net_device *ndev) +{ + struct brcmf_if *ifp = netdev_priv(ndev); + + brcmf_dbg(TRACE, "Enter\n"); + if (!check_vif_up(ifp->vif)) + return -EIO; + + brcmf_link_down(ifp->vif); + + brcmf_dbg(TRACE, "Exit\n"); + + return 0; +} + +static s32 brcmf_set_wpa_version(struct net_device *ndev, + struct cfg80211_connect_params *sme) +{ + struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev); + struct brcmf_cfg80211_security *sec; + s32 val = 0; + s32 err = 0; + + if (sme->crypto.wpa_versions & NL80211_WPA_VERSION_1) + val = WPA_AUTH_PSK | WPA_AUTH_UNSPECIFIED; + else if (sme->crypto.wpa_versions & NL80211_WPA_VERSION_2) + val = WPA2_AUTH_PSK | WPA2_AUTH_UNSPECIFIED; + else + val = WPA_AUTH_DISABLED; + brcmf_dbg(CONN, "setting wpa_auth to 0x%0x\n", val); + err = brcmf_fil_bsscfg_int_set(netdev_priv(ndev), "wpa_auth", val); + if (err) { + brcmf_err("set wpa_auth failed (%d)\n", err); + return err; + } + sec = &profile->sec; + sec->wpa_versions = sme->crypto.wpa_versions; + return err; +} + +static s32 brcmf_set_auth_type(struct net_device *ndev, + struct cfg80211_connect_params *sme) +{ + struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev); + struct brcmf_cfg80211_security *sec; + s32 val = 0; + s32 err = 0; + + switch (sme->auth_type) { + case NL80211_AUTHTYPE_OPEN_SYSTEM: + val = 0; + brcmf_dbg(CONN, "open system\n"); + break; + case NL80211_AUTHTYPE_SHARED_KEY: + val = 1; + brcmf_dbg(CONN, "shared key\n"); + break; + case NL80211_AUTHTYPE_AUTOMATIC: + val = 2; + brcmf_dbg(CONN, "automatic\n"); + break; + case NL80211_AUTHTYPE_NETWORK_EAP: + brcmf_dbg(CONN, "network eap\n"); + default: + val = 2; + brcmf_err("invalid auth type (%d)\n", sme->auth_type); + break; + } + + err = brcmf_fil_bsscfg_int_set(netdev_priv(ndev), "auth", val); + if (err) { + brcmf_err("set auth failed (%d)\n", err); + return err; + } + sec = &profile->sec; + sec->auth_type = sme->auth_type; + return err; +} + +static s32 +brcmf_set_wsec_mode(struct net_device *ndev, + struct cfg80211_connect_params *sme, bool mfp) +{ + struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev); + struct brcmf_cfg80211_security *sec; + s32 pval = 0; + s32 gval = 0; + s32 wsec; + s32 err = 0; + + if (sme->crypto.n_ciphers_pairwise) { + switch (sme->crypto.ciphers_pairwise[0]) { + case WLAN_CIPHER_SUITE_WEP40: + case WLAN_CIPHER_SUITE_WEP104: + pval = WEP_ENABLED; + break; + case WLAN_CIPHER_SUITE_TKIP: + pval = TKIP_ENABLED; + break; + case WLAN_CIPHER_SUITE_CCMP: + pval = AES_ENABLED; + break; + case WLAN_CIPHER_SUITE_AES_CMAC: + pval = AES_ENABLED; + break; + default: + brcmf_err("invalid cipher pairwise (%d)\n", + sme->crypto.ciphers_pairwise[0]); + return -EINVAL; + } + } + if (sme->crypto.cipher_group) { + switch (sme->crypto.cipher_group) { + case WLAN_CIPHER_SUITE_WEP40: + case WLAN_CIPHER_SUITE_WEP104: + gval = WEP_ENABLED; + break; + case WLAN_CIPHER_SUITE_TKIP: + gval = TKIP_ENABLED; + break; + case WLAN_CIPHER_SUITE_CCMP: + gval = AES_ENABLED; + break; + case WLAN_CIPHER_SUITE_AES_CMAC: + gval = AES_ENABLED; + break; + default: + brcmf_err("invalid cipher group (%d)\n", + sme->crypto.cipher_group); + return -EINVAL; + } + } + + brcmf_dbg(CONN, "pval (%d) gval (%d)\n", pval, gval); + /* In case of privacy, but no security and WPS then simulate */ + /* setting AES. WPS-2.0 allows no security */ + if (brcmf_find_wpsie(sme->ie, sme->ie_len) && !pval && !gval && + sme->privacy) + pval = AES_ENABLED; + + if (mfp) + wsec = pval | gval | MFP_CAPABLE; + else + wsec = pval | gval; + err = brcmf_fil_bsscfg_int_set(netdev_priv(ndev), "wsec", wsec); + if (err) { + brcmf_err("error (%d)\n", err); + return err; + } + + sec = &profile->sec; + sec->cipher_pairwise = sme->crypto.ciphers_pairwise[0]; + sec->cipher_group = sme->crypto.cipher_group; + + return err; +} + +static s32 +brcmf_set_key_mgmt(struct net_device *ndev, struct cfg80211_connect_params *sme) +{ + struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev); + struct brcmf_cfg80211_security *sec; + s32 val = 0; + s32 err = 0; + + if (sme->crypto.n_akm_suites) { + err = brcmf_fil_bsscfg_int_get(netdev_priv(ndev), + "wpa_auth", &val); + if (err) { + brcmf_err("could not get wpa_auth (%d)\n", err); + return err; + } + if (val & (WPA_AUTH_PSK | WPA_AUTH_UNSPECIFIED)) { + switch (sme->crypto.akm_suites[0]) { + case WLAN_AKM_SUITE_8021X: + val = WPA_AUTH_UNSPECIFIED; + break; + case WLAN_AKM_SUITE_PSK: + val = WPA_AUTH_PSK; + break; + default: + brcmf_err("invalid cipher group (%d)\n", + sme->crypto.cipher_group); + return -EINVAL; + } + } else if (val & (WPA2_AUTH_PSK | WPA2_AUTH_UNSPECIFIED)) { + switch (sme->crypto.akm_suites[0]) { + case WLAN_AKM_SUITE_8021X: + val = WPA2_AUTH_UNSPECIFIED; + break; + case WLAN_AKM_SUITE_PSK: + val = WPA2_AUTH_PSK; + break; + default: + brcmf_err("invalid cipher group (%d)\n", + sme->crypto.cipher_group); + return -EINVAL; + } + } + + brcmf_dbg(CONN, "setting wpa_auth to %d\n", val); + err = brcmf_fil_bsscfg_int_set(netdev_priv(ndev), + "wpa_auth", val); + if (err) { + brcmf_err("could not set wpa_auth (%d)\n", err); + return err; + } + } + sec = &profile->sec; + sec->wpa_auth = sme->crypto.akm_suites[0]; + + return err; +} + +static s32 +brcmf_set_sharedkey(struct net_device *ndev, + struct cfg80211_connect_params *sme) +{ + struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev); + struct brcmf_cfg80211_security *sec; + struct brcmf_wsec_key key; + s32 val; + s32 err = 0; + + brcmf_dbg(CONN, "key len (%d)\n", sme->key_len); + + if (sme->key_len == 0) + return 0; + + sec = &profile->sec; + brcmf_dbg(CONN, "wpa_versions 0x%x cipher_pairwise 0x%x\n", + sec->wpa_versions, sec->cipher_pairwise); + + if (sec->wpa_versions & (NL80211_WPA_VERSION_1 | NL80211_WPA_VERSION_2)) + return 0; + + if (!(sec->cipher_pairwise & + (WLAN_CIPHER_SUITE_WEP40 | WLAN_CIPHER_SUITE_WEP104))) + return 0; + + memset(&key, 0, sizeof(key)); + key.len = (u32) sme->key_len; + key.index = (u32) sme->key_idx; + if (key.len > sizeof(key.data)) { + brcmf_err("Too long key length (%u)\n", key.len); + return -EINVAL; + } + memcpy(key.data, sme->key, key.len); + key.flags = BRCMF_PRIMARY_KEY; + switch (sec->cipher_pairwise) { + case WLAN_CIPHER_SUITE_WEP40: + key.algo = CRYPTO_ALGO_WEP1; + break; + case WLAN_CIPHER_SUITE_WEP104: + key.algo = CRYPTO_ALGO_WEP128; + break; + default: + brcmf_err("Invalid algorithm (%d)\n", + sme->crypto.ciphers_pairwise[0]); + return -EINVAL; + } + /* Set the new key/index */ + brcmf_dbg(CONN, "key length (%d) key index (%d) algo (%d)\n", + key.len, key.index, key.algo); + brcmf_dbg(CONN, "key \"%s\"\n", key.data); + err = send_key_to_dongle(ndev, &key); + if (err) + return err; + + if (sec->auth_type == NL80211_AUTHTYPE_SHARED_KEY) { + brcmf_dbg(CONN, "set auth_type to shared key\n"); + val = WL_AUTH_SHARED_KEY; /* shared key */ + err = brcmf_fil_bsscfg_int_set(netdev_priv(ndev), "auth", val); + if (err) + brcmf_err("set auth failed (%d)\n", err); + } + return err; +} + +static +enum nl80211_auth_type brcmf_war_auth_type(struct brcmf_if *ifp, + enum nl80211_auth_type type) +{ + if (type == NL80211_AUTHTYPE_AUTOMATIC && + brcmf_feat_is_quirk_enabled(ifp, BRCMF_FEAT_QUIRK_AUTO_AUTH)) { + brcmf_dbg(CONN, "WAR: use OPEN instead of AUTO\n"); + type = NL80211_AUTHTYPE_OPEN_SYSTEM; + } + return type; +} + +static s32 +brcmf_cfg80211_connect(struct wiphy *wiphy, struct net_device *ndev, + struct cfg80211_connect_params *sme) +{ + struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); + struct brcmf_if *ifp = netdev_priv(ndev); + struct brcmf_cfg80211_profile *profile = &ifp->vif->profile; + struct ieee80211_channel *chan = sme->channel; + struct brcmf_join_params join_params; + size_t join_params_size; + const struct brcmf_tlv *rsn_ie; + const struct brcmf_vs_tlv *wpa_ie; + const void *ie; + u32 ie_len; + struct brcmf_ext_join_params_le *ext_join_params; + u16 chanspec; + s32 err = 0; + + brcmf_dbg(TRACE, "Enter\n"); + if (!check_vif_up(ifp->vif)) + return -EIO; + + if (!sme->ssid) { + brcmf_err("Invalid ssid\n"); + return -EOPNOTSUPP; + } + + if (ifp->vif == cfg->p2p.bss_idx[P2PAPI_BSSCFG_PRIMARY].vif) { + /* A normal (non P2P) connection request setup. */ + ie = NULL; + ie_len = 0; + /* find the WPA_IE */ + wpa_ie = brcmf_find_wpaie((u8 *)sme->ie, sme->ie_len); + if (wpa_ie) { + ie = wpa_ie; + ie_len = wpa_ie->len + TLV_HDR_LEN; + } else { + /* find the RSN_IE */ + rsn_ie = brcmf_parse_tlvs((const u8 *)sme->ie, + sme->ie_len, + WLAN_EID_RSN); + if (rsn_ie) { + ie = rsn_ie; + ie_len = rsn_ie->len + TLV_HDR_LEN; + } + } + brcmf_fil_iovar_data_set(ifp, "wpaie", ie, ie_len); + } + + err = brcmf_vif_set_mgmt_ie(ifp->vif, BRCMF_VNDR_IE_ASSOCREQ_FLAG, + sme->ie, sme->ie_len); + if (err) + brcmf_err("Set Assoc REQ IE Failed\n"); + else + brcmf_dbg(TRACE, "Applied Vndr IEs for Assoc request\n"); + + set_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state); + + if (chan) { + cfg->channel = + ieee80211_frequency_to_channel(chan->center_freq); + chanspec = channel_to_chanspec(&cfg->d11inf, chan); + brcmf_dbg(CONN, "channel=%d, center_req=%d, chanspec=0x%04x\n", + cfg->channel, chan->center_freq, chanspec); + } else { + cfg->channel = 0; + chanspec = 0; + } + + brcmf_dbg(INFO, "ie (%p), ie_len (%zd)\n", sme->ie, sme->ie_len); + + err = brcmf_set_wpa_version(ndev, sme); + if (err) { + brcmf_err("wl_set_wpa_version failed (%d)\n", err); + goto done; + } + + sme->auth_type = brcmf_war_auth_type(ifp, sme->auth_type); + err = brcmf_set_auth_type(ndev, sme); + if (err) { + brcmf_err("wl_set_auth_type failed (%d)\n", err); + goto done; + } + + err = brcmf_set_wsec_mode(ndev, sme, sme->mfp == NL80211_MFP_REQUIRED); + if (err) { + brcmf_err("wl_set_set_cipher failed (%d)\n", err); + goto done; + } + + err = brcmf_set_key_mgmt(ndev, sme); + if (err) { + brcmf_err("wl_set_key_mgmt failed (%d)\n", err); + goto done; + } + + err = brcmf_set_sharedkey(ndev, sme); + if (err) { + brcmf_err("brcmf_set_sharedkey failed (%d)\n", err); + goto done; + } + + profile->ssid.SSID_len = min_t(u32, (u32)sizeof(profile->ssid.SSID), + (u32)sme->ssid_len); + memcpy(&profile->ssid.SSID, sme->ssid, profile->ssid.SSID_len); + if (profile->ssid.SSID_len < IEEE80211_MAX_SSID_LEN) { + profile->ssid.SSID[profile->ssid.SSID_len] = 0; + brcmf_dbg(CONN, "SSID \"%s\", len (%d)\n", profile->ssid.SSID, + profile->ssid.SSID_len); + } + + /* Join with specific BSSID and cached SSID + * If SSID is zero join based on BSSID only + */ + join_params_size = offsetof(struct brcmf_ext_join_params_le, assoc_le) + + offsetof(struct brcmf_assoc_params_le, chanspec_list); + if (cfg->channel) + join_params_size += sizeof(u16); + ext_join_params = kzalloc(join_params_size, GFP_KERNEL); + if (ext_join_params == NULL) { + err = -ENOMEM; + goto done; + } + ext_join_params->ssid_le.SSID_len = cpu_to_le32(profile->ssid.SSID_len); + memcpy(&ext_join_params->ssid_le.SSID, sme->ssid, + profile->ssid.SSID_len); + + /* Set up join scan parameters */ + ext_join_params->scan_le.scan_type = -1; + ext_join_params->scan_le.home_time = cpu_to_le32(-1); + + if (sme->bssid) + memcpy(&ext_join_params->assoc_le.bssid, sme->bssid, ETH_ALEN); + else + memset(&ext_join_params->assoc_le.bssid, 0xFF, ETH_ALEN); + + if (cfg->channel) { + ext_join_params->assoc_le.chanspec_num = cpu_to_le32(1); + + ext_join_params->assoc_le.chanspec_list[0] = + cpu_to_le16(chanspec); + /* Increase dwell time to receive probe response or detect + * beacon from target AP at a noisy air only during connect + * command. + */ + ext_join_params->scan_le.active_time = + cpu_to_le32(BRCMF_SCAN_JOIN_ACTIVE_DWELL_TIME_MS); + ext_join_params->scan_le.passive_time = + cpu_to_le32(BRCMF_SCAN_JOIN_PASSIVE_DWELL_TIME_MS); + /* To sync with presence period of VSDB GO send probe request + * more frequently. Probe request will be stopped when it gets + * probe response from target AP/GO. + */ + ext_join_params->scan_le.nprobes = + cpu_to_le32(BRCMF_SCAN_JOIN_ACTIVE_DWELL_TIME_MS / + BRCMF_SCAN_JOIN_PROBE_INTERVAL_MS); + } else { + ext_join_params->scan_le.active_time = cpu_to_le32(-1); + ext_join_params->scan_le.passive_time = cpu_to_le32(-1); + ext_join_params->scan_le.nprobes = cpu_to_le32(-1); + } + + err = brcmf_fil_bsscfg_data_set(ifp, "join", ext_join_params, + join_params_size); + kfree(ext_join_params); + if (!err) + /* This is it. join command worked, we are done */ + goto done; + + /* join command failed, fallback to set ssid */ + memset(&join_params, 0, sizeof(join_params)); + join_params_size = sizeof(join_params.ssid_le); + + memcpy(&join_params.ssid_le.SSID, sme->ssid, profile->ssid.SSID_len); + join_params.ssid_le.SSID_len = cpu_to_le32(profile->ssid.SSID_len); + + if (sme->bssid) + memcpy(join_params.params_le.bssid, sme->bssid, ETH_ALEN); + else + memset(join_params.params_le.bssid, 0xFF, ETH_ALEN); + + if (cfg->channel) { + join_params.params_le.chanspec_list[0] = cpu_to_le16(chanspec); + join_params.params_le.chanspec_num = cpu_to_le32(1); + join_params_size += sizeof(join_params.params_le); + } + err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SSID, + &join_params, join_params_size); + if (err) + brcmf_err("BRCMF_C_SET_SSID failed (%d)\n", err); + +done: + if (err) + clear_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state); + brcmf_dbg(TRACE, "Exit\n"); + return err; +} + +static s32 +brcmf_cfg80211_disconnect(struct wiphy *wiphy, struct net_device *ndev, + u16 reason_code) +{ + struct brcmf_if *ifp = netdev_priv(ndev); + struct brcmf_cfg80211_profile *profile = &ifp->vif->profile; + struct brcmf_scb_val_le scbval; + s32 err = 0; + + brcmf_dbg(TRACE, "Enter. Reason code = %d\n", reason_code); + if (!check_vif_up(ifp->vif)) + return -EIO; + + clear_bit(BRCMF_VIF_STATUS_CONNECTED, &ifp->vif->sme_state); + cfg80211_disconnected(ndev, reason_code, NULL, 0, GFP_KERNEL); + + memcpy(&scbval.ea, &profile->bssid, ETH_ALEN); + scbval.val = cpu_to_le32(reason_code); + err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_DISASSOC, + &scbval, sizeof(scbval)); + if (err) + brcmf_err("error (%d)\n", err); + + brcmf_dbg(TRACE, "Exit\n"); + return err; +} + +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); + + brcmf_dbg(TRACE, "Enter\n"); + if (!check_vif_up(ifp->vif)) + return -EIO; + + switch (type) { + case NL80211_TX_POWER_AUTOMATIC: + break; + case NL80211_TX_POWER_LIMITED: + case NL80211_TX_POWER_FIXED: + if (dbm < 0) { + brcmf_err("TX_POWER_FIXED - dbm is negative\n"); + err = -EINVAL; + goto done; + } + break; + } + /* Make sure radio is off or on as far as software is concerned */ + disable = WL_RADIO_SW_DISABLE << 16; + err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_RADIO, disable); + 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)); + if (err) + brcmf_err("qtxpower error (%d)\n", err); + cfg->conf->tx_power = dbm; + +done: + brcmf_dbg(TRACE, "Exit\n"); + return err; +} + +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; + + brcmf_dbg(TRACE, "Enter\n"); + if (!check_vif_up(ifp->vif)) + return -EIO; + + err = brcmf_fil_iovar_int_get(ifp, "qtxpower", &txpwrdbm); + if (err) { + brcmf_err("error (%d)\n", err); + goto done; + } + + result = (u8) (txpwrdbm & ~WL_TXPWR_OVERRIDE); + *dbm = (s32) brcmf_qdbm_to_mw(result); + +done: + brcmf_dbg(TRACE, "Exit\n"); + return err; +} + +static s32 +brcmf_cfg80211_config_default_key(struct wiphy *wiphy, struct net_device *ndev, + u8 key_idx, bool unicast, bool multicast) +{ + struct brcmf_if *ifp = netdev_priv(ndev); + u32 index; + u32 wsec; + s32 err = 0; + + brcmf_dbg(TRACE, "Enter\n"); + brcmf_dbg(CONN, "key index (%d)\n", key_idx); + if (!check_vif_up(ifp->vif)) + return -EIO; + + err = brcmf_fil_bsscfg_int_get(ifp, "wsec", &wsec); + if (err) { + brcmf_err("WLC_GET_WSEC error (%d)\n", err); + goto done; + } + + if (wsec & WEP_ENABLED) { + /* Just select a new current key */ + index = key_idx; + err = brcmf_fil_cmd_int_set(ifp, + BRCMF_C_SET_KEY_PRIMARY, index); + if (err) + brcmf_err("error (%d)\n", err); + } +done: + brcmf_dbg(TRACE, "Exit\n"); + return err; +} + +static s32 +brcmf_add_keyext(struct wiphy *wiphy, struct net_device *ndev, + u8 key_idx, const u8 *mac_addr, struct key_params *params) +{ + struct brcmf_if *ifp = netdev_priv(ndev); + struct brcmf_wsec_key key; + s32 err = 0; + u8 keybuf[8]; + + memset(&key, 0, sizeof(key)); + key.index = (u32) key_idx; + /* Instead of bcast for ea address for default wep keys, + driver needs it to be Null */ + if (!is_multicast_ether_addr(mac_addr)) + memcpy((char *)&key.ea, (void *)mac_addr, ETH_ALEN); + key.len = (u32) params->key_len; + /* check for key index change */ + if (key.len == 0) { + /* key delete */ + err = send_key_to_dongle(ndev, &key); + if (err) + brcmf_err("key delete error (%d)\n", err); + } else { + if (key.len > sizeof(key.data)) { + brcmf_err("Invalid key length (%d)\n", key.len); + return -EINVAL; + } + + brcmf_dbg(CONN, "Setting the key index %d\n", key.index); + memcpy(key.data, params->key, key.len); + + if (!brcmf_is_apmode(ifp->vif) && + (params->cipher == WLAN_CIPHER_SUITE_TKIP)) { + brcmf_dbg(CONN, "Swapping RX/TX MIC key\n"); + memcpy(keybuf, &key.data[24], sizeof(keybuf)); + memcpy(&key.data[24], &key.data[16], sizeof(keybuf)); + memcpy(&key.data[16], keybuf, sizeof(keybuf)); + } + + /* if IW_ENCODE_EXT_RX_SEQ_VALID set */ + if (params->seq && params->seq_len == 6) { + /* rx iv */ + u8 *ivptr; + ivptr = (u8 *) params->seq; + key.rxiv.hi = (ivptr[5] << 24) | (ivptr[4] << 16) | + (ivptr[3] << 8) | ivptr[2]; + key.rxiv.lo = (ivptr[1] << 8) | ivptr[0]; + key.iv_initialized = true; + } + + switch (params->cipher) { + case WLAN_CIPHER_SUITE_WEP40: + key.algo = CRYPTO_ALGO_WEP1; + brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP40\n"); + break; + case WLAN_CIPHER_SUITE_WEP104: + key.algo = CRYPTO_ALGO_WEP128; + brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP104\n"); + break; + case WLAN_CIPHER_SUITE_TKIP: + key.algo = CRYPTO_ALGO_TKIP; + brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_TKIP\n"); + break; + case WLAN_CIPHER_SUITE_AES_CMAC: + key.algo = CRYPTO_ALGO_AES_CCM; + brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_AES_CMAC\n"); + break; + case WLAN_CIPHER_SUITE_CCMP: + key.algo = CRYPTO_ALGO_AES_CCM; + brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_CCMP\n"); + break; + default: + brcmf_err("Invalid cipher (0x%x)\n", params->cipher); + return -EINVAL; + } + err = send_key_to_dongle(ndev, &key); + if (err) + brcmf_err("wsec_key error (%d)\n", err); + } + return err; +} + +static s32 +brcmf_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev, + u8 key_idx, bool pairwise, const u8 *mac_addr, + struct key_params *params) +{ + struct brcmf_if *ifp = netdev_priv(ndev); + struct brcmf_wsec_key key; + s32 val; + s32 wsec; + s32 err = 0; + u8 keybuf[8]; + + brcmf_dbg(TRACE, "Enter\n"); + brcmf_dbg(CONN, "key index (%d)\n", key_idx); + if (!check_vif_up(ifp->vif)) + return -EIO; + + if (mac_addr && + (params->cipher != WLAN_CIPHER_SUITE_WEP40) && + (params->cipher != WLAN_CIPHER_SUITE_WEP104)) { + brcmf_dbg(TRACE, "Exit"); + return brcmf_add_keyext(wiphy, ndev, key_idx, mac_addr, params); + } + memset(&key, 0, sizeof(key)); + + key.len = (u32) params->key_len; + key.index = (u32) key_idx; + + if (key.len > sizeof(key.data)) { + brcmf_err("Too long key length (%u)\n", key.len); + err = -EINVAL; + goto done; + } + memcpy(key.data, params->key, key.len); + + key.flags = BRCMF_PRIMARY_KEY; + switch (params->cipher) { + case WLAN_CIPHER_SUITE_WEP40: + key.algo = CRYPTO_ALGO_WEP1; + val = WEP_ENABLED; + brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP40\n"); + break; + case WLAN_CIPHER_SUITE_WEP104: + key.algo = CRYPTO_ALGO_WEP128; + val = WEP_ENABLED; + brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP104\n"); + break; + case WLAN_CIPHER_SUITE_TKIP: + if (!brcmf_is_apmode(ifp->vif)) { + brcmf_dbg(CONN, "Swapping RX/TX MIC key\n"); + memcpy(keybuf, &key.data[24], sizeof(keybuf)); + memcpy(&key.data[24], &key.data[16], sizeof(keybuf)); + memcpy(&key.data[16], keybuf, sizeof(keybuf)); + } + key.algo = CRYPTO_ALGO_TKIP; + val = TKIP_ENABLED; + brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_TKIP\n"); + break; + case WLAN_CIPHER_SUITE_AES_CMAC: + key.algo = CRYPTO_ALGO_AES_CCM; + val = AES_ENABLED; + brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_AES_CMAC\n"); + break; + case WLAN_CIPHER_SUITE_CCMP: + key.algo = CRYPTO_ALGO_AES_CCM; + val = AES_ENABLED; + brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_CCMP\n"); + break; + default: + brcmf_err("Invalid cipher (0x%x)\n", params->cipher); + err = -EINVAL; + goto done; + } + + err = send_key_to_dongle(ndev, &key); + if (err) + goto done; + + err = brcmf_fil_bsscfg_int_get(ifp, "wsec", &wsec); + if (err) { + brcmf_err("get wsec error (%d)\n", err); + goto done; + } + wsec |= val; + err = brcmf_fil_bsscfg_int_set(ifp, "wsec", wsec); + if (err) { + brcmf_err("set wsec error (%d)\n", err); + goto done; + } + +done: + brcmf_dbg(TRACE, "Exit\n"); + return err; +} + +static s32 +brcmf_cfg80211_del_key(struct wiphy *wiphy, struct net_device *ndev, + u8 key_idx, bool pairwise, const u8 *mac_addr) +{ + struct brcmf_if *ifp = netdev_priv(ndev); + struct brcmf_wsec_key key; + s32 err = 0; + + brcmf_dbg(TRACE, "Enter\n"); + if (!check_vif_up(ifp->vif)) + return -EIO; + + if (key_idx >= DOT11_MAX_DEFAULT_KEYS) { + /* we ignore this key index in this case */ + brcmf_err("invalid key index (%d)\n", key_idx); + return -EINVAL; + } + + memset(&key, 0, sizeof(key)); + + key.index = (u32) key_idx; + key.flags = BRCMF_PRIMARY_KEY; + key.algo = CRYPTO_ALGO_OFF; + + brcmf_dbg(CONN, "key index (%d)\n", key_idx); + + /* Set the new key/index */ + err = send_key_to_dongle(ndev, &key); + + brcmf_dbg(TRACE, "Exit\n"); + return err; +} + +static s32 +brcmf_cfg80211_get_key(struct wiphy *wiphy, struct net_device *ndev, + u8 key_idx, bool pairwise, const u8 *mac_addr, void *cookie, + void (*callback) (void *cookie, struct key_params * params)) +{ + struct key_params params; + struct brcmf_if *ifp = netdev_priv(ndev); + struct brcmf_cfg80211_profile *profile = &ifp->vif->profile; + struct brcmf_cfg80211_security *sec; + s32 wsec; + s32 err = 0; + + brcmf_dbg(TRACE, "Enter\n"); + brcmf_dbg(CONN, "key index (%d)\n", key_idx); + if (!check_vif_up(ifp->vif)) + return -EIO; + + memset(¶ms, 0, sizeof(params)); + + err = brcmf_fil_bsscfg_int_get(ifp, "wsec", &wsec); + if (err) { + brcmf_err("WLC_GET_WSEC error (%d)\n", err); + /* Ignore this error, may happen during DISASSOC */ + err = -EAGAIN; + goto done; + } + if (wsec & WEP_ENABLED) { + sec = &profile->sec; + if (sec->cipher_pairwise & WLAN_CIPHER_SUITE_WEP40) { + params.cipher = WLAN_CIPHER_SUITE_WEP40; + brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP40\n"); + } else if (sec->cipher_pairwise & WLAN_CIPHER_SUITE_WEP104) { + params.cipher = WLAN_CIPHER_SUITE_WEP104; + brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP104\n"); + } + } else if (wsec & TKIP_ENABLED) { + params.cipher = WLAN_CIPHER_SUITE_TKIP; + brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_TKIP\n"); + } else if (wsec & AES_ENABLED) { + params.cipher = WLAN_CIPHER_SUITE_AES_CMAC; + brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_AES_CMAC\n"); + } else { + brcmf_err("Invalid algo (0x%x)\n", wsec); + err = -EINVAL; + goto done; + } + callback(cookie, ¶ms); + +done: + brcmf_dbg(TRACE, "Exit\n"); + return err; +} + +static s32 +brcmf_cfg80211_config_default_mgmt_key(struct wiphy *wiphy, + struct net_device *ndev, u8 key_idx) +{ + brcmf_dbg(INFO, "Not supported\n"); + + return -EOPNOTSUPP; +} + +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; + + 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); + err = brcmf_fil_iovar_data_get(ifp, "sta_info", + &sta_info_le, + sizeof(sta_info_le)); + if (err < 0) { + brcmf_err("GET STA INFO failed, %d\n", err); + goto done; + } + sinfo->filled = STATION_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 |= STATION_INFO_CONNECTED_TIME; + sinfo->connected_time = le32_to_cpu(sta_info_le.in); + } + brcmf_dbg(TRACE, "STA idle time : %d ms, connected time :%d sec\n", + sinfo->inactive_time, sinfo->connected_time); + } else if (ifp->vif->wdev.iftype == NL80211_IFTYPE_STATION) { + if (memcmp(mac, bssid, ETH_ALEN)) { + brcmf_err("Wrong Mac address cfg_mac-%pM wl_bssid-%pM\n", + mac, bssid); + err = -ENOENT; + goto done; + } + /* Report the current tx rate */ + err = brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_RATE, &rate); + if (err) { + brcmf_err("Could not get rate (%d)\n", err); + goto done; + } else { + sinfo->filled |= STATION_INFO_TX_BITRATE; + sinfo->txrate.legacy = rate * 5; + brcmf_dbg(CONN, "Rate %d Mbps\n", rate / 2); + } + + 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 |= STATION_INFO_SIGNAL; + sinfo->signal = rssi; + brcmf_dbg(CONN, "RSSI %d dBm\n", rssi); + } + err = brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_BCNPRD, + &beacon_period); + if (err) { + brcmf_err("Could not get beacon period (%d)\n", + err); + goto done; + } else { + sinfo->bss_param.beacon_interval = + beacon_period; + brcmf_dbg(CONN, "Beacon peroid %d\n", + beacon_period); + } + err = brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_DTIMPRD, + &dtim_period); + if (err) { + brcmf_err("Could not get DTIM period (%d)\n", + err); + goto done; + } else { + sinfo->bss_param.dtim_period = dtim_period; + brcmf_dbg(CONN, "DTIM peroid %d\n", + dtim_period); + } + sinfo->filled |= STATION_INFO_BSS_PARAM; + } + } else + err = -EPERM; +done: + brcmf_dbg(TRACE, "Exit\n"); + return err; +} + +static s32 +brcmf_cfg80211_set_power_mgmt(struct wiphy *wiphy, struct net_device *ndev, + bool enabled, s32 timeout) +{ + s32 pm; + s32 err = 0; + struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); + struct brcmf_if *ifp = netdev_priv(ndev); + + brcmf_dbg(TRACE, "Enter\n"); + + /* + * Powersave enable/disable request is coming from the + * cfg80211 even before the interface is up. In that + * scenario, driver will be storing the power save + * preference in cfg struct to apply this to + * FW later while initializing the dongle + */ + cfg->pwr_save = enabled; + if (!check_vif_up(ifp->vif)) { + + brcmf_dbg(INFO, "Device is not ready, storing the value in cfg_info struct\n"); + goto done; + } + + pm = enabled ? PM_FAST : PM_OFF; + /* Do not enable the power save after assoc if it is a p2p interface */ + if (ifp->vif->wdev.iftype == NL80211_IFTYPE_P2P_CLIENT) { + brcmf_dbg(INFO, "Do not enable power save for P2P clients\n"); + pm = PM_OFF; + } + brcmf_dbg(INFO, "power save %s\n", (pm ? "enabled" : "disabled")); + + err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM, pm); + if (err) { + if (err == -ENODEV) + brcmf_err("net_device is not ready yet\n"); + else + brcmf_err("error (%d)\n", err); + } +done: + brcmf_dbg(TRACE, "Exit\n"); + return err; +} + +static s32 brcmf_inform_single_bss(struct brcmf_cfg80211_info *cfg, + struct brcmf_bss_info_le *bi) +{ + struct wiphy *wiphy = cfg_to_wiphy(cfg); + struct ieee80211_channel *notify_channel; + struct cfg80211_bss *bss; + struct ieee80211_supported_band *band; + struct brcmu_chan ch; + u16 channel; + u32 freq; + u16 notify_capability; + u16 notify_interval; + u8 *notify_ie; + size_t notify_ielen; + s32 notify_signal; + + if (le32_to_cpu(bi->length) > WL_BSS_INFO_MAX) { + brcmf_err("Bss info is larger than buffer. Discarding\n"); + return 0; + } + + if (!bi->ctl_ch) { + ch.chspec = le16_to_cpu(bi->chanspec); + cfg->d11inf.decchspec(&ch); + bi->ctl_ch = ch.chnum; + } + channel = bi->ctl_ch; + + if (channel <= CH_MAX_2G_CHANNEL) + band = wiphy->bands[IEEE80211_BAND_2GHZ]; + else + band = wiphy->bands[IEEE80211_BAND_5GHZ]; + + freq = ieee80211_channel_to_frequency(channel, band->band); + notify_channel = ieee80211_get_channel(wiphy, freq); + + notify_capability = le16_to_cpu(bi->capability); + notify_interval = le16_to_cpu(bi->beacon_period); + notify_ie = (u8 *)bi + le16_to_cpu(bi->ie_offset); + notify_ielen = le32_to_cpu(bi->ie_length); + notify_signal = (s16)le16_to_cpu(bi->RSSI) * 100; + + brcmf_dbg(CONN, "bssid: %pM\n", bi->BSSID); + brcmf_dbg(CONN, "Channel: %d(%d)\n", channel, freq); + brcmf_dbg(CONN, "Capability: %X\n", notify_capability); + brcmf_dbg(CONN, "Beacon interval: %d\n", notify_interval); + brcmf_dbg(CONN, "Signal: %d\n", notify_signal); + + bss = cfg80211_inform_bss(wiphy, notify_channel, + CFG80211_BSS_FTYPE_UNKNOWN, + (const u8 *)bi->BSSID, + 0, notify_capability, + notify_interval, notify_ie, + notify_ielen, notify_signal, + GFP_KERNEL); + + if (!bss) + return -ENOMEM; + + cfg80211_put_bss(wiphy, bss); + + return 0; +} + +static struct brcmf_bss_info_le * +next_bss_le(struct brcmf_scan_results *list, struct brcmf_bss_info_le *bss) +{ + if (bss == NULL) + return list->bss_info_le; + return (struct brcmf_bss_info_le *)((unsigned long)bss + + le32_to_cpu(bss->length)); +} + +static s32 brcmf_inform_bss(struct brcmf_cfg80211_info *cfg) +{ + struct brcmf_scan_results *bss_list; + struct brcmf_bss_info_le *bi = NULL; /* must be initialized */ + s32 err = 0; + int i; + + bss_list = (struct brcmf_scan_results *)cfg->escan_info.escan_buf; + if (bss_list->count != 0 && + bss_list->version != BRCMF_BSS_INFO_VERSION) { + brcmf_err("Version %d != WL_BSS_INFO_VERSION\n", + bss_list->version); + return -EOPNOTSUPP; + } + brcmf_dbg(SCAN, "scanned AP count (%d)\n", bss_list->count); + for (i = 0; i < bss_list->count; i++) { + bi = next_bss_le(bss_list, bi); + err = brcmf_inform_single_bss(cfg, bi); + if (err) + break; + } + return err; +} + +static s32 wl_inform_ibss(struct brcmf_cfg80211_info *cfg, + struct net_device *ndev, const u8 *bssid) +{ + struct wiphy *wiphy = cfg_to_wiphy(cfg); + struct ieee80211_channel *notify_channel; + struct brcmf_bss_info_le *bi = NULL; + struct ieee80211_supported_band *band; + struct cfg80211_bss *bss; + struct brcmu_chan ch; + u8 *buf = NULL; + s32 err = 0; + u32 freq; + u16 notify_capability; + u16 notify_interval; + u8 *notify_ie; + size_t notify_ielen; + s32 notify_signal; + + brcmf_dbg(TRACE, "Enter\n"); + + buf = kzalloc(WL_BSS_INFO_MAX, GFP_KERNEL); + if (buf == NULL) { + err = -ENOMEM; + goto CleanUp; + } + + *(__le32 *)buf = cpu_to_le32(WL_BSS_INFO_MAX); + + err = brcmf_fil_cmd_data_get(netdev_priv(ndev), BRCMF_C_GET_BSS_INFO, + buf, WL_BSS_INFO_MAX); + if (err) { + brcmf_err("WLC_GET_BSS_INFO failed: %d\n", err); + goto CleanUp; + } + + bi = (struct brcmf_bss_info_le *)(buf + 4); + + ch.chspec = le16_to_cpu(bi->chanspec); + cfg->d11inf.decchspec(&ch); + + if (ch.band == BRCMU_CHAN_BAND_2G) + band = wiphy->bands[IEEE80211_BAND_2GHZ]; + else + band = wiphy->bands[IEEE80211_BAND_5GHZ]; + + freq = ieee80211_channel_to_frequency(ch.chnum, band->band); + notify_channel = ieee80211_get_channel(wiphy, freq); + + notify_capability = le16_to_cpu(bi->capability); + notify_interval = le16_to_cpu(bi->beacon_period); + notify_ie = (u8 *)bi + le16_to_cpu(bi->ie_offset); + notify_ielen = le32_to_cpu(bi->ie_length); + notify_signal = (s16)le16_to_cpu(bi->RSSI) * 100; + + brcmf_dbg(CONN, "channel: %d(%d)\n", ch.chnum, freq); + brcmf_dbg(CONN, "capability: %X\n", notify_capability); + brcmf_dbg(CONN, "beacon interval: %d\n", notify_interval); + brcmf_dbg(CONN, "signal: %d\n", notify_signal); + + bss = cfg80211_inform_bss(wiphy, notify_channel, + CFG80211_BSS_FTYPE_UNKNOWN, bssid, 0, + notify_capability, notify_interval, + notify_ie, notify_ielen, notify_signal, + GFP_KERNEL); + + if (!bss) { + err = -ENOMEM; + goto CleanUp; + } + + cfg80211_put_bss(wiphy, bss); + +CleanUp: + + kfree(buf); + + brcmf_dbg(TRACE, "Exit\n"); + + return err; +} + +static s32 brcmf_update_bss_info(struct brcmf_cfg80211_info *cfg, + struct brcmf_if *ifp) +{ + struct brcmf_cfg80211_profile *profile = ndev_to_prof(ifp->ndev); + struct brcmf_bss_info_le *bi; + struct brcmf_ssid *ssid; + const struct brcmf_tlv *tim; + u16 beacon_interval; + u8 dtim_period; + size_t ie_len; + u8 *ie; + s32 err = 0; + + brcmf_dbg(TRACE, "Enter\n"); + if (brcmf_is_ibssmode(ifp->vif)) + return err; + + ssid = &profile->ssid; + + *(__le32 *)cfg->extra_buf = cpu_to_le32(WL_EXTRA_BUF_MAX); + err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BSS_INFO, + cfg->extra_buf, WL_EXTRA_BUF_MAX); + if (err) { + brcmf_err("Could not get bss info %d\n", err); + goto update_bss_info_out; + } + + bi = (struct brcmf_bss_info_le *)(cfg->extra_buf + 4); + err = brcmf_inform_single_bss(cfg, bi); + if (err) + goto update_bss_info_out; + + ie = ((u8 *)bi) + le16_to_cpu(bi->ie_offset); + ie_len = le32_to_cpu(bi->ie_length); + beacon_interval = le16_to_cpu(bi->beacon_period); + + tim = brcmf_parse_tlvs(ie, ie_len, WLAN_EID_TIM); + if (tim) + dtim_period = tim->data[1]; + else { + /* + * active scan was done so we could not get dtim + * information out of probe response. + * so we speficially query dtim information to dongle. + */ + u32 var; + err = brcmf_fil_iovar_int_get(ifp, "dtim_assoc", &var); + if (err) { + brcmf_err("wl dtim_assoc failed (%d)\n", err); + goto update_bss_info_out; + } + dtim_period = (u8)var; + } + +update_bss_info_out: + brcmf_dbg(TRACE, "Exit"); + return err; +} + +void brcmf_abort_scanning(struct brcmf_cfg80211_info *cfg) +{ + struct escan_info *escan = &cfg->escan_info; + + set_bit(BRCMF_SCAN_STATUS_ABORT, &cfg->scan_status); + if (cfg->scan_request) { + escan->escan_state = WL_ESCAN_STATE_IDLE; + brcmf_notify_escan_complete(cfg, escan->ifp, true, true); + } + clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status); + clear_bit(BRCMF_SCAN_STATUS_ABORT, &cfg->scan_status); +} + +static void brcmf_cfg80211_escan_timeout_worker(struct work_struct *work) +{ + struct brcmf_cfg80211_info *cfg = + container_of(work, struct brcmf_cfg80211_info, + escan_timeout_work); + + brcmf_inform_bss(cfg); + brcmf_notify_escan_complete(cfg, cfg->escan_info.ifp, true, true); +} + +static void brcmf_escan_timeout(unsigned long data) +{ + struct brcmf_cfg80211_info *cfg = + (struct brcmf_cfg80211_info *)data; + + if (cfg->scan_request) { + brcmf_err("timer expired\n"); + schedule_work(&cfg->escan_timeout_work); + } +} + +static s32 +brcmf_compare_update_same_bss(struct brcmf_cfg80211_info *cfg, + struct brcmf_bss_info_le *bss, + struct brcmf_bss_info_le *bss_info_le) +{ + struct brcmu_chan ch_bss, ch_bss_info_le; + + ch_bss.chspec = le16_to_cpu(bss->chanspec); + cfg->d11inf.decchspec(&ch_bss); + ch_bss_info_le.chspec = le16_to_cpu(bss_info_le->chanspec); + cfg->d11inf.decchspec(&ch_bss_info_le); + + if (!memcmp(&bss_info_le->BSSID, &bss->BSSID, ETH_ALEN) && + ch_bss.band == ch_bss_info_le.band && + bss_info_le->SSID_len == bss->SSID_len && + !memcmp(bss_info_le->SSID, bss->SSID, bss_info_le->SSID_len)) { + if ((bss->flags & BRCMF_BSS_RSSI_ON_CHANNEL) == + (bss_info_le->flags & BRCMF_BSS_RSSI_ON_CHANNEL)) { + s16 bss_rssi = le16_to_cpu(bss->RSSI); + s16 bss_info_rssi = le16_to_cpu(bss_info_le->RSSI); + + /* preserve max RSSI if the measurements are + * both on-channel or both off-channel + */ + if (bss_info_rssi > bss_rssi) + bss->RSSI = bss_info_le->RSSI; + } else if ((bss->flags & BRCMF_BSS_RSSI_ON_CHANNEL) && + (bss_info_le->flags & BRCMF_BSS_RSSI_ON_CHANNEL) == 0) { + /* preserve the on-channel rssi measurement + * if the new measurement is off channel + */ + bss->RSSI = bss_info_le->RSSI; + bss->flags |= BRCMF_BSS_RSSI_ON_CHANNEL; + } + return 1; + } + return 0; +} + +static s32 +brcmf_cfg80211_escan_handler(struct brcmf_if *ifp, + const struct brcmf_event_msg *e, void *data) +{ + struct brcmf_cfg80211_info *cfg = ifp->drvr->config; + s32 status; + struct brcmf_escan_result_le *escan_result_le; + struct brcmf_bss_info_le *bss_info_le; + struct brcmf_bss_info_le *bss = NULL; + u32 bi_length; + struct brcmf_scan_results *list; + u32 i; + bool aborted; + + status = e->status; + + if (!test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) { + brcmf_err("scan not ready, bssidx=%d\n", ifp->bssidx); + return -EPERM; + } + + if (status == BRCMF_E_STATUS_PARTIAL) { + brcmf_dbg(SCAN, "ESCAN Partial result\n"); + escan_result_le = (struct brcmf_escan_result_le *) data; + if (!escan_result_le) { + brcmf_err("Invalid escan result (NULL pointer)\n"); + goto exit; + } + if (le16_to_cpu(escan_result_le->bss_count) != 1) { + brcmf_err("Invalid bss_count %d: ignoring\n", + escan_result_le->bss_count); + goto exit; + } + bss_info_le = &escan_result_le->bss_info_le; + + if (brcmf_p2p_scan_finding_common_channel(cfg, bss_info_le)) + goto exit; + + if (!cfg->scan_request) { + brcmf_dbg(SCAN, "result without cfg80211 request\n"); + goto exit; + } + + bi_length = le32_to_cpu(bss_info_le->length); + if (bi_length != (le32_to_cpu(escan_result_le->buflen) - + WL_ESCAN_RESULTS_FIXED_SIZE)) { + brcmf_err("Invalid bss_info length %d: ignoring\n", + bi_length); + goto exit; + } + + if (!(cfg_to_wiphy(cfg)->interface_modes & + BIT(NL80211_IFTYPE_ADHOC))) { + if (le16_to_cpu(bss_info_le->capability) & + WLAN_CAPABILITY_IBSS) { + brcmf_err("Ignoring IBSS result\n"); + goto exit; + } + } + + list = (struct brcmf_scan_results *) + cfg->escan_info.escan_buf; + if (bi_length > WL_ESCAN_BUF_SIZE - list->buflen) { + brcmf_err("Buffer is too small: ignoring\n"); + goto exit; + } + + for (i = 0; i < list->count; i++) { + bss = bss ? (struct brcmf_bss_info_le *) + ((unsigned char *)bss + + le32_to_cpu(bss->length)) : list->bss_info_le; + if (brcmf_compare_update_same_bss(cfg, bss, + bss_info_le)) + goto exit; + } + memcpy(&(cfg->escan_info.escan_buf[list->buflen]), + bss_info_le, bi_length); + list->version = le32_to_cpu(bss_info_le->version); + list->buflen += bi_length; + list->count++; + } else { + cfg->escan_info.escan_state = WL_ESCAN_STATE_IDLE; + if (brcmf_p2p_scan_finding_common_channel(cfg, NULL)) + goto exit; + if (cfg->scan_request) { + brcmf_inform_bss(cfg); + aborted = status != BRCMF_E_STATUS_SUCCESS; + brcmf_notify_escan_complete(cfg, ifp, aborted, false); + } else + brcmf_dbg(SCAN, "Ignored scan complete result 0x%x\n", + status); + } +exit: + return 0; +} + +static void brcmf_init_escan(struct brcmf_cfg80211_info *cfg) +{ + brcmf_fweh_register(cfg->pub, BRCMF_E_ESCAN_RESULT, + brcmf_cfg80211_escan_handler); + cfg->escan_info.escan_state = WL_ESCAN_STATE_IDLE; + /* Init scan_timeout timer */ + init_timer(&cfg->escan_timeout); + cfg->escan_timeout.data = (unsigned long) cfg; + cfg->escan_timeout.function = brcmf_escan_timeout; + INIT_WORK(&cfg->escan_timeout_work, + brcmf_cfg80211_escan_timeout_worker); +} + +static __always_inline void brcmf_delay(u32 ms) +{ + if (ms < 1000 / HZ) { + cond_resched(); + mdelay(ms); + } else { + msleep(ms); + } +} + +static s32 brcmf_config_wowl_pattern(struct brcmf_if *ifp, u8 cmd[4], + u8 *pattern, u32 patternsize, u8 *mask, + u32 packet_offset) +{ + struct brcmf_fil_wowl_pattern_le *filter; + u32 masksize; + u32 patternoffset; + u8 *buf; + u32 bufsize; + s32 ret; + + masksize = (patternsize + 7) / 8; + patternoffset = sizeof(*filter) - sizeof(filter->cmd) + masksize; + + bufsize = sizeof(*filter) + patternsize + masksize; + buf = kzalloc(bufsize, GFP_KERNEL); + if (!buf) + return -ENOMEM; + filter = (struct brcmf_fil_wowl_pattern_le *)buf; + + memcpy(filter->cmd, cmd, 4); + filter->masksize = cpu_to_le32(masksize); + filter->offset = cpu_to_le32(packet_offset); + filter->patternoffset = cpu_to_le32(patternoffset); + filter->patternsize = cpu_to_le32(patternsize); + filter->type = cpu_to_le32(BRCMF_WOWL_PATTERN_TYPE_BITMAP); + + if ((mask) && (masksize)) + memcpy(buf + sizeof(*filter), mask, masksize); + if ((pattern) && (patternsize)) + memcpy(buf + sizeof(*filter) + masksize, pattern, patternsize); + + ret = brcmf_fil_iovar_data_set(ifp, "wowl_pattern", buf, bufsize); + + kfree(buf); + return ret; +} + +static s32 brcmf_cfg80211_resume(struct wiphy *wiphy) +{ + struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); + struct net_device *ndev = cfg_to_ndev(cfg); + struct brcmf_if *ifp = netdev_priv(ndev); + + brcmf_dbg(TRACE, "Enter\n"); + + if (cfg->wowl_enabled) { + brcmf_configure_arp_offload(ifp, true); + brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM, + cfg->pre_wowl_pmmode); + brcmf_fil_iovar_int_set(ifp, "wowl_clear", 0); + brcmf_config_wowl_pattern(ifp, "clr", NULL, 0, NULL, 0); + cfg->wowl_enabled = false; + } + return 0; +} + +static void brcmf_configure_wowl(struct brcmf_cfg80211_info *cfg, + struct brcmf_if *ifp, + struct cfg80211_wowlan *wowl) +{ + u32 wowl_config; + u32 i; + + brcmf_dbg(TRACE, "Suspend, wowl config.\n"); + + brcmf_configure_arp_offload(ifp, false); + brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_PM, &cfg->pre_wowl_pmmode); + brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM, PM_MAX); + + wowl_config = 0; + if (wowl->disconnect) + wowl_config = BRCMF_WOWL_DIS | BRCMF_WOWL_BCN | BRCMF_WOWL_RETR; + if (wowl->magic_pkt) + wowl_config |= BRCMF_WOWL_MAGIC; + if ((wowl->patterns) && (wowl->n_patterns)) { + wowl_config |= BRCMF_WOWL_NET; + for (i = 0; i < wowl->n_patterns; i++) { + brcmf_config_wowl_pattern(ifp, "add", + (u8 *)wowl->patterns[i].pattern, + wowl->patterns[i].pattern_len, + (u8 *)wowl->patterns[i].mask, + wowl->patterns[i].pkt_offset); + } + } + brcmf_fil_iovar_int_set(ifp, "wowl", wowl_config); + brcmf_fil_iovar_int_set(ifp, "wowl_activate", 1); + brcmf_bus_wowl_config(cfg->pub->bus_if, true); + cfg->wowl_enabled = true; +} + +static s32 brcmf_cfg80211_suspend(struct wiphy *wiphy, + struct cfg80211_wowlan *wowl) +{ + struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); + struct net_device *ndev = cfg_to_ndev(cfg); + struct brcmf_if *ifp = netdev_priv(ndev); + struct brcmf_cfg80211_vif *vif; + + brcmf_dbg(TRACE, "Enter\n"); + + /* if the primary net_device is not READY there is nothing + * we can do but pray resume goes smoothly. + */ + if (!check_vif_up(ifp->vif)) + goto exit; + + /* end any scanning */ + if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) + brcmf_abort_scanning(cfg); + + if (wowl == NULL) { + brcmf_bus_wowl_config(cfg->pub->bus_if, false); + list_for_each_entry(vif, &cfg->vif_list, list) { + if (!test_bit(BRCMF_VIF_STATUS_READY, &vif->sme_state)) + continue; + /* While going to suspend if associated with AP + * disassociate from AP to save power while system is + * in suspended state + */ + brcmf_link_down(vif); + /* Make sure WPA_Supplicant receives all the event + * generated due to DISASSOC call to the fw to keep + * the state fw and WPA_Supplicant state consistent + */ + brcmf_delay(500); + } + /* Configure MPC */ + brcmf_set_mpc(ifp, 1); + + } else { + /* Configure WOWL paramaters */ + brcmf_configure_wowl(cfg, ifp, wowl); + } + +exit: + brcmf_dbg(TRACE, "Exit\n"); + /* clear any scanning activity */ + cfg->scan_status = 0; + return 0; +} + +static __used s32 +brcmf_update_pmklist(struct net_device *ndev, + struct brcmf_cfg80211_pmk_list *pmk_list, s32 err) +{ + int i, j; + int pmkid_len; + + pmkid_len = le32_to_cpu(pmk_list->pmkids.npmkid); + + brcmf_dbg(CONN, "No of elements %d\n", pmkid_len); + for (i = 0; i < pmkid_len; i++) { + brcmf_dbg(CONN, "PMKID[%d]: %pM =\n", i, + &pmk_list->pmkids.pmkid[i].BSSID); + for (j = 0; j < WLAN_PMKID_LEN; j++) + brcmf_dbg(CONN, "%02x\n", + pmk_list->pmkids.pmkid[i].PMKID[j]); + } + + if (!err) + brcmf_fil_iovar_data_set(netdev_priv(ndev), "pmkid_info", + (char *)pmk_list, sizeof(*pmk_list)); + + return err; +} + +static s32 +brcmf_cfg80211_set_pmksa(struct wiphy *wiphy, struct net_device *ndev, + struct cfg80211_pmksa *pmksa) +{ + struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); + struct brcmf_if *ifp = netdev_priv(ndev); + struct pmkid_list *pmkids = &cfg->pmk_list->pmkids; + s32 err = 0; + int i; + int pmkid_len; + + brcmf_dbg(TRACE, "Enter\n"); + if (!check_vif_up(ifp->vif)) + return -EIO; + + pmkid_len = le32_to_cpu(pmkids->npmkid); + for (i = 0; i < pmkid_len; i++) + if (!memcmp(pmksa->bssid, pmkids->pmkid[i].BSSID, ETH_ALEN)) + break; + if (i < WL_NUM_PMKIDS_MAX) { + memcpy(pmkids->pmkid[i].BSSID, pmksa->bssid, ETH_ALEN); + memcpy(pmkids->pmkid[i].PMKID, pmksa->pmkid, WLAN_PMKID_LEN); + if (i == pmkid_len) { + pmkid_len++; + pmkids->npmkid = cpu_to_le32(pmkid_len); + } + } else + err = -EINVAL; + + brcmf_dbg(CONN, "set_pmksa,IW_PMKSA_ADD - PMKID: %pM =\n", + pmkids->pmkid[pmkid_len].BSSID); + for (i = 0; i < WLAN_PMKID_LEN; i++) + brcmf_dbg(CONN, "%02x\n", pmkids->pmkid[pmkid_len].PMKID[i]); + + err = brcmf_update_pmklist(ndev, cfg->pmk_list, err); + + brcmf_dbg(TRACE, "Exit\n"); + return err; +} + +static s32 +brcmf_cfg80211_del_pmksa(struct wiphy *wiphy, struct net_device *ndev, + struct cfg80211_pmksa *pmksa) +{ + struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); + struct brcmf_if *ifp = netdev_priv(ndev); + struct pmkid_list pmkid; + s32 err = 0; + int i, pmkid_len; + + brcmf_dbg(TRACE, "Enter\n"); + if (!check_vif_up(ifp->vif)) + return -EIO; + + memcpy(&pmkid.pmkid[0].BSSID, pmksa->bssid, ETH_ALEN); + memcpy(&pmkid.pmkid[0].PMKID, pmksa->pmkid, WLAN_PMKID_LEN); + + brcmf_dbg(CONN, "del_pmksa,IW_PMKSA_REMOVE - PMKID: %pM =\n", + &pmkid.pmkid[0].BSSID); + for (i = 0; i < WLAN_PMKID_LEN; i++) + brcmf_dbg(CONN, "%02x\n", pmkid.pmkid[0].PMKID[i]); + + pmkid_len = le32_to_cpu(cfg->pmk_list->pmkids.npmkid); + for (i = 0; i < pmkid_len; i++) + if (!memcmp + (pmksa->bssid, &cfg->pmk_list->pmkids.pmkid[i].BSSID, + ETH_ALEN)) + break; + + if ((pmkid_len > 0) + && (i < pmkid_len)) { + memset(&cfg->pmk_list->pmkids.pmkid[i], 0, + sizeof(struct pmkid)); + for (; i < (pmkid_len - 1); i++) { + memcpy(&cfg->pmk_list->pmkids.pmkid[i].BSSID, + &cfg->pmk_list->pmkids.pmkid[i + 1].BSSID, + ETH_ALEN); + memcpy(&cfg->pmk_list->pmkids.pmkid[i].PMKID, + &cfg->pmk_list->pmkids.pmkid[i + 1].PMKID, + WLAN_PMKID_LEN); + } + cfg->pmk_list->pmkids.npmkid = cpu_to_le32(pmkid_len - 1); + } else + err = -EINVAL; + + err = brcmf_update_pmklist(ndev, cfg->pmk_list, err); + + brcmf_dbg(TRACE, "Exit\n"); + return err; + +} + +static s32 +brcmf_cfg80211_flush_pmksa(struct wiphy *wiphy, struct net_device *ndev) +{ + struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); + struct brcmf_if *ifp = netdev_priv(ndev); + s32 err = 0; + + brcmf_dbg(TRACE, "Enter\n"); + if (!check_vif_up(ifp->vif)) + return -EIO; + + memset(cfg->pmk_list, 0, sizeof(*cfg->pmk_list)); + err = brcmf_update_pmklist(ndev, cfg->pmk_list, err); + + brcmf_dbg(TRACE, "Exit\n"); + return err; + +} + +/* + * PFN result doesn't have all the info which are + * required by the supplicant + * (For e.g IEs) Do a target Escan so that sched scan results are reported + * via wl_inform_single_bss in the required format. Escan does require the + * scan request in the form of cfg80211_scan_request. For timebeing, create + * cfg80211_scan_request one out of the received PNO event. + */ +static s32 +brcmf_notify_sched_scan_results(struct brcmf_if *ifp, + const struct brcmf_event_msg *e, void *data) +{ + struct brcmf_cfg80211_info *cfg = ifp->drvr->config; + struct brcmf_pno_net_info_le *netinfo, *netinfo_start; + struct cfg80211_scan_request *request = NULL; + struct cfg80211_ssid *ssid = NULL; + struct ieee80211_channel *channel = NULL; + struct wiphy *wiphy = cfg_to_wiphy(cfg); + int err = 0; + int channel_req = 0; + int band = 0; + struct brcmf_pno_scanresults_le *pfn_result; + u32 result_count; + u32 status; + + brcmf_dbg(SCAN, "Enter\n"); + + if (e->event_code == BRCMF_E_PFN_NET_LOST) { + brcmf_dbg(SCAN, "PFN NET LOST event. Do Nothing\n"); + return 0; + } + + pfn_result = (struct brcmf_pno_scanresults_le *)data; + result_count = le32_to_cpu(pfn_result->count); + status = le32_to_cpu(pfn_result->status); + + /* + * PFN event is limited to fit 512 bytes so we may get + * multiple NET_FOUND events. For now place a warning here. + */ + WARN_ON(status != BRCMF_PNO_SCAN_COMPLETE); + brcmf_dbg(SCAN, "PFN NET FOUND event. count: %d\n", result_count); + if (result_count > 0) { + int i; + + request = kzalloc(sizeof(*request), GFP_KERNEL); + ssid = kcalloc(result_count, sizeof(*ssid), GFP_KERNEL); + channel = kcalloc(result_count, sizeof(*channel), GFP_KERNEL); + if (!request || !ssid || !channel) { + err = -ENOMEM; + goto out_err; + } + + request->wiphy = wiphy; + data += sizeof(struct brcmf_pno_scanresults_le); + netinfo_start = (struct brcmf_pno_net_info_le *)data; + + for (i = 0; i < result_count; i++) { + netinfo = &netinfo_start[i]; + if (!netinfo) { + brcmf_err("Invalid netinfo ptr. index: %d\n", + i); + err = -EINVAL; + goto out_err; + } + + brcmf_dbg(SCAN, "SSID:%s Channel:%d\n", + netinfo->SSID, netinfo->channel); + memcpy(ssid[i].ssid, netinfo->SSID, netinfo->SSID_len); + ssid[i].ssid_len = netinfo->SSID_len; + request->n_ssids++; + + channel_req = netinfo->channel; + if (channel_req <= CH_MAX_2G_CHANNEL) + band = NL80211_BAND_2GHZ; + else + band = NL80211_BAND_5GHZ; + channel[i].center_freq = + ieee80211_channel_to_frequency(channel_req, + band); + channel[i].band = band; + channel[i].flags |= IEEE80211_CHAN_NO_HT40; + request->channels[i] = &channel[i]; + request->n_channels++; + } + + /* assign parsed ssid array */ + if (request->n_ssids) + request->ssids = &ssid[0]; + + if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) { + /* Abort any on-going scan */ + brcmf_abort_scanning(cfg); + } + + set_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status); + cfg->escan_info.run = brcmf_run_escan; + err = brcmf_do_escan(cfg, wiphy, ifp, request); + if (err) { + clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status); + goto out_err; + } + cfg->sched_escan = true; + cfg->scan_request = request; + } else { + brcmf_err("FALSE PNO Event. (pfn_count == 0)\n"); + goto out_err; + } + + kfree(ssid); + kfree(channel); + kfree(request); + return 0; + +out_err: + kfree(ssid); + kfree(channel); + kfree(request); + cfg80211_sched_scan_stopped(wiphy); + return err; +} + +static int brcmf_dev_pno_clean(struct net_device *ndev) +{ + int ret; + + /* Disable pfn */ + ret = brcmf_fil_iovar_int_set(netdev_priv(ndev), "pfn", 0); + if (ret == 0) { + /* clear pfn */ + ret = brcmf_fil_iovar_data_set(netdev_priv(ndev), "pfnclear", + NULL, 0); + } + if (ret < 0) + brcmf_err("failed code %d\n", ret); + + return ret; +} + +static int brcmf_dev_pno_config(struct net_device *ndev) +{ + struct brcmf_pno_param_le pfn_param; + + memset(&pfn_param, 0, sizeof(pfn_param)); + pfn_param.version = cpu_to_le32(BRCMF_PNO_VERSION); + + /* set extra pno params */ + pfn_param.flags = cpu_to_le16(1 << BRCMF_PNO_ENABLE_ADAPTSCAN_BIT); + pfn_param.repeat = BRCMF_PNO_REPEAT; + pfn_param.exp = BRCMF_PNO_FREQ_EXPO_MAX; + + /* set up pno scan fr */ + pfn_param.scan_freq = cpu_to_le32(BRCMF_PNO_TIME); + + return brcmf_fil_iovar_data_set(netdev_priv(ndev), "pfn_set", + &pfn_param, sizeof(pfn_param)); +} + +static int +brcmf_cfg80211_sched_scan_start(struct wiphy *wiphy, + struct net_device *ndev, + struct cfg80211_sched_scan_request *request) +{ + struct brcmf_if *ifp = netdev_priv(ndev); + struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy); + struct brcmf_pno_net_param_le pfn; + int i; + int ret = 0; + + brcmf_dbg(SCAN, "Enter n_match_sets:%d n_ssids:%d\n", + request->n_match_sets, request->n_ssids); + if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) { + brcmf_err("Scanning already: status (%lu)\n", cfg->scan_status); + return -EAGAIN; + } + if (test_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status)) { + brcmf_err("Scanning suppressed: status (%lu)\n", + cfg->scan_status); + return -EAGAIN; + } + + if (!request->n_ssids || !request->n_match_sets) { + brcmf_dbg(SCAN, "Invalid sched scan req!! n_ssids:%d\n", + request->n_ssids); + return -EINVAL; + } + + if (request->n_ssids > 0) { + for (i = 0; i < request->n_ssids; i++) { + /* Active scan req for ssids */ + brcmf_dbg(SCAN, ">>> Active scan req for ssid (%s)\n", + request->ssids[i].ssid); + + /* + * match_set ssids is a supert set of n_ssid list, + * so we need not add these set seperately. + */ + } + } + + if (request->n_match_sets > 0) { + /* clean up everything */ + ret = brcmf_dev_pno_clean(ndev); + if (ret < 0) { + brcmf_err("failed error=%d\n", ret); + return ret; + } + + /* configure pno */ + ret = brcmf_dev_pno_config(ndev); + if (ret < 0) { + brcmf_err("PNO setup failed!! ret=%d\n", ret); + return -EINVAL; + } + + /* configure each match set */ + for (i = 0; i < request->n_match_sets; i++) { + struct cfg80211_ssid *ssid; + u32 ssid_len; + + ssid = &request->match_sets[i].ssid; + ssid_len = ssid->ssid_len; + + if (!ssid_len) { + brcmf_err("skip broadcast ssid\n"); + continue; + } + pfn.auth = cpu_to_le32(WLAN_AUTH_OPEN); + pfn.wpa_auth = cpu_to_le32(BRCMF_PNO_WPA_AUTH_ANY); + pfn.wsec = cpu_to_le32(0); + pfn.infra = cpu_to_le32(1); + pfn.flags = cpu_to_le32(1 << BRCMF_PNO_HIDDEN_BIT); + pfn.ssid.SSID_len = cpu_to_le32(ssid_len); + memcpy(pfn.ssid.SSID, ssid->ssid, ssid_len); + ret = brcmf_fil_iovar_data_set(ifp, "pfn_add", &pfn, + sizeof(pfn)); + brcmf_dbg(SCAN, ">>> PNO filter %s for ssid (%s)\n", + ret == 0 ? "set" : "failed", ssid->ssid); + } + /* Enable the PNO */ + if (brcmf_fil_iovar_int_set(ifp, "pfn", 1) < 0) { + brcmf_err("PNO enable failed!! ret=%d\n", ret); + return -EINVAL; + } + } else { + return -EINVAL; + } + + return 0; +} + +static int brcmf_cfg80211_sched_scan_stop(struct wiphy *wiphy, + struct net_device *ndev) +{ + struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); + + brcmf_dbg(SCAN, "enter\n"); + brcmf_dev_pno_clean(ndev); + if (cfg->sched_escan) + brcmf_notify_escan_complete(cfg, netdev_priv(ndev), true, true); + return 0; +} + +static s32 brcmf_configure_opensecurity(struct brcmf_if *ifp) +{ + s32 err; + + /* set auth */ + err = brcmf_fil_bsscfg_int_set(ifp, "auth", 0); + if (err < 0) { + brcmf_err("auth error %d\n", err); + return err; + } + /* set wsec */ + err = brcmf_fil_bsscfg_int_set(ifp, "wsec", 0); + if (err < 0) { + brcmf_err("wsec error %d\n", err); + return err; + } + /* set upper-layer auth */ + err = brcmf_fil_bsscfg_int_set(ifp, "wpa_auth", WPA_AUTH_NONE); + if (err < 0) { + brcmf_err("wpa_auth error %d\n", err); + return err; + } + + return 0; +} + +static bool brcmf_valid_wpa_oui(u8 *oui, bool is_rsn_ie) +{ + if (is_rsn_ie) + return (memcmp(oui, RSN_OUI, TLV_OUI_LEN) == 0); + + return (memcmp(oui, WPA_OUI, TLV_OUI_LEN) == 0); +} + +static s32 +brcmf_configure_wpaie(struct net_device *ndev, + const struct brcmf_vs_tlv *wpa_ie, + bool is_rsn_ie) +{ + struct brcmf_if *ifp = netdev_priv(ndev); + u32 auth = 0; /* d11 open authentication */ + u16 count; + s32 err = 0; + s32 len = 0; + u32 i; + u32 wsec; + u32 pval = 0; + u32 gval = 0; + u32 wpa_auth = 0; + u32 offset; + u8 *data; + u16 rsn_cap; + u32 wme_bss_disable; + + brcmf_dbg(TRACE, "Enter\n"); + if (wpa_ie == NULL) + goto exit; + + len = wpa_ie->len + TLV_HDR_LEN; + data = (u8 *)wpa_ie; + offset = TLV_HDR_LEN; + if (!is_rsn_ie) + offset += VS_IE_FIXED_HDR_LEN; + else + offset += WPA_IE_VERSION_LEN; + + /* check for multicast cipher suite */ + if (offset + WPA_IE_MIN_OUI_LEN > len) { + err = -EINVAL; + brcmf_err("no multicast cipher suite\n"); + goto exit; + } + + if (!brcmf_valid_wpa_oui(&data[offset], is_rsn_ie)) { + err = -EINVAL; + brcmf_err("ivalid OUI\n"); + goto exit; + } + offset += TLV_OUI_LEN; + + /* pick up multicast cipher */ + switch (data[offset]) { + case WPA_CIPHER_NONE: + gval = 0; + break; + case WPA_CIPHER_WEP_40: + case WPA_CIPHER_WEP_104: + gval = WEP_ENABLED; + break; + case WPA_CIPHER_TKIP: + gval = TKIP_ENABLED; + break; + case WPA_CIPHER_AES_CCM: + gval = AES_ENABLED; + break; + default: + err = -EINVAL; + brcmf_err("Invalid multi cast cipher info\n"); + goto exit; + } + + offset++; + /* walk thru unicast cipher list and pick up what we recognize */ + count = data[offset] + (data[offset + 1] << 8); + offset += WPA_IE_SUITE_COUNT_LEN; + /* Check for unicast suite(s) */ + if (offset + (WPA_IE_MIN_OUI_LEN * count) > len) { + err = -EINVAL; + brcmf_err("no unicast cipher suite\n"); + goto exit; + } + for (i = 0; i < count; i++) { + if (!brcmf_valid_wpa_oui(&data[offset], is_rsn_ie)) { + err = -EINVAL; + brcmf_err("ivalid OUI\n"); + goto exit; + } + offset += TLV_OUI_LEN; + switch (data[offset]) { + case WPA_CIPHER_NONE: + break; + case WPA_CIPHER_WEP_40: + case WPA_CIPHER_WEP_104: + pval |= WEP_ENABLED; + break; + case WPA_CIPHER_TKIP: + pval |= TKIP_ENABLED; + break; + case WPA_CIPHER_AES_CCM: + pval |= AES_ENABLED; + break; + default: + brcmf_err("Ivalid unicast security info\n"); + } + offset++; + } + /* walk thru auth management suite list and pick up what we recognize */ + count = data[offset] + (data[offset + 1] << 8); + offset += WPA_IE_SUITE_COUNT_LEN; + /* Check for auth key management suite(s) */ + if (offset + (WPA_IE_MIN_OUI_LEN * count) > len) { + err = -EINVAL; + brcmf_err("no auth key mgmt suite\n"); + goto exit; + } + for (i = 0; i < count; i++) { + if (!brcmf_valid_wpa_oui(&data[offset], is_rsn_ie)) { + err = -EINVAL; + brcmf_err("ivalid OUI\n"); + goto exit; + } + offset += TLV_OUI_LEN; + switch (data[offset]) { + case RSN_AKM_NONE: + brcmf_dbg(TRACE, "RSN_AKM_NONE\n"); + wpa_auth |= WPA_AUTH_NONE; + break; + case RSN_AKM_UNSPECIFIED: + brcmf_dbg(TRACE, "RSN_AKM_UNSPECIFIED\n"); + is_rsn_ie ? (wpa_auth |= WPA2_AUTH_UNSPECIFIED) : + (wpa_auth |= WPA_AUTH_UNSPECIFIED); + break; + case RSN_AKM_PSK: + brcmf_dbg(TRACE, "RSN_AKM_PSK\n"); + is_rsn_ie ? (wpa_auth |= WPA2_AUTH_PSK) : + (wpa_auth |= WPA_AUTH_PSK); + break; + default: + brcmf_err("Ivalid key mgmt info\n"); + } + offset++; + } + + if (is_rsn_ie) { + wme_bss_disable = 1; + if ((offset + RSN_CAP_LEN) <= len) { + rsn_cap = data[offset] + (data[offset + 1] << 8); + if (rsn_cap & RSN_CAP_PTK_REPLAY_CNTR_MASK) + wme_bss_disable = 0; + } + /* set wme_bss_disable to sync RSN Capabilities */ + err = brcmf_fil_bsscfg_int_set(ifp, "wme_bss_disable", + wme_bss_disable); + if (err < 0) { + brcmf_err("wme_bss_disable error %d\n", err); + goto exit; + } + } + /* FOR WPS , set SES_OW_ENABLED */ + wsec = (pval | gval | SES_OW_ENABLED); + + /* set auth */ + err = brcmf_fil_bsscfg_int_set(ifp, "auth", auth); + if (err < 0) { + brcmf_err("auth error %d\n", err); + goto exit; + } + /* set wsec */ + err = brcmf_fil_bsscfg_int_set(ifp, "wsec", wsec); + if (err < 0) { + brcmf_err("wsec error %d\n", err); + goto exit; + } + /* set upper-layer auth */ + err = brcmf_fil_bsscfg_int_set(ifp, "wpa_auth", wpa_auth); + if (err < 0) { + brcmf_err("wpa_auth error %d\n", err); + goto exit; + } + +exit: + return err; +} + +static s32 +brcmf_parse_vndr_ies(const u8 *vndr_ie_buf, u32 vndr_ie_len, + struct parsed_vndr_ies *vndr_ies) +{ + struct brcmf_vs_tlv *vndrie; + struct brcmf_tlv *ie; + struct parsed_vndr_ie_info *parsed_info; + s32 remaining_len; + + remaining_len = (s32)vndr_ie_len; + memset(vndr_ies, 0, sizeof(*vndr_ies)); + + ie = (struct brcmf_tlv *)vndr_ie_buf; + while (ie) { + if (ie->id != WLAN_EID_VENDOR_SPECIFIC) + goto next; + vndrie = (struct brcmf_vs_tlv *)ie; + /* len should be bigger than OUI length + one */ + if (vndrie->len < (VS_IE_FIXED_HDR_LEN - TLV_HDR_LEN + 1)) { + brcmf_err("invalid vndr ie. length is too small %d\n", + vndrie->len); + goto next; + } + /* if wpa or wme ie, do not add ie */ + if (!memcmp(vndrie->oui, (u8 *)WPA_OUI, TLV_OUI_LEN) && + ((vndrie->oui_type == WPA_OUI_TYPE) || + (vndrie->oui_type == WME_OUI_TYPE))) { + brcmf_dbg(TRACE, "Found WPA/WME oui. Do not add it\n"); + goto next; + } + + parsed_info = &vndr_ies->ie_info[vndr_ies->count]; + + /* save vndr ie information */ + parsed_info->ie_ptr = (char *)vndrie; + parsed_info->ie_len = vndrie->len + TLV_HDR_LEN; + memcpy(&parsed_info->vndrie, vndrie, sizeof(*vndrie)); + + vndr_ies->count++; + + brcmf_dbg(TRACE, "** OUI %02x %02x %02x, type 0x%02x\n", + parsed_info->vndrie.oui[0], + parsed_info->vndrie.oui[1], + parsed_info->vndrie.oui[2], + parsed_info->vndrie.oui_type); + + if (vndr_ies->count >= VNDR_IE_PARSE_LIMIT) + break; +next: + remaining_len -= (ie->len + TLV_HDR_LEN); + if (remaining_len <= TLV_HDR_LEN) + ie = NULL; + else + ie = (struct brcmf_tlv *)(((u8 *)ie) + ie->len + + TLV_HDR_LEN); + } + return 0; +} + +static u32 +brcmf_vndr_ie(u8 *iebuf, s32 pktflag, u8 *ie_ptr, u32 ie_len, s8 *add_del_cmd) +{ + + __le32 iecount_le; + __le32 pktflag_le; + + strncpy(iebuf, add_del_cmd, VNDR_IE_CMD_LEN - 1); + iebuf[VNDR_IE_CMD_LEN - 1] = '\0'; + + iecount_le = cpu_to_le32(1); + memcpy(&iebuf[VNDR_IE_COUNT_OFFSET], &iecount_le, sizeof(iecount_le)); + + pktflag_le = cpu_to_le32(pktflag); + memcpy(&iebuf[VNDR_IE_PKTFLAG_OFFSET], &pktflag_le, sizeof(pktflag_le)); + + memcpy(&iebuf[VNDR_IE_VSIE_OFFSET], ie_ptr, ie_len); + + return ie_len + VNDR_IE_HDR_SIZE; +} + +s32 brcmf_vif_set_mgmt_ie(struct brcmf_cfg80211_vif *vif, s32 pktflag, + const u8 *vndr_ie_buf, u32 vndr_ie_len) +{ + struct brcmf_if *ifp; + struct vif_saved_ie *saved_ie; + s32 err = 0; + u8 *iovar_ie_buf; + u8 *curr_ie_buf; + u8 *mgmt_ie_buf = NULL; + int mgmt_ie_buf_len; + u32 *mgmt_ie_len; + u32 del_add_ie_buf_len = 0; + u32 total_ie_buf_len = 0; + u32 parsed_ie_buf_len = 0; + struct parsed_vndr_ies old_vndr_ies; + struct parsed_vndr_ies new_vndr_ies; + struct parsed_vndr_ie_info *vndrie_info; + s32 i; + u8 *ptr; + int remained_buf_len; + + if (!vif) + return -ENODEV; + ifp = vif->ifp; + saved_ie = &vif->saved_ie; + + brcmf_dbg(TRACE, "bssidx %d, pktflag : 0x%02X\n", ifp->bssidx, pktflag); + iovar_ie_buf = kzalloc(WL_EXTRA_BUF_MAX, GFP_KERNEL); + if (!iovar_ie_buf) + return -ENOMEM; + curr_ie_buf = iovar_ie_buf; + switch (pktflag) { + case BRCMF_VNDR_IE_PRBREQ_FLAG: + mgmt_ie_buf = saved_ie->probe_req_ie; + mgmt_ie_len = &saved_ie->probe_req_ie_len; + mgmt_ie_buf_len = sizeof(saved_ie->probe_req_ie); + break; + case BRCMF_VNDR_IE_PRBRSP_FLAG: + mgmt_ie_buf = saved_ie->probe_res_ie; + mgmt_ie_len = &saved_ie->probe_res_ie_len; + mgmt_ie_buf_len = sizeof(saved_ie->probe_res_ie); + break; + case BRCMF_VNDR_IE_BEACON_FLAG: + mgmt_ie_buf = saved_ie->beacon_ie; + mgmt_ie_len = &saved_ie->beacon_ie_len; + mgmt_ie_buf_len = sizeof(saved_ie->beacon_ie); + break; + case BRCMF_VNDR_IE_ASSOCREQ_FLAG: + mgmt_ie_buf = saved_ie->assoc_req_ie; + mgmt_ie_len = &saved_ie->assoc_req_ie_len; + mgmt_ie_buf_len = sizeof(saved_ie->assoc_req_ie); + break; + default: + err = -EPERM; + brcmf_err("not suitable type\n"); + goto exit; + } + + if (vndr_ie_len > mgmt_ie_buf_len) { + err = -ENOMEM; + brcmf_err("extra IE size too big\n"); + goto exit; + } + + /* parse and save new vndr_ie in curr_ie_buff before comparing it */ + if (vndr_ie_buf && vndr_ie_len && curr_ie_buf) { + ptr = curr_ie_buf; + brcmf_parse_vndr_ies(vndr_ie_buf, vndr_ie_len, &new_vndr_ies); + for (i = 0; i < new_vndr_ies.count; i++) { + vndrie_info = &new_vndr_ies.ie_info[i]; + memcpy(ptr + parsed_ie_buf_len, vndrie_info->ie_ptr, + vndrie_info->ie_len); + parsed_ie_buf_len += vndrie_info->ie_len; + } + } + + if (mgmt_ie_buf && *mgmt_ie_len) { + if (parsed_ie_buf_len && (parsed_ie_buf_len == *mgmt_ie_len) && + (memcmp(mgmt_ie_buf, curr_ie_buf, + parsed_ie_buf_len) == 0)) { + brcmf_dbg(TRACE, "Previous mgmt IE equals to current IE\n"); + goto exit; + } + + /* parse old vndr_ie */ + brcmf_parse_vndr_ies(mgmt_ie_buf, *mgmt_ie_len, &old_vndr_ies); + + /* make a command to delete old ie */ + for (i = 0; i < old_vndr_ies.count; i++) { + vndrie_info = &old_vndr_ies.ie_info[i]; + + brcmf_dbg(TRACE, "DEL ID : %d, Len: %d , OUI:%02x:%02x:%02x\n", + vndrie_info->vndrie.id, + vndrie_info->vndrie.len, + vndrie_info->vndrie.oui[0], + vndrie_info->vndrie.oui[1], + vndrie_info->vndrie.oui[2]); + + del_add_ie_buf_len = brcmf_vndr_ie(curr_ie_buf, pktflag, + vndrie_info->ie_ptr, + vndrie_info->ie_len, + "del"); + curr_ie_buf += del_add_ie_buf_len; + total_ie_buf_len += del_add_ie_buf_len; + } + } + + *mgmt_ie_len = 0; + /* Add if there is any extra IE */ + if (mgmt_ie_buf && parsed_ie_buf_len) { + ptr = mgmt_ie_buf; + + remained_buf_len = mgmt_ie_buf_len; + + /* make a command to add new ie */ + for (i = 0; i < new_vndr_ies.count; i++) { + vndrie_info = &new_vndr_ies.ie_info[i]; + + /* verify remained buf size before copy data */ + if (remained_buf_len < (vndrie_info->vndrie.len + + VNDR_IE_VSIE_OFFSET)) { + brcmf_err("no space in mgmt_ie_buf: len left %d", + remained_buf_len); + break; + } + remained_buf_len -= (vndrie_info->ie_len + + VNDR_IE_VSIE_OFFSET); + + brcmf_dbg(TRACE, "ADDED ID : %d, Len: %d, OUI:%02x:%02x:%02x\n", + vndrie_info->vndrie.id, + vndrie_info->vndrie.len, + vndrie_info->vndrie.oui[0], + vndrie_info->vndrie.oui[1], + vndrie_info->vndrie.oui[2]); + + del_add_ie_buf_len = brcmf_vndr_ie(curr_ie_buf, pktflag, + vndrie_info->ie_ptr, + vndrie_info->ie_len, + "add"); + + /* save the parsed IE in wl struct */ + memcpy(ptr + (*mgmt_ie_len), vndrie_info->ie_ptr, + vndrie_info->ie_len); + *mgmt_ie_len += vndrie_info->ie_len; + + curr_ie_buf += del_add_ie_buf_len; + total_ie_buf_len += del_add_ie_buf_len; + } + } + if (total_ie_buf_len) { + err = brcmf_fil_bsscfg_data_set(ifp, "vndr_ie", iovar_ie_buf, + total_ie_buf_len); + if (err) + brcmf_err("vndr ie set error : %d\n", err); + } + +exit: + kfree(iovar_ie_buf); + return err; +} + +s32 brcmf_vif_clear_mgmt_ies(struct brcmf_cfg80211_vif *vif) +{ + s32 pktflags[] = { + BRCMF_VNDR_IE_PRBREQ_FLAG, + BRCMF_VNDR_IE_PRBRSP_FLAG, + BRCMF_VNDR_IE_BEACON_FLAG + }; + int i; + + for (i = 0; i < ARRAY_SIZE(pktflags); i++) + brcmf_vif_set_mgmt_ie(vif, pktflags[i], NULL, 0); + + memset(&vif->saved_ie, 0, sizeof(vif->saved_ie)); + return 0; +} + +static s32 +brcmf_config_ap_mgmt_ie(struct brcmf_cfg80211_vif *vif, + struct cfg80211_beacon_data *beacon) +{ + s32 err; + + /* Set Beacon IEs to FW */ + err = brcmf_vif_set_mgmt_ie(vif, BRCMF_VNDR_IE_BEACON_FLAG, + beacon->tail, beacon->tail_len); + if (err) { + brcmf_err("Set Beacon IE Failed\n"); + return err; + } + brcmf_dbg(TRACE, "Applied Vndr IEs for Beacon\n"); + + /* Set Probe Response IEs to FW */ + err = brcmf_vif_set_mgmt_ie(vif, BRCMF_VNDR_IE_PRBRSP_FLAG, + beacon->proberesp_ies, + beacon->proberesp_ies_len); + if (err) + brcmf_err("Set Probe Resp IE Failed\n"); + else + brcmf_dbg(TRACE, "Applied Vndr IEs for Probe Resp\n"); + + return err; +} + +static s32 +brcmf_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *ndev, + struct cfg80211_ap_settings *settings) +{ + s32 ie_offset; + struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); + struct brcmf_if *ifp = netdev_priv(ndev); + const struct brcmf_tlv *ssid_ie; + struct brcmf_ssid_le ssid_le; + s32 err = -EPERM; + const struct brcmf_tlv *rsn_ie; + const struct brcmf_vs_tlv *wpa_ie; + struct brcmf_join_params join_params; + enum nl80211_iftype dev_role; + struct brcmf_fil_bss_enable_le bss_enable; + u16 chanspec; + + brcmf_dbg(TRACE, "ctrlchn=%d, center=%d, bw=%d, beacon_interval=%d, dtim_period=%d,\n", + settings->chandef.chan->hw_value, + settings->chandef.center_freq1, settings->chandef.width, + settings->beacon_interval, settings->dtim_period); + brcmf_dbg(TRACE, "ssid=%s(%zu), auth_type=%d, inactivity_timeout=%d\n", + settings->ssid, settings->ssid_len, settings->auth_type, + settings->inactivity_timeout); + + dev_role = ifp->vif->wdev.iftype; + + memset(&ssid_le, 0, sizeof(ssid_le)); + if (settings->ssid == NULL || settings->ssid_len == 0) { + ie_offset = DOT11_MGMT_HDR_LEN + DOT11_BCN_PRB_FIXED_LEN; + ssid_ie = brcmf_parse_tlvs( + (u8 *)&settings->beacon.head[ie_offset], + settings->beacon.head_len - ie_offset, + WLAN_EID_SSID); + if (!ssid_ie) + return -EINVAL; + + memcpy(ssid_le.SSID, ssid_ie->data, ssid_ie->len); + ssid_le.SSID_len = cpu_to_le32(ssid_ie->len); + brcmf_dbg(TRACE, "SSID is (%s) in Head\n", ssid_le.SSID); + } else { + memcpy(ssid_le.SSID, settings->ssid, settings->ssid_len); + ssid_le.SSID_len = cpu_to_le32((u32)settings->ssid_len); + } + + brcmf_set_mpc(ifp, 0); + brcmf_configure_arp_offload(ifp, false); + + /* find the RSN_IE */ + rsn_ie = brcmf_parse_tlvs((u8 *)settings->beacon.tail, + settings->beacon.tail_len, WLAN_EID_RSN); + + /* find the WPA_IE */ + wpa_ie = brcmf_find_wpaie((u8 *)settings->beacon.tail, + settings->beacon.tail_len); + + if ((wpa_ie != NULL || rsn_ie != NULL)) { + brcmf_dbg(TRACE, "WPA(2) IE is found\n"); + if (wpa_ie != NULL) { + /* WPA IE */ + err = brcmf_configure_wpaie(ndev, wpa_ie, false); + if (err < 0) + goto exit; + } else { + /* RSN IE */ + err = brcmf_configure_wpaie(ndev, + (struct brcmf_vs_tlv *)rsn_ie, true); + if (err < 0) + goto exit; + } + } else { + brcmf_dbg(TRACE, "No WPA(2) IEs found\n"); + brcmf_configure_opensecurity(ifp); + } + + brcmf_config_ap_mgmt_ie(ifp->vif, &settings->beacon); + + chanspec = chandef_to_chanspec(&cfg->d11inf, &settings->chandef); + err = brcmf_fil_iovar_int_set(ifp, "chanspec", chanspec); + if (err < 0) { + brcmf_err("Set Channel failed: chspec=%d, %d\n", chanspec, err); + goto exit; + } + + if (settings->beacon_interval) { + err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_BCNPRD, + settings->beacon_interval); + if (err < 0) { + brcmf_err("Beacon Interval Set Error, %d\n", err); + goto exit; + } + } + if (settings->dtim_period) { + err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_DTIMPRD, + settings->dtim_period); + if (err < 0) { + brcmf_err("DTIM Interval Set Error, %d\n", err); + goto exit; + } + } + + if (dev_role == NL80211_IFTYPE_AP) { + err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_DOWN, 1); + if (err < 0) { + brcmf_err("BRCMF_C_DOWN error %d\n", err); + goto exit; + } + brcmf_fil_iovar_int_set(ifp, "apsta", 0); + } + + err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_INFRA, 1); + if (err < 0) { + brcmf_err("SET INFRA error %d\n", err); + goto exit; + } + if (dev_role == NL80211_IFTYPE_AP) { + err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_AP, 1); + if (err < 0) { + brcmf_err("setting AP mode failed %d\n", err); + goto exit; + } + err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_UP, 1); + if (err < 0) { + brcmf_err("BRCMF_C_UP error (%d)\n", err); + goto exit; + } + + memset(&join_params, 0, sizeof(join_params)); + /* join parameters starts with ssid */ + memcpy(&join_params.ssid_le, &ssid_le, sizeof(ssid_le)); + /* create softap */ + err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SSID, + &join_params, sizeof(join_params)); + if (err < 0) { + brcmf_err("SET SSID error (%d)\n", err); + goto exit; + } + brcmf_dbg(TRACE, "AP mode configuration complete\n"); + } else { + err = brcmf_fil_bsscfg_data_set(ifp, "ssid", &ssid_le, + sizeof(ssid_le)); + if (err < 0) { + brcmf_err("setting ssid failed %d\n", err); + goto exit; + } + bss_enable.bsscfg_idx = cpu_to_le32(ifp->bssidx); + bss_enable.enable = cpu_to_le32(1); + err = brcmf_fil_iovar_data_set(ifp, "bss", &bss_enable, + sizeof(bss_enable)); + if (err < 0) { + brcmf_err("bss_enable config failed %d\n", err); + goto exit; + } + + 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); + +exit: + if (err) { + brcmf_set_mpc(ifp, 1); + brcmf_configure_arp_offload(ifp, true); + } + return err; +} + +static int brcmf_cfg80211_stop_ap(struct wiphy *wiphy, struct net_device *ndev) +{ + struct brcmf_if *ifp = netdev_priv(ndev); + s32 err; + struct brcmf_fil_bss_enable_le bss_enable; + struct brcmf_join_params join_params; + + brcmf_dbg(TRACE, "Enter\n"); + + if (ifp->vif->wdev.iftype == NL80211_IFTYPE_AP) { + /* Due to most likely deauths outstanding we sleep */ + /* first to make sure they get processed by fw. */ + msleep(400); + + memset(&join_params, 0, sizeof(join_params)); + err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SSID, + &join_params, sizeof(join_params)); + if (err < 0) + brcmf_err("SET SSID error (%d)\n", err); + err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_UP, 0); + if (err < 0) + brcmf_err("BRCMF_C_UP error %d\n", err); + err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_AP, 0); + if (err < 0) + brcmf_err("setting AP mode failed %d\n", err); + err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_INFRA, 0); + if (err < 0) + brcmf_err("setting INFRA mode failed %d\n", err); + } else { + bss_enable.bsscfg_idx = cpu_to_le32(ifp->bssidx); + bss_enable.enable = cpu_to_le32(0); + err = brcmf_fil_iovar_data_set(ifp, "bss", &bss_enable, + sizeof(bss_enable)); + if (err < 0) + brcmf_err("bss_enable config failed %d\n", err); + } + 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); + + return err; +} + +static s32 +brcmf_cfg80211_change_beacon(struct wiphy *wiphy, struct net_device *ndev, + struct cfg80211_beacon_data *info) +{ + struct brcmf_if *ifp = netdev_priv(ndev); + s32 err; + + brcmf_dbg(TRACE, "Enter\n"); + + err = brcmf_config_ap_mgmt_ie(ifp->vif, info); + + return err; +} + +static int +brcmf_cfg80211_del_station(struct wiphy *wiphy, struct net_device *ndev, + const u8 *mac) +{ + struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); + struct brcmf_scb_val_le scbval; + struct brcmf_if *ifp = netdev_priv(ndev); + s32 err; + + if (!mac) + return -EFAULT; + + brcmf_dbg(TRACE, "Enter %pM\n", mac); + + if (ifp->vif == cfg->p2p.bss_idx[P2PAPI_BSSCFG_DEVICE].vif) + ifp = cfg->p2p.bss_idx[P2PAPI_BSSCFG_PRIMARY].vif->ifp; + if (!check_vif_up(ifp->vif)) + return -EIO; + + memcpy(&scbval.ea, mac, ETH_ALEN); + scbval.val = cpu_to_le32(WLAN_REASON_DEAUTH_LEAVING); + err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SCB_DEAUTHENTICATE_FOR_REASON, + &scbval, sizeof(scbval)); + if (err) + brcmf_err("SCB_DEAUTHENTICATE_FOR_REASON failed %d\n", err); + + brcmf_dbg(TRACE, "Exit\n"); + return err; +} + + +static void +brcmf_cfg80211_mgmt_frame_register(struct wiphy *wiphy, + struct wireless_dev *wdev, + u16 frame_type, bool reg) +{ + struct brcmf_cfg80211_vif *vif; + u16 mgmt_type; + + brcmf_dbg(TRACE, "Enter, frame_type %04x, reg=%d\n", frame_type, reg); + + mgmt_type = (frame_type & IEEE80211_FCTL_STYPE) >> 4; + vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev); + if (reg) + vif->mgmt_rx_reg |= BIT(mgmt_type); + else + vif->mgmt_rx_reg &= ~BIT(mgmt_type); +} + + +static int +brcmf_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, + struct cfg80211_mgmt_tx_params *params, u64 *cookie) +{ + struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); + struct ieee80211_channel *chan = params->chan; + const u8 *buf = params->buf; + size_t len = params->len; + const struct ieee80211_mgmt *mgmt; + struct brcmf_cfg80211_vif *vif; + s32 err = 0; + s32 ie_offset; + s32 ie_len; + struct brcmf_fil_action_frame_le *action_frame; + struct brcmf_fil_af_params_le *af_params; + bool ack; + s32 chan_nr; + u32 freq; + + brcmf_dbg(TRACE, "Enter\n"); + + *cookie = 0; + + mgmt = (const struct ieee80211_mgmt *)buf; + + if (!ieee80211_is_mgmt(mgmt->frame_control)) { + brcmf_err("Driver only allows MGMT packet type\n"); + return -EPERM; + } + + vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev); + + if (ieee80211_is_probe_resp(mgmt->frame_control)) { + /* Right now the only reason to get a probe response */ + /* is for p2p listen response or for p2p GO from */ + /* wpa_supplicant. Unfortunately the probe is send */ + /* on primary ndev, while dongle wants it on the p2p */ + /* vif. Since this is only reason for a probe */ + /* response to be sent, the vif is taken from cfg. */ + /* If ever desired to send proberesp for non p2p */ + /* response then data should be checked for */ + /* "DIRECT-". Note in future supplicant will take */ + /* dedicated p2p wdev to do this and then this 'hack'*/ + /* is not needed anymore. */ + ie_offset = DOT11_MGMT_HDR_LEN + + DOT11_BCN_PRB_FIXED_LEN; + ie_len = len - ie_offset; + if (vif == cfg->p2p.bss_idx[P2PAPI_BSSCFG_PRIMARY].vif) + vif = cfg->p2p.bss_idx[P2PAPI_BSSCFG_DEVICE].vif; + err = brcmf_vif_set_mgmt_ie(vif, + BRCMF_VNDR_IE_PRBRSP_FLAG, + &buf[ie_offset], + ie_len); + cfg80211_mgmt_tx_status(wdev, *cookie, buf, len, true, + GFP_KERNEL); + } else if (ieee80211_is_action(mgmt->frame_control)) { + af_params = kzalloc(sizeof(*af_params), GFP_KERNEL); + if (af_params == NULL) { + brcmf_err("unable to allocate frame\n"); + err = -ENOMEM; + goto exit; + } + action_frame = &af_params->action_frame; + /* Add the packet Id */ + action_frame->packet_id = cpu_to_le32(*cookie); + /* Add BSSID */ + memcpy(&action_frame->da[0], &mgmt->da[0], ETH_ALEN); + memcpy(&af_params->bssid[0], &mgmt->bssid[0], ETH_ALEN); + /* Add the length exepted for 802.11 header */ + action_frame->len = cpu_to_le16(len - DOT11_MGMT_HDR_LEN); + /* Add the channel. Use the one specified as parameter if any or + * the current one (got from the firmware) otherwise + */ + if (chan) + freq = chan->center_freq; + else + brcmf_fil_cmd_int_get(vif->ifp, BRCMF_C_GET_CHANNEL, + &freq); + chan_nr = ieee80211_frequency_to_channel(freq); + af_params->channel = cpu_to_le32(chan_nr); + + memcpy(action_frame->data, &buf[DOT11_MGMT_HDR_LEN], + le16_to_cpu(action_frame->len)); + + brcmf_dbg(TRACE, "Action frame, cookie=%lld, len=%d, freq=%d\n", + *cookie, le16_to_cpu(action_frame->len), freq); + + ack = brcmf_p2p_send_action_frame(cfg, cfg_to_ndev(cfg), + af_params); + + cfg80211_mgmt_tx_status(wdev, *cookie, buf, len, ack, + GFP_KERNEL); + kfree(af_params); + } else { + brcmf_dbg(TRACE, "Unhandled, fc=%04x!!\n", mgmt->frame_control); + brcmf_dbg_hex_dump(true, buf, len, "payload, len=%Zu\n", len); + } + +exit: + return err; +} + + +static int +brcmf_cfg80211_cancel_remain_on_channel(struct wiphy *wiphy, + struct wireless_dev *wdev, + u64 cookie) +{ + struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); + struct brcmf_cfg80211_vif *vif; + int err = 0; + + brcmf_dbg(TRACE, "Enter p2p listen cancel\n"); + + vif = cfg->p2p.bss_idx[P2PAPI_BSSCFG_DEVICE].vif; + if (vif == NULL) { + brcmf_err("No p2p device available for probe response\n"); + err = -ENODEV; + goto exit; + } + brcmf_p2p_cancel_remain_on_channel(vif->ifp); +exit: + return err; +} + +static int brcmf_cfg80211_crit_proto_start(struct wiphy *wiphy, + struct wireless_dev *wdev, + enum nl80211_crit_proto_id proto, + u16 duration) +{ + struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); + struct brcmf_cfg80211_vif *vif; + + vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev); + + /* only DHCP support for now */ + if (proto != NL80211_CRIT_PROTO_DHCP) + return -EINVAL; + + /* suppress and abort scanning */ + set_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status); + brcmf_abort_scanning(cfg); + + return brcmf_btcoex_set_mode(vif, BRCMF_BTCOEX_DISABLED, duration); +} + +static void brcmf_cfg80211_crit_proto_stop(struct wiphy *wiphy, + struct wireless_dev *wdev) +{ + struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); + struct brcmf_cfg80211_vif *vif; + + vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev); + + brcmf_btcoex_set_mode(vif, BRCMF_BTCOEX_ENABLED, 0); + clear_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status); +} + +static s32 +brcmf_notify_tdls_peer_event(struct brcmf_if *ifp, + const struct brcmf_event_msg *e, void *data) +{ + switch (e->reason) { + case BRCMF_E_REASON_TDLS_PEER_DISCOVERED: + brcmf_dbg(TRACE, "TDLS Peer Discovered\n"); + break; + case BRCMF_E_REASON_TDLS_PEER_CONNECTED: + brcmf_dbg(TRACE, "TDLS Peer Connected\n"); + brcmf_proto_add_tdls_peer(ifp->drvr, ifp->ifidx, (u8 *)e->addr); + break; + case BRCMF_E_REASON_TDLS_PEER_DISCONNECTED: + brcmf_dbg(TRACE, "TDLS Peer Disconnected\n"); + brcmf_proto_delete_peer(ifp->drvr, ifp->ifidx, (u8 *)e->addr); + break; + } + + return 0; +} + +static int brcmf_convert_nl80211_tdls_oper(enum nl80211_tdls_operation oper) +{ + int ret; + + switch (oper) { + case NL80211_TDLS_DISCOVERY_REQ: + ret = BRCMF_TDLS_MANUAL_EP_DISCOVERY; + break; + case NL80211_TDLS_SETUP: + ret = BRCMF_TDLS_MANUAL_EP_CREATE; + break; + case NL80211_TDLS_TEARDOWN: + ret = BRCMF_TDLS_MANUAL_EP_DELETE; + break; + default: + brcmf_err("unsupported operation: %d\n", oper); + ret = -EOPNOTSUPP; + } + return ret; +} + +static int brcmf_cfg80211_tdls_oper(struct wiphy *wiphy, + struct net_device *ndev, const u8 *peer, + enum nl80211_tdls_operation oper) +{ + struct brcmf_if *ifp; + struct brcmf_tdls_iovar_le info; + int ret = 0; + + ret = brcmf_convert_nl80211_tdls_oper(oper); + if (ret < 0) + return ret; + + ifp = netdev_priv(ndev); + memset(&info, 0, sizeof(info)); + info.mode = (u8)ret; + if (peer) + memcpy(info.ea, peer, ETH_ALEN); + + ret = brcmf_fil_iovar_data_set(ifp, "tdls_endpoint", + &info, sizeof(info)); + if (ret < 0) + brcmf_err("tdls_endpoint iovar failed: ret=%d\n", ret); + + return ret; +} + +static struct cfg80211_ops wl_cfg80211_ops = { + .add_virtual_intf = brcmf_cfg80211_add_iface, + .del_virtual_intf = brcmf_cfg80211_del_iface, + .change_virtual_intf = brcmf_cfg80211_change_iface, + .scan = brcmf_cfg80211_scan, + .set_wiphy_params = brcmf_cfg80211_set_wiphy_params, + .join_ibss = brcmf_cfg80211_join_ibss, + .leave_ibss = brcmf_cfg80211_leave_ibss, + .get_station = brcmf_cfg80211_get_station, + .set_tx_power = brcmf_cfg80211_set_tx_power, + .get_tx_power = brcmf_cfg80211_get_tx_power, + .add_key = brcmf_cfg80211_add_key, + .del_key = brcmf_cfg80211_del_key, + .get_key = brcmf_cfg80211_get_key, + .set_default_key = brcmf_cfg80211_config_default_key, + .set_default_mgmt_key = brcmf_cfg80211_config_default_mgmt_key, + .set_power_mgmt = brcmf_cfg80211_set_power_mgmt, + .connect = brcmf_cfg80211_connect, + .disconnect = brcmf_cfg80211_disconnect, + .suspend = brcmf_cfg80211_suspend, + .resume = brcmf_cfg80211_resume, + .set_pmksa = brcmf_cfg80211_set_pmksa, + .del_pmksa = brcmf_cfg80211_del_pmksa, + .flush_pmksa = brcmf_cfg80211_flush_pmksa, + .start_ap = brcmf_cfg80211_start_ap, + .stop_ap = brcmf_cfg80211_stop_ap, + .change_beacon = brcmf_cfg80211_change_beacon, + .del_station = brcmf_cfg80211_del_station, + .sched_scan_start = brcmf_cfg80211_sched_scan_start, + .sched_scan_stop = brcmf_cfg80211_sched_scan_stop, + .mgmt_frame_register = brcmf_cfg80211_mgmt_frame_register, + .mgmt_tx = brcmf_cfg80211_mgmt_tx, + .remain_on_channel = brcmf_p2p_remain_on_channel, + .cancel_remain_on_channel = brcmf_cfg80211_cancel_remain_on_channel, + .start_p2p_device = brcmf_p2p_start_device, + .stop_p2p_device = brcmf_p2p_stop_device, + .crit_proto_start = brcmf_cfg80211_crit_proto_start, + .crit_proto_stop = brcmf_cfg80211_crit_proto_stop, + .tdls_oper = brcmf_cfg80211_tdls_oper, +}; + +struct brcmf_cfg80211_vif *brcmf_alloc_vif(struct brcmf_cfg80211_info *cfg, + enum nl80211_iftype type, + bool pm_block) +{ + struct brcmf_cfg80211_vif *vif; + + brcmf_dbg(TRACE, "allocating virtual interface (size=%zu)\n", + sizeof(*vif)); + vif = kzalloc(sizeof(*vif), GFP_KERNEL); + if (!vif) + return ERR_PTR(-ENOMEM); + + vif->wdev.wiphy = cfg->wiphy; + vif->wdev.iftype = type; + + vif->pm_block = pm_block; + vif->roam_off = -1; + + brcmf_init_prof(&vif->profile); + + list_add_tail(&vif->list, &cfg->vif_list); + return vif; +} + +void brcmf_free_vif(struct brcmf_cfg80211_vif *vif) +{ + list_del(&vif->list); + kfree(vif); +} + +void brcmf_cfg80211_free_netdev(struct net_device *ndev) +{ + struct brcmf_cfg80211_vif *vif; + struct brcmf_if *ifp; + + ifp = netdev_priv(ndev); + vif = ifp->vif; + + brcmf_free_vif(vif); + free_netdev(ndev); +} + +static bool brcmf_is_linkup(const struct brcmf_event_msg *e) +{ + u32 event = e->event_code; + u32 status = e->status; + + if (event == BRCMF_E_SET_SSID && status == BRCMF_E_STATUS_SUCCESS) { + brcmf_dbg(CONN, "Processing set ssid\n"); + return true; + } + + return false; +} + +static bool brcmf_is_linkdown(const struct brcmf_event_msg *e) +{ + u32 event = e->event_code; + u16 flags = e->flags; + + if ((event == BRCMF_E_DEAUTH) || (event == BRCMF_E_DEAUTH_IND) || + (event == BRCMF_E_DISASSOC_IND) || + ((event == BRCMF_E_LINK) && (!(flags & BRCMF_EVENT_MSG_LINK)))) { + brcmf_dbg(CONN, "Processing link down\n"); + return true; + } + return false; +} + +static bool brcmf_is_nonetwork(struct brcmf_cfg80211_info *cfg, + const struct brcmf_event_msg *e) +{ + u32 event = e->event_code; + u32 status = e->status; + + if (event == BRCMF_E_LINK && status == BRCMF_E_STATUS_NO_NETWORKS) { + brcmf_dbg(CONN, "Processing Link %s & no network found\n", + e->flags & BRCMF_EVENT_MSG_LINK ? "up" : "down"); + return true; + } + + if (event == BRCMF_E_SET_SSID && status != BRCMF_E_STATUS_SUCCESS) { + brcmf_dbg(CONN, "Processing connecting & no network found\n"); + return true; + } + + return false; +} + +static void brcmf_clear_assoc_ies(struct brcmf_cfg80211_info *cfg) +{ + struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg); + + kfree(conn_info->req_ie); + conn_info->req_ie = NULL; + conn_info->req_ie_len = 0; + kfree(conn_info->resp_ie); + conn_info->resp_ie = NULL; + conn_info->resp_ie_len = 0; +} + +static s32 brcmf_get_assoc_ies(struct brcmf_cfg80211_info *cfg, + struct brcmf_if *ifp) +{ + struct brcmf_cfg80211_assoc_ielen_le *assoc_info; + struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg); + u32 req_len; + u32 resp_len; + s32 err = 0; + + brcmf_clear_assoc_ies(cfg); + + err = brcmf_fil_iovar_data_get(ifp, "assoc_info", + cfg->extra_buf, WL_ASSOC_INFO_MAX); + if (err) { + brcmf_err("could not get assoc info (%d)\n", err); + return err; + } + assoc_info = + (struct brcmf_cfg80211_assoc_ielen_le *)cfg->extra_buf; + req_len = le32_to_cpu(assoc_info->req_len); + resp_len = le32_to_cpu(assoc_info->resp_len); + if (req_len) { + err = brcmf_fil_iovar_data_get(ifp, "assoc_req_ies", + cfg->extra_buf, + WL_ASSOC_INFO_MAX); + if (err) { + brcmf_err("could not get assoc req (%d)\n", err); + return err; + } + conn_info->req_ie_len = req_len; + conn_info->req_ie = + kmemdup(cfg->extra_buf, conn_info->req_ie_len, + GFP_KERNEL); + } else { + conn_info->req_ie_len = 0; + conn_info->req_ie = NULL; + } + if (resp_len) { + err = brcmf_fil_iovar_data_get(ifp, "assoc_resp_ies", + cfg->extra_buf, + WL_ASSOC_INFO_MAX); + if (err) { + brcmf_err("could not get assoc resp (%d)\n", err); + return err; + } + conn_info->resp_ie_len = resp_len; + conn_info->resp_ie = + kmemdup(cfg->extra_buf, conn_info->resp_ie_len, + GFP_KERNEL); + } else { + conn_info->resp_ie_len = 0; + conn_info->resp_ie = NULL; + } + brcmf_dbg(CONN, "req len (%d) resp len (%d)\n", + conn_info->req_ie_len, conn_info->resp_ie_len); + + return err; +} + +static s32 +brcmf_bss_roaming_done(struct brcmf_cfg80211_info *cfg, + struct net_device *ndev, + const struct brcmf_event_msg *e) +{ + struct brcmf_if *ifp = netdev_priv(ndev); + struct brcmf_cfg80211_profile *profile = &ifp->vif->profile; + struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg); + struct wiphy *wiphy = cfg_to_wiphy(cfg); + struct ieee80211_channel *notify_channel = NULL; + struct ieee80211_supported_band *band; + struct brcmf_bss_info_le *bi; + struct brcmu_chan ch; + u32 freq; + s32 err = 0; + u8 *buf; + + brcmf_dbg(TRACE, "Enter\n"); + + brcmf_get_assoc_ies(cfg, ifp); + memcpy(profile->bssid, e->addr, ETH_ALEN); + brcmf_update_bss_info(cfg, ifp); + + buf = kzalloc(WL_BSS_INFO_MAX, GFP_KERNEL); + if (buf == NULL) { + err = -ENOMEM; + goto done; + } + + /* data sent to dongle has to be little endian */ + *(__le32 *)buf = 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) + goto done; + + bi = (struct brcmf_bss_info_le *)(buf + 4); + ch.chspec = le16_to_cpu(bi->chanspec); + cfg->d11inf.decchspec(&ch); + + if (ch.band == BRCMU_CHAN_BAND_2G) + band = wiphy->bands[IEEE80211_BAND_2GHZ]; + else + band = wiphy->bands[IEEE80211_BAND_5GHZ]; + + freq = ieee80211_channel_to_frequency(ch.chnum, band->band); + notify_channel = ieee80211_get_channel(wiphy, freq); + +done: + kfree(buf); + cfg80211_roamed(ndev, notify_channel, (u8 *)profile->bssid, + conn_info->req_ie, conn_info->req_ie_len, + conn_info->resp_ie, conn_info->resp_ie_len, GFP_KERNEL); + brcmf_dbg(CONN, "Report roaming result\n"); + + set_bit(BRCMF_VIF_STATUS_CONNECTED, &ifp->vif->sme_state); + brcmf_dbg(TRACE, "Exit\n"); + return err; +} + +static s32 +brcmf_bss_connect_done(struct brcmf_cfg80211_info *cfg, + struct net_device *ndev, const struct brcmf_event_msg *e, + bool completed) +{ + struct brcmf_if *ifp = netdev_priv(ndev); + struct brcmf_cfg80211_profile *profile = &ifp->vif->profile; + struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg); + + brcmf_dbg(TRACE, "Enter\n"); + + if (test_and_clear_bit(BRCMF_VIF_STATUS_CONNECTING, + &ifp->vif->sme_state)) { + if (completed) { + brcmf_get_assoc_ies(cfg, ifp); + memcpy(profile->bssid, e->addr, ETH_ALEN); + brcmf_update_bss_info(cfg, ifp); + set_bit(BRCMF_VIF_STATUS_CONNECTED, + &ifp->vif->sme_state); + } + cfg80211_connect_result(ndev, + (u8 *)profile->bssid, + conn_info->req_ie, + conn_info->req_ie_len, + conn_info->resp_ie, + conn_info->resp_ie_len, + completed ? WLAN_STATUS_SUCCESS : + WLAN_STATUS_AUTH_TIMEOUT, + GFP_KERNEL); + brcmf_dbg(CONN, "Report connect result - connection %s\n", + completed ? "succeeded" : "failed"); + } + brcmf_dbg(TRACE, "Exit\n"); + return 0; +} + +static s32 +brcmf_notify_connect_status_ap(struct brcmf_cfg80211_info *cfg, + struct net_device *ndev, + const struct brcmf_event_msg *e, void *data) +{ + static int generation; + u32 event = e->event_code; + u32 reason = e->reason; + struct station_info sinfo; + + brcmf_dbg(CONN, "event %d, reason %d\n", event, reason); + if (event == BRCMF_E_LINK && reason == BRCMF_E_REASON_LINK_BSSCFG_DIS && + ndev != cfg_to_ndev(cfg)) { + brcmf_dbg(CONN, "AP mode link down\n"); + complete(&cfg->vif_disabled); + return 0; + } + + if (((event == BRCMF_E_ASSOC_IND) || (event == BRCMF_E_REASSOC_IND)) && + (reason == BRCMF_E_STATUS_SUCCESS)) { + memset(&sinfo, 0, sizeof(sinfo)); + sinfo.filled = STATION_INFO_ASSOC_REQ_IES; + if (!data) { + brcmf_err("No IEs present in ASSOC/REASSOC_IND"); + return -EINVAL; + } + sinfo.assoc_req_ies = data; + sinfo.assoc_req_ies_len = e->datalen; + generation++; + sinfo.generation = generation; + cfg80211_new_sta(ndev, e->addr, &sinfo, GFP_KERNEL); + } else if ((event == BRCMF_E_DISASSOC_IND) || + (event == BRCMF_E_DEAUTH_IND) || + (event == BRCMF_E_DEAUTH)) { + cfg80211_del_sta(ndev, e->addr, GFP_KERNEL); + } + return 0; +} + +static s32 +brcmf_notify_connect_status(struct brcmf_if *ifp, + const struct brcmf_event_msg *e, void *data) +{ + struct brcmf_cfg80211_info *cfg = ifp->drvr->config; + struct net_device *ndev = ifp->ndev; + struct brcmf_cfg80211_profile *profile = &ifp->vif->profile; + struct ieee80211_channel *chan; + s32 err = 0; + + if ((e->event_code == BRCMF_E_DEAUTH) || + (e->event_code == BRCMF_E_DEAUTH_IND) || + (e->event_code == BRCMF_E_DISASSOC_IND) || + ((e->event_code == BRCMF_E_LINK) && (!e->flags))) { + brcmf_proto_delete_peer(ifp->drvr, ifp->ifidx, (u8 *)e->addr); + } + + if (brcmf_is_apmode(ifp->vif)) { + err = brcmf_notify_connect_status_ap(cfg, ndev, e, data); + } else if (brcmf_is_linkup(e)) { + brcmf_dbg(CONN, "Linkup\n"); + if (brcmf_is_ibssmode(ifp->vif)) { + chan = ieee80211_get_channel(cfg->wiphy, cfg->channel); + memcpy(profile->bssid, e->addr, ETH_ALEN); + wl_inform_ibss(cfg, ndev, e->addr); + cfg80211_ibss_joined(ndev, e->addr, chan, GFP_KERNEL); + clear_bit(BRCMF_VIF_STATUS_CONNECTING, + &ifp->vif->sme_state); + set_bit(BRCMF_VIF_STATUS_CONNECTED, + &ifp->vif->sme_state); + } else + brcmf_bss_connect_done(cfg, ndev, e, true); + } else if (brcmf_is_linkdown(e)) { + brcmf_dbg(CONN, "Linkdown\n"); + if (!brcmf_is_ibssmode(ifp->vif)) { + brcmf_bss_connect_done(cfg, ndev, e, false); + } + brcmf_link_down(ifp->vif); + brcmf_init_prof(ndev_to_prof(ndev)); + if (ndev != cfg_to_ndev(cfg)) + complete(&cfg->vif_disabled); + } else if (brcmf_is_nonetwork(cfg, e)) { + if (brcmf_is_ibssmode(ifp->vif)) + clear_bit(BRCMF_VIF_STATUS_CONNECTING, + &ifp->vif->sme_state); + else + brcmf_bss_connect_done(cfg, ndev, e, false); + } + + return err; +} + +static s32 +brcmf_notify_roaming_status(struct brcmf_if *ifp, + const struct brcmf_event_msg *e, void *data) +{ + struct brcmf_cfg80211_info *cfg = ifp->drvr->config; + u32 event = e->event_code; + u32 status = e->status; + + if (event == BRCMF_E_ROAM && status == BRCMF_E_STATUS_SUCCESS) { + if (test_bit(BRCMF_VIF_STATUS_CONNECTED, &ifp->vif->sme_state)) + brcmf_bss_roaming_done(cfg, ifp->ndev, e); + else + brcmf_bss_connect_done(cfg, ifp->ndev, e, true); + } + + return 0; +} + +static s32 +brcmf_notify_mic_status(struct brcmf_if *ifp, + const struct brcmf_event_msg *e, void *data) +{ + u16 flags = e->flags; + enum nl80211_key_type key_type; + + if (flags & BRCMF_EVENT_MSG_GROUP) + key_type = NL80211_KEYTYPE_GROUP; + else + key_type = NL80211_KEYTYPE_PAIRWISE; + + cfg80211_michael_mic_failure(ifp->ndev, (u8 *)&e->addr, key_type, -1, + NULL, GFP_KERNEL); + + return 0; +} + +static s32 brcmf_notify_vif_event(struct brcmf_if *ifp, + const struct brcmf_event_msg *e, void *data) +{ + struct brcmf_cfg80211_info *cfg = ifp->drvr->config; + struct brcmf_if_event *ifevent = (struct brcmf_if_event *)data; + struct brcmf_cfg80211_vif_event *event = &cfg->vif_event; + struct brcmf_cfg80211_vif *vif; + + brcmf_dbg(TRACE, "Enter: action %u flags %u ifidx %u bsscfg %u\n", + ifevent->action, ifevent->flags, ifevent->ifidx, + ifevent->bssidx); + + mutex_lock(&event->vif_event_lock); + event->action = ifevent->action; + vif = event->vif; + + switch (ifevent->action) { + case BRCMF_E_IF_ADD: + /* waiting process may have timed out */ + if (!cfg->vif_event.vif) { + mutex_unlock(&event->vif_event_lock); + return -EBADF; + } + + ifp->vif = vif; + vif->ifp = ifp; + if (ifp->ndev) { + vif->wdev.netdev = ifp->ndev; + ifp->ndev->ieee80211_ptr = &vif->wdev; + SET_NETDEV_DEV(ifp->ndev, wiphy_dev(cfg->wiphy)); + } + mutex_unlock(&event->vif_event_lock); + wake_up(&event->vif_wq); + return 0; + + case BRCMF_E_IF_DEL: + mutex_unlock(&event->vif_event_lock); + /* event may not be upon user request */ + if (brcmf_cfg80211_vif_event_armed(cfg)) + wake_up(&event->vif_wq); + return 0; + + case BRCMF_E_IF_CHANGE: + mutex_unlock(&event->vif_event_lock); + wake_up(&event->vif_wq); + return 0; + + default: + mutex_unlock(&event->vif_event_lock); + break; + } + return -EINVAL; +} + +static void brcmf_init_conf(struct brcmf_cfg80211_conf *conf) +{ + conf->frag_threshold = (u32)-1; + conf->rts_threshold = (u32)-1; + conf->retry_short = (u32)-1; + conf->retry_long = (u32)-1; + conf->tx_power = -1; +} + +static void brcmf_register_event_handlers(struct brcmf_cfg80211_info *cfg) +{ + brcmf_fweh_register(cfg->pub, BRCMF_E_LINK, + brcmf_notify_connect_status); + brcmf_fweh_register(cfg->pub, BRCMF_E_DEAUTH_IND, + brcmf_notify_connect_status); + brcmf_fweh_register(cfg->pub, BRCMF_E_DEAUTH, + brcmf_notify_connect_status); + brcmf_fweh_register(cfg->pub, BRCMF_E_DISASSOC_IND, + brcmf_notify_connect_status); + brcmf_fweh_register(cfg->pub, BRCMF_E_ASSOC_IND, + brcmf_notify_connect_status); + brcmf_fweh_register(cfg->pub, BRCMF_E_REASSOC_IND, + brcmf_notify_connect_status); + brcmf_fweh_register(cfg->pub, BRCMF_E_ROAM, + brcmf_notify_roaming_status); + brcmf_fweh_register(cfg->pub, BRCMF_E_MIC_ERROR, + brcmf_notify_mic_status); + brcmf_fweh_register(cfg->pub, BRCMF_E_SET_SSID, + brcmf_notify_connect_status); + brcmf_fweh_register(cfg->pub, BRCMF_E_PFN_NET_FOUND, + brcmf_notify_sched_scan_results); + brcmf_fweh_register(cfg->pub, BRCMF_E_IF, + brcmf_notify_vif_event); + brcmf_fweh_register(cfg->pub, BRCMF_E_P2P_PROBEREQ_MSG, + brcmf_p2p_notify_rx_mgmt_p2p_probereq); + brcmf_fweh_register(cfg->pub, BRCMF_E_P2P_DISC_LISTEN_COMPLETE, + brcmf_p2p_notify_listen_complete); + brcmf_fweh_register(cfg->pub, BRCMF_E_ACTION_FRAME_RX, + brcmf_p2p_notify_action_frame_rx); + brcmf_fweh_register(cfg->pub, BRCMF_E_ACTION_FRAME_COMPLETE, + brcmf_p2p_notify_action_tx_complete); + brcmf_fweh_register(cfg->pub, BRCMF_E_ACTION_FRAME_OFF_CHAN_COMPLETE, + brcmf_p2p_notify_action_tx_complete); +} + +static void brcmf_deinit_priv_mem(struct brcmf_cfg80211_info *cfg) +{ + kfree(cfg->conf); + cfg->conf = NULL; + kfree(cfg->escan_ioctl_buf); + cfg->escan_ioctl_buf = NULL; + kfree(cfg->extra_buf); + cfg->extra_buf = NULL; + kfree(cfg->pmk_list); + cfg->pmk_list = NULL; +} + +static s32 brcmf_init_priv_mem(struct brcmf_cfg80211_info *cfg) +{ + cfg->conf = kzalloc(sizeof(*cfg->conf), GFP_KERNEL); + if (!cfg->conf) + goto init_priv_mem_out; + cfg->escan_ioctl_buf = kzalloc(BRCMF_DCMD_MEDLEN, GFP_KERNEL); + if (!cfg->escan_ioctl_buf) + goto init_priv_mem_out; + cfg->extra_buf = kzalloc(WL_EXTRA_BUF_MAX, GFP_KERNEL); + if (!cfg->extra_buf) + goto init_priv_mem_out; + cfg->pmk_list = kzalloc(sizeof(*cfg->pmk_list), GFP_KERNEL); + if (!cfg->pmk_list) + goto init_priv_mem_out; + + return 0; + +init_priv_mem_out: + brcmf_deinit_priv_mem(cfg); + + return -ENOMEM; +} + +static s32 wl_init_priv(struct brcmf_cfg80211_info *cfg) +{ + s32 err = 0; + + cfg->scan_request = NULL; + cfg->pwr_save = true; + cfg->active_scan = true; /* we do active scan per default */ + cfg->dongle_up = false; /* dongle is not up yet */ + err = brcmf_init_priv_mem(cfg); + if (err) + return err; + brcmf_register_event_handlers(cfg); + mutex_init(&cfg->usr_sync); + brcmf_init_escan(cfg); + brcmf_init_conf(cfg->conf); + init_completion(&cfg->vif_disabled); + return err; +} + +static void wl_deinit_priv(struct brcmf_cfg80211_info *cfg) +{ + cfg->dongle_up = false; /* dongle down */ + brcmf_abort_scanning(cfg); + brcmf_deinit_priv_mem(cfg); +} + +static void init_vif_event(struct brcmf_cfg80211_vif_event *event) +{ + init_waitqueue_head(&event->vif_wq); + mutex_init(&event->vif_event_lock); +} + +static s32 +brcmf_dongle_roam(struct brcmf_if *ifp, u32 bcn_timeout) +{ + s32 err = 0; + __le32 roamtrigger[2]; + __le32 roam_delta[2]; + + /* + * Setup timeout if Beacons are lost and roam is + * off to report link down + */ + if (brcmf_roamoff) { + err = brcmf_fil_iovar_int_set(ifp, "bcn_timeout", bcn_timeout); + if (err) { + brcmf_err("bcn_timeout error (%d)\n", err); + goto dongle_rom_out; + } + } + + /* + * Enable/Disable built-in roaming to allow supplicant + * to take care of roaming + */ + brcmf_dbg(INFO, "Internal Roaming = %s\n", + brcmf_roamoff ? "Off" : "On"); + err = brcmf_fil_iovar_int_set(ifp, "roam_off", !!(brcmf_roamoff)); + if (err) { + brcmf_err("roam_off error (%d)\n", err); + goto dongle_rom_out; + } + + roamtrigger[0] = cpu_to_le32(WL_ROAM_TRIGGER_LEVEL); + roamtrigger[1] = cpu_to_le32(BRCM_BAND_ALL); + err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_ROAM_TRIGGER, + (void *)roamtrigger, sizeof(roamtrigger)); + if (err) { + brcmf_err("WLC_SET_ROAM_TRIGGER error (%d)\n", err); + goto dongle_rom_out; + } + + roam_delta[0] = cpu_to_le32(WL_ROAM_DELTA); + roam_delta[1] = cpu_to_le32(BRCM_BAND_ALL); + err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_ROAM_DELTA, + (void *)roam_delta, sizeof(roam_delta)); + if (err) { + brcmf_err("WLC_SET_ROAM_DELTA error (%d)\n", err); + goto dongle_rom_out; + } + +dongle_rom_out: + return err; +} + +static s32 +brcmf_dongle_scantime(struct brcmf_if *ifp, s32 scan_assoc_time, + s32 scan_unassoc_time, s32 scan_passive_time) +{ + s32 err = 0; + + err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_SCAN_CHANNEL_TIME, + scan_assoc_time); + if (err) { + if (err == -EOPNOTSUPP) + brcmf_dbg(INFO, "Scan assoc time is not supported\n"); + else + brcmf_err("Scan assoc time error (%d)\n", err); + goto dongle_scantime_out; + } + err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_SCAN_UNASSOC_TIME, + scan_unassoc_time); + if (err) { + if (err == -EOPNOTSUPP) + brcmf_dbg(INFO, "Scan unassoc time is not supported\n"); + else + brcmf_err("Scan unassoc time error (%d)\n", err); + goto dongle_scantime_out; + } + + err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_SCAN_PASSIVE_TIME, + scan_passive_time); + if (err) { + if (err == -EOPNOTSUPP) + brcmf_dbg(INFO, "Scan passive time is not supported\n"); + else + brcmf_err("Scan passive time error (%d)\n", err); + goto dongle_scantime_out; + } + +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) +{ + u32 ht40_flag; + + ht40_flag = channel->flags & IEEE80211_CHAN_NO_HT40; + if (ch->sb == BRCMU_CHAN_SB_U) { + if (ht40_flag == IEEE80211_CHAN_NO_HT40) + channel->flags &= ~IEEE80211_CHAN_NO_HT40; + channel->flags |= IEEE80211_CHAN_NO_HT40PLUS; + } else { + /* It should be one of + * IEEE80211_CHAN_NO_HT40 or + * IEEE80211_CHAN_NO_HT40PLUS + */ + channel->flags &= ~IEEE80211_CHAN_NO_HT40; + if (ht40_flag == IEEE80211_CHAN_NO_HT40) + channel->flags |= IEEE80211_CHAN_NO_HT40MINUS; + } +} + +static int brcmf_construct_chaninfo(struct brcmf_cfg80211_info *cfg, + u32 bw_cap[]) +{ + struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg)); + struct ieee80211_supported_band *band; + struct ieee80211_channel *channel; + struct wiphy *wiphy; + struct brcmf_chanspec_list *list; + struct brcmu_chan ch; + int err; + u8 *pbuf; + u32 i, j; + u32 total; + u32 chaninfo; + u32 chcnt[2] = { 0, 0 }; + u32 index; + + pbuf = kzalloc(BRCMF_DCMD_MEDLEN, GFP_KERNEL); + + if (pbuf == NULL) + return -ENOMEM; + + list = (struct brcmf_chanspec_list *)pbuf; + + err = brcmf_fil_iovar_data_get(ifp, "chanspecs", pbuf, + BRCMF_DCMD_MEDLEN); + if (err) { + brcmf_err("get chanspecs error (%d)\n", err); + 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; + } + + total = le32_to_cpu(list->count); + for (i = 0; i < total; i++) { + ch.chspec = (u16)le32_to_cpu(list->element[i]); + cfg->d11inf.decchspec(&ch); + + if (ch.band == BRCMU_CHAN_BAND_2G) { + band = wiphy->bands[IEEE80211_BAND_2GHZ]; + } else if (ch.band == BRCMU_CHAN_BAND_5G) { + band = wiphy->bands[IEEE80211_BAND_5GHZ]; + } else { + brcmf_err("Invalid channel Spec. 0x%x.\n", ch.chspec); + continue; + } + if (!(bw_cap[band->band] & WLC_BW_40MHZ_BIT) && + ch.bw == BRCMU_CHAN_BW_40) + continue; + if (!(bw_cap[band->band] & WLC_BW_80MHZ_BIT) && + ch.bw == BRCMU_CHAN_BW_80) + continue; + + channel = band->channels; + index = band->n_channels; + for (j = 0; j < band->n_channels; j++) { + if (channel[j].hw_value == ch.chnum) { + index = j; + break; + } + } + channel[index].center_freq = + ieee80211_channel_to_frequency(ch.chnum, band->band); + channel[index].hw_value = ch.chnum; + + /* assuming the chanspecs order is HT20, + * HT40 upper, HT40 lower, and VHT80. + */ + if (ch.bw == BRCMU_CHAN_BW_80) { + channel[index].flags &= ~IEEE80211_CHAN_NO_80MHZ; + } 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. + */ + channel[index].flags = IEEE80211_CHAN_NO_HT40 | + IEEE80211_CHAN_NO_80MHZ; + ch.bw = BRCMU_CHAN_BW_20; + cfg->d11inf.encchspec(&ch); + chaninfo = ch.chspec; + err = brcmf_fil_bsscfg_int_get(ifp, "per_chan_info", + &chaninfo); + if (!err) { + if (chaninfo & WL_CHAN_RADAR) + channel[index].flags |= + (IEEE80211_CHAN_RADAR | + IEEE80211_CHAN_NO_IR); + if (chaninfo & WL_CHAN_PASSIVE) + channel[index].flags |= + 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; +} + +static int brcmf_enable_bw40_2g(struct brcmf_cfg80211_info *cfg) +{ + struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg)); + struct ieee80211_supported_band *band; + struct brcmf_fil_bwcap_le band_bwcap; + struct brcmf_chanspec_list *list; + u8 *pbuf; + u32 val; + int err; + struct brcmu_chan ch; + u32 num_chan; + int i, j; + + /* verify support for bw_cap command */ + val = WLC_BAND_5G; + err = brcmf_fil_iovar_int_get(ifp, "bw_cap", &val); + + if (!err) { + /* only set 2G bandwidth using bw_cap command */ + band_bwcap.band = cpu_to_le32(WLC_BAND_2G); + band_bwcap.bw_cap = cpu_to_le32(WLC_BW_CAP_40MHZ); + err = brcmf_fil_iovar_data_set(ifp, "bw_cap", &band_bwcap, + sizeof(band_bwcap)); + } else { + brcmf_dbg(INFO, "fallback to mimo_bw_cap\n"); + val = WLC_N_BW_40ALL; + err = brcmf_fil_iovar_int_set(ifp, "mimo_bw_cap", val); + } + + if (!err) { + /* update channel info in 2G band */ + pbuf = kzalloc(BRCMF_DCMD_MEDLEN, GFP_KERNEL); + + if (pbuf == NULL) + return -ENOMEM; + + ch.band = BRCMU_CHAN_BAND_2G; + ch.bw = BRCMU_CHAN_BW_40; + ch.sb = BRCMU_CHAN_SB_NONE; + ch.chnum = 0; + cfg->d11inf.encchspec(&ch); + + /* pass encoded chanspec in query */ + *(__le16 *)pbuf = cpu_to_le16(ch.chspec); + + err = brcmf_fil_iovar_data_get(ifp, "chanspecs", pbuf, + BRCMF_DCMD_MEDLEN); + if (err) { + brcmf_err("get chanspecs error (%d)\n", err); + kfree(pbuf); + return err; + } + + band = cfg_to_wiphy(cfg)->bands[IEEE80211_BAND_2GHZ]; + list = (struct brcmf_chanspec_list *)pbuf; + num_chan = le32_to_cpu(list->count); + for (i = 0; i < num_chan; i++) { + ch.chspec = (u16)le32_to_cpu(list->element[i]); + cfg->d11inf.decchspec(&ch); + if (WARN_ON(ch.band != BRCMU_CHAN_BAND_2G)) + continue; + if (WARN_ON(ch.bw != BRCMU_CHAN_BW_40)) + continue; + for (j = 0; j < band->n_channels; j++) { + if (band->channels[j].hw_value == ch.chnum) + break; + } + if (WARN_ON(j == band->n_channels)) + continue; + + brcmf_update_bw40_channel_flag(&band->channels[j], &ch); + } + kfree(pbuf); + } + return err; +} + +static void brcmf_get_bwcap(struct brcmf_if *ifp, u32 bw_cap[]) +{ + u32 band, mimo_bwcap; + int err; + + band = WLC_BAND_2G; + err = brcmf_fil_iovar_int_get(ifp, "bw_cap", &band); + if (!err) { + bw_cap[IEEE80211_BAND_2GHZ] = band; + band = WLC_BAND_5G; + err = brcmf_fil_iovar_int_get(ifp, "bw_cap", &band); + if (!err) { + bw_cap[IEEE80211_BAND_5GHZ] = band; + return; + } + WARN_ON(1); + return; + } + brcmf_dbg(INFO, "fallback to mimo_bw_cap info\n"); + mimo_bwcap = 0; + err = brcmf_fil_iovar_int_get(ifp, "mimo_bw_cap", &mimo_bwcap); + if (err) + /* assume 20MHz if firmware does not give a clue */ + mimo_bwcap = WLC_N_BW_20ALL; + + switch (mimo_bwcap) { + case WLC_N_BW_40ALL: + bw_cap[IEEE80211_BAND_2GHZ] |= WLC_BW_40MHZ_BIT; + /* fall-thru */ + case WLC_N_BW_20IN2G_40IN5G: + bw_cap[IEEE80211_BAND_5GHZ] |= WLC_BW_40MHZ_BIT; + /* fall-thru */ + case WLC_N_BW_20ALL: + bw_cap[IEEE80211_BAND_2GHZ] |= WLC_BW_20MHZ_BIT; + bw_cap[IEEE80211_BAND_5GHZ] |= WLC_BW_20MHZ_BIT; + break; + default: + brcmf_err("invalid mimo_bw_cap value\n"); + } +} + +static void brcmf_update_ht_cap(struct ieee80211_supported_band *band, + u32 bw_cap[2], u32 nchain) +{ + band->ht_cap.ht_supported = true; + if (bw_cap[band->band] & WLC_BW_40MHZ_BIT) { + band->ht_cap.cap |= IEEE80211_HT_CAP_SGI_40; + band->ht_cap.cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40; + } + band->ht_cap.cap |= IEEE80211_HT_CAP_SGI_20; + band->ht_cap.cap |= IEEE80211_HT_CAP_DSSSCCK40; + band->ht_cap.ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K; + band->ht_cap.ampdu_density = IEEE80211_HT_MPDU_DENSITY_16; + memset(band->ht_cap.mcs.rx_mask, 0xff, nchain); + band->ht_cap.mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED; +} + +static __le16 brcmf_get_mcs_map(u32 nchain, enum ieee80211_vht_mcs_support supp) +{ + u16 mcs_map; + int i; + + for (i = 0, mcs_map = 0xFFFF; i < nchain; i++) + mcs_map = (mcs_map << 2) | supp; + + return cpu_to_le16(mcs_map); +} + +static void brcmf_update_vht_cap(struct ieee80211_supported_band *band, + u32 bw_cap[2], u32 nchain) +{ + __le16 mcs_map; + + /* not allowed in 2.4G band */ + if (band->band == IEEE80211_BAND_2GHZ) + return; + + band->vht_cap.vht_supported = true; + /* 80MHz is mandatory */ + band->vht_cap.cap |= IEEE80211_VHT_CAP_SHORT_GI_80; + if (bw_cap[band->band] & WLC_BW_160MHZ_BIT) { + band->vht_cap.cap |= IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ; + band->vht_cap.cap |= IEEE80211_VHT_CAP_SHORT_GI_160; + } + /* all support 256-QAM */ + mcs_map = brcmf_get_mcs_map(nchain, IEEE80211_VHT_MCS_SUPPORT_0_9); + band->vht_cap.vht_mcs.rx_mcs_map = mcs_map; + band->vht_cap.vht_mcs.tx_mcs_map = mcs_map; +} + +static int brcmf_setup_wiphybands(struct wiphy *wiphy) +{ + struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy); + struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg)); + u32 nmode = 0; + u32 vhtmode = 0; + u32 bw_cap[2] = { WLC_BW_20MHZ_BIT, WLC_BW_20MHZ_BIT }; + u32 rxchain; + u32 nchain; + int err; + s32 i; + struct ieee80211_supported_band *band; + + (void)brcmf_fil_iovar_int_get(ifp, "vhtmode", &vhtmode); + err = brcmf_fil_iovar_int_get(ifp, "nmode", &nmode); + if (err) { + brcmf_err("nmode error (%d)\n", err); + } else { + brcmf_get_bwcap(ifp, bw_cap); + } + brcmf_dbg(INFO, "nmode=%d, vhtmode=%d, bw_cap=(%d, %d)\n", + nmode, vhtmode, bw_cap[IEEE80211_BAND_2GHZ], + bw_cap[IEEE80211_BAND_5GHZ]); + + err = brcmf_fil_iovar_int_get(ifp, "rxchain", &rxchain); + if (err) { + brcmf_err("rxchain error (%d)\n", err); + nchain = 1; + } else { + for (nchain = 0; rxchain; nchain++) + rxchain = rxchain & (rxchain - 1); + } + brcmf_dbg(INFO, "nchain=%d\n", nchain); + + err = brcmf_construct_chaninfo(cfg, bw_cap); + if (err) { + brcmf_err("brcmf_construct_chaninfo failed (%d)\n", err); + return err; + } + + wiphy = cfg_to_wiphy(cfg); + for (i = 0; i < ARRAY_SIZE(wiphy->bands); i++) { + band = wiphy->bands[i]; + if (band == NULL) + continue; + + if (nmode) + brcmf_update_ht_cap(band, bw_cap, nchain); + if (vhtmode) + brcmf_update_vht_cap(band, bw_cap, nchain); + } + + return 0; +} + +static const struct ieee80211_iface_limit brcmf_iface_limits[] = { + { + .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), + .limits = brcmf_iface_limits + } +}; + +static const struct ieee80211_txrx_stypes +brcmf_txrx_stypes[NUM_NL80211_IFTYPES] = { + [NL80211_IFTYPE_STATION] = { + .tx = 0xffff, + .rx = BIT(IEEE80211_STYPE_ACTION >> 4) | + BIT(IEEE80211_STYPE_PROBE_REQ >> 4) + }, + [NL80211_IFTYPE_P2P_CLIENT] = { + .tx = 0xffff, + .rx = BIT(IEEE80211_STYPE_ACTION >> 4) | + BIT(IEEE80211_STYPE_PROBE_REQ >> 4) + }, + [NL80211_IFTYPE_P2P_GO] = { + .tx = 0xffff, + .rx = BIT(IEEE80211_STYPE_ASSOC_REQ >> 4) | + BIT(IEEE80211_STYPE_REASSOC_REQ >> 4) | + BIT(IEEE80211_STYPE_PROBE_REQ >> 4) | + BIT(IEEE80211_STYPE_DISASSOC >> 4) | + BIT(IEEE80211_STYPE_AUTH >> 4) | + BIT(IEEE80211_STYPE_DEAUTH >> 4) | + BIT(IEEE80211_STYPE_ACTION >> 4) + }, + [NL80211_IFTYPE_P2P_DEVICE] = { + .tx = 0xffff, + .rx = BIT(IEEE80211_STYPE_ACTION >> 4) | + BIT(IEEE80211_STYPE_PROBE_REQ >> 4) + } +}; + +static void brcmf_wiphy_pno_params(struct wiphy *wiphy) +{ + /* scheduled scan settings */ + wiphy->max_sched_scan_ssids = BRCMF_PNO_MAX_PFN_COUNT; + wiphy->max_match_sets = BRCMF_PNO_MAX_PFN_COUNT; + wiphy->max_sched_scan_ie_len = BRCMF_SCAN_IE_LEN_MAX; + wiphy->flags |= WIPHY_FLAG_SUPPORTS_SCHED_SCAN; +} + +#ifdef CONFIG_PM +static const struct wiphy_wowlan_support brcmf_wowlan_support = { + .flags = WIPHY_WOWLAN_MAGIC_PKT | WIPHY_WOWLAN_DISCONNECT, + .n_patterns = BRCMF_WOWL_MAXPATTERNS, + .pattern_max_len = BRCMF_WOWL_MAXPATTERNSIZE, + .pattern_min_len = 1, + .max_pkt_offset = 1500, +}; +#endif + +static void brcmf_wiphy_wowl_params(struct wiphy *wiphy) +{ +#ifdef CONFIG_PM + /* wowl settings */ + wiphy->wowlan = &brcmf_wowlan_support; +#endif +} + +static int brcmf_setup_wiphy(struct wiphy *wiphy, struct brcmf_if *ifp) +{ + struct ieee80211_iface_combination ifc_combo; + 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; + wiphy->iface_combinations = kmemdup(&ifc_combo, + sizeof(ifc_combo), + GFP_KERNEL); + wiphy->n_iface_combinations = ARRAY_SIZE(brcmf_iface_combos); + wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM; + wiphy->cipher_suites = __wl_cipher_suites; + wiphy->n_cipher_suites = ARRAY_SIZE(__wl_cipher_suites); + wiphy->flags |= WIPHY_FLAG_PS_ON_BY_DEFAULT | + WIPHY_FLAG_OFFCHAN_TX | + WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL | + WIPHY_FLAG_SUPPORTS_TDLS; + if (!brcmf_roamoff) + 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); + + /* vendor commands/events support */ + wiphy->vendor_commands = brcmf_vendor_cmds; + wiphy->n_vendor_commands = BRCMF_VNDR_CMDS_LAST - 1; + + if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_WOWL)) + brcmf_wiphy_wowl_params(wiphy); + + return brcmf_setup_wiphybands(wiphy); +} + +static s32 brcmf_config_dongle(struct brcmf_cfg80211_info *cfg) +{ + struct net_device *ndev; + struct wireless_dev *wdev; + struct brcmf_if *ifp; + s32 power_mode; + s32 err = 0; + + if (cfg->dongle_up) + return err; + + ndev = cfg_to_ndev(cfg); + wdev = ndev->ieee80211_ptr; + ifp = netdev_priv(ndev); + + /* make sure RF is ready for work */ + brcmf_fil_cmd_int_set(ifp, BRCMF_C_UP, 0); + + brcmf_dongle_scantime(ifp, WL_SCAN_CHANNEL_TIME, + WL_SCAN_UNASSOC_TIME, WL_SCAN_PASSIVE_TIME); + + power_mode = cfg->pwr_save ? PM_FAST : PM_OFF; + err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM, power_mode); + if (err) + goto default_conf_out; + brcmf_dbg(INFO, "power save set to %s\n", + (power_mode ? "enabled" : "disabled")); + + err = brcmf_dongle_roam(ifp, WL_BEACON_TIMEOUT); + if (err) + goto default_conf_out; + err = brcmf_cfg80211_change_iface(wdev->wiphy, ndev, wdev->iftype, + NULL, NULL); + if (err) + goto default_conf_out; + + brcmf_configure_arp_offload(ifp, true); + + cfg->dongle_up = true; +default_conf_out: + + return err; + +} + +static s32 __brcmf_cfg80211_up(struct brcmf_if *ifp) +{ + set_bit(BRCMF_VIF_STATUS_READY, &ifp->vif->sme_state); + + return brcmf_config_dongle(ifp->drvr->config); +} + +static s32 __brcmf_cfg80211_down(struct brcmf_if *ifp) +{ + struct brcmf_cfg80211_info *cfg = ifp->drvr->config; + + /* + * While going down, if associated with AP disassociate + * from AP to save power + */ + if (check_vif_up(ifp->vif)) { + brcmf_link_down(ifp->vif); + + /* Make sure WPA_Supplicant receives all the event + generated due to DISASSOC call to the fw to keep + the state fw and WPA_Supplicant state consistent + */ + brcmf_delay(500); + } + + brcmf_abort_scanning(cfg); + clear_bit(BRCMF_VIF_STATUS_READY, &ifp->vif->sme_state); + + return 0; +} + +s32 brcmf_cfg80211_up(struct net_device *ndev) +{ + struct brcmf_if *ifp = netdev_priv(ndev); + struct brcmf_cfg80211_info *cfg = ifp->drvr->config; + s32 err = 0; + + mutex_lock(&cfg->usr_sync); + err = __brcmf_cfg80211_up(ifp); + mutex_unlock(&cfg->usr_sync); + + return err; +} + +s32 brcmf_cfg80211_down(struct net_device *ndev) +{ + struct brcmf_if *ifp = netdev_priv(ndev); + struct brcmf_cfg80211_info *cfg = ifp->drvr->config; + s32 err = 0; + + mutex_lock(&cfg->usr_sync); + err = __brcmf_cfg80211_down(ifp); + mutex_unlock(&cfg->usr_sync); + + return err; +} + +enum nl80211_iftype brcmf_cfg80211_get_iftype(struct brcmf_if *ifp) +{ + struct wireless_dev *wdev = &ifp->vif->wdev; + + return wdev->iftype; +} + +bool brcmf_get_vif_state_any(struct brcmf_cfg80211_info *cfg, + unsigned long state) +{ + struct brcmf_cfg80211_vif *vif; + + list_for_each_entry(vif, &cfg->vif_list, list) { + if (test_bit(state, &vif->sme_state)) + return true; + } + return false; +} + +static inline bool vif_event_equals(struct brcmf_cfg80211_vif_event *event, + u8 action) +{ + u8 evt_action; + + mutex_lock(&event->vif_event_lock); + evt_action = event->action; + mutex_unlock(&event->vif_event_lock); + return evt_action == action; +} + +void brcmf_cfg80211_arm_vif_event(struct brcmf_cfg80211_info *cfg, + struct brcmf_cfg80211_vif *vif) +{ + struct brcmf_cfg80211_vif_event *event = &cfg->vif_event; + + mutex_lock(&event->vif_event_lock); + event->vif = vif; + event->action = 0; + mutex_unlock(&event->vif_event_lock); +} + +bool brcmf_cfg80211_vif_event_armed(struct brcmf_cfg80211_info *cfg) +{ + struct brcmf_cfg80211_vif_event *event = &cfg->vif_event; + bool armed; + + mutex_lock(&event->vif_event_lock); + armed = event->vif != NULL; + mutex_unlock(&event->vif_event_lock); + + return armed; +} +int brcmf_cfg80211_wait_vif_event_timeout(struct brcmf_cfg80211_info *cfg, + u8 action, ulong timeout) +{ + struct brcmf_cfg80211_vif_event *event = &cfg->vif_event; + + return wait_event_timeout(event->vif_wq, + vif_event_equals(event, action), timeout); +} + +static void brcmf_free_wiphy(struct wiphy *wiphy) +{ + kfree(wiphy->iface_combinations); + if (wiphy->bands[IEEE80211_BAND_2GHZ]) { + kfree(wiphy->bands[IEEE80211_BAND_2GHZ]->channels); + kfree(wiphy->bands[IEEE80211_BAND_2GHZ]); + } + if (wiphy->bands[IEEE80211_BAND_5GHZ]) { + kfree(wiphy->bands[IEEE80211_BAND_5GHZ]->channels); + kfree(wiphy->bands[IEEE80211_BAND_5GHZ]); + } + wiphy_free(wiphy); +} + +struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr, + struct device *busdev) +{ + struct net_device *ndev = drvr->iflist[0]->ndev; + struct brcmf_cfg80211_info *cfg; + struct wiphy *wiphy; + struct brcmf_cfg80211_vif *vif; + struct brcmf_if *ifp; + s32 err = 0; + s32 io_type; + u16 *cap = NULL; + + if (!ndev) { + brcmf_err("ndev is invalid\n"); + return NULL; + } + + ifp = netdev_priv(ndev); + wiphy = wiphy_new(&wl_cfg80211_ops, sizeof(struct brcmf_cfg80211_info)); + if (!wiphy) { + brcmf_err("Could not allocate wiphy device\n"); + return NULL; + } + set_wiphy_dev(wiphy, busdev); + + cfg = wiphy_priv(wiphy); + cfg->wiphy = wiphy; + cfg->pub = drvr; + init_vif_event(&cfg->vif_event); + INIT_LIST_HEAD(&cfg->vif_list); + + vif = brcmf_alloc_vif(cfg, NL80211_IFTYPE_STATION, false); + if (IS_ERR(vif)) + goto wiphy_out; + + vif->ifp = ifp; + vif->wdev.netdev = ndev; + ndev->ieee80211_ptr = &vif->wdev; + SET_NETDEV_DEV(ndev, wiphy_dev(cfg->wiphy)); + + err = wl_init_priv(cfg); + if (err) { + brcmf_err("Failed to init iwm_priv (%d)\n", err); + brcmf_free_vif(vif); + goto wiphy_out; + } + ifp->vif = vif; + + /* determine d11 io type before wiphy setup */ + err = brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_VERSION, &io_type); + if (err) { + brcmf_err("Failed to get D11 version (%d)\n", err); + goto priv_out; + } + cfg->d11inf.io_type = (u8)io_type; + brcmu_d11_attach(&cfg->d11inf); + + err = brcmf_setup_wiphy(wiphy, ifp); + if (err < 0) + goto priv_out; + + brcmf_dbg(INFO, "Registering custom regulatory\n"); + wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG; + wiphy_apply_custom_regulatory(wiphy, &brcmf_regdom); + + /* firmware defaults to 40MHz disabled in 2G band. We signal + * cfg80211 here that we do and have it decide we can enable + * it. But first check if device does support 2G operation. + */ + if (wiphy->bands[IEEE80211_BAND_2GHZ]) { + cap = &wiphy->bands[IEEE80211_BAND_2GHZ]->ht_cap.cap; + *cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40; + } + err = wiphy_register(wiphy); + if (err < 0) { + brcmf_err("Could not register wiphy device (%d)\n", err); + goto priv_out; + } + + /* If cfg80211 didn't disable 40MHz HT CAP in wiphy_register(), + * setup 40MHz in 2GHz band and enable OBSS scanning. + */ + if (cap && (*cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40)) { + err = brcmf_enable_bw40_2g(cfg); + if (!err) + err = brcmf_fil_iovar_int_set(ifp, "obss_coex", + BRCMF_OBSS_COEX_AUTO); + else + *cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40; + } + + err = brcmf_p2p_attach(cfg); + if (err) { + brcmf_err("P2P initilisation failed (%d)\n", err); + goto wiphy_unreg_out; + } + err = brcmf_btcoex_attach(cfg); + if (err) { + brcmf_err("BT-coex initialisation failed (%d)\n", err); + brcmf_p2p_detach(&cfg->p2p); + goto wiphy_unreg_out; + } + + err = brcmf_fil_iovar_int_set(ifp, "tdls_enable", 1); + if (err) { + brcmf_dbg(INFO, "TDLS not enabled (%d)\n", err); + wiphy->flags &= ~WIPHY_FLAG_SUPPORTS_TDLS; + } else { + brcmf_fweh_register(cfg->pub, BRCMF_E_TDLS_PEER_EVENT, + brcmf_notify_tdls_peer_event); + } + + return cfg; + +wiphy_unreg_out: + wiphy_unregister(cfg->wiphy); +priv_out: + wl_deinit_priv(cfg); + brcmf_free_vif(vif); +wiphy_out: + brcmf_free_wiphy(wiphy); + return NULL; +} + +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); + wl_deinit_priv(cfg); + brcmf_free_wiphy(cfg->wiphy); +} diff --git a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.h b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.h new file mode 100644 index 000000000000..2a5b22cb3fef --- /dev/null +++ b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.h @@ -0,0 +1,497 @@ +/* + * Copyright (c) 2010 Broadcom Corporation + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef BRCMFMAC_CFG80211_H +#define BRCMFMAC_CFG80211_H + +/* for brcmu_d11inf */ +#include + +#define WL_NUM_SCAN_MAX 10 +#define WL_NUM_PMKIDS_MAX MAXPMKID +#define WL_TLV_INFO_MAX 1024 +#define WL_BSS_INFO_MAX 2048 +#define WL_ASSOC_INFO_MAX 512 /* assoc related fil max buf */ +#define WL_EXTRA_BUF_MAX 2048 +#define WL_ROAM_TRIGGER_LEVEL -75 +#define WL_ROAM_DELTA 20 +#define WL_BEACON_TIMEOUT 3 + +#define WL_SCAN_CHANNEL_TIME 40 +#define WL_SCAN_UNASSOC_TIME 40 +#define WL_SCAN_PASSIVE_TIME 120 + +#define WL_ESCAN_BUF_SIZE (1024 * 64) +#define WL_ESCAN_TIMER_INTERVAL_MS 10000 /* E-Scan timeout */ + +#define WL_ESCAN_ACTION_START 1 +#define WL_ESCAN_ACTION_CONTINUE 2 +#define WL_ESCAN_ACTION_ABORT 3 + +#define WL_AUTH_SHARED_KEY 1 /* d11 shared authentication */ +#define IE_MAX_LEN 512 + +/* IE TLV processing */ +#define TLV_LEN_OFF 1 /* length offset */ +#define TLV_HDR_LEN 2 /* header length */ +#define TLV_BODY_OFF 2 /* body offset */ +#define TLV_OUI_LEN 3 /* oui id length */ + +/* 802.11 Mgmt Packet flags */ +#define BRCMF_VNDR_IE_BEACON_FLAG 0x1 +#define BRCMF_VNDR_IE_PRBRSP_FLAG 0x2 +#define BRCMF_VNDR_IE_ASSOCRSP_FLAG 0x4 +#define BRCMF_VNDR_IE_AUTHRSP_FLAG 0x8 +#define BRCMF_VNDR_IE_PRBREQ_FLAG 0x10 +#define BRCMF_VNDR_IE_ASSOCREQ_FLAG 0x20 +/* vendor IE in IW advertisement protocol ID field */ +#define BRCMF_VNDR_IE_IWAPID_FLAG 0x40 +/* allow custom IE id */ +#define BRCMF_VNDR_IE_CUSTOM_FLAG 0x100 + +/* P2P Action Frames flags (spec ordered) */ +#define BRCMF_VNDR_IE_GONREQ_FLAG 0x001000 +#define BRCMF_VNDR_IE_GONRSP_FLAG 0x002000 +#define BRCMF_VNDR_IE_GONCFM_FLAG 0x004000 +#define BRCMF_VNDR_IE_INVREQ_FLAG 0x008000 +#define BRCMF_VNDR_IE_INVRSP_FLAG 0x010000 +#define BRCMF_VNDR_IE_DISREQ_FLAG 0x020000 +#define BRCMF_VNDR_IE_DISRSP_FLAG 0x040000 +#define BRCMF_VNDR_IE_PRDREQ_FLAG 0x080000 +#define BRCMF_VNDR_IE_PRDRSP_FLAG 0x100000 + +#define BRCMF_VNDR_IE_P2PAF_SHIFT 12 + + +/** + * enum brcmf_scan_status - scan engine status + * + * @BRCMF_SCAN_STATUS_BUSY: scanning in progress on dongle. + * @BRCMF_SCAN_STATUS_ABORT: scan being aborted on dongle. + * @BRCMF_SCAN_STATUS_SUPPRESS: scanning is suppressed in driver. + */ +enum brcmf_scan_status { + BRCMF_SCAN_STATUS_BUSY, + BRCMF_SCAN_STATUS_ABORT, + BRCMF_SCAN_STATUS_SUPPRESS, +}; + +/* dongle configuration */ +struct brcmf_cfg80211_conf { + u32 frag_threshold; + u32 rts_threshold; + u32 retry_short; + u32 retry_long; + s32 tx_power; + struct ieee80211_channel channel; +}; + +/* basic structure of scan request */ +struct brcmf_cfg80211_scan_req { + struct brcmf_ssid_le ssid_le; +}; + +/* basic structure of information element */ +struct brcmf_cfg80211_ie { + u16 offset; + u8 buf[WL_TLV_INFO_MAX]; +}; + +/* security information with currently associated ap */ +struct brcmf_cfg80211_security { + u32 wpa_versions; + u32 auth_type; + u32 cipher_pairwise; + u32 cipher_group; + u32 wpa_auth; +}; + +/** + * struct brcmf_cfg80211_profile - profile information. + * + * @ssid: ssid of associated/associating ap. + * @bssid: bssid of joined/joining ibss. + * @sec: security information. + */ +struct brcmf_cfg80211_profile { + struct brcmf_ssid ssid; + u8 bssid[ETH_ALEN]; + struct brcmf_cfg80211_security sec; +}; + +/** + * enum brcmf_vif_status - bit indices for vif status. + * + * @BRCMF_VIF_STATUS_READY: ready for operation. + * @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 { + BRCMF_VIF_STATUS_READY, + BRCMF_VIF_STATUS_CONNECTING, + BRCMF_VIF_STATUS_CONNECTED, + BRCMF_VIF_STATUS_DISCONNECTING, + BRCMF_VIF_STATUS_AP_CREATING, + BRCMF_VIF_STATUS_AP_CREATED +}; + +/** + * struct vif_saved_ie - holds saved IEs for a virtual interface. + * + * @probe_req_ie: IE info for probe request. + * @probe_res_ie: IE info for probe response. + * @beacon_ie: IE info for beacon frame. + * @probe_req_ie_len: IE info length for probe request. + * @probe_res_ie_len: IE info length for probe response. + * @beacon_ie_len: IE info length for beacon frame. + */ +struct vif_saved_ie { + u8 probe_req_ie[IE_MAX_LEN]; + u8 probe_res_ie[IE_MAX_LEN]; + u8 beacon_ie[IE_MAX_LEN]; + u8 assoc_req_ie[IE_MAX_LEN]; + u32 probe_req_ie_len; + u32 probe_res_ie_len; + u32 beacon_ie_len; + u32 assoc_req_ie_len; +}; + +/** + * struct brcmf_cfg80211_vif - virtual interface specific information. + * + * @ifp: lower layer interface pointer + * @wdev: wireless device. + * @profile: profile information. + * @roam_off: roaming state. + * @sme_state: SME state using enum brcmf_vif_status bits. + * @pm_block: power-management blocked. + * @list: linked list. + * @mgmt_rx_reg: registered rx mgmt frame types. + */ +struct brcmf_cfg80211_vif { + struct brcmf_if *ifp; + struct wireless_dev wdev; + struct brcmf_cfg80211_profile profile; + s32 roam_off; + unsigned long sme_state; + bool pm_block; + struct vif_saved_ie saved_ie; + struct list_head list; + u16 mgmt_rx_reg; +}; + +/* association inform */ +struct brcmf_cfg80211_connect_info { + u8 *req_ie; + s32 req_ie_len; + u8 *resp_ie; + s32 resp_ie_len; +}; + +/* assoc ie length */ +struct brcmf_cfg80211_assoc_ielen_le { + __le32 req_len; + __le32 resp_len; +}; + +/* wpa2 pmk list */ +struct brcmf_cfg80211_pmk_list { + struct pmkid_list pmkids; + struct pmkid foo[MAXPMKID - 1]; +}; + +/* dongle escan state */ +enum wl_escan_state { + WL_ESCAN_STATE_IDLE, + WL_ESCAN_STATE_SCANNING +}; + +struct escan_info { + u32 escan_state; + u8 escan_buf[WL_ESCAN_BUF_SIZE]; + struct wiphy *wiphy; + struct brcmf_if *ifp; + s32 (*run)(struct brcmf_cfg80211_info *cfg, struct brcmf_if *ifp, + struct cfg80211_scan_request *request, u16 action); +}; + +/** + * struct brcmf_pno_param_le - PNO scan configuration parameters + * + * @version: PNO parameters version. + * @scan_freq: scan frequency. + * @lost_network_timeout: #sec. to declare discovered network as lost. + * @flags: Bit field to control features of PFN such as sort criteria auto + * enable switch and background scan. + * @rssi_margin: Margin to avoid jitter for choosing a PFN based on RSSI sort + * criteria. + * @bestn: number of best networks in each scan. + * @mscan: number of scans recorded. + * @repeat: minimum number of scan intervals before scan frequency changes + * in adaptive scan. + * @exp: exponent of 2 for maximum scan interval. + * @slow_freq: slow scan period. + */ +struct brcmf_pno_param_le { + __le32 version; + __le32 scan_freq; + __le32 lost_network_timeout; + __le16 flags; + __le16 rssi_margin; + u8 bestn; + u8 mscan; + u8 repeat; + u8 exp; + __le32 slow_freq; +}; + +/** + * struct brcmf_pno_net_param_le - scan parameters per preferred network. + * + * @ssid: ssid name and its length. + * @flags: bit2: hidden. + * @infra: BSS vs IBSS. + * @auth: Open vs Closed. + * @wpa_auth: WPA type. + * @wsec: wsec value. + */ +struct brcmf_pno_net_param_le { + struct brcmf_ssid_le ssid; + __le32 flags; + __le32 infra; + __le32 auth; + __le32 wpa_auth; + __le32 wsec; +}; + +/** + * struct brcmf_pno_net_info_le - information per found network. + * + * @bssid: BSS network identifier. + * @channel: channel number only. + * @SSID_len: length of ssid. + * @SSID: ssid characters. + * @RSSI: receive signal strength (in dBm). + * @timestamp: age in seconds. + */ +struct brcmf_pno_net_info_le { + u8 bssid[ETH_ALEN]; + u8 channel; + u8 SSID_len; + u8 SSID[32]; + __le16 RSSI; + __le16 timestamp; +}; + +/** + * struct brcmf_pno_scanresults_le - result returned in PNO NET FOUND event. + * + * @version: PNO version identifier. + * @status: indicates completion status of PNO scan. + * @count: amount of brcmf_pno_net_info_le entries appended. + */ +struct brcmf_pno_scanresults_le { + __le32 version; + __le32 status; + __le32 count; +}; + +/** + * struct brcmf_cfg80211_vif_event - virtual interface event information. + * + * @vif_wq: waitqueue awaiting interface event from firmware. + * @vif_event_lock: protects other members in this structure. + * @vif_complete: completion for net attach. + * @action: either add, change, or delete. + * @vif: virtual interface object related to the event. + */ +struct brcmf_cfg80211_vif_event { + wait_queue_head_t vif_wq; + struct mutex vif_event_lock; + u8 action; + struct brcmf_cfg80211_vif *vif; +}; + +/** + * struct brcmf_cfg80211_info - dongle private data of cfg80211 interface + * + * @wiphy: wiphy object for cfg80211 interface. + * @conf: dongle configuration. + * @p2p: peer-to-peer specific information. + * @btcoex: Bluetooth coexistence information. + * @scan_request: cfg80211 scan request object. + * @usr_sync: mainly for dongle up/down synchronization. + * @bss_list: bss_list holding scanned ap information. + * @scan_req_int: internal scan request object. + * @bss_info: bss information for cfg80211 layer. + * @ie: information element object for internal purpose. + * @conn_info: association info. + * @pmk_list: wpa2 pmk list. + * @scan_status: scan activity on the dongle. + * @pub: common driver information. + * @channel: current channel. + * @active_scan: current scan mode. + * @sched_escan: e-scan for scheduled scan support running. + * @ibss_starter: indicates this sta is ibss starter. + * @pwr_save: indicate whether dongle to support power save mode. + * @dongle_up: indicate whether dongle up or not. + * @roam_on: on/off switch for dongle self-roaming. + * @scan_tried: indicates if first scan attempted. + * @dcmd_buf: dcmd buffer. + * @extra_buf: mainly to grab assoc information. + * @debugfsdir: debugfs folder for this device. + * @escan_info: escan information. + * @escan_timeout: Timer for catch scan timeout. + * @escan_timeout_work: scan timeout worker. + * @escan_ioctl_buf: dongle command buffer for escan commands. + * @vif_list: linked list of vif instances. + * @vif_cnt: number of vif instances. + * @vif_event: vif event signalling. + * @wowl_enabled; set during suspend, is wowl used. + * @pre_wowl_pmmode: intermediate storage of pm mode during wowl. + */ +struct brcmf_cfg80211_info { + struct wiphy *wiphy; + struct brcmf_cfg80211_conf *conf; + struct brcmf_p2p_info p2p; + struct brcmf_btcoex_info *btcoex; + struct cfg80211_scan_request *scan_request; + struct mutex usr_sync; + struct brcmf_cfg80211_scan_req scan_req_int; + struct wl_cfg80211_bss_info *bss_info; + struct brcmf_cfg80211_ie ie; + struct brcmf_cfg80211_connect_info conn_info; + struct brcmf_cfg80211_pmk_list *pmk_list; + unsigned long scan_status; + struct brcmf_pub *pub; + u32 channel; + bool active_scan; + bool sched_escan; + bool ibss_starter; + bool pwr_save; + bool dongle_up; + bool scan_tried; + u8 *dcmd_buf; + u8 *extra_buf; + struct dentry *debugfsdir; + struct escan_info escan_info; + struct timer_list escan_timeout; + struct work_struct escan_timeout_work; + u8 *escan_ioctl_buf; + struct list_head vif_list; + struct brcmf_cfg80211_vif_event vif_event; + struct completion vif_disabled; + struct brcmu_d11inf d11inf; + bool wowl_enabled; + u32 pre_wowl_pmmode; +}; + +/** + * struct brcmf_tlv - tag_ID/length/value_buffer tuple. + * + * @id: tag identifier. + * @len: number of bytes in value buffer. + * @data: value buffer. + */ +struct brcmf_tlv { + u8 id; + u8 len; + u8 data[1]; +}; + +static inline struct wiphy *cfg_to_wiphy(struct brcmf_cfg80211_info *cfg) +{ + return cfg->wiphy; +} + +static inline struct brcmf_cfg80211_info *wiphy_to_cfg(struct wiphy *w) +{ + return (struct brcmf_cfg80211_info *)(wiphy_priv(w)); +} + +static inline struct brcmf_cfg80211_info *wdev_to_cfg(struct wireless_dev *wd) +{ + return (struct brcmf_cfg80211_info *)(wdev_priv(wd)); +} + +static inline +struct net_device *cfg_to_ndev(struct brcmf_cfg80211_info *cfg) +{ + struct brcmf_cfg80211_vif *vif; + vif = list_first_entry(&cfg->vif_list, struct brcmf_cfg80211_vif, list); + return vif->wdev.netdev; +} + +static inline struct brcmf_cfg80211_info *ndev_to_cfg(struct net_device *ndev) +{ + return wdev_to_cfg(ndev->ieee80211_ptr); +} + +static inline struct brcmf_cfg80211_profile *ndev_to_prof(struct net_device *nd) +{ + struct brcmf_if *ifp = netdev_priv(nd); + return &ifp->vif->profile; +} + +static inline struct brcmf_cfg80211_vif *ndev_to_vif(struct net_device *ndev) +{ + struct brcmf_if *ifp = netdev_priv(ndev); + return ifp->vif; +} + +static inline struct +brcmf_cfg80211_connect_info *cfg_to_conn(struct brcmf_cfg80211_info *cfg) +{ + return &cfg->conn_info; +} + +struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr, + struct device *busdev); +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); +enum nl80211_iftype brcmf_cfg80211_get_iftype(struct brcmf_if *ifp); + +struct brcmf_cfg80211_vif *brcmf_alloc_vif(struct brcmf_cfg80211_info *cfg, + enum nl80211_iftype type, + bool pm_block); +void brcmf_free_vif(struct brcmf_cfg80211_vif *vif); + +s32 brcmf_vif_set_mgmt_ie(struct brcmf_cfg80211_vif *vif, s32 pktflag, + const u8 *vndr_ie_buf, u32 vndr_ie_len); +s32 brcmf_vif_clear_mgmt_ies(struct brcmf_cfg80211_vif *vif); +const struct brcmf_tlv * +brcmf_parse_tlvs(const void *buf, int buflen, uint key); +u16 channel_to_chanspec(struct brcmu_d11inf *d11inf, + struct ieee80211_channel *ch); +bool brcmf_get_vif_state_any(struct brcmf_cfg80211_info *cfg, + unsigned long state); +void brcmf_cfg80211_arm_vif_event(struct brcmf_cfg80211_info *cfg, + struct brcmf_cfg80211_vif *vif); +bool brcmf_cfg80211_vif_event_armed(struct brcmf_cfg80211_info *cfg); +int brcmf_cfg80211_wait_vif_event_timeout(struct brcmf_cfg80211_info *cfg, + u8 action, ulong timeout); +s32 brcmf_notify_escan_complete(struct brcmf_cfg80211_info *cfg, + struct brcmf_if *ifp, bool aborted, + bool fw_abort); +void brcmf_set_mpc(struct brcmf_if *ndev, int mpc); +void brcmf_abort_scanning(struct brcmf_cfg80211_info *cfg); +void brcmf_cfg80211_free_netdev(struct net_device *ndev); + +#endif /* BRCMFMAC_CFG80211_H */ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c index 8dbf2c155b5b..53650b9cb768 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c @@ -27,7 +27,7 @@ #include "debug.h" #include "fwil_types.h" #include "p2p.h" -#include "wl_cfg80211.h" +#include "cfg80211.h" #include "fwil.h" #include "fwsignal.h" #include "feature.h" diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c index dbd52ccbde56..6d33331dd2b0 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c @@ -34,7 +34,7 @@ #include "fweh.h" #include "fwsignal.h" #include "p2p.h" -#include "wl_cfg80211.h" +#include "cfg80211.h" #include "proto.h" /** diff --git a/drivers/net/wireless/brcm80211/brcmfmac/p2p.c b/drivers/net/wireless/brcm80211/brcmfmac/p2p.c index 7459c252c1dd..ae504b40e554 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/p2p.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/p2p.c @@ -26,7 +26,7 @@ #include "fwil.h" #include "fwil_types.h" #include "p2p.h" -#include "wl_cfg80211.h" +#include "cfg80211.h" /* parameters used for p2p escan */ #define P2PAPI_SCAN_NPROBES 1 diff --git a/drivers/net/wireless/brcm80211/brcmfmac/vendor.c b/drivers/net/wireless/brcm80211/brcmfmac/vendor.c index f204c83cd61f..b86a08369517 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/vendor.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/vendor.c @@ -23,7 +23,7 @@ #include "dhd.h" #include "p2p.h" #include "debug.h" -#include "wl_cfg80211.h" +#include "cfg80211.h" #include "vendor.h" #include "fwil.h" diff --git a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c deleted file mode 100644 index 2560dff4151e..000000000000 --- a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c +++ /dev/null @@ -1,5862 +0,0 @@ -/* - * Copyright (c) 2010 Broadcom Corporation - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY - * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION - * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN - * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -/* Toplevel file. Relies on dhd_linux.c to send commands to the dongle. */ - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include "dhd.h" -#include "debug.h" -#include "tracepoint.h" -#include "fwil_types.h" -#include "p2p.h" -#include "btcoex.h" -#include "wl_cfg80211.h" -#include "feature.h" -#include "fwil.h" -#include "proto.h" -#include "vendor.h" -#include "bus.h" - -#define BRCMF_SCAN_IE_LEN_MAX 2048 -#define BRCMF_PNO_VERSION 2 -#define BRCMF_PNO_TIME 30 -#define BRCMF_PNO_REPEAT 4 -#define BRCMF_PNO_FREQ_EXPO_MAX 3 -#define BRCMF_PNO_MAX_PFN_COUNT 16 -#define BRCMF_PNO_ENABLE_ADAPTSCAN_BIT 6 -#define BRCMF_PNO_HIDDEN_BIT 2 -#define BRCMF_PNO_WPA_AUTH_ANY 0xFFFFFFFF -#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 */ -#define WME_OUI_TYPE 2 -#define WPS_OUI_TYPE 4 - -#define VS_IE_FIXED_HDR_LEN 6 -#define WPA_IE_VERSION_LEN 2 -#define WPA_IE_MIN_OUI_LEN 4 -#define WPA_IE_SUITE_COUNT_LEN 2 - -#define WPA_CIPHER_NONE 0 /* None */ -#define WPA_CIPHER_WEP_40 1 /* WEP (40-bit) */ -#define WPA_CIPHER_TKIP 2 /* TKIP: default for WPA */ -#define WPA_CIPHER_AES_CCM 4 /* AES (CCM) */ -#define WPA_CIPHER_WEP_104 5 /* WEP (104-bit) */ - -#define RSN_AKM_NONE 0 /* None (IBSS) */ -#define RSN_AKM_UNSPECIFIED 1 /* Over 802.1x */ -#define RSN_AKM_PSK 2 /* Pre-shared Key */ -#define RSN_CAP_LEN 2 /* Length of RSN capabilities */ -#define RSN_CAP_PTK_REPLAY_CNTR_MASK 0x000C - -#define VNDR_IE_CMD_LEN 4 /* length of the set command - * string :"add", "del" (+ NUL) - */ -#define VNDR_IE_COUNT_OFFSET 4 -#define VNDR_IE_PKTFLAG_OFFSET 8 -#define VNDR_IE_VSIE_OFFSET 12 -#define VNDR_IE_HDR_SIZE 12 -#define VNDR_IE_PARSE_LIMIT 5 - -#define DOT11_MGMT_HDR_LEN 24 /* d11 management header len */ -#define DOT11_BCN_PRB_FIXED_LEN 12 /* beacon/probe fixed length */ - -#define BRCMF_SCAN_JOIN_ACTIVE_DWELL_TIME_MS 320 -#define BRCMF_SCAN_JOIN_PASSIVE_DWELL_TIME_MS 400 -#define BRCMF_SCAN_JOIN_PROBE_INTERVAL_MS 20 - -#define BRCMF_ASSOC_PARAMS_FIXED_SIZE \ - (sizeof(struct brcmf_assoc_params_le) - sizeof(u16)) - -static bool check_vif_up(struct brcmf_cfg80211_vif *vif) -{ - if (!test_bit(BRCMF_VIF_STATUS_READY, &vif->sme_state)) { - brcmf_dbg(INFO, "device is not ready : status (%lu)\n", - vif->sme_state); - return false; - } - return true; -} - -#define RATE_TO_BASE100KBPS(rate) (((rate) * 10) / 2) -#define RATETAB_ENT(_rateid, _flags) \ - { \ - .bitrate = RATE_TO_BASE100KBPS(_rateid), \ - .hw_value = (_rateid), \ - .flags = (_flags), \ - } - -static struct ieee80211_rate __wl_rates[] = { - RATETAB_ENT(BRCM_RATE_1M, 0), - RATETAB_ENT(BRCM_RATE_2M, IEEE80211_RATE_SHORT_PREAMBLE), - RATETAB_ENT(BRCM_RATE_5M5, IEEE80211_RATE_SHORT_PREAMBLE), - RATETAB_ENT(BRCM_RATE_11M, IEEE80211_RATE_SHORT_PREAMBLE), - RATETAB_ENT(BRCM_RATE_6M, 0), - RATETAB_ENT(BRCM_RATE_9M, 0), - RATETAB_ENT(BRCM_RATE_12M, 0), - RATETAB_ENT(BRCM_RATE_18M, 0), - RATETAB_ENT(BRCM_RATE_24M, 0), - RATETAB_ENT(BRCM_RATE_36M, 0), - RATETAB_ENT(BRCM_RATE_48M, 0), - 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 - -/* Band templates duplicated per wiphy. The channel info - * is filled in after querying the device. - */ -static const struct ieee80211_supported_band __wl_band_2ghz = { - .band = IEEE80211_BAND_2GHZ, - .bitrates = wl_g_rates, - .n_bitrates = wl_g_rates_size, -}; - -static const struct ieee80211_supported_band __wl_band_5ghz_a = { - .band = IEEE80211_BAND_5GHZ, - .bitrates = wl_a_rates, - .n_bitrates = wl_a_rates_size, -}; - -/* This is to override regulatory domains defined in cfg80211 module (reg.c) - * By default world regulatory domain defined in reg.c puts the flags - * NL80211_RRF_NO_IR for 5GHz channels (for * 36..48 and 149..165). - * With respect to these flags, wpa_supplicant doesn't * start p2p - * operations on 5GHz channels. All the changes in world regulatory - * domain are to be done here. - */ -static const struct ieee80211_regdomain brcmf_regdom = { - .n_reg_rules = 4, - .alpha2 = "99", - .reg_rules = { - /* IEEE 802.11b/g, channels 1..11 */ - REG_RULE(2412-10, 2472+10, 40, 6, 20, 0), - /* If any */ - /* IEEE 802.11 channel 14 - Only JP enables - * this and for 802.11b only - */ - REG_RULE(2484-10, 2484+10, 20, 6, 20, 0), - /* IEEE 802.11a, channel 36..64 */ - REG_RULE(5150-10, 5350+10, 80, 6, 20, 0), - /* IEEE 802.11a, channel 100..165 */ - REG_RULE(5470-10, 5850+10, 80, 6, 20, 0), } -}; - -static const u32 __wl_cipher_suites[] = { - WLAN_CIPHER_SUITE_WEP40, - WLAN_CIPHER_SUITE_WEP104, - WLAN_CIPHER_SUITE_TKIP, - WLAN_CIPHER_SUITE_CCMP, - WLAN_CIPHER_SUITE_AES_CMAC, -}; - -/* Vendor specific ie. id = 221, oui and type defines exact ie */ -struct brcmf_vs_tlv { - u8 id; - u8 len; - u8 oui[3]; - u8 oui_type; -}; - -struct parsed_vndr_ie_info { - u8 *ie_ptr; - u32 ie_len; /* total length including id & length field */ - struct brcmf_vs_tlv vndrie; -}; - -struct parsed_vndr_ies { - u32 count; - struct parsed_vndr_ie_info ie_info[VNDR_IE_PARSE_LIMIT]; -}; - -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) -{ - struct brcmu_chan ch_inf; - s32 primary_offset; - - brcmf_dbg(TRACE, "chandef: control %d center %d width %d\n", - ch->chan->center_freq, ch->center_freq1, ch->width); - ch_inf.chnum = ieee80211_frequency_to_channel(ch->center_freq1); - primary_offset = ch->center_freq1 - ch->chan->center_freq; - switch (ch->width) { - case NL80211_CHAN_WIDTH_20: - ch_inf.bw = BRCMU_CHAN_BW_20; - WARN_ON(primary_offset != 0); - break; - case NL80211_CHAN_WIDTH_40: - ch_inf.bw = BRCMU_CHAN_BW_40; - if (primary_offset < 0) - ch_inf.sb = BRCMU_CHAN_SB_U; - else - ch_inf.sb = BRCMU_CHAN_SB_L; - break; - case NL80211_CHAN_WIDTH_80: - ch_inf.bw = BRCMU_CHAN_BW_80; - if (primary_offset < 0) { - if (primary_offset < -CH_10MHZ_APART) - ch_inf.sb = BRCMU_CHAN_SB_UU; - else - ch_inf.sb = BRCMU_CHAN_SB_UL; - } else { - if (primary_offset > CH_10MHZ_APART) - ch_inf.sb = BRCMU_CHAN_SB_LL; - else - ch_inf.sb = BRCMU_CHAN_SB_LU; - } - break; - default: - WARN_ON_ONCE(1); - } - switch (ch->chan->band) { - case IEEE80211_BAND_2GHZ: - ch_inf.band = BRCMU_CHAN_BAND_2G; - break; - case IEEE80211_BAND_5GHZ: - ch_inf.band = BRCMU_CHAN_BAND_5G; - break; - default: - WARN_ON_ONCE(1); - } - d11inf->encchspec(&ch_inf); - - return ch_inf.chspec; -} - -u16 channel_to_chanspec(struct brcmu_d11inf *d11inf, - struct ieee80211_channel *ch) -{ - struct brcmu_chan ch_inf; - - ch_inf.chnum = ieee80211_frequency_to_channel(ch->center_freq); - ch_inf.bw = BRCMU_CHAN_BW_20; - d11inf->encchspec(&ch_inf); - - return ch_inf.chspec; -} - -/* Traverse a string of 1-byte tag/1-byte length/variable-length value - * triples, returning a pointer to the substring whose first element - * matches tag - */ -const struct brcmf_tlv * -brcmf_parse_tlvs(const void *buf, int buflen, uint key) -{ - const struct brcmf_tlv *elt = buf; - int totlen = buflen; - - /* find tagged parameter */ - while (totlen >= TLV_HDR_LEN) { - int len = elt->len; - - /* validate remaining totlen */ - if ((elt->id == key) && (totlen >= (len + TLV_HDR_LEN))) - return elt; - - elt = (struct brcmf_tlv *)((u8 *)elt + (len + TLV_HDR_LEN)); - totlen -= (len + TLV_HDR_LEN); - } - - return NULL; -} - -/* Is any of the tlvs the expected entry? If - * not update the tlvs buffer pointer/length. - */ -static bool -brcmf_tlv_has_ie(const u8 *ie, const u8 **tlvs, u32 *tlvs_len, - const u8 *oui, u32 oui_len, u8 type) -{ - /* If the contents match the OUI and the type */ - if (ie[TLV_LEN_OFF] >= oui_len + 1 && - !memcmp(&ie[TLV_BODY_OFF], oui, oui_len) && - type == ie[TLV_BODY_OFF + oui_len]) { - return true; - } - - if (tlvs == NULL) - return false; - /* point to the next ie */ - ie += ie[TLV_LEN_OFF] + TLV_HDR_LEN; - /* calculate the length of the rest of the buffer */ - *tlvs_len -= (int)(ie - *tlvs); - /* update the pointer to the start of the buffer */ - *tlvs = ie; - - return false; -} - -static struct brcmf_vs_tlv * -brcmf_find_wpaie(const u8 *parse, u32 len) -{ - const struct brcmf_tlv *ie; - - while ((ie = brcmf_parse_tlvs(parse, len, WLAN_EID_VENDOR_SPECIFIC))) { - if (brcmf_tlv_has_ie((const u8 *)ie, &parse, &len, - WPA_OUI, TLV_OUI_LEN, WPA_OUI_TYPE)) - return (struct brcmf_vs_tlv *)ie; - } - return NULL; -} - -static struct brcmf_vs_tlv * -brcmf_find_wpsie(const u8 *parse, u32 len) -{ - const struct brcmf_tlv *ie; - - while ((ie = brcmf_parse_tlvs(parse, len, WLAN_EID_VENDOR_SPECIFIC))) { - if (brcmf_tlv_has_ie((u8 *)ie, &parse, &len, - WPA_OUI, TLV_OUI_LEN, WPS_OUI_TYPE)) - return (struct brcmf_vs_tlv *)ie; - } - return NULL; -} - - -static void convert_key_from_CPU(struct brcmf_wsec_key *key, - struct brcmf_wsec_key_le *key_le) -{ - key_le->index = cpu_to_le32(key->index); - key_le->len = cpu_to_le32(key->len); - key_le->algo = cpu_to_le32(key->algo); - key_le->flags = cpu_to_le32(key->flags); - key_le->rxiv.hi = cpu_to_le32(key->rxiv.hi); - key_le->rxiv.lo = cpu_to_le16(key->rxiv.lo); - key_le->iv_initialized = cpu_to_le32(key->iv_initialized); - memcpy(key_le->data, key->data, sizeof(key->data)); - memcpy(key_le->ea, key->ea, sizeof(key->ea)); -} - -static int -send_key_to_dongle(struct net_device *ndev, struct brcmf_wsec_key *key) -{ - int err; - struct brcmf_wsec_key_le key_le; - - convert_key_from_CPU(key, &key_le); - - brcmf_netdev_wait_pend8021x(ndev); - - err = brcmf_fil_bsscfg_data_set(netdev_priv(ndev), "wsec_key", &key_le, - sizeof(key_le)); - - if (err) - brcmf_err("wsec_key error (%d)\n", err); - return err; -} - -static s32 -brcmf_configure_arp_offload(struct brcmf_if *ifp, bool enable) -{ - s32 err; - u32 mode; - - if (enable) - mode = BRCMF_ARP_OL_AGENT | BRCMF_ARP_OL_PEER_AUTO_REPLY; - else - mode = 0; - - /* Try to set and enable ARP offload feature, this may fail, then it */ - /* is simply not supported and err 0 will be returned */ - err = brcmf_fil_iovar_int_set(ifp, "arp_ol", mode); - if (err) { - brcmf_dbg(TRACE, "failed to set ARP offload mode to 0x%x, err = %d\n", - mode, err); - err = 0; - } else { - err = brcmf_fil_iovar_int_set(ifp, "arpoe", enable); - if (err) { - brcmf_dbg(TRACE, "failed to configure (%d) ARP offload err = %d\n", - enable, err); - err = 0; - } else - brcmf_dbg(TRACE, "successfully configured (%d) ARP offload to 0x%x\n", - enable, mode); - } - - return err; -} - -static void -brcmf_cfg80211_update_proto_addr_mode(struct wireless_dev *wdev) -{ - struct brcmf_cfg80211_vif *vif; - struct brcmf_if *ifp; - - vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev); - ifp = vif->ifp; - - if ((wdev->iftype == NL80211_IFTYPE_ADHOC) || - (wdev->iftype == NL80211_IFTYPE_AP) || - (wdev->iftype == NL80211_IFTYPE_P2P_GO)) - brcmf_proto_configure_addr_mode(ifp->drvr, ifp->ifidx, - ADDR_DIRECT); - else - brcmf_proto_configure_addr_mode(ifp->drvr, ifp->ifidx, - ADDR_INDIRECT); -} - -static bool brcmf_is_apmode(struct brcmf_cfg80211_vif *vif) -{ - enum nl80211_iftype iftype; - - iftype = vif->wdev.iftype; - return iftype == NL80211_IFTYPE_AP || iftype == NL80211_IFTYPE_P2P_GO; -} - -static bool brcmf_is_ibssmode(struct brcmf_cfg80211_vif *vif) -{ - return vif->wdev.iftype == NL80211_IFTYPE_ADHOC; -} - -static struct wireless_dev *brcmf_cfg80211_add_iface(struct wiphy *wiphy, - const char *name, - enum nl80211_iftype type, - u32 *flags, - struct vif_params *params) -{ - struct wireless_dev *wdev; - - brcmf_dbg(TRACE, "enter: %s type %d\n", name, type); - switch (type) { - case NL80211_IFTYPE_ADHOC: - case NL80211_IFTYPE_STATION: - case NL80211_IFTYPE_AP: - case NL80211_IFTYPE_AP_VLAN: - case NL80211_IFTYPE_WDS: - case NL80211_IFTYPE_MONITOR: - case NL80211_IFTYPE_MESH_POINT: - return ERR_PTR(-EOPNOTSUPP); - case NL80211_IFTYPE_P2P_CLIENT: - case NL80211_IFTYPE_P2P_GO: - case NL80211_IFTYPE_P2P_DEVICE: - wdev = brcmf_p2p_add_vif(wiphy, name, type, flags, params); - if (!IS_ERR(wdev)) - brcmf_cfg80211_update_proto_addr_mode(wdev); - return wdev; - case NL80211_IFTYPE_UNSPECIFIED: - default: - return ERR_PTR(-EINVAL); - } -} - -static void brcmf_scan_config_mpc(struct brcmf_if *ifp, int mpc) -{ - if (brcmf_feat_is_quirk_enabled(ifp, BRCMF_FEAT_QUIRK_NEED_MPC)) - brcmf_set_mpc(ifp, mpc); -} - -void brcmf_set_mpc(struct brcmf_if *ifp, int mpc) -{ - s32 err = 0; - - if (check_vif_up(ifp->vif)) { - err = brcmf_fil_iovar_int_set(ifp, "mpc", mpc); - if (err) { - brcmf_err("fail to set mpc\n"); - return; - } - brcmf_dbg(INFO, "MPC : %d\n", mpc); - } -} - -s32 brcmf_notify_escan_complete(struct brcmf_cfg80211_info *cfg, - struct brcmf_if *ifp, bool aborted, - bool fw_abort) -{ - struct brcmf_scan_params_le params_le; - struct cfg80211_scan_request *scan_request; - s32 err = 0; - - brcmf_dbg(SCAN, "Enter\n"); - - /* clear scan request, because the FW abort can cause a second call */ - /* to this functon and might cause a double cfg80211_scan_done */ - scan_request = cfg->scan_request; - cfg->scan_request = NULL; - - if (timer_pending(&cfg->escan_timeout)) - del_timer_sync(&cfg->escan_timeout); - - if (fw_abort) { - /* Do a scan abort to stop the driver's scan engine */ - brcmf_dbg(SCAN, "ABORT scan in firmware\n"); - memset(¶ms_le, 0, sizeof(params_le)); - memset(params_le.bssid, 0xFF, ETH_ALEN); - params_le.bss_type = DOT11_BSSTYPE_ANY; - params_le.scan_type = 0; - params_le.channel_num = cpu_to_le32(1); - params_le.nprobes = cpu_to_le32(1); - params_le.active_time = cpu_to_le32(-1); - params_le.passive_time = cpu_to_le32(-1); - params_le.home_time = cpu_to_le32(-1); - /* Scan is aborted by setting channel_list[0] to -1 */ - params_le.channel_list[0] = cpu_to_le16(-1); - /* E-Scan (or anyother type) can be aborted by SCAN */ - err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SCAN, - ¶ms_le, sizeof(params_le)); - if (err) - brcmf_err("Scan abort failed\n"); - } - - brcmf_scan_config_mpc(ifp, 1); - - /* - * e-scan can be initiated by scheduled scan - * which takes precedence. - */ - if (cfg->sched_escan) { - brcmf_dbg(SCAN, "scheduled scan completed\n"); - cfg->sched_escan = false; - if (!aborted) - cfg80211_sched_scan_results(cfg_to_wiphy(cfg)); - } else if (scan_request) { - brcmf_dbg(SCAN, "ESCAN Completed scan: %s\n", - aborted ? "Aborted" : "Done"); - cfg80211_scan_done(scan_request, aborted); - } - if (!test_and_clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) - brcmf_dbg(SCAN, "Scan complete, probably P2P scan\n"); - - return err; -} - -static -int brcmf_cfg80211_del_iface(struct wiphy *wiphy, struct wireless_dev *wdev) -{ - struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy); - struct net_device *ndev = wdev->netdev; - - /* vif event pending in firmware */ - if (brcmf_cfg80211_vif_event_armed(cfg)) - return -EBUSY; - - if (ndev) { - if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status) && - cfg->escan_info.ifp == netdev_priv(ndev)) - brcmf_notify_escan_complete(cfg, netdev_priv(ndev), - true, true); - - brcmf_fil_iovar_int_set(netdev_priv(ndev), "mpc", 1); - } - - switch (wdev->iftype) { - case NL80211_IFTYPE_ADHOC: - case NL80211_IFTYPE_STATION: - case NL80211_IFTYPE_AP: - case NL80211_IFTYPE_AP_VLAN: - case NL80211_IFTYPE_WDS: - case NL80211_IFTYPE_MONITOR: - case NL80211_IFTYPE_MESH_POINT: - return -EOPNOTSUPP; - case NL80211_IFTYPE_P2P_CLIENT: - case NL80211_IFTYPE_P2P_GO: - case NL80211_IFTYPE_P2P_DEVICE: - return brcmf_p2p_del_vif(wiphy, wdev); - case NL80211_IFTYPE_UNSPECIFIED: - default: - return -EINVAL; - } - return -EOPNOTSUPP; -} - -static s32 -brcmf_cfg80211_change_iface(struct wiphy *wiphy, struct net_device *ndev, - enum nl80211_iftype type, u32 *flags, - struct vif_params *params) -{ - struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy); - struct brcmf_if *ifp = netdev_priv(ndev); - struct brcmf_cfg80211_vif *vif = ifp->vif; - s32 infra = 0; - s32 ap = 0; - s32 err = 0; - - brcmf_dbg(TRACE, "Enter, ndev=%p, type=%d\n", ndev, type); - - switch (type) { - case NL80211_IFTYPE_MONITOR: - case NL80211_IFTYPE_WDS: - brcmf_err("type (%d) : currently we do not support this type\n", - type); - return -EOPNOTSUPP; - case NL80211_IFTYPE_ADHOC: - 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: - case NL80211_IFTYPE_P2P_GO: - ap = 1; - break; - default: - err = -EINVAL; - goto done; - } - - if (ap) { - if (type == NL80211_IFTYPE_P2P_GO) { - brcmf_dbg(INFO, "IF Type = P2P GO\n"); - 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 { - err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_INFRA, infra); - if (err) { - brcmf_err("WLC_SET_INFRA error (%d)\n", err); - err = -EAGAIN; - goto done; - } - brcmf_dbg(INFO, "IF Type = %s\n", brcmf_is_ibssmode(vif) ? - "Adhoc" : "Infra"); - } - ndev->ieee80211_ptr->iftype = type; - - brcmf_cfg80211_update_proto_addr_mode(&vif->wdev); - -done: - brcmf_dbg(TRACE, "Exit\n"); - - return err; -} - -static void brcmf_escan_prep(struct brcmf_cfg80211_info *cfg, - struct brcmf_scan_params_le *params_le, - struct cfg80211_scan_request *request) -{ - u32 n_ssids; - u32 n_channels; - s32 i; - s32 offset; - u16 chanspec; - char *ptr; - struct brcmf_ssid_le ssid_le; - - memset(params_le->bssid, 0xFF, ETH_ALEN); - params_le->bss_type = DOT11_BSSTYPE_ANY; - params_le->scan_type = 0; - params_le->channel_num = 0; - params_le->nprobes = cpu_to_le32(-1); - params_le->active_time = cpu_to_le32(-1); - params_le->passive_time = cpu_to_le32(-1); - params_le->home_time = cpu_to_le32(-1); - memset(¶ms_le->ssid_le, 0, sizeof(params_le->ssid_le)); - - /* if request is null exit so it will be all channel broadcast scan */ - if (!request) - return; - - n_ssids = request->n_ssids; - n_channels = request->n_channels; - /* Copy channel array if applicable */ - brcmf_dbg(SCAN, "### List of channelspecs to scan ### %d\n", - n_channels); - if (n_channels > 0) { - for (i = 0; i < n_channels; i++) { - chanspec = channel_to_chanspec(&cfg->d11inf, - request->channels[i]); - brcmf_dbg(SCAN, "Chan : %d, Channel spec: %x\n", - request->channels[i]->hw_value, chanspec); - params_le->channel_list[i] = cpu_to_le16(chanspec); - } - } else { - brcmf_dbg(SCAN, "Scanning all channels\n"); - } - /* Copy ssid array if applicable */ - brcmf_dbg(SCAN, "### List of SSIDs to scan ### %d\n", n_ssids); - if (n_ssids > 0) { - offset = offsetof(struct brcmf_scan_params_le, channel_list) + - n_channels * sizeof(u16); - offset = roundup(offset, sizeof(u32)); - ptr = (char *)params_le + offset; - for (i = 0; i < n_ssids; i++) { - memset(&ssid_le, 0, sizeof(ssid_le)); - ssid_le.SSID_len = - cpu_to_le32(request->ssids[i].ssid_len); - memcpy(ssid_le.SSID, request->ssids[i].ssid, - request->ssids[i].ssid_len); - if (!ssid_le.SSID_len) - brcmf_dbg(SCAN, "%d: Broadcast scan\n", i); - else - brcmf_dbg(SCAN, "%d: scan for %s size =%d\n", - i, ssid_le.SSID, ssid_le.SSID_len); - memcpy(ptr, &ssid_le, sizeof(ssid_le)); - ptr += sizeof(ssid_le); - } - } else { - brcmf_dbg(SCAN, "Broadcast scan %p\n", request->ssids); - if ((request->ssids) && request->ssids->ssid_len) { - brcmf_dbg(SCAN, "SSID %s len=%d\n", - params_le->ssid_le.SSID, - request->ssids->ssid_len); - params_le->ssid_le.SSID_len = - cpu_to_le32(request->ssids->ssid_len); - memcpy(¶ms_le->ssid_le.SSID, request->ssids->ssid, - request->ssids->ssid_len); - } - } - /* Adding mask to channel numbers */ - params_le->channel_num = - cpu_to_le32((n_ssids << BRCMF_SCAN_PARAMS_NSSID_SHIFT) | - (n_channels & BRCMF_SCAN_PARAMS_COUNT_MASK)); -} - -static s32 -brcmf_run_escan(struct brcmf_cfg80211_info *cfg, struct brcmf_if *ifp, - struct cfg80211_scan_request *request, u16 action) -{ - s32 params_size = BRCMF_SCAN_PARAMS_FIXED_SIZE + - offsetof(struct brcmf_escan_params_le, params_le); - struct brcmf_escan_params_le *params; - s32 err = 0; - - brcmf_dbg(SCAN, "E-SCAN START\n"); - - if (request != NULL) { - /* Allocate space for populating ssids in struct */ - params_size += sizeof(u32) * ((request->n_channels + 1) / 2); - - /* Allocate space for populating ssids in struct */ - params_size += sizeof(struct brcmf_ssid) * request->n_ssids; - } - - params = kzalloc(params_size, GFP_KERNEL); - if (!params) { - err = -ENOMEM; - goto exit; - } - BUG_ON(params_size + sizeof("escan") >= BRCMF_DCMD_MEDLEN); - brcmf_escan_prep(cfg, ¶ms->params_le, request); - params->version = cpu_to_le32(BRCMF_ESCAN_REQ_VERSION); - params->action = cpu_to_le16(action); - params->sync_id = cpu_to_le16(0x1234); - - err = brcmf_fil_iovar_data_set(ifp, "escan", params, params_size); - if (err) { - if (err == -EBUSY) - brcmf_dbg(INFO, "system busy : escan canceled\n"); - else - brcmf_err("error (%d)\n", err); - } - - kfree(params); -exit: - return err; -} - -static s32 -brcmf_do_escan(struct brcmf_cfg80211_info *cfg, struct wiphy *wiphy, - struct brcmf_if *ifp, struct cfg80211_scan_request *request) -{ - s32 err; - u32 passive_scan; - struct brcmf_scan_results *results; - struct escan_info *escan = &cfg->escan_info; - - brcmf_dbg(SCAN, "Enter\n"); - escan->ifp = ifp; - escan->wiphy = wiphy; - escan->escan_state = WL_ESCAN_STATE_SCANNING; - passive_scan = cfg->active_scan ? 0 : 1; - err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PASSIVE_SCAN, - passive_scan); - if (err) { - brcmf_err("error (%d)\n", err); - return err; - } - brcmf_scan_config_mpc(ifp, 0); - results = (struct brcmf_scan_results *)cfg->escan_info.escan_buf; - results->version = 0; - results->count = 0; - results->buflen = WL_ESCAN_RESULTS_FIXED_SIZE; - - err = escan->run(cfg, ifp, request, WL_ESCAN_ACTION_START); - if (err) - brcmf_scan_config_mpc(ifp, 1); - return err; -} - -static s32 -brcmf_cfg80211_escan(struct wiphy *wiphy, struct brcmf_cfg80211_vif *vif, - struct cfg80211_scan_request *request, - struct cfg80211_ssid *this_ssid) -{ - struct brcmf_if *ifp = vif->ifp; - struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); - struct cfg80211_ssid *ssids; - struct brcmf_cfg80211_scan_req *sr = &cfg->scan_req_int; - u32 passive_scan; - bool escan_req; - bool spec_scan; - s32 err; - u32 SSID_len; - - brcmf_dbg(SCAN, "START ESCAN\n"); - - if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) { - brcmf_err("Scanning already: status (%lu)\n", cfg->scan_status); - return -EAGAIN; - } - if (test_bit(BRCMF_SCAN_STATUS_ABORT, &cfg->scan_status)) { - brcmf_err("Scanning being aborted: status (%lu)\n", - cfg->scan_status); - return -EAGAIN; - } - if (test_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status)) { - brcmf_err("Scanning suppressed: status (%lu)\n", - cfg->scan_status); - return -EAGAIN; - } - if (test_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state)) { - brcmf_err("Connecting: status (%lu)\n", ifp->vif->sme_state); - return -EAGAIN; - } - - /* If scan req comes for p2p0, send it over primary I/F */ - if (vif == cfg->p2p.bss_idx[P2PAPI_BSSCFG_DEVICE].vif) - vif = cfg->p2p.bss_idx[P2PAPI_BSSCFG_PRIMARY].vif; - - /* Arm scan timeout timer */ - mod_timer(&cfg->escan_timeout, jiffies + - WL_ESCAN_TIMER_INTERVAL_MS * HZ / 1000); - - escan_req = false; - if (request) { - /* scan bss */ - ssids = request->ssids; - escan_req = true; - } else { - /* scan in ibss */ - /* we don't do escan in ibss */ - ssids = this_ssid; - } - - cfg->scan_request = request; - set_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status); - if (escan_req) { - cfg->escan_info.run = brcmf_run_escan; - err = brcmf_p2p_scan_prep(wiphy, request, vif); - if (err) - goto scan_out; - - err = brcmf_do_escan(cfg, wiphy, vif->ifp, request); - if (err) - goto scan_out; - } else { - brcmf_dbg(SCAN, "ssid \"%s\", ssid_len (%d)\n", - ssids->ssid, ssids->ssid_len); - memset(&sr->ssid_le, 0, sizeof(sr->ssid_le)); - SSID_len = min_t(u8, sizeof(sr->ssid_le.SSID), ssids->ssid_len); - sr->ssid_le.SSID_len = cpu_to_le32(0); - spec_scan = false; - if (SSID_len) { - memcpy(sr->ssid_le.SSID, ssids->ssid, SSID_len); - sr->ssid_le.SSID_len = cpu_to_le32(SSID_len); - spec_scan = true; - } else - brcmf_dbg(SCAN, "Broadcast scan\n"); - - passive_scan = cfg->active_scan ? 0 : 1; - err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PASSIVE_SCAN, - passive_scan); - if (err) { - brcmf_err("WLC_SET_PASSIVE_SCAN error (%d)\n", err); - goto scan_out; - } - brcmf_scan_config_mpc(ifp, 0); - err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SCAN, - &sr->ssid_le, sizeof(sr->ssid_le)); - if (err) { - if (err == -EBUSY) - brcmf_dbg(INFO, "BUSY: scan for \"%s\" canceled\n", - sr->ssid_le.SSID); - else - brcmf_err("WLC_SCAN error (%d)\n", err); - - brcmf_scan_config_mpc(ifp, 1); - goto scan_out; - } - } - - return 0; - -scan_out: - clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status); - if (timer_pending(&cfg->escan_timeout)) - del_timer_sync(&cfg->escan_timeout); - cfg->scan_request = NULL; - return err; -} - -static s32 -brcmf_cfg80211_scan(struct wiphy *wiphy, struct cfg80211_scan_request *request) -{ - struct brcmf_cfg80211_vif *vif; - s32 err = 0; - - brcmf_dbg(TRACE, "Enter\n"); - vif = container_of(request->wdev, struct brcmf_cfg80211_vif, wdev); - if (!check_vif_up(vif)) - return -EIO; - - err = brcmf_cfg80211_escan(wiphy, vif, request, NULL); - - if (err) - brcmf_err("scan error (%d)\n", err); - - brcmf_dbg(TRACE, "Exit\n"); - return err; -} - -static s32 brcmf_set_rts(struct net_device *ndev, u32 rts_threshold) -{ - s32 err = 0; - - err = brcmf_fil_iovar_int_set(netdev_priv(ndev), "rtsthresh", - rts_threshold); - if (err) - brcmf_err("Error (%d)\n", err); - - return err; -} - -static s32 brcmf_set_frag(struct net_device *ndev, u32 frag_threshold) -{ - s32 err = 0; - - err = brcmf_fil_iovar_int_set(netdev_priv(ndev), "fragthresh", - frag_threshold); - if (err) - brcmf_err("Error (%d)\n", err); - - return err; -} - -static s32 brcmf_set_retry(struct net_device *ndev, u32 retry, bool l) -{ - s32 err = 0; - u32 cmd = (l ? BRCMF_C_SET_LRL : BRCMF_C_SET_SRL); - - err = brcmf_fil_cmd_int_set(netdev_priv(ndev), cmd, retry); - if (err) { - brcmf_err("cmd (%d) , error (%d)\n", cmd, err); - return err; - } - return err; -} - -static s32 brcmf_cfg80211_set_wiphy_params(struct wiphy *wiphy, u32 changed) -{ - struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); - struct net_device *ndev = cfg_to_ndev(cfg); - struct brcmf_if *ifp = netdev_priv(ndev); - s32 err = 0; - - brcmf_dbg(TRACE, "Enter\n"); - if (!check_vif_up(ifp->vif)) - return -EIO; - - if (changed & WIPHY_PARAM_RTS_THRESHOLD && - (cfg->conf->rts_threshold != wiphy->rts_threshold)) { - cfg->conf->rts_threshold = wiphy->rts_threshold; - err = brcmf_set_rts(ndev, cfg->conf->rts_threshold); - if (!err) - goto done; - } - if (changed & WIPHY_PARAM_FRAG_THRESHOLD && - (cfg->conf->frag_threshold != wiphy->frag_threshold)) { - cfg->conf->frag_threshold = wiphy->frag_threshold; - err = brcmf_set_frag(ndev, cfg->conf->frag_threshold); - if (!err) - goto done; - } - if (changed & WIPHY_PARAM_RETRY_LONG - && (cfg->conf->retry_long != wiphy->retry_long)) { - cfg->conf->retry_long = wiphy->retry_long; - err = brcmf_set_retry(ndev, cfg->conf->retry_long, true); - if (!err) - goto done; - } - if (changed & WIPHY_PARAM_RETRY_SHORT - && (cfg->conf->retry_short != wiphy->retry_short)) { - cfg->conf->retry_short = wiphy->retry_short; - err = brcmf_set_retry(ndev, cfg->conf->retry_short, false); - if (!err) - goto done; - } - -done: - brcmf_dbg(TRACE, "Exit\n"); - return err; -} - -static void brcmf_init_prof(struct brcmf_cfg80211_profile *prof) -{ - memset(prof, 0, sizeof(*prof)); -} - -static void brcmf_link_down(struct brcmf_cfg80211_vif *vif) -{ - struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(vif->wdev.wiphy); - s32 err = 0; - - brcmf_dbg(TRACE, "Enter\n"); - - if (test_bit(BRCMF_VIF_STATUS_CONNECTED, &vif->sme_state)) { - brcmf_dbg(INFO, "Call WLC_DISASSOC to stop excess roaming\n "); - err = brcmf_fil_cmd_data_set(vif->ifp, - BRCMF_C_DISASSOC, NULL, 0); - if (err) { - brcmf_err("WLC_DISASSOC failed (%d)\n", err); - } - clear_bit(BRCMF_VIF_STATUS_CONNECTED, &vif->sme_state); - cfg80211_disconnected(vif->wdev.netdev, 0, NULL, 0, GFP_KERNEL); - - } - clear_bit(BRCMF_VIF_STATUS_CONNECTING, &vif->sme_state); - clear_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status); - brcmf_btcoex_set_mode(vif, BRCMF_BTCOEX_ENABLED, 0); - brcmf_dbg(TRACE, "Exit\n"); -} - -static s32 -brcmf_cfg80211_join_ibss(struct wiphy *wiphy, struct net_device *ndev, - struct cfg80211_ibss_params *params) -{ - struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); - struct brcmf_if *ifp = netdev_priv(ndev); - struct brcmf_cfg80211_profile *profile = &ifp->vif->profile; - struct brcmf_join_params join_params; - size_t join_params_size = 0; - s32 err = 0; - s32 wsec = 0; - s32 bcnprd; - u16 chanspec; - - brcmf_dbg(TRACE, "Enter\n"); - if (!check_vif_up(ifp->vif)) - return -EIO; - - if (params->ssid) - brcmf_dbg(CONN, "SSID: %s\n", params->ssid); - else { - brcmf_dbg(CONN, "SSID: NULL, Not supported\n"); - return -EOPNOTSUPP; - } - - set_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state); - - if (params->bssid) - brcmf_dbg(CONN, "BSSID: %pM\n", params->bssid); - else - brcmf_dbg(CONN, "No BSSID specified\n"); - - if (params->chandef.chan) - brcmf_dbg(CONN, "channel: %d\n", - params->chandef.chan->center_freq); - else - brcmf_dbg(CONN, "no channel specified\n"); - - if (params->channel_fixed) - brcmf_dbg(CONN, "fixed channel required\n"); - else - brcmf_dbg(CONN, "no fixed channel required\n"); - - if (params->ie && params->ie_len) - brcmf_dbg(CONN, "ie len: %d\n", params->ie_len); - else - brcmf_dbg(CONN, "no ie specified\n"); - - if (params->beacon_interval) - brcmf_dbg(CONN, "beacon interval: %d\n", - params->beacon_interval); - else - brcmf_dbg(CONN, "no beacon interval specified\n"); - - if (params->basic_rates) - brcmf_dbg(CONN, "basic rates: %08X\n", params->basic_rates); - else - brcmf_dbg(CONN, "no basic rates specified\n"); - - if (params->privacy) - brcmf_dbg(CONN, "privacy required\n"); - else - brcmf_dbg(CONN, "no privacy required\n"); - - /* Configure Privacy for starter */ - if (params->privacy) - wsec |= WEP_ENABLED; - - err = brcmf_fil_iovar_int_set(ifp, "wsec", wsec); - if (err) { - brcmf_err("wsec failed (%d)\n", err); - goto done; - } - - /* Configure Beacon Interval for starter */ - if (params->beacon_interval) - bcnprd = params->beacon_interval; - else - bcnprd = 100; - - err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_BCNPRD, bcnprd); - if (err) { - brcmf_err("WLC_SET_BCNPRD failed (%d)\n", err); - goto done; - } - - /* Configure required join parameter */ - memset(&join_params, 0, sizeof(struct brcmf_join_params)); - - /* SSID */ - profile->ssid.SSID_len = min_t(u32, params->ssid_len, 32); - memcpy(profile->ssid.SSID, params->ssid, profile->ssid.SSID_len); - memcpy(join_params.ssid_le.SSID, params->ssid, profile->ssid.SSID_len); - join_params.ssid_le.SSID_len = cpu_to_le32(profile->ssid.SSID_len); - join_params_size = sizeof(join_params.ssid_le); - - /* BSSID */ - if (params->bssid) { - memcpy(join_params.params_le.bssid, params->bssid, ETH_ALEN); - join_params_size = sizeof(join_params.ssid_le) + - BRCMF_ASSOC_PARAMS_FIXED_SIZE; - memcpy(profile->bssid, params->bssid, ETH_ALEN); - } else { - memset(join_params.params_le.bssid, 0xFF, ETH_ALEN); - memset(profile->bssid, 0, ETH_ALEN); - } - - /* Channel */ - if (params->chandef.chan) { - u32 target_channel; - - cfg->channel = - ieee80211_frequency_to_channel( - params->chandef.chan->center_freq); - if (params->channel_fixed) { - /* adding chanspec */ - chanspec = chandef_to_chanspec(&cfg->d11inf, - ¶ms->chandef); - join_params.params_le.chanspec_list[0] = - cpu_to_le16(chanspec); - join_params.params_le.chanspec_num = cpu_to_le32(1); - join_params_size += sizeof(join_params.params_le); - } - - /* set channel for starter */ - target_channel = cfg->channel; - err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_CHANNEL, - target_channel); - if (err) { - brcmf_err("WLC_SET_CHANNEL failed (%d)\n", err); - goto done; - } - } else - cfg->channel = 0; - - cfg->ibss_starter = false; - - - err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SSID, - &join_params, join_params_size); - if (err) { - brcmf_err("WLC_SET_SSID failed (%d)\n", err); - goto done; - } - -done: - if (err) - clear_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state); - brcmf_dbg(TRACE, "Exit\n"); - return err; -} - -static s32 -brcmf_cfg80211_leave_ibss(struct wiphy *wiphy, struct net_device *ndev) -{ - struct brcmf_if *ifp = netdev_priv(ndev); - - brcmf_dbg(TRACE, "Enter\n"); - if (!check_vif_up(ifp->vif)) - return -EIO; - - brcmf_link_down(ifp->vif); - - brcmf_dbg(TRACE, "Exit\n"); - - return 0; -} - -static s32 brcmf_set_wpa_version(struct net_device *ndev, - struct cfg80211_connect_params *sme) -{ - struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev); - struct brcmf_cfg80211_security *sec; - s32 val = 0; - s32 err = 0; - - if (sme->crypto.wpa_versions & NL80211_WPA_VERSION_1) - val = WPA_AUTH_PSK | WPA_AUTH_UNSPECIFIED; - else if (sme->crypto.wpa_versions & NL80211_WPA_VERSION_2) - val = WPA2_AUTH_PSK | WPA2_AUTH_UNSPECIFIED; - else - val = WPA_AUTH_DISABLED; - brcmf_dbg(CONN, "setting wpa_auth to 0x%0x\n", val); - err = brcmf_fil_bsscfg_int_set(netdev_priv(ndev), "wpa_auth", val); - if (err) { - brcmf_err("set wpa_auth failed (%d)\n", err); - return err; - } - sec = &profile->sec; - sec->wpa_versions = sme->crypto.wpa_versions; - return err; -} - -static s32 brcmf_set_auth_type(struct net_device *ndev, - struct cfg80211_connect_params *sme) -{ - struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev); - struct brcmf_cfg80211_security *sec; - s32 val = 0; - s32 err = 0; - - switch (sme->auth_type) { - case NL80211_AUTHTYPE_OPEN_SYSTEM: - val = 0; - brcmf_dbg(CONN, "open system\n"); - break; - case NL80211_AUTHTYPE_SHARED_KEY: - val = 1; - brcmf_dbg(CONN, "shared key\n"); - break; - case NL80211_AUTHTYPE_AUTOMATIC: - val = 2; - brcmf_dbg(CONN, "automatic\n"); - break; - case NL80211_AUTHTYPE_NETWORK_EAP: - brcmf_dbg(CONN, "network eap\n"); - default: - val = 2; - brcmf_err("invalid auth type (%d)\n", sme->auth_type); - break; - } - - err = brcmf_fil_bsscfg_int_set(netdev_priv(ndev), "auth", val); - if (err) { - brcmf_err("set auth failed (%d)\n", err); - return err; - } - sec = &profile->sec; - sec->auth_type = sme->auth_type; - return err; -} - -static s32 -brcmf_set_wsec_mode(struct net_device *ndev, - struct cfg80211_connect_params *sme, bool mfp) -{ - struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev); - struct brcmf_cfg80211_security *sec; - s32 pval = 0; - s32 gval = 0; - s32 wsec; - s32 err = 0; - - if (sme->crypto.n_ciphers_pairwise) { - switch (sme->crypto.ciphers_pairwise[0]) { - case WLAN_CIPHER_SUITE_WEP40: - case WLAN_CIPHER_SUITE_WEP104: - pval = WEP_ENABLED; - break; - case WLAN_CIPHER_SUITE_TKIP: - pval = TKIP_ENABLED; - break; - case WLAN_CIPHER_SUITE_CCMP: - pval = AES_ENABLED; - break; - case WLAN_CIPHER_SUITE_AES_CMAC: - pval = AES_ENABLED; - break; - default: - brcmf_err("invalid cipher pairwise (%d)\n", - sme->crypto.ciphers_pairwise[0]); - return -EINVAL; - } - } - if (sme->crypto.cipher_group) { - switch (sme->crypto.cipher_group) { - case WLAN_CIPHER_SUITE_WEP40: - case WLAN_CIPHER_SUITE_WEP104: - gval = WEP_ENABLED; - break; - case WLAN_CIPHER_SUITE_TKIP: - gval = TKIP_ENABLED; - break; - case WLAN_CIPHER_SUITE_CCMP: - gval = AES_ENABLED; - break; - case WLAN_CIPHER_SUITE_AES_CMAC: - gval = AES_ENABLED; - break; - default: - brcmf_err("invalid cipher group (%d)\n", - sme->crypto.cipher_group); - return -EINVAL; - } - } - - brcmf_dbg(CONN, "pval (%d) gval (%d)\n", pval, gval); - /* In case of privacy, but no security and WPS then simulate */ - /* setting AES. WPS-2.0 allows no security */ - if (brcmf_find_wpsie(sme->ie, sme->ie_len) && !pval && !gval && - sme->privacy) - pval = AES_ENABLED; - - if (mfp) - wsec = pval | gval | MFP_CAPABLE; - else - wsec = pval | gval; - err = brcmf_fil_bsscfg_int_set(netdev_priv(ndev), "wsec", wsec); - if (err) { - brcmf_err("error (%d)\n", err); - return err; - } - - sec = &profile->sec; - sec->cipher_pairwise = sme->crypto.ciphers_pairwise[0]; - sec->cipher_group = sme->crypto.cipher_group; - - return err; -} - -static s32 -brcmf_set_key_mgmt(struct net_device *ndev, struct cfg80211_connect_params *sme) -{ - struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev); - struct brcmf_cfg80211_security *sec; - s32 val = 0; - s32 err = 0; - - if (sme->crypto.n_akm_suites) { - err = brcmf_fil_bsscfg_int_get(netdev_priv(ndev), - "wpa_auth", &val); - if (err) { - brcmf_err("could not get wpa_auth (%d)\n", err); - return err; - } - if (val & (WPA_AUTH_PSK | WPA_AUTH_UNSPECIFIED)) { - switch (sme->crypto.akm_suites[0]) { - case WLAN_AKM_SUITE_8021X: - val = WPA_AUTH_UNSPECIFIED; - break; - case WLAN_AKM_SUITE_PSK: - val = WPA_AUTH_PSK; - break; - default: - brcmf_err("invalid cipher group (%d)\n", - sme->crypto.cipher_group); - return -EINVAL; - } - } else if (val & (WPA2_AUTH_PSK | WPA2_AUTH_UNSPECIFIED)) { - switch (sme->crypto.akm_suites[0]) { - case WLAN_AKM_SUITE_8021X: - val = WPA2_AUTH_UNSPECIFIED; - break; - case WLAN_AKM_SUITE_PSK: - val = WPA2_AUTH_PSK; - break; - default: - brcmf_err("invalid cipher group (%d)\n", - sme->crypto.cipher_group); - return -EINVAL; - } - } - - brcmf_dbg(CONN, "setting wpa_auth to %d\n", val); - err = brcmf_fil_bsscfg_int_set(netdev_priv(ndev), - "wpa_auth", val); - if (err) { - brcmf_err("could not set wpa_auth (%d)\n", err); - return err; - } - } - sec = &profile->sec; - sec->wpa_auth = sme->crypto.akm_suites[0]; - - return err; -} - -static s32 -brcmf_set_sharedkey(struct net_device *ndev, - struct cfg80211_connect_params *sme) -{ - struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev); - struct brcmf_cfg80211_security *sec; - struct brcmf_wsec_key key; - s32 val; - s32 err = 0; - - brcmf_dbg(CONN, "key len (%d)\n", sme->key_len); - - if (sme->key_len == 0) - return 0; - - sec = &profile->sec; - brcmf_dbg(CONN, "wpa_versions 0x%x cipher_pairwise 0x%x\n", - sec->wpa_versions, sec->cipher_pairwise); - - if (sec->wpa_versions & (NL80211_WPA_VERSION_1 | NL80211_WPA_VERSION_2)) - return 0; - - if (!(sec->cipher_pairwise & - (WLAN_CIPHER_SUITE_WEP40 | WLAN_CIPHER_SUITE_WEP104))) - return 0; - - memset(&key, 0, sizeof(key)); - key.len = (u32) sme->key_len; - key.index = (u32) sme->key_idx; - if (key.len > sizeof(key.data)) { - brcmf_err("Too long key length (%u)\n", key.len); - return -EINVAL; - } - memcpy(key.data, sme->key, key.len); - key.flags = BRCMF_PRIMARY_KEY; - switch (sec->cipher_pairwise) { - case WLAN_CIPHER_SUITE_WEP40: - key.algo = CRYPTO_ALGO_WEP1; - break; - case WLAN_CIPHER_SUITE_WEP104: - key.algo = CRYPTO_ALGO_WEP128; - break; - default: - brcmf_err("Invalid algorithm (%d)\n", - sme->crypto.ciphers_pairwise[0]); - return -EINVAL; - } - /* Set the new key/index */ - brcmf_dbg(CONN, "key length (%d) key index (%d) algo (%d)\n", - key.len, key.index, key.algo); - brcmf_dbg(CONN, "key \"%s\"\n", key.data); - err = send_key_to_dongle(ndev, &key); - if (err) - return err; - - if (sec->auth_type == NL80211_AUTHTYPE_SHARED_KEY) { - brcmf_dbg(CONN, "set auth_type to shared key\n"); - val = WL_AUTH_SHARED_KEY; /* shared key */ - err = brcmf_fil_bsscfg_int_set(netdev_priv(ndev), "auth", val); - if (err) - brcmf_err("set auth failed (%d)\n", err); - } - return err; -} - -static -enum nl80211_auth_type brcmf_war_auth_type(struct brcmf_if *ifp, - enum nl80211_auth_type type) -{ - if (type == NL80211_AUTHTYPE_AUTOMATIC && - brcmf_feat_is_quirk_enabled(ifp, BRCMF_FEAT_QUIRK_AUTO_AUTH)) { - brcmf_dbg(CONN, "WAR: use OPEN instead of AUTO\n"); - type = NL80211_AUTHTYPE_OPEN_SYSTEM; - } - return type; -} - -static s32 -brcmf_cfg80211_connect(struct wiphy *wiphy, struct net_device *ndev, - struct cfg80211_connect_params *sme) -{ - struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); - struct brcmf_if *ifp = netdev_priv(ndev); - struct brcmf_cfg80211_profile *profile = &ifp->vif->profile; - struct ieee80211_channel *chan = sme->channel; - struct brcmf_join_params join_params; - size_t join_params_size; - const struct brcmf_tlv *rsn_ie; - const struct brcmf_vs_tlv *wpa_ie; - const void *ie; - u32 ie_len; - struct brcmf_ext_join_params_le *ext_join_params; - u16 chanspec; - s32 err = 0; - - brcmf_dbg(TRACE, "Enter\n"); - if (!check_vif_up(ifp->vif)) - return -EIO; - - if (!sme->ssid) { - brcmf_err("Invalid ssid\n"); - return -EOPNOTSUPP; - } - - if (ifp->vif == cfg->p2p.bss_idx[P2PAPI_BSSCFG_PRIMARY].vif) { - /* A normal (non P2P) connection request setup. */ - ie = NULL; - ie_len = 0; - /* find the WPA_IE */ - wpa_ie = brcmf_find_wpaie((u8 *)sme->ie, sme->ie_len); - if (wpa_ie) { - ie = wpa_ie; - ie_len = wpa_ie->len + TLV_HDR_LEN; - } else { - /* find the RSN_IE */ - rsn_ie = brcmf_parse_tlvs((const u8 *)sme->ie, - sme->ie_len, - WLAN_EID_RSN); - if (rsn_ie) { - ie = rsn_ie; - ie_len = rsn_ie->len + TLV_HDR_LEN; - } - } - brcmf_fil_iovar_data_set(ifp, "wpaie", ie, ie_len); - } - - err = brcmf_vif_set_mgmt_ie(ifp->vif, BRCMF_VNDR_IE_ASSOCREQ_FLAG, - sme->ie, sme->ie_len); - if (err) - brcmf_err("Set Assoc REQ IE Failed\n"); - else - brcmf_dbg(TRACE, "Applied Vndr IEs for Assoc request\n"); - - set_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state); - - if (chan) { - cfg->channel = - ieee80211_frequency_to_channel(chan->center_freq); - chanspec = channel_to_chanspec(&cfg->d11inf, chan); - brcmf_dbg(CONN, "channel=%d, center_req=%d, chanspec=0x%04x\n", - cfg->channel, chan->center_freq, chanspec); - } else { - cfg->channel = 0; - chanspec = 0; - } - - brcmf_dbg(INFO, "ie (%p), ie_len (%zd)\n", sme->ie, sme->ie_len); - - err = brcmf_set_wpa_version(ndev, sme); - if (err) { - brcmf_err("wl_set_wpa_version failed (%d)\n", err); - goto done; - } - - sme->auth_type = brcmf_war_auth_type(ifp, sme->auth_type); - err = brcmf_set_auth_type(ndev, sme); - if (err) { - brcmf_err("wl_set_auth_type failed (%d)\n", err); - goto done; - } - - err = brcmf_set_wsec_mode(ndev, sme, sme->mfp == NL80211_MFP_REQUIRED); - if (err) { - brcmf_err("wl_set_set_cipher failed (%d)\n", err); - goto done; - } - - err = brcmf_set_key_mgmt(ndev, sme); - if (err) { - brcmf_err("wl_set_key_mgmt failed (%d)\n", err); - goto done; - } - - err = brcmf_set_sharedkey(ndev, sme); - if (err) { - brcmf_err("brcmf_set_sharedkey failed (%d)\n", err); - goto done; - } - - profile->ssid.SSID_len = min_t(u32, (u32)sizeof(profile->ssid.SSID), - (u32)sme->ssid_len); - memcpy(&profile->ssid.SSID, sme->ssid, profile->ssid.SSID_len); - if (profile->ssid.SSID_len < IEEE80211_MAX_SSID_LEN) { - profile->ssid.SSID[profile->ssid.SSID_len] = 0; - brcmf_dbg(CONN, "SSID \"%s\", len (%d)\n", profile->ssid.SSID, - profile->ssid.SSID_len); - } - - /* Join with specific BSSID and cached SSID - * If SSID is zero join based on BSSID only - */ - join_params_size = offsetof(struct brcmf_ext_join_params_le, assoc_le) + - offsetof(struct brcmf_assoc_params_le, chanspec_list); - if (cfg->channel) - join_params_size += sizeof(u16); - ext_join_params = kzalloc(join_params_size, GFP_KERNEL); - if (ext_join_params == NULL) { - err = -ENOMEM; - goto done; - } - ext_join_params->ssid_le.SSID_len = cpu_to_le32(profile->ssid.SSID_len); - memcpy(&ext_join_params->ssid_le.SSID, sme->ssid, - profile->ssid.SSID_len); - - /* Set up join scan parameters */ - ext_join_params->scan_le.scan_type = -1; - ext_join_params->scan_le.home_time = cpu_to_le32(-1); - - if (sme->bssid) - memcpy(&ext_join_params->assoc_le.bssid, sme->bssid, ETH_ALEN); - else - memset(&ext_join_params->assoc_le.bssid, 0xFF, ETH_ALEN); - - if (cfg->channel) { - ext_join_params->assoc_le.chanspec_num = cpu_to_le32(1); - - ext_join_params->assoc_le.chanspec_list[0] = - cpu_to_le16(chanspec); - /* Increase dwell time to receive probe response or detect - * beacon from target AP at a noisy air only during connect - * command. - */ - ext_join_params->scan_le.active_time = - cpu_to_le32(BRCMF_SCAN_JOIN_ACTIVE_DWELL_TIME_MS); - ext_join_params->scan_le.passive_time = - cpu_to_le32(BRCMF_SCAN_JOIN_PASSIVE_DWELL_TIME_MS); - /* To sync with presence period of VSDB GO send probe request - * more frequently. Probe request will be stopped when it gets - * probe response from target AP/GO. - */ - ext_join_params->scan_le.nprobes = - cpu_to_le32(BRCMF_SCAN_JOIN_ACTIVE_DWELL_TIME_MS / - BRCMF_SCAN_JOIN_PROBE_INTERVAL_MS); - } else { - ext_join_params->scan_le.active_time = cpu_to_le32(-1); - ext_join_params->scan_le.passive_time = cpu_to_le32(-1); - ext_join_params->scan_le.nprobes = cpu_to_le32(-1); - } - - err = brcmf_fil_bsscfg_data_set(ifp, "join", ext_join_params, - join_params_size); - kfree(ext_join_params); - if (!err) - /* This is it. join command worked, we are done */ - goto done; - - /* join command failed, fallback to set ssid */ - memset(&join_params, 0, sizeof(join_params)); - join_params_size = sizeof(join_params.ssid_le); - - memcpy(&join_params.ssid_le.SSID, sme->ssid, profile->ssid.SSID_len); - join_params.ssid_le.SSID_len = cpu_to_le32(profile->ssid.SSID_len); - - if (sme->bssid) - memcpy(join_params.params_le.bssid, sme->bssid, ETH_ALEN); - else - memset(join_params.params_le.bssid, 0xFF, ETH_ALEN); - - if (cfg->channel) { - join_params.params_le.chanspec_list[0] = cpu_to_le16(chanspec); - join_params.params_le.chanspec_num = cpu_to_le32(1); - join_params_size += sizeof(join_params.params_le); - } - err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SSID, - &join_params, join_params_size); - if (err) - brcmf_err("BRCMF_C_SET_SSID failed (%d)\n", err); - -done: - if (err) - clear_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state); - brcmf_dbg(TRACE, "Exit\n"); - return err; -} - -static s32 -brcmf_cfg80211_disconnect(struct wiphy *wiphy, struct net_device *ndev, - u16 reason_code) -{ - struct brcmf_if *ifp = netdev_priv(ndev); - struct brcmf_cfg80211_profile *profile = &ifp->vif->profile; - struct brcmf_scb_val_le scbval; - s32 err = 0; - - brcmf_dbg(TRACE, "Enter. Reason code = %d\n", reason_code); - if (!check_vif_up(ifp->vif)) - return -EIO; - - clear_bit(BRCMF_VIF_STATUS_CONNECTED, &ifp->vif->sme_state); - cfg80211_disconnected(ndev, reason_code, NULL, 0, GFP_KERNEL); - - memcpy(&scbval.ea, &profile->bssid, ETH_ALEN); - scbval.val = cpu_to_le32(reason_code); - err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_DISASSOC, - &scbval, sizeof(scbval)); - if (err) - brcmf_err("error (%d)\n", err); - - brcmf_dbg(TRACE, "Exit\n"); - return err; -} - -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); - - brcmf_dbg(TRACE, "Enter\n"); - if (!check_vif_up(ifp->vif)) - return -EIO; - - switch (type) { - case NL80211_TX_POWER_AUTOMATIC: - break; - case NL80211_TX_POWER_LIMITED: - case NL80211_TX_POWER_FIXED: - if (dbm < 0) { - brcmf_err("TX_POWER_FIXED - dbm is negative\n"); - err = -EINVAL; - goto done; - } - break; - } - /* Make sure radio is off or on as far as software is concerned */ - disable = WL_RADIO_SW_DISABLE << 16; - err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_RADIO, disable); - 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)); - if (err) - brcmf_err("qtxpower error (%d)\n", err); - cfg->conf->tx_power = dbm; - -done: - brcmf_dbg(TRACE, "Exit\n"); - return err; -} - -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; - - brcmf_dbg(TRACE, "Enter\n"); - if (!check_vif_up(ifp->vif)) - return -EIO; - - err = brcmf_fil_iovar_int_get(ifp, "qtxpower", &txpwrdbm); - if (err) { - brcmf_err("error (%d)\n", err); - goto done; - } - - result = (u8) (txpwrdbm & ~WL_TXPWR_OVERRIDE); - *dbm = (s32) brcmf_qdbm_to_mw(result); - -done: - brcmf_dbg(TRACE, "Exit\n"); - return err; -} - -static s32 -brcmf_cfg80211_config_default_key(struct wiphy *wiphy, struct net_device *ndev, - u8 key_idx, bool unicast, bool multicast) -{ - struct brcmf_if *ifp = netdev_priv(ndev); - u32 index; - u32 wsec; - s32 err = 0; - - brcmf_dbg(TRACE, "Enter\n"); - brcmf_dbg(CONN, "key index (%d)\n", key_idx); - if (!check_vif_up(ifp->vif)) - return -EIO; - - err = brcmf_fil_bsscfg_int_get(ifp, "wsec", &wsec); - if (err) { - brcmf_err("WLC_GET_WSEC error (%d)\n", err); - goto done; - } - - if (wsec & WEP_ENABLED) { - /* Just select a new current key */ - index = key_idx; - err = brcmf_fil_cmd_int_set(ifp, - BRCMF_C_SET_KEY_PRIMARY, index); - if (err) - brcmf_err("error (%d)\n", err); - } -done: - brcmf_dbg(TRACE, "Exit\n"); - return err; -} - -static s32 -brcmf_add_keyext(struct wiphy *wiphy, struct net_device *ndev, - u8 key_idx, const u8 *mac_addr, struct key_params *params) -{ - struct brcmf_if *ifp = netdev_priv(ndev); - struct brcmf_wsec_key key; - s32 err = 0; - u8 keybuf[8]; - - memset(&key, 0, sizeof(key)); - key.index = (u32) key_idx; - /* Instead of bcast for ea address for default wep keys, - driver needs it to be Null */ - if (!is_multicast_ether_addr(mac_addr)) - memcpy((char *)&key.ea, (void *)mac_addr, ETH_ALEN); - key.len = (u32) params->key_len; - /* check for key index change */ - if (key.len == 0) { - /* key delete */ - err = send_key_to_dongle(ndev, &key); - if (err) - brcmf_err("key delete error (%d)\n", err); - } else { - if (key.len > sizeof(key.data)) { - brcmf_err("Invalid key length (%d)\n", key.len); - return -EINVAL; - } - - brcmf_dbg(CONN, "Setting the key index %d\n", key.index); - memcpy(key.data, params->key, key.len); - - if (!brcmf_is_apmode(ifp->vif) && - (params->cipher == WLAN_CIPHER_SUITE_TKIP)) { - brcmf_dbg(CONN, "Swapping RX/TX MIC key\n"); - memcpy(keybuf, &key.data[24], sizeof(keybuf)); - memcpy(&key.data[24], &key.data[16], sizeof(keybuf)); - memcpy(&key.data[16], keybuf, sizeof(keybuf)); - } - - /* if IW_ENCODE_EXT_RX_SEQ_VALID set */ - if (params->seq && params->seq_len == 6) { - /* rx iv */ - u8 *ivptr; - ivptr = (u8 *) params->seq; - key.rxiv.hi = (ivptr[5] << 24) | (ivptr[4] << 16) | - (ivptr[3] << 8) | ivptr[2]; - key.rxiv.lo = (ivptr[1] << 8) | ivptr[0]; - key.iv_initialized = true; - } - - switch (params->cipher) { - case WLAN_CIPHER_SUITE_WEP40: - key.algo = CRYPTO_ALGO_WEP1; - brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP40\n"); - break; - case WLAN_CIPHER_SUITE_WEP104: - key.algo = CRYPTO_ALGO_WEP128; - brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP104\n"); - break; - case WLAN_CIPHER_SUITE_TKIP: - key.algo = CRYPTO_ALGO_TKIP; - brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_TKIP\n"); - break; - case WLAN_CIPHER_SUITE_AES_CMAC: - key.algo = CRYPTO_ALGO_AES_CCM; - brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_AES_CMAC\n"); - break; - case WLAN_CIPHER_SUITE_CCMP: - key.algo = CRYPTO_ALGO_AES_CCM; - brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_CCMP\n"); - break; - default: - brcmf_err("Invalid cipher (0x%x)\n", params->cipher); - return -EINVAL; - } - err = send_key_to_dongle(ndev, &key); - if (err) - brcmf_err("wsec_key error (%d)\n", err); - } - return err; -} - -static s32 -brcmf_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev, - u8 key_idx, bool pairwise, const u8 *mac_addr, - struct key_params *params) -{ - struct brcmf_if *ifp = netdev_priv(ndev); - struct brcmf_wsec_key key; - s32 val; - s32 wsec; - s32 err = 0; - u8 keybuf[8]; - - brcmf_dbg(TRACE, "Enter\n"); - brcmf_dbg(CONN, "key index (%d)\n", key_idx); - if (!check_vif_up(ifp->vif)) - return -EIO; - - if (mac_addr && - (params->cipher != WLAN_CIPHER_SUITE_WEP40) && - (params->cipher != WLAN_CIPHER_SUITE_WEP104)) { - brcmf_dbg(TRACE, "Exit"); - return brcmf_add_keyext(wiphy, ndev, key_idx, mac_addr, params); - } - memset(&key, 0, sizeof(key)); - - key.len = (u32) params->key_len; - key.index = (u32) key_idx; - - if (key.len > sizeof(key.data)) { - brcmf_err("Too long key length (%u)\n", key.len); - err = -EINVAL; - goto done; - } - memcpy(key.data, params->key, key.len); - - key.flags = BRCMF_PRIMARY_KEY; - switch (params->cipher) { - case WLAN_CIPHER_SUITE_WEP40: - key.algo = CRYPTO_ALGO_WEP1; - val = WEP_ENABLED; - brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP40\n"); - break; - case WLAN_CIPHER_SUITE_WEP104: - key.algo = CRYPTO_ALGO_WEP128; - val = WEP_ENABLED; - brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP104\n"); - break; - case WLAN_CIPHER_SUITE_TKIP: - if (!brcmf_is_apmode(ifp->vif)) { - brcmf_dbg(CONN, "Swapping RX/TX MIC key\n"); - memcpy(keybuf, &key.data[24], sizeof(keybuf)); - memcpy(&key.data[24], &key.data[16], sizeof(keybuf)); - memcpy(&key.data[16], keybuf, sizeof(keybuf)); - } - key.algo = CRYPTO_ALGO_TKIP; - val = TKIP_ENABLED; - brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_TKIP\n"); - break; - case WLAN_CIPHER_SUITE_AES_CMAC: - key.algo = CRYPTO_ALGO_AES_CCM; - val = AES_ENABLED; - brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_AES_CMAC\n"); - break; - case WLAN_CIPHER_SUITE_CCMP: - key.algo = CRYPTO_ALGO_AES_CCM; - val = AES_ENABLED; - brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_CCMP\n"); - break; - default: - brcmf_err("Invalid cipher (0x%x)\n", params->cipher); - err = -EINVAL; - goto done; - } - - err = send_key_to_dongle(ndev, &key); - if (err) - goto done; - - err = brcmf_fil_bsscfg_int_get(ifp, "wsec", &wsec); - if (err) { - brcmf_err("get wsec error (%d)\n", err); - goto done; - } - wsec |= val; - err = brcmf_fil_bsscfg_int_set(ifp, "wsec", wsec); - if (err) { - brcmf_err("set wsec error (%d)\n", err); - goto done; - } - -done: - brcmf_dbg(TRACE, "Exit\n"); - return err; -} - -static s32 -brcmf_cfg80211_del_key(struct wiphy *wiphy, struct net_device *ndev, - u8 key_idx, bool pairwise, const u8 *mac_addr) -{ - struct brcmf_if *ifp = netdev_priv(ndev); - struct brcmf_wsec_key key; - s32 err = 0; - - brcmf_dbg(TRACE, "Enter\n"); - if (!check_vif_up(ifp->vif)) - return -EIO; - - if (key_idx >= DOT11_MAX_DEFAULT_KEYS) { - /* we ignore this key index in this case */ - brcmf_err("invalid key index (%d)\n", key_idx); - return -EINVAL; - } - - memset(&key, 0, sizeof(key)); - - key.index = (u32) key_idx; - key.flags = BRCMF_PRIMARY_KEY; - key.algo = CRYPTO_ALGO_OFF; - - brcmf_dbg(CONN, "key index (%d)\n", key_idx); - - /* Set the new key/index */ - err = send_key_to_dongle(ndev, &key); - - brcmf_dbg(TRACE, "Exit\n"); - return err; -} - -static s32 -brcmf_cfg80211_get_key(struct wiphy *wiphy, struct net_device *ndev, - u8 key_idx, bool pairwise, const u8 *mac_addr, void *cookie, - void (*callback) (void *cookie, struct key_params * params)) -{ - struct key_params params; - struct brcmf_if *ifp = netdev_priv(ndev); - struct brcmf_cfg80211_profile *profile = &ifp->vif->profile; - struct brcmf_cfg80211_security *sec; - s32 wsec; - s32 err = 0; - - brcmf_dbg(TRACE, "Enter\n"); - brcmf_dbg(CONN, "key index (%d)\n", key_idx); - if (!check_vif_up(ifp->vif)) - return -EIO; - - memset(¶ms, 0, sizeof(params)); - - err = brcmf_fil_bsscfg_int_get(ifp, "wsec", &wsec); - if (err) { - brcmf_err("WLC_GET_WSEC error (%d)\n", err); - /* Ignore this error, may happen during DISASSOC */ - err = -EAGAIN; - goto done; - } - if (wsec & WEP_ENABLED) { - sec = &profile->sec; - if (sec->cipher_pairwise & WLAN_CIPHER_SUITE_WEP40) { - params.cipher = WLAN_CIPHER_SUITE_WEP40; - brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP40\n"); - } else if (sec->cipher_pairwise & WLAN_CIPHER_SUITE_WEP104) { - params.cipher = WLAN_CIPHER_SUITE_WEP104; - brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP104\n"); - } - } else if (wsec & TKIP_ENABLED) { - params.cipher = WLAN_CIPHER_SUITE_TKIP; - brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_TKIP\n"); - } else if (wsec & AES_ENABLED) { - params.cipher = WLAN_CIPHER_SUITE_AES_CMAC; - brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_AES_CMAC\n"); - } else { - brcmf_err("Invalid algo (0x%x)\n", wsec); - err = -EINVAL; - goto done; - } - callback(cookie, ¶ms); - -done: - brcmf_dbg(TRACE, "Exit\n"); - return err; -} - -static s32 -brcmf_cfg80211_config_default_mgmt_key(struct wiphy *wiphy, - struct net_device *ndev, u8 key_idx) -{ - brcmf_dbg(INFO, "Not supported\n"); - - return -EOPNOTSUPP; -} - -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; - - 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); - err = brcmf_fil_iovar_data_get(ifp, "sta_info", - &sta_info_le, - sizeof(sta_info_le)); - if (err < 0) { - brcmf_err("GET STA INFO failed, %d\n", err); - goto done; - } - sinfo->filled = STATION_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 |= STATION_INFO_CONNECTED_TIME; - sinfo->connected_time = le32_to_cpu(sta_info_le.in); - } - brcmf_dbg(TRACE, "STA idle time : %d ms, connected time :%d sec\n", - sinfo->inactive_time, sinfo->connected_time); - } else if (ifp->vif->wdev.iftype == NL80211_IFTYPE_STATION) { - if (memcmp(mac, bssid, ETH_ALEN)) { - brcmf_err("Wrong Mac address cfg_mac-%pM wl_bssid-%pM\n", - mac, bssid); - err = -ENOENT; - goto done; - } - /* Report the current tx rate */ - err = brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_RATE, &rate); - if (err) { - brcmf_err("Could not get rate (%d)\n", err); - goto done; - } else { - sinfo->filled |= STATION_INFO_TX_BITRATE; - sinfo->txrate.legacy = rate * 5; - brcmf_dbg(CONN, "Rate %d Mbps\n", rate / 2); - } - - 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 |= STATION_INFO_SIGNAL; - sinfo->signal = rssi; - brcmf_dbg(CONN, "RSSI %d dBm\n", rssi); - } - err = brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_BCNPRD, - &beacon_period); - if (err) { - brcmf_err("Could not get beacon period (%d)\n", - err); - goto done; - } else { - sinfo->bss_param.beacon_interval = - beacon_period; - brcmf_dbg(CONN, "Beacon peroid %d\n", - beacon_period); - } - err = brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_DTIMPRD, - &dtim_period); - if (err) { - brcmf_err("Could not get DTIM period (%d)\n", - err); - goto done; - } else { - sinfo->bss_param.dtim_period = dtim_period; - brcmf_dbg(CONN, "DTIM peroid %d\n", - dtim_period); - } - sinfo->filled |= STATION_INFO_BSS_PARAM; - } - } else - err = -EPERM; -done: - brcmf_dbg(TRACE, "Exit\n"); - return err; -} - -static s32 -brcmf_cfg80211_set_power_mgmt(struct wiphy *wiphy, struct net_device *ndev, - bool enabled, s32 timeout) -{ - s32 pm; - s32 err = 0; - struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); - struct brcmf_if *ifp = netdev_priv(ndev); - - brcmf_dbg(TRACE, "Enter\n"); - - /* - * Powersave enable/disable request is coming from the - * cfg80211 even before the interface is up. In that - * scenario, driver will be storing the power save - * preference in cfg struct to apply this to - * FW later while initializing the dongle - */ - cfg->pwr_save = enabled; - if (!check_vif_up(ifp->vif)) { - - brcmf_dbg(INFO, "Device is not ready, storing the value in cfg_info struct\n"); - goto done; - } - - pm = enabled ? PM_FAST : PM_OFF; - /* Do not enable the power save after assoc if it is a p2p interface */ - if (ifp->vif->wdev.iftype == NL80211_IFTYPE_P2P_CLIENT) { - brcmf_dbg(INFO, "Do not enable power save for P2P clients\n"); - pm = PM_OFF; - } - brcmf_dbg(INFO, "power save %s\n", (pm ? "enabled" : "disabled")); - - err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM, pm); - if (err) { - if (err == -ENODEV) - brcmf_err("net_device is not ready yet\n"); - else - brcmf_err("error (%d)\n", err); - } -done: - brcmf_dbg(TRACE, "Exit\n"); - return err; -} - -static s32 brcmf_inform_single_bss(struct brcmf_cfg80211_info *cfg, - struct brcmf_bss_info_le *bi) -{ - struct wiphy *wiphy = cfg_to_wiphy(cfg); - struct ieee80211_channel *notify_channel; - struct cfg80211_bss *bss; - struct ieee80211_supported_band *band; - struct brcmu_chan ch; - u16 channel; - u32 freq; - u16 notify_capability; - u16 notify_interval; - u8 *notify_ie; - size_t notify_ielen; - s32 notify_signal; - - if (le32_to_cpu(bi->length) > WL_BSS_INFO_MAX) { - brcmf_err("Bss info is larger than buffer. Discarding\n"); - return 0; - } - - if (!bi->ctl_ch) { - ch.chspec = le16_to_cpu(bi->chanspec); - cfg->d11inf.decchspec(&ch); - bi->ctl_ch = ch.chnum; - } - channel = bi->ctl_ch; - - if (channel <= CH_MAX_2G_CHANNEL) - band = wiphy->bands[IEEE80211_BAND_2GHZ]; - else - band = wiphy->bands[IEEE80211_BAND_5GHZ]; - - freq = ieee80211_channel_to_frequency(channel, band->band); - notify_channel = ieee80211_get_channel(wiphy, freq); - - notify_capability = le16_to_cpu(bi->capability); - notify_interval = le16_to_cpu(bi->beacon_period); - notify_ie = (u8 *)bi + le16_to_cpu(bi->ie_offset); - notify_ielen = le32_to_cpu(bi->ie_length); - notify_signal = (s16)le16_to_cpu(bi->RSSI) * 100; - - brcmf_dbg(CONN, "bssid: %pM\n", bi->BSSID); - brcmf_dbg(CONN, "Channel: %d(%d)\n", channel, freq); - brcmf_dbg(CONN, "Capability: %X\n", notify_capability); - brcmf_dbg(CONN, "Beacon interval: %d\n", notify_interval); - brcmf_dbg(CONN, "Signal: %d\n", notify_signal); - - bss = cfg80211_inform_bss(wiphy, notify_channel, - CFG80211_BSS_FTYPE_UNKNOWN, - (const u8 *)bi->BSSID, - 0, notify_capability, - notify_interval, notify_ie, - notify_ielen, notify_signal, - GFP_KERNEL); - - if (!bss) - return -ENOMEM; - - cfg80211_put_bss(wiphy, bss); - - return 0; -} - -static struct brcmf_bss_info_le * -next_bss_le(struct brcmf_scan_results *list, struct brcmf_bss_info_le *bss) -{ - if (bss == NULL) - return list->bss_info_le; - return (struct brcmf_bss_info_le *)((unsigned long)bss + - le32_to_cpu(bss->length)); -} - -static s32 brcmf_inform_bss(struct brcmf_cfg80211_info *cfg) -{ - struct brcmf_scan_results *bss_list; - struct brcmf_bss_info_le *bi = NULL; /* must be initialized */ - s32 err = 0; - int i; - - bss_list = (struct brcmf_scan_results *)cfg->escan_info.escan_buf; - if (bss_list->count != 0 && - bss_list->version != BRCMF_BSS_INFO_VERSION) { - brcmf_err("Version %d != WL_BSS_INFO_VERSION\n", - bss_list->version); - return -EOPNOTSUPP; - } - brcmf_dbg(SCAN, "scanned AP count (%d)\n", bss_list->count); - for (i = 0; i < bss_list->count; i++) { - bi = next_bss_le(bss_list, bi); - err = brcmf_inform_single_bss(cfg, bi); - if (err) - break; - } - return err; -} - -static s32 wl_inform_ibss(struct brcmf_cfg80211_info *cfg, - struct net_device *ndev, const u8 *bssid) -{ - struct wiphy *wiphy = cfg_to_wiphy(cfg); - struct ieee80211_channel *notify_channel; - struct brcmf_bss_info_le *bi = NULL; - struct ieee80211_supported_band *band; - struct cfg80211_bss *bss; - struct brcmu_chan ch; - u8 *buf = NULL; - s32 err = 0; - u32 freq; - u16 notify_capability; - u16 notify_interval; - u8 *notify_ie; - size_t notify_ielen; - s32 notify_signal; - - brcmf_dbg(TRACE, "Enter\n"); - - buf = kzalloc(WL_BSS_INFO_MAX, GFP_KERNEL); - if (buf == NULL) { - err = -ENOMEM; - goto CleanUp; - } - - *(__le32 *)buf = cpu_to_le32(WL_BSS_INFO_MAX); - - err = brcmf_fil_cmd_data_get(netdev_priv(ndev), BRCMF_C_GET_BSS_INFO, - buf, WL_BSS_INFO_MAX); - if (err) { - brcmf_err("WLC_GET_BSS_INFO failed: %d\n", err); - goto CleanUp; - } - - bi = (struct brcmf_bss_info_le *)(buf + 4); - - ch.chspec = le16_to_cpu(bi->chanspec); - cfg->d11inf.decchspec(&ch); - - if (ch.band == BRCMU_CHAN_BAND_2G) - band = wiphy->bands[IEEE80211_BAND_2GHZ]; - else - band = wiphy->bands[IEEE80211_BAND_5GHZ]; - - freq = ieee80211_channel_to_frequency(ch.chnum, band->band); - notify_channel = ieee80211_get_channel(wiphy, freq); - - notify_capability = le16_to_cpu(bi->capability); - notify_interval = le16_to_cpu(bi->beacon_period); - notify_ie = (u8 *)bi + le16_to_cpu(bi->ie_offset); - notify_ielen = le32_to_cpu(bi->ie_length); - notify_signal = (s16)le16_to_cpu(bi->RSSI) * 100; - - brcmf_dbg(CONN, "channel: %d(%d)\n", ch.chnum, freq); - brcmf_dbg(CONN, "capability: %X\n", notify_capability); - brcmf_dbg(CONN, "beacon interval: %d\n", notify_interval); - brcmf_dbg(CONN, "signal: %d\n", notify_signal); - - bss = cfg80211_inform_bss(wiphy, notify_channel, - CFG80211_BSS_FTYPE_UNKNOWN, bssid, 0, - notify_capability, notify_interval, - notify_ie, notify_ielen, notify_signal, - GFP_KERNEL); - - if (!bss) { - err = -ENOMEM; - goto CleanUp; - } - - cfg80211_put_bss(wiphy, bss); - -CleanUp: - - kfree(buf); - - brcmf_dbg(TRACE, "Exit\n"); - - return err; -} - -static s32 brcmf_update_bss_info(struct brcmf_cfg80211_info *cfg, - struct brcmf_if *ifp) -{ - struct brcmf_cfg80211_profile *profile = ndev_to_prof(ifp->ndev); - struct brcmf_bss_info_le *bi; - struct brcmf_ssid *ssid; - const struct brcmf_tlv *tim; - u16 beacon_interval; - u8 dtim_period; - size_t ie_len; - u8 *ie; - s32 err = 0; - - brcmf_dbg(TRACE, "Enter\n"); - if (brcmf_is_ibssmode(ifp->vif)) - return err; - - ssid = &profile->ssid; - - *(__le32 *)cfg->extra_buf = cpu_to_le32(WL_EXTRA_BUF_MAX); - err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BSS_INFO, - cfg->extra_buf, WL_EXTRA_BUF_MAX); - if (err) { - brcmf_err("Could not get bss info %d\n", err); - goto update_bss_info_out; - } - - bi = (struct brcmf_bss_info_le *)(cfg->extra_buf + 4); - err = brcmf_inform_single_bss(cfg, bi); - if (err) - goto update_bss_info_out; - - ie = ((u8 *)bi) + le16_to_cpu(bi->ie_offset); - ie_len = le32_to_cpu(bi->ie_length); - beacon_interval = le16_to_cpu(bi->beacon_period); - - tim = brcmf_parse_tlvs(ie, ie_len, WLAN_EID_TIM); - if (tim) - dtim_period = tim->data[1]; - else { - /* - * active scan was done so we could not get dtim - * information out of probe response. - * so we speficially query dtim information to dongle. - */ - u32 var; - err = brcmf_fil_iovar_int_get(ifp, "dtim_assoc", &var); - if (err) { - brcmf_err("wl dtim_assoc failed (%d)\n", err); - goto update_bss_info_out; - } - dtim_period = (u8)var; - } - -update_bss_info_out: - brcmf_dbg(TRACE, "Exit"); - return err; -} - -void brcmf_abort_scanning(struct brcmf_cfg80211_info *cfg) -{ - struct escan_info *escan = &cfg->escan_info; - - set_bit(BRCMF_SCAN_STATUS_ABORT, &cfg->scan_status); - if (cfg->scan_request) { - escan->escan_state = WL_ESCAN_STATE_IDLE; - brcmf_notify_escan_complete(cfg, escan->ifp, true, true); - } - clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status); - clear_bit(BRCMF_SCAN_STATUS_ABORT, &cfg->scan_status); -} - -static void brcmf_cfg80211_escan_timeout_worker(struct work_struct *work) -{ - struct brcmf_cfg80211_info *cfg = - container_of(work, struct brcmf_cfg80211_info, - escan_timeout_work); - - brcmf_inform_bss(cfg); - brcmf_notify_escan_complete(cfg, cfg->escan_info.ifp, true, true); -} - -static void brcmf_escan_timeout(unsigned long data) -{ - struct brcmf_cfg80211_info *cfg = - (struct brcmf_cfg80211_info *)data; - - if (cfg->scan_request) { - brcmf_err("timer expired\n"); - schedule_work(&cfg->escan_timeout_work); - } -} - -static s32 -brcmf_compare_update_same_bss(struct brcmf_cfg80211_info *cfg, - struct brcmf_bss_info_le *bss, - struct brcmf_bss_info_le *bss_info_le) -{ - struct brcmu_chan ch_bss, ch_bss_info_le; - - ch_bss.chspec = le16_to_cpu(bss->chanspec); - cfg->d11inf.decchspec(&ch_bss); - ch_bss_info_le.chspec = le16_to_cpu(bss_info_le->chanspec); - cfg->d11inf.decchspec(&ch_bss_info_le); - - if (!memcmp(&bss_info_le->BSSID, &bss->BSSID, ETH_ALEN) && - ch_bss.band == ch_bss_info_le.band && - bss_info_le->SSID_len == bss->SSID_len && - !memcmp(bss_info_le->SSID, bss->SSID, bss_info_le->SSID_len)) { - if ((bss->flags & BRCMF_BSS_RSSI_ON_CHANNEL) == - (bss_info_le->flags & BRCMF_BSS_RSSI_ON_CHANNEL)) { - s16 bss_rssi = le16_to_cpu(bss->RSSI); - s16 bss_info_rssi = le16_to_cpu(bss_info_le->RSSI); - - /* preserve max RSSI if the measurements are - * both on-channel or both off-channel - */ - if (bss_info_rssi > bss_rssi) - bss->RSSI = bss_info_le->RSSI; - } else if ((bss->flags & BRCMF_BSS_RSSI_ON_CHANNEL) && - (bss_info_le->flags & BRCMF_BSS_RSSI_ON_CHANNEL) == 0) { - /* preserve the on-channel rssi measurement - * if the new measurement is off channel - */ - bss->RSSI = bss_info_le->RSSI; - bss->flags |= BRCMF_BSS_RSSI_ON_CHANNEL; - } - return 1; - } - return 0; -} - -static s32 -brcmf_cfg80211_escan_handler(struct brcmf_if *ifp, - const struct brcmf_event_msg *e, void *data) -{ - struct brcmf_cfg80211_info *cfg = ifp->drvr->config; - s32 status; - struct brcmf_escan_result_le *escan_result_le; - struct brcmf_bss_info_le *bss_info_le; - struct brcmf_bss_info_le *bss = NULL; - u32 bi_length; - struct brcmf_scan_results *list; - u32 i; - bool aborted; - - status = e->status; - - if (!test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) { - brcmf_err("scan not ready, bssidx=%d\n", ifp->bssidx); - return -EPERM; - } - - if (status == BRCMF_E_STATUS_PARTIAL) { - brcmf_dbg(SCAN, "ESCAN Partial result\n"); - escan_result_le = (struct brcmf_escan_result_le *) data; - if (!escan_result_le) { - brcmf_err("Invalid escan result (NULL pointer)\n"); - goto exit; - } - if (le16_to_cpu(escan_result_le->bss_count) != 1) { - brcmf_err("Invalid bss_count %d: ignoring\n", - escan_result_le->bss_count); - goto exit; - } - bss_info_le = &escan_result_le->bss_info_le; - - if (brcmf_p2p_scan_finding_common_channel(cfg, bss_info_le)) - goto exit; - - if (!cfg->scan_request) { - brcmf_dbg(SCAN, "result without cfg80211 request\n"); - goto exit; - } - - bi_length = le32_to_cpu(bss_info_le->length); - if (bi_length != (le32_to_cpu(escan_result_le->buflen) - - WL_ESCAN_RESULTS_FIXED_SIZE)) { - brcmf_err("Invalid bss_info length %d: ignoring\n", - bi_length); - goto exit; - } - - if (!(cfg_to_wiphy(cfg)->interface_modes & - BIT(NL80211_IFTYPE_ADHOC))) { - if (le16_to_cpu(bss_info_le->capability) & - WLAN_CAPABILITY_IBSS) { - brcmf_err("Ignoring IBSS result\n"); - goto exit; - } - } - - list = (struct brcmf_scan_results *) - cfg->escan_info.escan_buf; - if (bi_length > WL_ESCAN_BUF_SIZE - list->buflen) { - brcmf_err("Buffer is too small: ignoring\n"); - goto exit; - } - - for (i = 0; i < list->count; i++) { - bss = bss ? (struct brcmf_bss_info_le *) - ((unsigned char *)bss + - le32_to_cpu(bss->length)) : list->bss_info_le; - if (brcmf_compare_update_same_bss(cfg, bss, - bss_info_le)) - goto exit; - } - memcpy(&(cfg->escan_info.escan_buf[list->buflen]), - bss_info_le, bi_length); - list->version = le32_to_cpu(bss_info_le->version); - list->buflen += bi_length; - list->count++; - } else { - cfg->escan_info.escan_state = WL_ESCAN_STATE_IDLE; - if (brcmf_p2p_scan_finding_common_channel(cfg, NULL)) - goto exit; - if (cfg->scan_request) { - brcmf_inform_bss(cfg); - aborted = status != BRCMF_E_STATUS_SUCCESS; - brcmf_notify_escan_complete(cfg, ifp, aborted, false); - } else - brcmf_dbg(SCAN, "Ignored scan complete result 0x%x\n", - status); - } -exit: - return 0; -} - -static void brcmf_init_escan(struct brcmf_cfg80211_info *cfg) -{ - brcmf_fweh_register(cfg->pub, BRCMF_E_ESCAN_RESULT, - brcmf_cfg80211_escan_handler); - cfg->escan_info.escan_state = WL_ESCAN_STATE_IDLE; - /* Init scan_timeout timer */ - init_timer(&cfg->escan_timeout); - cfg->escan_timeout.data = (unsigned long) cfg; - cfg->escan_timeout.function = brcmf_escan_timeout; - INIT_WORK(&cfg->escan_timeout_work, - brcmf_cfg80211_escan_timeout_worker); -} - -static __always_inline void brcmf_delay(u32 ms) -{ - if (ms < 1000 / HZ) { - cond_resched(); - mdelay(ms); - } else { - msleep(ms); - } -} - -static s32 brcmf_config_wowl_pattern(struct brcmf_if *ifp, u8 cmd[4], - u8 *pattern, u32 patternsize, u8 *mask, - u32 packet_offset) -{ - struct brcmf_fil_wowl_pattern_le *filter; - u32 masksize; - u32 patternoffset; - u8 *buf; - u32 bufsize; - s32 ret; - - masksize = (patternsize + 7) / 8; - patternoffset = sizeof(*filter) - sizeof(filter->cmd) + masksize; - - bufsize = sizeof(*filter) + patternsize + masksize; - buf = kzalloc(bufsize, GFP_KERNEL); - if (!buf) - return -ENOMEM; - filter = (struct brcmf_fil_wowl_pattern_le *)buf; - - memcpy(filter->cmd, cmd, 4); - filter->masksize = cpu_to_le32(masksize); - filter->offset = cpu_to_le32(packet_offset); - filter->patternoffset = cpu_to_le32(patternoffset); - filter->patternsize = cpu_to_le32(patternsize); - filter->type = cpu_to_le32(BRCMF_WOWL_PATTERN_TYPE_BITMAP); - - if ((mask) && (masksize)) - memcpy(buf + sizeof(*filter), mask, masksize); - if ((pattern) && (patternsize)) - memcpy(buf + sizeof(*filter) + masksize, pattern, patternsize); - - ret = brcmf_fil_iovar_data_set(ifp, "wowl_pattern", buf, bufsize); - - kfree(buf); - return ret; -} - -static s32 brcmf_cfg80211_resume(struct wiphy *wiphy) -{ - struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); - struct net_device *ndev = cfg_to_ndev(cfg); - struct brcmf_if *ifp = netdev_priv(ndev); - - brcmf_dbg(TRACE, "Enter\n"); - - if (cfg->wowl_enabled) { - brcmf_configure_arp_offload(ifp, true); - brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM, - cfg->pre_wowl_pmmode); - brcmf_fil_iovar_int_set(ifp, "wowl_clear", 0); - brcmf_config_wowl_pattern(ifp, "clr", NULL, 0, NULL, 0); - cfg->wowl_enabled = false; - } - return 0; -} - -static void brcmf_configure_wowl(struct brcmf_cfg80211_info *cfg, - struct brcmf_if *ifp, - struct cfg80211_wowlan *wowl) -{ - u32 wowl_config; - u32 i; - - brcmf_dbg(TRACE, "Suspend, wowl config.\n"); - - brcmf_configure_arp_offload(ifp, false); - brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_PM, &cfg->pre_wowl_pmmode); - brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM, PM_MAX); - - wowl_config = 0; - if (wowl->disconnect) - wowl_config = BRCMF_WOWL_DIS | BRCMF_WOWL_BCN | BRCMF_WOWL_RETR; - if (wowl->magic_pkt) - wowl_config |= BRCMF_WOWL_MAGIC; - if ((wowl->patterns) && (wowl->n_patterns)) { - wowl_config |= BRCMF_WOWL_NET; - for (i = 0; i < wowl->n_patterns; i++) { - brcmf_config_wowl_pattern(ifp, "add", - (u8 *)wowl->patterns[i].pattern, - wowl->patterns[i].pattern_len, - (u8 *)wowl->patterns[i].mask, - wowl->patterns[i].pkt_offset); - } - } - brcmf_fil_iovar_int_set(ifp, "wowl", wowl_config); - brcmf_fil_iovar_int_set(ifp, "wowl_activate", 1); - brcmf_bus_wowl_config(cfg->pub->bus_if, true); - cfg->wowl_enabled = true; -} - -static s32 brcmf_cfg80211_suspend(struct wiphy *wiphy, - struct cfg80211_wowlan *wowl) -{ - struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); - struct net_device *ndev = cfg_to_ndev(cfg); - struct brcmf_if *ifp = netdev_priv(ndev); - struct brcmf_cfg80211_vif *vif; - - brcmf_dbg(TRACE, "Enter\n"); - - /* if the primary net_device is not READY there is nothing - * we can do but pray resume goes smoothly. - */ - if (!check_vif_up(ifp->vif)) - goto exit; - - /* end any scanning */ - if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) - brcmf_abort_scanning(cfg); - - if (wowl == NULL) { - brcmf_bus_wowl_config(cfg->pub->bus_if, false); - list_for_each_entry(vif, &cfg->vif_list, list) { - if (!test_bit(BRCMF_VIF_STATUS_READY, &vif->sme_state)) - continue; - /* While going to suspend if associated with AP - * disassociate from AP to save power while system is - * in suspended state - */ - brcmf_link_down(vif); - /* Make sure WPA_Supplicant receives all the event - * generated due to DISASSOC call to the fw to keep - * the state fw and WPA_Supplicant state consistent - */ - brcmf_delay(500); - } - /* Configure MPC */ - brcmf_set_mpc(ifp, 1); - - } else { - /* Configure WOWL paramaters */ - brcmf_configure_wowl(cfg, ifp, wowl); - } - -exit: - brcmf_dbg(TRACE, "Exit\n"); - /* clear any scanning activity */ - cfg->scan_status = 0; - return 0; -} - -static __used s32 -brcmf_update_pmklist(struct net_device *ndev, - struct brcmf_cfg80211_pmk_list *pmk_list, s32 err) -{ - int i, j; - int pmkid_len; - - pmkid_len = le32_to_cpu(pmk_list->pmkids.npmkid); - - brcmf_dbg(CONN, "No of elements %d\n", pmkid_len); - for (i = 0; i < pmkid_len; i++) { - brcmf_dbg(CONN, "PMKID[%d]: %pM =\n", i, - &pmk_list->pmkids.pmkid[i].BSSID); - for (j = 0; j < WLAN_PMKID_LEN; j++) - brcmf_dbg(CONN, "%02x\n", - pmk_list->pmkids.pmkid[i].PMKID[j]); - } - - if (!err) - brcmf_fil_iovar_data_set(netdev_priv(ndev), "pmkid_info", - (char *)pmk_list, sizeof(*pmk_list)); - - return err; -} - -static s32 -brcmf_cfg80211_set_pmksa(struct wiphy *wiphy, struct net_device *ndev, - struct cfg80211_pmksa *pmksa) -{ - struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); - struct brcmf_if *ifp = netdev_priv(ndev); - struct pmkid_list *pmkids = &cfg->pmk_list->pmkids; - s32 err = 0; - int i; - int pmkid_len; - - brcmf_dbg(TRACE, "Enter\n"); - if (!check_vif_up(ifp->vif)) - return -EIO; - - pmkid_len = le32_to_cpu(pmkids->npmkid); - for (i = 0; i < pmkid_len; i++) - if (!memcmp(pmksa->bssid, pmkids->pmkid[i].BSSID, ETH_ALEN)) - break; - if (i < WL_NUM_PMKIDS_MAX) { - memcpy(pmkids->pmkid[i].BSSID, pmksa->bssid, ETH_ALEN); - memcpy(pmkids->pmkid[i].PMKID, pmksa->pmkid, WLAN_PMKID_LEN); - if (i == pmkid_len) { - pmkid_len++; - pmkids->npmkid = cpu_to_le32(pmkid_len); - } - } else - err = -EINVAL; - - brcmf_dbg(CONN, "set_pmksa,IW_PMKSA_ADD - PMKID: %pM =\n", - pmkids->pmkid[pmkid_len].BSSID); - for (i = 0; i < WLAN_PMKID_LEN; i++) - brcmf_dbg(CONN, "%02x\n", pmkids->pmkid[pmkid_len].PMKID[i]); - - err = brcmf_update_pmklist(ndev, cfg->pmk_list, err); - - brcmf_dbg(TRACE, "Exit\n"); - return err; -} - -static s32 -brcmf_cfg80211_del_pmksa(struct wiphy *wiphy, struct net_device *ndev, - struct cfg80211_pmksa *pmksa) -{ - struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); - struct brcmf_if *ifp = netdev_priv(ndev); - struct pmkid_list pmkid; - s32 err = 0; - int i, pmkid_len; - - brcmf_dbg(TRACE, "Enter\n"); - if (!check_vif_up(ifp->vif)) - return -EIO; - - memcpy(&pmkid.pmkid[0].BSSID, pmksa->bssid, ETH_ALEN); - memcpy(&pmkid.pmkid[0].PMKID, pmksa->pmkid, WLAN_PMKID_LEN); - - brcmf_dbg(CONN, "del_pmksa,IW_PMKSA_REMOVE - PMKID: %pM =\n", - &pmkid.pmkid[0].BSSID); - for (i = 0; i < WLAN_PMKID_LEN; i++) - brcmf_dbg(CONN, "%02x\n", pmkid.pmkid[0].PMKID[i]); - - pmkid_len = le32_to_cpu(cfg->pmk_list->pmkids.npmkid); - for (i = 0; i < pmkid_len; i++) - if (!memcmp - (pmksa->bssid, &cfg->pmk_list->pmkids.pmkid[i].BSSID, - ETH_ALEN)) - break; - - if ((pmkid_len > 0) - && (i < pmkid_len)) { - memset(&cfg->pmk_list->pmkids.pmkid[i], 0, - sizeof(struct pmkid)); - for (; i < (pmkid_len - 1); i++) { - memcpy(&cfg->pmk_list->pmkids.pmkid[i].BSSID, - &cfg->pmk_list->pmkids.pmkid[i + 1].BSSID, - ETH_ALEN); - memcpy(&cfg->pmk_list->pmkids.pmkid[i].PMKID, - &cfg->pmk_list->pmkids.pmkid[i + 1].PMKID, - WLAN_PMKID_LEN); - } - cfg->pmk_list->pmkids.npmkid = cpu_to_le32(pmkid_len - 1); - } else - err = -EINVAL; - - err = brcmf_update_pmklist(ndev, cfg->pmk_list, err); - - brcmf_dbg(TRACE, "Exit\n"); - return err; - -} - -static s32 -brcmf_cfg80211_flush_pmksa(struct wiphy *wiphy, struct net_device *ndev) -{ - struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); - struct brcmf_if *ifp = netdev_priv(ndev); - s32 err = 0; - - brcmf_dbg(TRACE, "Enter\n"); - if (!check_vif_up(ifp->vif)) - return -EIO; - - memset(cfg->pmk_list, 0, sizeof(*cfg->pmk_list)); - err = brcmf_update_pmklist(ndev, cfg->pmk_list, err); - - brcmf_dbg(TRACE, "Exit\n"); - return err; - -} - -/* - * PFN result doesn't have all the info which are - * required by the supplicant - * (For e.g IEs) Do a target Escan so that sched scan results are reported - * via wl_inform_single_bss in the required format. Escan does require the - * scan request in the form of cfg80211_scan_request. For timebeing, create - * cfg80211_scan_request one out of the received PNO event. - */ -static s32 -brcmf_notify_sched_scan_results(struct brcmf_if *ifp, - const struct brcmf_event_msg *e, void *data) -{ - struct brcmf_cfg80211_info *cfg = ifp->drvr->config; - struct brcmf_pno_net_info_le *netinfo, *netinfo_start; - struct cfg80211_scan_request *request = NULL; - struct cfg80211_ssid *ssid = NULL; - struct ieee80211_channel *channel = NULL; - struct wiphy *wiphy = cfg_to_wiphy(cfg); - int err = 0; - int channel_req = 0; - int band = 0; - struct brcmf_pno_scanresults_le *pfn_result; - u32 result_count; - u32 status; - - brcmf_dbg(SCAN, "Enter\n"); - - if (e->event_code == BRCMF_E_PFN_NET_LOST) { - brcmf_dbg(SCAN, "PFN NET LOST event. Do Nothing\n"); - return 0; - } - - pfn_result = (struct brcmf_pno_scanresults_le *)data; - result_count = le32_to_cpu(pfn_result->count); - status = le32_to_cpu(pfn_result->status); - - /* - * PFN event is limited to fit 512 bytes so we may get - * multiple NET_FOUND events. For now place a warning here. - */ - WARN_ON(status != BRCMF_PNO_SCAN_COMPLETE); - brcmf_dbg(SCAN, "PFN NET FOUND event. count: %d\n", result_count); - if (result_count > 0) { - int i; - - request = kzalloc(sizeof(*request), GFP_KERNEL); - ssid = kcalloc(result_count, sizeof(*ssid), GFP_KERNEL); - channel = kcalloc(result_count, sizeof(*channel), GFP_KERNEL); - if (!request || !ssid || !channel) { - err = -ENOMEM; - goto out_err; - } - - request->wiphy = wiphy; - data += sizeof(struct brcmf_pno_scanresults_le); - netinfo_start = (struct brcmf_pno_net_info_le *)data; - - for (i = 0; i < result_count; i++) { - netinfo = &netinfo_start[i]; - if (!netinfo) { - brcmf_err("Invalid netinfo ptr. index: %d\n", - i); - err = -EINVAL; - goto out_err; - } - - brcmf_dbg(SCAN, "SSID:%s Channel:%d\n", - netinfo->SSID, netinfo->channel); - memcpy(ssid[i].ssid, netinfo->SSID, netinfo->SSID_len); - ssid[i].ssid_len = netinfo->SSID_len; - request->n_ssids++; - - channel_req = netinfo->channel; - if (channel_req <= CH_MAX_2G_CHANNEL) - band = NL80211_BAND_2GHZ; - else - band = NL80211_BAND_5GHZ; - channel[i].center_freq = - ieee80211_channel_to_frequency(channel_req, - band); - channel[i].band = band; - channel[i].flags |= IEEE80211_CHAN_NO_HT40; - request->channels[i] = &channel[i]; - request->n_channels++; - } - - /* assign parsed ssid array */ - if (request->n_ssids) - request->ssids = &ssid[0]; - - if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) { - /* Abort any on-going scan */ - brcmf_abort_scanning(cfg); - } - - set_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status); - cfg->escan_info.run = brcmf_run_escan; - err = brcmf_do_escan(cfg, wiphy, ifp, request); - if (err) { - clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status); - goto out_err; - } - cfg->sched_escan = true; - cfg->scan_request = request; - } else { - brcmf_err("FALSE PNO Event. (pfn_count == 0)\n"); - goto out_err; - } - - kfree(ssid); - kfree(channel); - kfree(request); - return 0; - -out_err: - kfree(ssid); - kfree(channel); - kfree(request); - cfg80211_sched_scan_stopped(wiphy); - return err; -} - -static int brcmf_dev_pno_clean(struct net_device *ndev) -{ - int ret; - - /* Disable pfn */ - ret = brcmf_fil_iovar_int_set(netdev_priv(ndev), "pfn", 0); - if (ret == 0) { - /* clear pfn */ - ret = brcmf_fil_iovar_data_set(netdev_priv(ndev), "pfnclear", - NULL, 0); - } - if (ret < 0) - brcmf_err("failed code %d\n", ret); - - return ret; -} - -static int brcmf_dev_pno_config(struct net_device *ndev) -{ - struct brcmf_pno_param_le pfn_param; - - memset(&pfn_param, 0, sizeof(pfn_param)); - pfn_param.version = cpu_to_le32(BRCMF_PNO_VERSION); - - /* set extra pno params */ - pfn_param.flags = cpu_to_le16(1 << BRCMF_PNO_ENABLE_ADAPTSCAN_BIT); - pfn_param.repeat = BRCMF_PNO_REPEAT; - pfn_param.exp = BRCMF_PNO_FREQ_EXPO_MAX; - - /* set up pno scan fr */ - pfn_param.scan_freq = cpu_to_le32(BRCMF_PNO_TIME); - - return brcmf_fil_iovar_data_set(netdev_priv(ndev), "pfn_set", - &pfn_param, sizeof(pfn_param)); -} - -static int -brcmf_cfg80211_sched_scan_start(struct wiphy *wiphy, - struct net_device *ndev, - struct cfg80211_sched_scan_request *request) -{ - struct brcmf_if *ifp = netdev_priv(ndev); - struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy); - struct brcmf_pno_net_param_le pfn; - int i; - int ret = 0; - - brcmf_dbg(SCAN, "Enter n_match_sets:%d n_ssids:%d\n", - request->n_match_sets, request->n_ssids); - if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) { - brcmf_err("Scanning already: status (%lu)\n", cfg->scan_status); - return -EAGAIN; - } - if (test_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status)) { - brcmf_err("Scanning suppressed: status (%lu)\n", - cfg->scan_status); - return -EAGAIN; - } - - if (!request->n_ssids || !request->n_match_sets) { - brcmf_dbg(SCAN, "Invalid sched scan req!! n_ssids:%d\n", - request->n_ssids); - return -EINVAL; - } - - if (request->n_ssids > 0) { - for (i = 0; i < request->n_ssids; i++) { - /* Active scan req for ssids */ - brcmf_dbg(SCAN, ">>> Active scan req for ssid (%s)\n", - request->ssids[i].ssid); - - /* - * match_set ssids is a supert set of n_ssid list, - * so we need not add these set seperately. - */ - } - } - - if (request->n_match_sets > 0) { - /* clean up everything */ - ret = brcmf_dev_pno_clean(ndev); - if (ret < 0) { - brcmf_err("failed error=%d\n", ret); - return ret; - } - - /* configure pno */ - ret = brcmf_dev_pno_config(ndev); - if (ret < 0) { - brcmf_err("PNO setup failed!! ret=%d\n", ret); - return -EINVAL; - } - - /* configure each match set */ - for (i = 0; i < request->n_match_sets; i++) { - struct cfg80211_ssid *ssid; - u32 ssid_len; - - ssid = &request->match_sets[i].ssid; - ssid_len = ssid->ssid_len; - - if (!ssid_len) { - brcmf_err("skip broadcast ssid\n"); - continue; - } - pfn.auth = cpu_to_le32(WLAN_AUTH_OPEN); - pfn.wpa_auth = cpu_to_le32(BRCMF_PNO_WPA_AUTH_ANY); - pfn.wsec = cpu_to_le32(0); - pfn.infra = cpu_to_le32(1); - pfn.flags = cpu_to_le32(1 << BRCMF_PNO_HIDDEN_BIT); - pfn.ssid.SSID_len = cpu_to_le32(ssid_len); - memcpy(pfn.ssid.SSID, ssid->ssid, ssid_len); - ret = brcmf_fil_iovar_data_set(ifp, "pfn_add", &pfn, - sizeof(pfn)); - brcmf_dbg(SCAN, ">>> PNO filter %s for ssid (%s)\n", - ret == 0 ? "set" : "failed", ssid->ssid); - } - /* Enable the PNO */ - if (brcmf_fil_iovar_int_set(ifp, "pfn", 1) < 0) { - brcmf_err("PNO enable failed!! ret=%d\n", ret); - return -EINVAL; - } - } else { - return -EINVAL; - } - - return 0; -} - -static int brcmf_cfg80211_sched_scan_stop(struct wiphy *wiphy, - struct net_device *ndev) -{ - struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); - - brcmf_dbg(SCAN, "enter\n"); - brcmf_dev_pno_clean(ndev); - if (cfg->sched_escan) - brcmf_notify_escan_complete(cfg, netdev_priv(ndev), true, true); - return 0; -} - -static s32 brcmf_configure_opensecurity(struct brcmf_if *ifp) -{ - s32 err; - - /* set auth */ - err = brcmf_fil_bsscfg_int_set(ifp, "auth", 0); - if (err < 0) { - brcmf_err("auth error %d\n", err); - return err; - } - /* set wsec */ - err = brcmf_fil_bsscfg_int_set(ifp, "wsec", 0); - if (err < 0) { - brcmf_err("wsec error %d\n", err); - return err; - } - /* set upper-layer auth */ - err = brcmf_fil_bsscfg_int_set(ifp, "wpa_auth", WPA_AUTH_NONE); - if (err < 0) { - brcmf_err("wpa_auth error %d\n", err); - return err; - } - - return 0; -} - -static bool brcmf_valid_wpa_oui(u8 *oui, bool is_rsn_ie) -{ - if (is_rsn_ie) - return (memcmp(oui, RSN_OUI, TLV_OUI_LEN) == 0); - - return (memcmp(oui, WPA_OUI, TLV_OUI_LEN) == 0); -} - -static s32 -brcmf_configure_wpaie(struct net_device *ndev, - const struct brcmf_vs_tlv *wpa_ie, - bool is_rsn_ie) -{ - struct brcmf_if *ifp = netdev_priv(ndev); - u32 auth = 0; /* d11 open authentication */ - u16 count; - s32 err = 0; - s32 len = 0; - u32 i; - u32 wsec; - u32 pval = 0; - u32 gval = 0; - u32 wpa_auth = 0; - u32 offset; - u8 *data; - u16 rsn_cap; - u32 wme_bss_disable; - - brcmf_dbg(TRACE, "Enter\n"); - if (wpa_ie == NULL) - goto exit; - - len = wpa_ie->len + TLV_HDR_LEN; - data = (u8 *)wpa_ie; - offset = TLV_HDR_LEN; - if (!is_rsn_ie) - offset += VS_IE_FIXED_HDR_LEN; - else - offset += WPA_IE_VERSION_LEN; - - /* check for multicast cipher suite */ - if (offset + WPA_IE_MIN_OUI_LEN > len) { - err = -EINVAL; - brcmf_err("no multicast cipher suite\n"); - goto exit; - } - - if (!brcmf_valid_wpa_oui(&data[offset], is_rsn_ie)) { - err = -EINVAL; - brcmf_err("ivalid OUI\n"); - goto exit; - } - offset += TLV_OUI_LEN; - - /* pick up multicast cipher */ - switch (data[offset]) { - case WPA_CIPHER_NONE: - gval = 0; - break; - case WPA_CIPHER_WEP_40: - case WPA_CIPHER_WEP_104: - gval = WEP_ENABLED; - break; - case WPA_CIPHER_TKIP: - gval = TKIP_ENABLED; - break; - case WPA_CIPHER_AES_CCM: - gval = AES_ENABLED; - break; - default: - err = -EINVAL; - brcmf_err("Invalid multi cast cipher info\n"); - goto exit; - } - - offset++; - /* walk thru unicast cipher list and pick up what we recognize */ - count = data[offset] + (data[offset + 1] << 8); - offset += WPA_IE_SUITE_COUNT_LEN; - /* Check for unicast suite(s) */ - if (offset + (WPA_IE_MIN_OUI_LEN * count) > len) { - err = -EINVAL; - brcmf_err("no unicast cipher suite\n"); - goto exit; - } - for (i = 0; i < count; i++) { - if (!brcmf_valid_wpa_oui(&data[offset], is_rsn_ie)) { - err = -EINVAL; - brcmf_err("ivalid OUI\n"); - goto exit; - } - offset += TLV_OUI_LEN; - switch (data[offset]) { - case WPA_CIPHER_NONE: - break; - case WPA_CIPHER_WEP_40: - case WPA_CIPHER_WEP_104: - pval |= WEP_ENABLED; - break; - case WPA_CIPHER_TKIP: - pval |= TKIP_ENABLED; - break; - case WPA_CIPHER_AES_CCM: - pval |= AES_ENABLED; - break; - default: - brcmf_err("Ivalid unicast security info\n"); - } - offset++; - } - /* walk thru auth management suite list and pick up what we recognize */ - count = data[offset] + (data[offset + 1] << 8); - offset += WPA_IE_SUITE_COUNT_LEN; - /* Check for auth key management suite(s) */ - if (offset + (WPA_IE_MIN_OUI_LEN * count) > len) { - err = -EINVAL; - brcmf_err("no auth key mgmt suite\n"); - goto exit; - } - for (i = 0; i < count; i++) { - if (!brcmf_valid_wpa_oui(&data[offset], is_rsn_ie)) { - err = -EINVAL; - brcmf_err("ivalid OUI\n"); - goto exit; - } - offset += TLV_OUI_LEN; - switch (data[offset]) { - case RSN_AKM_NONE: - brcmf_dbg(TRACE, "RSN_AKM_NONE\n"); - wpa_auth |= WPA_AUTH_NONE; - break; - case RSN_AKM_UNSPECIFIED: - brcmf_dbg(TRACE, "RSN_AKM_UNSPECIFIED\n"); - is_rsn_ie ? (wpa_auth |= WPA2_AUTH_UNSPECIFIED) : - (wpa_auth |= WPA_AUTH_UNSPECIFIED); - break; - case RSN_AKM_PSK: - brcmf_dbg(TRACE, "RSN_AKM_PSK\n"); - is_rsn_ie ? (wpa_auth |= WPA2_AUTH_PSK) : - (wpa_auth |= WPA_AUTH_PSK); - break; - default: - brcmf_err("Ivalid key mgmt info\n"); - } - offset++; - } - - if (is_rsn_ie) { - wme_bss_disable = 1; - if ((offset + RSN_CAP_LEN) <= len) { - rsn_cap = data[offset] + (data[offset + 1] << 8); - if (rsn_cap & RSN_CAP_PTK_REPLAY_CNTR_MASK) - wme_bss_disable = 0; - } - /* set wme_bss_disable to sync RSN Capabilities */ - err = brcmf_fil_bsscfg_int_set(ifp, "wme_bss_disable", - wme_bss_disable); - if (err < 0) { - brcmf_err("wme_bss_disable error %d\n", err); - goto exit; - } - } - /* FOR WPS , set SES_OW_ENABLED */ - wsec = (pval | gval | SES_OW_ENABLED); - - /* set auth */ - err = brcmf_fil_bsscfg_int_set(ifp, "auth", auth); - if (err < 0) { - brcmf_err("auth error %d\n", err); - goto exit; - } - /* set wsec */ - err = brcmf_fil_bsscfg_int_set(ifp, "wsec", wsec); - if (err < 0) { - brcmf_err("wsec error %d\n", err); - goto exit; - } - /* set upper-layer auth */ - err = brcmf_fil_bsscfg_int_set(ifp, "wpa_auth", wpa_auth); - if (err < 0) { - brcmf_err("wpa_auth error %d\n", err); - goto exit; - } - -exit: - return err; -} - -static s32 -brcmf_parse_vndr_ies(const u8 *vndr_ie_buf, u32 vndr_ie_len, - struct parsed_vndr_ies *vndr_ies) -{ - struct brcmf_vs_tlv *vndrie; - struct brcmf_tlv *ie; - struct parsed_vndr_ie_info *parsed_info; - s32 remaining_len; - - remaining_len = (s32)vndr_ie_len; - memset(vndr_ies, 0, sizeof(*vndr_ies)); - - ie = (struct brcmf_tlv *)vndr_ie_buf; - while (ie) { - if (ie->id != WLAN_EID_VENDOR_SPECIFIC) - goto next; - vndrie = (struct brcmf_vs_tlv *)ie; - /* len should be bigger than OUI length + one */ - if (vndrie->len < (VS_IE_FIXED_HDR_LEN - TLV_HDR_LEN + 1)) { - brcmf_err("invalid vndr ie. length is too small %d\n", - vndrie->len); - goto next; - } - /* if wpa or wme ie, do not add ie */ - if (!memcmp(vndrie->oui, (u8 *)WPA_OUI, TLV_OUI_LEN) && - ((vndrie->oui_type == WPA_OUI_TYPE) || - (vndrie->oui_type == WME_OUI_TYPE))) { - brcmf_dbg(TRACE, "Found WPA/WME oui. Do not add it\n"); - goto next; - } - - parsed_info = &vndr_ies->ie_info[vndr_ies->count]; - - /* save vndr ie information */ - parsed_info->ie_ptr = (char *)vndrie; - parsed_info->ie_len = vndrie->len + TLV_HDR_LEN; - memcpy(&parsed_info->vndrie, vndrie, sizeof(*vndrie)); - - vndr_ies->count++; - - brcmf_dbg(TRACE, "** OUI %02x %02x %02x, type 0x%02x\n", - parsed_info->vndrie.oui[0], - parsed_info->vndrie.oui[1], - parsed_info->vndrie.oui[2], - parsed_info->vndrie.oui_type); - - if (vndr_ies->count >= VNDR_IE_PARSE_LIMIT) - break; -next: - remaining_len -= (ie->len + TLV_HDR_LEN); - if (remaining_len <= TLV_HDR_LEN) - ie = NULL; - else - ie = (struct brcmf_tlv *)(((u8 *)ie) + ie->len + - TLV_HDR_LEN); - } - return 0; -} - -static u32 -brcmf_vndr_ie(u8 *iebuf, s32 pktflag, u8 *ie_ptr, u32 ie_len, s8 *add_del_cmd) -{ - - __le32 iecount_le; - __le32 pktflag_le; - - strncpy(iebuf, add_del_cmd, VNDR_IE_CMD_LEN - 1); - iebuf[VNDR_IE_CMD_LEN - 1] = '\0'; - - iecount_le = cpu_to_le32(1); - memcpy(&iebuf[VNDR_IE_COUNT_OFFSET], &iecount_le, sizeof(iecount_le)); - - pktflag_le = cpu_to_le32(pktflag); - memcpy(&iebuf[VNDR_IE_PKTFLAG_OFFSET], &pktflag_le, sizeof(pktflag_le)); - - memcpy(&iebuf[VNDR_IE_VSIE_OFFSET], ie_ptr, ie_len); - - return ie_len + VNDR_IE_HDR_SIZE; -} - -s32 brcmf_vif_set_mgmt_ie(struct brcmf_cfg80211_vif *vif, s32 pktflag, - const u8 *vndr_ie_buf, u32 vndr_ie_len) -{ - struct brcmf_if *ifp; - struct vif_saved_ie *saved_ie; - s32 err = 0; - u8 *iovar_ie_buf; - u8 *curr_ie_buf; - u8 *mgmt_ie_buf = NULL; - int mgmt_ie_buf_len; - u32 *mgmt_ie_len; - u32 del_add_ie_buf_len = 0; - u32 total_ie_buf_len = 0; - u32 parsed_ie_buf_len = 0; - struct parsed_vndr_ies old_vndr_ies; - struct parsed_vndr_ies new_vndr_ies; - struct parsed_vndr_ie_info *vndrie_info; - s32 i; - u8 *ptr; - int remained_buf_len; - - if (!vif) - return -ENODEV; - ifp = vif->ifp; - saved_ie = &vif->saved_ie; - - brcmf_dbg(TRACE, "bssidx %d, pktflag : 0x%02X\n", ifp->bssidx, pktflag); - iovar_ie_buf = kzalloc(WL_EXTRA_BUF_MAX, GFP_KERNEL); - if (!iovar_ie_buf) - return -ENOMEM; - curr_ie_buf = iovar_ie_buf; - switch (pktflag) { - case BRCMF_VNDR_IE_PRBREQ_FLAG: - mgmt_ie_buf = saved_ie->probe_req_ie; - mgmt_ie_len = &saved_ie->probe_req_ie_len; - mgmt_ie_buf_len = sizeof(saved_ie->probe_req_ie); - break; - case BRCMF_VNDR_IE_PRBRSP_FLAG: - mgmt_ie_buf = saved_ie->probe_res_ie; - mgmt_ie_len = &saved_ie->probe_res_ie_len; - mgmt_ie_buf_len = sizeof(saved_ie->probe_res_ie); - break; - case BRCMF_VNDR_IE_BEACON_FLAG: - mgmt_ie_buf = saved_ie->beacon_ie; - mgmt_ie_len = &saved_ie->beacon_ie_len; - mgmt_ie_buf_len = sizeof(saved_ie->beacon_ie); - break; - case BRCMF_VNDR_IE_ASSOCREQ_FLAG: - mgmt_ie_buf = saved_ie->assoc_req_ie; - mgmt_ie_len = &saved_ie->assoc_req_ie_len; - mgmt_ie_buf_len = sizeof(saved_ie->assoc_req_ie); - break; - default: - err = -EPERM; - brcmf_err("not suitable type\n"); - goto exit; - } - - if (vndr_ie_len > mgmt_ie_buf_len) { - err = -ENOMEM; - brcmf_err("extra IE size too big\n"); - goto exit; - } - - /* parse and save new vndr_ie in curr_ie_buff before comparing it */ - if (vndr_ie_buf && vndr_ie_len && curr_ie_buf) { - ptr = curr_ie_buf; - brcmf_parse_vndr_ies(vndr_ie_buf, vndr_ie_len, &new_vndr_ies); - for (i = 0; i < new_vndr_ies.count; i++) { - vndrie_info = &new_vndr_ies.ie_info[i]; - memcpy(ptr + parsed_ie_buf_len, vndrie_info->ie_ptr, - vndrie_info->ie_len); - parsed_ie_buf_len += vndrie_info->ie_len; - } - } - - if (mgmt_ie_buf && *mgmt_ie_len) { - if (parsed_ie_buf_len && (parsed_ie_buf_len == *mgmt_ie_len) && - (memcmp(mgmt_ie_buf, curr_ie_buf, - parsed_ie_buf_len) == 0)) { - brcmf_dbg(TRACE, "Previous mgmt IE equals to current IE\n"); - goto exit; - } - - /* parse old vndr_ie */ - brcmf_parse_vndr_ies(mgmt_ie_buf, *mgmt_ie_len, &old_vndr_ies); - - /* make a command to delete old ie */ - for (i = 0; i < old_vndr_ies.count; i++) { - vndrie_info = &old_vndr_ies.ie_info[i]; - - brcmf_dbg(TRACE, "DEL ID : %d, Len: %d , OUI:%02x:%02x:%02x\n", - vndrie_info->vndrie.id, - vndrie_info->vndrie.len, - vndrie_info->vndrie.oui[0], - vndrie_info->vndrie.oui[1], - vndrie_info->vndrie.oui[2]); - - del_add_ie_buf_len = brcmf_vndr_ie(curr_ie_buf, pktflag, - vndrie_info->ie_ptr, - vndrie_info->ie_len, - "del"); - curr_ie_buf += del_add_ie_buf_len; - total_ie_buf_len += del_add_ie_buf_len; - } - } - - *mgmt_ie_len = 0; - /* Add if there is any extra IE */ - if (mgmt_ie_buf && parsed_ie_buf_len) { - ptr = mgmt_ie_buf; - - remained_buf_len = mgmt_ie_buf_len; - - /* make a command to add new ie */ - for (i = 0; i < new_vndr_ies.count; i++) { - vndrie_info = &new_vndr_ies.ie_info[i]; - - /* verify remained buf size before copy data */ - if (remained_buf_len < (vndrie_info->vndrie.len + - VNDR_IE_VSIE_OFFSET)) { - brcmf_err("no space in mgmt_ie_buf: len left %d", - remained_buf_len); - break; - } - remained_buf_len -= (vndrie_info->ie_len + - VNDR_IE_VSIE_OFFSET); - - brcmf_dbg(TRACE, "ADDED ID : %d, Len: %d, OUI:%02x:%02x:%02x\n", - vndrie_info->vndrie.id, - vndrie_info->vndrie.len, - vndrie_info->vndrie.oui[0], - vndrie_info->vndrie.oui[1], - vndrie_info->vndrie.oui[2]); - - del_add_ie_buf_len = brcmf_vndr_ie(curr_ie_buf, pktflag, - vndrie_info->ie_ptr, - vndrie_info->ie_len, - "add"); - - /* save the parsed IE in wl struct */ - memcpy(ptr + (*mgmt_ie_len), vndrie_info->ie_ptr, - vndrie_info->ie_len); - *mgmt_ie_len += vndrie_info->ie_len; - - curr_ie_buf += del_add_ie_buf_len; - total_ie_buf_len += del_add_ie_buf_len; - } - } - if (total_ie_buf_len) { - err = brcmf_fil_bsscfg_data_set(ifp, "vndr_ie", iovar_ie_buf, - total_ie_buf_len); - if (err) - brcmf_err("vndr ie set error : %d\n", err); - } - -exit: - kfree(iovar_ie_buf); - return err; -} - -s32 brcmf_vif_clear_mgmt_ies(struct brcmf_cfg80211_vif *vif) -{ - s32 pktflags[] = { - BRCMF_VNDR_IE_PRBREQ_FLAG, - BRCMF_VNDR_IE_PRBRSP_FLAG, - BRCMF_VNDR_IE_BEACON_FLAG - }; - int i; - - for (i = 0; i < ARRAY_SIZE(pktflags); i++) - brcmf_vif_set_mgmt_ie(vif, pktflags[i], NULL, 0); - - memset(&vif->saved_ie, 0, sizeof(vif->saved_ie)); - return 0; -} - -static s32 -brcmf_config_ap_mgmt_ie(struct brcmf_cfg80211_vif *vif, - struct cfg80211_beacon_data *beacon) -{ - s32 err; - - /* Set Beacon IEs to FW */ - err = brcmf_vif_set_mgmt_ie(vif, BRCMF_VNDR_IE_BEACON_FLAG, - beacon->tail, beacon->tail_len); - if (err) { - brcmf_err("Set Beacon IE Failed\n"); - return err; - } - brcmf_dbg(TRACE, "Applied Vndr IEs for Beacon\n"); - - /* Set Probe Response IEs to FW */ - err = brcmf_vif_set_mgmt_ie(vif, BRCMF_VNDR_IE_PRBRSP_FLAG, - beacon->proberesp_ies, - beacon->proberesp_ies_len); - if (err) - brcmf_err("Set Probe Resp IE Failed\n"); - else - brcmf_dbg(TRACE, "Applied Vndr IEs for Probe Resp\n"); - - return err; -} - -static s32 -brcmf_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *ndev, - struct cfg80211_ap_settings *settings) -{ - s32 ie_offset; - struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); - struct brcmf_if *ifp = netdev_priv(ndev); - const struct brcmf_tlv *ssid_ie; - struct brcmf_ssid_le ssid_le; - s32 err = -EPERM; - const struct brcmf_tlv *rsn_ie; - const struct brcmf_vs_tlv *wpa_ie; - struct brcmf_join_params join_params; - enum nl80211_iftype dev_role; - struct brcmf_fil_bss_enable_le bss_enable; - u16 chanspec; - - brcmf_dbg(TRACE, "ctrlchn=%d, center=%d, bw=%d, beacon_interval=%d, dtim_period=%d,\n", - settings->chandef.chan->hw_value, - settings->chandef.center_freq1, settings->chandef.width, - settings->beacon_interval, settings->dtim_period); - brcmf_dbg(TRACE, "ssid=%s(%zu), auth_type=%d, inactivity_timeout=%d\n", - settings->ssid, settings->ssid_len, settings->auth_type, - settings->inactivity_timeout); - - dev_role = ifp->vif->wdev.iftype; - - memset(&ssid_le, 0, sizeof(ssid_le)); - if (settings->ssid == NULL || settings->ssid_len == 0) { - ie_offset = DOT11_MGMT_HDR_LEN + DOT11_BCN_PRB_FIXED_LEN; - ssid_ie = brcmf_parse_tlvs( - (u8 *)&settings->beacon.head[ie_offset], - settings->beacon.head_len - ie_offset, - WLAN_EID_SSID); - if (!ssid_ie) - return -EINVAL; - - memcpy(ssid_le.SSID, ssid_ie->data, ssid_ie->len); - ssid_le.SSID_len = cpu_to_le32(ssid_ie->len); - brcmf_dbg(TRACE, "SSID is (%s) in Head\n", ssid_le.SSID); - } else { - memcpy(ssid_le.SSID, settings->ssid, settings->ssid_len); - ssid_le.SSID_len = cpu_to_le32((u32)settings->ssid_len); - } - - brcmf_set_mpc(ifp, 0); - brcmf_configure_arp_offload(ifp, false); - - /* find the RSN_IE */ - rsn_ie = brcmf_parse_tlvs((u8 *)settings->beacon.tail, - settings->beacon.tail_len, WLAN_EID_RSN); - - /* find the WPA_IE */ - wpa_ie = brcmf_find_wpaie((u8 *)settings->beacon.tail, - settings->beacon.tail_len); - - if ((wpa_ie != NULL || rsn_ie != NULL)) { - brcmf_dbg(TRACE, "WPA(2) IE is found\n"); - if (wpa_ie != NULL) { - /* WPA IE */ - err = brcmf_configure_wpaie(ndev, wpa_ie, false); - if (err < 0) - goto exit; - } else { - /* RSN IE */ - err = brcmf_configure_wpaie(ndev, - (struct brcmf_vs_tlv *)rsn_ie, true); - if (err < 0) - goto exit; - } - } else { - brcmf_dbg(TRACE, "No WPA(2) IEs found\n"); - brcmf_configure_opensecurity(ifp); - } - - brcmf_config_ap_mgmt_ie(ifp->vif, &settings->beacon); - - chanspec = chandef_to_chanspec(&cfg->d11inf, &settings->chandef); - err = brcmf_fil_iovar_int_set(ifp, "chanspec", chanspec); - if (err < 0) { - brcmf_err("Set Channel failed: chspec=%d, %d\n", chanspec, err); - goto exit; - } - - if (settings->beacon_interval) { - err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_BCNPRD, - settings->beacon_interval); - if (err < 0) { - brcmf_err("Beacon Interval Set Error, %d\n", err); - goto exit; - } - } - if (settings->dtim_period) { - err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_DTIMPRD, - settings->dtim_period); - if (err < 0) { - brcmf_err("DTIM Interval Set Error, %d\n", err); - goto exit; - } - } - - if (dev_role == NL80211_IFTYPE_AP) { - err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_DOWN, 1); - if (err < 0) { - brcmf_err("BRCMF_C_DOWN error %d\n", err); - goto exit; - } - brcmf_fil_iovar_int_set(ifp, "apsta", 0); - } - - err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_INFRA, 1); - if (err < 0) { - brcmf_err("SET INFRA error %d\n", err); - goto exit; - } - if (dev_role == NL80211_IFTYPE_AP) { - err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_AP, 1); - if (err < 0) { - brcmf_err("setting AP mode failed %d\n", err); - goto exit; - } - err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_UP, 1); - if (err < 0) { - brcmf_err("BRCMF_C_UP error (%d)\n", err); - goto exit; - } - - memset(&join_params, 0, sizeof(join_params)); - /* join parameters starts with ssid */ - memcpy(&join_params.ssid_le, &ssid_le, sizeof(ssid_le)); - /* create softap */ - err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SSID, - &join_params, sizeof(join_params)); - if (err < 0) { - brcmf_err("SET SSID error (%d)\n", err); - goto exit; - } - brcmf_dbg(TRACE, "AP mode configuration complete\n"); - } else { - err = brcmf_fil_bsscfg_data_set(ifp, "ssid", &ssid_le, - sizeof(ssid_le)); - if (err < 0) { - brcmf_err("setting ssid failed %d\n", err); - goto exit; - } - bss_enable.bsscfg_idx = cpu_to_le32(ifp->bssidx); - bss_enable.enable = cpu_to_le32(1); - err = brcmf_fil_iovar_data_set(ifp, "bss", &bss_enable, - sizeof(bss_enable)); - if (err < 0) { - brcmf_err("bss_enable config failed %d\n", err); - goto exit; - } - - 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); - -exit: - if (err) { - brcmf_set_mpc(ifp, 1); - brcmf_configure_arp_offload(ifp, true); - } - return err; -} - -static int brcmf_cfg80211_stop_ap(struct wiphy *wiphy, struct net_device *ndev) -{ - struct brcmf_if *ifp = netdev_priv(ndev); - s32 err; - struct brcmf_fil_bss_enable_le bss_enable; - struct brcmf_join_params join_params; - - brcmf_dbg(TRACE, "Enter\n"); - - if (ifp->vif->wdev.iftype == NL80211_IFTYPE_AP) { - /* Due to most likely deauths outstanding we sleep */ - /* first to make sure they get processed by fw. */ - msleep(400); - - memset(&join_params, 0, sizeof(join_params)); - err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SSID, - &join_params, sizeof(join_params)); - if (err < 0) - brcmf_err("SET SSID error (%d)\n", err); - err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_UP, 0); - if (err < 0) - brcmf_err("BRCMF_C_UP error %d\n", err); - err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_AP, 0); - if (err < 0) - brcmf_err("setting AP mode failed %d\n", err); - err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_INFRA, 0); - if (err < 0) - brcmf_err("setting INFRA mode failed %d\n", err); - } else { - bss_enable.bsscfg_idx = cpu_to_le32(ifp->bssidx); - bss_enable.enable = cpu_to_le32(0); - err = brcmf_fil_iovar_data_set(ifp, "bss", &bss_enable, - sizeof(bss_enable)); - if (err < 0) - brcmf_err("bss_enable config failed %d\n", err); - } - 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); - - return err; -} - -static s32 -brcmf_cfg80211_change_beacon(struct wiphy *wiphy, struct net_device *ndev, - struct cfg80211_beacon_data *info) -{ - struct brcmf_if *ifp = netdev_priv(ndev); - s32 err; - - brcmf_dbg(TRACE, "Enter\n"); - - err = brcmf_config_ap_mgmt_ie(ifp->vif, info); - - return err; -} - -static int -brcmf_cfg80211_del_station(struct wiphy *wiphy, struct net_device *ndev, - const u8 *mac) -{ - struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); - struct brcmf_scb_val_le scbval; - struct brcmf_if *ifp = netdev_priv(ndev); - s32 err; - - if (!mac) - return -EFAULT; - - brcmf_dbg(TRACE, "Enter %pM\n", mac); - - if (ifp->vif == cfg->p2p.bss_idx[P2PAPI_BSSCFG_DEVICE].vif) - ifp = cfg->p2p.bss_idx[P2PAPI_BSSCFG_PRIMARY].vif->ifp; - if (!check_vif_up(ifp->vif)) - return -EIO; - - memcpy(&scbval.ea, mac, ETH_ALEN); - scbval.val = cpu_to_le32(WLAN_REASON_DEAUTH_LEAVING); - err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SCB_DEAUTHENTICATE_FOR_REASON, - &scbval, sizeof(scbval)); - if (err) - brcmf_err("SCB_DEAUTHENTICATE_FOR_REASON failed %d\n", err); - - brcmf_dbg(TRACE, "Exit\n"); - return err; -} - - -static void -brcmf_cfg80211_mgmt_frame_register(struct wiphy *wiphy, - struct wireless_dev *wdev, - u16 frame_type, bool reg) -{ - struct brcmf_cfg80211_vif *vif; - u16 mgmt_type; - - brcmf_dbg(TRACE, "Enter, frame_type %04x, reg=%d\n", frame_type, reg); - - mgmt_type = (frame_type & IEEE80211_FCTL_STYPE) >> 4; - vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev); - if (reg) - vif->mgmt_rx_reg |= BIT(mgmt_type); - else - vif->mgmt_rx_reg &= ~BIT(mgmt_type); -} - - -static int -brcmf_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, - struct cfg80211_mgmt_tx_params *params, u64 *cookie) -{ - struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); - struct ieee80211_channel *chan = params->chan; - const u8 *buf = params->buf; - size_t len = params->len; - const struct ieee80211_mgmt *mgmt; - struct brcmf_cfg80211_vif *vif; - s32 err = 0; - s32 ie_offset; - s32 ie_len; - struct brcmf_fil_action_frame_le *action_frame; - struct brcmf_fil_af_params_le *af_params; - bool ack; - s32 chan_nr; - u32 freq; - - brcmf_dbg(TRACE, "Enter\n"); - - *cookie = 0; - - mgmt = (const struct ieee80211_mgmt *)buf; - - if (!ieee80211_is_mgmt(mgmt->frame_control)) { - brcmf_err("Driver only allows MGMT packet type\n"); - return -EPERM; - } - - vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev); - - if (ieee80211_is_probe_resp(mgmt->frame_control)) { - /* Right now the only reason to get a probe response */ - /* is for p2p listen response or for p2p GO from */ - /* wpa_supplicant. Unfortunately the probe is send */ - /* on primary ndev, while dongle wants it on the p2p */ - /* vif. Since this is only reason for a probe */ - /* response to be sent, the vif is taken from cfg. */ - /* If ever desired to send proberesp for non p2p */ - /* response then data should be checked for */ - /* "DIRECT-". Note in future supplicant will take */ - /* dedicated p2p wdev to do this and then this 'hack'*/ - /* is not needed anymore. */ - ie_offset = DOT11_MGMT_HDR_LEN + - DOT11_BCN_PRB_FIXED_LEN; - ie_len = len - ie_offset; - if (vif == cfg->p2p.bss_idx[P2PAPI_BSSCFG_PRIMARY].vif) - vif = cfg->p2p.bss_idx[P2PAPI_BSSCFG_DEVICE].vif; - err = brcmf_vif_set_mgmt_ie(vif, - BRCMF_VNDR_IE_PRBRSP_FLAG, - &buf[ie_offset], - ie_len); - cfg80211_mgmt_tx_status(wdev, *cookie, buf, len, true, - GFP_KERNEL); - } else if (ieee80211_is_action(mgmt->frame_control)) { - af_params = kzalloc(sizeof(*af_params), GFP_KERNEL); - if (af_params == NULL) { - brcmf_err("unable to allocate frame\n"); - err = -ENOMEM; - goto exit; - } - action_frame = &af_params->action_frame; - /* Add the packet Id */ - action_frame->packet_id = cpu_to_le32(*cookie); - /* Add BSSID */ - memcpy(&action_frame->da[0], &mgmt->da[0], ETH_ALEN); - memcpy(&af_params->bssid[0], &mgmt->bssid[0], ETH_ALEN); - /* Add the length exepted for 802.11 header */ - action_frame->len = cpu_to_le16(len - DOT11_MGMT_HDR_LEN); - /* Add the channel. Use the one specified as parameter if any or - * the current one (got from the firmware) otherwise - */ - if (chan) - freq = chan->center_freq; - else - brcmf_fil_cmd_int_get(vif->ifp, BRCMF_C_GET_CHANNEL, - &freq); - chan_nr = ieee80211_frequency_to_channel(freq); - af_params->channel = cpu_to_le32(chan_nr); - - memcpy(action_frame->data, &buf[DOT11_MGMT_HDR_LEN], - le16_to_cpu(action_frame->len)); - - brcmf_dbg(TRACE, "Action frame, cookie=%lld, len=%d, freq=%d\n", - *cookie, le16_to_cpu(action_frame->len), freq); - - ack = brcmf_p2p_send_action_frame(cfg, cfg_to_ndev(cfg), - af_params); - - cfg80211_mgmt_tx_status(wdev, *cookie, buf, len, ack, - GFP_KERNEL); - kfree(af_params); - } else { - brcmf_dbg(TRACE, "Unhandled, fc=%04x!!\n", mgmt->frame_control); - brcmf_dbg_hex_dump(true, buf, len, "payload, len=%Zu\n", len); - } - -exit: - return err; -} - - -static int -brcmf_cfg80211_cancel_remain_on_channel(struct wiphy *wiphy, - struct wireless_dev *wdev, - u64 cookie) -{ - struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); - struct brcmf_cfg80211_vif *vif; - int err = 0; - - brcmf_dbg(TRACE, "Enter p2p listen cancel\n"); - - vif = cfg->p2p.bss_idx[P2PAPI_BSSCFG_DEVICE].vif; - if (vif == NULL) { - brcmf_err("No p2p device available for probe response\n"); - err = -ENODEV; - goto exit; - } - brcmf_p2p_cancel_remain_on_channel(vif->ifp); -exit: - return err; -} - -static int brcmf_cfg80211_crit_proto_start(struct wiphy *wiphy, - struct wireless_dev *wdev, - enum nl80211_crit_proto_id proto, - u16 duration) -{ - struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); - struct brcmf_cfg80211_vif *vif; - - vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev); - - /* only DHCP support for now */ - if (proto != NL80211_CRIT_PROTO_DHCP) - return -EINVAL; - - /* suppress and abort scanning */ - set_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status); - brcmf_abort_scanning(cfg); - - return brcmf_btcoex_set_mode(vif, BRCMF_BTCOEX_DISABLED, duration); -} - -static void brcmf_cfg80211_crit_proto_stop(struct wiphy *wiphy, - struct wireless_dev *wdev) -{ - struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); - struct brcmf_cfg80211_vif *vif; - - vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev); - - brcmf_btcoex_set_mode(vif, BRCMF_BTCOEX_ENABLED, 0); - clear_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status); -} - -static s32 -brcmf_notify_tdls_peer_event(struct brcmf_if *ifp, - const struct brcmf_event_msg *e, void *data) -{ - switch (e->reason) { - case BRCMF_E_REASON_TDLS_PEER_DISCOVERED: - brcmf_dbg(TRACE, "TDLS Peer Discovered\n"); - break; - case BRCMF_E_REASON_TDLS_PEER_CONNECTED: - brcmf_dbg(TRACE, "TDLS Peer Connected\n"); - brcmf_proto_add_tdls_peer(ifp->drvr, ifp->ifidx, (u8 *)e->addr); - break; - case BRCMF_E_REASON_TDLS_PEER_DISCONNECTED: - brcmf_dbg(TRACE, "TDLS Peer Disconnected\n"); - brcmf_proto_delete_peer(ifp->drvr, ifp->ifidx, (u8 *)e->addr); - break; - } - - return 0; -} - -static int brcmf_convert_nl80211_tdls_oper(enum nl80211_tdls_operation oper) -{ - int ret; - - switch (oper) { - case NL80211_TDLS_DISCOVERY_REQ: - ret = BRCMF_TDLS_MANUAL_EP_DISCOVERY; - break; - case NL80211_TDLS_SETUP: - ret = BRCMF_TDLS_MANUAL_EP_CREATE; - break; - case NL80211_TDLS_TEARDOWN: - ret = BRCMF_TDLS_MANUAL_EP_DELETE; - break; - default: - brcmf_err("unsupported operation: %d\n", oper); - ret = -EOPNOTSUPP; - } - return ret; -} - -static int brcmf_cfg80211_tdls_oper(struct wiphy *wiphy, - struct net_device *ndev, const u8 *peer, - enum nl80211_tdls_operation oper) -{ - struct brcmf_if *ifp; - struct brcmf_tdls_iovar_le info; - int ret = 0; - - ret = brcmf_convert_nl80211_tdls_oper(oper); - if (ret < 0) - return ret; - - ifp = netdev_priv(ndev); - memset(&info, 0, sizeof(info)); - info.mode = (u8)ret; - if (peer) - memcpy(info.ea, peer, ETH_ALEN); - - ret = brcmf_fil_iovar_data_set(ifp, "tdls_endpoint", - &info, sizeof(info)); - if (ret < 0) - brcmf_err("tdls_endpoint iovar failed: ret=%d\n", ret); - - return ret; -} - -static struct cfg80211_ops wl_cfg80211_ops = { - .add_virtual_intf = brcmf_cfg80211_add_iface, - .del_virtual_intf = brcmf_cfg80211_del_iface, - .change_virtual_intf = brcmf_cfg80211_change_iface, - .scan = brcmf_cfg80211_scan, - .set_wiphy_params = brcmf_cfg80211_set_wiphy_params, - .join_ibss = brcmf_cfg80211_join_ibss, - .leave_ibss = brcmf_cfg80211_leave_ibss, - .get_station = brcmf_cfg80211_get_station, - .set_tx_power = brcmf_cfg80211_set_tx_power, - .get_tx_power = brcmf_cfg80211_get_tx_power, - .add_key = brcmf_cfg80211_add_key, - .del_key = brcmf_cfg80211_del_key, - .get_key = brcmf_cfg80211_get_key, - .set_default_key = brcmf_cfg80211_config_default_key, - .set_default_mgmt_key = brcmf_cfg80211_config_default_mgmt_key, - .set_power_mgmt = brcmf_cfg80211_set_power_mgmt, - .connect = brcmf_cfg80211_connect, - .disconnect = brcmf_cfg80211_disconnect, - .suspend = brcmf_cfg80211_suspend, - .resume = brcmf_cfg80211_resume, - .set_pmksa = brcmf_cfg80211_set_pmksa, - .del_pmksa = brcmf_cfg80211_del_pmksa, - .flush_pmksa = brcmf_cfg80211_flush_pmksa, - .start_ap = brcmf_cfg80211_start_ap, - .stop_ap = brcmf_cfg80211_stop_ap, - .change_beacon = brcmf_cfg80211_change_beacon, - .del_station = brcmf_cfg80211_del_station, - .sched_scan_start = brcmf_cfg80211_sched_scan_start, - .sched_scan_stop = brcmf_cfg80211_sched_scan_stop, - .mgmt_frame_register = brcmf_cfg80211_mgmt_frame_register, - .mgmt_tx = brcmf_cfg80211_mgmt_tx, - .remain_on_channel = brcmf_p2p_remain_on_channel, - .cancel_remain_on_channel = brcmf_cfg80211_cancel_remain_on_channel, - .start_p2p_device = brcmf_p2p_start_device, - .stop_p2p_device = brcmf_p2p_stop_device, - .crit_proto_start = brcmf_cfg80211_crit_proto_start, - .crit_proto_stop = brcmf_cfg80211_crit_proto_stop, - .tdls_oper = brcmf_cfg80211_tdls_oper, -}; - -struct brcmf_cfg80211_vif *brcmf_alloc_vif(struct brcmf_cfg80211_info *cfg, - enum nl80211_iftype type, - bool pm_block) -{ - struct brcmf_cfg80211_vif *vif; - - brcmf_dbg(TRACE, "allocating virtual interface (size=%zu)\n", - sizeof(*vif)); - vif = kzalloc(sizeof(*vif), GFP_KERNEL); - if (!vif) - return ERR_PTR(-ENOMEM); - - vif->wdev.wiphy = cfg->wiphy; - vif->wdev.iftype = type; - - vif->pm_block = pm_block; - vif->roam_off = -1; - - brcmf_init_prof(&vif->profile); - - list_add_tail(&vif->list, &cfg->vif_list); - return vif; -} - -void brcmf_free_vif(struct brcmf_cfg80211_vif *vif) -{ - list_del(&vif->list); - kfree(vif); -} - -void brcmf_cfg80211_free_netdev(struct net_device *ndev) -{ - struct brcmf_cfg80211_vif *vif; - struct brcmf_if *ifp; - - ifp = netdev_priv(ndev); - vif = ifp->vif; - - brcmf_free_vif(vif); - free_netdev(ndev); -} - -static bool brcmf_is_linkup(const struct brcmf_event_msg *e) -{ - u32 event = e->event_code; - u32 status = e->status; - - if (event == BRCMF_E_SET_SSID && status == BRCMF_E_STATUS_SUCCESS) { - brcmf_dbg(CONN, "Processing set ssid\n"); - return true; - } - - return false; -} - -static bool brcmf_is_linkdown(const struct brcmf_event_msg *e) -{ - u32 event = e->event_code; - u16 flags = e->flags; - - if ((event == BRCMF_E_DEAUTH) || (event == BRCMF_E_DEAUTH_IND) || - (event == BRCMF_E_DISASSOC_IND) || - ((event == BRCMF_E_LINK) && (!(flags & BRCMF_EVENT_MSG_LINK)))) { - brcmf_dbg(CONN, "Processing link down\n"); - return true; - } - return false; -} - -static bool brcmf_is_nonetwork(struct brcmf_cfg80211_info *cfg, - const struct brcmf_event_msg *e) -{ - u32 event = e->event_code; - u32 status = e->status; - - if (event == BRCMF_E_LINK && status == BRCMF_E_STATUS_NO_NETWORKS) { - brcmf_dbg(CONN, "Processing Link %s & no network found\n", - e->flags & BRCMF_EVENT_MSG_LINK ? "up" : "down"); - return true; - } - - if (event == BRCMF_E_SET_SSID && status != BRCMF_E_STATUS_SUCCESS) { - brcmf_dbg(CONN, "Processing connecting & no network found\n"); - return true; - } - - return false; -} - -static void brcmf_clear_assoc_ies(struct brcmf_cfg80211_info *cfg) -{ - struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg); - - kfree(conn_info->req_ie); - conn_info->req_ie = NULL; - conn_info->req_ie_len = 0; - kfree(conn_info->resp_ie); - conn_info->resp_ie = NULL; - conn_info->resp_ie_len = 0; -} - -static s32 brcmf_get_assoc_ies(struct brcmf_cfg80211_info *cfg, - struct brcmf_if *ifp) -{ - struct brcmf_cfg80211_assoc_ielen_le *assoc_info; - struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg); - u32 req_len; - u32 resp_len; - s32 err = 0; - - brcmf_clear_assoc_ies(cfg); - - err = brcmf_fil_iovar_data_get(ifp, "assoc_info", - cfg->extra_buf, WL_ASSOC_INFO_MAX); - if (err) { - brcmf_err("could not get assoc info (%d)\n", err); - return err; - } - assoc_info = - (struct brcmf_cfg80211_assoc_ielen_le *)cfg->extra_buf; - req_len = le32_to_cpu(assoc_info->req_len); - resp_len = le32_to_cpu(assoc_info->resp_len); - if (req_len) { - err = brcmf_fil_iovar_data_get(ifp, "assoc_req_ies", - cfg->extra_buf, - WL_ASSOC_INFO_MAX); - if (err) { - brcmf_err("could not get assoc req (%d)\n", err); - return err; - } - conn_info->req_ie_len = req_len; - conn_info->req_ie = - kmemdup(cfg->extra_buf, conn_info->req_ie_len, - GFP_KERNEL); - } else { - conn_info->req_ie_len = 0; - conn_info->req_ie = NULL; - } - if (resp_len) { - err = brcmf_fil_iovar_data_get(ifp, "assoc_resp_ies", - cfg->extra_buf, - WL_ASSOC_INFO_MAX); - if (err) { - brcmf_err("could not get assoc resp (%d)\n", err); - return err; - } - conn_info->resp_ie_len = resp_len; - conn_info->resp_ie = - kmemdup(cfg->extra_buf, conn_info->resp_ie_len, - GFP_KERNEL); - } else { - conn_info->resp_ie_len = 0; - conn_info->resp_ie = NULL; - } - brcmf_dbg(CONN, "req len (%d) resp len (%d)\n", - conn_info->req_ie_len, conn_info->resp_ie_len); - - return err; -} - -static s32 -brcmf_bss_roaming_done(struct brcmf_cfg80211_info *cfg, - struct net_device *ndev, - const struct brcmf_event_msg *e) -{ - struct brcmf_if *ifp = netdev_priv(ndev); - struct brcmf_cfg80211_profile *profile = &ifp->vif->profile; - struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg); - struct wiphy *wiphy = cfg_to_wiphy(cfg); - struct ieee80211_channel *notify_channel = NULL; - struct ieee80211_supported_band *band; - struct brcmf_bss_info_le *bi; - struct brcmu_chan ch; - u32 freq; - s32 err = 0; - u8 *buf; - - brcmf_dbg(TRACE, "Enter\n"); - - brcmf_get_assoc_ies(cfg, ifp); - memcpy(profile->bssid, e->addr, ETH_ALEN); - brcmf_update_bss_info(cfg, ifp); - - buf = kzalloc(WL_BSS_INFO_MAX, GFP_KERNEL); - if (buf == NULL) { - err = -ENOMEM; - goto done; - } - - /* data sent to dongle has to be little endian */ - *(__le32 *)buf = 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) - goto done; - - bi = (struct brcmf_bss_info_le *)(buf + 4); - ch.chspec = le16_to_cpu(bi->chanspec); - cfg->d11inf.decchspec(&ch); - - if (ch.band == BRCMU_CHAN_BAND_2G) - band = wiphy->bands[IEEE80211_BAND_2GHZ]; - else - band = wiphy->bands[IEEE80211_BAND_5GHZ]; - - freq = ieee80211_channel_to_frequency(ch.chnum, band->band); - notify_channel = ieee80211_get_channel(wiphy, freq); - -done: - kfree(buf); - cfg80211_roamed(ndev, notify_channel, (u8 *)profile->bssid, - conn_info->req_ie, conn_info->req_ie_len, - conn_info->resp_ie, conn_info->resp_ie_len, GFP_KERNEL); - brcmf_dbg(CONN, "Report roaming result\n"); - - set_bit(BRCMF_VIF_STATUS_CONNECTED, &ifp->vif->sme_state); - brcmf_dbg(TRACE, "Exit\n"); - return err; -} - -static s32 -brcmf_bss_connect_done(struct brcmf_cfg80211_info *cfg, - struct net_device *ndev, const struct brcmf_event_msg *e, - bool completed) -{ - struct brcmf_if *ifp = netdev_priv(ndev); - struct brcmf_cfg80211_profile *profile = &ifp->vif->profile; - struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg); - - brcmf_dbg(TRACE, "Enter\n"); - - if (test_and_clear_bit(BRCMF_VIF_STATUS_CONNECTING, - &ifp->vif->sme_state)) { - if (completed) { - brcmf_get_assoc_ies(cfg, ifp); - memcpy(profile->bssid, e->addr, ETH_ALEN); - brcmf_update_bss_info(cfg, ifp); - set_bit(BRCMF_VIF_STATUS_CONNECTED, - &ifp->vif->sme_state); - } - cfg80211_connect_result(ndev, - (u8 *)profile->bssid, - conn_info->req_ie, - conn_info->req_ie_len, - conn_info->resp_ie, - conn_info->resp_ie_len, - completed ? WLAN_STATUS_SUCCESS : - WLAN_STATUS_AUTH_TIMEOUT, - GFP_KERNEL); - brcmf_dbg(CONN, "Report connect result - connection %s\n", - completed ? "succeeded" : "failed"); - } - brcmf_dbg(TRACE, "Exit\n"); - return 0; -} - -static s32 -brcmf_notify_connect_status_ap(struct brcmf_cfg80211_info *cfg, - struct net_device *ndev, - const struct brcmf_event_msg *e, void *data) -{ - static int generation; - u32 event = e->event_code; - u32 reason = e->reason; - struct station_info sinfo; - - brcmf_dbg(CONN, "event %d, reason %d\n", event, reason); - if (event == BRCMF_E_LINK && reason == BRCMF_E_REASON_LINK_BSSCFG_DIS && - ndev != cfg_to_ndev(cfg)) { - brcmf_dbg(CONN, "AP mode link down\n"); - complete(&cfg->vif_disabled); - return 0; - } - - if (((event == BRCMF_E_ASSOC_IND) || (event == BRCMF_E_REASSOC_IND)) && - (reason == BRCMF_E_STATUS_SUCCESS)) { - memset(&sinfo, 0, sizeof(sinfo)); - sinfo.filled = STATION_INFO_ASSOC_REQ_IES; - if (!data) { - brcmf_err("No IEs present in ASSOC/REASSOC_IND"); - return -EINVAL; - } - sinfo.assoc_req_ies = data; - sinfo.assoc_req_ies_len = e->datalen; - generation++; - sinfo.generation = generation; - cfg80211_new_sta(ndev, e->addr, &sinfo, GFP_KERNEL); - } else if ((event == BRCMF_E_DISASSOC_IND) || - (event == BRCMF_E_DEAUTH_IND) || - (event == BRCMF_E_DEAUTH)) { - cfg80211_del_sta(ndev, e->addr, GFP_KERNEL); - } - return 0; -} - -static s32 -brcmf_notify_connect_status(struct brcmf_if *ifp, - const struct brcmf_event_msg *e, void *data) -{ - struct brcmf_cfg80211_info *cfg = ifp->drvr->config; - struct net_device *ndev = ifp->ndev; - struct brcmf_cfg80211_profile *profile = &ifp->vif->profile; - struct ieee80211_channel *chan; - s32 err = 0; - - if ((e->event_code == BRCMF_E_DEAUTH) || - (e->event_code == BRCMF_E_DEAUTH_IND) || - (e->event_code == BRCMF_E_DISASSOC_IND) || - ((e->event_code == BRCMF_E_LINK) && (!e->flags))) { - brcmf_proto_delete_peer(ifp->drvr, ifp->ifidx, (u8 *)e->addr); - } - - if (brcmf_is_apmode(ifp->vif)) { - err = brcmf_notify_connect_status_ap(cfg, ndev, e, data); - } else if (brcmf_is_linkup(e)) { - brcmf_dbg(CONN, "Linkup\n"); - if (brcmf_is_ibssmode(ifp->vif)) { - chan = ieee80211_get_channel(cfg->wiphy, cfg->channel); - memcpy(profile->bssid, e->addr, ETH_ALEN); - wl_inform_ibss(cfg, ndev, e->addr); - cfg80211_ibss_joined(ndev, e->addr, chan, GFP_KERNEL); - clear_bit(BRCMF_VIF_STATUS_CONNECTING, - &ifp->vif->sme_state); - set_bit(BRCMF_VIF_STATUS_CONNECTED, - &ifp->vif->sme_state); - } else - brcmf_bss_connect_done(cfg, ndev, e, true); - } else if (brcmf_is_linkdown(e)) { - brcmf_dbg(CONN, "Linkdown\n"); - if (!brcmf_is_ibssmode(ifp->vif)) { - brcmf_bss_connect_done(cfg, ndev, e, false); - } - brcmf_link_down(ifp->vif); - brcmf_init_prof(ndev_to_prof(ndev)); - if (ndev != cfg_to_ndev(cfg)) - complete(&cfg->vif_disabled); - } else if (brcmf_is_nonetwork(cfg, e)) { - if (brcmf_is_ibssmode(ifp->vif)) - clear_bit(BRCMF_VIF_STATUS_CONNECTING, - &ifp->vif->sme_state); - else - brcmf_bss_connect_done(cfg, ndev, e, false); - } - - return err; -} - -static s32 -brcmf_notify_roaming_status(struct brcmf_if *ifp, - const struct brcmf_event_msg *e, void *data) -{ - struct brcmf_cfg80211_info *cfg = ifp->drvr->config; - u32 event = e->event_code; - u32 status = e->status; - - if (event == BRCMF_E_ROAM && status == BRCMF_E_STATUS_SUCCESS) { - if (test_bit(BRCMF_VIF_STATUS_CONNECTED, &ifp->vif->sme_state)) - brcmf_bss_roaming_done(cfg, ifp->ndev, e); - else - brcmf_bss_connect_done(cfg, ifp->ndev, e, true); - } - - return 0; -} - -static s32 -brcmf_notify_mic_status(struct brcmf_if *ifp, - const struct brcmf_event_msg *e, void *data) -{ - u16 flags = e->flags; - enum nl80211_key_type key_type; - - if (flags & BRCMF_EVENT_MSG_GROUP) - key_type = NL80211_KEYTYPE_GROUP; - else - key_type = NL80211_KEYTYPE_PAIRWISE; - - cfg80211_michael_mic_failure(ifp->ndev, (u8 *)&e->addr, key_type, -1, - NULL, GFP_KERNEL); - - return 0; -} - -static s32 brcmf_notify_vif_event(struct brcmf_if *ifp, - const struct brcmf_event_msg *e, void *data) -{ - struct brcmf_cfg80211_info *cfg = ifp->drvr->config; - struct brcmf_if_event *ifevent = (struct brcmf_if_event *)data; - struct brcmf_cfg80211_vif_event *event = &cfg->vif_event; - struct brcmf_cfg80211_vif *vif; - - brcmf_dbg(TRACE, "Enter: action %u flags %u ifidx %u bsscfg %u\n", - ifevent->action, ifevent->flags, ifevent->ifidx, - ifevent->bssidx); - - mutex_lock(&event->vif_event_lock); - event->action = ifevent->action; - vif = event->vif; - - switch (ifevent->action) { - case BRCMF_E_IF_ADD: - /* waiting process may have timed out */ - if (!cfg->vif_event.vif) { - mutex_unlock(&event->vif_event_lock); - return -EBADF; - } - - ifp->vif = vif; - vif->ifp = ifp; - if (ifp->ndev) { - vif->wdev.netdev = ifp->ndev; - ifp->ndev->ieee80211_ptr = &vif->wdev; - SET_NETDEV_DEV(ifp->ndev, wiphy_dev(cfg->wiphy)); - } - mutex_unlock(&event->vif_event_lock); - wake_up(&event->vif_wq); - return 0; - - case BRCMF_E_IF_DEL: - mutex_unlock(&event->vif_event_lock); - /* event may not be upon user request */ - if (brcmf_cfg80211_vif_event_armed(cfg)) - wake_up(&event->vif_wq); - return 0; - - case BRCMF_E_IF_CHANGE: - mutex_unlock(&event->vif_event_lock); - wake_up(&event->vif_wq); - return 0; - - default: - mutex_unlock(&event->vif_event_lock); - break; - } - return -EINVAL; -} - -static void brcmf_init_conf(struct brcmf_cfg80211_conf *conf) -{ - conf->frag_threshold = (u32)-1; - conf->rts_threshold = (u32)-1; - conf->retry_short = (u32)-1; - conf->retry_long = (u32)-1; - conf->tx_power = -1; -} - -static void brcmf_register_event_handlers(struct brcmf_cfg80211_info *cfg) -{ - brcmf_fweh_register(cfg->pub, BRCMF_E_LINK, - brcmf_notify_connect_status); - brcmf_fweh_register(cfg->pub, BRCMF_E_DEAUTH_IND, - brcmf_notify_connect_status); - brcmf_fweh_register(cfg->pub, BRCMF_E_DEAUTH, - brcmf_notify_connect_status); - brcmf_fweh_register(cfg->pub, BRCMF_E_DISASSOC_IND, - brcmf_notify_connect_status); - brcmf_fweh_register(cfg->pub, BRCMF_E_ASSOC_IND, - brcmf_notify_connect_status); - brcmf_fweh_register(cfg->pub, BRCMF_E_REASSOC_IND, - brcmf_notify_connect_status); - brcmf_fweh_register(cfg->pub, BRCMF_E_ROAM, - brcmf_notify_roaming_status); - brcmf_fweh_register(cfg->pub, BRCMF_E_MIC_ERROR, - brcmf_notify_mic_status); - brcmf_fweh_register(cfg->pub, BRCMF_E_SET_SSID, - brcmf_notify_connect_status); - brcmf_fweh_register(cfg->pub, BRCMF_E_PFN_NET_FOUND, - brcmf_notify_sched_scan_results); - brcmf_fweh_register(cfg->pub, BRCMF_E_IF, - brcmf_notify_vif_event); - brcmf_fweh_register(cfg->pub, BRCMF_E_P2P_PROBEREQ_MSG, - brcmf_p2p_notify_rx_mgmt_p2p_probereq); - brcmf_fweh_register(cfg->pub, BRCMF_E_P2P_DISC_LISTEN_COMPLETE, - brcmf_p2p_notify_listen_complete); - brcmf_fweh_register(cfg->pub, BRCMF_E_ACTION_FRAME_RX, - brcmf_p2p_notify_action_frame_rx); - brcmf_fweh_register(cfg->pub, BRCMF_E_ACTION_FRAME_COMPLETE, - brcmf_p2p_notify_action_tx_complete); - brcmf_fweh_register(cfg->pub, BRCMF_E_ACTION_FRAME_OFF_CHAN_COMPLETE, - brcmf_p2p_notify_action_tx_complete); -} - -static void brcmf_deinit_priv_mem(struct brcmf_cfg80211_info *cfg) -{ - kfree(cfg->conf); - cfg->conf = NULL; - kfree(cfg->escan_ioctl_buf); - cfg->escan_ioctl_buf = NULL; - kfree(cfg->extra_buf); - cfg->extra_buf = NULL; - kfree(cfg->pmk_list); - cfg->pmk_list = NULL; -} - -static s32 brcmf_init_priv_mem(struct brcmf_cfg80211_info *cfg) -{ - cfg->conf = kzalloc(sizeof(*cfg->conf), GFP_KERNEL); - if (!cfg->conf) - goto init_priv_mem_out; - cfg->escan_ioctl_buf = kzalloc(BRCMF_DCMD_MEDLEN, GFP_KERNEL); - if (!cfg->escan_ioctl_buf) - goto init_priv_mem_out; - cfg->extra_buf = kzalloc(WL_EXTRA_BUF_MAX, GFP_KERNEL); - if (!cfg->extra_buf) - goto init_priv_mem_out; - cfg->pmk_list = kzalloc(sizeof(*cfg->pmk_list), GFP_KERNEL); - if (!cfg->pmk_list) - goto init_priv_mem_out; - - return 0; - -init_priv_mem_out: - brcmf_deinit_priv_mem(cfg); - - return -ENOMEM; -} - -static s32 wl_init_priv(struct brcmf_cfg80211_info *cfg) -{ - s32 err = 0; - - cfg->scan_request = NULL; - cfg->pwr_save = true; - cfg->active_scan = true; /* we do active scan per default */ - cfg->dongle_up = false; /* dongle is not up yet */ - err = brcmf_init_priv_mem(cfg); - if (err) - return err; - brcmf_register_event_handlers(cfg); - mutex_init(&cfg->usr_sync); - brcmf_init_escan(cfg); - brcmf_init_conf(cfg->conf); - init_completion(&cfg->vif_disabled); - return err; -} - -static void wl_deinit_priv(struct brcmf_cfg80211_info *cfg) -{ - cfg->dongle_up = false; /* dongle down */ - brcmf_abort_scanning(cfg); - brcmf_deinit_priv_mem(cfg); -} - -static void init_vif_event(struct brcmf_cfg80211_vif_event *event) -{ - init_waitqueue_head(&event->vif_wq); - mutex_init(&event->vif_event_lock); -} - -static s32 -brcmf_dongle_roam(struct brcmf_if *ifp, u32 bcn_timeout) -{ - s32 err = 0; - __le32 roamtrigger[2]; - __le32 roam_delta[2]; - - /* - * Setup timeout if Beacons are lost and roam is - * off to report link down - */ - if (brcmf_roamoff) { - err = brcmf_fil_iovar_int_set(ifp, "bcn_timeout", bcn_timeout); - if (err) { - brcmf_err("bcn_timeout error (%d)\n", err); - goto dongle_rom_out; - } - } - - /* - * Enable/Disable built-in roaming to allow supplicant - * to take care of roaming - */ - brcmf_dbg(INFO, "Internal Roaming = %s\n", - brcmf_roamoff ? "Off" : "On"); - err = brcmf_fil_iovar_int_set(ifp, "roam_off", !!(brcmf_roamoff)); - if (err) { - brcmf_err("roam_off error (%d)\n", err); - goto dongle_rom_out; - } - - roamtrigger[0] = cpu_to_le32(WL_ROAM_TRIGGER_LEVEL); - roamtrigger[1] = cpu_to_le32(BRCM_BAND_ALL); - err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_ROAM_TRIGGER, - (void *)roamtrigger, sizeof(roamtrigger)); - if (err) { - brcmf_err("WLC_SET_ROAM_TRIGGER error (%d)\n", err); - goto dongle_rom_out; - } - - roam_delta[0] = cpu_to_le32(WL_ROAM_DELTA); - roam_delta[1] = cpu_to_le32(BRCM_BAND_ALL); - err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_ROAM_DELTA, - (void *)roam_delta, sizeof(roam_delta)); - if (err) { - brcmf_err("WLC_SET_ROAM_DELTA error (%d)\n", err); - goto dongle_rom_out; - } - -dongle_rom_out: - return err; -} - -static s32 -brcmf_dongle_scantime(struct brcmf_if *ifp, s32 scan_assoc_time, - s32 scan_unassoc_time, s32 scan_passive_time) -{ - s32 err = 0; - - err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_SCAN_CHANNEL_TIME, - scan_assoc_time); - if (err) { - if (err == -EOPNOTSUPP) - brcmf_dbg(INFO, "Scan assoc time is not supported\n"); - else - brcmf_err("Scan assoc time error (%d)\n", err); - goto dongle_scantime_out; - } - err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_SCAN_UNASSOC_TIME, - scan_unassoc_time); - if (err) { - if (err == -EOPNOTSUPP) - brcmf_dbg(INFO, "Scan unassoc time is not supported\n"); - else - brcmf_err("Scan unassoc time error (%d)\n", err); - goto dongle_scantime_out; - } - - err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_SCAN_PASSIVE_TIME, - scan_passive_time); - if (err) { - if (err == -EOPNOTSUPP) - brcmf_dbg(INFO, "Scan passive time is not supported\n"); - else - brcmf_err("Scan passive time error (%d)\n", err); - goto dongle_scantime_out; - } - -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) -{ - u32 ht40_flag; - - ht40_flag = channel->flags & IEEE80211_CHAN_NO_HT40; - if (ch->sb == BRCMU_CHAN_SB_U) { - if (ht40_flag == IEEE80211_CHAN_NO_HT40) - channel->flags &= ~IEEE80211_CHAN_NO_HT40; - channel->flags |= IEEE80211_CHAN_NO_HT40PLUS; - } else { - /* It should be one of - * IEEE80211_CHAN_NO_HT40 or - * IEEE80211_CHAN_NO_HT40PLUS - */ - channel->flags &= ~IEEE80211_CHAN_NO_HT40; - if (ht40_flag == IEEE80211_CHAN_NO_HT40) - channel->flags |= IEEE80211_CHAN_NO_HT40MINUS; - } -} - -static int brcmf_construct_chaninfo(struct brcmf_cfg80211_info *cfg, - u32 bw_cap[]) -{ - struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg)); - struct ieee80211_supported_band *band; - struct ieee80211_channel *channel; - struct wiphy *wiphy; - struct brcmf_chanspec_list *list; - struct brcmu_chan ch; - int err; - u8 *pbuf; - u32 i, j; - u32 total; - u32 chaninfo; - u32 chcnt[2] = { 0, 0 }; - u32 index; - - pbuf = kzalloc(BRCMF_DCMD_MEDLEN, GFP_KERNEL); - - if (pbuf == NULL) - return -ENOMEM; - - list = (struct brcmf_chanspec_list *)pbuf; - - err = brcmf_fil_iovar_data_get(ifp, "chanspecs", pbuf, - BRCMF_DCMD_MEDLEN); - if (err) { - brcmf_err("get chanspecs error (%d)\n", err); - 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; - } - - total = le32_to_cpu(list->count); - for (i = 0; i < total; i++) { - ch.chspec = (u16)le32_to_cpu(list->element[i]); - cfg->d11inf.decchspec(&ch); - - if (ch.band == BRCMU_CHAN_BAND_2G) { - band = wiphy->bands[IEEE80211_BAND_2GHZ]; - } else if (ch.band == BRCMU_CHAN_BAND_5G) { - band = wiphy->bands[IEEE80211_BAND_5GHZ]; - } else { - brcmf_err("Invalid channel Spec. 0x%x.\n", ch.chspec); - continue; - } - if (!(bw_cap[band->band] & WLC_BW_40MHZ_BIT) && - ch.bw == BRCMU_CHAN_BW_40) - continue; - if (!(bw_cap[band->band] & WLC_BW_80MHZ_BIT) && - ch.bw == BRCMU_CHAN_BW_80) - continue; - - channel = band->channels; - index = band->n_channels; - for (j = 0; j < band->n_channels; j++) { - if (channel[j].hw_value == ch.chnum) { - index = j; - break; - } - } - channel[index].center_freq = - ieee80211_channel_to_frequency(ch.chnum, band->band); - channel[index].hw_value = ch.chnum; - - /* assuming the chanspecs order is HT20, - * HT40 upper, HT40 lower, and VHT80. - */ - if (ch.bw == BRCMU_CHAN_BW_80) { - channel[index].flags &= ~IEEE80211_CHAN_NO_80MHZ; - } 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. - */ - channel[index].flags = IEEE80211_CHAN_NO_HT40 | - IEEE80211_CHAN_NO_80MHZ; - ch.bw = BRCMU_CHAN_BW_20; - cfg->d11inf.encchspec(&ch); - chaninfo = ch.chspec; - err = brcmf_fil_bsscfg_int_get(ifp, "per_chan_info", - &chaninfo); - if (!err) { - if (chaninfo & WL_CHAN_RADAR) - channel[index].flags |= - (IEEE80211_CHAN_RADAR | - IEEE80211_CHAN_NO_IR); - if (chaninfo & WL_CHAN_PASSIVE) - channel[index].flags |= - 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; -} - -static int brcmf_enable_bw40_2g(struct brcmf_cfg80211_info *cfg) -{ - struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg)); - struct ieee80211_supported_band *band; - struct brcmf_fil_bwcap_le band_bwcap; - struct brcmf_chanspec_list *list; - u8 *pbuf; - u32 val; - int err; - struct brcmu_chan ch; - u32 num_chan; - int i, j; - - /* verify support for bw_cap command */ - val = WLC_BAND_5G; - err = brcmf_fil_iovar_int_get(ifp, "bw_cap", &val); - - if (!err) { - /* only set 2G bandwidth using bw_cap command */ - band_bwcap.band = cpu_to_le32(WLC_BAND_2G); - band_bwcap.bw_cap = cpu_to_le32(WLC_BW_CAP_40MHZ); - err = brcmf_fil_iovar_data_set(ifp, "bw_cap", &band_bwcap, - sizeof(band_bwcap)); - } else { - brcmf_dbg(INFO, "fallback to mimo_bw_cap\n"); - val = WLC_N_BW_40ALL; - err = brcmf_fil_iovar_int_set(ifp, "mimo_bw_cap", val); - } - - if (!err) { - /* update channel info in 2G band */ - pbuf = kzalloc(BRCMF_DCMD_MEDLEN, GFP_KERNEL); - - if (pbuf == NULL) - return -ENOMEM; - - ch.band = BRCMU_CHAN_BAND_2G; - ch.bw = BRCMU_CHAN_BW_40; - ch.sb = BRCMU_CHAN_SB_NONE; - ch.chnum = 0; - cfg->d11inf.encchspec(&ch); - - /* pass encoded chanspec in query */ - *(__le16 *)pbuf = cpu_to_le16(ch.chspec); - - err = brcmf_fil_iovar_data_get(ifp, "chanspecs", pbuf, - BRCMF_DCMD_MEDLEN); - if (err) { - brcmf_err("get chanspecs error (%d)\n", err); - kfree(pbuf); - return err; - } - - band = cfg_to_wiphy(cfg)->bands[IEEE80211_BAND_2GHZ]; - list = (struct brcmf_chanspec_list *)pbuf; - num_chan = le32_to_cpu(list->count); - for (i = 0; i < num_chan; i++) { - ch.chspec = (u16)le32_to_cpu(list->element[i]); - cfg->d11inf.decchspec(&ch); - if (WARN_ON(ch.band != BRCMU_CHAN_BAND_2G)) - continue; - if (WARN_ON(ch.bw != BRCMU_CHAN_BW_40)) - continue; - for (j = 0; j < band->n_channels; j++) { - if (band->channels[j].hw_value == ch.chnum) - break; - } - if (WARN_ON(j == band->n_channels)) - continue; - - brcmf_update_bw40_channel_flag(&band->channels[j], &ch); - } - kfree(pbuf); - } - return err; -} - -static void brcmf_get_bwcap(struct brcmf_if *ifp, u32 bw_cap[]) -{ - u32 band, mimo_bwcap; - int err; - - band = WLC_BAND_2G; - err = brcmf_fil_iovar_int_get(ifp, "bw_cap", &band); - if (!err) { - bw_cap[IEEE80211_BAND_2GHZ] = band; - band = WLC_BAND_5G; - err = brcmf_fil_iovar_int_get(ifp, "bw_cap", &band); - if (!err) { - bw_cap[IEEE80211_BAND_5GHZ] = band; - return; - } - WARN_ON(1); - return; - } - brcmf_dbg(INFO, "fallback to mimo_bw_cap info\n"); - mimo_bwcap = 0; - err = brcmf_fil_iovar_int_get(ifp, "mimo_bw_cap", &mimo_bwcap); - if (err) - /* assume 20MHz if firmware does not give a clue */ - mimo_bwcap = WLC_N_BW_20ALL; - - switch (mimo_bwcap) { - case WLC_N_BW_40ALL: - bw_cap[IEEE80211_BAND_2GHZ] |= WLC_BW_40MHZ_BIT; - /* fall-thru */ - case WLC_N_BW_20IN2G_40IN5G: - bw_cap[IEEE80211_BAND_5GHZ] |= WLC_BW_40MHZ_BIT; - /* fall-thru */ - case WLC_N_BW_20ALL: - bw_cap[IEEE80211_BAND_2GHZ] |= WLC_BW_20MHZ_BIT; - bw_cap[IEEE80211_BAND_5GHZ] |= WLC_BW_20MHZ_BIT; - break; - default: - brcmf_err("invalid mimo_bw_cap value\n"); - } -} - -static void brcmf_update_ht_cap(struct ieee80211_supported_band *band, - u32 bw_cap[2], u32 nchain) -{ - band->ht_cap.ht_supported = true; - if (bw_cap[band->band] & WLC_BW_40MHZ_BIT) { - band->ht_cap.cap |= IEEE80211_HT_CAP_SGI_40; - band->ht_cap.cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40; - } - band->ht_cap.cap |= IEEE80211_HT_CAP_SGI_20; - band->ht_cap.cap |= IEEE80211_HT_CAP_DSSSCCK40; - band->ht_cap.ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K; - band->ht_cap.ampdu_density = IEEE80211_HT_MPDU_DENSITY_16; - memset(band->ht_cap.mcs.rx_mask, 0xff, nchain); - band->ht_cap.mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED; -} - -static __le16 brcmf_get_mcs_map(u32 nchain, enum ieee80211_vht_mcs_support supp) -{ - u16 mcs_map; - int i; - - for (i = 0, mcs_map = 0xFFFF; i < nchain; i++) - mcs_map = (mcs_map << 2) | supp; - - return cpu_to_le16(mcs_map); -} - -static void brcmf_update_vht_cap(struct ieee80211_supported_band *band, - u32 bw_cap[2], u32 nchain) -{ - __le16 mcs_map; - - /* not allowed in 2.4G band */ - if (band->band == IEEE80211_BAND_2GHZ) - return; - - band->vht_cap.vht_supported = true; - /* 80MHz is mandatory */ - band->vht_cap.cap |= IEEE80211_VHT_CAP_SHORT_GI_80; - if (bw_cap[band->band] & WLC_BW_160MHZ_BIT) { - band->vht_cap.cap |= IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ; - band->vht_cap.cap |= IEEE80211_VHT_CAP_SHORT_GI_160; - } - /* all support 256-QAM */ - mcs_map = brcmf_get_mcs_map(nchain, IEEE80211_VHT_MCS_SUPPORT_0_9); - band->vht_cap.vht_mcs.rx_mcs_map = mcs_map; - band->vht_cap.vht_mcs.tx_mcs_map = mcs_map; -} - -static int brcmf_setup_wiphybands(struct wiphy *wiphy) -{ - struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy); - struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg)); - u32 nmode = 0; - u32 vhtmode = 0; - u32 bw_cap[2] = { WLC_BW_20MHZ_BIT, WLC_BW_20MHZ_BIT }; - u32 rxchain; - u32 nchain; - int err; - s32 i; - struct ieee80211_supported_band *band; - - (void)brcmf_fil_iovar_int_get(ifp, "vhtmode", &vhtmode); - err = brcmf_fil_iovar_int_get(ifp, "nmode", &nmode); - if (err) { - brcmf_err("nmode error (%d)\n", err); - } else { - brcmf_get_bwcap(ifp, bw_cap); - } - brcmf_dbg(INFO, "nmode=%d, vhtmode=%d, bw_cap=(%d, %d)\n", - nmode, vhtmode, bw_cap[IEEE80211_BAND_2GHZ], - bw_cap[IEEE80211_BAND_5GHZ]); - - err = brcmf_fil_iovar_int_get(ifp, "rxchain", &rxchain); - if (err) { - brcmf_err("rxchain error (%d)\n", err); - nchain = 1; - } else { - for (nchain = 0; rxchain; nchain++) - rxchain = rxchain & (rxchain - 1); - } - brcmf_dbg(INFO, "nchain=%d\n", nchain); - - err = brcmf_construct_chaninfo(cfg, bw_cap); - if (err) { - brcmf_err("brcmf_construct_chaninfo failed (%d)\n", err); - return err; - } - - wiphy = cfg_to_wiphy(cfg); - for (i = 0; i < ARRAY_SIZE(wiphy->bands); i++) { - band = wiphy->bands[i]; - if (band == NULL) - continue; - - if (nmode) - brcmf_update_ht_cap(band, bw_cap, nchain); - if (vhtmode) - brcmf_update_vht_cap(band, bw_cap, nchain); - } - - return 0; -} - -static const struct ieee80211_iface_limit brcmf_iface_limits[] = { - { - .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), - .limits = brcmf_iface_limits - } -}; - -static const struct ieee80211_txrx_stypes -brcmf_txrx_stypes[NUM_NL80211_IFTYPES] = { - [NL80211_IFTYPE_STATION] = { - .tx = 0xffff, - .rx = BIT(IEEE80211_STYPE_ACTION >> 4) | - BIT(IEEE80211_STYPE_PROBE_REQ >> 4) - }, - [NL80211_IFTYPE_P2P_CLIENT] = { - .tx = 0xffff, - .rx = BIT(IEEE80211_STYPE_ACTION >> 4) | - BIT(IEEE80211_STYPE_PROBE_REQ >> 4) - }, - [NL80211_IFTYPE_P2P_GO] = { - .tx = 0xffff, - .rx = BIT(IEEE80211_STYPE_ASSOC_REQ >> 4) | - BIT(IEEE80211_STYPE_REASSOC_REQ >> 4) | - BIT(IEEE80211_STYPE_PROBE_REQ >> 4) | - BIT(IEEE80211_STYPE_DISASSOC >> 4) | - BIT(IEEE80211_STYPE_AUTH >> 4) | - BIT(IEEE80211_STYPE_DEAUTH >> 4) | - BIT(IEEE80211_STYPE_ACTION >> 4) - }, - [NL80211_IFTYPE_P2P_DEVICE] = { - .tx = 0xffff, - .rx = BIT(IEEE80211_STYPE_ACTION >> 4) | - BIT(IEEE80211_STYPE_PROBE_REQ >> 4) - } -}; - -static void brcmf_wiphy_pno_params(struct wiphy *wiphy) -{ - /* scheduled scan settings */ - wiphy->max_sched_scan_ssids = BRCMF_PNO_MAX_PFN_COUNT; - wiphy->max_match_sets = BRCMF_PNO_MAX_PFN_COUNT; - wiphy->max_sched_scan_ie_len = BRCMF_SCAN_IE_LEN_MAX; - wiphy->flags |= WIPHY_FLAG_SUPPORTS_SCHED_SCAN; -} - -#ifdef CONFIG_PM -static const struct wiphy_wowlan_support brcmf_wowlan_support = { - .flags = WIPHY_WOWLAN_MAGIC_PKT | WIPHY_WOWLAN_DISCONNECT, - .n_patterns = BRCMF_WOWL_MAXPATTERNS, - .pattern_max_len = BRCMF_WOWL_MAXPATTERNSIZE, - .pattern_min_len = 1, - .max_pkt_offset = 1500, -}; -#endif - -static void brcmf_wiphy_wowl_params(struct wiphy *wiphy) -{ -#ifdef CONFIG_PM - /* wowl settings */ - wiphy->wowlan = &brcmf_wowlan_support; -#endif -} - -static int brcmf_setup_wiphy(struct wiphy *wiphy, struct brcmf_if *ifp) -{ - struct ieee80211_iface_combination ifc_combo; - 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; - wiphy->iface_combinations = kmemdup(&ifc_combo, - sizeof(ifc_combo), - GFP_KERNEL); - wiphy->n_iface_combinations = ARRAY_SIZE(brcmf_iface_combos); - wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM; - wiphy->cipher_suites = __wl_cipher_suites; - wiphy->n_cipher_suites = ARRAY_SIZE(__wl_cipher_suites); - wiphy->flags |= WIPHY_FLAG_PS_ON_BY_DEFAULT | - WIPHY_FLAG_OFFCHAN_TX | - WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL | - WIPHY_FLAG_SUPPORTS_TDLS; - if (!brcmf_roamoff) - 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); - - /* vendor commands/events support */ - wiphy->vendor_commands = brcmf_vendor_cmds; - wiphy->n_vendor_commands = BRCMF_VNDR_CMDS_LAST - 1; - - if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_WOWL)) - brcmf_wiphy_wowl_params(wiphy); - - return brcmf_setup_wiphybands(wiphy); -} - -static s32 brcmf_config_dongle(struct brcmf_cfg80211_info *cfg) -{ - struct net_device *ndev; - struct wireless_dev *wdev; - struct brcmf_if *ifp; - s32 power_mode; - s32 err = 0; - - if (cfg->dongle_up) - return err; - - ndev = cfg_to_ndev(cfg); - wdev = ndev->ieee80211_ptr; - ifp = netdev_priv(ndev); - - /* make sure RF is ready for work */ - brcmf_fil_cmd_int_set(ifp, BRCMF_C_UP, 0); - - brcmf_dongle_scantime(ifp, WL_SCAN_CHANNEL_TIME, - WL_SCAN_UNASSOC_TIME, WL_SCAN_PASSIVE_TIME); - - power_mode = cfg->pwr_save ? PM_FAST : PM_OFF; - err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM, power_mode); - if (err) - goto default_conf_out; - brcmf_dbg(INFO, "power save set to %s\n", - (power_mode ? "enabled" : "disabled")); - - err = brcmf_dongle_roam(ifp, WL_BEACON_TIMEOUT); - if (err) - goto default_conf_out; - err = brcmf_cfg80211_change_iface(wdev->wiphy, ndev, wdev->iftype, - NULL, NULL); - if (err) - goto default_conf_out; - - brcmf_configure_arp_offload(ifp, true); - - cfg->dongle_up = true; -default_conf_out: - - return err; - -} - -static s32 __brcmf_cfg80211_up(struct brcmf_if *ifp) -{ - set_bit(BRCMF_VIF_STATUS_READY, &ifp->vif->sme_state); - - return brcmf_config_dongle(ifp->drvr->config); -} - -static s32 __brcmf_cfg80211_down(struct brcmf_if *ifp) -{ - struct brcmf_cfg80211_info *cfg = ifp->drvr->config; - - /* - * While going down, if associated with AP disassociate - * from AP to save power - */ - if (check_vif_up(ifp->vif)) { - brcmf_link_down(ifp->vif); - - /* Make sure WPA_Supplicant receives all the event - generated due to DISASSOC call to the fw to keep - the state fw and WPA_Supplicant state consistent - */ - brcmf_delay(500); - } - - brcmf_abort_scanning(cfg); - clear_bit(BRCMF_VIF_STATUS_READY, &ifp->vif->sme_state); - - return 0; -} - -s32 brcmf_cfg80211_up(struct net_device *ndev) -{ - struct brcmf_if *ifp = netdev_priv(ndev); - struct brcmf_cfg80211_info *cfg = ifp->drvr->config; - s32 err = 0; - - mutex_lock(&cfg->usr_sync); - err = __brcmf_cfg80211_up(ifp); - mutex_unlock(&cfg->usr_sync); - - return err; -} - -s32 brcmf_cfg80211_down(struct net_device *ndev) -{ - struct brcmf_if *ifp = netdev_priv(ndev); - struct brcmf_cfg80211_info *cfg = ifp->drvr->config; - s32 err = 0; - - mutex_lock(&cfg->usr_sync); - err = __brcmf_cfg80211_down(ifp); - mutex_unlock(&cfg->usr_sync); - - return err; -} - -enum nl80211_iftype brcmf_cfg80211_get_iftype(struct brcmf_if *ifp) -{ - struct wireless_dev *wdev = &ifp->vif->wdev; - - return wdev->iftype; -} - -bool brcmf_get_vif_state_any(struct brcmf_cfg80211_info *cfg, unsigned long state) -{ - struct brcmf_cfg80211_vif *vif; - - list_for_each_entry(vif, &cfg->vif_list, list) { - if (test_bit(state, &vif->sme_state)) - return true; - } - return false; -} - -static inline bool vif_event_equals(struct brcmf_cfg80211_vif_event *event, - u8 action) -{ - u8 evt_action; - - mutex_lock(&event->vif_event_lock); - evt_action = event->action; - mutex_unlock(&event->vif_event_lock); - return evt_action == action; -} - -void brcmf_cfg80211_arm_vif_event(struct brcmf_cfg80211_info *cfg, - struct brcmf_cfg80211_vif *vif) -{ - struct brcmf_cfg80211_vif_event *event = &cfg->vif_event; - - mutex_lock(&event->vif_event_lock); - event->vif = vif; - event->action = 0; - mutex_unlock(&event->vif_event_lock); -} - -bool brcmf_cfg80211_vif_event_armed(struct brcmf_cfg80211_info *cfg) -{ - struct brcmf_cfg80211_vif_event *event = &cfg->vif_event; - bool armed; - - mutex_lock(&event->vif_event_lock); - armed = event->vif != NULL; - mutex_unlock(&event->vif_event_lock); - - return armed; -} -int brcmf_cfg80211_wait_vif_event_timeout(struct brcmf_cfg80211_info *cfg, - u8 action, ulong timeout) -{ - struct brcmf_cfg80211_vif_event *event = &cfg->vif_event; - - return wait_event_timeout(event->vif_wq, - vif_event_equals(event, action), timeout); -} - -static void brcmf_free_wiphy(struct wiphy *wiphy) -{ - kfree(wiphy->iface_combinations); - if (wiphy->bands[IEEE80211_BAND_2GHZ]) { - kfree(wiphy->bands[IEEE80211_BAND_2GHZ]->channels); - kfree(wiphy->bands[IEEE80211_BAND_2GHZ]); - } - if (wiphy->bands[IEEE80211_BAND_5GHZ]) { - kfree(wiphy->bands[IEEE80211_BAND_5GHZ]->channels); - kfree(wiphy->bands[IEEE80211_BAND_5GHZ]); - } - wiphy_free(wiphy); -} - -struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr, - struct device *busdev) -{ - struct net_device *ndev = drvr->iflist[0]->ndev; - struct brcmf_cfg80211_info *cfg; - struct wiphy *wiphy; - struct brcmf_cfg80211_vif *vif; - struct brcmf_if *ifp; - s32 err = 0; - s32 io_type; - u16 *cap = NULL; - - if (!ndev) { - brcmf_err("ndev is invalid\n"); - return NULL; - } - - ifp = netdev_priv(ndev); - wiphy = wiphy_new(&wl_cfg80211_ops, sizeof(struct brcmf_cfg80211_info)); - if (!wiphy) { - brcmf_err("Could not allocate wiphy device\n"); - return NULL; - } - set_wiphy_dev(wiphy, busdev); - - cfg = wiphy_priv(wiphy); - cfg->wiphy = wiphy; - cfg->pub = drvr; - init_vif_event(&cfg->vif_event); - INIT_LIST_HEAD(&cfg->vif_list); - - vif = brcmf_alloc_vif(cfg, NL80211_IFTYPE_STATION, false); - if (IS_ERR(vif)) - goto wiphy_out; - - vif->ifp = ifp; - vif->wdev.netdev = ndev; - ndev->ieee80211_ptr = &vif->wdev; - SET_NETDEV_DEV(ndev, wiphy_dev(cfg->wiphy)); - - err = wl_init_priv(cfg); - if (err) { - brcmf_err("Failed to init iwm_priv (%d)\n", err); - brcmf_free_vif(vif); - goto wiphy_out; - } - ifp->vif = vif; - - /* determine d11 io type before wiphy setup */ - err = brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_VERSION, &io_type); - if (err) { - brcmf_err("Failed to get D11 version (%d)\n", err); - goto priv_out; - } - cfg->d11inf.io_type = (u8)io_type; - brcmu_d11_attach(&cfg->d11inf); - - err = brcmf_setup_wiphy(wiphy, ifp); - if (err < 0) - goto priv_out; - - brcmf_dbg(INFO, "Registering custom regulatory\n"); - wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG; - wiphy_apply_custom_regulatory(wiphy, &brcmf_regdom); - - /* firmware defaults to 40MHz disabled in 2G band. We signal - * cfg80211 here that we do and have it decide we can enable - * it. But first check if device does support 2G operation. - */ - if (wiphy->bands[IEEE80211_BAND_2GHZ]) { - cap = &wiphy->bands[IEEE80211_BAND_2GHZ]->ht_cap.cap; - *cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40; - } - err = wiphy_register(wiphy); - if (err < 0) { - brcmf_err("Could not register wiphy device (%d)\n", err); - goto priv_out; - } - - /* If cfg80211 didn't disable 40MHz HT CAP in wiphy_register(), - * setup 40MHz in 2GHz band and enable OBSS scanning. - */ - if (cap && (*cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40)) { - err = brcmf_enable_bw40_2g(cfg); - if (!err) - err = brcmf_fil_iovar_int_set(ifp, "obss_coex", - BRCMF_OBSS_COEX_AUTO); - else - *cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40; - } - - err = brcmf_p2p_attach(cfg); - if (err) { - brcmf_err("P2P initilisation failed (%d)\n", err); - goto wiphy_unreg_out; - } - err = brcmf_btcoex_attach(cfg); - if (err) { - brcmf_err("BT-coex initialisation failed (%d)\n", err); - brcmf_p2p_detach(&cfg->p2p); - goto wiphy_unreg_out; - } - - err = brcmf_fil_iovar_int_set(ifp, "tdls_enable", 1); - if (err) { - brcmf_dbg(INFO, "TDLS not enabled (%d)\n", err); - wiphy->flags &= ~WIPHY_FLAG_SUPPORTS_TDLS; - } else { - brcmf_fweh_register(cfg->pub, BRCMF_E_TDLS_PEER_EVENT, - brcmf_notify_tdls_peer_event); - } - - return cfg; - -wiphy_unreg_out: - wiphy_unregister(cfg->wiphy); -priv_out: - wl_deinit_priv(cfg); - brcmf_free_vif(vif); -wiphy_out: - brcmf_free_wiphy(wiphy); - return NULL; -} - -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); - wl_deinit_priv(cfg); - brcmf_free_wiphy(cfg->wiphy); -} diff --git a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.h b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.h deleted file mode 100644 index 6abf94e41d3d..000000000000 --- a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.h +++ /dev/null @@ -1,496 +0,0 @@ -/* - * Copyright (c) 2010 Broadcom Corporation - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY - * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION - * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN - * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef _wl_cfg80211_h_ -#define _wl_cfg80211_h_ - -/* for brcmu_d11inf */ -#include - -#define WL_NUM_SCAN_MAX 10 -#define WL_NUM_PMKIDS_MAX MAXPMKID -#define WL_TLV_INFO_MAX 1024 -#define WL_BSS_INFO_MAX 2048 -#define WL_ASSOC_INFO_MAX 512 /* assoc related fil max buf */ -#define WL_EXTRA_BUF_MAX 2048 -#define WL_ROAM_TRIGGER_LEVEL -75 -#define WL_ROAM_DELTA 20 -#define WL_BEACON_TIMEOUT 3 - -#define WL_SCAN_CHANNEL_TIME 40 -#define WL_SCAN_UNASSOC_TIME 40 -#define WL_SCAN_PASSIVE_TIME 120 - -#define WL_ESCAN_BUF_SIZE (1024 * 64) -#define WL_ESCAN_TIMER_INTERVAL_MS 10000 /* E-Scan timeout */ - -#define WL_ESCAN_ACTION_START 1 -#define WL_ESCAN_ACTION_CONTINUE 2 -#define WL_ESCAN_ACTION_ABORT 3 - -#define WL_AUTH_SHARED_KEY 1 /* d11 shared authentication */ -#define IE_MAX_LEN 512 - -/* IE TLV processing */ -#define TLV_LEN_OFF 1 /* length offset */ -#define TLV_HDR_LEN 2 /* header length */ -#define TLV_BODY_OFF 2 /* body offset */ -#define TLV_OUI_LEN 3 /* oui id length */ - -/* 802.11 Mgmt Packet flags */ -#define BRCMF_VNDR_IE_BEACON_FLAG 0x1 -#define BRCMF_VNDR_IE_PRBRSP_FLAG 0x2 -#define BRCMF_VNDR_IE_ASSOCRSP_FLAG 0x4 -#define BRCMF_VNDR_IE_AUTHRSP_FLAG 0x8 -#define BRCMF_VNDR_IE_PRBREQ_FLAG 0x10 -#define BRCMF_VNDR_IE_ASSOCREQ_FLAG 0x20 -/* vendor IE in IW advertisement protocol ID field */ -#define BRCMF_VNDR_IE_IWAPID_FLAG 0x40 -/* allow custom IE id */ -#define BRCMF_VNDR_IE_CUSTOM_FLAG 0x100 - -/* P2P Action Frames flags (spec ordered) */ -#define BRCMF_VNDR_IE_GONREQ_FLAG 0x001000 -#define BRCMF_VNDR_IE_GONRSP_FLAG 0x002000 -#define BRCMF_VNDR_IE_GONCFM_FLAG 0x004000 -#define BRCMF_VNDR_IE_INVREQ_FLAG 0x008000 -#define BRCMF_VNDR_IE_INVRSP_FLAG 0x010000 -#define BRCMF_VNDR_IE_DISREQ_FLAG 0x020000 -#define BRCMF_VNDR_IE_DISRSP_FLAG 0x040000 -#define BRCMF_VNDR_IE_PRDREQ_FLAG 0x080000 -#define BRCMF_VNDR_IE_PRDRSP_FLAG 0x100000 - -#define BRCMF_VNDR_IE_P2PAF_SHIFT 12 - - -/** - * enum brcmf_scan_status - scan engine status - * - * @BRCMF_SCAN_STATUS_BUSY: scanning in progress on dongle. - * @BRCMF_SCAN_STATUS_ABORT: scan being aborted on dongle. - * @BRCMF_SCAN_STATUS_SUPPRESS: scanning is suppressed in driver. - */ -enum brcmf_scan_status { - BRCMF_SCAN_STATUS_BUSY, - BRCMF_SCAN_STATUS_ABORT, - BRCMF_SCAN_STATUS_SUPPRESS, -}; - -/* dongle configuration */ -struct brcmf_cfg80211_conf { - u32 frag_threshold; - u32 rts_threshold; - u32 retry_short; - u32 retry_long; - s32 tx_power; - struct ieee80211_channel channel; -}; - -/* basic structure of scan request */ -struct brcmf_cfg80211_scan_req { - struct brcmf_ssid_le ssid_le; -}; - -/* basic structure of information element */ -struct brcmf_cfg80211_ie { - u16 offset; - u8 buf[WL_TLV_INFO_MAX]; -}; - -/* security information with currently associated ap */ -struct brcmf_cfg80211_security { - u32 wpa_versions; - u32 auth_type; - u32 cipher_pairwise; - u32 cipher_group; - u32 wpa_auth; -}; - -/** - * struct brcmf_cfg80211_profile - profile information. - * - * @ssid: ssid of associated/associating ap. - * @bssid: bssid of joined/joining ibss. - * @sec: security information. - */ -struct brcmf_cfg80211_profile { - struct brcmf_ssid ssid; - u8 bssid[ETH_ALEN]; - struct brcmf_cfg80211_security sec; -}; - -/** - * enum brcmf_vif_status - bit indices for vif status. - * - * @BRCMF_VIF_STATUS_READY: ready for operation. - * @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 { - BRCMF_VIF_STATUS_READY, - BRCMF_VIF_STATUS_CONNECTING, - BRCMF_VIF_STATUS_CONNECTED, - BRCMF_VIF_STATUS_DISCONNECTING, - BRCMF_VIF_STATUS_AP_CREATING, - BRCMF_VIF_STATUS_AP_CREATED -}; - -/** - * struct vif_saved_ie - holds saved IEs for a virtual interface. - * - * @probe_req_ie: IE info for probe request. - * @probe_res_ie: IE info for probe response. - * @beacon_ie: IE info for beacon frame. - * @probe_req_ie_len: IE info length for probe request. - * @probe_res_ie_len: IE info length for probe response. - * @beacon_ie_len: IE info length for beacon frame. - */ -struct vif_saved_ie { - u8 probe_req_ie[IE_MAX_LEN]; - u8 probe_res_ie[IE_MAX_LEN]; - u8 beacon_ie[IE_MAX_LEN]; - u8 assoc_req_ie[IE_MAX_LEN]; - u32 probe_req_ie_len; - u32 probe_res_ie_len; - u32 beacon_ie_len; - u32 assoc_req_ie_len; -}; - -/** - * struct brcmf_cfg80211_vif - virtual interface specific information. - * - * @ifp: lower layer interface pointer - * @wdev: wireless device. - * @profile: profile information. - * @roam_off: roaming state. - * @sme_state: SME state using enum brcmf_vif_status bits. - * @pm_block: power-management blocked. - * @list: linked list. - * @mgmt_rx_reg: registered rx mgmt frame types. - */ -struct brcmf_cfg80211_vif { - struct brcmf_if *ifp; - struct wireless_dev wdev; - struct brcmf_cfg80211_profile profile; - s32 roam_off; - unsigned long sme_state; - bool pm_block; - struct vif_saved_ie saved_ie; - struct list_head list; - u16 mgmt_rx_reg; -}; - -/* association inform */ -struct brcmf_cfg80211_connect_info { - u8 *req_ie; - s32 req_ie_len; - u8 *resp_ie; - s32 resp_ie_len; -}; - -/* assoc ie length */ -struct brcmf_cfg80211_assoc_ielen_le { - __le32 req_len; - __le32 resp_len; -}; - -/* wpa2 pmk list */ -struct brcmf_cfg80211_pmk_list { - struct pmkid_list pmkids; - struct pmkid foo[MAXPMKID - 1]; -}; - -/* dongle escan state */ -enum wl_escan_state { - WL_ESCAN_STATE_IDLE, - WL_ESCAN_STATE_SCANNING -}; - -struct escan_info { - u32 escan_state; - u8 escan_buf[WL_ESCAN_BUF_SIZE]; - struct wiphy *wiphy; - struct brcmf_if *ifp; - s32 (*run)(struct brcmf_cfg80211_info *cfg, struct brcmf_if *ifp, - struct cfg80211_scan_request *request, u16 action); -}; - -/** - * struct brcmf_pno_param_le - PNO scan configuration parameters - * - * @version: PNO parameters version. - * @scan_freq: scan frequency. - * @lost_network_timeout: #sec. to declare discovered network as lost. - * @flags: Bit field to control features of PFN such as sort criteria auto - * enable switch and background scan. - * @rssi_margin: Margin to avoid jitter for choosing a PFN based on RSSI sort - * criteria. - * @bestn: number of best networks in each scan. - * @mscan: number of scans recorded. - * @repeat: minimum number of scan intervals before scan frequency changes - * in adaptive scan. - * @exp: exponent of 2 for maximum scan interval. - * @slow_freq: slow scan period. - */ -struct brcmf_pno_param_le { - __le32 version; - __le32 scan_freq; - __le32 lost_network_timeout; - __le16 flags; - __le16 rssi_margin; - u8 bestn; - u8 mscan; - u8 repeat; - u8 exp; - __le32 slow_freq; -}; - -/** - * struct brcmf_pno_net_param_le - scan parameters per preferred network. - * - * @ssid: ssid name and its length. - * @flags: bit2: hidden. - * @infra: BSS vs IBSS. - * @auth: Open vs Closed. - * @wpa_auth: WPA type. - * @wsec: wsec value. - */ -struct brcmf_pno_net_param_le { - struct brcmf_ssid_le ssid; - __le32 flags; - __le32 infra; - __le32 auth; - __le32 wpa_auth; - __le32 wsec; -}; - -/** - * struct brcmf_pno_net_info_le - information per found network. - * - * @bssid: BSS network identifier. - * @channel: channel number only. - * @SSID_len: length of ssid. - * @SSID: ssid characters. - * @RSSI: receive signal strength (in dBm). - * @timestamp: age in seconds. - */ -struct brcmf_pno_net_info_le { - u8 bssid[ETH_ALEN]; - u8 channel; - u8 SSID_len; - u8 SSID[32]; - __le16 RSSI; - __le16 timestamp; -}; - -/** - * struct brcmf_pno_scanresults_le - result returned in PNO NET FOUND event. - * - * @version: PNO version identifier. - * @status: indicates completion status of PNO scan. - * @count: amount of brcmf_pno_net_info_le entries appended. - */ -struct brcmf_pno_scanresults_le { - __le32 version; - __le32 status; - __le32 count; -}; - -/** - * struct brcmf_cfg80211_vif_event - virtual interface event information. - * - * @vif_wq: waitqueue awaiting interface event from firmware. - * @vif_event_lock: protects other members in this structure. - * @vif_complete: completion for net attach. - * @action: either add, change, or delete. - * @vif: virtual interface object related to the event. - */ -struct brcmf_cfg80211_vif_event { - wait_queue_head_t vif_wq; - struct mutex vif_event_lock; - u8 action; - struct brcmf_cfg80211_vif *vif; -}; - -/** - * struct brcmf_cfg80211_info - dongle private data of cfg80211 interface - * - * @wiphy: wiphy object for cfg80211 interface. - * @conf: dongle configuration. - * @p2p: peer-to-peer specific information. - * @btcoex: Bluetooth coexistence information. - * @scan_request: cfg80211 scan request object. - * @usr_sync: mainly for dongle up/down synchronization. - * @bss_list: bss_list holding scanned ap information. - * @scan_req_int: internal scan request object. - * @bss_info: bss information for cfg80211 layer. - * @ie: information element object for internal purpose. - * @conn_info: association info. - * @pmk_list: wpa2 pmk list. - * @scan_status: scan activity on the dongle. - * @pub: common driver information. - * @channel: current channel. - * @active_scan: current scan mode. - * @sched_escan: e-scan for scheduled scan support running. - * @ibss_starter: indicates this sta is ibss starter. - * @pwr_save: indicate whether dongle to support power save mode. - * @dongle_up: indicate whether dongle up or not. - * @roam_on: on/off switch for dongle self-roaming. - * @scan_tried: indicates if first scan attempted. - * @dcmd_buf: dcmd buffer. - * @extra_buf: mainly to grab assoc information. - * @debugfsdir: debugfs folder for this device. - * @escan_info: escan information. - * @escan_timeout: Timer for catch scan timeout. - * @escan_timeout_work: scan timeout worker. - * @escan_ioctl_buf: dongle command buffer for escan commands. - * @vif_list: linked list of vif instances. - * @vif_cnt: number of vif instances. - * @vif_event: vif event signalling. - * @wowl_enabled; set during suspend, is wowl used. - * @pre_wowl_pmmode: intermediate storage of pm mode during wowl. - */ -struct brcmf_cfg80211_info { - struct wiphy *wiphy; - struct brcmf_cfg80211_conf *conf; - struct brcmf_p2p_info p2p; - struct brcmf_btcoex_info *btcoex; - struct cfg80211_scan_request *scan_request; - struct mutex usr_sync; - struct brcmf_cfg80211_scan_req scan_req_int; - struct wl_cfg80211_bss_info *bss_info; - struct brcmf_cfg80211_ie ie; - struct brcmf_cfg80211_connect_info conn_info; - struct brcmf_cfg80211_pmk_list *pmk_list; - unsigned long scan_status; - struct brcmf_pub *pub; - u32 channel; - bool active_scan; - bool sched_escan; - bool ibss_starter; - bool pwr_save; - bool dongle_up; - bool scan_tried; - u8 *dcmd_buf; - u8 *extra_buf; - struct dentry *debugfsdir; - struct escan_info escan_info; - struct timer_list escan_timeout; - struct work_struct escan_timeout_work; - u8 *escan_ioctl_buf; - struct list_head vif_list; - struct brcmf_cfg80211_vif_event vif_event; - struct completion vif_disabled; - struct brcmu_d11inf d11inf; - bool wowl_enabled; - u32 pre_wowl_pmmode; -}; - -/** - * struct brcmf_tlv - tag_ID/length/value_buffer tuple. - * - * @id: tag identifier. - * @len: number of bytes in value buffer. - * @data: value buffer. - */ -struct brcmf_tlv { - u8 id; - u8 len; - u8 data[1]; -}; - -static inline struct wiphy *cfg_to_wiphy(struct brcmf_cfg80211_info *cfg) -{ - return cfg->wiphy; -} - -static inline struct brcmf_cfg80211_info *wiphy_to_cfg(struct wiphy *w) -{ - return (struct brcmf_cfg80211_info *)(wiphy_priv(w)); -} - -static inline struct brcmf_cfg80211_info *wdev_to_cfg(struct wireless_dev *wd) -{ - return (struct brcmf_cfg80211_info *)(wdev_priv(wd)); -} - -static inline -struct net_device *cfg_to_ndev(struct brcmf_cfg80211_info *cfg) -{ - struct brcmf_cfg80211_vif *vif; - vif = list_first_entry(&cfg->vif_list, struct brcmf_cfg80211_vif, list); - return vif->wdev.netdev; -} - -static inline struct brcmf_cfg80211_info *ndev_to_cfg(struct net_device *ndev) -{ - return wdev_to_cfg(ndev->ieee80211_ptr); -} - -static inline struct brcmf_cfg80211_profile *ndev_to_prof(struct net_device *nd) -{ - struct brcmf_if *ifp = netdev_priv(nd); - return &ifp->vif->profile; -} - -static inline struct brcmf_cfg80211_vif *ndev_to_vif(struct net_device *ndev) -{ - struct brcmf_if *ifp = netdev_priv(ndev); - return ifp->vif; -} - -static inline struct -brcmf_cfg80211_connect_info *cfg_to_conn(struct brcmf_cfg80211_info *cfg) -{ - return &cfg->conn_info; -} - -struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr, - struct device *busdev); -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); -enum nl80211_iftype brcmf_cfg80211_get_iftype(struct brcmf_if *ifp); - -struct brcmf_cfg80211_vif *brcmf_alloc_vif(struct brcmf_cfg80211_info *cfg, - enum nl80211_iftype type, - bool pm_block); -void brcmf_free_vif(struct brcmf_cfg80211_vif *vif); - -s32 brcmf_vif_set_mgmt_ie(struct brcmf_cfg80211_vif *vif, s32 pktflag, - const u8 *vndr_ie_buf, u32 vndr_ie_len); -s32 brcmf_vif_clear_mgmt_ies(struct brcmf_cfg80211_vif *vif); -const struct brcmf_tlv * -brcmf_parse_tlvs(const void *buf, int buflen, uint key); -u16 channel_to_chanspec(struct brcmu_d11inf *d11inf, - struct ieee80211_channel *ch); -bool brcmf_get_vif_state_any(struct brcmf_cfg80211_info *cfg, unsigned long state); -void brcmf_cfg80211_arm_vif_event(struct brcmf_cfg80211_info *cfg, - struct brcmf_cfg80211_vif *vif); -bool brcmf_cfg80211_vif_event_armed(struct brcmf_cfg80211_info *cfg); -int brcmf_cfg80211_wait_vif_event_timeout(struct brcmf_cfg80211_info *cfg, - u8 action, ulong timeout); -s32 brcmf_notify_escan_complete(struct brcmf_cfg80211_info *cfg, - struct brcmf_if *ifp, bool aborted, - bool fw_abort); -void brcmf_set_mpc(struct brcmf_if *ndev, int mpc); -void brcmf_abort_scanning(struct brcmf_cfg80211_info *cfg); -void brcmf_cfg80211_free_netdev(struct net_device *ndev); - -#endif /* _wl_cfg80211_h_ */ -- cgit v1.2.3 From 888bf76e41119bf72256830bce0eb192a6a4033a Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Tue, 28 Oct 2014 14:56:17 +0100 Subject: brcmfmac: (clean) Rename sdio related files. Rename sdio_host.h to sdio.h and dhd_sdio.c to sdio.c. Reviewed-by: Arend Van Spriel Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/Makefile | 2 +- drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c | 2 +- drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c | 4276 -------------------- drivers/net/wireless/brcm80211/brcmfmac/of.c | 2 +- drivers/net/wireless/brcm80211/brcmfmac/sdio.c | 4276 ++++++++++++++++++++ drivers/net/wireless/brcm80211/brcmfmac/sdio.h | 340 ++ .../net/wireless/brcm80211/brcmfmac/sdio_host.h | 340 -- 7 files changed, 4619 insertions(+), 4619 deletions(-) delete mode 100644 drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c create mode 100644 drivers/net/wireless/brcm80211/brcmfmac/sdio.c create mode 100644 drivers/net/wireless/brcm80211/brcmfmac/sdio.h delete mode 100644 drivers/net/wireless/brcm80211/brcmfmac/sdio_host.h (limited to 'drivers/net/wireless/brcm80211') diff --git a/drivers/net/wireless/brcm80211/brcmfmac/Makefile b/drivers/net/wireless/brcm80211/brcmfmac/Makefile index 35b680550446..40ba46b4809d 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/Makefile +++ b/drivers/net/wireless/brcm80211/brcmfmac/Makefile @@ -43,7 +43,7 @@ brcmfmac-$(CONFIG_BRCMFMAC_PROTO_MSGBUF) += \ flowring.o \ msgbuf.o brcmfmac-$(CONFIG_BRCMFMAC_SDIO) += \ - dhd_sdio.o \ + sdio.o \ bcmsdh.o brcmfmac-$(CONFIG_BRCMFMAC_USB) += \ usb.o diff --git a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c index 969bdd655c7b..f754ffcd0308 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c @@ -43,7 +43,7 @@ #include "chip.h" #include "bus.h" #include "debug.h" -#include "sdio_host.h" +#include "sdio.h" #include "of.h" #define SDIOH_API_ACCESS_RETRY_LIMIT 2 diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c deleted file mode 100644 index 224e922d139a..000000000000 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c +++ /dev/null @@ -1,4276 +0,0 @@ -/* - * Copyright (c) 2010 Broadcom Corporation - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY - * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION - * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN - * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "sdio_host.h" -#include "chip.h" -#include "firmware.h" - -#define DCMD_RESP_TIMEOUT 2000 /* In milli second */ - -#ifdef DEBUG - -#define BRCMF_TRAP_INFO_SIZE 80 - -#define CBUF_LEN (128) - -/* Device console log buffer state */ -#define CONSOLE_BUFFER_MAX 2024 - -struct rte_log_le { - __le32 buf; /* Can't be pointer on (64-bit) hosts */ - __le32 buf_size; - __le32 idx; - char *_buf_compat; /* Redundant pointer for backward compat. */ -}; - -struct rte_console { - /* Virtual UART - * When there is no UART (e.g. Quickturn), - * the host should write a complete - * input line directly into cbuf and then write - * the length into vcons_in. - * This may also be used when there is a real UART - * (at risk of conflicting with - * the real UART). vcons_out is currently unused. - */ - uint vcons_in; - uint vcons_out; - - /* Output (logging) buffer - * Console output is written to a ring buffer log_buf at index log_idx. - * The host may read the output when it sees log_idx advance. - * Output will be lost if the output wraps around faster than the host - * polls. - */ - struct rte_log_le log_le; - - /* Console input line buffer - * Characters are read one at a time into cbuf - * until is received, then - * the buffer is processed as a command line. - * Also used for virtual UART. - */ - uint cbuf_idx; - char cbuf[CBUF_LEN]; -}; - -#endif /* DEBUG */ -#include - -#include "bus.h" -#include "debug.h" -#include "tracepoint.h" - -#define TXQLEN 2048 /* bulk tx queue length */ -#define TXHI (TXQLEN - 256) /* turn on flow control above TXHI */ -#define TXLOW (TXHI - 256) /* turn off flow control below TXLOW */ -#define PRIOMASK 7 - -#define TXRETRIES 2 /* # of retries for tx frames */ - -#define BRCMF_RXBOUND 50 /* Default for max rx frames in - one scheduling */ - -#define BRCMF_TXBOUND 20 /* Default for max tx frames in - one scheduling */ - -#define BRCMF_TXMINMAX 1 /* Max tx frames if rx still pending */ - -#define MEMBLOCK 2048 /* Block size used for downloading - of dongle image */ -#define MAX_DATA_BUF (32 * 1024) /* Must be large enough to hold - biggest possible glom */ - -#define BRCMF_FIRSTREAD (1 << 6) - - -/* SBSDIO_DEVICE_CTL */ - -/* 1: device will assert busy signal when receiving CMD53 */ -#define SBSDIO_DEVCTL_SETBUSY 0x01 -/* 1: assertion of sdio interrupt is synchronous to the sdio clock */ -#define SBSDIO_DEVCTL_SPI_INTR_SYNC 0x02 -/* 1: mask all interrupts to host except the chipActive (rev 8) */ -#define SBSDIO_DEVCTL_CA_INT_ONLY 0x04 -/* 1: isolate internal sdio signals, put external pads in tri-state; requires - * sdio bus power cycle to clear (rev 9) */ -#define SBSDIO_DEVCTL_PADS_ISO 0x08 -/* Force SD->SB reset mapping (rev 11) */ -#define SBSDIO_DEVCTL_SB_RST_CTL 0x30 -/* Determined by CoreControl bit */ -#define SBSDIO_DEVCTL_RST_CORECTL 0x00 -/* Force backplane reset */ -#define SBSDIO_DEVCTL_RST_BPRESET 0x10 -/* Force no backplane reset */ -#define SBSDIO_DEVCTL_RST_NOBPRESET 0x20 - -/* direct(mapped) cis space */ - -/* MAPPED common CIS address */ -#define SBSDIO_CIS_BASE_COMMON 0x1000 -/* maximum bytes in one CIS */ -#define SBSDIO_CIS_SIZE_LIMIT 0x200 -/* cis offset addr is < 17 bits */ -#define SBSDIO_CIS_OFT_ADDR_MASK 0x1FFFF - -/* manfid tuple length, include tuple, link bytes */ -#define SBSDIO_CIS_MANFID_TUPLE_LEN 6 - -#define CORE_BUS_REG(base, field) \ - (base + offsetof(struct sdpcmd_regs, field)) - -/* SDIO function 1 register CHIPCLKCSR */ -/* Force ALP request to backplane */ -#define SBSDIO_FORCE_ALP 0x01 -/* Force HT request to backplane */ -#define SBSDIO_FORCE_HT 0x02 -/* Force ILP request to backplane */ -#define SBSDIO_FORCE_ILP 0x04 -/* Make ALP ready (power up xtal) */ -#define SBSDIO_ALP_AVAIL_REQ 0x08 -/* Make HT ready (power up PLL) */ -#define SBSDIO_HT_AVAIL_REQ 0x10 -/* Squelch clock requests from HW */ -#define SBSDIO_FORCE_HW_CLKREQ_OFF 0x20 -/* Status: ALP is ready */ -#define SBSDIO_ALP_AVAIL 0x40 -/* Status: HT is ready */ -#define SBSDIO_HT_AVAIL 0x80 -#define SBSDIO_CSR_MASK 0x1F -#define SBSDIO_AVBITS (SBSDIO_HT_AVAIL | SBSDIO_ALP_AVAIL) -#define SBSDIO_ALPAV(regval) ((regval) & SBSDIO_AVBITS) -#define SBSDIO_HTAV(regval) (((regval) & SBSDIO_AVBITS) == SBSDIO_AVBITS) -#define SBSDIO_ALPONLY(regval) (SBSDIO_ALPAV(regval) && !SBSDIO_HTAV(regval)) -#define SBSDIO_CLKAV(regval, alponly) \ - (SBSDIO_ALPAV(regval) && (alponly ? 1 : SBSDIO_HTAV(regval))) - -/* intstatus */ -#define I_SMB_SW0 (1 << 0) /* To SB Mail S/W interrupt 0 */ -#define I_SMB_SW1 (1 << 1) /* To SB Mail S/W interrupt 1 */ -#define I_SMB_SW2 (1 << 2) /* To SB Mail S/W interrupt 2 */ -#define I_SMB_SW3 (1 << 3) /* To SB Mail S/W interrupt 3 */ -#define I_SMB_SW_MASK 0x0000000f /* To SB Mail S/W interrupts mask */ -#define I_SMB_SW_SHIFT 0 /* To SB Mail S/W interrupts shift */ -#define I_HMB_SW0 (1 << 4) /* To Host Mail S/W interrupt 0 */ -#define I_HMB_SW1 (1 << 5) /* To Host Mail S/W interrupt 1 */ -#define I_HMB_SW2 (1 << 6) /* To Host Mail S/W interrupt 2 */ -#define I_HMB_SW3 (1 << 7) /* To Host Mail S/W interrupt 3 */ -#define I_HMB_SW_MASK 0x000000f0 /* To Host Mail S/W interrupts mask */ -#define I_HMB_SW_SHIFT 4 /* To Host Mail S/W interrupts shift */ -#define I_WR_OOSYNC (1 << 8) /* Write Frame Out Of Sync */ -#define I_RD_OOSYNC (1 << 9) /* Read Frame Out Of Sync */ -#define I_PC (1 << 10) /* descriptor error */ -#define I_PD (1 << 11) /* data error */ -#define I_DE (1 << 12) /* Descriptor protocol Error */ -#define I_RU (1 << 13) /* Receive descriptor Underflow */ -#define I_RO (1 << 14) /* Receive fifo Overflow */ -#define I_XU (1 << 15) /* Transmit fifo Underflow */ -#define I_RI (1 << 16) /* Receive Interrupt */ -#define I_BUSPWR (1 << 17) /* SDIO Bus Power Change (rev 9) */ -#define I_XMTDATA_AVAIL (1 << 23) /* bits in fifo */ -#define I_XI (1 << 24) /* Transmit Interrupt */ -#define I_RF_TERM (1 << 25) /* Read Frame Terminate */ -#define I_WF_TERM (1 << 26) /* Write Frame Terminate */ -#define I_PCMCIA_XU (1 << 27) /* PCMCIA Transmit FIFO Underflow */ -#define I_SBINT (1 << 28) /* sbintstatus Interrupt */ -#define I_CHIPACTIVE (1 << 29) /* chip from doze to active state */ -#define I_SRESET (1 << 30) /* CCCR RES interrupt */ -#define I_IOE2 (1U << 31) /* CCCR IOE2 Bit Changed */ -#define I_ERRORS (I_PC | I_PD | I_DE | I_RU | I_RO | I_XU) -#define I_DMA (I_RI | I_XI | I_ERRORS) - -/* corecontrol */ -#define CC_CISRDY (1 << 0) /* CIS Ready */ -#define CC_BPRESEN (1 << 1) /* CCCR RES signal */ -#define CC_F2RDY (1 << 2) /* set CCCR IOR2 bit */ -#define CC_CLRPADSISO (1 << 3) /* clear SDIO pads isolation */ -#define CC_XMTDATAAVAIL_MODE (1 << 4) -#define CC_XMTDATAAVAIL_CTRL (1 << 5) - -/* SDA_FRAMECTRL */ -#define SFC_RF_TERM (1 << 0) /* Read Frame Terminate */ -#define SFC_WF_TERM (1 << 1) /* Write Frame Terminate */ -#define SFC_CRC4WOOS (1 << 2) /* CRC error for write out of sync */ -#define SFC_ABORTALL (1 << 3) /* Abort all in-progress frames */ - -/* - * Software allocation of To SB Mailbox resources - */ - -/* tosbmailbox bits corresponding to intstatus bits */ -#define SMB_NAK (1 << 0) /* Frame NAK */ -#define SMB_INT_ACK (1 << 1) /* Host Interrupt ACK */ -#define SMB_USE_OOB (1 << 2) /* Use OOB Wakeup */ -#define SMB_DEV_INT (1 << 3) /* Miscellaneous Interrupt */ - -/* tosbmailboxdata */ -#define SMB_DATA_VERSION_SHIFT 16 /* host protocol version */ - -/* - * Software allocation of To Host Mailbox resources - */ - -/* intstatus bits */ -#define I_HMB_FC_STATE I_HMB_SW0 /* Flow Control State */ -#define I_HMB_FC_CHANGE I_HMB_SW1 /* Flow Control State Changed */ -#define I_HMB_FRAME_IND I_HMB_SW2 /* Frame Indication */ -#define I_HMB_HOST_INT I_HMB_SW3 /* Miscellaneous Interrupt */ - -/* tohostmailboxdata */ -#define HMB_DATA_NAKHANDLED 1 /* retransmit NAK'd frame */ -#define HMB_DATA_DEVREADY 2 /* talk to host after enable */ -#define HMB_DATA_FC 4 /* per prio flowcontrol update flag */ -#define HMB_DATA_FWREADY 8 /* fw ready for protocol activity */ - -#define HMB_DATA_FCDATA_MASK 0xff000000 -#define HMB_DATA_FCDATA_SHIFT 24 - -#define HMB_DATA_VERSION_MASK 0x00ff0000 -#define HMB_DATA_VERSION_SHIFT 16 - -/* - * Software-defined protocol header - */ - -/* Current protocol version */ -#define SDPCM_PROT_VERSION 4 - -/* - * Shared structure between dongle and the host. - * The structure contains pointers to trap or assert information. - */ -#define SDPCM_SHARED_VERSION 0x0003 -#define SDPCM_SHARED_VERSION_MASK 0x00FF -#define SDPCM_SHARED_ASSERT_BUILT 0x0100 -#define SDPCM_SHARED_ASSERT 0x0200 -#define SDPCM_SHARED_TRAP 0x0400 - -/* Space for header read, limit for data packets */ -#define MAX_HDR_READ (1 << 6) -#define MAX_RX_DATASZ 2048 - -/* Bump up limit on waiting for HT to account for first startup; - * if the image is doing a CRC calculation before programming the PMU - * for HT availability, it could take a couple hundred ms more, so - * max out at a 1 second (1000000us). - */ -#undef PMU_MAX_TRANSITION_DLY -#define PMU_MAX_TRANSITION_DLY 1000000 - -/* Value for ChipClockCSR during initial setup */ -#define BRCMF_INIT_CLKCTL1 (SBSDIO_FORCE_HW_CLKREQ_OFF | \ - SBSDIO_ALP_AVAIL_REQ) - -/* Flags for SDH calls */ -#define F2SYNC (SDIO_REQ_4BYTE | SDIO_REQ_FIXED) - -#define BRCMF_IDLE_ACTIVE 0 /* Do not request any SD clock change - * when idle - */ -#define BRCMF_IDLE_INTERVAL 1 - -#define KSO_WAIT_US 50 -#define MAX_KSO_ATTEMPTS (PMU_MAX_TRANSITION_DLY/KSO_WAIT_US) - -/* - * Conversion of 802.1D priority to precedence level - */ -static uint prio2prec(u32 prio) -{ - return (prio == PRIO_8021D_NONE || prio == PRIO_8021D_BE) ? - (prio^2) : prio; -} - -#ifdef DEBUG -/* Device console log buffer state */ -struct brcmf_console { - uint count; /* Poll interval msec counter */ - uint log_addr; /* Log struct address (fixed) */ - struct rte_log_le log_le; /* Log struct (host copy) */ - uint bufsize; /* Size of log buffer */ - u8 *buf; /* Log buffer (host copy) */ - uint last; /* Last buffer read index */ -}; - -struct brcmf_trap_info { - __le32 type; - __le32 epc; - __le32 cpsr; - __le32 spsr; - __le32 r0; /* a1 */ - __le32 r1; /* a2 */ - __le32 r2; /* a3 */ - __le32 r3; /* a4 */ - __le32 r4; /* v1 */ - __le32 r5; /* v2 */ - __le32 r6; /* v3 */ - __le32 r7; /* v4 */ - __le32 r8; /* v5 */ - __le32 r9; /* sb/v6 */ - __le32 r10; /* sl/v7 */ - __le32 r11; /* fp/v8 */ - __le32 r12; /* ip */ - __le32 r13; /* sp */ - __le32 r14; /* lr */ - __le32 pc; /* r15 */ -}; -#endif /* DEBUG */ - -struct sdpcm_shared { - u32 flags; - u32 trap_addr; - u32 assert_exp_addr; - u32 assert_file_addr; - u32 assert_line; - u32 console_addr; /* Address of struct rte_console */ - u32 msgtrace_addr; - u8 tag[32]; - u32 brpt_addr; -}; - -struct sdpcm_shared_le { - __le32 flags; - __le32 trap_addr; - __le32 assert_exp_addr; - __le32 assert_file_addr; - __le32 assert_line; - __le32 console_addr; /* Address of struct rte_console */ - __le32 msgtrace_addr; - u8 tag[32]; - __le32 brpt_addr; -}; - -/* dongle SDIO bus specific header info */ -struct brcmf_sdio_hdrinfo { - u8 seq_num; - u8 channel; - u16 len; - u16 len_left; - u16 len_nxtfrm; - u8 dat_offset; - bool lastfrm; - u16 tail_pad; -}; - -/* - * hold counter variables - */ -struct brcmf_sdio_count { - uint intrcount; /* Count of device interrupt callbacks */ - uint lastintrs; /* Count as of last watchdog timer */ - uint pollcnt; /* Count of active polls */ - uint regfails; /* Count of R_REG failures */ - uint tx_sderrs; /* Count of tx attempts with sd errors */ - uint fcqueued; /* Tx packets that got queued */ - uint rxrtx; /* Count of rtx requests (NAK to dongle) */ - uint rx_toolong; /* Receive frames too long to receive */ - uint rxc_errors; /* SDIO errors when reading control frames */ - uint rx_hdrfail; /* SDIO errors on header reads */ - uint rx_badhdr; /* Bad received headers (roosync?) */ - uint rx_badseq; /* Mismatched rx sequence number */ - uint fc_rcvd; /* Number of flow-control events received */ - uint fc_xoff; /* Number which turned on flow-control */ - uint fc_xon; /* Number which turned off flow-control */ - uint rxglomfail; /* Failed deglom attempts */ - uint rxglomframes; /* Number of glom frames (superframes) */ - uint rxglompkts; /* Number of packets from glom frames */ - uint f2rxhdrs; /* Number of header reads */ - uint f2rxdata; /* Number of frame data reads */ - uint f2txdata; /* Number of f2 frame writes */ - uint f1regdata; /* Number of f1 register accesses */ - uint tickcnt; /* Number of watchdog been schedule */ - ulong tx_ctlerrs; /* Err of sending ctrl frames */ - ulong tx_ctlpkts; /* Ctrl frames sent to dongle */ - ulong rx_ctlerrs; /* Err of processing rx ctrl frames */ - ulong rx_ctlpkts; /* Ctrl frames processed from dongle */ - ulong rx_readahead_cnt; /* packets where header read-ahead was used */ -}; - -/* misc chip info needed by some of the routines */ -/* Private data for SDIO bus interaction */ -struct brcmf_sdio { - struct brcmf_sdio_dev *sdiodev; /* sdio device handler */ - struct brcmf_chip *ci; /* Chip info struct */ - - u32 ramsize; /* Size of RAM in SOCRAM (bytes) */ - - u32 hostintmask; /* Copy of Host Interrupt Mask */ - atomic_t intstatus; /* Intstatus bits (events) pending */ - atomic_t fcstate; /* State of dongle flow-control */ - - uint blocksize; /* Block size of SDIO transfers */ - uint roundup; /* Max roundup limit */ - - struct pktq txq; /* Queue length used for flow-control */ - u8 flowcontrol; /* per prio flow control bitmask */ - u8 tx_seq; /* Transmit sequence number (next) */ - u8 tx_max; /* Maximum transmit sequence allowed */ - - u8 *hdrbuf; /* buffer for handling rx frame */ - u8 *rxhdr; /* Header of current rx frame (in hdrbuf) */ - u8 rx_seq; /* Receive sequence number (expected) */ - struct brcmf_sdio_hdrinfo cur_read; - /* info of current read frame */ - bool rxskip; /* Skip receive (awaiting NAK ACK) */ - bool rxpending; /* Data frame pending in dongle */ - - uint rxbound; /* Rx frames to read before resched */ - uint txbound; /* Tx frames to send before resched */ - uint txminmax; - - struct sk_buff *glomd; /* Packet containing glomming descriptor */ - struct sk_buff_head glom; /* Packet list for glommed superframe */ - uint glomerr; /* Glom packet read errors */ - - u8 *rxbuf; /* Buffer for receiving control packets */ - uint rxblen; /* Allocated length of rxbuf */ - u8 *rxctl; /* Aligned pointer into rxbuf */ - u8 *rxctl_orig; /* pointer for freeing rxctl */ - uint rxlen; /* Length of valid data in buffer */ - spinlock_t rxctl_lock; /* protection lock for ctrl frame resources */ - - u8 sdpcm_ver; /* Bus protocol reported by dongle */ - - bool intr; /* Use interrupts */ - bool poll; /* Use polling */ - atomic_t ipend; /* Device interrupt is pending */ - uint spurious; /* Count of spurious interrupts */ - uint pollrate; /* Ticks between device polls */ - uint polltick; /* Tick counter */ - -#ifdef DEBUG - uint console_interval; - struct brcmf_console console; /* Console output polling support */ - uint console_addr; /* Console address from shared struct */ -#endif /* DEBUG */ - - uint clkstate; /* State of sd and backplane clock(s) */ - bool activity; /* Activity flag for clock down */ - s32 idletime; /* Control for activity timeout */ - s32 idlecount; /* Activity timeout counter */ - s32 idleclock; /* How to set bus driver when idle */ - bool rxflow_mode; /* Rx flow control mode */ - bool rxflow; /* Is rx flow control on */ - bool alp_only; /* Don't use HT clock (ALP only) */ - - u8 *ctrl_frame_buf; - u16 ctrl_frame_len; - bool ctrl_frame_stat; - - spinlock_t txq_lock; /* protect bus->txq */ - struct semaphore tx_seq_lock; /* protect bus->tx_seq */ - wait_queue_head_t ctrl_wait; - wait_queue_head_t dcmd_resp_wait; - - struct timer_list timer; - struct completion watchdog_wait; - struct task_struct *watchdog_tsk; - bool wd_timer_valid; - uint save_ms; - - struct workqueue_struct *brcmf_wq; - struct work_struct datawork; - atomic_t dpc_tskcnt; - - bool txoff; /* Transmit flow-controlled */ - struct brcmf_sdio_count sdcnt; - bool sr_enabled; /* SaveRestore enabled */ - bool sleeping; /* SDIO bus sleeping */ - - u8 tx_hdrlen; /* sdio bus header length for tx packet */ - bool txglom; /* host tx glomming enable flag */ - u16 head_align; /* buffer pointer alignment */ - u16 sgentry_align; /* scatter-gather buffer alignment */ -}; - -/* clkstate */ -#define CLK_NONE 0 -#define CLK_SDONLY 1 -#define CLK_PENDING 2 -#define CLK_AVAIL 3 - -#ifdef DEBUG -static int qcount[NUMPRIO]; -#endif /* DEBUG */ - -#define DEFAULT_SDIO_DRIVE_STRENGTH 6 /* in milliamps */ - -#define RETRYCHAN(chan) ((chan) == SDPCM_EVENT_CHANNEL) - -/* Retry count for register access failures */ -static const uint retry_limit = 2; - -/* Limit on rounding up frames */ -static const uint max_roundup = 512; - -#define ALIGNMENT 4 - -enum brcmf_sdio_frmtype { - BRCMF_SDIO_FT_NORMAL, - BRCMF_SDIO_FT_SUPER, - BRCMF_SDIO_FT_SUB, -}; - -#define SDIOD_DRVSTR_KEY(chip, pmu) (((chip) << 16) | (pmu)) - -/* SDIO Pad drive strength to select value mappings */ -struct sdiod_drive_str { - u8 strength; /* Pad Drive Strength in mA */ - u8 sel; /* Chip-specific select value */ -}; - -/* SDIO Drive Strength to sel value table for PMU Rev 11 (1.8V) */ -static const struct sdiod_drive_str sdiod_drvstr_tab1_1v8[] = { - {32, 0x6}, - {26, 0x7}, - {22, 0x4}, - {16, 0x5}, - {12, 0x2}, - {8, 0x3}, - {4, 0x0}, - {0, 0x1} -}; - -/* SDIO Drive Strength to sel value table for PMU Rev 13 (1.8v) */ -static const struct sdiod_drive_str sdiod_drive_strength_tab5_1v8[] = { - {6, 0x7}, - {5, 0x6}, - {4, 0x5}, - {3, 0x4}, - {2, 0x2}, - {1, 0x1}, - {0, 0x0} -}; - -/* SDIO Drive Strength to sel value table for PMU Rev 17 (1.8v) */ -static const struct sdiod_drive_str sdiod_drvstr_tab6_1v8[] = { - {3, 0x3}, - {2, 0x2}, - {1, 0x1}, - {0, 0x0} }; - -/* SDIO Drive Strength to sel value table for 43143 PMU Rev 17 (3.3V) */ -static const struct sdiod_drive_str sdiod_drvstr_tab2_3v3[] = { - {16, 0x7}, - {12, 0x5}, - {8, 0x3}, - {4, 0x1} -}; - -#define BCM43143_FIRMWARE_NAME "brcm/brcmfmac43143-sdio.bin" -#define BCM43143_NVRAM_NAME "brcm/brcmfmac43143-sdio.txt" -#define BCM43241B0_FIRMWARE_NAME "brcm/brcmfmac43241b0-sdio.bin" -#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 BCM4329_FIRMWARE_NAME "brcm/brcmfmac4329-sdio.bin" -#define BCM4329_NVRAM_NAME "brcm/brcmfmac4329-sdio.txt" -#define BCM4330_FIRMWARE_NAME "brcm/brcmfmac4330-sdio.bin" -#define BCM4330_NVRAM_NAME "brcm/brcmfmac4330-sdio.txt" -#define BCM4334_FIRMWARE_NAME "brcm/brcmfmac4334-sdio.bin" -#define BCM4334_NVRAM_NAME "brcm/brcmfmac4334-sdio.txt" -#define BCM4335_FIRMWARE_NAME "brcm/brcmfmac4335-sdio.bin" -#define BCM4335_NVRAM_NAME "brcm/brcmfmac4335-sdio.txt" -#define BCM43362_FIRMWARE_NAME "brcm/brcmfmac43362-sdio.bin" -#define BCM43362_NVRAM_NAME "brcm/brcmfmac43362-sdio.txt" -#define BCM4339_FIRMWARE_NAME "brcm/brcmfmac4339-sdio.bin" -#define BCM4339_NVRAM_NAME "brcm/brcmfmac4339-sdio.txt" -#define BCM4354_FIRMWARE_NAME "brcm/brcmfmac4354-sdio.bin" -#define BCM4354_NVRAM_NAME "brcm/brcmfmac4354-sdio.txt" - -MODULE_FIRMWARE(BCM43143_FIRMWARE_NAME); -MODULE_FIRMWARE(BCM43143_NVRAM_NAME); -MODULE_FIRMWARE(BCM43241B0_FIRMWARE_NAME); -MODULE_FIRMWARE(BCM43241B0_NVRAM_NAME); -MODULE_FIRMWARE(BCM43241B4_FIRMWARE_NAME); -MODULE_FIRMWARE(BCM43241B4_NVRAM_NAME); -MODULE_FIRMWARE(BCM4329_FIRMWARE_NAME); -MODULE_FIRMWARE(BCM4329_NVRAM_NAME); -MODULE_FIRMWARE(BCM4330_FIRMWARE_NAME); -MODULE_FIRMWARE(BCM4330_NVRAM_NAME); -MODULE_FIRMWARE(BCM4334_FIRMWARE_NAME); -MODULE_FIRMWARE(BCM4334_NVRAM_NAME); -MODULE_FIRMWARE(BCM4335_FIRMWARE_NAME); -MODULE_FIRMWARE(BCM4335_NVRAM_NAME); -MODULE_FIRMWARE(BCM43362_FIRMWARE_NAME); -MODULE_FIRMWARE(BCM43362_NVRAM_NAME); -MODULE_FIRMWARE(BCM4339_FIRMWARE_NAME); -MODULE_FIRMWARE(BCM4339_NVRAM_NAME); -MODULE_FIRMWARE(BCM4354_FIRMWARE_NAME); -MODULE_FIRMWARE(BCM4354_NVRAM_NAME); - -struct brcmf_firmware_names { - u32 chipid; - u32 revmsk; - const char *bin; - const char *nv; -}; - -enum brcmf_firmware_type { - BRCMF_FIRMWARE_BIN, - BRCMF_FIRMWARE_NVRAM -}; - -#define BRCMF_FIRMWARE_NVRAM(name) \ - name ## _FIRMWARE_NAME, name ## _NVRAM_NAME - -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_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) }, - { BRCM_CC_4335_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4335) }, - { BRCM_CC_43362_CHIP_ID, 0xFFFFFFFE, BRCMF_FIRMWARE_NVRAM(BCM43362) }, - { BRCM_CC_4339_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4339) }, - { BRCM_CC_4354_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4354) } -}; - -static int brcmf_sdio_get_fwnames(struct brcmf_chip *ci, - struct brcmf_sdio_dev *sdiodev) -{ - int i; - uint fw_len, nv_len; - char end; - - for (i = 0; i < ARRAY_SIZE(brcmf_fwname_data); i++) { - if (brcmf_fwname_data[i].chipid == ci->chip && - brcmf_fwname_data[i].revmsk & BIT(ci->chiprev)) - break; - } - - if (i == ARRAY_SIZE(brcmf_fwname_data)) { - brcmf_err("Unknown chipid %d [%d]\n", ci->chip, ci->chiprev); - return -ENODEV; - } - - fw_len = sizeof(sdiodev->fw_name) - 1; - nv_len = sizeof(sdiodev->nvram_name) - 1; - /* check if firmware path is provided by module parameter */ - if (brcmf_firmware_path[0] != '\0') { - strncpy(sdiodev->fw_name, brcmf_firmware_path, fw_len); - strncpy(sdiodev->nvram_name, brcmf_firmware_path, nv_len); - fw_len -= strlen(sdiodev->fw_name); - nv_len -= strlen(sdiodev->nvram_name); - - end = brcmf_firmware_path[strlen(brcmf_firmware_path) - 1]; - if (end != '/') { - strncat(sdiodev->fw_name, "/", fw_len); - strncat(sdiodev->nvram_name, "/", nv_len); - fw_len--; - nv_len--; - } - } - strncat(sdiodev->fw_name, brcmf_fwname_data[i].bin, fw_len); - strncat(sdiodev->nvram_name, brcmf_fwname_data[i].nv, nv_len); - - return 0; -} - -static void pkt_align(struct sk_buff *p, int len, int align) -{ - uint datalign; - datalign = (unsigned long)(p->data); - datalign = roundup(datalign, (align)) - datalign; - if (datalign) - skb_pull(p, datalign); - __skb_trim(p, len); -} - -/* To check if there's window offered */ -static bool data_ok(struct brcmf_sdio *bus) -{ - return (u8)(bus->tx_max - bus->tx_seq) != 0 && - ((u8)(bus->tx_max - bus->tx_seq) & 0x80) == 0; -} - -/* - * Reads a register in the SDIO hardware block. This block occupies a series of - * adresses on the 32 bit backplane bus. - */ -static int r_sdreg32(struct brcmf_sdio *bus, u32 *regvar, u32 offset) -{ - struct brcmf_core *core; - int ret; - - core = brcmf_chip_get_core(bus->ci, BCMA_CORE_SDIO_DEV); - *regvar = brcmf_sdiod_regrl(bus->sdiodev, core->base + offset, &ret); - - return ret; -} - -static int w_sdreg32(struct brcmf_sdio *bus, u32 regval, u32 reg_offset) -{ - struct brcmf_core *core; - int ret; - - core = brcmf_chip_get_core(bus->ci, BCMA_CORE_SDIO_DEV); - brcmf_sdiod_regwl(bus->sdiodev, core->base + reg_offset, regval, &ret); - - return ret; -} - -static int -brcmf_sdio_kso_control(struct brcmf_sdio *bus, bool on) -{ - u8 wr_val = 0, rd_val, cmp_val, bmask; - int err = 0; - int try_cnt = 0; - - brcmf_dbg(TRACE, "Enter: on=%d\n", on); - - wr_val = (on << SBSDIO_FUNC1_SLEEPCSR_KSO_SHIFT); - /* 1st KSO write goes to AOS wake up core if device is asleep */ - brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_SLEEPCSR, - wr_val, &err); - - if (on) { - /* device WAKEUP through KSO: - * write bit 0 & read back until - * both bits 0 (kso bit) & 1 (dev on status) are set - */ - cmp_val = SBSDIO_FUNC1_SLEEPCSR_KSO_MASK | - SBSDIO_FUNC1_SLEEPCSR_DEVON_MASK; - bmask = cmp_val; - usleep_range(2000, 3000); - } else { - /* Put device to sleep, turn off KSO */ - cmp_val = 0; - /* only check for bit0, bit1(dev on status) may not - * get cleared right away - */ - bmask = SBSDIO_FUNC1_SLEEPCSR_KSO_MASK; - } - - do { - /* reliable KSO bit set/clr: - * the sdiod sleep write access is synced to PMU 32khz clk - * just one write attempt may fail, - * read it back until it matches written value - */ - rd_val = brcmf_sdiod_regrb(bus->sdiodev, SBSDIO_FUNC1_SLEEPCSR, - &err); - if (((rd_val & bmask) == cmp_val) && !err) - break; - - udelay(KSO_WAIT_US); - brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_SLEEPCSR, - wr_val, &err); - } while (try_cnt++ < MAX_KSO_ATTEMPTS); - - if (try_cnt > 2) - brcmf_dbg(SDIO, "try_cnt=%d rd_val=0x%x err=%d\n", try_cnt, - rd_val, err); - - if (try_cnt > MAX_KSO_ATTEMPTS) - brcmf_err("max tries: rd_val=0x%x err=%d\n", rd_val, err); - - return err; -} - -#define HOSTINTMASK (I_HMB_SW_MASK | I_CHIPACTIVE) - -/* Turn backplane clock on or off */ -static int brcmf_sdio_htclk(struct brcmf_sdio *bus, bool on, bool pendok) -{ - int err; - u8 clkctl, clkreq, devctl; - unsigned long timeout; - - brcmf_dbg(SDIO, "Enter\n"); - - clkctl = 0; - - if (bus->sr_enabled) { - bus->clkstate = (on ? CLK_AVAIL : CLK_SDONLY); - return 0; - } - - if (on) { - /* Request HT Avail */ - clkreq = - bus->alp_only ? SBSDIO_ALP_AVAIL_REQ : SBSDIO_HT_AVAIL_REQ; - - brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, - clkreq, &err); - if (err) { - brcmf_err("HT Avail request error: %d\n", err); - return -EBADE; - } - - /* Check current status */ - clkctl = brcmf_sdiod_regrb(bus->sdiodev, - SBSDIO_FUNC1_CHIPCLKCSR, &err); - if (err) { - brcmf_err("HT Avail read error: %d\n", err); - return -EBADE; - } - - /* Go to pending and await interrupt if appropriate */ - if (!SBSDIO_CLKAV(clkctl, bus->alp_only) && pendok) { - /* Allow only clock-available interrupt */ - devctl = brcmf_sdiod_regrb(bus->sdiodev, - SBSDIO_DEVICE_CTL, &err); - if (err) { - brcmf_err("Devctl error setting CA: %d\n", - err); - return -EBADE; - } - - devctl |= SBSDIO_DEVCTL_CA_INT_ONLY; - brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_DEVICE_CTL, - devctl, &err); - brcmf_dbg(SDIO, "CLKCTL: set PENDING\n"); - bus->clkstate = CLK_PENDING; - - return 0; - } else if (bus->clkstate == CLK_PENDING) { - /* Cancel CA-only interrupt filter */ - devctl = brcmf_sdiod_regrb(bus->sdiodev, - SBSDIO_DEVICE_CTL, &err); - devctl &= ~SBSDIO_DEVCTL_CA_INT_ONLY; - brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_DEVICE_CTL, - devctl, &err); - } - - /* Otherwise, wait here (polling) for HT Avail */ - timeout = jiffies + - msecs_to_jiffies(PMU_MAX_TRANSITION_DLY/1000); - while (!SBSDIO_CLKAV(clkctl, bus->alp_only)) { - clkctl = brcmf_sdiod_regrb(bus->sdiodev, - SBSDIO_FUNC1_CHIPCLKCSR, - &err); - if (time_after(jiffies, timeout)) - break; - else - usleep_range(5000, 10000); - } - if (err) { - brcmf_err("HT Avail request error: %d\n", err); - return -EBADE; - } - if (!SBSDIO_CLKAV(clkctl, bus->alp_only)) { - brcmf_err("HT Avail timeout (%d): clkctl 0x%02x\n", - PMU_MAX_TRANSITION_DLY, clkctl); - return -EBADE; - } - - /* Mark clock available */ - bus->clkstate = CLK_AVAIL; - brcmf_dbg(SDIO, "CLKCTL: turned ON\n"); - -#if defined(DEBUG) - if (!bus->alp_only) { - if (SBSDIO_ALPONLY(clkctl)) - brcmf_err("HT Clock should be on\n"); - } -#endif /* defined (DEBUG) */ - - } else { - clkreq = 0; - - if (bus->clkstate == CLK_PENDING) { - /* Cancel CA-only interrupt filter */ - devctl = brcmf_sdiod_regrb(bus->sdiodev, - SBSDIO_DEVICE_CTL, &err); - devctl &= ~SBSDIO_DEVCTL_CA_INT_ONLY; - brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_DEVICE_CTL, - devctl, &err); - } - - bus->clkstate = CLK_SDONLY; - brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, - clkreq, &err); - brcmf_dbg(SDIO, "CLKCTL: turned OFF\n"); - if (err) { - brcmf_err("Failed access turning clock off: %d\n", - err); - return -EBADE; - } - } - return 0; -} - -/* Change idle/active SD state */ -static int brcmf_sdio_sdclk(struct brcmf_sdio *bus, bool on) -{ - brcmf_dbg(SDIO, "Enter\n"); - - if (on) - bus->clkstate = CLK_SDONLY; - else - bus->clkstate = CLK_NONE; - - return 0; -} - -/* Transition SD and backplane clock readiness */ -static int brcmf_sdio_clkctl(struct brcmf_sdio *bus, uint target, bool pendok) -{ -#ifdef DEBUG - uint oldstate = bus->clkstate; -#endif /* DEBUG */ - - brcmf_dbg(SDIO, "Enter\n"); - - /* Early exit if we're already there */ - if (bus->clkstate == target) { - if (target == CLK_AVAIL) { - brcmf_sdio_wd_timer(bus, BRCMF_WD_POLL_MS); - bus->activity = true; - } - return 0; - } - - switch (target) { - case CLK_AVAIL: - /* Make sure SD clock is available */ - if (bus->clkstate == CLK_NONE) - brcmf_sdio_sdclk(bus, true); - /* Now request HT Avail on the backplane */ - brcmf_sdio_htclk(bus, true, pendok); - brcmf_sdio_wd_timer(bus, BRCMF_WD_POLL_MS); - bus->activity = true; - break; - - case CLK_SDONLY: - /* Remove HT request, or bring up SD clock */ - if (bus->clkstate == CLK_NONE) - brcmf_sdio_sdclk(bus, true); - else if (bus->clkstate == CLK_AVAIL) - brcmf_sdio_htclk(bus, false, false); - else - brcmf_err("request for %d -> %d\n", - bus->clkstate, target); - brcmf_sdio_wd_timer(bus, BRCMF_WD_POLL_MS); - break; - - case CLK_NONE: - /* Make sure to remove HT request */ - if (bus->clkstate == CLK_AVAIL) - brcmf_sdio_htclk(bus, false, false); - /* Now remove the SD clock */ - brcmf_sdio_sdclk(bus, false); - brcmf_sdio_wd_timer(bus, 0); - break; - } -#ifdef DEBUG - brcmf_dbg(SDIO, "%d -> %d\n", oldstate, bus->clkstate); -#endif /* DEBUG */ - - return 0; -} - -static int -brcmf_sdio_bus_sleep(struct brcmf_sdio *bus, bool sleep, bool pendok) -{ - int err = 0; - u8 clkcsr; - - brcmf_dbg(SDIO, "Enter: request %s currently %s\n", - (sleep ? "SLEEP" : "WAKE"), - (bus->sleeping ? "SLEEP" : "WAKE")); - - /* If SR is enabled control bus state with KSO */ - if (bus->sr_enabled) { - /* Done if we're already in the requested state */ - if (sleep == bus->sleeping) - goto end; - - /* Going to sleep */ - if (sleep) { - /* Don't sleep if something is pending */ - if (atomic_read(&bus->intstatus) || - atomic_read(&bus->ipend) > 0 || - (!atomic_read(&bus->fcstate) && - brcmu_pktq_mlen(&bus->txq, ~bus->flowcontrol) && - data_ok(bus))) { - err = -EBUSY; - goto done; - } - - clkcsr = brcmf_sdiod_regrb(bus->sdiodev, - SBSDIO_FUNC1_CHIPCLKCSR, - &err); - if ((clkcsr & SBSDIO_CSR_MASK) == 0) { - brcmf_dbg(SDIO, "no clock, set ALP\n"); - brcmf_sdiod_regwb(bus->sdiodev, - SBSDIO_FUNC1_CHIPCLKCSR, - SBSDIO_ALP_AVAIL_REQ, &err); - } - err = brcmf_sdio_kso_control(bus, false); - /* disable watchdog */ - if (!err) - brcmf_sdio_wd_timer(bus, 0); - } else { - bus->idlecount = 0; - err = brcmf_sdio_kso_control(bus, true); - } - if (!err) { - /* Change state */ - bus->sleeping = sleep; - brcmf_dbg(SDIO, "new state %s\n", - (sleep ? "SLEEP" : "WAKE")); - } else { - brcmf_err("error while changing bus sleep state %d\n", - err); - goto done; - } - } - -end: - /* control clocks */ - if (sleep) { - if (!bus->sr_enabled) - brcmf_sdio_clkctl(bus, CLK_NONE, pendok); - } else { - brcmf_sdio_clkctl(bus, CLK_AVAIL, pendok); - } -done: - brcmf_dbg(SDIO, "Exit: err=%d\n", err); - return err; - -} - -#ifdef DEBUG -static inline bool brcmf_sdio_valid_shared_address(u32 addr) -{ - return !(addr == 0 || ((~addr >> 16) & 0xffff) == (addr & 0xffff)); -} - -static int brcmf_sdio_readshared(struct brcmf_sdio *bus, - struct sdpcm_shared *sh) -{ - u32 addr; - int rv; - u32 shaddr = 0; - struct sdpcm_shared_le sh_le; - __le32 addr_le; - - shaddr = bus->ci->rambase + bus->ramsize - 4; - - /* - * Read last word in socram to determine - * address of sdpcm_shared structure - */ - sdio_claim_host(bus->sdiodev->func[1]); - brcmf_sdio_bus_sleep(bus, false, false); - rv = brcmf_sdiod_ramrw(bus->sdiodev, false, shaddr, (u8 *)&addr_le, 4); - sdio_release_host(bus->sdiodev->func[1]); - if (rv < 0) - return rv; - - addr = le32_to_cpu(addr_le); - - brcmf_dbg(SDIO, "sdpcm_shared address 0x%08X\n", addr); - - /* - * Check if addr is valid. - * NVRAM length at the end of memory should have been overwritten. - */ - if (!brcmf_sdio_valid_shared_address(addr)) { - brcmf_err("invalid sdpcm_shared address 0x%08X\n", - addr); - return -EINVAL; - } - - /* Read hndrte_shared structure */ - rv = brcmf_sdiod_ramrw(bus->sdiodev, false, addr, (u8 *)&sh_le, - sizeof(struct sdpcm_shared_le)); - if (rv < 0) - return rv; - - /* Endianness */ - sh->flags = le32_to_cpu(sh_le.flags); - sh->trap_addr = le32_to_cpu(sh_le.trap_addr); - sh->assert_exp_addr = le32_to_cpu(sh_le.assert_exp_addr); - sh->assert_file_addr = le32_to_cpu(sh_le.assert_file_addr); - sh->assert_line = le32_to_cpu(sh_le.assert_line); - sh->console_addr = le32_to_cpu(sh_le.console_addr); - sh->msgtrace_addr = le32_to_cpu(sh_le.msgtrace_addr); - - if ((sh->flags & SDPCM_SHARED_VERSION_MASK) > SDPCM_SHARED_VERSION) { - brcmf_err("sdpcm shared version unsupported: dhd %d dongle %d\n", - SDPCM_SHARED_VERSION, - sh->flags & SDPCM_SHARED_VERSION_MASK); - return -EPROTO; - } - - return 0; -} - -static void brcmf_sdio_get_console_addr(struct brcmf_sdio *bus) -{ - struct sdpcm_shared sh; - - if (brcmf_sdio_readshared(bus, &sh) == 0) - bus->console_addr = sh.console_addr; -} -#else -static void brcmf_sdio_get_console_addr(struct brcmf_sdio *bus) -{ -} -#endif /* DEBUG */ - -static u32 brcmf_sdio_hostmail(struct brcmf_sdio *bus) -{ - u32 intstatus = 0; - u32 hmb_data; - u8 fcbits; - int ret; - - brcmf_dbg(SDIO, "Enter\n"); - - /* Read mailbox data and ack that we did so */ - ret = r_sdreg32(bus, &hmb_data, - offsetof(struct sdpcmd_regs, tohostmailboxdata)); - - if (ret == 0) - w_sdreg32(bus, SMB_INT_ACK, - offsetof(struct sdpcmd_regs, tosbmailbox)); - bus->sdcnt.f1regdata += 2; - - /* Dongle recomposed rx frames, accept them again */ - if (hmb_data & HMB_DATA_NAKHANDLED) { - brcmf_dbg(SDIO, "Dongle reports NAK handled, expect rtx of %d\n", - bus->rx_seq); - if (!bus->rxskip) - brcmf_err("unexpected NAKHANDLED!\n"); - - bus->rxskip = false; - intstatus |= I_HMB_FRAME_IND; - } - - /* - * DEVREADY does not occur with gSPI. - */ - if (hmb_data & (HMB_DATA_DEVREADY | HMB_DATA_FWREADY)) { - bus->sdpcm_ver = - (hmb_data & HMB_DATA_VERSION_MASK) >> - HMB_DATA_VERSION_SHIFT; - if (bus->sdpcm_ver != SDPCM_PROT_VERSION) - brcmf_err("Version mismatch, dongle reports %d, " - "expecting %d\n", - bus->sdpcm_ver, SDPCM_PROT_VERSION); - else - brcmf_dbg(SDIO, "Dongle ready, protocol version %d\n", - bus->sdpcm_ver); - - /* - * Retrieve console state address now that firmware should have - * updated it. - */ - brcmf_sdio_get_console_addr(bus); - } - - /* - * Flow Control has been moved into the RX headers and this out of band - * method isn't used any more. - * remaining backward compatible with older dongles. - */ - if (hmb_data & HMB_DATA_FC) { - fcbits = (hmb_data & HMB_DATA_FCDATA_MASK) >> - HMB_DATA_FCDATA_SHIFT; - - if (fcbits & ~bus->flowcontrol) - bus->sdcnt.fc_xoff++; - - if (bus->flowcontrol & ~fcbits) - bus->sdcnt.fc_xon++; - - bus->sdcnt.fc_rcvd++; - bus->flowcontrol = fcbits; - } - - /* Shouldn't be any others */ - if (hmb_data & ~(HMB_DATA_DEVREADY | - HMB_DATA_NAKHANDLED | - HMB_DATA_FC | - HMB_DATA_FWREADY | - HMB_DATA_FCDATA_MASK | HMB_DATA_VERSION_MASK)) - brcmf_err("Unknown mailbox data content: 0x%02x\n", - hmb_data); - - return intstatus; -} - -static void brcmf_sdio_rxfail(struct brcmf_sdio *bus, bool abort, bool rtx) -{ - uint retries = 0; - u16 lastrbc; - u8 hi, lo; - int err; - - brcmf_err("%sterminate frame%s\n", - abort ? "abort command, " : "", - rtx ? ", send NAK" : ""); - - if (abort) - brcmf_sdiod_abort(bus->sdiodev, SDIO_FUNC_2); - - brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_FRAMECTRL, - SFC_RF_TERM, &err); - bus->sdcnt.f1regdata++; - - /* Wait until the packet has been flushed (device/FIFO stable) */ - for (lastrbc = retries = 0xffff; retries > 0; retries--) { - hi = brcmf_sdiod_regrb(bus->sdiodev, - SBSDIO_FUNC1_RFRAMEBCHI, &err); - lo = brcmf_sdiod_regrb(bus->sdiodev, - SBSDIO_FUNC1_RFRAMEBCLO, &err); - bus->sdcnt.f1regdata += 2; - - if ((hi == 0) && (lo == 0)) - break; - - if ((hi > (lastrbc >> 8)) && (lo > (lastrbc & 0x00ff))) { - brcmf_err("count growing: last 0x%04x now 0x%04x\n", - lastrbc, (hi << 8) + lo); - } - lastrbc = (hi << 8) + lo; - } - - if (!retries) - brcmf_err("count never zeroed: last 0x%04x\n", lastrbc); - else - brcmf_dbg(SDIO, "flush took %d iterations\n", 0xffff - retries); - - if (rtx) { - bus->sdcnt.rxrtx++; - err = w_sdreg32(bus, SMB_NAK, - offsetof(struct sdpcmd_regs, tosbmailbox)); - - bus->sdcnt.f1regdata++; - if (err == 0) - bus->rxskip = true; - } - - /* Clear partial in any case */ - bus->cur_read.len = 0; -} - -static void brcmf_sdio_txfail(struct brcmf_sdio *bus) -{ - struct brcmf_sdio_dev *sdiodev = bus->sdiodev; - u8 i, hi, lo; - - /* On failure, abort the command and terminate the frame */ - brcmf_err("sdio error, abort command and terminate frame\n"); - bus->sdcnt.tx_sderrs++; - - brcmf_sdiod_abort(sdiodev, SDIO_FUNC_2); - brcmf_sdiod_regwb(sdiodev, SBSDIO_FUNC1_FRAMECTRL, SFC_WF_TERM, NULL); - bus->sdcnt.f1regdata++; - - for (i = 0; i < 3; i++) { - hi = brcmf_sdiod_regrb(sdiodev, SBSDIO_FUNC1_WFRAMEBCHI, NULL); - lo = brcmf_sdiod_regrb(sdiodev, SBSDIO_FUNC1_WFRAMEBCLO, NULL); - bus->sdcnt.f1regdata += 2; - if ((hi == 0) && (lo == 0)) - break; - } -} - -/* return total length of buffer chain */ -static uint brcmf_sdio_glom_len(struct brcmf_sdio *bus) -{ - struct sk_buff *p; - uint total; - - total = 0; - skb_queue_walk(&bus->glom, p) - total += p->len; - return total; -} - -static void brcmf_sdio_free_glom(struct brcmf_sdio *bus) -{ - struct sk_buff *cur, *next; - - skb_queue_walk_safe(&bus->glom, cur, next) { - skb_unlink(cur, &bus->glom); - brcmu_pkt_buf_free_skb(cur); - } -} - -/** - * brcmfmac sdio bus specific header - * This is the lowest layer header wrapped on the packets transmitted between - * host and WiFi dongle which contains information needed for SDIO core and - * firmware - * - * It consists of 3 parts: hardware header, hardware extension header and - * software header - * hardware header (frame tag) - 4 bytes - * Byte 0~1: Frame length - * Byte 2~3: Checksum, bit-wise inverse of frame length - * hardware extension header - 8 bytes - * Tx glom mode only, N/A for Rx or normal Tx - * Byte 0~1: Packet length excluding hw frame tag - * Byte 2: Reserved - * Byte 3: Frame flags, bit 0: last frame indication - * Byte 4~5: Reserved - * Byte 6~7: Tail padding length - * software header - 8 bytes - * Byte 0: Rx/Tx sequence number - * Byte 1: 4 MSB Channel number, 4 LSB arbitrary flag - * Byte 2: Length of next data frame, reserved for Tx - * Byte 3: Data offset - * Byte 4: Flow control bits, reserved for Tx - * Byte 5: Maximum Sequence number allowed by firmware for Tx, N/A for Tx packet - * Byte 6~7: Reserved - */ -#define SDPCM_HWHDR_LEN 4 -#define SDPCM_HWEXT_LEN 8 -#define SDPCM_SWHDR_LEN 8 -#define SDPCM_HDRLEN (SDPCM_HWHDR_LEN + SDPCM_SWHDR_LEN) -/* software header */ -#define SDPCM_SEQ_MASK 0x000000ff -#define SDPCM_SEQ_WRAP 256 -#define SDPCM_CHANNEL_MASK 0x00000f00 -#define SDPCM_CHANNEL_SHIFT 8 -#define SDPCM_CONTROL_CHANNEL 0 /* Control */ -#define SDPCM_EVENT_CHANNEL 1 /* Asyc Event Indication */ -#define SDPCM_DATA_CHANNEL 2 /* Data Xmit/Recv */ -#define SDPCM_GLOM_CHANNEL 3 /* Coalesced packets */ -#define SDPCM_TEST_CHANNEL 15 /* Test/debug packets */ -#define SDPCM_GLOMDESC(p) (((u8 *)p)[1] & 0x80) -#define SDPCM_NEXTLEN_MASK 0x00ff0000 -#define SDPCM_NEXTLEN_SHIFT 16 -#define SDPCM_DOFFSET_MASK 0xff000000 -#define SDPCM_DOFFSET_SHIFT 24 -#define SDPCM_FCMASK_MASK 0x000000ff -#define SDPCM_WINDOW_MASK 0x0000ff00 -#define SDPCM_WINDOW_SHIFT 8 - -static inline u8 brcmf_sdio_getdatoffset(u8 *swheader) -{ - u32 hdrvalue; - hdrvalue = *(u32 *)swheader; - return (u8)((hdrvalue & SDPCM_DOFFSET_MASK) >> SDPCM_DOFFSET_SHIFT); -} - -static int brcmf_sdio_hdparse(struct brcmf_sdio *bus, u8 *header, - struct brcmf_sdio_hdrinfo *rd, - enum brcmf_sdio_frmtype type) -{ - u16 len, checksum; - u8 rx_seq, fc, tx_seq_max; - u32 swheader; - - trace_brcmf_sdpcm_hdr(SDPCM_RX, header); - - /* hw header */ - len = get_unaligned_le16(header); - checksum = get_unaligned_le16(header + sizeof(u16)); - /* All zero means no more to read */ - if (!(len | checksum)) { - bus->rxpending = false; - return -ENODATA; - } - if ((u16)(~(len ^ checksum))) { - brcmf_err("HW header checksum error\n"); - bus->sdcnt.rx_badhdr++; - brcmf_sdio_rxfail(bus, false, false); - return -EIO; - } - if (len < SDPCM_HDRLEN) { - brcmf_err("HW header length error\n"); - return -EPROTO; - } - if (type == BRCMF_SDIO_FT_SUPER && - (roundup(len, bus->blocksize) != rd->len)) { - brcmf_err("HW superframe header length error\n"); - return -EPROTO; - } - if (type == BRCMF_SDIO_FT_SUB && len > rd->len) { - brcmf_err("HW subframe header length error\n"); - return -EPROTO; - } - rd->len = len; - - /* software header */ - header += SDPCM_HWHDR_LEN; - swheader = le32_to_cpu(*(__le32 *)header); - if (type == BRCMF_SDIO_FT_SUPER && SDPCM_GLOMDESC(header)) { - brcmf_err("Glom descriptor found in superframe head\n"); - rd->len = 0; - return -EINVAL; - } - rx_seq = (u8)(swheader & SDPCM_SEQ_MASK); - rd->channel = (swheader & SDPCM_CHANNEL_MASK) >> SDPCM_CHANNEL_SHIFT; - if (len > MAX_RX_DATASZ && rd->channel != SDPCM_CONTROL_CHANNEL && - type != BRCMF_SDIO_FT_SUPER) { - brcmf_err("HW header length too long\n"); - bus->sdcnt.rx_toolong++; - brcmf_sdio_rxfail(bus, false, false); - rd->len = 0; - return -EPROTO; - } - if (type == BRCMF_SDIO_FT_SUPER && rd->channel != SDPCM_GLOM_CHANNEL) { - brcmf_err("Wrong channel for superframe\n"); - rd->len = 0; - return -EINVAL; - } - if (type == BRCMF_SDIO_FT_SUB && rd->channel != SDPCM_DATA_CHANNEL && - rd->channel != SDPCM_EVENT_CHANNEL) { - brcmf_err("Wrong channel for subframe\n"); - rd->len = 0; - return -EINVAL; - } - rd->dat_offset = brcmf_sdio_getdatoffset(header); - if (rd->dat_offset < SDPCM_HDRLEN || rd->dat_offset > rd->len) { - brcmf_err("seq %d: bad data offset\n", rx_seq); - bus->sdcnt.rx_badhdr++; - brcmf_sdio_rxfail(bus, false, false); - rd->len = 0; - return -ENXIO; - } - if (rd->seq_num != rx_seq) { - brcmf_err("seq %d: sequence number error, expect %d\n", - rx_seq, rd->seq_num); - bus->sdcnt.rx_badseq++; - rd->seq_num = rx_seq; - } - /* no need to check the reset for subframe */ - if (type == BRCMF_SDIO_FT_SUB) - return 0; - rd->len_nxtfrm = (swheader & SDPCM_NEXTLEN_MASK) >> SDPCM_NEXTLEN_SHIFT; - if (rd->len_nxtfrm << 4 > MAX_RX_DATASZ) { - /* only warm for NON glom packet */ - if (rd->channel != SDPCM_GLOM_CHANNEL) - brcmf_err("seq %d: next length error\n", rx_seq); - rd->len_nxtfrm = 0; - } - swheader = le32_to_cpu(*(__le32 *)(header + 4)); - fc = swheader & SDPCM_FCMASK_MASK; - if (bus->flowcontrol != fc) { - if (~bus->flowcontrol & fc) - bus->sdcnt.fc_xoff++; - if (bus->flowcontrol & ~fc) - bus->sdcnt.fc_xon++; - bus->sdcnt.fc_rcvd++; - bus->flowcontrol = fc; - } - tx_seq_max = (swheader & SDPCM_WINDOW_MASK) >> SDPCM_WINDOW_SHIFT; - if ((u8)(tx_seq_max - bus->tx_seq) > 0x40) { - brcmf_err("seq %d: max tx seq number error\n", rx_seq); - tx_seq_max = bus->tx_seq + 2; - } - bus->tx_max = tx_seq_max; - - return 0; -} - -static inline void brcmf_sdio_update_hwhdr(u8 *header, u16 frm_length) -{ - *(__le16 *)header = cpu_to_le16(frm_length); - *(((__le16 *)header) + 1) = cpu_to_le16(~frm_length); -} - -static void brcmf_sdio_hdpack(struct brcmf_sdio *bus, u8 *header, - struct brcmf_sdio_hdrinfo *hd_info) -{ - u32 hdrval; - u8 hdr_offset; - - brcmf_sdio_update_hwhdr(header, hd_info->len); - hdr_offset = SDPCM_HWHDR_LEN; - - if (bus->txglom) { - hdrval = (hd_info->len - hdr_offset) | (hd_info->lastfrm << 24); - *((__le32 *)(header + hdr_offset)) = cpu_to_le32(hdrval); - hdrval = (u16)hd_info->tail_pad << 16; - *(((__le32 *)(header + hdr_offset)) + 1) = cpu_to_le32(hdrval); - hdr_offset += SDPCM_HWEXT_LEN; - } - - hdrval = hd_info->seq_num; - hdrval |= (hd_info->channel << SDPCM_CHANNEL_SHIFT) & - SDPCM_CHANNEL_MASK; - hdrval |= (hd_info->dat_offset << SDPCM_DOFFSET_SHIFT) & - SDPCM_DOFFSET_MASK; - *((__le32 *)(header + hdr_offset)) = cpu_to_le32(hdrval); - *(((__le32 *)(header + hdr_offset)) + 1) = 0; - trace_brcmf_sdpcm_hdr(SDPCM_TX + !!(bus->txglom), header); -} - -static u8 brcmf_sdio_rxglom(struct brcmf_sdio *bus, u8 rxseq) -{ - u16 dlen, totlen; - u8 *dptr, num = 0; - u16 sublen; - struct sk_buff *pfirst, *pnext; - - int errcode; - u8 doff, sfdoff; - - struct brcmf_sdio_hdrinfo rd_new; - - /* If packets, issue read(s) and send up packet chain */ - /* Return sequence numbers consumed? */ - - brcmf_dbg(SDIO, "start: glomd %p glom %p\n", - bus->glomd, skb_peek(&bus->glom)); - - /* If there's a descriptor, generate the packet chain */ - if (bus->glomd) { - pfirst = pnext = NULL; - dlen = (u16) (bus->glomd->len); - dptr = bus->glomd->data; - if (!dlen || (dlen & 1)) { - brcmf_err("bad glomd len(%d), ignore descriptor\n", - dlen); - dlen = 0; - } - - for (totlen = num = 0; dlen; num++) { - /* Get (and move past) next length */ - sublen = get_unaligned_le16(dptr); - dlen -= sizeof(u16); - dptr += sizeof(u16); - if ((sublen < SDPCM_HDRLEN) || - ((num == 0) && (sublen < (2 * SDPCM_HDRLEN)))) { - brcmf_err("descriptor len %d bad: %d\n", - num, sublen); - pnext = NULL; - break; - } - if (sublen % bus->sgentry_align) { - brcmf_err("sublen %d not multiple of %d\n", - sublen, bus->sgentry_align); - } - totlen += sublen; - - /* For last frame, adjust read len so total - is a block multiple */ - if (!dlen) { - sublen += - (roundup(totlen, bus->blocksize) - totlen); - totlen = roundup(totlen, bus->blocksize); - } - - /* Allocate/chain packet for next subframe */ - pnext = brcmu_pkt_buf_get_skb(sublen + bus->sgentry_align); - if (pnext == NULL) { - brcmf_err("bcm_pkt_buf_get_skb failed, num %d len %d\n", - num, sublen); - break; - } - skb_queue_tail(&bus->glom, pnext); - - /* Adhere to start alignment requirements */ - pkt_align(pnext, sublen, bus->sgentry_align); - } - - /* If all allocations succeeded, save packet chain - in bus structure */ - if (pnext) { - brcmf_dbg(GLOM, "allocated %d-byte packet chain for %d subframes\n", - totlen, num); - if (BRCMF_GLOM_ON() && bus->cur_read.len && - totlen != bus->cur_read.len) { - brcmf_dbg(GLOM, "glomdesc mismatch: nextlen %d glomdesc %d rxseq %d\n", - bus->cur_read.len, totlen, rxseq); - } - pfirst = pnext = NULL; - } else { - brcmf_sdio_free_glom(bus); - num = 0; - } - - /* Done with descriptor packet */ - brcmu_pkt_buf_free_skb(bus->glomd); - bus->glomd = NULL; - bus->cur_read.len = 0; - } - - /* Ok -- either we just generated a packet chain, - or had one from before */ - if (!skb_queue_empty(&bus->glom)) { - if (BRCMF_GLOM_ON()) { - brcmf_dbg(GLOM, "try superframe read, packet chain:\n"); - skb_queue_walk(&bus->glom, pnext) { - brcmf_dbg(GLOM, " %p: %p len 0x%04x (%d)\n", - pnext, (u8 *) (pnext->data), - pnext->len, pnext->len); - } - } - - pfirst = skb_peek(&bus->glom); - dlen = (u16) brcmf_sdio_glom_len(bus); - - /* Do an SDIO read for the superframe. Configurable iovar to - * read directly into the chained packet, or allocate a large - * packet and and copy into the chain. - */ - sdio_claim_host(bus->sdiodev->func[1]); - errcode = brcmf_sdiod_recv_chain(bus->sdiodev, - &bus->glom, dlen); - sdio_release_host(bus->sdiodev->func[1]); - bus->sdcnt.f2rxdata++; - - /* On failure, kill the superframe, allow a couple retries */ - if (errcode < 0) { - brcmf_err("glom read of %d bytes failed: %d\n", - dlen, errcode); - - sdio_claim_host(bus->sdiodev->func[1]); - if (bus->glomerr++ < 3) { - brcmf_sdio_rxfail(bus, true, true); - } else { - bus->glomerr = 0; - brcmf_sdio_rxfail(bus, true, false); - bus->sdcnt.rxglomfail++; - brcmf_sdio_free_glom(bus); - } - sdio_release_host(bus->sdiodev->func[1]); - return 0; - } - - brcmf_dbg_hex_dump(BRCMF_GLOM_ON(), - pfirst->data, min_t(int, pfirst->len, 48), - "SUPERFRAME:\n"); - - rd_new.seq_num = rxseq; - rd_new.len = dlen; - sdio_claim_host(bus->sdiodev->func[1]); - errcode = brcmf_sdio_hdparse(bus, pfirst->data, &rd_new, - BRCMF_SDIO_FT_SUPER); - sdio_release_host(bus->sdiodev->func[1]); - bus->cur_read.len = rd_new.len_nxtfrm << 4; - - /* Remove superframe header, remember offset */ - skb_pull(pfirst, rd_new.dat_offset); - sfdoff = rd_new.dat_offset; - num = 0; - - /* Validate all the subframe headers */ - skb_queue_walk(&bus->glom, pnext) { - /* leave when invalid subframe is found */ - if (errcode) - break; - - rd_new.len = pnext->len; - rd_new.seq_num = rxseq++; - sdio_claim_host(bus->sdiodev->func[1]); - errcode = brcmf_sdio_hdparse(bus, pnext->data, &rd_new, - BRCMF_SDIO_FT_SUB); - sdio_release_host(bus->sdiodev->func[1]); - brcmf_dbg_hex_dump(BRCMF_GLOM_ON(), - pnext->data, 32, "subframe:\n"); - - num++; - } - - if (errcode) { - /* Terminate frame on error, request - a couple retries */ - sdio_claim_host(bus->sdiodev->func[1]); - if (bus->glomerr++ < 3) { - /* Restore superframe header space */ - skb_push(pfirst, sfdoff); - brcmf_sdio_rxfail(bus, true, true); - } else { - bus->glomerr = 0; - brcmf_sdio_rxfail(bus, true, false); - bus->sdcnt.rxglomfail++; - brcmf_sdio_free_glom(bus); - } - sdio_release_host(bus->sdiodev->func[1]); - bus->cur_read.len = 0; - return 0; - } - - /* Basic SD framing looks ok - process each packet (header) */ - - skb_queue_walk_safe(&bus->glom, pfirst, pnext) { - dptr = (u8 *) (pfirst->data); - sublen = get_unaligned_le16(dptr); - doff = brcmf_sdio_getdatoffset(&dptr[SDPCM_HWHDR_LEN]); - - brcmf_dbg_hex_dump(BRCMF_BYTES_ON() && BRCMF_DATA_ON(), - dptr, pfirst->len, - "Rx Subframe Data:\n"); - - __skb_trim(pfirst, sublen); - skb_pull(pfirst, doff); - - if (pfirst->len == 0) { - skb_unlink(pfirst, &bus->glom); - brcmu_pkt_buf_free_skb(pfirst); - continue; - } - - brcmf_dbg_hex_dump(BRCMF_GLOM_ON(), - pfirst->data, - min_t(int, pfirst->len, 32), - "subframe %d to stack, %p (%p/%d) nxt/lnk %p/%p\n", - bus->glom.qlen, pfirst, pfirst->data, - pfirst->len, pfirst->next, - pfirst->prev); - skb_unlink(pfirst, &bus->glom); - brcmf_rx_frame(bus->sdiodev->dev, pfirst); - bus->sdcnt.rxglompkts++; - } - - bus->sdcnt.rxglomframes++; - } - return num; -} - -static int brcmf_sdio_dcmd_resp_wait(struct brcmf_sdio *bus, uint *condition, - bool *pending) -{ - DECLARE_WAITQUEUE(wait, current); - int timeout = msecs_to_jiffies(DCMD_RESP_TIMEOUT); - - /* Wait until control frame is available */ - add_wait_queue(&bus->dcmd_resp_wait, &wait); - set_current_state(TASK_INTERRUPTIBLE); - - while (!(*condition) && (!signal_pending(current) && timeout)) - timeout = schedule_timeout(timeout); - - if (signal_pending(current)) - *pending = true; - - set_current_state(TASK_RUNNING); - remove_wait_queue(&bus->dcmd_resp_wait, &wait); - - return timeout; -} - -static int brcmf_sdio_dcmd_resp_wake(struct brcmf_sdio *bus) -{ - if (waitqueue_active(&bus->dcmd_resp_wait)) - wake_up_interruptible(&bus->dcmd_resp_wait); - - return 0; -} -static void -brcmf_sdio_read_control(struct brcmf_sdio *bus, u8 *hdr, uint len, uint doff) -{ - uint rdlen, pad; - u8 *buf = NULL, *rbuf; - int sdret; - - brcmf_dbg(TRACE, "Enter\n"); - - if (bus->rxblen) - buf = vzalloc(bus->rxblen); - if (!buf) - goto done; - - rbuf = bus->rxbuf; - pad = ((unsigned long)rbuf % bus->head_align); - if (pad) - rbuf += (bus->head_align - pad); - - /* Copy the already-read portion over */ - memcpy(buf, hdr, BRCMF_FIRSTREAD); - if (len <= BRCMF_FIRSTREAD) - goto gotpkt; - - /* Raise rdlen to next SDIO block to avoid tail command */ - rdlen = len - BRCMF_FIRSTREAD; - if (bus->roundup && bus->blocksize && (rdlen > bus->blocksize)) { - pad = bus->blocksize - (rdlen % bus->blocksize); - if ((pad <= bus->roundup) && (pad < bus->blocksize) && - ((len + pad) < bus->sdiodev->bus_if->maxctl)) - rdlen += pad; - } else if (rdlen % bus->head_align) { - rdlen += bus->head_align - (rdlen % bus->head_align); - } - - /* Drop if the read is too big or it exceeds our maximum */ - if ((rdlen + BRCMF_FIRSTREAD) > bus->sdiodev->bus_if->maxctl) { - brcmf_err("%d-byte control read exceeds %d-byte buffer\n", - rdlen, bus->sdiodev->bus_if->maxctl); - brcmf_sdio_rxfail(bus, false, false); - goto done; - } - - if ((len - doff) > bus->sdiodev->bus_if->maxctl) { - brcmf_err("%d-byte ctl frame (%d-byte ctl data) exceeds %d-byte limit\n", - len, len - doff, bus->sdiodev->bus_if->maxctl); - bus->sdcnt.rx_toolong++; - brcmf_sdio_rxfail(bus, false, false); - goto done; - } - - /* Read remain of frame body */ - sdret = brcmf_sdiod_recv_buf(bus->sdiodev, rbuf, rdlen); - bus->sdcnt.f2rxdata++; - - /* Control frame failures need retransmission */ - if (sdret < 0) { - brcmf_err("read %d control bytes failed: %d\n", - rdlen, sdret); - bus->sdcnt.rxc_errors++; - brcmf_sdio_rxfail(bus, true, true); - goto done; - } else - memcpy(buf + BRCMF_FIRSTREAD, rbuf, rdlen); - -gotpkt: - - brcmf_dbg_hex_dump(BRCMF_BYTES_ON() && BRCMF_CTL_ON(), - buf, len, "RxCtrl:\n"); - - /* Point to valid data and indicate its length */ - spin_lock_bh(&bus->rxctl_lock); - if (bus->rxctl) { - brcmf_err("last control frame is being processed.\n"); - spin_unlock_bh(&bus->rxctl_lock); - vfree(buf); - goto done; - } - bus->rxctl = buf + doff; - bus->rxctl_orig = buf; - bus->rxlen = len - doff; - spin_unlock_bh(&bus->rxctl_lock); - -done: - /* Awake any waiters */ - brcmf_sdio_dcmd_resp_wake(bus); -} - -/* Pad read to blocksize for efficiency */ -static void brcmf_sdio_pad(struct brcmf_sdio *bus, u16 *pad, u16 *rdlen) -{ - if (bus->roundup && bus->blocksize && *rdlen > bus->blocksize) { - *pad = bus->blocksize - (*rdlen % bus->blocksize); - if (*pad <= bus->roundup && *pad < bus->blocksize && - *rdlen + *pad + BRCMF_FIRSTREAD < MAX_RX_DATASZ) - *rdlen += *pad; - } else if (*rdlen % bus->head_align) { - *rdlen += bus->head_align - (*rdlen % bus->head_align); - } -} - -static uint brcmf_sdio_readframes(struct brcmf_sdio *bus, uint maxframes) -{ - struct sk_buff *pkt; /* Packet for event or data frames */ - u16 pad; /* Number of pad bytes to read */ - uint rxleft = 0; /* Remaining number of frames allowed */ - int ret; /* Return code from calls */ - uint rxcount = 0; /* Total frames read */ - struct brcmf_sdio_hdrinfo *rd = &bus->cur_read, rd_new; - u8 head_read = 0; - - brcmf_dbg(TRACE, "Enter\n"); - - /* Not finished unless we encounter no more frames indication */ - bus->rxpending = true; - - for (rd->seq_num = bus->rx_seq, rxleft = maxframes; - !bus->rxskip && rxleft && brcmf_bus_ready(bus->sdiodev->bus_if); - rd->seq_num++, rxleft--) { - - /* Handle glomming separately */ - if (bus->glomd || !skb_queue_empty(&bus->glom)) { - u8 cnt; - brcmf_dbg(GLOM, "calling rxglom: glomd %p, glom %p\n", - bus->glomd, skb_peek(&bus->glom)); - cnt = brcmf_sdio_rxglom(bus, rd->seq_num); - brcmf_dbg(GLOM, "rxglom returned %d\n", cnt); - rd->seq_num += cnt - 1; - rxleft = (rxleft > cnt) ? (rxleft - cnt) : 1; - continue; - } - - rd->len_left = rd->len; - /* read header first for unknow frame length */ - sdio_claim_host(bus->sdiodev->func[1]); - if (!rd->len) { - ret = brcmf_sdiod_recv_buf(bus->sdiodev, - bus->rxhdr, BRCMF_FIRSTREAD); - bus->sdcnt.f2rxhdrs++; - if (ret < 0) { - brcmf_err("RXHEADER FAILED: %d\n", - ret); - bus->sdcnt.rx_hdrfail++; - brcmf_sdio_rxfail(bus, true, true); - sdio_release_host(bus->sdiodev->func[1]); - continue; - } - - brcmf_dbg_hex_dump(BRCMF_BYTES_ON() || BRCMF_HDRS_ON(), - bus->rxhdr, SDPCM_HDRLEN, - "RxHdr:\n"); - - if (brcmf_sdio_hdparse(bus, bus->rxhdr, rd, - BRCMF_SDIO_FT_NORMAL)) { - sdio_release_host(bus->sdiodev->func[1]); - if (!bus->rxpending) - break; - else - continue; - } - - if (rd->channel == SDPCM_CONTROL_CHANNEL) { - brcmf_sdio_read_control(bus, bus->rxhdr, - rd->len, - rd->dat_offset); - /* prepare the descriptor for the next read */ - rd->len = rd->len_nxtfrm << 4; - rd->len_nxtfrm = 0; - /* treat all packet as event if we don't know */ - rd->channel = SDPCM_EVENT_CHANNEL; - sdio_release_host(bus->sdiodev->func[1]); - continue; - } - rd->len_left = rd->len > BRCMF_FIRSTREAD ? - rd->len - BRCMF_FIRSTREAD : 0; - head_read = BRCMF_FIRSTREAD; - } - - brcmf_sdio_pad(bus, &pad, &rd->len_left); - - pkt = brcmu_pkt_buf_get_skb(rd->len_left + head_read + - bus->head_align); - if (!pkt) { - /* Give up on data, request rtx of events */ - brcmf_err("brcmu_pkt_buf_get_skb failed\n"); - brcmf_sdio_rxfail(bus, false, - RETRYCHAN(rd->channel)); - sdio_release_host(bus->sdiodev->func[1]); - continue; - } - skb_pull(pkt, head_read); - pkt_align(pkt, rd->len_left, bus->head_align); - - ret = brcmf_sdiod_recv_pkt(bus->sdiodev, pkt); - bus->sdcnt.f2rxdata++; - sdio_release_host(bus->sdiodev->func[1]); - - if (ret < 0) { - brcmf_err("read %d bytes from channel %d failed: %d\n", - rd->len, rd->channel, ret); - brcmu_pkt_buf_free_skb(pkt); - sdio_claim_host(bus->sdiodev->func[1]); - brcmf_sdio_rxfail(bus, true, - RETRYCHAN(rd->channel)); - sdio_release_host(bus->sdiodev->func[1]); - continue; - } - - if (head_read) { - skb_push(pkt, head_read); - memcpy(pkt->data, bus->rxhdr, head_read); - head_read = 0; - } else { - memcpy(bus->rxhdr, pkt->data, SDPCM_HDRLEN); - rd_new.seq_num = rd->seq_num; - sdio_claim_host(bus->sdiodev->func[1]); - if (brcmf_sdio_hdparse(bus, bus->rxhdr, &rd_new, - BRCMF_SDIO_FT_NORMAL)) { - rd->len = 0; - brcmu_pkt_buf_free_skb(pkt); - } - bus->sdcnt.rx_readahead_cnt++; - if (rd->len != roundup(rd_new.len, 16)) { - brcmf_err("frame length mismatch:read %d, should be %d\n", - rd->len, - roundup(rd_new.len, 16) >> 4); - rd->len = 0; - brcmf_sdio_rxfail(bus, true, true); - sdio_release_host(bus->sdiodev->func[1]); - brcmu_pkt_buf_free_skb(pkt); - continue; - } - sdio_release_host(bus->sdiodev->func[1]); - rd->len_nxtfrm = rd_new.len_nxtfrm; - rd->channel = rd_new.channel; - rd->dat_offset = rd_new.dat_offset; - - brcmf_dbg_hex_dump(!(BRCMF_BYTES_ON() && - BRCMF_DATA_ON()) && - BRCMF_HDRS_ON(), - bus->rxhdr, SDPCM_HDRLEN, - "RxHdr:\n"); - - if (rd_new.channel == SDPCM_CONTROL_CHANNEL) { - brcmf_err("readahead on control packet %d?\n", - rd_new.seq_num); - /* Force retry w/normal header read */ - rd->len = 0; - sdio_claim_host(bus->sdiodev->func[1]); - brcmf_sdio_rxfail(bus, false, true); - sdio_release_host(bus->sdiodev->func[1]); - brcmu_pkt_buf_free_skb(pkt); - continue; - } - } - - brcmf_dbg_hex_dump(BRCMF_BYTES_ON() && BRCMF_DATA_ON(), - pkt->data, rd->len, "Rx Data:\n"); - - /* Save superframe descriptor and allocate packet frame */ - if (rd->channel == SDPCM_GLOM_CHANNEL) { - if (SDPCM_GLOMDESC(&bus->rxhdr[SDPCM_HWHDR_LEN])) { - brcmf_dbg(GLOM, "glom descriptor, %d bytes:\n", - rd->len); - brcmf_dbg_hex_dump(BRCMF_GLOM_ON(), - pkt->data, rd->len, - "Glom Data:\n"); - __skb_trim(pkt, rd->len); - skb_pull(pkt, SDPCM_HDRLEN); - bus->glomd = pkt; - } else { - brcmf_err("%s: glom superframe w/o " - "descriptor!\n", __func__); - sdio_claim_host(bus->sdiodev->func[1]); - brcmf_sdio_rxfail(bus, false, false); - sdio_release_host(bus->sdiodev->func[1]); - } - /* prepare the descriptor for the next read */ - rd->len = rd->len_nxtfrm << 4; - rd->len_nxtfrm = 0; - /* treat all packet as event if we don't know */ - rd->channel = SDPCM_EVENT_CHANNEL; - continue; - } - - /* Fill in packet len and prio, deliver upward */ - __skb_trim(pkt, rd->len); - skb_pull(pkt, rd->dat_offset); - - /* prepare the descriptor for the next read */ - rd->len = rd->len_nxtfrm << 4; - rd->len_nxtfrm = 0; - /* treat all packet as event if we don't know */ - rd->channel = SDPCM_EVENT_CHANNEL; - - if (pkt->len == 0) { - brcmu_pkt_buf_free_skb(pkt); - continue; - } - - brcmf_rx_frame(bus->sdiodev->dev, pkt); - } - - rxcount = maxframes - rxleft; - /* Message if we hit the limit */ - if (!rxleft) - brcmf_dbg(DATA, "hit rx limit of %d frames\n", maxframes); - else - brcmf_dbg(DATA, "processed %d frames\n", rxcount); - /* Back off rxseq if awaiting rtx, update rx_seq */ - if (bus->rxskip) - rd->seq_num--; - bus->rx_seq = rd->seq_num; - - return rxcount; -} - -static void -brcmf_sdio_wait_event_wakeup(struct brcmf_sdio *bus) -{ - if (waitqueue_active(&bus->ctrl_wait)) - wake_up_interruptible(&bus->ctrl_wait); - return; -} - -static int brcmf_sdio_txpkt_hdalign(struct brcmf_sdio *bus, struct sk_buff *pkt) -{ - u16 head_pad; - u8 *dat_buf; - - dat_buf = (u8 *)(pkt->data); - - /* Check head padding */ - head_pad = ((unsigned long)dat_buf % bus->head_align); - if (head_pad) { - if (skb_headroom(pkt) < head_pad) { - bus->sdiodev->bus_if->tx_realloc++; - head_pad = 0; - if (skb_cow(pkt, head_pad)) - return -ENOMEM; - } - skb_push(pkt, head_pad); - dat_buf = (u8 *)(pkt->data); - memset(dat_buf, 0, head_pad + bus->tx_hdrlen); - } - return head_pad; -} - -/** - * struct brcmf_skbuff_cb reserves first two bytes in sk_buff::cb for - * bus layer usage. - */ -/* flag marking a dummy skb added for DMA alignment requirement */ -#define ALIGN_SKB_FLAG 0x8000 -/* bit mask of data length chopped from the previous packet */ -#define ALIGN_SKB_CHOP_LEN_MASK 0x7fff - -static int brcmf_sdio_txpkt_prep_sg(struct brcmf_sdio *bus, - struct sk_buff_head *pktq, - struct sk_buff *pkt, u16 total_len) -{ - struct brcmf_sdio_dev *sdiodev; - struct sk_buff *pkt_pad; - u16 tail_pad, tail_chop, chain_pad; - unsigned int blksize; - bool lastfrm; - int ntail, ret; - - sdiodev = bus->sdiodev; - blksize = sdiodev->func[SDIO_FUNC_2]->cur_blksize; - /* sg entry alignment should be a divisor of block size */ - WARN_ON(blksize % bus->sgentry_align); - - /* Check tail padding */ - lastfrm = skb_queue_is_last(pktq, pkt); - tail_pad = 0; - tail_chop = pkt->len % bus->sgentry_align; - if (tail_chop) - tail_pad = bus->sgentry_align - tail_chop; - chain_pad = (total_len + tail_pad) % blksize; - if (lastfrm && chain_pad) - tail_pad += blksize - chain_pad; - if (skb_tailroom(pkt) < tail_pad && pkt->len > blksize) { - pkt_pad = brcmu_pkt_buf_get_skb(tail_pad + tail_chop + - bus->head_align); - if (pkt_pad == NULL) - return -ENOMEM; - ret = brcmf_sdio_txpkt_hdalign(bus, pkt_pad); - if (unlikely(ret < 0)) { - kfree_skb(pkt_pad); - return ret; - } - memcpy(pkt_pad->data, - pkt->data + pkt->len - tail_chop, - tail_chop); - *(u16 *)(pkt_pad->cb) = ALIGN_SKB_FLAG + tail_chop; - skb_trim(pkt, pkt->len - tail_chop); - skb_trim(pkt_pad, tail_pad + tail_chop); - __skb_queue_after(pktq, pkt, pkt_pad); - } else { - ntail = pkt->data_len + tail_pad - - (pkt->end - pkt->tail); - if (skb_cloned(pkt) || ntail > 0) - if (pskb_expand_head(pkt, 0, ntail, GFP_ATOMIC)) - return -ENOMEM; - if (skb_linearize(pkt)) - return -ENOMEM; - __skb_put(pkt, tail_pad); - } - - return tail_pad; -} - -/** - * brcmf_sdio_txpkt_prep - packet preparation for transmit - * @bus: brcmf_sdio structure pointer - * @pktq: packet list pointer - * @chan: virtual channel to transmit the packet - * - * Processes to be applied to the packet - * - Align data buffer pointer - * - Align data buffer length - * - Prepare header - * Return: negative value if there is error - */ -static int -brcmf_sdio_txpkt_prep(struct brcmf_sdio *bus, struct sk_buff_head *pktq, - uint chan) -{ - u16 head_pad, total_len; - struct sk_buff *pkt_next; - u8 txseq; - int ret; - struct brcmf_sdio_hdrinfo hd_info = {0}; - - txseq = bus->tx_seq; - total_len = 0; - skb_queue_walk(pktq, pkt_next) { - /* alignment packet inserted in previous - * loop cycle can be skipped as it is - * already properly aligned and does not - * need an sdpcm header. - */ - if (*(u16 *)(pkt_next->cb) & ALIGN_SKB_FLAG) - continue; - - /* align packet data pointer */ - ret = brcmf_sdio_txpkt_hdalign(bus, pkt_next); - if (ret < 0) - return ret; - head_pad = (u16)ret; - if (head_pad) - memset(pkt_next->data + bus->tx_hdrlen, 0, head_pad); - - total_len += pkt_next->len; - - hd_info.len = pkt_next->len; - hd_info.lastfrm = skb_queue_is_last(pktq, pkt_next); - if (bus->txglom && pktq->qlen > 1) { - ret = brcmf_sdio_txpkt_prep_sg(bus, pktq, - pkt_next, total_len); - if (ret < 0) - return ret; - hd_info.tail_pad = (u16)ret; - total_len += (u16)ret; - } - - hd_info.channel = chan; - hd_info.dat_offset = head_pad + bus->tx_hdrlen; - hd_info.seq_num = txseq++; - - /* Now fill the header */ - brcmf_sdio_hdpack(bus, pkt_next->data, &hd_info); - - if (BRCMF_BYTES_ON() && - ((BRCMF_CTL_ON() && chan == SDPCM_CONTROL_CHANNEL) || - (BRCMF_DATA_ON() && chan != SDPCM_CONTROL_CHANNEL))) - brcmf_dbg_hex_dump(true, pkt_next->data, hd_info.len, - "Tx Frame:\n"); - else if (BRCMF_HDRS_ON()) - brcmf_dbg_hex_dump(true, pkt_next->data, - head_pad + bus->tx_hdrlen, - "Tx Header:\n"); - } - /* Hardware length tag of the first packet should be total - * length of the chain (including padding) - */ - if (bus->txglom) - brcmf_sdio_update_hwhdr(pktq->next->data, total_len); - return 0; -} - -/** - * brcmf_sdio_txpkt_postp - packet post processing for transmit - * @bus: brcmf_sdio structure pointer - * @pktq: packet list pointer - * - * Processes to be applied to the packet - * - Remove head padding - * - Remove tail padding - */ -static void -brcmf_sdio_txpkt_postp(struct brcmf_sdio *bus, struct sk_buff_head *pktq) -{ - u8 *hdr; - u32 dat_offset; - u16 tail_pad; - u16 dummy_flags, chop_len; - struct sk_buff *pkt_next, *tmp, *pkt_prev; - - skb_queue_walk_safe(pktq, pkt_next, tmp) { - dummy_flags = *(u16 *)(pkt_next->cb); - if (dummy_flags & ALIGN_SKB_FLAG) { - chop_len = dummy_flags & ALIGN_SKB_CHOP_LEN_MASK; - if (chop_len) { - pkt_prev = pkt_next->prev; - skb_put(pkt_prev, chop_len); - } - __skb_unlink(pkt_next, pktq); - brcmu_pkt_buf_free_skb(pkt_next); - } else { - hdr = pkt_next->data + bus->tx_hdrlen - SDPCM_SWHDR_LEN; - dat_offset = le32_to_cpu(*(__le32 *)hdr); - dat_offset = (dat_offset & SDPCM_DOFFSET_MASK) >> - SDPCM_DOFFSET_SHIFT; - skb_pull(pkt_next, dat_offset); - if (bus->txglom) { - tail_pad = le16_to_cpu(*(__le16 *)(hdr - 2)); - skb_trim(pkt_next, pkt_next->len - tail_pad); - } - } - } -} - -/* Writes a HW/SW header into the packet and sends it. */ -/* Assumes: (a) header space already there, (b) caller holds lock */ -static int brcmf_sdio_txpkt(struct brcmf_sdio *bus, struct sk_buff_head *pktq, - uint chan) -{ - int ret; - struct sk_buff *pkt_next, *tmp; - - brcmf_dbg(TRACE, "Enter\n"); - - ret = brcmf_sdio_txpkt_prep(bus, pktq, chan); - if (ret) - goto done; - - sdio_claim_host(bus->sdiodev->func[1]); - ret = brcmf_sdiod_send_pkt(bus->sdiodev, pktq); - bus->sdcnt.f2txdata++; - - if (ret < 0) - brcmf_sdio_txfail(bus); - - sdio_release_host(bus->sdiodev->func[1]); - -done: - brcmf_sdio_txpkt_postp(bus, pktq); - if (ret == 0) - bus->tx_seq = (bus->tx_seq + pktq->qlen) % SDPCM_SEQ_WRAP; - skb_queue_walk_safe(pktq, pkt_next, tmp) { - __skb_unlink(pkt_next, pktq); - brcmf_txcomplete(bus->sdiodev->dev, pkt_next, ret == 0); - } - return ret; -} - -static uint brcmf_sdio_sendfromq(struct brcmf_sdio *bus, uint maxframes) -{ - struct sk_buff *pkt; - struct sk_buff_head pktq; - u32 intstatus = 0; - int ret = 0, prec_out, i; - uint cnt = 0; - u8 tx_prec_map, pkt_num; - - brcmf_dbg(TRACE, "Enter\n"); - - tx_prec_map = ~bus->flowcontrol; - - /* Send frames until the limit or some other event */ - for (cnt = 0; (cnt < maxframes) && data_ok(bus);) { - pkt_num = 1; - if (down_interruptible(&bus->tx_seq_lock)) - return cnt; - if (bus->txglom) - pkt_num = min_t(u8, bus->tx_max - bus->tx_seq, - bus->sdiodev->txglomsz); - pkt_num = min_t(u32, pkt_num, - brcmu_pktq_mlen(&bus->txq, ~bus->flowcontrol)); - __skb_queue_head_init(&pktq); - spin_lock_bh(&bus->txq_lock); - for (i = 0; i < pkt_num; i++) { - pkt = brcmu_pktq_mdeq(&bus->txq, tx_prec_map, - &prec_out); - if (pkt == NULL) - break; - __skb_queue_tail(&pktq, pkt); - } - spin_unlock_bh(&bus->txq_lock); - if (i == 0) { - up(&bus->tx_seq_lock); - break; - } - - ret = brcmf_sdio_txpkt(bus, &pktq, SDPCM_DATA_CHANNEL); - up(&bus->tx_seq_lock); - - cnt += i; - - /* In poll mode, need to check for other events */ - if (!bus->intr) { - /* Check device status, signal pending interrupt */ - sdio_claim_host(bus->sdiodev->func[1]); - ret = r_sdreg32(bus, &intstatus, - offsetof(struct sdpcmd_regs, - intstatus)); - sdio_release_host(bus->sdiodev->func[1]); - bus->sdcnt.f2txdata++; - if (ret != 0) - break; - if (intstatus & bus->hostintmask) - atomic_set(&bus->ipend, 1); - } - } - - /* Deflow-control stack if needed */ - if ((bus->sdiodev->bus_if->state == BRCMF_BUS_DATA) && - bus->txoff && (pktq_len(&bus->txq) < TXLOW)) { - bus->txoff = false; - brcmf_txflowblock(bus->sdiodev->dev, false); - } - - return cnt; -} - -static int brcmf_sdio_tx_ctrlframe(struct brcmf_sdio *bus, u8 *frame, u16 len) -{ - u8 doff; - u16 pad; - uint retries = 0; - struct brcmf_sdio_hdrinfo hd_info = {0}; - int ret; - - brcmf_dbg(TRACE, "Enter\n"); - - /* Back the pointer to make room for bus header */ - frame -= bus->tx_hdrlen; - len += bus->tx_hdrlen; - - /* Add alignment padding (optional for ctl frames) */ - doff = ((unsigned long)frame % bus->head_align); - if (doff) { - frame -= doff; - len += doff; - memset(frame + bus->tx_hdrlen, 0, doff); - } - - /* Round send length to next SDIO block */ - pad = 0; - if (bus->roundup && bus->blocksize && (len > bus->blocksize)) { - pad = bus->blocksize - (len % bus->blocksize); - if ((pad > bus->roundup) || (pad >= bus->blocksize)) - pad = 0; - } else if (len % bus->head_align) { - pad = bus->head_align - (len % bus->head_align); - } - len += pad; - - hd_info.len = len - pad; - hd_info.channel = SDPCM_CONTROL_CHANNEL; - hd_info.dat_offset = doff + bus->tx_hdrlen; - hd_info.seq_num = bus->tx_seq; - hd_info.lastfrm = true; - hd_info.tail_pad = pad; - brcmf_sdio_hdpack(bus, frame, &hd_info); - - if (bus->txglom) - brcmf_sdio_update_hwhdr(frame, len); - - brcmf_dbg_hex_dump(BRCMF_BYTES_ON() && BRCMF_CTL_ON(), - frame, len, "Tx Frame:\n"); - brcmf_dbg_hex_dump(!(BRCMF_BYTES_ON() && BRCMF_CTL_ON()) && - BRCMF_HDRS_ON(), - frame, min_t(u16, len, 16), "TxHdr:\n"); - - do { - ret = brcmf_sdiod_send_buf(bus->sdiodev, frame, len); - - if (ret < 0) - brcmf_sdio_txfail(bus); - else - bus->tx_seq = (bus->tx_seq + 1) % SDPCM_SEQ_WRAP; - } while (ret < 0 && retries++ < TXRETRIES); - - return ret; -} - -static void brcmf_sdio_bus_stop(struct device *dev) -{ - u32 local_hostintmask; - u8 saveclk; - int err; - 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; - - brcmf_dbg(TRACE, "Enter\n"); - - if (bus->watchdog_tsk) { - send_sig(SIGTERM, bus->watchdog_tsk, 1); - kthread_stop(bus->watchdog_tsk); - bus->watchdog_tsk = NULL; - } - - if (bus_if->state == BRCMF_BUS_DOWN) { - sdio_claim_host(sdiodev->func[1]); - - /* Enable clock for device interrupts */ - brcmf_sdio_bus_sleep(bus, false, false); - - /* Disable and clear interrupts at the chip level also */ - w_sdreg32(bus, 0, offsetof(struct sdpcmd_regs, hostintmask)); - local_hostintmask = bus->hostintmask; - bus->hostintmask = 0; - - /* Force backplane clocks to assure F2 interrupt propagates */ - saveclk = brcmf_sdiod_regrb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, - &err); - if (!err) - brcmf_sdiod_regwb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, - (saveclk | SBSDIO_FORCE_HT), &err); - if (err) - brcmf_err("Failed to force clock for F2: err %d\n", - err); - - /* Turn off the bus (F2), free any pending packets */ - brcmf_dbg(INTR, "disable SDIO interrupts\n"); - sdio_disable_func(sdiodev->func[SDIO_FUNC_2]); - - /* Clear any pending interrupts now that F2 is disabled */ - w_sdreg32(bus, local_hostintmask, - offsetof(struct sdpcmd_regs, intstatus)); - - sdio_release_host(sdiodev->func[1]); - } - /* Clear the data packet queues */ - brcmu_pktq_flush(&bus->txq, true, NULL, NULL); - - /* Clear any held glomming stuff */ - if (bus->glomd) - brcmu_pkt_buf_free_skb(bus->glomd); - brcmf_sdio_free_glom(bus); - - /* Clear rx control and wake any waiters */ - spin_lock_bh(&bus->rxctl_lock); - bus->rxlen = 0; - spin_unlock_bh(&bus->rxctl_lock); - brcmf_sdio_dcmd_resp_wake(bus); - - /* Reset some F2 state stuff */ - bus->rxskip = false; - bus->tx_seq = bus->rx_seq = 0; -} - -static inline void brcmf_sdio_clrintr(struct brcmf_sdio *bus) -{ - unsigned long flags; - - if (bus->sdiodev->oob_irq_requested) { - spin_lock_irqsave(&bus->sdiodev->irq_en_lock, flags); - if (!bus->sdiodev->irq_en && !atomic_read(&bus->ipend)) { - enable_irq(bus->sdiodev->pdata->oob_irq_nr); - bus->sdiodev->irq_en = true; - } - spin_unlock_irqrestore(&bus->sdiodev->irq_en_lock, flags); - } -} - -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; - u32 addr; - unsigned long val; - int ret; - - buscore = brcmf_chip_get_core(bus->ci, BCMA_CORE_SDIO_DEV); - addr = buscore->base + offsetof(struct sdpcmd_regs, intstatus); - - val = brcmf_sdiod_regrl(bus->sdiodev, addr, &ret); - bus->sdcnt.f1regdata++; - if (ret != 0) - return ret; - - val &= bus->hostintmask; - atomic_set(&bus->fcstate, !!(val & I_HMB_FC_STATE)); - - /* Clear interrupts */ - if (val) { - brcmf_sdiod_regwl(bus->sdiodev, addr, val, &ret); - bus->sdcnt.f1regdata++; - atomic_orr(val, &bus->intstatus); - } - - return ret; -} - -static void brcmf_sdio_dpc(struct brcmf_sdio *bus) -{ - u32 newstatus = 0; - unsigned long intstatus; - uint txlimit = bus->txbound; /* Tx frames to send before resched */ - uint framecnt; /* Temporary counter of tx/rx frames */ - int err = 0; - - brcmf_dbg(TRACE, "Enter\n"); - - sdio_claim_host(bus->sdiodev->func[1]); - - /* If waiting for HTAVAIL, check status */ - if (!bus->sr_enabled && bus->clkstate == CLK_PENDING) { - u8 clkctl, devctl = 0; - -#ifdef DEBUG - /* Check for inconsistent device control */ - devctl = brcmf_sdiod_regrb(bus->sdiodev, - SBSDIO_DEVICE_CTL, &err); -#endif /* DEBUG */ - - /* Read CSR, if clock on switch to AVAIL, else ignore */ - clkctl = brcmf_sdiod_regrb(bus->sdiodev, - SBSDIO_FUNC1_CHIPCLKCSR, &err); - - brcmf_dbg(SDIO, "DPC: PENDING, devctl 0x%02x clkctl 0x%02x\n", - devctl, clkctl); - - if (SBSDIO_HTAV(clkctl)) { - devctl = brcmf_sdiod_regrb(bus->sdiodev, - SBSDIO_DEVICE_CTL, &err); - devctl &= ~SBSDIO_DEVCTL_CA_INT_ONLY; - brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_DEVICE_CTL, - devctl, &err); - bus->clkstate = CLK_AVAIL; - } - } - - /* Make sure backplane clock is on */ - brcmf_sdio_bus_sleep(bus, false, true); - - /* Pending interrupt indicates new device status */ - if (atomic_read(&bus->ipend) > 0) { - atomic_set(&bus->ipend, 0); - err = brcmf_sdio_intr_rstatus(bus); - } - - /* Start with leftover status bits */ - intstatus = atomic_xchg(&bus->intstatus, 0); - - /* Handle flow-control change: read new state in case our ack - * crossed another change interrupt. If change still set, assume - * FC ON for safety, let next loop through do the debounce. - */ - if (intstatus & I_HMB_FC_CHANGE) { - intstatus &= ~I_HMB_FC_CHANGE; - err = w_sdreg32(bus, I_HMB_FC_CHANGE, - offsetof(struct sdpcmd_regs, intstatus)); - - err = r_sdreg32(bus, &newstatus, - offsetof(struct sdpcmd_regs, intstatus)); - bus->sdcnt.f1regdata += 2; - atomic_set(&bus->fcstate, - !!(newstatus & (I_HMB_FC_STATE | I_HMB_FC_CHANGE))); - intstatus |= (newstatus & bus->hostintmask); - } - - /* Handle host mailbox indication */ - if (intstatus & I_HMB_HOST_INT) { - intstatus &= ~I_HMB_HOST_INT; - intstatus |= brcmf_sdio_hostmail(bus); - } - - sdio_release_host(bus->sdiodev->func[1]); - - /* Generally don't ask for these, can get CRC errors... */ - if (intstatus & I_WR_OOSYNC) { - brcmf_err("Dongle reports WR_OOSYNC\n"); - intstatus &= ~I_WR_OOSYNC; - } - - if (intstatus & I_RD_OOSYNC) { - brcmf_err("Dongle reports RD_OOSYNC\n"); - intstatus &= ~I_RD_OOSYNC; - } - - if (intstatus & I_SBINT) { - brcmf_err("Dongle reports SBINT\n"); - intstatus &= ~I_SBINT; - } - - /* Would be active due to wake-wlan in gSPI */ - if (intstatus & I_CHIPACTIVE) { - brcmf_dbg(INFO, "Dongle reports CHIPACTIVE\n"); - intstatus &= ~I_CHIPACTIVE; - } - - /* Ignore frame indications if rxskip is set */ - if (bus->rxskip) - intstatus &= ~I_HMB_FRAME_IND; - - /* On frame indication, read available frames */ - if ((intstatus & I_HMB_FRAME_IND) && (bus->clkstate == CLK_AVAIL)) { - brcmf_sdio_readframes(bus, bus->rxbound); - if (!bus->rxpending) - intstatus &= ~I_HMB_FRAME_IND; - } - - /* Keep still-pending events for next scheduling */ - if (intstatus) - atomic_orr(intstatus, &bus->intstatus); - - brcmf_sdio_clrintr(bus); - - if (bus->ctrl_frame_stat && (bus->clkstate == CLK_AVAIL) && - (down_interruptible(&bus->tx_seq_lock) == 0)) { - if (data_ok(bus)) { - sdio_claim_host(bus->sdiodev->func[1]); - err = brcmf_sdio_tx_ctrlframe(bus, bus->ctrl_frame_buf, - bus->ctrl_frame_len); - sdio_release_host(bus->sdiodev->func[1]); - - bus->ctrl_frame_stat = false; - brcmf_sdio_wait_event_wakeup(bus); - } - up(&bus->tx_seq_lock); - } - /* Send queued frames (limit 1 if rx may still be pending) */ - if ((bus->clkstate == CLK_AVAIL) && !atomic_read(&bus->fcstate) && - brcmu_pktq_mlen(&bus->txq, ~bus->flowcontrol) && txlimit && - data_ok(bus)) { - framecnt = bus->rxpending ? min(txlimit, bus->txminmax) : - txlimit; - brcmf_sdio_sendfromq(bus, framecnt); - } - - if (!brcmf_bus_ready(bus->sdiodev->bus_if) || (err != 0)) { - brcmf_err("failed backplane access over SDIO, halting operation\n"); - atomic_set(&bus->intstatus, 0); - } else if (atomic_read(&bus->intstatus) || - atomic_read(&bus->ipend) > 0 || - (!atomic_read(&bus->fcstate) && - brcmu_pktq_mlen(&bus->txq, ~bus->flowcontrol) && - data_ok(bus))) { - atomic_inc(&bus->dpc_tskcnt); - } -} - -static struct pktq *brcmf_sdio_bus_gettxq(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->txq; -} - -static int brcmf_sdio_bus_txdata(struct device *dev, struct sk_buff *pkt) -{ - int ret = -EBADE; - uint prec; - 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; - - brcmf_dbg(TRACE, "Enter: pkt: data %p len %d\n", pkt->data, pkt->len); - - /* Add space for the header */ - skb_push(pkt, bus->tx_hdrlen); - /* precondition: IS_ALIGNED((unsigned long)(pkt->data), 2) */ - - prec = prio2prec((pkt->priority & PRIOMASK)); - - /* Check for existing queue, current flow-control, - pending event, or pending clock */ - brcmf_dbg(TRACE, "deferring pktq len %d\n", pktq_len(&bus->txq)); - bus->sdcnt.fcqueued++; - - /* Priority based enq */ - spin_lock_bh(&bus->txq_lock); - /* reset bus_flags in packet cb */ - *(u16 *)(pkt->cb) = 0; - if (!brcmf_c_prec_enq(bus->sdiodev->dev, &bus->txq, pkt, prec)) { - skb_pull(pkt, bus->tx_hdrlen); - brcmf_err("out of bus->txq !!!\n"); - ret = -ENOSR; - } else { - ret = 0; - } - - if (pktq_len(&bus->txq) >= TXHI) { - bus->txoff = true; - brcmf_txflowblock(bus->sdiodev->dev, true); - } - spin_unlock_bh(&bus->txq_lock); - -#ifdef DEBUG - if (pktq_plen(&bus->txq, prec) > qcount[prec]) - qcount[prec] = pktq_plen(&bus->txq, prec); -#endif - - if (atomic_read(&bus->dpc_tskcnt) == 0) { - atomic_inc(&bus->dpc_tskcnt); - queue_work(bus->brcmf_wq, &bus->datawork); - } - - return ret; -} - -#ifdef DEBUG -#define CONSOLE_LINE_MAX 192 - -static int brcmf_sdio_readconsole(struct brcmf_sdio *bus) -{ - struct brcmf_console *c = &bus->console; - u8 line[CONSOLE_LINE_MAX], ch; - u32 n, idx, addr; - int rv; - - /* Don't do anything until FWREADY updates console address */ - if (bus->console_addr == 0) - return 0; - - /* Read console log struct */ - addr = bus->console_addr + offsetof(struct rte_console, log_le); - rv = brcmf_sdiod_ramrw(bus->sdiodev, false, addr, (u8 *)&c->log_le, - sizeof(c->log_le)); - if (rv < 0) - return rv; - - /* Allocate console buffer (one time only) */ - if (c->buf == NULL) { - c->bufsize = le32_to_cpu(c->log_le.buf_size); - c->buf = kmalloc(c->bufsize, GFP_ATOMIC); - if (c->buf == NULL) - return -ENOMEM; - } - - idx = le32_to_cpu(c->log_le.idx); - - /* Protect against corrupt value */ - if (idx > c->bufsize) - return -EBADE; - - /* Skip reading the console buffer if the index pointer - has not moved */ - if (idx == c->last) - return 0; - - /* Read the console buffer */ - addr = le32_to_cpu(c->log_le.buf); - rv = brcmf_sdiod_ramrw(bus->sdiodev, false, addr, c->buf, c->bufsize); - if (rv < 0) - return rv; - - while (c->last != idx) { - for (n = 0; n < CONSOLE_LINE_MAX - 2; n++) { - if (c->last == idx) { - /* This would output a partial line. - * Instead, back up - * the buffer pointer and output this - * line next time around. - */ - if (c->last >= n) - c->last -= n; - else - c->last = c->bufsize - n; - goto break2; - } - ch = c->buf[c->last]; - c->last = (c->last + 1) % c->bufsize; - if (ch == '\n') - break; - line[n] = ch; - } - - if (n > 0) { - if (line[n - 1] == '\r') - n--; - line[n] = 0; - pr_debug("CONSOLE: %s\n", line); - } - } -break2: - - return 0; -} -#endif /* DEBUG */ - -static int -brcmf_sdio_bus_txctl(struct device *dev, unsigned char *msg, uint msglen) -{ - 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 ret = -1; - - brcmf_dbg(TRACE, "Enter\n"); - - if (down_interruptible(&bus->tx_seq_lock)) - return -EINTR; - - if (!data_ok(bus)) { - brcmf_dbg(INFO, "No bus credit bus->tx_max %d, bus->tx_seq %d\n", - bus->tx_max, bus->tx_seq); - up(&bus->tx_seq_lock); - /* Send from dpc */ - bus->ctrl_frame_buf = msg; - bus->ctrl_frame_len = msglen; - bus->ctrl_frame_stat = true; - - wait_event_interruptible_timeout(bus->ctrl_wait, - !bus->ctrl_frame_stat, - msecs_to_jiffies(2000)); - - if (!bus->ctrl_frame_stat) { - brcmf_dbg(SDIO, "ctrl_frame_stat == false\n"); - ret = 0; - } else { - brcmf_dbg(SDIO, "ctrl_frame_stat == true\n"); - bus->ctrl_frame_stat = false; - if (down_interruptible(&bus->tx_seq_lock)) - return -EINTR; - ret = -1; - } - } - if (ret == -1) { - sdio_claim_host(bus->sdiodev->func[1]); - brcmf_sdio_bus_sleep(bus, false, false); - ret = brcmf_sdio_tx_ctrlframe(bus, msg, msglen); - sdio_release_host(bus->sdiodev->func[1]); - up(&bus->tx_seq_lock); - } - - if (ret) - bus->sdcnt.tx_ctlerrs++; - else - bus->sdcnt.tx_ctlpkts++; - - return ret ? -EIO : 0; -} - -#ifdef DEBUG -static int brcmf_sdio_dump_console(struct seq_file *seq, struct brcmf_sdio *bus, - struct sdpcm_shared *sh) -{ - u32 addr, console_ptr, console_size, console_index; - char *conbuf = NULL; - __le32 sh_val; - int rv; - - /* obtain console information from device memory */ - addr = sh->console_addr + offsetof(struct rte_console, log_le); - rv = brcmf_sdiod_ramrw(bus->sdiodev, false, addr, - (u8 *)&sh_val, sizeof(u32)); - if (rv < 0) - return rv; - console_ptr = le32_to_cpu(sh_val); - - addr = sh->console_addr + offsetof(struct rte_console, log_le.buf_size); - rv = brcmf_sdiod_ramrw(bus->sdiodev, false, addr, - (u8 *)&sh_val, sizeof(u32)); - if (rv < 0) - return rv; - console_size = le32_to_cpu(sh_val); - - addr = sh->console_addr + offsetof(struct rte_console, log_le.idx); - rv = brcmf_sdiod_ramrw(bus->sdiodev, false, addr, - (u8 *)&sh_val, sizeof(u32)); - if (rv < 0) - return rv; - console_index = le32_to_cpu(sh_val); - - /* allocate buffer for console data */ - if (console_size <= CONSOLE_BUFFER_MAX) - conbuf = vzalloc(console_size+1); - - if (!conbuf) - return -ENOMEM; - - /* obtain the console data from device */ - conbuf[console_size] = '\0'; - rv = brcmf_sdiod_ramrw(bus->sdiodev, false, console_ptr, (u8 *)conbuf, - console_size); - if (rv < 0) - goto done; - - rv = seq_write(seq, conbuf + console_index, - console_size - console_index); - if (rv < 0) - goto done; - - if (console_index > 0) - rv = seq_write(seq, conbuf, console_index - 1); - -done: - vfree(conbuf); - return rv; -} - -static int brcmf_sdio_trap_info(struct seq_file *seq, struct brcmf_sdio *bus, - struct sdpcm_shared *sh) -{ - int error; - struct brcmf_trap_info tr; - - if ((sh->flags & SDPCM_SHARED_TRAP) == 0) { - brcmf_dbg(INFO, "no trap in firmware\n"); - return 0; - } - - error = brcmf_sdiod_ramrw(bus->sdiodev, false, sh->trap_addr, (u8 *)&tr, - sizeof(struct brcmf_trap_info)); - if (error < 0) - return error; - - seq_printf(seq, - "dongle trap info: type 0x%x @ epc 0x%08x\n" - " cpsr 0x%08x spsr 0x%08x sp 0x%08x\n" - " lr 0x%08x pc 0x%08x offset 0x%x\n" - " r0 0x%08x r1 0x%08x r2 0x%08x r3 0x%08x\n" - " r4 0x%08x r5 0x%08x r6 0x%08x r7 0x%08x\n", - le32_to_cpu(tr.type), le32_to_cpu(tr.epc), - le32_to_cpu(tr.cpsr), le32_to_cpu(tr.spsr), - le32_to_cpu(tr.r13), le32_to_cpu(tr.r14), - le32_to_cpu(tr.pc), sh->trap_addr, - le32_to_cpu(tr.r0), le32_to_cpu(tr.r1), - le32_to_cpu(tr.r2), le32_to_cpu(tr.r3), - le32_to_cpu(tr.r4), le32_to_cpu(tr.r5), - le32_to_cpu(tr.r6), le32_to_cpu(tr.r7)); - - return 0; -} - -static int brcmf_sdio_assert_info(struct seq_file *seq, struct brcmf_sdio *bus, - struct sdpcm_shared *sh) -{ - int error = 0; - char file[80] = "?"; - char expr[80] = ""; - - if ((sh->flags & SDPCM_SHARED_ASSERT_BUILT) == 0) { - brcmf_dbg(INFO, "firmware not built with -assert\n"); - return 0; - } else if ((sh->flags & SDPCM_SHARED_ASSERT) == 0) { - brcmf_dbg(INFO, "no assert in dongle\n"); - return 0; - } - - sdio_claim_host(bus->sdiodev->func[1]); - if (sh->assert_file_addr != 0) { - error = brcmf_sdiod_ramrw(bus->sdiodev, false, - sh->assert_file_addr, (u8 *)file, 80); - if (error < 0) - return error; - } - if (sh->assert_exp_addr != 0) { - error = brcmf_sdiod_ramrw(bus->sdiodev, false, - sh->assert_exp_addr, (u8 *)expr, 80); - if (error < 0) - return error; - } - sdio_release_host(bus->sdiodev->func[1]); - - seq_printf(seq, "dongle assert: %s:%d: assert(%s)\n", - file, sh->assert_line, expr); - return 0; -} - -static int brcmf_sdio_checkdied(struct brcmf_sdio *bus) -{ - int error; - struct sdpcm_shared sh; - - error = brcmf_sdio_readshared(bus, &sh); - - if (error < 0) - return error; - - if ((sh.flags & SDPCM_SHARED_ASSERT_BUILT) == 0) - brcmf_dbg(INFO, "firmware not built with -assert\n"); - else if (sh.flags & SDPCM_SHARED_ASSERT) - brcmf_err("assertion in dongle\n"); - - if (sh.flags & SDPCM_SHARED_TRAP) - brcmf_err("firmware trap in dongle\n"); - - return 0; -} - -static int brcmf_sdio_died_dump(struct seq_file *seq, struct brcmf_sdio *bus) -{ - int error = 0; - struct sdpcm_shared sh; - - error = brcmf_sdio_readshared(bus, &sh); - if (error < 0) - goto done; - - error = brcmf_sdio_assert_info(seq, bus, &sh); - if (error < 0) - goto done; - - error = brcmf_sdio_trap_info(seq, bus, &sh); - if (error < 0) - goto done; - - error = brcmf_sdio_dump_console(seq, bus, &sh); - -done: - return error; -} - -static int brcmf_sdio_forensic_read(struct seq_file *seq, void *data) -{ - struct brcmf_bus *bus_if = dev_get_drvdata(seq->private); - struct brcmf_sdio *bus = bus_if->bus_priv.sdio->bus; - - return brcmf_sdio_died_dump(seq, bus); -} - -static int brcmf_debugfs_sdio_count_read(struct seq_file *seq, void *data) -{ - struct brcmf_bus *bus_if = dev_get_drvdata(seq->private); - struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio; - struct brcmf_sdio_count *sdcnt = &sdiodev->bus->sdcnt; - - seq_printf(seq, - "intrcount: %u\nlastintrs: %u\n" - "pollcnt: %u\nregfails: %u\n" - "tx_sderrs: %u\nfcqueued: %u\n" - "rxrtx: %u\nrx_toolong: %u\n" - "rxc_errors: %u\nrx_hdrfail: %u\n" - "rx_badhdr: %u\nrx_badseq: %u\n" - "fc_rcvd: %u\nfc_xoff: %u\n" - "fc_xon: %u\nrxglomfail: %u\n" - "rxglomframes: %u\nrxglompkts: %u\n" - "f2rxhdrs: %u\nf2rxdata: %u\n" - "f2txdata: %u\nf1regdata: %u\n" - "tickcnt: %u\ntx_ctlerrs: %lu\n" - "tx_ctlpkts: %lu\nrx_ctlerrs: %lu\n" - "rx_ctlpkts: %lu\nrx_readahead: %lu\n", - sdcnt->intrcount, sdcnt->lastintrs, - sdcnt->pollcnt, sdcnt->regfails, - sdcnt->tx_sderrs, sdcnt->fcqueued, - sdcnt->rxrtx, sdcnt->rx_toolong, - sdcnt->rxc_errors, sdcnt->rx_hdrfail, - sdcnt->rx_badhdr, sdcnt->rx_badseq, - sdcnt->fc_rcvd, sdcnt->fc_xoff, - sdcnt->fc_xon, sdcnt->rxglomfail, - sdcnt->rxglomframes, sdcnt->rxglompkts, - sdcnt->f2rxhdrs, sdcnt->f2rxdata, - sdcnt->f2txdata, sdcnt->f1regdata, - sdcnt->tickcnt, sdcnt->tx_ctlerrs, - sdcnt->tx_ctlpkts, sdcnt->rx_ctlerrs, - sdcnt->rx_ctlpkts, sdcnt->rx_readahead_cnt); - - return 0; -} - -static void brcmf_sdio_debugfs_create(struct brcmf_sdio *bus) -{ - struct brcmf_pub *drvr = bus->sdiodev->bus_if->drvr; - struct dentry *dentry = brcmf_debugfs_get_devdir(drvr); - - if (IS_ERR_OR_NULL(dentry)) - return; - - brcmf_debugfs_add_entry(drvr, "forensics", brcmf_sdio_forensic_read); - brcmf_debugfs_add_entry(drvr, "counters", - brcmf_debugfs_sdio_count_read); - debugfs_create_u32("console_interval", 0644, dentry, - &bus->console_interval); -} -#else -static int brcmf_sdio_checkdied(struct brcmf_sdio *bus) -{ - return 0; -} - -static void brcmf_sdio_debugfs_create(struct brcmf_sdio *bus) -{ -} -#endif /* DEBUG */ - -static int -brcmf_sdio_bus_rxctl(struct device *dev, unsigned char *msg, uint msglen) -{ - int timeleft; - uint rxlen = 0; - bool pending; - u8 *buf; - 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; - - brcmf_dbg(TRACE, "Enter\n"); - - /* Wait until control frame is available */ - timeleft = brcmf_sdio_dcmd_resp_wait(bus, &bus->rxlen, &pending); - - spin_lock_bh(&bus->rxctl_lock); - rxlen = bus->rxlen; - memcpy(msg, bus->rxctl, min(msglen, rxlen)); - bus->rxctl = NULL; - buf = bus->rxctl_orig; - bus->rxctl_orig = NULL; - bus->rxlen = 0; - spin_unlock_bh(&bus->rxctl_lock); - vfree(buf); - - if (rxlen) { - brcmf_dbg(CTL, "resumed on rxctl frame, got %d expected %d\n", - rxlen, msglen); - } else if (timeleft == 0) { - brcmf_err("resumed on timeout\n"); - brcmf_sdio_checkdied(bus); - } else if (pending) { - brcmf_dbg(CTL, "cancelled\n"); - return -ERESTARTSYS; - } else { - brcmf_dbg(CTL, "resumed for unknown reason?\n"); - brcmf_sdio_checkdied(bus); - } - - if (rxlen) - bus->sdcnt.rx_ctlpkts++; - else - bus->sdcnt.rx_ctlerrs++; - - return rxlen ? (int)rxlen : -ETIMEDOUT; -} - -#ifdef DEBUG -static bool -brcmf_sdio_verifymemory(struct brcmf_sdio_dev *sdiodev, u32 ram_addr, - u8 *ram_data, uint ram_sz) -{ - char *ram_cmp; - int err; - bool ret = true; - int address; - int offset; - int len; - - /* read back and verify */ - brcmf_dbg(INFO, "Compare RAM dl & ul at 0x%08x; size=%d\n", ram_addr, - ram_sz); - ram_cmp = kmalloc(MEMBLOCK, GFP_KERNEL); - /* do not proceed while no memory but */ - if (!ram_cmp) - return true; - - address = ram_addr; - offset = 0; - while (offset < ram_sz) { - len = ((offset + MEMBLOCK) < ram_sz) ? MEMBLOCK : - ram_sz - offset; - err = brcmf_sdiod_ramrw(sdiodev, false, address, ram_cmp, len); - if (err) { - brcmf_err("error %d on reading %d membytes at 0x%08x\n", - err, len, address); - ret = false; - break; - } else if (memcmp(ram_cmp, &ram_data[offset], len)) { - brcmf_err("Downloaded RAM image is corrupted, block offset is %d, len is %d\n", - offset, len); - ret = false; - break; - } - offset += len; - address += len; - } - - kfree(ram_cmp); - - return ret; -} -#else /* DEBUG */ -static bool -brcmf_sdio_verifymemory(struct brcmf_sdio_dev *sdiodev, u32 ram_addr, - u8 *ram_data, uint ram_sz) -{ - return true; -} -#endif /* DEBUG */ - -static int brcmf_sdio_download_code_file(struct brcmf_sdio *bus, - const struct firmware *fw) -{ - int err; - - brcmf_dbg(TRACE, "Enter\n"); - - err = brcmf_sdiod_ramrw(bus->sdiodev, true, bus->ci->rambase, - (u8 *)fw->data, fw->size); - if (err) - brcmf_err("error %d on writing %d membytes at 0x%08x\n", - err, (int)fw->size, bus->ci->rambase); - else if (!brcmf_sdio_verifymemory(bus->sdiodev, bus->ci->rambase, - (u8 *)fw->data, fw->size)) - err = -EIO; - - return err; -} - -static int brcmf_sdio_download_nvram(struct brcmf_sdio *bus, - void *vars, u32 varsz) -{ - int address; - int err; - - brcmf_dbg(TRACE, "Enter\n"); - - address = bus->ci->ramsize - varsz + bus->ci->rambase; - err = brcmf_sdiod_ramrw(bus->sdiodev, true, address, vars, varsz); - if (err) - brcmf_err("error %d on writing %d nvram bytes at 0x%08x\n", - err, varsz, address); - else if (!brcmf_sdio_verifymemory(bus->sdiodev, address, vars, varsz)) - err = -EIO; - - return err; -} - -static int brcmf_sdio_download_firmware(struct brcmf_sdio *bus, - const struct firmware *fw, - void *nvram, u32 nvlen) -{ - int bcmerror = -EFAULT; - u32 rstvec; - - sdio_claim_host(bus->sdiodev->func[1]); - brcmf_sdio_clkctl(bus, CLK_AVAIL, false); - - /* Keep arm in reset */ - brcmf_chip_enter_download(bus->ci); - - rstvec = get_unaligned_le32(fw->data); - brcmf_dbg(SDIO, "firmware rstvec: %x\n", rstvec); - - bcmerror = brcmf_sdio_download_code_file(bus, fw); - release_firmware(fw); - if (bcmerror) { - brcmf_err("dongle image file download failed\n"); - brcmf_fw_nvram_free(nvram); - goto err; - } - - bcmerror = brcmf_sdio_download_nvram(bus, nvram, nvlen); - brcmf_fw_nvram_free(nvram); - if (bcmerror) { - brcmf_err("dongle nvram file download failed\n"); - goto err; - } - - /* Take arm out of reset */ - if (!brcmf_chip_exit_download(bus->ci, rstvec)) { - brcmf_err("error getting out of ARM core reset\n"); - goto err; - } - - /* Allow HT Clock now that the ARM is running. */ - brcmf_bus_change_state(bus->sdiodev->bus_if, BRCMF_BUS_LOAD); - bcmerror = 0; - -err: - brcmf_sdio_clkctl(bus, CLK_SDONLY, false); - sdio_release_host(bus->sdiodev->func[1]); - return bcmerror; -} - -static void brcmf_sdio_sr_init(struct brcmf_sdio *bus) -{ - int err = 0; - u8 val; - - brcmf_dbg(TRACE, "Enter\n"); - - val = brcmf_sdiod_regrb(bus->sdiodev, SBSDIO_FUNC1_WAKEUPCTRL, &err); - if (err) { - brcmf_err("error reading SBSDIO_FUNC1_WAKEUPCTRL\n"); - return; - } - - val |= 1 << SBSDIO_FUNC1_WCTRL_HTWAIT_SHIFT; - brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_WAKEUPCTRL, val, &err); - if (err) { - brcmf_err("error writing SBSDIO_FUNC1_WAKEUPCTRL\n"); - return; - } - - /* Add CMD14 Support */ - brcmf_sdiod_regwb(bus->sdiodev, SDIO_CCCR_BRCM_CARDCAP, - (SDIO_CCCR_BRCM_CARDCAP_CMD14_SUPPORT | - SDIO_CCCR_BRCM_CARDCAP_CMD14_EXT), - &err); - if (err) { - brcmf_err("error writing SDIO_CCCR_BRCM_CARDCAP\n"); - return; - } - - brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, - SBSDIO_FORCE_HT, &err); - if (err) { - brcmf_err("error writing SBSDIO_FUNC1_CHIPCLKCSR\n"); - return; - } - - /* set flag */ - bus->sr_enabled = true; - brcmf_dbg(INFO, "SR enabled\n"); -} - -/* enable KSO bit */ -static int brcmf_sdio_kso_init(struct brcmf_sdio *bus) -{ - u8 val; - int err = 0; - - brcmf_dbg(TRACE, "Enter\n"); - - /* KSO bit added in SDIO core rev 12 */ - if (brcmf_chip_get_core(bus->ci, BCMA_CORE_SDIO_DEV)->rev < 12) - return 0; - - val = brcmf_sdiod_regrb(bus->sdiodev, SBSDIO_FUNC1_SLEEPCSR, &err); - if (err) { - brcmf_err("error reading SBSDIO_FUNC1_SLEEPCSR\n"); - return err; - } - - if (!(val & SBSDIO_FUNC1_SLEEPCSR_KSO_MASK)) { - val |= (SBSDIO_FUNC1_SLEEPCSR_KSO_EN << - SBSDIO_FUNC1_SLEEPCSR_KSO_SHIFT); - brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_SLEEPCSR, - val, &err); - if (err) { - brcmf_err("error writing SBSDIO_FUNC1_SLEEPCSR\n"); - return err; - } - } - - return 0; -} - - -static int brcmf_sdio_bus_preinit(struct device *dev) -{ - struct brcmf_bus *bus_if = dev_get_drvdata(dev); - struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio; - struct brcmf_sdio *bus = sdiodev->bus; - uint pad_size; - u32 value; - int err; - - /* the commands below use the terms tx and rx from - * a device perspective, ie. bus:txglom affects the - * bus transfers from device to host. - */ - if (brcmf_chip_get_core(bus->ci, BCMA_CORE_SDIO_DEV)->rev < 12) { - /* for sdio core rev < 12, disable txgloming */ - value = 0; - err = brcmf_iovar_data_set(dev, "bus:txglom", &value, - sizeof(u32)); - } else { - /* otherwise, set txglomalign */ - value = 4; - if (sdiodev->pdata) - value = sdiodev->pdata->sd_sgentry_align; - /* SDIO ADMA requires at least 32 bit alignment */ - value = max_t(u32, value, 4); - err = brcmf_iovar_data_set(dev, "bus:txglomalign", &value, - sizeof(u32)); - } - - if (err < 0) - goto done; - - bus->tx_hdrlen = SDPCM_HWHDR_LEN + SDPCM_SWHDR_LEN; - if (sdiodev->sg_support) { - bus->txglom = false; - value = 1; - pad_size = bus->sdiodev->func[2]->cur_blksize << 1; - err = brcmf_iovar_data_set(bus->sdiodev->dev, "bus:rxglom", - &value, sizeof(u32)); - if (err < 0) { - /* bus:rxglom is allowed to fail */ - err = 0; - } else { - bus->txglom = true; - bus->tx_hdrlen += SDPCM_HWEXT_LEN; - } - } - brcmf_bus_add_txhdrlen(bus->sdiodev->dev, bus->tx_hdrlen); - -done: - return err; -} - -void brcmf_sdio_isr(struct brcmf_sdio *bus) -{ - brcmf_dbg(TRACE, "Enter\n"); - - if (!bus) { - brcmf_err("bus is null pointer, exiting\n"); - return; - } - - if (!brcmf_bus_ready(bus->sdiodev->bus_if)) { - brcmf_err("bus is down. we have nothing to do\n"); - return; - } - /* Count the interrupt call */ - bus->sdcnt.intrcount++; - if (in_interrupt()) - atomic_set(&bus->ipend, 1); - else - if (brcmf_sdio_intr_rstatus(bus)) { - brcmf_err("failed backplane access\n"); - } - - /* Disable additional interrupts (is this needed now)? */ - if (!bus->intr) - brcmf_err("isr w/o interrupt configured!\n"); - - atomic_inc(&bus->dpc_tskcnt); - queue_work(bus->brcmf_wq, &bus->datawork); -} - -static bool brcmf_sdio_bus_watchdog(struct brcmf_sdio *bus) -{ -#ifdef DEBUG - struct brcmf_bus *bus_if = dev_get_drvdata(bus->sdiodev->dev); -#endif /* DEBUG */ - - brcmf_dbg(TIMER, "Enter\n"); - - /* Poll period: check device if appropriate. */ - if (!bus->sr_enabled && - bus->poll && (++bus->polltick >= bus->pollrate)) { - u32 intstatus = 0; - - /* Reset poll tick */ - bus->polltick = 0; - - /* Check device if no interrupts */ - if (!bus->intr || - (bus->sdcnt.intrcount == bus->sdcnt.lastintrs)) { - - if (atomic_read(&bus->dpc_tskcnt) == 0) { - u8 devpend; - - sdio_claim_host(bus->sdiodev->func[1]); - devpend = brcmf_sdiod_regrb(bus->sdiodev, - SDIO_CCCR_INTx, - NULL); - sdio_release_host(bus->sdiodev->func[1]); - intstatus = - devpend & (INTR_STATUS_FUNC1 | - INTR_STATUS_FUNC2); - } - - /* If there is something, make like the ISR and - schedule the DPC */ - if (intstatus) { - bus->sdcnt.pollcnt++; - atomic_set(&bus->ipend, 1); - - atomic_inc(&bus->dpc_tskcnt); - queue_work(bus->brcmf_wq, &bus->datawork); - } - } - - /* Update interrupt tracking */ - bus->sdcnt.lastintrs = bus->sdcnt.intrcount; - } -#ifdef DEBUG - /* Poll for console output periodically */ - if (bus_if && bus_if->state == BRCMF_BUS_DATA && - bus->console_interval != 0) { - bus->console.count += BRCMF_WD_POLL_MS; - if (bus->console.count >= bus->console_interval) { - bus->console.count -= bus->console_interval; - sdio_claim_host(bus->sdiodev->func[1]); - /* Make sure backplane clock is on */ - brcmf_sdio_bus_sleep(bus, false, false); - if (brcmf_sdio_readconsole(bus) < 0) - /* stop on error */ - bus->console_interval = 0; - sdio_release_host(bus->sdiodev->func[1]); - } - } -#endif /* DEBUG */ - - /* On idle timeout clear activity flag and/or turn off clock */ - if ((bus->idletime > 0) && (bus->clkstate == CLK_AVAIL)) { - if (++bus->idlecount >= bus->idletime) { - bus->idlecount = 0; - if (bus->activity) { - bus->activity = false; - brcmf_sdio_wd_timer(bus, BRCMF_WD_POLL_MS); - } else { - brcmf_dbg(SDIO, "idle\n"); - sdio_claim_host(bus->sdiodev->func[1]); - brcmf_sdio_bus_sleep(bus, true, false); - sdio_release_host(bus->sdiodev->func[1]); - } - } - } - - return (atomic_read(&bus->ipend) > 0); -} - -static void brcmf_sdio_dataworker(struct work_struct *work) -{ - struct brcmf_sdio *bus = container_of(work, struct brcmf_sdio, - datawork); - - while (atomic_read(&bus->dpc_tskcnt)) { - atomic_set(&bus->dpc_tskcnt, 0); - brcmf_sdio_dpc(bus); - } -} - -static void -brcmf_sdio_drivestrengthinit(struct brcmf_sdio_dev *sdiodev, - struct brcmf_chip *ci, u32 drivestrength) -{ - const struct sdiod_drive_str *str_tab = NULL; - u32 str_mask; - u32 str_shift; - u32 base; - u32 i; - u32 drivestrength_sel = 0; - u32 cc_data_temp; - u32 addr; - - if (!(ci->cc_caps & CC_CAP_PMU)) - return; - - switch (SDIOD_DRVSTR_KEY(ci->chip, ci->pmurev)) { - case SDIOD_DRVSTR_KEY(BRCM_CC_4330_CHIP_ID, 12): - str_tab = sdiod_drvstr_tab1_1v8; - str_mask = 0x00003800; - str_shift = 11; - break; - case SDIOD_DRVSTR_KEY(BRCM_CC_4334_CHIP_ID, 17): - str_tab = sdiod_drvstr_tab6_1v8; - str_mask = 0x00001800; - str_shift = 11; - break; - case SDIOD_DRVSTR_KEY(BRCM_CC_43143_CHIP_ID, 17): - /* note: 43143 does not support tristate */ - i = ARRAY_SIZE(sdiod_drvstr_tab2_3v3) - 1; - if (drivestrength >= sdiod_drvstr_tab2_3v3[i].strength) { - str_tab = sdiod_drvstr_tab2_3v3; - str_mask = 0x00000007; - str_shift = 0; - } else - brcmf_err("Invalid SDIO Drive strength for chip %s, strength=%d\n", - ci->name, drivestrength); - break; - case SDIOD_DRVSTR_KEY(BRCM_CC_43362_CHIP_ID, 13): - str_tab = sdiod_drive_strength_tab5_1v8; - str_mask = 0x00003800; - str_shift = 11; - break; - default: - brcmf_err("No SDIO Drive strength init done for chip %s rev %d pmurev %d\n", - ci->name, ci->chiprev, ci->pmurev); - break; - } - - if (str_tab != NULL) { - for (i = 0; str_tab[i].strength != 0; i++) { - if (drivestrength >= str_tab[i].strength) { - drivestrength_sel = str_tab[i].sel; - break; - } - } - base = brcmf_chip_get_chipcommon(ci)->base; - addr = CORE_CC_REG(base, chipcontrol_addr); - brcmf_sdiod_regwl(sdiodev, addr, 1, NULL); - cc_data_temp = brcmf_sdiod_regrl(sdiodev, addr, NULL); - cc_data_temp &= ~str_mask; - drivestrength_sel <<= str_shift; - cc_data_temp |= drivestrength_sel; - brcmf_sdiod_regwl(sdiodev, addr, cc_data_temp, NULL); - - brcmf_dbg(INFO, "SDIO: %d mA (req=%d mA) drive strength selected, set to 0x%08x\n", - str_tab[i].strength, drivestrength, cc_data_temp); - } -} - -static int brcmf_sdio_buscoreprep(void *ctx) -{ - struct brcmf_sdio_dev *sdiodev = ctx; - int err = 0; - u8 clkval, clkset; - - /* Try forcing SDIO core to do ALPAvail request only */ - clkset = SBSDIO_FORCE_HW_CLKREQ_OFF | SBSDIO_ALP_AVAIL_REQ; - brcmf_sdiod_regwb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, clkset, &err); - if (err) { - brcmf_err("error writing for HT off\n"); - return err; - } - - /* If register supported, wait for ALPAvail and then force ALP */ - /* This may take up to 15 milliseconds */ - clkval = brcmf_sdiod_regrb(sdiodev, - SBSDIO_FUNC1_CHIPCLKCSR, NULL); - - if ((clkval & ~SBSDIO_AVBITS) != clkset) { - brcmf_err("ChipClkCSR access: wrote 0x%02x read 0x%02x\n", - clkset, clkval); - return -EACCES; - } - - SPINWAIT(((clkval = brcmf_sdiod_regrb(sdiodev, - SBSDIO_FUNC1_CHIPCLKCSR, NULL)), - !SBSDIO_ALPAV(clkval)), - PMU_MAX_TRANSITION_DLY); - if (!SBSDIO_ALPAV(clkval)) { - brcmf_err("timeout on ALPAV wait, clkval 0x%02x\n", - clkval); - return -EBUSY; - } - - clkset = SBSDIO_FORCE_HW_CLKREQ_OFF | SBSDIO_FORCE_ALP; - brcmf_sdiod_regwb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, clkset, &err); - udelay(65); - - /* Also, disable the extra SDIO pull-ups */ - brcmf_sdiod_regwb(sdiodev, SBSDIO_FUNC1_SDIOPULLUP, 0, NULL); - - return 0; -} - -static void brcmf_sdio_buscore_exitdl(void *ctx, struct brcmf_chip *chip, - u32 rstvec) -{ - struct brcmf_sdio_dev *sdiodev = ctx; - struct brcmf_core *core; - u32 reg_addr; - - /* clear all interrupts */ - core = brcmf_chip_get_core(chip, BCMA_CORE_SDIO_DEV); - reg_addr = core->base + offsetof(struct sdpcmd_regs, intstatus); - brcmf_sdiod_regwl(sdiodev, reg_addr, 0xFFFFFFFF, NULL); - - if (rstvec) - /* Write reset vector to address 0 */ - brcmf_sdiod_ramrw(sdiodev, true, 0, (void *)&rstvec, - sizeof(rstvec)); -} - -static u32 brcmf_sdio_buscore_read32(void *ctx, u32 addr) -{ - struct brcmf_sdio_dev *sdiodev = ctx; - u32 val, rev; - - val = brcmf_sdiod_regrl(sdiodev, addr, NULL); - if (sdiodev->func[0]->device == BRCM_SDIO_4335_4339_DEVICE_ID && - addr == CORE_CC_REG(SI_ENUM_BASE, chipid)) { - rev = (val & CID_REV_MASK) >> CID_REV_SHIFT; - if (rev >= 2) { - val &= ~CID_ID_MASK; - val |= BRCM_CC_4339_CHIP_ID; - } - } - return val; -} - -static void brcmf_sdio_buscore_write32(void *ctx, u32 addr, u32 val) -{ - struct brcmf_sdio_dev *sdiodev = ctx; - - brcmf_sdiod_regwl(sdiodev, addr, val, NULL); -} - -static const struct brcmf_buscore_ops brcmf_sdio_buscore_ops = { - .prepare = brcmf_sdio_buscoreprep, - .exit_dl = brcmf_sdio_buscore_exitdl, - .read32 = brcmf_sdio_buscore_read32, - .write32 = brcmf_sdio_buscore_write32, -}; - -static bool -brcmf_sdio_probe_attach(struct brcmf_sdio *bus) -{ - u8 clkctl = 0; - int err = 0; - int reg_addr; - u32 reg_val; - u32 drivestrength; - - sdio_claim_host(bus->sdiodev->func[1]); - - pr_debug("F1 signature read @0x18000000=0x%4x\n", - brcmf_sdiod_regrl(bus->sdiodev, SI_ENUM_BASE, NULL)); - - /* - * Force PLL off until brcmf_chip_attach() - * programs PLL control regs - */ - - brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, - BRCMF_INIT_CLKCTL1, &err); - if (!err) - clkctl = brcmf_sdiod_regrb(bus->sdiodev, - SBSDIO_FUNC1_CHIPCLKCSR, &err); - - if (err || ((clkctl & ~SBSDIO_AVBITS) != BRCMF_INIT_CLKCTL1)) { - brcmf_err("ChipClkCSR access: err %d wrote 0x%02x read 0x%02x\n", - err, BRCMF_INIT_CLKCTL1, clkctl); - goto fail; - } - - /* SDIO register access works so moving - * state from UNKNOWN to DOWN. - */ - brcmf_bus_change_state(bus->sdiodev->bus_if, BRCMF_BUS_DOWN); - - bus->ci = brcmf_chip_attach(bus->sdiodev, &brcmf_sdio_buscore_ops); - if (IS_ERR(bus->ci)) { - brcmf_err("brcmf_chip_attach failed!\n"); - bus->ci = NULL; - goto fail; - } - - if (brcmf_sdio_kso_init(bus)) { - brcmf_err("error enabling KSO\n"); - goto fail; - } - - if ((bus->sdiodev->pdata) && (bus->sdiodev->pdata->drive_strength)) - drivestrength = bus->sdiodev->pdata->drive_strength; - else - drivestrength = DEFAULT_SDIO_DRIVE_STRENGTH; - brcmf_sdio_drivestrengthinit(bus->sdiodev, bus->ci, drivestrength); - - /* Get info on the SOCRAM cores... */ - bus->ramsize = bus->ci->ramsize; - if (!(bus->ramsize)) { - brcmf_err("failed to find SOCRAM memory!\n"); - goto fail; - } - - /* Set card control so an SDIO card reset does a WLAN backplane reset */ - reg_val = brcmf_sdiod_regrb(bus->sdiodev, - SDIO_CCCR_BRCM_CARDCTRL, &err); - if (err) - goto fail; - - reg_val |= SDIO_CCCR_BRCM_CARDCTRL_WLANRESET; - - brcmf_sdiod_regwb(bus->sdiodev, - SDIO_CCCR_BRCM_CARDCTRL, reg_val, &err); - if (err) - goto fail; - - /* set PMUControl so a backplane reset does PMU state reload */ - reg_addr = CORE_CC_REG(brcmf_chip_get_chipcommon(bus->ci)->base, - pmucontrol); - reg_val = brcmf_sdiod_regrl(bus->sdiodev, reg_addr, &err); - if (err) - goto fail; - - reg_val |= (BCMA_CC_PMU_CTL_RES_RELOAD << BCMA_CC_PMU_CTL_RES_SHIFT); - - brcmf_sdiod_regwl(bus->sdiodev, reg_addr, reg_val, &err); - if (err) - goto fail; - - sdio_release_host(bus->sdiodev->func[1]); - - brcmu_pktq_init(&bus->txq, (PRIOMASK + 1), TXQLEN); - - /* allocate header buffer */ - bus->hdrbuf = kzalloc(MAX_HDR_READ + bus->head_align, GFP_KERNEL); - if (!bus->hdrbuf) - return false; - /* Locate an appropriately-aligned portion of hdrbuf */ - bus->rxhdr = (u8 *) roundup((unsigned long)&bus->hdrbuf[0], - bus->head_align); - - /* Set the poll and/or interrupt flags */ - bus->intr = true; - bus->poll = false; - if (bus->poll) - bus->pollrate = 1; - - return true; - -fail: - sdio_release_host(bus->sdiodev->func[1]); - return false; -} - -static int -brcmf_sdio_watchdog_thread(void *data) -{ - struct brcmf_sdio *bus = (struct brcmf_sdio *)data; - - allow_signal(SIGTERM); - /* Run until signal received */ - while (1) { - if (kthread_should_stop()) - break; - if (!wait_for_completion_interruptible(&bus->watchdog_wait)) { - brcmf_sdio_bus_watchdog(bus); - /* Count the tick for reference */ - bus->sdcnt.tickcnt++; - reinit_completion(&bus->watchdog_wait); - } else - break; - } - return 0; -} - -static void -brcmf_sdio_watchdog(unsigned long data) -{ - struct brcmf_sdio *bus = (struct brcmf_sdio *)data; - - if (bus->watchdog_tsk) { - complete(&bus->watchdog_wait); - /* Reschedule the watchdog */ - if (bus->wd_timer_valid) - mod_timer(&bus->timer, - jiffies + BRCMF_WD_POLL_MS * HZ / 1000); - } -} - -static struct brcmf_bus_ops brcmf_sdio_bus_ops = { - .stop = brcmf_sdio_bus_stop, - .preinit = brcmf_sdio_bus_preinit, - .txdata = brcmf_sdio_bus_txdata, - .txctl = brcmf_sdio_bus_txctl, - .rxctl = brcmf_sdio_bus_rxctl, - .gettxq = brcmf_sdio_bus_gettxq, - .wowl_config = brcmf_sdio_wowl_config -}; - -static void brcmf_sdio_firmware_callback(struct device *dev, - const struct firmware *code, - void *nvram, u32 nvram_len) -{ - 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 = 0; - u8 saveclk; - - brcmf_dbg(TRACE, "Enter: dev=%s\n", dev_name(dev)); - - /* try to download image and nvram to the dongle */ - if (bus_if->state == BRCMF_BUS_DOWN) { - bus->alp_only = true; - err = brcmf_sdio_download_firmware(bus, code, nvram, nvram_len); - if (err) - goto fail; - bus->alp_only = false; - } - - if (!bus_if->drvr) - return; - - /* Start the watchdog timer */ - bus->sdcnt.tickcnt = 0; - brcmf_sdio_wd_timer(bus, BRCMF_WD_POLL_MS); - - sdio_claim_host(sdiodev->func[1]); - - /* Make sure backplane clock is on, needed to generate F2 interrupt */ - brcmf_sdio_clkctl(bus, CLK_AVAIL, false); - if (bus->clkstate != CLK_AVAIL) - goto release; - - /* Force clocks on backplane to be sure F2 interrupt propagates */ - saveclk = brcmf_sdiod_regrb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, &err); - if (!err) { - brcmf_sdiod_regwb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, - (saveclk | SBSDIO_FORCE_HT), &err); - } - if (err) { - brcmf_err("Failed to force clock for F2: err %d\n", err); - goto release; - } - - /* Enable function 2 (frame transfers) */ - w_sdreg32(bus, SDPCM_PROT_VERSION << SMB_DATA_VERSION_SHIFT, - offsetof(struct sdpcmd_regs, tosbmailboxdata)); - err = sdio_enable_func(sdiodev->func[SDIO_FUNC_2]); - - - brcmf_dbg(INFO, "enable F2: err=%d\n", err); - - /* If F2 successfully enabled, set core and enable interrupts */ - if (!err) { - /* Set up the interrupt mask and enable interrupts */ - bus->hostintmask = HOSTINTMASK; - w_sdreg32(bus, bus->hostintmask, - offsetof(struct sdpcmd_regs, hostintmask)); - - brcmf_sdiod_regwb(sdiodev, SBSDIO_WATERMARK, 8, &err); - } else { - /* Disable F2 again */ - sdio_disable_func(sdiodev->func[SDIO_FUNC_2]); - goto release; - } - - if (brcmf_chip_sr_capable(bus->ci)) { - brcmf_sdio_sr_init(bus); - } else { - /* Restore previous clock setting */ - brcmf_sdiod_regwb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, - saveclk, &err); - } - - if (err == 0) { - err = brcmf_sdiod_intr_register(sdiodev); - if (err != 0) - brcmf_err("intr register failed:%d\n", err); - } - - /* If we didn't come up, turn off backplane clock */ - if (err != 0) - brcmf_sdio_clkctl(bus, CLK_NONE, false); - - sdio_release_host(sdiodev->func[1]); - - err = brcmf_bus_start(dev); - if (err != 0) { - brcmf_err("dongle is not responding\n"); - goto fail; - } - return; - -release: - sdio_release_host(sdiodev->func[1]); -fail: - brcmf_dbg(TRACE, "failed: dev=%s, err=%d\n", dev_name(dev), err); - device_release_driver(dev); -} - -struct brcmf_sdio *brcmf_sdio_probe(struct brcmf_sdio_dev *sdiodev) -{ - int ret; - struct brcmf_sdio *bus; - - brcmf_dbg(TRACE, "Enter\n"); - - /* Allocate private bus interface state */ - bus = kzalloc(sizeof(struct brcmf_sdio), GFP_ATOMIC); - if (!bus) - goto fail; - - bus->sdiodev = sdiodev; - sdiodev->bus = bus; - skb_queue_head_init(&bus->glom); - bus->txbound = BRCMF_TXBOUND; - bus->rxbound = BRCMF_RXBOUND; - bus->txminmax = BRCMF_TXMINMAX; - bus->tx_seq = SDPCM_SEQ_WRAP - 1; - - /* platform specific configuration: - * alignments must be at least 4 bytes for ADMA - */ - bus->head_align = ALIGNMENT; - bus->sgentry_align = ALIGNMENT; - if (sdiodev->pdata) { - if (sdiodev->pdata->sd_head_align > ALIGNMENT) - bus->head_align = sdiodev->pdata->sd_head_align; - if (sdiodev->pdata->sd_sgentry_align > ALIGNMENT) - bus->sgentry_align = sdiodev->pdata->sd_sgentry_align; - } - - INIT_WORK(&bus->datawork, brcmf_sdio_dataworker); - bus->brcmf_wq = create_singlethread_workqueue("brcmf_wq"); - if (bus->brcmf_wq == NULL) { - brcmf_err("insufficient memory to create txworkqueue\n"); - goto fail; - } - - /* attempt to attach to the dongle */ - if (!(brcmf_sdio_probe_attach(bus))) { - brcmf_err("brcmf_sdio_probe_attach failed\n"); - goto fail; - } - - spin_lock_init(&bus->rxctl_lock); - spin_lock_init(&bus->txq_lock); - sema_init(&bus->tx_seq_lock, 1); - init_waitqueue_head(&bus->ctrl_wait); - init_waitqueue_head(&bus->dcmd_resp_wait); - - /* Set up the watchdog timer */ - init_timer(&bus->timer); - bus->timer.data = (unsigned long)bus; - bus->timer.function = brcmf_sdio_watchdog; - - /* Initialize watchdog thread */ - init_completion(&bus->watchdog_wait); - bus->watchdog_tsk = kthread_run(brcmf_sdio_watchdog_thread, - bus, "brcmf_watchdog"); - if (IS_ERR(bus->watchdog_tsk)) { - pr_warn("brcmf_watchdog thread failed to start\n"); - bus->watchdog_tsk = NULL; - } - /* Initialize DPC thread */ - atomic_set(&bus->dpc_tskcnt, 0); - - /* Assign bus interface call back */ - bus->sdiodev->bus_if->dev = bus->sdiodev->dev; - bus->sdiodev->bus_if->ops = &brcmf_sdio_bus_ops; - bus->sdiodev->bus_if->chip = bus->ci->chip; - bus->sdiodev->bus_if->chiprev = bus->ci->chiprev; - - /* default sdio bus header length for tx packet */ - bus->tx_hdrlen = SDPCM_HWHDR_LEN + SDPCM_SWHDR_LEN; - - /* Attach to the common layer, reserve hdr space */ - ret = brcmf_attach(bus->sdiodev->dev); - if (ret != 0) { - brcmf_err("brcmf_attach failed\n"); - goto fail; - } - - /* Query the F2 block size, set roundup accordingly */ - bus->blocksize = bus->sdiodev->func[2]->cur_blksize; - bus->roundup = min(max_roundup, bus->blocksize); - - /* Allocate buffers */ - if (bus->sdiodev->bus_if->maxctl) { - bus->sdiodev->bus_if->maxctl += bus->roundup; - bus->rxblen = - roundup((bus->sdiodev->bus_if->maxctl + SDPCM_HDRLEN), - ALIGNMENT) + bus->head_align; - bus->rxbuf = kmalloc(bus->rxblen, GFP_ATOMIC); - if (!(bus->rxbuf)) { - brcmf_err("rxbuf allocation failed\n"); - goto fail; - } - } - - sdio_claim_host(bus->sdiodev->func[1]); - - /* Disable F2 to clear any intermediate frame state on the dongle */ - sdio_disable_func(bus->sdiodev->func[SDIO_FUNC_2]); - - bus->rxflow = false; - - /* Done with backplane-dependent accesses, can drop clock... */ - brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, 0, NULL); - - sdio_release_host(bus->sdiodev->func[1]); - - /* ...and initialize clock/power states */ - bus->clkstate = CLK_SDONLY; - bus->idletime = BRCMF_IDLE_INTERVAL; - bus->idleclock = BRCMF_IDLE_ACTIVE; - - /* SR state */ - bus->sleeping = false; - bus->sr_enabled = false; - - brcmf_sdio_debugfs_create(bus); - brcmf_dbg(INFO, "completed!!\n"); - - ret = brcmf_sdio_get_fwnames(bus->ci, sdiodev); - if (ret) - goto fail; - - ret = brcmf_fw_get_firmwares(sdiodev->dev, BRCMF_FW_REQUEST_NVRAM, - sdiodev->fw_name, sdiodev->nvram_name, - brcmf_sdio_firmware_callback); - if (ret != 0) { - brcmf_err("async firmware request failed: %d\n", ret); - goto fail; - } - - return bus; - -fail: - brcmf_sdio_remove(bus); - return NULL; -} - -/* Detach and free everything */ -void brcmf_sdio_remove(struct brcmf_sdio *bus) -{ - brcmf_dbg(TRACE, "Enter\n"); - - if (bus) { - /* De-register interrupt handler */ - brcmf_sdiod_intr_unregister(bus->sdiodev); - - brcmf_detach(bus->sdiodev->dev); - - cancel_work_sync(&bus->datawork); - if (bus->brcmf_wq) - destroy_workqueue(bus->brcmf_wq); - - if (bus->ci) { - if (bus->sdiodev->bus_if->state == BRCMF_BUS_DOWN) { - sdio_claim_host(bus->sdiodev->func[1]); - brcmf_sdio_clkctl(bus, CLK_AVAIL, false); - /* Leave the device in state where it is - * 'quiet'. This is done by putting it in - * download_state which essentially resets - * all necessary cores. - */ - msleep(20); - brcmf_chip_enter_download(bus->ci); - brcmf_sdio_clkctl(bus, CLK_NONE, false); - sdio_release_host(bus->sdiodev->func[1]); - } - brcmf_chip_detach(bus->ci); - } - - kfree(bus->rxbuf); - kfree(bus->hdrbuf); - kfree(bus); - } - - brcmf_dbg(TRACE, "Disconnected\n"); -} - -void brcmf_sdio_wd_timer(struct brcmf_sdio *bus, uint wdtick) -{ - /* Totally stop the timer */ - if (!wdtick && bus->wd_timer_valid) { - del_timer_sync(&bus->timer); - bus->wd_timer_valid = false; - bus->save_ms = wdtick; - return; - } - - /* don't start the wd until fw is loaded */ - if (bus->sdiodev->bus_if->state != BRCMF_BUS_DATA) - return; - - if (wdtick) { - if (bus->save_ms != BRCMF_WD_POLL_MS) { - if (bus->wd_timer_valid) - /* Stop timer and restart at new value */ - del_timer_sync(&bus->timer); - - /* Create timer again when watchdog period is - dynamically changed or in the first instance - */ - bus->timer.expires = - jiffies + BRCMF_WD_POLL_MS * HZ / 1000; - add_timer(&bus->timer); - - } else { - /* Re arm the timer, at last watchdog period */ - mod_timer(&bus->timer, - jiffies + BRCMF_WD_POLL_MS * HZ / 1000); - } - - bus->wd_timer_valid = true; - bus->save_ms = wdtick; - } -} diff --git a/drivers/net/wireless/brcm80211/brcmfmac/of.c b/drivers/net/wireless/brcm80211/brcmfmac/of.c index 875060c54a00..eb3fce82a223 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/of.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/of.c @@ -22,7 +22,7 @@ #include #include "debug.h" -#include "sdio_host.h" +#include "sdio.h" void brcmf_of_probe(struct brcmf_sdio_dev *sdiodev) { diff --git a/drivers/net/wireless/brcm80211/brcmfmac/sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/sdio.c new file mode 100644 index 000000000000..73ac0c38a32a --- /dev/null +++ b/drivers/net/wireless/brcm80211/brcmfmac/sdio.c @@ -0,0 +1,4276 @@ +/* + * Copyright (c) 2010 Broadcom Corporation + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "sdio.h" +#include "chip.h" +#include "firmware.h" + +#define DCMD_RESP_TIMEOUT 2000 /* In milli second */ + +#ifdef DEBUG + +#define BRCMF_TRAP_INFO_SIZE 80 + +#define CBUF_LEN (128) + +/* Device console log buffer state */ +#define CONSOLE_BUFFER_MAX 2024 + +struct rte_log_le { + __le32 buf; /* Can't be pointer on (64-bit) hosts */ + __le32 buf_size; + __le32 idx; + char *_buf_compat; /* Redundant pointer for backward compat. */ +}; + +struct rte_console { + /* Virtual UART + * When there is no UART (e.g. Quickturn), + * the host should write a complete + * input line directly into cbuf and then write + * the length into vcons_in. + * This may also be used when there is a real UART + * (at risk of conflicting with + * the real UART). vcons_out is currently unused. + */ + uint vcons_in; + uint vcons_out; + + /* Output (logging) buffer + * Console output is written to a ring buffer log_buf at index log_idx. + * The host may read the output when it sees log_idx advance. + * Output will be lost if the output wraps around faster than the host + * polls. + */ + struct rte_log_le log_le; + + /* Console input line buffer + * Characters are read one at a time into cbuf + * until is received, then + * the buffer is processed as a command line. + * Also used for virtual UART. + */ + uint cbuf_idx; + char cbuf[CBUF_LEN]; +}; + +#endif /* DEBUG */ +#include + +#include "bus.h" +#include "debug.h" +#include "tracepoint.h" + +#define TXQLEN 2048 /* bulk tx queue length */ +#define TXHI (TXQLEN - 256) /* turn on flow control above TXHI */ +#define TXLOW (TXHI - 256) /* turn off flow control below TXLOW */ +#define PRIOMASK 7 + +#define TXRETRIES 2 /* # of retries for tx frames */ + +#define BRCMF_RXBOUND 50 /* Default for max rx frames in + one scheduling */ + +#define BRCMF_TXBOUND 20 /* Default for max tx frames in + one scheduling */ + +#define BRCMF_TXMINMAX 1 /* Max tx frames if rx still pending */ + +#define MEMBLOCK 2048 /* Block size used for downloading + of dongle image */ +#define MAX_DATA_BUF (32 * 1024) /* Must be large enough to hold + biggest possible glom */ + +#define BRCMF_FIRSTREAD (1 << 6) + + +/* SBSDIO_DEVICE_CTL */ + +/* 1: device will assert busy signal when receiving CMD53 */ +#define SBSDIO_DEVCTL_SETBUSY 0x01 +/* 1: assertion of sdio interrupt is synchronous to the sdio clock */ +#define SBSDIO_DEVCTL_SPI_INTR_SYNC 0x02 +/* 1: mask all interrupts to host except the chipActive (rev 8) */ +#define SBSDIO_DEVCTL_CA_INT_ONLY 0x04 +/* 1: isolate internal sdio signals, put external pads in tri-state; requires + * sdio bus power cycle to clear (rev 9) */ +#define SBSDIO_DEVCTL_PADS_ISO 0x08 +/* Force SD->SB reset mapping (rev 11) */ +#define SBSDIO_DEVCTL_SB_RST_CTL 0x30 +/* Determined by CoreControl bit */ +#define SBSDIO_DEVCTL_RST_CORECTL 0x00 +/* Force backplane reset */ +#define SBSDIO_DEVCTL_RST_BPRESET 0x10 +/* Force no backplane reset */ +#define SBSDIO_DEVCTL_RST_NOBPRESET 0x20 + +/* direct(mapped) cis space */ + +/* MAPPED common CIS address */ +#define SBSDIO_CIS_BASE_COMMON 0x1000 +/* maximum bytes in one CIS */ +#define SBSDIO_CIS_SIZE_LIMIT 0x200 +/* cis offset addr is < 17 bits */ +#define SBSDIO_CIS_OFT_ADDR_MASK 0x1FFFF + +/* manfid tuple length, include tuple, link bytes */ +#define SBSDIO_CIS_MANFID_TUPLE_LEN 6 + +#define CORE_BUS_REG(base, field) \ + (base + offsetof(struct sdpcmd_regs, field)) + +/* SDIO function 1 register CHIPCLKCSR */ +/* Force ALP request to backplane */ +#define SBSDIO_FORCE_ALP 0x01 +/* Force HT request to backplane */ +#define SBSDIO_FORCE_HT 0x02 +/* Force ILP request to backplane */ +#define SBSDIO_FORCE_ILP 0x04 +/* Make ALP ready (power up xtal) */ +#define SBSDIO_ALP_AVAIL_REQ 0x08 +/* Make HT ready (power up PLL) */ +#define SBSDIO_HT_AVAIL_REQ 0x10 +/* Squelch clock requests from HW */ +#define SBSDIO_FORCE_HW_CLKREQ_OFF 0x20 +/* Status: ALP is ready */ +#define SBSDIO_ALP_AVAIL 0x40 +/* Status: HT is ready */ +#define SBSDIO_HT_AVAIL 0x80 +#define SBSDIO_CSR_MASK 0x1F +#define SBSDIO_AVBITS (SBSDIO_HT_AVAIL | SBSDIO_ALP_AVAIL) +#define SBSDIO_ALPAV(regval) ((regval) & SBSDIO_AVBITS) +#define SBSDIO_HTAV(regval) (((regval) & SBSDIO_AVBITS) == SBSDIO_AVBITS) +#define SBSDIO_ALPONLY(regval) (SBSDIO_ALPAV(regval) && !SBSDIO_HTAV(regval)) +#define SBSDIO_CLKAV(regval, alponly) \ + (SBSDIO_ALPAV(regval) && (alponly ? 1 : SBSDIO_HTAV(regval))) + +/* intstatus */ +#define I_SMB_SW0 (1 << 0) /* To SB Mail S/W interrupt 0 */ +#define I_SMB_SW1 (1 << 1) /* To SB Mail S/W interrupt 1 */ +#define I_SMB_SW2 (1 << 2) /* To SB Mail S/W interrupt 2 */ +#define I_SMB_SW3 (1 << 3) /* To SB Mail S/W interrupt 3 */ +#define I_SMB_SW_MASK 0x0000000f /* To SB Mail S/W interrupts mask */ +#define I_SMB_SW_SHIFT 0 /* To SB Mail S/W interrupts shift */ +#define I_HMB_SW0 (1 << 4) /* To Host Mail S/W interrupt 0 */ +#define I_HMB_SW1 (1 << 5) /* To Host Mail S/W interrupt 1 */ +#define I_HMB_SW2 (1 << 6) /* To Host Mail S/W interrupt 2 */ +#define I_HMB_SW3 (1 << 7) /* To Host Mail S/W interrupt 3 */ +#define I_HMB_SW_MASK 0x000000f0 /* To Host Mail S/W interrupts mask */ +#define I_HMB_SW_SHIFT 4 /* To Host Mail S/W interrupts shift */ +#define I_WR_OOSYNC (1 << 8) /* Write Frame Out Of Sync */ +#define I_RD_OOSYNC (1 << 9) /* Read Frame Out Of Sync */ +#define I_PC (1 << 10) /* descriptor error */ +#define I_PD (1 << 11) /* data error */ +#define I_DE (1 << 12) /* Descriptor protocol Error */ +#define I_RU (1 << 13) /* Receive descriptor Underflow */ +#define I_RO (1 << 14) /* Receive fifo Overflow */ +#define I_XU (1 << 15) /* Transmit fifo Underflow */ +#define I_RI (1 << 16) /* Receive Interrupt */ +#define I_BUSPWR (1 << 17) /* SDIO Bus Power Change (rev 9) */ +#define I_XMTDATA_AVAIL (1 << 23) /* bits in fifo */ +#define I_XI (1 << 24) /* Transmit Interrupt */ +#define I_RF_TERM (1 << 25) /* Read Frame Terminate */ +#define I_WF_TERM (1 << 26) /* Write Frame Terminate */ +#define I_PCMCIA_XU (1 << 27) /* PCMCIA Transmit FIFO Underflow */ +#define I_SBINT (1 << 28) /* sbintstatus Interrupt */ +#define I_CHIPACTIVE (1 << 29) /* chip from doze to active state */ +#define I_SRESET (1 << 30) /* CCCR RES interrupt */ +#define I_IOE2 (1U << 31) /* CCCR IOE2 Bit Changed */ +#define I_ERRORS (I_PC | I_PD | I_DE | I_RU | I_RO | I_XU) +#define I_DMA (I_RI | I_XI | I_ERRORS) + +/* corecontrol */ +#define CC_CISRDY (1 << 0) /* CIS Ready */ +#define CC_BPRESEN (1 << 1) /* CCCR RES signal */ +#define CC_F2RDY (1 << 2) /* set CCCR IOR2 bit */ +#define CC_CLRPADSISO (1 << 3) /* clear SDIO pads isolation */ +#define CC_XMTDATAAVAIL_MODE (1 << 4) +#define CC_XMTDATAAVAIL_CTRL (1 << 5) + +/* SDA_FRAMECTRL */ +#define SFC_RF_TERM (1 << 0) /* Read Frame Terminate */ +#define SFC_WF_TERM (1 << 1) /* Write Frame Terminate */ +#define SFC_CRC4WOOS (1 << 2) /* CRC error for write out of sync */ +#define SFC_ABORTALL (1 << 3) /* Abort all in-progress frames */ + +/* + * Software allocation of To SB Mailbox resources + */ + +/* tosbmailbox bits corresponding to intstatus bits */ +#define SMB_NAK (1 << 0) /* Frame NAK */ +#define SMB_INT_ACK (1 << 1) /* Host Interrupt ACK */ +#define SMB_USE_OOB (1 << 2) /* Use OOB Wakeup */ +#define SMB_DEV_INT (1 << 3) /* Miscellaneous Interrupt */ + +/* tosbmailboxdata */ +#define SMB_DATA_VERSION_SHIFT 16 /* host protocol version */ + +/* + * Software allocation of To Host Mailbox resources + */ + +/* intstatus bits */ +#define I_HMB_FC_STATE I_HMB_SW0 /* Flow Control State */ +#define I_HMB_FC_CHANGE I_HMB_SW1 /* Flow Control State Changed */ +#define I_HMB_FRAME_IND I_HMB_SW2 /* Frame Indication */ +#define I_HMB_HOST_INT I_HMB_SW3 /* Miscellaneous Interrupt */ + +/* tohostmailboxdata */ +#define HMB_DATA_NAKHANDLED 1 /* retransmit NAK'd frame */ +#define HMB_DATA_DEVREADY 2 /* talk to host after enable */ +#define HMB_DATA_FC 4 /* per prio flowcontrol update flag */ +#define HMB_DATA_FWREADY 8 /* fw ready for protocol activity */ + +#define HMB_DATA_FCDATA_MASK 0xff000000 +#define HMB_DATA_FCDATA_SHIFT 24 + +#define HMB_DATA_VERSION_MASK 0x00ff0000 +#define HMB_DATA_VERSION_SHIFT 16 + +/* + * Software-defined protocol header + */ + +/* Current protocol version */ +#define SDPCM_PROT_VERSION 4 + +/* + * Shared structure between dongle and the host. + * The structure contains pointers to trap or assert information. + */ +#define SDPCM_SHARED_VERSION 0x0003 +#define SDPCM_SHARED_VERSION_MASK 0x00FF +#define SDPCM_SHARED_ASSERT_BUILT 0x0100 +#define SDPCM_SHARED_ASSERT 0x0200 +#define SDPCM_SHARED_TRAP 0x0400 + +/* Space for header read, limit for data packets */ +#define MAX_HDR_READ (1 << 6) +#define MAX_RX_DATASZ 2048 + +/* Bump up limit on waiting for HT to account for first startup; + * if the image is doing a CRC calculation before programming the PMU + * for HT availability, it could take a couple hundred ms more, so + * max out at a 1 second (1000000us). + */ +#undef PMU_MAX_TRANSITION_DLY +#define PMU_MAX_TRANSITION_DLY 1000000 + +/* Value for ChipClockCSR during initial setup */ +#define BRCMF_INIT_CLKCTL1 (SBSDIO_FORCE_HW_CLKREQ_OFF | \ + SBSDIO_ALP_AVAIL_REQ) + +/* Flags for SDH calls */ +#define F2SYNC (SDIO_REQ_4BYTE | SDIO_REQ_FIXED) + +#define BRCMF_IDLE_ACTIVE 0 /* Do not request any SD clock change + * when idle + */ +#define BRCMF_IDLE_INTERVAL 1 + +#define KSO_WAIT_US 50 +#define MAX_KSO_ATTEMPTS (PMU_MAX_TRANSITION_DLY/KSO_WAIT_US) + +/* + * Conversion of 802.1D priority to precedence level + */ +static uint prio2prec(u32 prio) +{ + return (prio == PRIO_8021D_NONE || prio == PRIO_8021D_BE) ? + (prio^2) : prio; +} + +#ifdef DEBUG +/* Device console log buffer state */ +struct brcmf_console { + uint count; /* Poll interval msec counter */ + uint log_addr; /* Log struct address (fixed) */ + struct rte_log_le log_le; /* Log struct (host copy) */ + uint bufsize; /* Size of log buffer */ + u8 *buf; /* Log buffer (host copy) */ + uint last; /* Last buffer read index */ +}; + +struct brcmf_trap_info { + __le32 type; + __le32 epc; + __le32 cpsr; + __le32 spsr; + __le32 r0; /* a1 */ + __le32 r1; /* a2 */ + __le32 r2; /* a3 */ + __le32 r3; /* a4 */ + __le32 r4; /* v1 */ + __le32 r5; /* v2 */ + __le32 r6; /* v3 */ + __le32 r7; /* v4 */ + __le32 r8; /* v5 */ + __le32 r9; /* sb/v6 */ + __le32 r10; /* sl/v7 */ + __le32 r11; /* fp/v8 */ + __le32 r12; /* ip */ + __le32 r13; /* sp */ + __le32 r14; /* lr */ + __le32 pc; /* r15 */ +}; +#endif /* DEBUG */ + +struct sdpcm_shared { + u32 flags; + u32 trap_addr; + u32 assert_exp_addr; + u32 assert_file_addr; + u32 assert_line; + u32 console_addr; /* Address of struct rte_console */ + u32 msgtrace_addr; + u8 tag[32]; + u32 brpt_addr; +}; + +struct sdpcm_shared_le { + __le32 flags; + __le32 trap_addr; + __le32 assert_exp_addr; + __le32 assert_file_addr; + __le32 assert_line; + __le32 console_addr; /* Address of struct rte_console */ + __le32 msgtrace_addr; + u8 tag[32]; + __le32 brpt_addr; +}; + +/* dongle SDIO bus specific header info */ +struct brcmf_sdio_hdrinfo { + u8 seq_num; + u8 channel; + u16 len; + u16 len_left; + u16 len_nxtfrm; + u8 dat_offset; + bool lastfrm; + u16 tail_pad; +}; + +/* + * hold counter variables + */ +struct brcmf_sdio_count { + uint intrcount; /* Count of device interrupt callbacks */ + uint lastintrs; /* Count as of last watchdog timer */ + uint pollcnt; /* Count of active polls */ + uint regfails; /* Count of R_REG failures */ + uint tx_sderrs; /* Count of tx attempts with sd errors */ + uint fcqueued; /* Tx packets that got queued */ + uint rxrtx; /* Count of rtx requests (NAK to dongle) */ + uint rx_toolong; /* Receive frames too long to receive */ + uint rxc_errors; /* SDIO errors when reading control frames */ + uint rx_hdrfail; /* SDIO errors on header reads */ + uint rx_badhdr; /* Bad received headers (roosync?) */ + uint rx_badseq; /* Mismatched rx sequence number */ + uint fc_rcvd; /* Number of flow-control events received */ + uint fc_xoff; /* Number which turned on flow-control */ + uint fc_xon; /* Number which turned off flow-control */ + uint rxglomfail; /* Failed deglom attempts */ + uint rxglomframes; /* Number of glom frames (superframes) */ + uint rxglompkts; /* Number of packets from glom frames */ + uint f2rxhdrs; /* Number of header reads */ + uint f2rxdata; /* Number of frame data reads */ + uint f2txdata; /* Number of f2 frame writes */ + uint f1regdata; /* Number of f1 register accesses */ + uint tickcnt; /* Number of watchdog been schedule */ + ulong tx_ctlerrs; /* Err of sending ctrl frames */ + ulong tx_ctlpkts; /* Ctrl frames sent to dongle */ + ulong rx_ctlerrs; /* Err of processing rx ctrl frames */ + ulong rx_ctlpkts; /* Ctrl frames processed from dongle */ + ulong rx_readahead_cnt; /* packets where header read-ahead was used */ +}; + +/* misc chip info needed by some of the routines */ +/* Private data for SDIO bus interaction */ +struct brcmf_sdio { + struct brcmf_sdio_dev *sdiodev; /* sdio device handler */ + struct brcmf_chip *ci; /* Chip info struct */ + + u32 ramsize; /* Size of RAM in SOCRAM (bytes) */ + + u32 hostintmask; /* Copy of Host Interrupt Mask */ + atomic_t intstatus; /* Intstatus bits (events) pending */ + atomic_t fcstate; /* State of dongle flow-control */ + + uint blocksize; /* Block size of SDIO transfers */ + uint roundup; /* Max roundup limit */ + + struct pktq txq; /* Queue length used for flow-control */ + u8 flowcontrol; /* per prio flow control bitmask */ + u8 tx_seq; /* Transmit sequence number (next) */ + u8 tx_max; /* Maximum transmit sequence allowed */ + + u8 *hdrbuf; /* buffer for handling rx frame */ + u8 *rxhdr; /* Header of current rx frame (in hdrbuf) */ + u8 rx_seq; /* Receive sequence number (expected) */ + struct brcmf_sdio_hdrinfo cur_read; + /* info of current read frame */ + bool rxskip; /* Skip receive (awaiting NAK ACK) */ + bool rxpending; /* Data frame pending in dongle */ + + uint rxbound; /* Rx frames to read before resched */ + uint txbound; /* Tx frames to send before resched */ + uint txminmax; + + struct sk_buff *glomd; /* Packet containing glomming descriptor */ + struct sk_buff_head glom; /* Packet list for glommed superframe */ + uint glomerr; /* Glom packet read errors */ + + u8 *rxbuf; /* Buffer for receiving control packets */ + uint rxblen; /* Allocated length of rxbuf */ + u8 *rxctl; /* Aligned pointer into rxbuf */ + u8 *rxctl_orig; /* pointer for freeing rxctl */ + uint rxlen; /* Length of valid data in buffer */ + spinlock_t rxctl_lock; /* protection lock for ctrl frame resources */ + + u8 sdpcm_ver; /* Bus protocol reported by dongle */ + + bool intr; /* Use interrupts */ + bool poll; /* Use polling */ + atomic_t ipend; /* Device interrupt is pending */ + uint spurious; /* Count of spurious interrupts */ + uint pollrate; /* Ticks between device polls */ + uint polltick; /* Tick counter */ + +#ifdef DEBUG + uint console_interval; + struct brcmf_console console; /* Console output polling support */ + uint console_addr; /* Console address from shared struct */ +#endif /* DEBUG */ + + uint clkstate; /* State of sd and backplane clock(s) */ + bool activity; /* Activity flag for clock down */ + s32 idletime; /* Control for activity timeout */ + s32 idlecount; /* Activity timeout counter */ + s32 idleclock; /* How to set bus driver when idle */ + bool rxflow_mode; /* Rx flow control mode */ + bool rxflow; /* Is rx flow control on */ + bool alp_only; /* Don't use HT clock (ALP only) */ + + u8 *ctrl_frame_buf; + u16 ctrl_frame_len; + bool ctrl_frame_stat; + + spinlock_t txq_lock; /* protect bus->txq */ + struct semaphore tx_seq_lock; /* protect bus->tx_seq */ + wait_queue_head_t ctrl_wait; + wait_queue_head_t dcmd_resp_wait; + + struct timer_list timer; + struct completion watchdog_wait; + struct task_struct *watchdog_tsk; + bool wd_timer_valid; + uint save_ms; + + struct workqueue_struct *brcmf_wq; + struct work_struct datawork; + atomic_t dpc_tskcnt; + + bool txoff; /* Transmit flow-controlled */ + struct brcmf_sdio_count sdcnt; + bool sr_enabled; /* SaveRestore enabled */ + bool sleeping; /* SDIO bus sleeping */ + + u8 tx_hdrlen; /* sdio bus header length for tx packet */ + bool txglom; /* host tx glomming enable flag */ + u16 head_align; /* buffer pointer alignment */ + u16 sgentry_align; /* scatter-gather buffer alignment */ +}; + +/* clkstate */ +#define CLK_NONE 0 +#define CLK_SDONLY 1 +#define CLK_PENDING 2 +#define CLK_AVAIL 3 + +#ifdef DEBUG +static int qcount[NUMPRIO]; +#endif /* DEBUG */ + +#define DEFAULT_SDIO_DRIVE_STRENGTH 6 /* in milliamps */ + +#define RETRYCHAN(chan) ((chan) == SDPCM_EVENT_CHANNEL) + +/* Retry count for register access failures */ +static const uint retry_limit = 2; + +/* Limit on rounding up frames */ +static const uint max_roundup = 512; + +#define ALIGNMENT 4 + +enum brcmf_sdio_frmtype { + BRCMF_SDIO_FT_NORMAL, + BRCMF_SDIO_FT_SUPER, + BRCMF_SDIO_FT_SUB, +}; + +#define SDIOD_DRVSTR_KEY(chip, pmu) (((chip) << 16) | (pmu)) + +/* SDIO Pad drive strength to select value mappings */ +struct sdiod_drive_str { + u8 strength; /* Pad Drive Strength in mA */ + u8 sel; /* Chip-specific select value */ +}; + +/* SDIO Drive Strength to sel value table for PMU Rev 11 (1.8V) */ +static const struct sdiod_drive_str sdiod_drvstr_tab1_1v8[] = { + {32, 0x6}, + {26, 0x7}, + {22, 0x4}, + {16, 0x5}, + {12, 0x2}, + {8, 0x3}, + {4, 0x0}, + {0, 0x1} +}; + +/* SDIO Drive Strength to sel value table for PMU Rev 13 (1.8v) */ +static const struct sdiod_drive_str sdiod_drive_strength_tab5_1v8[] = { + {6, 0x7}, + {5, 0x6}, + {4, 0x5}, + {3, 0x4}, + {2, 0x2}, + {1, 0x1}, + {0, 0x0} +}; + +/* SDIO Drive Strength to sel value table for PMU Rev 17 (1.8v) */ +static const struct sdiod_drive_str sdiod_drvstr_tab6_1v8[] = { + {3, 0x3}, + {2, 0x2}, + {1, 0x1}, + {0, 0x0} }; + +/* SDIO Drive Strength to sel value table for 43143 PMU Rev 17 (3.3V) */ +static const struct sdiod_drive_str sdiod_drvstr_tab2_3v3[] = { + {16, 0x7}, + {12, 0x5}, + {8, 0x3}, + {4, 0x1} +}; + +#define BCM43143_FIRMWARE_NAME "brcm/brcmfmac43143-sdio.bin" +#define BCM43143_NVRAM_NAME "brcm/brcmfmac43143-sdio.txt" +#define BCM43241B0_FIRMWARE_NAME "brcm/brcmfmac43241b0-sdio.bin" +#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 BCM4329_FIRMWARE_NAME "brcm/brcmfmac4329-sdio.bin" +#define BCM4329_NVRAM_NAME "brcm/brcmfmac4329-sdio.txt" +#define BCM4330_FIRMWARE_NAME "brcm/brcmfmac4330-sdio.bin" +#define BCM4330_NVRAM_NAME "brcm/brcmfmac4330-sdio.txt" +#define BCM4334_FIRMWARE_NAME "brcm/brcmfmac4334-sdio.bin" +#define BCM4334_NVRAM_NAME "brcm/brcmfmac4334-sdio.txt" +#define BCM4335_FIRMWARE_NAME "brcm/brcmfmac4335-sdio.bin" +#define BCM4335_NVRAM_NAME "brcm/brcmfmac4335-sdio.txt" +#define BCM43362_FIRMWARE_NAME "brcm/brcmfmac43362-sdio.bin" +#define BCM43362_NVRAM_NAME "brcm/brcmfmac43362-sdio.txt" +#define BCM4339_FIRMWARE_NAME "brcm/brcmfmac4339-sdio.bin" +#define BCM4339_NVRAM_NAME "brcm/brcmfmac4339-sdio.txt" +#define BCM4354_FIRMWARE_NAME "brcm/brcmfmac4354-sdio.bin" +#define BCM4354_NVRAM_NAME "brcm/brcmfmac4354-sdio.txt" + +MODULE_FIRMWARE(BCM43143_FIRMWARE_NAME); +MODULE_FIRMWARE(BCM43143_NVRAM_NAME); +MODULE_FIRMWARE(BCM43241B0_FIRMWARE_NAME); +MODULE_FIRMWARE(BCM43241B0_NVRAM_NAME); +MODULE_FIRMWARE(BCM43241B4_FIRMWARE_NAME); +MODULE_FIRMWARE(BCM43241B4_NVRAM_NAME); +MODULE_FIRMWARE(BCM4329_FIRMWARE_NAME); +MODULE_FIRMWARE(BCM4329_NVRAM_NAME); +MODULE_FIRMWARE(BCM4330_FIRMWARE_NAME); +MODULE_FIRMWARE(BCM4330_NVRAM_NAME); +MODULE_FIRMWARE(BCM4334_FIRMWARE_NAME); +MODULE_FIRMWARE(BCM4334_NVRAM_NAME); +MODULE_FIRMWARE(BCM4335_FIRMWARE_NAME); +MODULE_FIRMWARE(BCM4335_NVRAM_NAME); +MODULE_FIRMWARE(BCM43362_FIRMWARE_NAME); +MODULE_FIRMWARE(BCM43362_NVRAM_NAME); +MODULE_FIRMWARE(BCM4339_FIRMWARE_NAME); +MODULE_FIRMWARE(BCM4339_NVRAM_NAME); +MODULE_FIRMWARE(BCM4354_FIRMWARE_NAME); +MODULE_FIRMWARE(BCM4354_NVRAM_NAME); + +struct brcmf_firmware_names { + u32 chipid; + u32 revmsk; + const char *bin; + const char *nv; +}; + +enum brcmf_firmware_type { + BRCMF_FIRMWARE_BIN, + BRCMF_FIRMWARE_NVRAM +}; + +#define BRCMF_FIRMWARE_NVRAM(name) \ + name ## _FIRMWARE_NAME, name ## _NVRAM_NAME + +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_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) }, + { BRCM_CC_4335_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4335) }, + { BRCM_CC_43362_CHIP_ID, 0xFFFFFFFE, BRCMF_FIRMWARE_NVRAM(BCM43362) }, + { BRCM_CC_4339_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4339) }, + { BRCM_CC_4354_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4354) } +}; + +static int brcmf_sdio_get_fwnames(struct brcmf_chip *ci, + struct brcmf_sdio_dev *sdiodev) +{ + int i; + uint fw_len, nv_len; + char end; + + for (i = 0; i < ARRAY_SIZE(brcmf_fwname_data); i++) { + if (brcmf_fwname_data[i].chipid == ci->chip && + brcmf_fwname_data[i].revmsk & BIT(ci->chiprev)) + break; + } + + if (i == ARRAY_SIZE(brcmf_fwname_data)) { + brcmf_err("Unknown chipid %d [%d]\n", ci->chip, ci->chiprev); + return -ENODEV; + } + + fw_len = sizeof(sdiodev->fw_name) - 1; + nv_len = sizeof(sdiodev->nvram_name) - 1; + /* check if firmware path is provided by module parameter */ + if (brcmf_firmware_path[0] != '\0') { + strncpy(sdiodev->fw_name, brcmf_firmware_path, fw_len); + strncpy(sdiodev->nvram_name, brcmf_firmware_path, nv_len); + fw_len -= strlen(sdiodev->fw_name); + nv_len -= strlen(sdiodev->nvram_name); + + end = brcmf_firmware_path[strlen(brcmf_firmware_path) - 1]; + if (end != '/') { + strncat(sdiodev->fw_name, "/", fw_len); + strncat(sdiodev->nvram_name, "/", nv_len); + fw_len--; + nv_len--; + } + } + strncat(sdiodev->fw_name, brcmf_fwname_data[i].bin, fw_len); + strncat(sdiodev->nvram_name, brcmf_fwname_data[i].nv, nv_len); + + return 0; +} + +static void pkt_align(struct sk_buff *p, int len, int align) +{ + uint datalign; + datalign = (unsigned long)(p->data); + datalign = roundup(datalign, (align)) - datalign; + if (datalign) + skb_pull(p, datalign); + __skb_trim(p, len); +} + +/* To check if there's window offered */ +static bool data_ok(struct brcmf_sdio *bus) +{ + return (u8)(bus->tx_max - bus->tx_seq) != 0 && + ((u8)(bus->tx_max - bus->tx_seq) & 0x80) == 0; +} + +/* + * Reads a register in the SDIO hardware block. This block occupies a series of + * adresses on the 32 bit backplane bus. + */ +static int r_sdreg32(struct brcmf_sdio *bus, u32 *regvar, u32 offset) +{ + struct brcmf_core *core; + int ret; + + core = brcmf_chip_get_core(bus->ci, BCMA_CORE_SDIO_DEV); + *regvar = brcmf_sdiod_regrl(bus->sdiodev, core->base + offset, &ret); + + return ret; +} + +static int w_sdreg32(struct brcmf_sdio *bus, u32 regval, u32 reg_offset) +{ + struct brcmf_core *core; + int ret; + + core = brcmf_chip_get_core(bus->ci, BCMA_CORE_SDIO_DEV); + brcmf_sdiod_regwl(bus->sdiodev, core->base + reg_offset, regval, &ret); + + return ret; +} + +static int +brcmf_sdio_kso_control(struct brcmf_sdio *bus, bool on) +{ + u8 wr_val = 0, rd_val, cmp_val, bmask; + int err = 0; + int try_cnt = 0; + + brcmf_dbg(TRACE, "Enter: on=%d\n", on); + + wr_val = (on << SBSDIO_FUNC1_SLEEPCSR_KSO_SHIFT); + /* 1st KSO write goes to AOS wake up core if device is asleep */ + brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_SLEEPCSR, + wr_val, &err); + + if (on) { + /* device WAKEUP through KSO: + * write bit 0 & read back until + * both bits 0 (kso bit) & 1 (dev on status) are set + */ + cmp_val = SBSDIO_FUNC1_SLEEPCSR_KSO_MASK | + SBSDIO_FUNC1_SLEEPCSR_DEVON_MASK; + bmask = cmp_val; + usleep_range(2000, 3000); + } else { + /* Put device to sleep, turn off KSO */ + cmp_val = 0; + /* only check for bit0, bit1(dev on status) may not + * get cleared right away + */ + bmask = SBSDIO_FUNC1_SLEEPCSR_KSO_MASK; + } + + do { + /* reliable KSO bit set/clr: + * the sdiod sleep write access is synced to PMU 32khz clk + * just one write attempt may fail, + * read it back until it matches written value + */ + rd_val = brcmf_sdiod_regrb(bus->sdiodev, SBSDIO_FUNC1_SLEEPCSR, + &err); + if (((rd_val & bmask) == cmp_val) && !err) + break; + + udelay(KSO_WAIT_US); + brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_SLEEPCSR, + wr_val, &err); + } while (try_cnt++ < MAX_KSO_ATTEMPTS); + + if (try_cnt > 2) + brcmf_dbg(SDIO, "try_cnt=%d rd_val=0x%x err=%d\n", try_cnt, + rd_val, err); + + if (try_cnt > MAX_KSO_ATTEMPTS) + brcmf_err("max tries: rd_val=0x%x err=%d\n", rd_val, err); + + return err; +} + +#define HOSTINTMASK (I_HMB_SW_MASK | I_CHIPACTIVE) + +/* Turn backplane clock on or off */ +static int brcmf_sdio_htclk(struct brcmf_sdio *bus, bool on, bool pendok) +{ + int err; + u8 clkctl, clkreq, devctl; + unsigned long timeout; + + brcmf_dbg(SDIO, "Enter\n"); + + clkctl = 0; + + if (bus->sr_enabled) { + bus->clkstate = (on ? CLK_AVAIL : CLK_SDONLY); + return 0; + } + + if (on) { + /* Request HT Avail */ + clkreq = + bus->alp_only ? SBSDIO_ALP_AVAIL_REQ : SBSDIO_HT_AVAIL_REQ; + + brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, + clkreq, &err); + if (err) { + brcmf_err("HT Avail request error: %d\n", err); + return -EBADE; + } + + /* Check current status */ + clkctl = brcmf_sdiod_regrb(bus->sdiodev, + SBSDIO_FUNC1_CHIPCLKCSR, &err); + if (err) { + brcmf_err("HT Avail read error: %d\n", err); + return -EBADE; + } + + /* Go to pending and await interrupt if appropriate */ + if (!SBSDIO_CLKAV(clkctl, bus->alp_only) && pendok) { + /* Allow only clock-available interrupt */ + devctl = brcmf_sdiod_regrb(bus->sdiodev, + SBSDIO_DEVICE_CTL, &err); + if (err) { + brcmf_err("Devctl error setting CA: %d\n", + err); + return -EBADE; + } + + devctl |= SBSDIO_DEVCTL_CA_INT_ONLY; + brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_DEVICE_CTL, + devctl, &err); + brcmf_dbg(SDIO, "CLKCTL: set PENDING\n"); + bus->clkstate = CLK_PENDING; + + return 0; + } else if (bus->clkstate == CLK_PENDING) { + /* Cancel CA-only interrupt filter */ + devctl = brcmf_sdiod_regrb(bus->sdiodev, + SBSDIO_DEVICE_CTL, &err); + devctl &= ~SBSDIO_DEVCTL_CA_INT_ONLY; + brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_DEVICE_CTL, + devctl, &err); + } + + /* Otherwise, wait here (polling) for HT Avail */ + timeout = jiffies + + msecs_to_jiffies(PMU_MAX_TRANSITION_DLY/1000); + while (!SBSDIO_CLKAV(clkctl, bus->alp_only)) { + clkctl = brcmf_sdiod_regrb(bus->sdiodev, + SBSDIO_FUNC1_CHIPCLKCSR, + &err); + if (time_after(jiffies, timeout)) + break; + else + usleep_range(5000, 10000); + } + if (err) { + brcmf_err("HT Avail request error: %d\n", err); + return -EBADE; + } + if (!SBSDIO_CLKAV(clkctl, bus->alp_only)) { + brcmf_err("HT Avail timeout (%d): clkctl 0x%02x\n", + PMU_MAX_TRANSITION_DLY, clkctl); + return -EBADE; + } + + /* Mark clock available */ + bus->clkstate = CLK_AVAIL; + brcmf_dbg(SDIO, "CLKCTL: turned ON\n"); + +#if defined(DEBUG) + if (!bus->alp_only) { + if (SBSDIO_ALPONLY(clkctl)) + brcmf_err("HT Clock should be on\n"); + } +#endif /* defined (DEBUG) */ + + } else { + clkreq = 0; + + if (bus->clkstate == CLK_PENDING) { + /* Cancel CA-only interrupt filter */ + devctl = brcmf_sdiod_regrb(bus->sdiodev, + SBSDIO_DEVICE_CTL, &err); + devctl &= ~SBSDIO_DEVCTL_CA_INT_ONLY; + brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_DEVICE_CTL, + devctl, &err); + } + + bus->clkstate = CLK_SDONLY; + brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, + clkreq, &err); + brcmf_dbg(SDIO, "CLKCTL: turned OFF\n"); + if (err) { + brcmf_err("Failed access turning clock off: %d\n", + err); + return -EBADE; + } + } + return 0; +} + +/* Change idle/active SD state */ +static int brcmf_sdio_sdclk(struct brcmf_sdio *bus, bool on) +{ + brcmf_dbg(SDIO, "Enter\n"); + + if (on) + bus->clkstate = CLK_SDONLY; + else + bus->clkstate = CLK_NONE; + + return 0; +} + +/* Transition SD and backplane clock readiness */ +static int brcmf_sdio_clkctl(struct brcmf_sdio *bus, uint target, bool pendok) +{ +#ifdef DEBUG + uint oldstate = bus->clkstate; +#endif /* DEBUG */ + + brcmf_dbg(SDIO, "Enter\n"); + + /* Early exit if we're already there */ + if (bus->clkstate == target) { + if (target == CLK_AVAIL) { + brcmf_sdio_wd_timer(bus, BRCMF_WD_POLL_MS); + bus->activity = true; + } + return 0; + } + + switch (target) { + case CLK_AVAIL: + /* Make sure SD clock is available */ + if (bus->clkstate == CLK_NONE) + brcmf_sdio_sdclk(bus, true); + /* Now request HT Avail on the backplane */ + brcmf_sdio_htclk(bus, true, pendok); + brcmf_sdio_wd_timer(bus, BRCMF_WD_POLL_MS); + bus->activity = true; + break; + + case CLK_SDONLY: + /* Remove HT request, or bring up SD clock */ + if (bus->clkstate == CLK_NONE) + brcmf_sdio_sdclk(bus, true); + else if (bus->clkstate == CLK_AVAIL) + brcmf_sdio_htclk(bus, false, false); + else + brcmf_err("request for %d -> %d\n", + bus->clkstate, target); + brcmf_sdio_wd_timer(bus, BRCMF_WD_POLL_MS); + break; + + case CLK_NONE: + /* Make sure to remove HT request */ + if (bus->clkstate == CLK_AVAIL) + brcmf_sdio_htclk(bus, false, false); + /* Now remove the SD clock */ + brcmf_sdio_sdclk(bus, false); + brcmf_sdio_wd_timer(bus, 0); + break; + } +#ifdef DEBUG + brcmf_dbg(SDIO, "%d -> %d\n", oldstate, bus->clkstate); +#endif /* DEBUG */ + + return 0; +} + +static int +brcmf_sdio_bus_sleep(struct brcmf_sdio *bus, bool sleep, bool pendok) +{ + int err = 0; + u8 clkcsr; + + brcmf_dbg(SDIO, "Enter: request %s currently %s\n", + (sleep ? "SLEEP" : "WAKE"), + (bus->sleeping ? "SLEEP" : "WAKE")); + + /* If SR is enabled control bus state with KSO */ + if (bus->sr_enabled) { + /* Done if we're already in the requested state */ + if (sleep == bus->sleeping) + goto end; + + /* Going to sleep */ + if (sleep) { + /* Don't sleep if something is pending */ + if (atomic_read(&bus->intstatus) || + atomic_read(&bus->ipend) > 0 || + (!atomic_read(&bus->fcstate) && + brcmu_pktq_mlen(&bus->txq, ~bus->flowcontrol) && + data_ok(bus))) { + err = -EBUSY; + goto done; + } + + clkcsr = brcmf_sdiod_regrb(bus->sdiodev, + SBSDIO_FUNC1_CHIPCLKCSR, + &err); + if ((clkcsr & SBSDIO_CSR_MASK) == 0) { + brcmf_dbg(SDIO, "no clock, set ALP\n"); + brcmf_sdiod_regwb(bus->sdiodev, + SBSDIO_FUNC1_CHIPCLKCSR, + SBSDIO_ALP_AVAIL_REQ, &err); + } + err = brcmf_sdio_kso_control(bus, false); + /* disable watchdog */ + if (!err) + brcmf_sdio_wd_timer(bus, 0); + } else { + bus->idlecount = 0; + err = brcmf_sdio_kso_control(bus, true); + } + if (!err) { + /* Change state */ + bus->sleeping = sleep; + brcmf_dbg(SDIO, "new state %s\n", + (sleep ? "SLEEP" : "WAKE")); + } else { + brcmf_err("error while changing bus sleep state %d\n", + err); + goto done; + } + } + +end: + /* control clocks */ + if (sleep) { + if (!bus->sr_enabled) + brcmf_sdio_clkctl(bus, CLK_NONE, pendok); + } else { + brcmf_sdio_clkctl(bus, CLK_AVAIL, pendok); + } +done: + brcmf_dbg(SDIO, "Exit: err=%d\n", err); + return err; + +} + +#ifdef DEBUG +static inline bool brcmf_sdio_valid_shared_address(u32 addr) +{ + return !(addr == 0 || ((~addr >> 16) & 0xffff) == (addr & 0xffff)); +} + +static int brcmf_sdio_readshared(struct brcmf_sdio *bus, + struct sdpcm_shared *sh) +{ + u32 addr; + int rv; + u32 shaddr = 0; + struct sdpcm_shared_le sh_le; + __le32 addr_le; + + shaddr = bus->ci->rambase + bus->ramsize - 4; + + /* + * Read last word in socram to determine + * address of sdpcm_shared structure + */ + sdio_claim_host(bus->sdiodev->func[1]); + brcmf_sdio_bus_sleep(bus, false, false); + rv = brcmf_sdiod_ramrw(bus->sdiodev, false, shaddr, (u8 *)&addr_le, 4); + sdio_release_host(bus->sdiodev->func[1]); + if (rv < 0) + return rv; + + addr = le32_to_cpu(addr_le); + + brcmf_dbg(SDIO, "sdpcm_shared address 0x%08X\n", addr); + + /* + * Check if addr is valid. + * NVRAM length at the end of memory should have been overwritten. + */ + if (!brcmf_sdio_valid_shared_address(addr)) { + brcmf_err("invalid sdpcm_shared address 0x%08X\n", + addr); + return -EINVAL; + } + + /* Read hndrte_shared structure */ + rv = brcmf_sdiod_ramrw(bus->sdiodev, false, addr, (u8 *)&sh_le, + sizeof(struct sdpcm_shared_le)); + if (rv < 0) + return rv; + + /* Endianness */ + sh->flags = le32_to_cpu(sh_le.flags); + sh->trap_addr = le32_to_cpu(sh_le.trap_addr); + sh->assert_exp_addr = le32_to_cpu(sh_le.assert_exp_addr); + sh->assert_file_addr = le32_to_cpu(sh_le.assert_file_addr); + sh->assert_line = le32_to_cpu(sh_le.assert_line); + sh->console_addr = le32_to_cpu(sh_le.console_addr); + sh->msgtrace_addr = le32_to_cpu(sh_le.msgtrace_addr); + + if ((sh->flags & SDPCM_SHARED_VERSION_MASK) > SDPCM_SHARED_VERSION) { + brcmf_err("sdpcm shared version unsupported: dhd %d dongle %d\n", + SDPCM_SHARED_VERSION, + sh->flags & SDPCM_SHARED_VERSION_MASK); + return -EPROTO; + } + + return 0; +} + +static void brcmf_sdio_get_console_addr(struct brcmf_sdio *bus) +{ + struct sdpcm_shared sh; + + if (brcmf_sdio_readshared(bus, &sh) == 0) + bus->console_addr = sh.console_addr; +} +#else +static void brcmf_sdio_get_console_addr(struct brcmf_sdio *bus) +{ +} +#endif /* DEBUG */ + +static u32 brcmf_sdio_hostmail(struct brcmf_sdio *bus) +{ + u32 intstatus = 0; + u32 hmb_data; + u8 fcbits; + int ret; + + brcmf_dbg(SDIO, "Enter\n"); + + /* Read mailbox data and ack that we did so */ + ret = r_sdreg32(bus, &hmb_data, + offsetof(struct sdpcmd_regs, tohostmailboxdata)); + + if (ret == 0) + w_sdreg32(bus, SMB_INT_ACK, + offsetof(struct sdpcmd_regs, tosbmailbox)); + bus->sdcnt.f1regdata += 2; + + /* Dongle recomposed rx frames, accept them again */ + if (hmb_data & HMB_DATA_NAKHANDLED) { + brcmf_dbg(SDIO, "Dongle reports NAK handled, expect rtx of %d\n", + bus->rx_seq); + if (!bus->rxskip) + brcmf_err("unexpected NAKHANDLED!\n"); + + bus->rxskip = false; + intstatus |= I_HMB_FRAME_IND; + } + + /* + * DEVREADY does not occur with gSPI. + */ + if (hmb_data & (HMB_DATA_DEVREADY | HMB_DATA_FWREADY)) { + bus->sdpcm_ver = + (hmb_data & HMB_DATA_VERSION_MASK) >> + HMB_DATA_VERSION_SHIFT; + if (bus->sdpcm_ver != SDPCM_PROT_VERSION) + brcmf_err("Version mismatch, dongle reports %d, " + "expecting %d\n", + bus->sdpcm_ver, SDPCM_PROT_VERSION); + else + brcmf_dbg(SDIO, "Dongle ready, protocol version %d\n", + bus->sdpcm_ver); + + /* + * Retrieve console state address now that firmware should have + * updated it. + */ + brcmf_sdio_get_console_addr(bus); + } + + /* + * Flow Control has been moved into the RX headers and this out of band + * method isn't used any more. + * remaining backward compatible with older dongles. + */ + if (hmb_data & HMB_DATA_FC) { + fcbits = (hmb_data & HMB_DATA_FCDATA_MASK) >> + HMB_DATA_FCDATA_SHIFT; + + if (fcbits & ~bus->flowcontrol) + bus->sdcnt.fc_xoff++; + + if (bus->flowcontrol & ~fcbits) + bus->sdcnt.fc_xon++; + + bus->sdcnt.fc_rcvd++; + bus->flowcontrol = fcbits; + } + + /* Shouldn't be any others */ + if (hmb_data & ~(HMB_DATA_DEVREADY | + HMB_DATA_NAKHANDLED | + HMB_DATA_FC | + HMB_DATA_FWREADY | + HMB_DATA_FCDATA_MASK | HMB_DATA_VERSION_MASK)) + brcmf_err("Unknown mailbox data content: 0x%02x\n", + hmb_data); + + return intstatus; +} + +static void brcmf_sdio_rxfail(struct brcmf_sdio *bus, bool abort, bool rtx) +{ + uint retries = 0; + u16 lastrbc; + u8 hi, lo; + int err; + + brcmf_err("%sterminate frame%s\n", + abort ? "abort command, " : "", + rtx ? ", send NAK" : ""); + + if (abort) + brcmf_sdiod_abort(bus->sdiodev, SDIO_FUNC_2); + + brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_FRAMECTRL, + SFC_RF_TERM, &err); + bus->sdcnt.f1regdata++; + + /* Wait until the packet has been flushed (device/FIFO stable) */ + for (lastrbc = retries = 0xffff; retries > 0; retries--) { + hi = brcmf_sdiod_regrb(bus->sdiodev, + SBSDIO_FUNC1_RFRAMEBCHI, &err); + lo = brcmf_sdiod_regrb(bus->sdiodev, + SBSDIO_FUNC1_RFRAMEBCLO, &err); + bus->sdcnt.f1regdata += 2; + + if ((hi == 0) && (lo == 0)) + break; + + if ((hi > (lastrbc >> 8)) && (lo > (lastrbc & 0x00ff))) { + brcmf_err("count growing: last 0x%04x now 0x%04x\n", + lastrbc, (hi << 8) + lo); + } + lastrbc = (hi << 8) + lo; + } + + if (!retries) + brcmf_err("count never zeroed: last 0x%04x\n", lastrbc); + else + brcmf_dbg(SDIO, "flush took %d iterations\n", 0xffff - retries); + + if (rtx) { + bus->sdcnt.rxrtx++; + err = w_sdreg32(bus, SMB_NAK, + offsetof(struct sdpcmd_regs, tosbmailbox)); + + bus->sdcnt.f1regdata++; + if (err == 0) + bus->rxskip = true; + } + + /* Clear partial in any case */ + bus->cur_read.len = 0; +} + +static void brcmf_sdio_txfail(struct brcmf_sdio *bus) +{ + struct brcmf_sdio_dev *sdiodev = bus->sdiodev; + u8 i, hi, lo; + + /* On failure, abort the command and terminate the frame */ + brcmf_err("sdio error, abort command and terminate frame\n"); + bus->sdcnt.tx_sderrs++; + + brcmf_sdiod_abort(sdiodev, SDIO_FUNC_2); + brcmf_sdiod_regwb(sdiodev, SBSDIO_FUNC1_FRAMECTRL, SFC_WF_TERM, NULL); + bus->sdcnt.f1regdata++; + + for (i = 0; i < 3; i++) { + hi = brcmf_sdiod_regrb(sdiodev, SBSDIO_FUNC1_WFRAMEBCHI, NULL); + lo = brcmf_sdiod_regrb(sdiodev, SBSDIO_FUNC1_WFRAMEBCLO, NULL); + bus->sdcnt.f1regdata += 2; + if ((hi == 0) && (lo == 0)) + break; + } +} + +/* return total length of buffer chain */ +static uint brcmf_sdio_glom_len(struct brcmf_sdio *bus) +{ + struct sk_buff *p; + uint total; + + total = 0; + skb_queue_walk(&bus->glom, p) + total += p->len; + return total; +} + +static void brcmf_sdio_free_glom(struct brcmf_sdio *bus) +{ + struct sk_buff *cur, *next; + + skb_queue_walk_safe(&bus->glom, cur, next) { + skb_unlink(cur, &bus->glom); + brcmu_pkt_buf_free_skb(cur); + } +} + +/** + * brcmfmac sdio bus specific header + * This is the lowest layer header wrapped on the packets transmitted between + * host and WiFi dongle which contains information needed for SDIO core and + * firmware + * + * It consists of 3 parts: hardware header, hardware extension header and + * software header + * hardware header (frame tag) - 4 bytes + * Byte 0~1: Frame length + * Byte 2~3: Checksum, bit-wise inverse of frame length + * hardware extension header - 8 bytes + * Tx glom mode only, N/A for Rx or normal Tx + * Byte 0~1: Packet length excluding hw frame tag + * Byte 2: Reserved + * Byte 3: Frame flags, bit 0: last frame indication + * Byte 4~5: Reserved + * Byte 6~7: Tail padding length + * software header - 8 bytes + * Byte 0: Rx/Tx sequence number + * Byte 1: 4 MSB Channel number, 4 LSB arbitrary flag + * Byte 2: Length of next data frame, reserved for Tx + * Byte 3: Data offset + * Byte 4: Flow control bits, reserved for Tx + * Byte 5: Maximum Sequence number allowed by firmware for Tx, N/A for Tx packet + * Byte 6~7: Reserved + */ +#define SDPCM_HWHDR_LEN 4 +#define SDPCM_HWEXT_LEN 8 +#define SDPCM_SWHDR_LEN 8 +#define SDPCM_HDRLEN (SDPCM_HWHDR_LEN + SDPCM_SWHDR_LEN) +/* software header */ +#define SDPCM_SEQ_MASK 0x000000ff +#define SDPCM_SEQ_WRAP 256 +#define SDPCM_CHANNEL_MASK 0x00000f00 +#define SDPCM_CHANNEL_SHIFT 8 +#define SDPCM_CONTROL_CHANNEL 0 /* Control */ +#define SDPCM_EVENT_CHANNEL 1 /* Asyc Event Indication */ +#define SDPCM_DATA_CHANNEL 2 /* Data Xmit/Recv */ +#define SDPCM_GLOM_CHANNEL 3 /* Coalesced packets */ +#define SDPCM_TEST_CHANNEL 15 /* Test/debug packets */ +#define SDPCM_GLOMDESC(p) (((u8 *)p)[1] & 0x80) +#define SDPCM_NEXTLEN_MASK 0x00ff0000 +#define SDPCM_NEXTLEN_SHIFT 16 +#define SDPCM_DOFFSET_MASK 0xff000000 +#define SDPCM_DOFFSET_SHIFT 24 +#define SDPCM_FCMASK_MASK 0x000000ff +#define SDPCM_WINDOW_MASK 0x0000ff00 +#define SDPCM_WINDOW_SHIFT 8 + +static inline u8 brcmf_sdio_getdatoffset(u8 *swheader) +{ + u32 hdrvalue; + hdrvalue = *(u32 *)swheader; + return (u8)((hdrvalue & SDPCM_DOFFSET_MASK) >> SDPCM_DOFFSET_SHIFT); +} + +static int brcmf_sdio_hdparse(struct brcmf_sdio *bus, u8 *header, + struct brcmf_sdio_hdrinfo *rd, + enum brcmf_sdio_frmtype type) +{ + u16 len, checksum; + u8 rx_seq, fc, tx_seq_max; + u32 swheader; + + trace_brcmf_sdpcm_hdr(SDPCM_RX, header); + + /* hw header */ + len = get_unaligned_le16(header); + checksum = get_unaligned_le16(header + sizeof(u16)); + /* All zero means no more to read */ + if (!(len | checksum)) { + bus->rxpending = false; + return -ENODATA; + } + if ((u16)(~(len ^ checksum))) { + brcmf_err("HW header checksum error\n"); + bus->sdcnt.rx_badhdr++; + brcmf_sdio_rxfail(bus, false, false); + return -EIO; + } + if (len < SDPCM_HDRLEN) { + brcmf_err("HW header length error\n"); + return -EPROTO; + } + if (type == BRCMF_SDIO_FT_SUPER && + (roundup(len, bus->blocksize) != rd->len)) { + brcmf_err("HW superframe header length error\n"); + return -EPROTO; + } + if (type == BRCMF_SDIO_FT_SUB && len > rd->len) { + brcmf_err("HW subframe header length error\n"); + return -EPROTO; + } + rd->len = len; + + /* software header */ + header += SDPCM_HWHDR_LEN; + swheader = le32_to_cpu(*(__le32 *)header); + if (type == BRCMF_SDIO_FT_SUPER && SDPCM_GLOMDESC(header)) { + brcmf_err("Glom descriptor found in superframe head\n"); + rd->len = 0; + return -EINVAL; + } + rx_seq = (u8)(swheader & SDPCM_SEQ_MASK); + rd->channel = (swheader & SDPCM_CHANNEL_MASK) >> SDPCM_CHANNEL_SHIFT; + if (len > MAX_RX_DATASZ && rd->channel != SDPCM_CONTROL_CHANNEL && + type != BRCMF_SDIO_FT_SUPER) { + brcmf_err("HW header length too long\n"); + bus->sdcnt.rx_toolong++; + brcmf_sdio_rxfail(bus, false, false); + rd->len = 0; + return -EPROTO; + } + if (type == BRCMF_SDIO_FT_SUPER && rd->channel != SDPCM_GLOM_CHANNEL) { + brcmf_err("Wrong channel for superframe\n"); + rd->len = 0; + return -EINVAL; + } + if (type == BRCMF_SDIO_FT_SUB && rd->channel != SDPCM_DATA_CHANNEL && + rd->channel != SDPCM_EVENT_CHANNEL) { + brcmf_err("Wrong channel for subframe\n"); + rd->len = 0; + return -EINVAL; + } + rd->dat_offset = brcmf_sdio_getdatoffset(header); + if (rd->dat_offset < SDPCM_HDRLEN || rd->dat_offset > rd->len) { + brcmf_err("seq %d: bad data offset\n", rx_seq); + bus->sdcnt.rx_badhdr++; + brcmf_sdio_rxfail(bus, false, false); + rd->len = 0; + return -ENXIO; + } + if (rd->seq_num != rx_seq) { + brcmf_err("seq %d: sequence number error, expect %d\n", + rx_seq, rd->seq_num); + bus->sdcnt.rx_badseq++; + rd->seq_num = rx_seq; + } + /* no need to check the reset for subframe */ + if (type == BRCMF_SDIO_FT_SUB) + return 0; + rd->len_nxtfrm = (swheader & SDPCM_NEXTLEN_MASK) >> SDPCM_NEXTLEN_SHIFT; + if (rd->len_nxtfrm << 4 > MAX_RX_DATASZ) { + /* only warm for NON glom packet */ + if (rd->channel != SDPCM_GLOM_CHANNEL) + brcmf_err("seq %d: next length error\n", rx_seq); + rd->len_nxtfrm = 0; + } + swheader = le32_to_cpu(*(__le32 *)(header + 4)); + fc = swheader & SDPCM_FCMASK_MASK; + if (bus->flowcontrol != fc) { + if (~bus->flowcontrol & fc) + bus->sdcnt.fc_xoff++; + if (bus->flowcontrol & ~fc) + bus->sdcnt.fc_xon++; + bus->sdcnt.fc_rcvd++; + bus->flowcontrol = fc; + } + tx_seq_max = (swheader & SDPCM_WINDOW_MASK) >> SDPCM_WINDOW_SHIFT; + if ((u8)(tx_seq_max - bus->tx_seq) > 0x40) { + brcmf_err("seq %d: max tx seq number error\n", rx_seq); + tx_seq_max = bus->tx_seq + 2; + } + bus->tx_max = tx_seq_max; + + return 0; +} + +static inline void brcmf_sdio_update_hwhdr(u8 *header, u16 frm_length) +{ + *(__le16 *)header = cpu_to_le16(frm_length); + *(((__le16 *)header) + 1) = cpu_to_le16(~frm_length); +} + +static void brcmf_sdio_hdpack(struct brcmf_sdio *bus, u8 *header, + struct brcmf_sdio_hdrinfo *hd_info) +{ + u32 hdrval; + u8 hdr_offset; + + brcmf_sdio_update_hwhdr(header, hd_info->len); + hdr_offset = SDPCM_HWHDR_LEN; + + if (bus->txglom) { + hdrval = (hd_info->len - hdr_offset) | (hd_info->lastfrm << 24); + *((__le32 *)(header + hdr_offset)) = cpu_to_le32(hdrval); + hdrval = (u16)hd_info->tail_pad << 16; + *(((__le32 *)(header + hdr_offset)) + 1) = cpu_to_le32(hdrval); + hdr_offset += SDPCM_HWEXT_LEN; + } + + hdrval = hd_info->seq_num; + hdrval |= (hd_info->channel << SDPCM_CHANNEL_SHIFT) & + SDPCM_CHANNEL_MASK; + hdrval |= (hd_info->dat_offset << SDPCM_DOFFSET_SHIFT) & + SDPCM_DOFFSET_MASK; + *((__le32 *)(header + hdr_offset)) = cpu_to_le32(hdrval); + *(((__le32 *)(header + hdr_offset)) + 1) = 0; + trace_brcmf_sdpcm_hdr(SDPCM_TX + !!(bus->txglom), header); +} + +static u8 brcmf_sdio_rxglom(struct brcmf_sdio *bus, u8 rxseq) +{ + u16 dlen, totlen; + u8 *dptr, num = 0; + u16 sublen; + struct sk_buff *pfirst, *pnext; + + int errcode; + u8 doff, sfdoff; + + struct brcmf_sdio_hdrinfo rd_new; + + /* If packets, issue read(s) and send up packet chain */ + /* Return sequence numbers consumed? */ + + brcmf_dbg(SDIO, "start: glomd %p glom %p\n", + bus->glomd, skb_peek(&bus->glom)); + + /* If there's a descriptor, generate the packet chain */ + if (bus->glomd) { + pfirst = pnext = NULL; + dlen = (u16) (bus->glomd->len); + dptr = bus->glomd->data; + if (!dlen || (dlen & 1)) { + brcmf_err("bad glomd len(%d), ignore descriptor\n", + dlen); + dlen = 0; + } + + for (totlen = num = 0; dlen; num++) { + /* Get (and move past) next length */ + sublen = get_unaligned_le16(dptr); + dlen -= sizeof(u16); + dptr += sizeof(u16); + if ((sublen < SDPCM_HDRLEN) || + ((num == 0) && (sublen < (2 * SDPCM_HDRLEN)))) { + brcmf_err("descriptor len %d bad: %d\n", + num, sublen); + pnext = NULL; + break; + } + if (sublen % bus->sgentry_align) { + brcmf_err("sublen %d not multiple of %d\n", + sublen, bus->sgentry_align); + } + totlen += sublen; + + /* For last frame, adjust read len so total + is a block multiple */ + if (!dlen) { + sublen += + (roundup(totlen, bus->blocksize) - totlen); + totlen = roundup(totlen, bus->blocksize); + } + + /* Allocate/chain packet for next subframe */ + pnext = brcmu_pkt_buf_get_skb(sublen + bus->sgentry_align); + if (pnext == NULL) { + brcmf_err("bcm_pkt_buf_get_skb failed, num %d len %d\n", + num, sublen); + break; + } + skb_queue_tail(&bus->glom, pnext); + + /* Adhere to start alignment requirements */ + pkt_align(pnext, sublen, bus->sgentry_align); + } + + /* If all allocations succeeded, save packet chain + in bus structure */ + if (pnext) { + brcmf_dbg(GLOM, "allocated %d-byte packet chain for %d subframes\n", + totlen, num); + if (BRCMF_GLOM_ON() && bus->cur_read.len && + totlen != bus->cur_read.len) { + brcmf_dbg(GLOM, "glomdesc mismatch: nextlen %d glomdesc %d rxseq %d\n", + bus->cur_read.len, totlen, rxseq); + } + pfirst = pnext = NULL; + } else { + brcmf_sdio_free_glom(bus); + num = 0; + } + + /* Done with descriptor packet */ + brcmu_pkt_buf_free_skb(bus->glomd); + bus->glomd = NULL; + bus->cur_read.len = 0; + } + + /* Ok -- either we just generated a packet chain, + or had one from before */ + if (!skb_queue_empty(&bus->glom)) { + if (BRCMF_GLOM_ON()) { + brcmf_dbg(GLOM, "try superframe read, packet chain:\n"); + skb_queue_walk(&bus->glom, pnext) { + brcmf_dbg(GLOM, " %p: %p len 0x%04x (%d)\n", + pnext, (u8 *) (pnext->data), + pnext->len, pnext->len); + } + } + + pfirst = skb_peek(&bus->glom); + dlen = (u16) brcmf_sdio_glom_len(bus); + + /* Do an SDIO read for the superframe. Configurable iovar to + * read directly into the chained packet, or allocate a large + * packet and and copy into the chain. + */ + sdio_claim_host(bus->sdiodev->func[1]); + errcode = brcmf_sdiod_recv_chain(bus->sdiodev, + &bus->glom, dlen); + sdio_release_host(bus->sdiodev->func[1]); + bus->sdcnt.f2rxdata++; + + /* On failure, kill the superframe, allow a couple retries */ + if (errcode < 0) { + brcmf_err("glom read of %d bytes failed: %d\n", + dlen, errcode); + + sdio_claim_host(bus->sdiodev->func[1]); + if (bus->glomerr++ < 3) { + brcmf_sdio_rxfail(bus, true, true); + } else { + bus->glomerr = 0; + brcmf_sdio_rxfail(bus, true, false); + bus->sdcnt.rxglomfail++; + brcmf_sdio_free_glom(bus); + } + sdio_release_host(bus->sdiodev->func[1]); + return 0; + } + + brcmf_dbg_hex_dump(BRCMF_GLOM_ON(), + pfirst->data, min_t(int, pfirst->len, 48), + "SUPERFRAME:\n"); + + rd_new.seq_num = rxseq; + rd_new.len = dlen; + sdio_claim_host(bus->sdiodev->func[1]); + errcode = brcmf_sdio_hdparse(bus, pfirst->data, &rd_new, + BRCMF_SDIO_FT_SUPER); + sdio_release_host(bus->sdiodev->func[1]); + bus->cur_read.len = rd_new.len_nxtfrm << 4; + + /* Remove superframe header, remember offset */ + skb_pull(pfirst, rd_new.dat_offset); + sfdoff = rd_new.dat_offset; + num = 0; + + /* Validate all the subframe headers */ + skb_queue_walk(&bus->glom, pnext) { + /* leave when invalid subframe is found */ + if (errcode) + break; + + rd_new.len = pnext->len; + rd_new.seq_num = rxseq++; + sdio_claim_host(bus->sdiodev->func[1]); + errcode = brcmf_sdio_hdparse(bus, pnext->data, &rd_new, + BRCMF_SDIO_FT_SUB); + sdio_release_host(bus->sdiodev->func[1]); + brcmf_dbg_hex_dump(BRCMF_GLOM_ON(), + pnext->data, 32, "subframe:\n"); + + num++; + } + + if (errcode) { + /* Terminate frame on error, request + a couple retries */ + sdio_claim_host(bus->sdiodev->func[1]); + if (bus->glomerr++ < 3) { + /* Restore superframe header space */ + skb_push(pfirst, sfdoff); + brcmf_sdio_rxfail(bus, true, true); + } else { + bus->glomerr = 0; + brcmf_sdio_rxfail(bus, true, false); + bus->sdcnt.rxglomfail++; + brcmf_sdio_free_glom(bus); + } + sdio_release_host(bus->sdiodev->func[1]); + bus->cur_read.len = 0; + return 0; + } + + /* Basic SD framing looks ok - process each packet (header) */ + + skb_queue_walk_safe(&bus->glom, pfirst, pnext) { + dptr = (u8 *) (pfirst->data); + sublen = get_unaligned_le16(dptr); + doff = brcmf_sdio_getdatoffset(&dptr[SDPCM_HWHDR_LEN]); + + brcmf_dbg_hex_dump(BRCMF_BYTES_ON() && BRCMF_DATA_ON(), + dptr, pfirst->len, + "Rx Subframe Data:\n"); + + __skb_trim(pfirst, sublen); + skb_pull(pfirst, doff); + + if (pfirst->len == 0) { + skb_unlink(pfirst, &bus->glom); + brcmu_pkt_buf_free_skb(pfirst); + continue; + } + + brcmf_dbg_hex_dump(BRCMF_GLOM_ON(), + pfirst->data, + min_t(int, pfirst->len, 32), + "subframe %d to stack, %p (%p/%d) nxt/lnk %p/%p\n", + bus->glom.qlen, pfirst, pfirst->data, + pfirst->len, pfirst->next, + pfirst->prev); + skb_unlink(pfirst, &bus->glom); + brcmf_rx_frame(bus->sdiodev->dev, pfirst); + bus->sdcnt.rxglompkts++; + } + + bus->sdcnt.rxglomframes++; + } + return num; +} + +static int brcmf_sdio_dcmd_resp_wait(struct brcmf_sdio *bus, uint *condition, + bool *pending) +{ + DECLARE_WAITQUEUE(wait, current); + int timeout = msecs_to_jiffies(DCMD_RESP_TIMEOUT); + + /* Wait until control frame is available */ + add_wait_queue(&bus->dcmd_resp_wait, &wait); + set_current_state(TASK_INTERRUPTIBLE); + + while (!(*condition) && (!signal_pending(current) && timeout)) + timeout = schedule_timeout(timeout); + + if (signal_pending(current)) + *pending = true; + + set_current_state(TASK_RUNNING); + remove_wait_queue(&bus->dcmd_resp_wait, &wait); + + return timeout; +} + +static int brcmf_sdio_dcmd_resp_wake(struct brcmf_sdio *bus) +{ + if (waitqueue_active(&bus->dcmd_resp_wait)) + wake_up_interruptible(&bus->dcmd_resp_wait); + + return 0; +} +static void +brcmf_sdio_read_control(struct brcmf_sdio *bus, u8 *hdr, uint len, uint doff) +{ + uint rdlen, pad; + u8 *buf = NULL, *rbuf; + int sdret; + + brcmf_dbg(TRACE, "Enter\n"); + + if (bus->rxblen) + buf = vzalloc(bus->rxblen); + if (!buf) + goto done; + + rbuf = bus->rxbuf; + pad = ((unsigned long)rbuf % bus->head_align); + if (pad) + rbuf += (bus->head_align - pad); + + /* Copy the already-read portion over */ + memcpy(buf, hdr, BRCMF_FIRSTREAD); + if (len <= BRCMF_FIRSTREAD) + goto gotpkt; + + /* Raise rdlen to next SDIO block to avoid tail command */ + rdlen = len - BRCMF_FIRSTREAD; + if (bus->roundup && bus->blocksize && (rdlen > bus->blocksize)) { + pad = bus->blocksize - (rdlen % bus->blocksize); + if ((pad <= bus->roundup) && (pad < bus->blocksize) && + ((len + pad) < bus->sdiodev->bus_if->maxctl)) + rdlen += pad; + } else if (rdlen % bus->head_align) { + rdlen += bus->head_align - (rdlen % bus->head_align); + } + + /* Drop if the read is too big or it exceeds our maximum */ + if ((rdlen + BRCMF_FIRSTREAD) > bus->sdiodev->bus_if->maxctl) { + brcmf_err("%d-byte control read exceeds %d-byte buffer\n", + rdlen, bus->sdiodev->bus_if->maxctl); + brcmf_sdio_rxfail(bus, false, false); + goto done; + } + + if ((len - doff) > bus->sdiodev->bus_if->maxctl) { + brcmf_err("%d-byte ctl frame (%d-byte ctl data) exceeds %d-byte limit\n", + len, len - doff, bus->sdiodev->bus_if->maxctl); + bus->sdcnt.rx_toolong++; + brcmf_sdio_rxfail(bus, false, false); + goto done; + } + + /* Read remain of frame body */ + sdret = brcmf_sdiod_recv_buf(bus->sdiodev, rbuf, rdlen); + bus->sdcnt.f2rxdata++; + + /* Control frame failures need retransmission */ + if (sdret < 0) { + brcmf_err("read %d control bytes failed: %d\n", + rdlen, sdret); + bus->sdcnt.rxc_errors++; + brcmf_sdio_rxfail(bus, true, true); + goto done; + } else + memcpy(buf + BRCMF_FIRSTREAD, rbuf, rdlen); + +gotpkt: + + brcmf_dbg_hex_dump(BRCMF_BYTES_ON() && BRCMF_CTL_ON(), + buf, len, "RxCtrl:\n"); + + /* Point to valid data and indicate its length */ + spin_lock_bh(&bus->rxctl_lock); + if (bus->rxctl) { + brcmf_err("last control frame is being processed.\n"); + spin_unlock_bh(&bus->rxctl_lock); + vfree(buf); + goto done; + } + bus->rxctl = buf + doff; + bus->rxctl_orig = buf; + bus->rxlen = len - doff; + spin_unlock_bh(&bus->rxctl_lock); + +done: + /* Awake any waiters */ + brcmf_sdio_dcmd_resp_wake(bus); +} + +/* Pad read to blocksize for efficiency */ +static void brcmf_sdio_pad(struct brcmf_sdio *bus, u16 *pad, u16 *rdlen) +{ + if (bus->roundup && bus->blocksize && *rdlen > bus->blocksize) { + *pad = bus->blocksize - (*rdlen % bus->blocksize); + if (*pad <= bus->roundup && *pad < bus->blocksize && + *rdlen + *pad + BRCMF_FIRSTREAD < MAX_RX_DATASZ) + *rdlen += *pad; + } else if (*rdlen % bus->head_align) { + *rdlen += bus->head_align - (*rdlen % bus->head_align); + } +} + +static uint brcmf_sdio_readframes(struct brcmf_sdio *bus, uint maxframes) +{ + struct sk_buff *pkt; /* Packet for event or data frames */ + u16 pad; /* Number of pad bytes to read */ + uint rxleft = 0; /* Remaining number of frames allowed */ + int ret; /* Return code from calls */ + uint rxcount = 0; /* Total frames read */ + struct brcmf_sdio_hdrinfo *rd = &bus->cur_read, rd_new; + u8 head_read = 0; + + brcmf_dbg(TRACE, "Enter\n"); + + /* Not finished unless we encounter no more frames indication */ + bus->rxpending = true; + + for (rd->seq_num = bus->rx_seq, rxleft = maxframes; + !bus->rxskip && rxleft && brcmf_bus_ready(bus->sdiodev->bus_if); + rd->seq_num++, rxleft--) { + + /* Handle glomming separately */ + if (bus->glomd || !skb_queue_empty(&bus->glom)) { + u8 cnt; + brcmf_dbg(GLOM, "calling rxglom: glomd %p, glom %p\n", + bus->glomd, skb_peek(&bus->glom)); + cnt = brcmf_sdio_rxglom(bus, rd->seq_num); + brcmf_dbg(GLOM, "rxglom returned %d\n", cnt); + rd->seq_num += cnt - 1; + rxleft = (rxleft > cnt) ? (rxleft - cnt) : 1; + continue; + } + + rd->len_left = rd->len; + /* read header first for unknow frame length */ + sdio_claim_host(bus->sdiodev->func[1]); + if (!rd->len) { + ret = brcmf_sdiod_recv_buf(bus->sdiodev, + bus->rxhdr, BRCMF_FIRSTREAD); + bus->sdcnt.f2rxhdrs++; + if (ret < 0) { + brcmf_err("RXHEADER FAILED: %d\n", + ret); + bus->sdcnt.rx_hdrfail++; + brcmf_sdio_rxfail(bus, true, true); + sdio_release_host(bus->sdiodev->func[1]); + continue; + } + + brcmf_dbg_hex_dump(BRCMF_BYTES_ON() || BRCMF_HDRS_ON(), + bus->rxhdr, SDPCM_HDRLEN, + "RxHdr:\n"); + + if (brcmf_sdio_hdparse(bus, bus->rxhdr, rd, + BRCMF_SDIO_FT_NORMAL)) { + sdio_release_host(bus->sdiodev->func[1]); + if (!bus->rxpending) + break; + else + continue; + } + + if (rd->channel == SDPCM_CONTROL_CHANNEL) { + brcmf_sdio_read_control(bus, bus->rxhdr, + rd->len, + rd->dat_offset); + /* prepare the descriptor for the next read */ + rd->len = rd->len_nxtfrm << 4; + rd->len_nxtfrm = 0; + /* treat all packet as event if we don't know */ + rd->channel = SDPCM_EVENT_CHANNEL; + sdio_release_host(bus->sdiodev->func[1]); + continue; + } + rd->len_left = rd->len > BRCMF_FIRSTREAD ? + rd->len - BRCMF_FIRSTREAD : 0; + head_read = BRCMF_FIRSTREAD; + } + + brcmf_sdio_pad(bus, &pad, &rd->len_left); + + pkt = brcmu_pkt_buf_get_skb(rd->len_left + head_read + + bus->head_align); + if (!pkt) { + /* Give up on data, request rtx of events */ + brcmf_err("brcmu_pkt_buf_get_skb failed\n"); + brcmf_sdio_rxfail(bus, false, + RETRYCHAN(rd->channel)); + sdio_release_host(bus->sdiodev->func[1]); + continue; + } + skb_pull(pkt, head_read); + pkt_align(pkt, rd->len_left, bus->head_align); + + ret = brcmf_sdiod_recv_pkt(bus->sdiodev, pkt); + bus->sdcnt.f2rxdata++; + sdio_release_host(bus->sdiodev->func[1]); + + if (ret < 0) { + brcmf_err("read %d bytes from channel %d failed: %d\n", + rd->len, rd->channel, ret); + brcmu_pkt_buf_free_skb(pkt); + sdio_claim_host(bus->sdiodev->func[1]); + brcmf_sdio_rxfail(bus, true, + RETRYCHAN(rd->channel)); + sdio_release_host(bus->sdiodev->func[1]); + continue; + } + + if (head_read) { + skb_push(pkt, head_read); + memcpy(pkt->data, bus->rxhdr, head_read); + head_read = 0; + } else { + memcpy(bus->rxhdr, pkt->data, SDPCM_HDRLEN); + rd_new.seq_num = rd->seq_num; + sdio_claim_host(bus->sdiodev->func[1]); + if (brcmf_sdio_hdparse(bus, bus->rxhdr, &rd_new, + BRCMF_SDIO_FT_NORMAL)) { + rd->len = 0; + brcmu_pkt_buf_free_skb(pkt); + } + bus->sdcnt.rx_readahead_cnt++; + if (rd->len != roundup(rd_new.len, 16)) { + brcmf_err("frame length mismatch:read %d, should be %d\n", + rd->len, + roundup(rd_new.len, 16) >> 4); + rd->len = 0; + brcmf_sdio_rxfail(bus, true, true); + sdio_release_host(bus->sdiodev->func[1]); + brcmu_pkt_buf_free_skb(pkt); + continue; + } + sdio_release_host(bus->sdiodev->func[1]); + rd->len_nxtfrm = rd_new.len_nxtfrm; + rd->channel = rd_new.channel; + rd->dat_offset = rd_new.dat_offset; + + brcmf_dbg_hex_dump(!(BRCMF_BYTES_ON() && + BRCMF_DATA_ON()) && + BRCMF_HDRS_ON(), + bus->rxhdr, SDPCM_HDRLEN, + "RxHdr:\n"); + + if (rd_new.channel == SDPCM_CONTROL_CHANNEL) { + brcmf_err("readahead on control packet %d?\n", + rd_new.seq_num); + /* Force retry w/normal header read */ + rd->len = 0; + sdio_claim_host(bus->sdiodev->func[1]); + brcmf_sdio_rxfail(bus, false, true); + sdio_release_host(bus->sdiodev->func[1]); + brcmu_pkt_buf_free_skb(pkt); + continue; + } + } + + brcmf_dbg_hex_dump(BRCMF_BYTES_ON() && BRCMF_DATA_ON(), + pkt->data, rd->len, "Rx Data:\n"); + + /* Save superframe descriptor and allocate packet frame */ + if (rd->channel == SDPCM_GLOM_CHANNEL) { + if (SDPCM_GLOMDESC(&bus->rxhdr[SDPCM_HWHDR_LEN])) { + brcmf_dbg(GLOM, "glom descriptor, %d bytes:\n", + rd->len); + brcmf_dbg_hex_dump(BRCMF_GLOM_ON(), + pkt->data, rd->len, + "Glom Data:\n"); + __skb_trim(pkt, rd->len); + skb_pull(pkt, SDPCM_HDRLEN); + bus->glomd = pkt; + } else { + brcmf_err("%s: glom superframe w/o " + "descriptor!\n", __func__); + sdio_claim_host(bus->sdiodev->func[1]); + brcmf_sdio_rxfail(bus, false, false); + sdio_release_host(bus->sdiodev->func[1]); + } + /* prepare the descriptor for the next read */ + rd->len = rd->len_nxtfrm << 4; + rd->len_nxtfrm = 0; + /* treat all packet as event if we don't know */ + rd->channel = SDPCM_EVENT_CHANNEL; + continue; + } + + /* Fill in packet len and prio, deliver upward */ + __skb_trim(pkt, rd->len); + skb_pull(pkt, rd->dat_offset); + + /* prepare the descriptor for the next read */ + rd->len = rd->len_nxtfrm << 4; + rd->len_nxtfrm = 0; + /* treat all packet as event if we don't know */ + rd->channel = SDPCM_EVENT_CHANNEL; + + if (pkt->len == 0) { + brcmu_pkt_buf_free_skb(pkt); + continue; + } + + brcmf_rx_frame(bus->sdiodev->dev, pkt); + } + + rxcount = maxframes - rxleft; + /* Message if we hit the limit */ + if (!rxleft) + brcmf_dbg(DATA, "hit rx limit of %d frames\n", maxframes); + else + brcmf_dbg(DATA, "processed %d frames\n", rxcount); + /* Back off rxseq if awaiting rtx, update rx_seq */ + if (bus->rxskip) + rd->seq_num--; + bus->rx_seq = rd->seq_num; + + return rxcount; +} + +static void +brcmf_sdio_wait_event_wakeup(struct brcmf_sdio *bus) +{ + if (waitqueue_active(&bus->ctrl_wait)) + wake_up_interruptible(&bus->ctrl_wait); + return; +} + +static int brcmf_sdio_txpkt_hdalign(struct brcmf_sdio *bus, struct sk_buff *pkt) +{ + u16 head_pad; + u8 *dat_buf; + + dat_buf = (u8 *)(pkt->data); + + /* Check head padding */ + head_pad = ((unsigned long)dat_buf % bus->head_align); + if (head_pad) { + if (skb_headroom(pkt) < head_pad) { + bus->sdiodev->bus_if->tx_realloc++; + head_pad = 0; + if (skb_cow(pkt, head_pad)) + return -ENOMEM; + } + skb_push(pkt, head_pad); + dat_buf = (u8 *)(pkt->data); + memset(dat_buf, 0, head_pad + bus->tx_hdrlen); + } + return head_pad; +} + +/** + * struct brcmf_skbuff_cb reserves first two bytes in sk_buff::cb for + * bus layer usage. + */ +/* flag marking a dummy skb added for DMA alignment requirement */ +#define ALIGN_SKB_FLAG 0x8000 +/* bit mask of data length chopped from the previous packet */ +#define ALIGN_SKB_CHOP_LEN_MASK 0x7fff + +static int brcmf_sdio_txpkt_prep_sg(struct brcmf_sdio *bus, + struct sk_buff_head *pktq, + struct sk_buff *pkt, u16 total_len) +{ + struct brcmf_sdio_dev *sdiodev; + struct sk_buff *pkt_pad; + u16 tail_pad, tail_chop, chain_pad; + unsigned int blksize; + bool lastfrm; + int ntail, ret; + + sdiodev = bus->sdiodev; + blksize = sdiodev->func[SDIO_FUNC_2]->cur_blksize; + /* sg entry alignment should be a divisor of block size */ + WARN_ON(blksize % bus->sgentry_align); + + /* Check tail padding */ + lastfrm = skb_queue_is_last(pktq, pkt); + tail_pad = 0; + tail_chop = pkt->len % bus->sgentry_align; + if (tail_chop) + tail_pad = bus->sgentry_align - tail_chop; + chain_pad = (total_len + tail_pad) % blksize; + if (lastfrm && chain_pad) + tail_pad += blksize - chain_pad; + if (skb_tailroom(pkt) < tail_pad && pkt->len > blksize) { + pkt_pad = brcmu_pkt_buf_get_skb(tail_pad + tail_chop + + bus->head_align); + if (pkt_pad == NULL) + return -ENOMEM; + ret = brcmf_sdio_txpkt_hdalign(bus, pkt_pad); + if (unlikely(ret < 0)) { + kfree_skb(pkt_pad); + return ret; + } + memcpy(pkt_pad->data, + pkt->data + pkt->len - tail_chop, + tail_chop); + *(u16 *)(pkt_pad->cb) = ALIGN_SKB_FLAG + tail_chop; + skb_trim(pkt, pkt->len - tail_chop); + skb_trim(pkt_pad, tail_pad + tail_chop); + __skb_queue_after(pktq, pkt, pkt_pad); + } else { + ntail = pkt->data_len + tail_pad - + (pkt->end - pkt->tail); + if (skb_cloned(pkt) || ntail > 0) + if (pskb_expand_head(pkt, 0, ntail, GFP_ATOMIC)) + return -ENOMEM; + if (skb_linearize(pkt)) + return -ENOMEM; + __skb_put(pkt, tail_pad); + } + + return tail_pad; +} + +/** + * brcmf_sdio_txpkt_prep - packet preparation for transmit + * @bus: brcmf_sdio structure pointer + * @pktq: packet list pointer + * @chan: virtual channel to transmit the packet + * + * Processes to be applied to the packet + * - Align data buffer pointer + * - Align data buffer length + * - Prepare header + * Return: negative value if there is error + */ +static int +brcmf_sdio_txpkt_prep(struct brcmf_sdio *bus, struct sk_buff_head *pktq, + uint chan) +{ + u16 head_pad, total_len; + struct sk_buff *pkt_next; + u8 txseq; + int ret; + struct brcmf_sdio_hdrinfo hd_info = {0}; + + txseq = bus->tx_seq; + total_len = 0; + skb_queue_walk(pktq, pkt_next) { + /* alignment packet inserted in previous + * loop cycle can be skipped as it is + * already properly aligned and does not + * need an sdpcm header. + */ + if (*(u16 *)(pkt_next->cb) & ALIGN_SKB_FLAG) + continue; + + /* align packet data pointer */ + ret = brcmf_sdio_txpkt_hdalign(bus, pkt_next); + if (ret < 0) + return ret; + head_pad = (u16)ret; + if (head_pad) + memset(pkt_next->data + bus->tx_hdrlen, 0, head_pad); + + total_len += pkt_next->len; + + hd_info.len = pkt_next->len; + hd_info.lastfrm = skb_queue_is_last(pktq, pkt_next); + if (bus->txglom && pktq->qlen > 1) { + ret = brcmf_sdio_txpkt_prep_sg(bus, pktq, + pkt_next, total_len); + if (ret < 0) + return ret; + hd_info.tail_pad = (u16)ret; + total_len += (u16)ret; + } + + hd_info.channel = chan; + hd_info.dat_offset = head_pad + bus->tx_hdrlen; + hd_info.seq_num = txseq++; + + /* Now fill the header */ + brcmf_sdio_hdpack(bus, pkt_next->data, &hd_info); + + if (BRCMF_BYTES_ON() && + ((BRCMF_CTL_ON() && chan == SDPCM_CONTROL_CHANNEL) || + (BRCMF_DATA_ON() && chan != SDPCM_CONTROL_CHANNEL))) + brcmf_dbg_hex_dump(true, pkt_next->data, hd_info.len, + "Tx Frame:\n"); + else if (BRCMF_HDRS_ON()) + brcmf_dbg_hex_dump(true, pkt_next->data, + head_pad + bus->tx_hdrlen, + "Tx Header:\n"); + } + /* Hardware length tag of the first packet should be total + * length of the chain (including padding) + */ + if (bus->txglom) + brcmf_sdio_update_hwhdr(pktq->next->data, total_len); + return 0; +} + +/** + * brcmf_sdio_txpkt_postp - packet post processing for transmit + * @bus: brcmf_sdio structure pointer + * @pktq: packet list pointer + * + * Processes to be applied to the packet + * - Remove head padding + * - Remove tail padding + */ +static void +brcmf_sdio_txpkt_postp(struct brcmf_sdio *bus, struct sk_buff_head *pktq) +{ + u8 *hdr; + u32 dat_offset; + u16 tail_pad; + u16 dummy_flags, chop_len; + struct sk_buff *pkt_next, *tmp, *pkt_prev; + + skb_queue_walk_safe(pktq, pkt_next, tmp) { + dummy_flags = *(u16 *)(pkt_next->cb); + if (dummy_flags & ALIGN_SKB_FLAG) { + chop_len = dummy_flags & ALIGN_SKB_CHOP_LEN_MASK; + if (chop_len) { + pkt_prev = pkt_next->prev; + skb_put(pkt_prev, chop_len); + } + __skb_unlink(pkt_next, pktq); + brcmu_pkt_buf_free_skb(pkt_next); + } else { + hdr = pkt_next->data + bus->tx_hdrlen - SDPCM_SWHDR_LEN; + dat_offset = le32_to_cpu(*(__le32 *)hdr); + dat_offset = (dat_offset & SDPCM_DOFFSET_MASK) >> + SDPCM_DOFFSET_SHIFT; + skb_pull(pkt_next, dat_offset); + if (bus->txglom) { + tail_pad = le16_to_cpu(*(__le16 *)(hdr - 2)); + skb_trim(pkt_next, pkt_next->len - tail_pad); + } + } + } +} + +/* Writes a HW/SW header into the packet and sends it. */ +/* Assumes: (a) header space already there, (b) caller holds lock */ +static int brcmf_sdio_txpkt(struct brcmf_sdio *bus, struct sk_buff_head *pktq, + uint chan) +{ + int ret; + struct sk_buff *pkt_next, *tmp; + + brcmf_dbg(TRACE, "Enter\n"); + + ret = brcmf_sdio_txpkt_prep(bus, pktq, chan); + if (ret) + goto done; + + sdio_claim_host(bus->sdiodev->func[1]); + ret = brcmf_sdiod_send_pkt(bus->sdiodev, pktq); + bus->sdcnt.f2txdata++; + + if (ret < 0) + brcmf_sdio_txfail(bus); + + sdio_release_host(bus->sdiodev->func[1]); + +done: + brcmf_sdio_txpkt_postp(bus, pktq); + if (ret == 0) + bus->tx_seq = (bus->tx_seq + pktq->qlen) % SDPCM_SEQ_WRAP; + skb_queue_walk_safe(pktq, pkt_next, tmp) { + __skb_unlink(pkt_next, pktq); + brcmf_txcomplete(bus->sdiodev->dev, pkt_next, ret == 0); + } + return ret; +} + +static uint brcmf_sdio_sendfromq(struct brcmf_sdio *bus, uint maxframes) +{ + struct sk_buff *pkt; + struct sk_buff_head pktq; + u32 intstatus = 0; + int ret = 0, prec_out, i; + uint cnt = 0; + u8 tx_prec_map, pkt_num; + + brcmf_dbg(TRACE, "Enter\n"); + + tx_prec_map = ~bus->flowcontrol; + + /* Send frames until the limit or some other event */ + for (cnt = 0; (cnt < maxframes) && data_ok(bus);) { + pkt_num = 1; + if (down_interruptible(&bus->tx_seq_lock)) + return cnt; + if (bus->txglom) + pkt_num = min_t(u8, bus->tx_max - bus->tx_seq, + bus->sdiodev->txglomsz); + pkt_num = min_t(u32, pkt_num, + brcmu_pktq_mlen(&bus->txq, ~bus->flowcontrol)); + __skb_queue_head_init(&pktq); + spin_lock_bh(&bus->txq_lock); + for (i = 0; i < pkt_num; i++) { + pkt = brcmu_pktq_mdeq(&bus->txq, tx_prec_map, + &prec_out); + if (pkt == NULL) + break; + __skb_queue_tail(&pktq, pkt); + } + spin_unlock_bh(&bus->txq_lock); + if (i == 0) { + up(&bus->tx_seq_lock); + break; + } + + ret = brcmf_sdio_txpkt(bus, &pktq, SDPCM_DATA_CHANNEL); + up(&bus->tx_seq_lock); + + cnt += i; + + /* In poll mode, need to check for other events */ + if (!bus->intr) { + /* Check device status, signal pending interrupt */ + sdio_claim_host(bus->sdiodev->func[1]); + ret = r_sdreg32(bus, &intstatus, + offsetof(struct sdpcmd_regs, + intstatus)); + sdio_release_host(bus->sdiodev->func[1]); + bus->sdcnt.f2txdata++; + if (ret != 0) + break; + if (intstatus & bus->hostintmask) + atomic_set(&bus->ipend, 1); + } + } + + /* Deflow-control stack if needed */ + if ((bus->sdiodev->bus_if->state == BRCMF_BUS_DATA) && + bus->txoff && (pktq_len(&bus->txq) < TXLOW)) { + bus->txoff = false; + brcmf_txflowblock(bus->sdiodev->dev, false); + } + + return cnt; +} + +static int brcmf_sdio_tx_ctrlframe(struct brcmf_sdio *bus, u8 *frame, u16 len) +{ + u8 doff; + u16 pad; + uint retries = 0; + struct brcmf_sdio_hdrinfo hd_info = {0}; + int ret; + + brcmf_dbg(TRACE, "Enter\n"); + + /* Back the pointer to make room for bus header */ + frame -= bus->tx_hdrlen; + len += bus->tx_hdrlen; + + /* Add alignment padding (optional for ctl frames) */ + doff = ((unsigned long)frame % bus->head_align); + if (doff) { + frame -= doff; + len += doff; + memset(frame + bus->tx_hdrlen, 0, doff); + } + + /* Round send length to next SDIO block */ + pad = 0; + if (bus->roundup && bus->blocksize && (len > bus->blocksize)) { + pad = bus->blocksize - (len % bus->blocksize); + if ((pad > bus->roundup) || (pad >= bus->blocksize)) + pad = 0; + } else if (len % bus->head_align) { + pad = bus->head_align - (len % bus->head_align); + } + len += pad; + + hd_info.len = len - pad; + hd_info.channel = SDPCM_CONTROL_CHANNEL; + hd_info.dat_offset = doff + bus->tx_hdrlen; + hd_info.seq_num = bus->tx_seq; + hd_info.lastfrm = true; + hd_info.tail_pad = pad; + brcmf_sdio_hdpack(bus, frame, &hd_info); + + if (bus->txglom) + brcmf_sdio_update_hwhdr(frame, len); + + brcmf_dbg_hex_dump(BRCMF_BYTES_ON() && BRCMF_CTL_ON(), + frame, len, "Tx Frame:\n"); + brcmf_dbg_hex_dump(!(BRCMF_BYTES_ON() && BRCMF_CTL_ON()) && + BRCMF_HDRS_ON(), + frame, min_t(u16, len, 16), "TxHdr:\n"); + + do { + ret = brcmf_sdiod_send_buf(bus->sdiodev, frame, len); + + if (ret < 0) + brcmf_sdio_txfail(bus); + else + bus->tx_seq = (bus->tx_seq + 1) % SDPCM_SEQ_WRAP; + } while (ret < 0 && retries++ < TXRETRIES); + + return ret; +} + +static void brcmf_sdio_bus_stop(struct device *dev) +{ + u32 local_hostintmask; + u8 saveclk; + int err; + 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; + + brcmf_dbg(TRACE, "Enter\n"); + + if (bus->watchdog_tsk) { + send_sig(SIGTERM, bus->watchdog_tsk, 1); + kthread_stop(bus->watchdog_tsk); + bus->watchdog_tsk = NULL; + } + + if (bus_if->state == BRCMF_BUS_DOWN) { + sdio_claim_host(sdiodev->func[1]); + + /* Enable clock for device interrupts */ + brcmf_sdio_bus_sleep(bus, false, false); + + /* Disable and clear interrupts at the chip level also */ + w_sdreg32(bus, 0, offsetof(struct sdpcmd_regs, hostintmask)); + local_hostintmask = bus->hostintmask; + bus->hostintmask = 0; + + /* Force backplane clocks to assure F2 interrupt propagates */ + saveclk = brcmf_sdiod_regrb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, + &err); + if (!err) + brcmf_sdiod_regwb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, + (saveclk | SBSDIO_FORCE_HT), &err); + if (err) + brcmf_err("Failed to force clock for F2: err %d\n", + err); + + /* Turn off the bus (F2), free any pending packets */ + brcmf_dbg(INTR, "disable SDIO interrupts\n"); + sdio_disable_func(sdiodev->func[SDIO_FUNC_2]); + + /* Clear any pending interrupts now that F2 is disabled */ + w_sdreg32(bus, local_hostintmask, + offsetof(struct sdpcmd_regs, intstatus)); + + sdio_release_host(sdiodev->func[1]); + } + /* Clear the data packet queues */ + brcmu_pktq_flush(&bus->txq, true, NULL, NULL); + + /* Clear any held glomming stuff */ + if (bus->glomd) + brcmu_pkt_buf_free_skb(bus->glomd); + brcmf_sdio_free_glom(bus); + + /* Clear rx control and wake any waiters */ + spin_lock_bh(&bus->rxctl_lock); + bus->rxlen = 0; + spin_unlock_bh(&bus->rxctl_lock); + brcmf_sdio_dcmd_resp_wake(bus); + + /* Reset some F2 state stuff */ + bus->rxskip = false; + bus->tx_seq = bus->rx_seq = 0; +} + +static inline void brcmf_sdio_clrintr(struct brcmf_sdio *bus) +{ + unsigned long flags; + + if (bus->sdiodev->oob_irq_requested) { + spin_lock_irqsave(&bus->sdiodev->irq_en_lock, flags); + if (!bus->sdiodev->irq_en && !atomic_read(&bus->ipend)) { + enable_irq(bus->sdiodev->pdata->oob_irq_nr); + bus->sdiodev->irq_en = true; + } + spin_unlock_irqrestore(&bus->sdiodev->irq_en_lock, flags); + } +} + +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; + u32 addr; + unsigned long val; + int ret; + + buscore = brcmf_chip_get_core(bus->ci, BCMA_CORE_SDIO_DEV); + addr = buscore->base + offsetof(struct sdpcmd_regs, intstatus); + + val = brcmf_sdiod_regrl(bus->sdiodev, addr, &ret); + bus->sdcnt.f1regdata++; + if (ret != 0) + return ret; + + val &= bus->hostintmask; + atomic_set(&bus->fcstate, !!(val & I_HMB_FC_STATE)); + + /* Clear interrupts */ + if (val) { + brcmf_sdiod_regwl(bus->sdiodev, addr, val, &ret); + bus->sdcnt.f1regdata++; + atomic_orr(val, &bus->intstatus); + } + + return ret; +} + +static void brcmf_sdio_dpc(struct brcmf_sdio *bus) +{ + u32 newstatus = 0; + unsigned long intstatus; + uint txlimit = bus->txbound; /* Tx frames to send before resched */ + uint framecnt; /* Temporary counter of tx/rx frames */ + int err = 0; + + brcmf_dbg(TRACE, "Enter\n"); + + sdio_claim_host(bus->sdiodev->func[1]); + + /* If waiting for HTAVAIL, check status */ + if (!bus->sr_enabled && bus->clkstate == CLK_PENDING) { + u8 clkctl, devctl = 0; + +#ifdef DEBUG + /* Check for inconsistent device control */ + devctl = brcmf_sdiod_regrb(bus->sdiodev, + SBSDIO_DEVICE_CTL, &err); +#endif /* DEBUG */ + + /* Read CSR, if clock on switch to AVAIL, else ignore */ + clkctl = brcmf_sdiod_regrb(bus->sdiodev, + SBSDIO_FUNC1_CHIPCLKCSR, &err); + + brcmf_dbg(SDIO, "DPC: PENDING, devctl 0x%02x clkctl 0x%02x\n", + devctl, clkctl); + + if (SBSDIO_HTAV(clkctl)) { + devctl = brcmf_sdiod_regrb(bus->sdiodev, + SBSDIO_DEVICE_CTL, &err); + devctl &= ~SBSDIO_DEVCTL_CA_INT_ONLY; + brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_DEVICE_CTL, + devctl, &err); + bus->clkstate = CLK_AVAIL; + } + } + + /* Make sure backplane clock is on */ + brcmf_sdio_bus_sleep(bus, false, true); + + /* Pending interrupt indicates new device status */ + if (atomic_read(&bus->ipend) > 0) { + atomic_set(&bus->ipend, 0); + err = brcmf_sdio_intr_rstatus(bus); + } + + /* Start with leftover status bits */ + intstatus = atomic_xchg(&bus->intstatus, 0); + + /* Handle flow-control change: read new state in case our ack + * crossed another change interrupt. If change still set, assume + * FC ON for safety, let next loop through do the debounce. + */ + if (intstatus & I_HMB_FC_CHANGE) { + intstatus &= ~I_HMB_FC_CHANGE; + err = w_sdreg32(bus, I_HMB_FC_CHANGE, + offsetof(struct sdpcmd_regs, intstatus)); + + err = r_sdreg32(bus, &newstatus, + offsetof(struct sdpcmd_regs, intstatus)); + bus->sdcnt.f1regdata += 2; + atomic_set(&bus->fcstate, + !!(newstatus & (I_HMB_FC_STATE | I_HMB_FC_CHANGE))); + intstatus |= (newstatus & bus->hostintmask); + } + + /* Handle host mailbox indication */ + if (intstatus & I_HMB_HOST_INT) { + intstatus &= ~I_HMB_HOST_INT; + intstatus |= brcmf_sdio_hostmail(bus); + } + + sdio_release_host(bus->sdiodev->func[1]); + + /* Generally don't ask for these, can get CRC errors... */ + if (intstatus & I_WR_OOSYNC) { + brcmf_err("Dongle reports WR_OOSYNC\n"); + intstatus &= ~I_WR_OOSYNC; + } + + if (intstatus & I_RD_OOSYNC) { + brcmf_err("Dongle reports RD_OOSYNC\n"); + intstatus &= ~I_RD_OOSYNC; + } + + if (intstatus & I_SBINT) { + brcmf_err("Dongle reports SBINT\n"); + intstatus &= ~I_SBINT; + } + + /* Would be active due to wake-wlan in gSPI */ + if (intstatus & I_CHIPACTIVE) { + brcmf_dbg(INFO, "Dongle reports CHIPACTIVE\n"); + intstatus &= ~I_CHIPACTIVE; + } + + /* Ignore frame indications if rxskip is set */ + if (bus->rxskip) + intstatus &= ~I_HMB_FRAME_IND; + + /* On frame indication, read available frames */ + if ((intstatus & I_HMB_FRAME_IND) && (bus->clkstate == CLK_AVAIL)) { + brcmf_sdio_readframes(bus, bus->rxbound); + if (!bus->rxpending) + intstatus &= ~I_HMB_FRAME_IND; + } + + /* Keep still-pending events for next scheduling */ + if (intstatus) + atomic_orr(intstatus, &bus->intstatus); + + brcmf_sdio_clrintr(bus); + + if (bus->ctrl_frame_stat && (bus->clkstate == CLK_AVAIL) && + (down_interruptible(&bus->tx_seq_lock) == 0)) { + if (data_ok(bus)) { + sdio_claim_host(bus->sdiodev->func[1]); + err = brcmf_sdio_tx_ctrlframe(bus, bus->ctrl_frame_buf, + bus->ctrl_frame_len); + sdio_release_host(bus->sdiodev->func[1]); + + bus->ctrl_frame_stat = false; + brcmf_sdio_wait_event_wakeup(bus); + } + up(&bus->tx_seq_lock); + } + /* Send queued frames (limit 1 if rx may still be pending) */ + if ((bus->clkstate == CLK_AVAIL) && !atomic_read(&bus->fcstate) && + brcmu_pktq_mlen(&bus->txq, ~bus->flowcontrol) && txlimit && + data_ok(bus)) { + framecnt = bus->rxpending ? min(txlimit, bus->txminmax) : + txlimit; + brcmf_sdio_sendfromq(bus, framecnt); + } + + if (!brcmf_bus_ready(bus->sdiodev->bus_if) || (err != 0)) { + brcmf_err("failed backplane access over SDIO, halting operation\n"); + atomic_set(&bus->intstatus, 0); + } else if (atomic_read(&bus->intstatus) || + atomic_read(&bus->ipend) > 0 || + (!atomic_read(&bus->fcstate) && + brcmu_pktq_mlen(&bus->txq, ~bus->flowcontrol) && + data_ok(bus))) { + atomic_inc(&bus->dpc_tskcnt); + } +} + +static struct pktq *brcmf_sdio_bus_gettxq(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->txq; +} + +static int brcmf_sdio_bus_txdata(struct device *dev, struct sk_buff *pkt) +{ + int ret = -EBADE; + uint prec; + 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; + + brcmf_dbg(TRACE, "Enter: pkt: data %p len %d\n", pkt->data, pkt->len); + + /* Add space for the header */ + skb_push(pkt, bus->tx_hdrlen); + /* precondition: IS_ALIGNED((unsigned long)(pkt->data), 2) */ + + prec = prio2prec((pkt->priority & PRIOMASK)); + + /* Check for existing queue, current flow-control, + pending event, or pending clock */ + brcmf_dbg(TRACE, "deferring pktq len %d\n", pktq_len(&bus->txq)); + bus->sdcnt.fcqueued++; + + /* Priority based enq */ + spin_lock_bh(&bus->txq_lock); + /* reset bus_flags in packet cb */ + *(u16 *)(pkt->cb) = 0; + if (!brcmf_c_prec_enq(bus->sdiodev->dev, &bus->txq, pkt, prec)) { + skb_pull(pkt, bus->tx_hdrlen); + brcmf_err("out of bus->txq !!!\n"); + ret = -ENOSR; + } else { + ret = 0; + } + + if (pktq_len(&bus->txq) >= TXHI) { + bus->txoff = true; + brcmf_txflowblock(bus->sdiodev->dev, true); + } + spin_unlock_bh(&bus->txq_lock); + +#ifdef DEBUG + if (pktq_plen(&bus->txq, prec) > qcount[prec]) + qcount[prec] = pktq_plen(&bus->txq, prec); +#endif + + if (atomic_read(&bus->dpc_tskcnt) == 0) { + atomic_inc(&bus->dpc_tskcnt); + queue_work(bus->brcmf_wq, &bus->datawork); + } + + return ret; +} + +#ifdef DEBUG +#define CONSOLE_LINE_MAX 192 + +static int brcmf_sdio_readconsole(struct brcmf_sdio *bus) +{ + struct brcmf_console *c = &bus->console; + u8 line[CONSOLE_LINE_MAX], ch; + u32 n, idx, addr; + int rv; + + /* Don't do anything until FWREADY updates console address */ + if (bus->console_addr == 0) + return 0; + + /* Read console log struct */ + addr = bus->console_addr + offsetof(struct rte_console, log_le); + rv = brcmf_sdiod_ramrw(bus->sdiodev, false, addr, (u8 *)&c->log_le, + sizeof(c->log_le)); + if (rv < 0) + return rv; + + /* Allocate console buffer (one time only) */ + if (c->buf == NULL) { + c->bufsize = le32_to_cpu(c->log_le.buf_size); + c->buf = kmalloc(c->bufsize, GFP_ATOMIC); + if (c->buf == NULL) + return -ENOMEM; + } + + idx = le32_to_cpu(c->log_le.idx); + + /* Protect against corrupt value */ + if (idx > c->bufsize) + return -EBADE; + + /* Skip reading the console buffer if the index pointer + has not moved */ + if (idx == c->last) + return 0; + + /* Read the console buffer */ + addr = le32_to_cpu(c->log_le.buf); + rv = brcmf_sdiod_ramrw(bus->sdiodev, false, addr, c->buf, c->bufsize); + if (rv < 0) + return rv; + + while (c->last != idx) { + for (n = 0; n < CONSOLE_LINE_MAX - 2; n++) { + if (c->last == idx) { + /* This would output a partial line. + * Instead, back up + * the buffer pointer and output this + * line next time around. + */ + if (c->last >= n) + c->last -= n; + else + c->last = c->bufsize - n; + goto break2; + } + ch = c->buf[c->last]; + c->last = (c->last + 1) % c->bufsize; + if (ch == '\n') + break; + line[n] = ch; + } + + if (n > 0) { + if (line[n - 1] == '\r') + n--; + line[n] = 0; + pr_debug("CONSOLE: %s\n", line); + } + } +break2: + + return 0; +} +#endif /* DEBUG */ + +static int +brcmf_sdio_bus_txctl(struct device *dev, unsigned char *msg, uint msglen) +{ + 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 ret = -1; + + brcmf_dbg(TRACE, "Enter\n"); + + if (down_interruptible(&bus->tx_seq_lock)) + return -EINTR; + + if (!data_ok(bus)) { + brcmf_dbg(INFO, "No bus credit bus->tx_max %d, bus->tx_seq %d\n", + bus->tx_max, bus->tx_seq); + up(&bus->tx_seq_lock); + /* Send from dpc */ + bus->ctrl_frame_buf = msg; + bus->ctrl_frame_len = msglen; + bus->ctrl_frame_stat = true; + + wait_event_interruptible_timeout(bus->ctrl_wait, + !bus->ctrl_frame_stat, + msecs_to_jiffies(2000)); + + if (!bus->ctrl_frame_stat) { + brcmf_dbg(SDIO, "ctrl_frame_stat == false\n"); + ret = 0; + } else { + brcmf_dbg(SDIO, "ctrl_frame_stat == true\n"); + bus->ctrl_frame_stat = false; + if (down_interruptible(&bus->tx_seq_lock)) + return -EINTR; + ret = -1; + } + } + if (ret == -1) { + sdio_claim_host(bus->sdiodev->func[1]); + brcmf_sdio_bus_sleep(bus, false, false); + ret = brcmf_sdio_tx_ctrlframe(bus, msg, msglen); + sdio_release_host(bus->sdiodev->func[1]); + up(&bus->tx_seq_lock); + } + + if (ret) + bus->sdcnt.tx_ctlerrs++; + else + bus->sdcnt.tx_ctlpkts++; + + return ret ? -EIO : 0; +} + +#ifdef DEBUG +static int brcmf_sdio_dump_console(struct seq_file *seq, struct brcmf_sdio *bus, + struct sdpcm_shared *sh) +{ + u32 addr, console_ptr, console_size, console_index; + char *conbuf = NULL; + __le32 sh_val; + int rv; + + /* obtain console information from device memory */ + addr = sh->console_addr + offsetof(struct rte_console, log_le); + rv = brcmf_sdiod_ramrw(bus->sdiodev, false, addr, + (u8 *)&sh_val, sizeof(u32)); + if (rv < 0) + return rv; + console_ptr = le32_to_cpu(sh_val); + + addr = sh->console_addr + offsetof(struct rte_console, log_le.buf_size); + rv = brcmf_sdiod_ramrw(bus->sdiodev, false, addr, + (u8 *)&sh_val, sizeof(u32)); + if (rv < 0) + return rv; + console_size = le32_to_cpu(sh_val); + + addr = sh->console_addr + offsetof(struct rte_console, log_le.idx); + rv = brcmf_sdiod_ramrw(bus->sdiodev, false, addr, + (u8 *)&sh_val, sizeof(u32)); + if (rv < 0) + return rv; + console_index = le32_to_cpu(sh_val); + + /* allocate buffer for console data */ + if (console_size <= CONSOLE_BUFFER_MAX) + conbuf = vzalloc(console_size+1); + + if (!conbuf) + return -ENOMEM; + + /* obtain the console data from device */ + conbuf[console_size] = '\0'; + rv = brcmf_sdiod_ramrw(bus->sdiodev, false, console_ptr, (u8 *)conbuf, + console_size); + if (rv < 0) + goto done; + + rv = seq_write(seq, conbuf + console_index, + console_size - console_index); + if (rv < 0) + goto done; + + if (console_index > 0) + rv = seq_write(seq, conbuf, console_index - 1); + +done: + vfree(conbuf); + return rv; +} + +static int brcmf_sdio_trap_info(struct seq_file *seq, struct brcmf_sdio *bus, + struct sdpcm_shared *sh) +{ + int error; + struct brcmf_trap_info tr; + + if ((sh->flags & SDPCM_SHARED_TRAP) == 0) { + brcmf_dbg(INFO, "no trap in firmware\n"); + return 0; + } + + error = brcmf_sdiod_ramrw(bus->sdiodev, false, sh->trap_addr, (u8 *)&tr, + sizeof(struct brcmf_trap_info)); + if (error < 0) + return error; + + seq_printf(seq, + "dongle trap info: type 0x%x @ epc 0x%08x\n" + " cpsr 0x%08x spsr 0x%08x sp 0x%08x\n" + " lr 0x%08x pc 0x%08x offset 0x%x\n" + " r0 0x%08x r1 0x%08x r2 0x%08x r3 0x%08x\n" + " r4 0x%08x r5 0x%08x r6 0x%08x r7 0x%08x\n", + le32_to_cpu(tr.type), le32_to_cpu(tr.epc), + le32_to_cpu(tr.cpsr), le32_to_cpu(tr.spsr), + le32_to_cpu(tr.r13), le32_to_cpu(tr.r14), + le32_to_cpu(tr.pc), sh->trap_addr, + le32_to_cpu(tr.r0), le32_to_cpu(tr.r1), + le32_to_cpu(tr.r2), le32_to_cpu(tr.r3), + le32_to_cpu(tr.r4), le32_to_cpu(tr.r5), + le32_to_cpu(tr.r6), le32_to_cpu(tr.r7)); + + return 0; +} + +static int brcmf_sdio_assert_info(struct seq_file *seq, struct brcmf_sdio *bus, + struct sdpcm_shared *sh) +{ + int error = 0; + char file[80] = "?"; + char expr[80] = ""; + + if ((sh->flags & SDPCM_SHARED_ASSERT_BUILT) == 0) { + brcmf_dbg(INFO, "firmware not built with -assert\n"); + return 0; + } else if ((sh->flags & SDPCM_SHARED_ASSERT) == 0) { + brcmf_dbg(INFO, "no assert in dongle\n"); + return 0; + } + + sdio_claim_host(bus->sdiodev->func[1]); + if (sh->assert_file_addr != 0) { + error = brcmf_sdiod_ramrw(bus->sdiodev, false, + sh->assert_file_addr, (u8 *)file, 80); + if (error < 0) + return error; + } + if (sh->assert_exp_addr != 0) { + error = brcmf_sdiod_ramrw(bus->sdiodev, false, + sh->assert_exp_addr, (u8 *)expr, 80); + if (error < 0) + return error; + } + sdio_release_host(bus->sdiodev->func[1]); + + seq_printf(seq, "dongle assert: %s:%d: assert(%s)\n", + file, sh->assert_line, expr); + return 0; +} + +static int brcmf_sdio_checkdied(struct brcmf_sdio *bus) +{ + int error; + struct sdpcm_shared sh; + + error = brcmf_sdio_readshared(bus, &sh); + + if (error < 0) + return error; + + if ((sh.flags & SDPCM_SHARED_ASSERT_BUILT) == 0) + brcmf_dbg(INFO, "firmware not built with -assert\n"); + else if (sh.flags & SDPCM_SHARED_ASSERT) + brcmf_err("assertion in dongle\n"); + + if (sh.flags & SDPCM_SHARED_TRAP) + brcmf_err("firmware trap in dongle\n"); + + return 0; +} + +static int brcmf_sdio_died_dump(struct seq_file *seq, struct brcmf_sdio *bus) +{ + int error = 0; + struct sdpcm_shared sh; + + error = brcmf_sdio_readshared(bus, &sh); + if (error < 0) + goto done; + + error = brcmf_sdio_assert_info(seq, bus, &sh); + if (error < 0) + goto done; + + error = brcmf_sdio_trap_info(seq, bus, &sh); + if (error < 0) + goto done; + + error = brcmf_sdio_dump_console(seq, bus, &sh); + +done: + return error; +} + +static int brcmf_sdio_forensic_read(struct seq_file *seq, void *data) +{ + struct brcmf_bus *bus_if = dev_get_drvdata(seq->private); + struct brcmf_sdio *bus = bus_if->bus_priv.sdio->bus; + + return brcmf_sdio_died_dump(seq, bus); +} + +static int brcmf_debugfs_sdio_count_read(struct seq_file *seq, void *data) +{ + struct brcmf_bus *bus_if = dev_get_drvdata(seq->private); + struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio; + struct brcmf_sdio_count *sdcnt = &sdiodev->bus->sdcnt; + + seq_printf(seq, + "intrcount: %u\nlastintrs: %u\n" + "pollcnt: %u\nregfails: %u\n" + "tx_sderrs: %u\nfcqueued: %u\n" + "rxrtx: %u\nrx_toolong: %u\n" + "rxc_errors: %u\nrx_hdrfail: %u\n" + "rx_badhdr: %u\nrx_badseq: %u\n" + "fc_rcvd: %u\nfc_xoff: %u\n" + "fc_xon: %u\nrxglomfail: %u\n" + "rxglomframes: %u\nrxglompkts: %u\n" + "f2rxhdrs: %u\nf2rxdata: %u\n" + "f2txdata: %u\nf1regdata: %u\n" + "tickcnt: %u\ntx_ctlerrs: %lu\n" + "tx_ctlpkts: %lu\nrx_ctlerrs: %lu\n" + "rx_ctlpkts: %lu\nrx_readahead: %lu\n", + sdcnt->intrcount, sdcnt->lastintrs, + sdcnt->pollcnt, sdcnt->regfails, + sdcnt->tx_sderrs, sdcnt->fcqueued, + sdcnt->rxrtx, sdcnt->rx_toolong, + sdcnt->rxc_errors, sdcnt->rx_hdrfail, + sdcnt->rx_badhdr, sdcnt->rx_badseq, + sdcnt->fc_rcvd, sdcnt->fc_xoff, + sdcnt->fc_xon, sdcnt->rxglomfail, + sdcnt->rxglomframes, sdcnt->rxglompkts, + sdcnt->f2rxhdrs, sdcnt->f2rxdata, + sdcnt->f2txdata, sdcnt->f1regdata, + sdcnt->tickcnt, sdcnt->tx_ctlerrs, + sdcnt->tx_ctlpkts, sdcnt->rx_ctlerrs, + sdcnt->rx_ctlpkts, sdcnt->rx_readahead_cnt); + + return 0; +} + +static void brcmf_sdio_debugfs_create(struct brcmf_sdio *bus) +{ + struct brcmf_pub *drvr = bus->sdiodev->bus_if->drvr; + struct dentry *dentry = brcmf_debugfs_get_devdir(drvr); + + if (IS_ERR_OR_NULL(dentry)) + return; + + brcmf_debugfs_add_entry(drvr, "forensics", brcmf_sdio_forensic_read); + brcmf_debugfs_add_entry(drvr, "counters", + brcmf_debugfs_sdio_count_read); + debugfs_create_u32("console_interval", 0644, dentry, + &bus->console_interval); +} +#else +static int brcmf_sdio_checkdied(struct brcmf_sdio *bus) +{ + return 0; +} + +static void brcmf_sdio_debugfs_create(struct brcmf_sdio *bus) +{ +} +#endif /* DEBUG */ + +static int +brcmf_sdio_bus_rxctl(struct device *dev, unsigned char *msg, uint msglen) +{ + int timeleft; + uint rxlen = 0; + bool pending; + u8 *buf; + 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; + + brcmf_dbg(TRACE, "Enter\n"); + + /* Wait until control frame is available */ + timeleft = brcmf_sdio_dcmd_resp_wait(bus, &bus->rxlen, &pending); + + spin_lock_bh(&bus->rxctl_lock); + rxlen = bus->rxlen; + memcpy(msg, bus->rxctl, min(msglen, rxlen)); + bus->rxctl = NULL; + buf = bus->rxctl_orig; + bus->rxctl_orig = NULL; + bus->rxlen = 0; + spin_unlock_bh(&bus->rxctl_lock); + vfree(buf); + + if (rxlen) { + brcmf_dbg(CTL, "resumed on rxctl frame, got %d expected %d\n", + rxlen, msglen); + } else if (timeleft == 0) { + brcmf_err("resumed on timeout\n"); + brcmf_sdio_checkdied(bus); + } else if (pending) { + brcmf_dbg(CTL, "cancelled\n"); + return -ERESTARTSYS; + } else { + brcmf_dbg(CTL, "resumed for unknown reason?\n"); + brcmf_sdio_checkdied(bus); + } + + if (rxlen) + bus->sdcnt.rx_ctlpkts++; + else + bus->sdcnt.rx_ctlerrs++; + + return rxlen ? (int)rxlen : -ETIMEDOUT; +} + +#ifdef DEBUG +static bool +brcmf_sdio_verifymemory(struct brcmf_sdio_dev *sdiodev, u32 ram_addr, + u8 *ram_data, uint ram_sz) +{ + char *ram_cmp; + int err; + bool ret = true; + int address; + int offset; + int len; + + /* read back and verify */ + brcmf_dbg(INFO, "Compare RAM dl & ul at 0x%08x; size=%d\n", ram_addr, + ram_sz); + ram_cmp = kmalloc(MEMBLOCK, GFP_KERNEL); + /* do not proceed while no memory but */ + if (!ram_cmp) + return true; + + address = ram_addr; + offset = 0; + while (offset < ram_sz) { + len = ((offset + MEMBLOCK) < ram_sz) ? MEMBLOCK : + ram_sz - offset; + err = brcmf_sdiod_ramrw(sdiodev, false, address, ram_cmp, len); + if (err) { + brcmf_err("error %d on reading %d membytes at 0x%08x\n", + err, len, address); + ret = false; + break; + } else if (memcmp(ram_cmp, &ram_data[offset], len)) { + brcmf_err("Downloaded RAM image is corrupted, block offset is %d, len is %d\n", + offset, len); + ret = false; + break; + } + offset += len; + address += len; + } + + kfree(ram_cmp); + + return ret; +} +#else /* DEBUG */ +static bool +brcmf_sdio_verifymemory(struct brcmf_sdio_dev *sdiodev, u32 ram_addr, + u8 *ram_data, uint ram_sz) +{ + return true; +} +#endif /* DEBUG */ + +static int brcmf_sdio_download_code_file(struct brcmf_sdio *bus, + const struct firmware *fw) +{ + int err; + + brcmf_dbg(TRACE, "Enter\n"); + + err = brcmf_sdiod_ramrw(bus->sdiodev, true, bus->ci->rambase, + (u8 *)fw->data, fw->size); + if (err) + brcmf_err("error %d on writing %d membytes at 0x%08x\n", + err, (int)fw->size, bus->ci->rambase); + else if (!brcmf_sdio_verifymemory(bus->sdiodev, bus->ci->rambase, + (u8 *)fw->data, fw->size)) + err = -EIO; + + return err; +} + +static int brcmf_sdio_download_nvram(struct brcmf_sdio *bus, + void *vars, u32 varsz) +{ + int address; + int err; + + brcmf_dbg(TRACE, "Enter\n"); + + address = bus->ci->ramsize - varsz + bus->ci->rambase; + err = brcmf_sdiod_ramrw(bus->sdiodev, true, address, vars, varsz); + if (err) + brcmf_err("error %d on writing %d nvram bytes at 0x%08x\n", + err, varsz, address); + else if (!brcmf_sdio_verifymemory(bus->sdiodev, address, vars, varsz)) + err = -EIO; + + return err; +} + +static int brcmf_sdio_download_firmware(struct brcmf_sdio *bus, + const struct firmware *fw, + void *nvram, u32 nvlen) +{ + int bcmerror = -EFAULT; + u32 rstvec; + + sdio_claim_host(bus->sdiodev->func[1]); + brcmf_sdio_clkctl(bus, CLK_AVAIL, false); + + /* Keep arm in reset */ + brcmf_chip_enter_download(bus->ci); + + rstvec = get_unaligned_le32(fw->data); + brcmf_dbg(SDIO, "firmware rstvec: %x\n", rstvec); + + bcmerror = brcmf_sdio_download_code_file(bus, fw); + release_firmware(fw); + if (bcmerror) { + brcmf_err("dongle image file download failed\n"); + brcmf_fw_nvram_free(nvram); + goto err; + } + + bcmerror = brcmf_sdio_download_nvram(bus, nvram, nvlen); + brcmf_fw_nvram_free(nvram); + if (bcmerror) { + brcmf_err("dongle nvram file download failed\n"); + goto err; + } + + /* Take arm out of reset */ + if (!brcmf_chip_exit_download(bus->ci, rstvec)) { + brcmf_err("error getting out of ARM core reset\n"); + goto err; + } + + /* Allow HT Clock now that the ARM is running. */ + brcmf_bus_change_state(bus->sdiodev->bus_if, BRCMF_BUS_LOAD); + bcmerror = 0; + +err: + brcmf_sdio_clkctl(bus, CLK_SDONLY, false); + sdio_release_host(bus->sdiodev->func[1]); + return bcmerror; +} + +static void brcmf_sdio_sr_init(struct brcmf_sdio *bus) +{ + int err = 0; + u8 val; + + brcmf_dbg(TRACE, "Enter\n"); + + val = brcmf_sdiod_regrb(bus->sdiodev, SBSDIO_FUNC1_WAKEUPCTRL, &err); + if (err) { + brcmf_err("error reading SBSDIO_FUNC1_WAKEUPCTRL\n"); + return; + } + + val |= 1 << SBSDIO_FUNC1_WCTRL_HTWAIT_SHIFT; + brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_WAKEUPCTRL, val, &err); + if (err) { + brcmf_err("error writing SBSDIO_FUNC1_WAKEUPCTRL\n"); + return; + } + + /* Add CMD14 Support */ + brcmf_sdiod_regwb(bus->sdiodev, SDIO_CCCR_BRCM_CARDCAP, + (SDIO_CCCR_BRCM_CARDCAP_CMD14_SUPPORT | + SDIO_CCCR_BRCM_CARDCAP_CMD14_EXT), + &err); + if (err) { + brcmf_err("error writing SDIO_CCCR_BRCM_CARDCAP\n"); + return; + } + + brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, + SBSDIO_FORCE_HT, &err); + if (err) { + brcmf_err("error writing SBSDIO_FUNC1_CHIPCLKCSR\n"); + return; + } + + /* set flag */ + bus->sr_enabled = true; + brcmf_dbg(INFO, "SR enabled\n"); +} + +/* enable KSO bit */ +static int brcmf_sdio_kso_init(struct brcmf_sdio *bus) +{ + u8 val; + int err = 0; + + brcmf_dbg(TRACE, "Enter\n"); + + /* KSO bit added in SDIO core rev 12 */ + if (brcmf_chip_get_core(bus->ci, BCMA_CORE_SDIO_DEV)->rev < 12) + return 0; + + val = brcmf_sdiod_regrb(bus->sdiodev, SBSDIO_FUNC1_SLEEPCSR, &err); + if (err) { + brcmf_err("error reading SBSDIO_FUNC1_SLEEPCSR\n"); + return err; + } + + if (!(val & SBSDIO_FUNC1_SLEEPCSR_KSO_MASK)) { + val |= (SBSDIO_FUNC1_SLEEPCSR_KSO_EN << + SBSDIO_FUNC1_SLEEPCSR_KSO_SHIFT); + brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_SLEEPCSR, + val, &err); + if (err) { + brcmf_err("error writing SBSDIO_FUNC1_SLEEPCSR\n"); + return err; + } + } + + return 0; +} + + +static int brcmf_sdio_bus_preinit(struct device *dev) +{ + struct brcmf_bus *bus_if = dev_get_drvdata(dev); + struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio; + struct brcmf_sdio *bus = sdiodev->bus; + uint pad_size; + u32 value; + int err; + + /* the commands below use the terms tx and rx from + * a device perspective, ie. bus:txglom affects the + * bus transfers from device to host. + */ + if (brcmf_chip_get_core(bus->ci, BCMA_CORE_SDIO_DEV)->rev < 12) { + /* for sdio core rev < 12, disable txgloming */ + value = 0; + err = brcmf_iovar_data_set(dev, "bus:txglom", &value, + sizeof(u32)); + } else { + /* otherwise, set txglomalign */ + value = 4; + if (sdiodev->pdata) + value = sdiodev->pdata->sd_sgentry_align; + /* SDIO ADMA requires at least 32 bit alignment */ + value = max_t(u32, value, 4); + err = brcmf_iovar_data_set(dev, "bus:txglomalign", &value, + sizeof(u32)); + } + + if (err < 0) + goto done; + + bus->tx_hdrlen = SDPCM_HWHDR_LEN + SDPCM_SWHDR_LEN; + if (sdiodev->sg_support) { + bus->txglom = false; + value = 1; + pad_size = bus->sdiodev->func[2]->cur_blksize << 1; + err = brcmf_iovar_data_set(bus->sdiodev->dev, "bus:rxglom", + &value, sizeof(u32)); + if (err < 0) { + /* bus:rxglom is allowed to fail */ + err = 0; + } else { + bus->txglom = true; + bus->tx_hdrlen += SDPCM_HWEXT_LEN; + } + } + brcmf_bus_add_txhdrlen(bus->sdiodev->dev, bus->tx_hdrlen); + +done: + return err; +} + +void brcmf_sdio_isr(struct brcmf_sdio *bus) +{ + brcmf_dbg(TRACE, "Enter\n"); + + if (!bus) { + brcmf_err("bus is null pointer, exiting\n"); + return; + } + + if (!brcmf_bus_ready(bus->sdiodev->bus_if)) { + brcmf_err("bus is down. we have nothing to do\n"); + return; + } + /* Count the interrupt call */ + bus->sdcnt.intrcount++; + if (in_interrupt()) + atomic_set(&bus->ipend, 1); + else + if (brcmf_sdio_intr_rstatus(bus)) { + brcmf_err("failed backplane access\n"); + } + + /* Disable additional interrupts (is this needed now)? */ + if (!bus->intr) + brcmf_err("isr w/o interrupt configured!\n"); + + atomic_inc(&bus->dpc_tskcnt); + queue_work(bus->brcmf_wq, &bus->datawork); +} + +static bool brcmf_sdio_bus_watchdog(struct brcmf_sdio *bus) +{ +#ifdef DEBUG + struct brcmf_bus *bus_if = dev_get_drvdata(bus->sdiodev->dev); +#endif /* DEBUG */ + + brcmf_dbg(TIMER, "Enter\n"); + + /* Poll period: check device if appropriate. */ + if (!bus->sr_enabled && + bus->poll && (++bus->polltick >= bus->pollrate)) { + u32 intstatus = 0; + + /* Reset poll tick */ + bus->polltick = 0; + + /* Check device if no interrupts */ + if (!bus->intr || + (bus->sdcnt.intrcount == bus->sdcnt.lastintrs)) { + + if (atomic_read(&bus->dpc_tskcnt) == 0) { + u8 devpend; + + sdio_claim_host(bus->sdiodev->func[1]); + devpend = brcmf_sdiod_regrb(bus->sdiodev, + SDIO_CCCR_INTx, + NULL); + sdio_release_host(bus->sdiodev->func[1]); + intstatus = + devpend & (INTR_STATUS_FUNC1 | + INTR_STATUS_FUNC2); + } + + /* If there is something, make like the ISR and + schedule the DPC */ + if (intstatus) { + bus->sdcnt.pollcnt++; + atomic_set(&bus->ipend, 1); + + atomic_inc(&bus->dpc_tskcnt); + queue_work(bus->brcmf_wq, &bus->datawork); + } + } + + /* Update interrupt tracking */ + bus->sdcnt.lastintrs = bus->sdcnt.intrcount; + } +#ifdef DEBUG + /* Poll for console output periodically */ + if (bus_if && bus_if->state == BRCMF_BUS_DATA && + bus->console_interval != 0) { + bus->console.count += BRCMF_WD_POLL_MS; + if (bus->console.count >= bus->console_interval) { + bus->console.count -= bus->console_interval; + sdio_claim_host(bus->sdiodev->func[1]); + /* Make sure backplane clock is on */ + brcmf_sdio_bus_sleep(bus, false, false); + if (brcmf_sdio_readconsole(bus) < 0) + /* stop on error */ + bus->console_interval = 0; + sdio_release_host(bus->sdiodev->func[1]); + } + } +#endif /* DEBUG */ + + /* On idle timeout clear activity flag and/or turn off clock */ + if ((bus->idletime > 0) && (bus->clkstate == CLK_AVAIL)) { + if (++bus->idlecount >= bus->idletime) { + bus->idlecount = 0; + if (bus->activity) { + bus->activity = false; + brcmf_sdio_wd_timer(bus, BRCMF_WD_POLL_MS); + } else { + brcmf_dbg(SDIO, "idle\n"); + sdio_claim_host(bus->sdiodev->func[1]); + brcmf_sdio_bus_sleep(bus, true, false); + sdio_release_host(bus->sdiodev->func[1]); + } + } + } + + return (atomic_read(&bus->ipend) > 0); +} + +static void brcmf_sdio_dataworker(struct work_struct *work) +{ + struct brcmf_sdio *bus = container_of(work, struct brcmf_sdio, + datawork); + + while (atomic_read(&bus->dpc_tskcnt)) { + atomic_set(&bus->dpc_tskcnt, 0); + brcmf_sdio_dpc(bus); + } +} + +static void +brcmf_sdio_drivestrengthinit(struct brcmf_sdio_dev *sdiodev, + struct brcmf_chip *ci, u32 drivestrength) +{ + const struct sdiod_drive_str *str_tab = NULL; + u32 str_mask; + u32 str_shift; + u32 base; + u32 i; + u32 drivestrength_sel = 0; + u32 cc_data_temp; + u32 addr; + + if (!(ci->cc_caps & CC_CAP_PMU)) + return; + + switch (SDIOD_DRVSTR_KEY(ci->chip, ci->pmurev)) { + case SDIOD_DRVSTR_KEY(BRCM_CC_4330_CHIP_ID, 12): + str_tab = sdiod_drvstr_tab1_1v8; + str_mask = 0x00003800; + str_shift = 11; + break; + case SDIOD_DRVSTR_KEY(BRCM_CC_4334_CHIP_ID, 17): + str_tab = sdiod_drvstr_tab6_1v8; + str_mask = 0x00001800; + str_shift = 11; + break; + case SDIOD_DRVSTR_KEY(BRCM_CC_43143_CHIP_ID, 17): + /* note: 43143 does not support tristate */ + i = ARRAY_SIZE(sdiod_drvstr_tab2_3v3) - 1; + if (drivestrength >= sdiod_drvstr_tab2_3v3[i].strength) { + str_tab = sdiod_drvstr_tab2_3v3; + str_mask = 0x00000007; + str_shift = 0; + } else + brcmf_err("Invalid SDIO Drive strength for chip %s, strength=%d\n", + ci->name, drivestrength); + break; + case SDIOD_DRVSTR_KEY(BRCM_CC_43362_CHIP_ID, 13): + str_tab = sdiod_drive_strength_tab5_1v8; + str_mask = 0x00003800; + str_shift = 11; + break; + default: + brcmf_err("No SDIO Drive strength init done for chip %s rev %d pmurev %d\n", + ci->name, ci->chiprev, ci->pmurev); + break; + } + + if (str_tab != NULL) { + for (i = 0; str_tab[i].strength != 0; i++) { + if (drivestrength >= str_tab[i].strength) { + drivestrength_sel = str_tab[i].sel; + break; + } + } + base = brcmf_chip_get_chipcommon(ci)->base; + addr = CORE_CC_REG(base, chipcontrol_addr); + brcmf_sdiod_regwl(sdiodev, addr, 1, NULL); + cc_data_temp = brcmf_sdiod_regrl(sdiodev, addr, NULL); + cc_data_temp &= ~str_mask; + drivestrength_sel <<= str_shift; + cc_data_temp |= drivestrength_sel; + brcmf_sdiod_regwl(sdiodev, addr, cc_data_temp, NULL); + + brcmf_dbg(INFO, "SDIO: %d mA (req=%d mA) drive strength selected, set to 0x%08x\n", + str_tab[i].strength, drivestrength, cc_data_temp); + } +} + +static int brcmf_sdio_buscoreprep(void *ctx) +{ + struct brcmf_sdio_dev *sdiodev = ctx; + int err = 0; + u8 clkval, clkset; + + /* Try forcing SDIO core to do ALPAvail request only */ + clkset = SBSDIO_FORCE_HW_CLKREQ_OFF | SBSDIO_ALP_AVAIL_REQ; + brcmf_sdiod_regwb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, clkset, &err); + if (err) { + brcmf_err("error writing for HT off\n"); + return err; + } + + /* If register supported, wait for ALPAvail and then force ALP */ + /* This may take up to 15 milliseconds */ + clkval = brcmf_sdiod_regrb(sdiodev, + SBSDIO_FUNC1_CHIPCLKCSR, NULL); + + if ((clkval & ~SBSDIO_AVBITS) != clkset) { + brcmf_err("ChipClkCSR access: wrote 0x%02x read 0x%02x\n", + clkset, clkval); + return -EACCES; + } + + SPINWAIT(((clkval = brcmf_sdiod_regrb(sdiodev, + SBSDIO_FUNC1_CHIPCLKCSR, NULL)), + !SBSDIO_ALPAV(clkval)), + PMU_MAX_TRANSITION_DLY); + if (!SBSDIO_ALPAV(clkval)) { + brcmf_err("timeout on ALPAV wait, clkval 0x%02x\n", + clkval); + return -EBUSY; + } + + clkset = SBSDIO_FORCE_HW_CLKREQ_OFF | SBSDIO_FORCE_ALP; + brcmf_sdiod_regwb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, clkset, &err); + udelay(65); + + /* Also, disable the extra SDIO pull-ups */ + brcmf_sdiod_regwb(sdiodev, SBSDIO_FUNC1_SDIOPULLUP, 0, NULL); + + return 0; +} + +static void brcmf_sdio_buscore_exitdl(void *ctx, struct brcmf_chip *chip, + u32 rstvec) +{ + struct brcmf_sdio_dev *sdiodev = ctx; + struct brcmf_core *core; + u32 reg_addr; + + /* clear all interrupts */ + core = brcmf_chip_get_core(chip, BCMA_CORE_SDIO_DEV); + reg_addr = core->base + offsetof(struct sdpcmd_regs, intstatus); + brcmf_sdiod_regwl(sdiodev, reg_addr, 0xFFFFFFFF, NULL); + + if (rstvec) + /* Write reset vector to address 0 */ + brcmf_sdiod_ramrw(sdiodev, true, 0, (void *)&rstvec, + sizeof(rstvec)); +} + +static u32 brcmf_sdio_buscore_read32(void *ctx, u32 addr) +{ + struct brcmf_sdio_dev *sdiodev = ctx; + u32 val, rev; + + val = brcmf_sdiod_regrl(sdiodev, addr, NULL); + if (sdiodev->func[0]->device == BRCM_SDIO_4335_4339_DEVICE_ID && + addr == CORE_CC_REG(SI_ENUM_BASE, chipid)) { + rev = (val & CID_REV_MASK) >> CID_REV_SHIFT; + if (rev >= 2) { + val &= ~CID_ID_MASK; + val |= BRCM_CC_4339_CHIP_ID; + } + } + return val; +} + +static void brcmf_sdio_buscore_write32(void *ctx, u32 addr, u32 val) +{ + struct brcmf_sdio_dev *sdiodev = ctx; + + brcmf_sdiod_regwl(sdiodev, addr, val, NULL); +} + +static const struct brcmf_buscore_ops brcmf_sdio_buscore_ops = { + .prepare = brcmf_sdio_buscoreprep, + .exit_dl = brcmf_sdio_buscore_exitdl, + .read32 = brcmf_sdio_buscore_read32, + .write32 = brcmf_sdio_buscore_write32, +}; + +static bool +brcmf_sdio_probe_attach(struct brcmf_sdio *bus) +{ + u8 clkctl = 0; + int err = 0; + int reg_addr; + u32 reg_val; + u32 drivestrength; + + sdio_claim_host(bus->sdiodev->func[1]); + + pr_debug("F1 signature read @0x18000000=0x%4x\n", + brcmf_sdiod_regrl(bus->sdiodev, SI_ENUM_BASE, NULL)); + + /* + * Force PLL off until brcmf_chip_attach() + * programs PLL control regs + */ + + brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, + BRCMF_INIT_CLKCTL1, &err); + if (!err) + clkctl = brcmf_sdiod_regrb(bus->sdiodev, + SBSDIO_FUNC1_CHIPCLKCSR, &err); + + if (err || ((clkctl & ~SBSDIO_AVBITS) != BRCMF_INIT_CLKCTL1)) { + brcmf_err("ChipClkCSR access: err %d wrote 0x%02x read 0x%02x\n", + err, BRCMF_INIT_CLKCTL1, clkctl); + goto fail; + } + + /* SDIO register access works so moving + * state from UNKNOWN to DOWN. + */ + brcmf_bus_change_state(bus->sdiodev->bus_if, BRCMF_BUS_DOWN); + + bus->ci = brcmf_chip_attach(bus->sdiodev, &brcmf_sdio_buscore_ops); + if (IS_ERR(bus->ci)) { + brcmf_err("brcmf_chip_attach failed!\n"); + bus->ci = NULL; + goto fail; + } + + if (brcmf_sdio_kso_init(bus)) { + brcmf_err("error enabling KSO\n"); + goto fail; + } + + if ((bus->sdiodev->pdata) && (bus->sdiodev->pdata->drive_strength)) + drivestrength = bus->sdiodev->pdata->drive_strength; + else + drivestrength = DEFAULT_SDIO_DRIVE_STRENGTH; + brcmf_sdio_drivestrengthinit(bus->sdiodev, bus->ci, drivestrength); + + /* Get info on the SOCRAM cores... */ + bus->ramsize = bus->ci->ramsize; + if (!(bus->ramsize)) { + brcmf_err("failed to find SOCRAM memory!\n"); + goto fail; + } + + /* Set card control so an SDIO card reset does a WLAN backplane reset */ + reg_val = brcmf_sdiod_regrb(bus->sdiodev, + SDIO_CCCR_BRCM_CARDCTRL, &err); + if (err) + goto fail; + + reg_val |= SDIO_CCCR_BRCM_CARDCTRL_WLANRESET; + + brcmf_sdiod_regwb(bus->sdiodev, + SDIO_CCCR_BRCM_CARDCTRL, reg_val, &err); + if (err) + goto fail; + + /* set PMUControl so a backplane reset does PMU state reload */ + reg_addr = CORE_CC_REG(brcmf_chip_get_chipcommon(bus->ci)->base, + pmucontrol); + reg_val = brcmf_sdiod_regrl(bus->sdiodev, reg_addr, &err); + if (err) + goto fail; + + reg_val |= (BCMA_CC_PMU_CTL_RES_RELOAD << BCMA_CC_PMU_CTL_RES_SHIFT); + + brcmf_sdiod_regwl(bus->sdiodev, reg_addr, reg_val, &err); + if (err) + goto fail; + + sdio_release_host(bus->sdiodev->func[1]); + + brcmu_pktq_init(&bus->txq, (PRIOMASK + 1), TXQLEN); + + /* allocate header buffer */ + bus->hdrbuf = kzalloc(MAX_HDR_READ + bus->head_align, GFP_KERNEL); + if (!bus->hdrbuf) + return false; + /* Locate an appropriately-aligned portion of hdrbuf */ + bus->rxhdr = (u8 *) roundup((unsigned long)&bus->hdrbuf[0], + bus->head_align); + + /* Set the poll and/or interrupt flags */ + bus->intr = true; + bus->poll = false; + if (bus->poll) + bus->pollrate = 1; + + return true; + +fail: + sdio_release_host(bus->sdiodev->func[1]); + return false; +} + +static int +brcmf_sdio_watchdog_thread(void *data) +{ + struct brcmf_sdio *bus = (struct brcmf_sdio *)data; + + allow_signal(SIGTERM); + /* Run until signal received */ + while (1) { + if (kthread_should_stop()) + break; + if (!wait_for_completion_interruptible(&bus->watchdog_wait)) { + brcmf_sdio_bus_watchdog(bus); + /* Count the tick for reference */ + bus->sdcnt.tickcnt++; + reinit_completion(&bus->watchdog_wait); + } else + break; + } + return 0; +} + +static void +brcmf_sdio_watchdog(unsigned long data) +{ + struct brcmf_sdio *bus = (struct brcmf_sdio *)data; + + if (bus->watchdog_tsk) { + complete(&bus->watchdog_wait); + /* Reschedule the watchdog */ + if (bus->wd_timer_valid) + mod_timer(&bus->timer, + jiffies + BRCMF_WD_POLL_MS * HZ / 1000); + } +} + +static struct brcmf_bus_ops brcmf_sdio_bus_ops = { + .stop = brcmf_sdio_bus_stop, + .preinit = brcmf_sdio_bus_preinit, + .txdata = brcmf_sdio_bus_txdata, + .txctl = brcmf_sdio_bus_txctl, + .rxctl = brcmf_sdio_bus_rxctl, + .gettxq = brcmf_sdio_bus_gettxq, + .wowl_config = brcmf_sdio_wowl_config +}; + +static void brcmf_sdio_firmware_callback(struct device *dev, + const struct firmware *code, + void *nvram, u32 nvram_len) +{ + 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 = 0; + u8 saveclk; + + brcmf_dbg(TRACE, "Enter: dev=%s\n", dev_name(dev)); + + /* try to download image and nvram to the dongle */ + if (bus_if->state == BRCMF_BUS_DOWN) { + bus->alp_only = true; + err = brcmf_sdio_download_firmware(bus, code, nvram, nvram_len); + if (err) + goto fail; + bus->alp_only = false; + } + + if (!bus_if->drvr) + return; + + /* Start the watchdog timer */ + bus->sdcnt.tickcnt = 0; + brcmf_sdio_wd_timer(bus, BRCMF_WD_POLL_MS); + + sdio_claim_host(sdiodev->func[1]); + + /* Make sure backplane clock is on, needed to generate F2 interrupt */ + brcmf_sdio_clkctl(bus, CLK_AVAIL, false); + if (bus->clkstate != CLK_AVAIL) + goto release; + + /* Force clocks on backplane to be sure F2 interrupt propagates */ + saveclk = brcmf_sdiod_regrb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, &err); + if (!err) { + brcmf_sdiod_regwb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, + (saveclk | SBSDIO_FORCE_HT), &err); + } + if (err) { + brcmf_err("Failed to force clock for F2: err %d\n", err); + goto release; + } + + /* Enable function 2 (frame transfers) */ + w_sdreg32(bus, SDPCM_PROT_VERSION << SMB_DATA_VERSION_SHIFT, + offsetof(struct sdpcmd_regs, tosbmailboxdata)); + err = sdio_enable_func(sdiodev->func[SDIO_FUNC_2]); + + + brcmf_dbg(INFO, "enable F2: err=%d\n", err); + + /* If F2 successfully enabled, set core and enable interrupts */ + if (!err) { + /* Set up the interrupt mask and enable interrupts */ + bus->hostintmask = HOSTINTMASK; + w_sdreg32(bus, bus->hostintmask, + offsetof(struct sdpcmd_regs, hostintmask)); + + brcmf_sdiod_regwb(sdiodev, SBSDIO_WATERMARK, 8, &err); + } else { + /* Disable F2 again */ + sdio_disable_func(sdiodev->func[SDIO_FUNC_2]); + goto release; + } + + if (brcmf_chip_sr_capable(bus->ci)) { + brcmf_sdio_sr_init(bus); + } else { + /* Restore previous clock setting */ + brcmf_sdiod_regwb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, + saveclk, &err); + } + + if (err == 0) { + err = brcmf_sdiod_intr_register(sdiodev); + if (err != 0) + brcmf_err("intr register failed:%d\n", err); + } + + /* If we didn't come up, turn off backplane clock */ + if (err != 0) + brcmf_sdio_clkctl(bus, CLK_NONE, false); + + sdio_release_host(sdiodev->func[1]); + + err = brcmf_bus_start(dev); + if (err != 0) { + brcmf_err("dongle is not responding\n"); + goto fail; + } + return; + +release: + sdio_release_host(sdiodev->func[1]); +fail: + brcmf_dbg(TRACE, "failed: dev=%s, err=%d\n", dev_name(dev), err); + device_release_driver(dev); +} + +struct brcmf_sdio *brcmf_sdio_probe(struct brcmf_sdio_dev *sdiodev) +{ + int ret; + struct brcmf_sdio *bus; + + brcmf_dbg(TRACE, "Enter\n"); + + /* Allocate private bus interface state */ + bus = kzalloc(sizeof(struct brcmf_sdio), GFP_ATOMIC); + if (!bus) + goto fail; + + bus->sdiodev = sdiodev; + sdiodev->bus = bus; + skb_queue_head_init(&bus->glom); + bus->txbound = BRCMF_TXBOUND; + bus->rxbound = BRCMF_RXBOUND; + bus->txminmax = BRCMF_TXMINMAX; + bus->tx_seq = SDPCM_SEQ_WRAP - 1; + + /* platform specific configuration: + * alignments must be at least 4 bytes for ADMA + */ + bus->head_align = ALIGNMENT; + bus->sgentry_align = ALIGNMENT; + if (sdiodev->pdata) { + if (sdiodev->pdata->sd_head_align > ALIGNMENT) + bus->head_align = sdiodev->pdata->sd_head_align; + if (sdiodev->pdata->sd_sgentry_align > ALIGNMENT) + bus->sgentry_align = sdiodev->pdata->sd_sgentry_align; + } + + INIT_WORK(&bus->datawork, brcmf_sdio_dataworker); + bus->brcmf_wq = create_singlethread_workqueue("brcmf_wq"); + if (bus->brcmf_wq == NULL) { + brcmf_err("insufficient memory to create txworkqueue\n"); + goto fail; + } + + /* attempt to attach to the dongle */ + if (!(brcmf_sdio_probe_attach(bus))) { + brcmf_err("brcmf_sdio_probe_attach failed\n"); + goto fail; + } + + spin_lock_init(&bus->rxctl_lock); + spin_lock_init(&bus->txq_lock); + sema_init(&bus->tx_seq_lock, 1); + init_waitqueue_head(&bus->ctrl_wait); + init_waitqueue_head(&bus->dcmd_resp_wait); + + /* Set up the watchdog timer */ + init_timer(&bus->timer); + bus->timer.data = (unsigned long)bus; + bus->timer.function = brcmf_sdio_watchdog; + + /* Initialize watchdog thread */ + init_completion(&bus->watchdog_wait); + bus->watchdog_tsk = kthread_run(brcmf_sdio_watchdog_thread, + bus, "brcmf_watchdog"); + if (IS_ERR(bus->watchdog_tsk)) { + pr_warn("brcmf_watchdog thread failed to start\n"); + bus->watchdog_tsk = NULL; + } + /* Initialize DPC thread */ + atomic_set(&bus->dpc_tskcnt, 0); + + /* Assign bus interface call back */ + bus->sdiodev->bus_if->dev = bus->sdiodev->dev; + bus->sdiodev->bus_if->ops = &brcmf_sdio_bus_ops; + bus->sdiodev->bus_if->chip = bus->ci->chip; + bus->sdiodev->bus_if->chiprev = bus->ci->chiprev; + + /* default sdio bus header length for tx packet */ + bus->tx_hdrlen = SDPCM_HWHDR_LEN + SDPCM_SWHDR_LEN; + + /* Attach to the common layer, reserve hdr space */ + ret = brcmf_attach(bus->sdiodev->dev); + if (ret != 0) { + brcmf_err("brcmf_attach failed\n"); + goto fail; + } + + /* Query the F2 block size, set roundup accordingly */ + bus->blocksize = bus->sdiodev->func[2]->cur_blksize; + bus->roundup = min(max_roundup, bus->blocksize); + + /* Allocate buffers */ + if (bus->sdiodev->bus_if->maxctl) { + bus->sdiodev->bus_if->maxctl += bus->roundup; + bus->rxblen = + roundup((bus->sdiodev->bus_if->maxctl + SDPCM_HDRLEN), + ALIGNMENT) + bus->head_align; + bus->rxbuf = kmalloc(bus->rxblen, GFP_ATOMIC); + if (!(bus->rxbuf)) { + brcmf_err("rxbuf allocation failed\n"); + goto fail; + } + } + + sdio_claim_host(bus->sdiodev->func[1]); + + /* Disable F2 to clear any intermediate frame state on the dongle */ + sdio_disable_func(bus->sdiodev->func[SDIO_FUNC_2]); + + bus->rxflow = false; + + /* Done with backplane-dependent accesses, can drop clock... */ + brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, 0, NULL); + + sdio_release_host(bus->sdiodev->func[1]); + + /* ...and initialize clock/power states */ + bus->clkstate = CLK_SDONLY; + bus->idletime = BRCMF_IDLE_INTERVAL; + bus->idleclock = BRCMF_IDLE_ACTIVE; + + /* SR state */ + bus->sleeping = false; + bus->sr_enabled = false; + + brcmf_sdio_debugfs_create(bus); + brcmf_dbg(INFO, "completed!!\n"); + + ret = brcmf_sdio_get_fwnames(bus->ci, sdiodev); + if (ret) + goto fail; + + ret = brcmf_fw_get_firmwares(sdiodev->dev, BRCMF_FW_REQUEST_NVRAM, + sdiodev->fw_name, sdiodev->nvram_name, + brcmf_sdio_firmware_callback); + if (ret != 0) { + brcmf_err("async firmware request failed: %d\n", ret); + goto fail; + } + + return bus; + +fail: + brcmf_sdio_remove(bus); + return NULL; +} + +/* Detach and free everything */ +void brcmf_sdio_remove(struct brcmf_sdio *bus) +{ + brcmf_dbg(TRACE, "Enter\n"); + + if (bus) { + /* De-register interrupt handler */ + brcmf_sdiod_intr_unregister(bus->sdiodev); + + brcmf_detach(bus->sdiodev->dev); + + cancel_work_sync(&bus->datawork); + if (bus->brcmf_wq) + destroy_workqueue(bus->brcmf_wq); + + if (bus->ci) { + if (bus->sdiodev->bus_if->state == BRCMF_BUS_DOWN) { + sdio_claim_host(bus->sdiodev->func[1]); + brcmf_sdio_clkctl(bus, CLK_AVAIL, false); + /* Leave the device in state where it is + * 'quiet'. This is done by putting it in + * download_state which essentially resets + * all necessary cores. + */ + msleep(20); + brcmf_chip_enter_download(bus->ci); + brcmf_sdio_clkctl(bus, CLK_NONE, false); + sdio_release_host(bus->sdiodev->func[1]); + } + brcmf_chip_detach(bus->ci); + } + + kfree(bus->rxbuf); + kfree(bus->hdrbuf); + kfree(bus); + } + + brcmf_dbg(TRACE, "Disconnected\n"); +} + +void brcmf_sdio_wd_timer(struct brcmf_sdio *bus, uint wdtick) +{ + /* Totally stop the timer */ + if (!wdtick && bus->wd_timer_valid) { + del_timer_sync(&bus->timer); + bus->wd_timer_valid = false; + bus->save_ms = wdtick; + return; + } + + /* don't start the wd until fw is loaded */ + if (bus->sdiodev->bus_if->state != BRCMF_BUS_DATA) + return; + + if (wdtick) { + if (bus->save_ms != BRCMF_WD_POLL_MS) { + if (bus->wd_timer_valid) + /* Stop timer and restart at new value */ + del_timer_sync(&bus->timer); + + /* Create timer again when watchdog period is + dynamically changed or in the first instance + */ + bus->timer.expires = + jiffies + BRCMF_WD_POLL_MS * HZ / 1000; + add_timer(&bus->timer); + + } else { + /* Re arm the timer, at last watchdog period */ + mod_timer(&bus->timer, + jiffies + BRCMF_WD_POLL_MS * HZ / 1000); + } + + bus->wd_timer_valid = true; + bus->save_ms = wdtick; + } +} diff --git a/drivers/net/wireless/brcm80211/brcmfmac/sdio.h b/drivers/net/wireless/brcm80211/brcmfmac/sdio.h new file mode 100644 index 000000000000..8eb42620129c --- /dev/null +++ b/drivers/net/wireless/brcm80211/brcmfmac/sdio.h @@ -0,0 +1,340 @@ +/* + * Copyright (c) 2010 Broadcom Corporation + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef BRCMFMAC_SDIO_H +#define BRCMFMAC_SDIO_H + +#include +#include +#include "firmware.h" + +#define SDIO_FUNC_0 0 +#define SDIO_FUNC_1 1 +#define SDIO_FUNC_2 2 + +#define SDIOD_FBR_SIZE 0x100 + +/* io_en */ +#define SDIO_FUNC_ENABLE_1 0x02 +#define SDIO_FUNC_ENABLE_2 0x04 + +/* io_rdys */ +#define SDIO_FUNC_READY_1 0x02 +#define SDIO_FUNC_READY_2 0x04 + +/* intr_status */ +#define INTR_STATUS_FUNC1 0x2 +#define INTR_STATUS_FUNC2 0x4 + +/* Maximum number of I/O funcs */ +#define SDIOD_MAX_IOFUNCS 7 + +/* mask of register map */ +#define REG_F0_REG_MASK 0x7FF +#define REG_F1_MISC_MASK 0x1FFFF + +/* as of sdiod rev 0, supports 3 functions */ +#define SBSDIO_NUM_FUNCTION 3 + +/* function 0 vendor specific CCCR registers */ +#define SDIO_CCCR_BRCM_CARDCAP 0xf0 +#define SDIO_CCCR_BRCM_CARDCAP_CMD14_SUPPORT 0x02 +#define SDIO_CCCR_BRCM_CARDCAP_CMD14_EXT 0x04 +#define SDIO_CCCR_BRCM_CARDCAP_CMD_NODEC 0x08 +#define SDIO_CCCR_BRCM_CARDCTRL 0xf1 +#define SDIO_CCCR_BRCM_CARDCTRL_WLANRESET 0x02 +#define SDIO_CCCR_BRCM_SEPINT 0xf2 + +#define SDIO_SEPINT_MASK 0x01 +#define SDIO_SEPINT_OE 0x02 +#define SDIO_SEPINT_ACT_HI 0x04 + +/* function 1 miscellaneous registers */ + +/* sprom command and status */ +#define SBSDIO_SPROM_CS 0x10000 +/* sprom info register */ +#define SBSDIO_SPROM_INFO 0x10001 +/* sprom indirect access data byte 0 */ +#define SBSDIO_SPROM_DATA_LOW 0x10002 +/* sprom indirect access data byte 1 */ +#define SBSDIO_SPROM_DATA_HIGH 0x10003 +/* sprom indirect access addr byte 0 */ +#define SBSDIO_SPROM_ADDR_LOW 0x10004 +/* gpio select */ +#define SBSDIO_GPIO_SELECT 0x10005 +/* gpio output */ +#define SBSDIO_GPIO_OUT 0x10006 +/* gpio enable */ +#define SBSDIO_GPIO_EN 0x10007 +/* rev < 7, watermark for sdio device */ +#define SBSDIO_WATERMARK 0x10008 +/* control busy signal generation */ +#define SBSDIO_DEVICE_CTL 0x10009 + +/* SB Address Window Low (b15) */ +#define SBSDIO_FUNC1_SBADDRLOW 0x1000A +/* SB Address Window Mid (b23:b16) */ +#define SBSDIO_FUNC1_SBADDRMID 0x1000B +/* SB Address Window High (b31:b24) */ +#define SBSDIO_FUNC1_SBADDRHIGH 0x1000C +/* Frame Control (frame term/abort) */ +#define SBSDIO_FUNC1_FRAMECTRL 0x1000D +/* ChipClockCSR (ALP/HT ctl/status) */ +#define SBSDIO_FUNC1_CHIPCLKCSR 0x1000E +/* SdioPullUp (on cmd, d0-d2) */ +#define SBSDIO_FUNC1_SDIOPULLUP 0x1000F +/* Write Frame Byte Count Low */ +#define SBSDIO_FUNC1_WFRAMEBCLO 0x10019 +/* Write Frame Byte Count High */ +#define SBSDIO_FUNC1_WFRAMEBCHI 0x1001A +/* Read Frame Byte Count Low */ +#define SBSDIO_FUNC1_RFRAMEBCLO 0x1001B +/* Read Frame Byte Count High */ +#define SBSDIO_FUNC1_RFRAMEBCHI 0x1001C +/* MesBusyCtl (rev 11) */ +#define SBSDIO_FUNC1_MESBUSYCTRL 0x1001D +/* Sdio Core Rev 12 */ +#define SBSDIO_FUNC1_WAKEUPCTRL 0x1001E +#define SBSDIO_FUNC1_WCTRL_ALPWAIT_MASK 0x1 +#define SBSDIO_FUNC1_WCTRL_ALPWAIT_SHIFT 0 +#define SBSDIO_FUNC1_WCTRL_HTWAIT_MASK 0x2 +#define SBSDIO_FUNC1_WCTRL_HTWAIT_SHIFT 1 +#define SBSDIO_FUNC1_SLEEPCSR 0x1001F +#define SBSDIO_FUNC1_SLEEPCSR_KSO_MASK 0x1 +#define SBSDIO_FUNC1_SLEEPCSR_KSO_SHIFT 0 +#define SBSDIO_FUNC1_SLEEPCSR_KSO_EN 1 +#define SBSDIO_FUNC1_SLEEPCSR_DEVON_MASK 0x2 +#define SBSDIO_FUNC1_SLEEPCSR_DEVON_SHIFT 1 + +#define SBSDIO_FUNC1_MISC_REG_START 0x10000 /* f1 misc register start */ +#define SBSDIO_FUNC1_MISC_REG_LIMIT 0x1001F /* f1 misc register end */ + +/* function 1 OCP space */ + +/* sb offset addr is <= 15 bits, 32k */ +#define SBSDIO_SB_OFT_ADDR_MASK 0x07FFF +#define SBSDIO_SB_OFT_ADDR_LIMIT 0x08000 +/* with b15, maps to 32-bit SB access */ +#define SBSDIO_SB_ACCESS_2_4B_FLAG 0x08000 + +/* valid bits in SBSDIO_FUNC1_SBADDRxxx regs */ + +#define SBSDIO_SBADDRLOW_MASK 0x80 /* Valid bits in SBADDRLOW */ +#define SBSDIO_SBADDRMID_MASK 0xff /* Valid bits in SBADDRMID */ +#define SBSDIO_SBADDRHIGH_MASK 0xffU /* Valid bits in SBADDRHIGH */ +/* Address bits from SBADDR regs */ +#define SBSDIO_SBWINDOW_MASK 0xffff8000 + +#define SDIOH_READ 0 /* Read request */ +#define SDIOH_WRITE 1 /* Write request */ + +#define SDIOH_DATA_FIX 0 /* Fixed addressing */ +#define SDIOH_DATA_INC 1 /* Incremental addressing */ + +/* internal return code */ +#define SUCCESS 0 +#define ERROR 1 + +/* Packet alignment for most efficient SDIO (can change based on platform) */ +#define BRCMF_SDALIGN (1 << 6) + +/* watchdog polling interval in ms */ +#define BRCMF_WD_POLL_MS 10 + +struct brcmf_sdreg { + int func; + int offset; + int value; +}; + +struct brcmf_sdio; + +struct brcmf_sdio_dev { + struct sdio_func *func[SDIO_MAX_FUNCS]; + u8 num_funcs; /* Supported funcs on client */ + u32 sbwad; /* Save backplane window address */ + struct brcmf_sdio *bus; + atomic_t suspend; /* suspend flag */ + wait_queue_head_t request_word_wait; + wait_queue_head_t request_buffer_wait; + struct device *dev; + struct brcmf_bus *bus_if; + struct brcmfmac_sdio_platform_data *pdata; + bool oob_irq_requested; + bool irq_en; /* irq enable flags */ + spinlock_t irq_en_lock; + bool irq_wake; /* irq wake enable flags */ + bool sg_support; + uint max_request_size; + ushort max_segment_count; + uint max_segment_size; + uint txglomsz; + struct sg_table sgtable; + char fw_name[BRCMF_FW_PATH_LEN + BRCMF_FW_NAME_LEN]; + char nvram_name[BRCMF_FW_PATH_LEN + BRCMF_FW_NAME_LEN]; + bool wowl_enabled; +}; + +/* sdio core registers */ +struct sdpcmd_regs { + u32 corecontrol; /* 0x00, rev8 */ + u32 corestatus; /* rev8 */ + u32 PAD[1]; + u32 biststatus; /* rev8 */ + + /* PCMCIA access */ + u16 pcmciamesportaladdr; /* 0x010, rev8 */ + u16 PAD[1]; + u16 pcmciamesportalmask; /* rev8 */ + u16 PAD[1]; + u16 pcmciawrframebc; /* rev8 */ + u16 PAD[1]; + u16 pcmciaunderflowtimer; /* rev8 */ + u16 PAD[1]; + + /* interrupt */ + u32 intstatus; /* 0x020, rev8 */ + u32 hostintmask; /* rev8 */ + u32 intmask; /* rev8 */ + u32 sbintstatus; /* rev8 */ + u32 sbintmask; /* rev8 */ + u32 funcintmask; /* rev4 */ + u32 PAD[2]; + u32 tosbmailbox; /* 0x040, rev8 */ + u32 tohostmailbox; /* rev8 */ + u32 tosbmailboxdata; /* rev8 */ + u32 tohostmailboxdata; /* rev8 */ + + /* synchronized access to registers in SDIO clock domain */ + u32 sdioaccess; /* 0x050, rev8 */ + u32 PAD[3]; + + /* PCMCIA frame control */ + u8 pcmciaframectrl; /* 0x060, rev8 */ + u8 PAD[3]; + u8 pcmciawatermark; /* rev8 */ + u8 PAD[155]; + + /* interrupt batching control */ + u32 intrcvlazy; /* 0x100, rev8 */ + u32 PAD[3]; + + /* counters */ + u32 cmd52rd; /* 0x110, rev8 */ + u32 cmd52wr; /* rev8 */ + u32 cmd53rd; /* rev8 */ + u32 cmd53wr; /* rev8 */ + u32 abort; /* rev8 */ + u32 datacrcerror; /* rev8 */ + u32 rdoutofsync; /* rev8 */ + u32 wroutofsync; /* rev8 */ + u32 writebusy; /* rev8 */ + u32 readwait; /* rev8 */ + u32 readterm; /* rev8 */ + u32 writeterm; /* rev8 */ + u32 PAD[40]; + u32 clockctlstatus; /* rev8 */ + u32 PAD[7]; + + u32 PAD[128]; /* DMA engines */ + + /* SDIO/PCMCIA CIS region */ + char cis[512]; /* 0x400-0x5ff, rev6 */ + + /* PCMCIA function control registers */ + char pcmciafcr[256]; /* 0x600-6ff, rev6 */ + u16 PAD[55]; + + /* PCMCIA backplane access */ + u16 backplanecsr; /* 0x76E, rev6 */ + u16 backplaneaddr0; /* rev6 */ + u16 backplaneaddr1; /* rev6 */ + u16 backplaneaddr2; /* rev6 */ + u16 backplaneaddr3; /* rev6 */ + u16 backplanedata0; /* rev6 */ + u16 backplanedata1; /* rev6 */ + u16 backplanedata2; /* rev6 */ + u16 backplanedata3; /* rev6 */ + u16 PAD[31]; + + /* sprom "size" & "blank" info */ + u16 spromstatus; /* 0x7BE, rev2 */ + u32 PAD[464]; + + u16 PAD[0x80]; +}; + +/* Register/deregister interrupt handler. */ +int brcmf_sdiod_intr_register(struct brcmf_sdio_dev *sdiodev); +int brcmf_sdiod_intr_unregister(struct brcmf_sdio_dev *sdiodev); + +/* sdio device register access interface */ +u8 brcmf_sdiod_regrb(struct brcmf_sdio_dev *sdiodev, u32 addr, int *ret); +u32 brcmf_sdiod_regrl(struct brcmf_sdio_dev *sdiodev, u32 addr, int *ret); +void brcmf_sdiod_regwb(struct brcmf_sdio_dev *sdiodev, u32 addr, u8 data, + int *ret); +void brcmf_sdiod_regwl(struct brcmf_sdio_dev *sdiodev, u32 addr, u32 data, + int *ret); + +/* Buffer transfer to/from device (client) core via cmd53. + * fn: function number + * flags: backplane width, address increment, sync/async + * buf: pointer to memory data buffer + * nbytes: number of bytes to transfer to/from buf + * pkt: pointer to packet associated with buf (if any) + * complete: callback function for command completion (async only) + * handle: handle for completion callback (first arg in callback) + * Returns 0 or error code. + * NOTE: Async operation is not currently supported. + */ +int brcmf_sdiod_send_pkt(struct brcmf_sdio_dev *sdiodev, + struct sk_buff_head *pktq); +int brcmf_sdiod_send_buf(struct brcmf_sdio_dev *sdiodev, u8 *buf, uint nbytes); + +int brcmf_sdiod_recv_pkt(struct brcmf_sdio_dev *sdiodev, struct sk_buff *pkt); +int brcmf_sdiod_recv_buf(struct brcmf_sdio_dev *sdiodev, u8 *buf, uint nbytes); +int brcmf_sdiod_recv_chain(struct brcmf_sdio_dev *sdiodev, + struct sk_buff_head *pktq, uint totlen); + +/* Flags bits */ + +/* Four-byte target (backplane) width (vs. two-byte) */ +#define SDIO_REQ_4BYTE 0x1 +/* Fixed address (FIFO) (vs. incrementing address) */ +#define SDIO_REQ_FIXED 0x2 + +/* Read/write to memory block (F1, no FIFO) via CMD53 (sync only). + * rw: read or write (0/1) + * addr: direct SDIO address + * buf: pointer to memory data buffer + * nbytes: number of bytes to transfer to/from buf + * Returns 0 or error code. + */ +int brcmf_sdiod_ramrw(struct brcmf_sdio_dev *sdiodev, bool write, u32 address, + u8 *data, uint size); + +/* Issue an abort to the specified function */ +int brcmf_sdiod_abort(struct brcmf_sdio_dev *sdiodev, uint fn); + +struct brcmf_sdio *brcmf_sdio_probe(struct brcmf_sdio_dev *sdiodev); +void brcmf_sdio_remove(struct brcmf_sdio *bus); +void brcmf_sdio_isr(struct brcmf_sdio *bus); + +void brcmf_sdio_wd_timer(struct brcmf_sdio *bus, uint wdtick); +void brcmf_sdio_wowl_config(struct device *dev, bool enabled); + +#endif /* BRCMFMAC_SDIO_H */ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/sdio_host.h b/drivers/net/wireless/brcm80211/brcmfmac/sdio_host.h deleted file mode 100644 index 262aedfeaa30..000000000000 --- a/drivers/net/wireless/brcm80211/brcmfmac/sdio_host.h +++ /dev/null @@ -1,340 +0,0 @@ -/* - * Copyright (c) 2010 Broadcom Corporation - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY - * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION - * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN - * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef _BRCM_SDH_H_ -#define _BRCM_SDH_H_ - -#include -#include -#include "firmware.h" - -#define SDIO_FUNC_0 0 -#define SDIO_FUNC_1 1 -#define SDIO_FUNC_2 2 - -#define SDIOD_FBR_SIZE 0x100 - -/* io_en */ -#define SDIO_FUNC_ENABLE_1 0x02 -#define SDIO_FUNC_ENABLE_2 0x04 - -/* io_rdys */ -#define SDIO_FUNC_READY_1 0x02 -#define SDIO_FUNC_READY_2 0x04 - -/* intr_status */ -#define INTR_STATUS_FUNC1 0x2 -#define INTR_STATUS_FUNC2 0x4 - -/* Maximum number of I/O funcs */ -#define SDIOD_MAX_IOFUNCS 7 - -/* mask of register map */ -#define REG_F0_REG_MASK 0x7FF -#define REG_F1_MISC_MASK 0x1FFFF - -/* as of sdiod rev 0, supports 3 functions */ -#define SBSDIO_NUM_FUNCTION 3 - -/* function 0 vendor specific CCCR registers */ -#define SDIO_CCCR_BRCM_CARDCAP 0xf0 -#define SDIO_CCCR_BRCM_CARDCAP_CMD14_SUPPORT 0x02 -#define SDIO_CCCR_BRCM_CARDCAP_CMD14_EXT 0x04 -#define SDIO_CCCR_BRCM_CARDCAP_CMD_NODEC 0x08 -#define SDIO_CCCR_BRCM_CARDCTRL 0xf1 -#define SDIO_CCCR_BRCM_CARDCTRL_WLANRESET 0x02 -#define SDIO_CCCR_BRCM_SEPINT 0xf2 - -#define SDIO_SEPINT_MASK 0x01 -#define SDIO_SEPINT_OE 0x02 -#define SDIO_SEPINT_ACT_HI 0x04 - -/* function 1 miscellaneous registers */ - -/* sprom command and status */ -#define SBSDIO_SPROM_CS 0x10000 -/* sprom info register */ -#define SBSDIO_SPROM_INFO 0x10001 -/* sprom indirect access data byte 0 */ -#define SBSDIO_SPROM_DATA_LOW 0x10002 -/* sprom indirect access data byte 1 */ -#define SBSDIO_SPROM_DATA_HIGH 0x10003 -/* sprom indirect access addr byte 0 */ -#define SBSDIO_SPROM_ADDR_LOW 0x10004 -/* gpio select */ -#define SBSDIO_GPIO_SELECT 0x10005 -/* gpio output */ -#define SBSDIO_GPIO_OUT 0x10006 -/* gpio enable */ -#define SBSDIO_GPIO_EN 0x10007 -/* rev < 7, watermark for sdio device */ -#define SBSDIO_WATERMARK 0x10008 -/* control busy signal generation */ -#define SBSDIO_DEVICE_CTL 0x10009 - -/* SB Address Window Low (b15) */ -#define SBSDIO_FUNC1_SBADDRLOW 0x1000A -/* SB Address Window Mid (b23:b16) */ -#define SBSDIO_FUNC1_SBADDRMID 0x1000B -/* SB Address Window High (b31:b24) */ -#define SBSDIO_FUNC1_SBADDRHIGH 0x1000C -/* Frame Control (frame term/abort) */ -#define SBSDIO_FUNC1_FRAMECTRL 0x1000D -/* ChipClockCSR (ALP/HT ctl/status) */ -#define SBSDIO_FUNC1_CHIPCLKCSR 0x1000E -/* SdioPullUp (on cmd, d0-d2) */ -#define SBSDIO_FUNC1_SDIOPULLUP 0x1000F -/* Write Frame Byte Count Low */ -#define SBSDIO_FUNC1_WFRAMEBCLO 0x10019 -/* Write Frame Byte Count High */ -#define SBSDIO_FUNC1_WFRAMEBCHI 0x1001A -/* Read Frame Byte Count Low */ -#define SBSDIO_FUNC1_RFRAMEBCLO 0x1001B -/* Read Frame Byte Count High */ -#define SBSDIO_FUNC1_RFRAMEBCHI 0x1001C -/* MesBusyCtl (rev 11) */ -#define SBSDIO_FUNC1_MESBUSYCTRL 0x1001D -/* Sdio Core Rev 12 */ -#define SBSDIO_FUNC1_WAKEUPCTRL 0x1001E -#define SBSDIO_FUNC1_WCTRL_ALPWAIT_MASK 0x1 -#define SBSDIO_FUNC1_WCTRL_ALPWAIT_SHIFT 0 -#define SBSDIO_FUNC1_WCTRL_HTWAIT_MASK 0x2 -#define SBSDIO_FUNC1_WCTRL_HTWAIT_SHIFT 1 -#define SBSDIO_FUNC1_SLEEPCSR 0x1001F -#define SBSDIO_FUNC1_SLEEPCSR_KSO_MASK 0x1 -#define SBSDIO_FUNC1_SLEEPCSR_KSO_SHIFT 0 -#define SBSDIO_FUNC1_SLEEPCSR_KSO_EN 1 -#define SBSDIO_FUNC1_SLEEPCSR_DEVON_MASK 0x2 -#define SBSDIO_FUNC1_SLEEPCSR_DEVON_SHIFT 1 - -#define SBSDIO_FUNC1_MISC_REG_START 0x10000 /* f1 misc register start */ -#define SBSDIO_FUNC1_MISC_REG_LIMIT 0x1001F /* f1 misc register end */ - -/* function 1 OCP space */ - -/* sb offset addr is <= 15 bits, 32k */ -#define SBSDIO_SB_OFT_ADDR_MASK 0x07FFF -#define SBSDIO_SB_OFT_ADDR_LIMIT 0x08000 -/* with b15, maps to 32-bit SB access */ -#define SBSDIO_SB_ACCESS_2_4B_FLAG 0x08000 - -/* valid bits in SBSDIO_FUNC1_SBADDRxxx regs */ - -#define SBSDIO_SBADDRLOW_MASK 0x80 /* Valid bits in SBADDRLOW */ -#define SBSDIO_SBADDRMID_MASK 0xff /* Valid bits in SBADDRMID */ -#define SBSDIO_SBADDRHIGH_MASK 0xffU /* Valid bits in SBADDRHIGH */ -/* Address bits from SBADDR regs */ -#define SBSDIO_SBWINDOW_MASK 0xffff8000 - -#define SDIOH_READ 0 /* Read request */ -#define SDIOH_WRITE 1 /* Write request */ - -#define SDIOH_DATA_FIX 0 /* Fixed addressing */ -#define SDIOH_DATA_INC 1 /* Incremental addressing */ - -/* internal return code */ -#define SUCCESS 0 -#define ERROR 1 - -/* Packet alignment for most efficient SDIO (can change based on platform) */ -#define BRCMF_SDALIGN (1 << 6) - -/* watchdog polling interval in ms */ -#define BRCMF_WD_POLL_MS 10 - -struct brcmf_sdreg { - int func; - int offset; - int value; -}; - -struct brcmf_sdio; - -struct brcmf_sdio_dev { - struct sdio_func *func[SDIO_MAX_FUNCS]; - u8 num_funcs; /* Supported funcs on client */ - u32 sbwad; /* Save backplane window address */ - struct brcmf_sdio *bus; - atomic_t suspend; /* suspend flag */ - wait_queue_head_t request_word_wait; - wait_queue_head_t request_buffer_wait; - struct device *dev; - struct brcmf_bus *bus_if; - struct brcmfmac_sdio_platform_data *pdata; - bool oob_irq_requested; - bool irq_en; /* irq enable flags */ - spinlock_t irq_en_lock; - bool irq_wake; /* irq wake enable flags */ - bool sg_support; - uint max_request_size; - ushort max_segment_count; - uint max_segment_size; - uint txglomsz; - struct sg_table sgtable; - char fw_name[BRCMF_FW_PATH_LEN + BRCMF_FW_NAME_LEN]; - char nvram_name[BRCMF_FW_PATH_LEN + BRCMF_FW_NAME_LEN]; - bool wowl_enabled; -}; - -/* sdio core registers */ -struct sdpcmd_regs { - u32 corecontrol; /* 0x00, rev8 */ - u32 corestatus; /* rev8 */ - u32 PAD[1]; - u32 biststatus; /* rev8 */ - - /* PCMCIA access */ - u16 pcmciamesportaladdr; /* 0x010, rev8 */ - u16 PAD[1]; - u16 pcmciamesportalmask; /* rev8 */ - u16 PAD[1]; - u16 pcmciawrframebc; /* rev8 */ - u16 PAD[1]; - u16 pcmciaunderflowtimer; /* rev8 */ - u16 PAD[1]; - - /* interrupt */ - u32 intstatus; /* 0x020, rev8 */ - u32 hostintmask; /* rev8 */ - u32 intmask; /* rev8 */ - u32 sbintstatus; /* rev8 */ - u32 sbintmask; /* rev8 */ - u32 funcintmask; /* rev4 */ - u32 PAD[2]; - u32 tosbmailbox; /* 0x040, rev8 */ - u32 tohostmailbox; /* rev8 */ - u32 tosbmailboxdata; /* rev8 */ - u32 tohostmailboxdata; /* rev8 */ - - /* synchronized access to registers in SDIO clock domain */ - u32 sdioaccess; /* 0x050, rev8 */ - u32 PAD[3]; - - /* PCMCIA frame control */ - u8 pcmciaframectrl; /* 0x060, rev8 */ - u8 PAD[3]; - u8 pcmciawatermark; /* rev8 */ - u8 PAD[155]; - - /* interrupt batching control */ - u32 intrcvlazy; /* 0x100, rev8 */ - u32 PAD[3]; - - /* counters */ - u32 cmd52rd; /* 0x110, rev8 */ - u32 cmd52wr; /* rev8 */ - u32 cmd53rd; /* rev8 */ - u32 cmd53wr; /* rev8 */ - u32 abort; /* rev8 */ - u32 datacrcerror; /* rev8 */ - u32 rdoutofsync; /* rev8 */ - u32 wroutofsync; /* rev8 */ - u32 writebusy; /* rev8 */ - u32 readwait; /* rev8 */ - u32 readterm; /* rev8 */ - u32 writeterm; /* rev8 */ - u32 PAD[40]; - u32 clockctlstatus; /* rev8 */ - u32 PAD[7]; - - u32 PAD[128]; /* DMA engines */ - - /* SDIO/PCMCIA CIS region */ - char cis[512]; /* 0x400-0x5ff, rev6 */ - - /* PCMCIA function control registers */ - char pcmciafcr[256]; /* 0x600-6ff, rev6 */ - u16 PAD[55]; - - /* PCMCIA backplane access */ - u16 backplanecsr; /* 0x76E, rev6 */ - u16 backplaneaddr0; /* rev6 */ - u16 backplaneaddr1; /* rev6 */ - u16 backplaneaddr2; /* rev6 */ - u16 backplaneaddr3; /* rev6 */ - u16 backplanedata0; /* rev6 */ - u16 backplanedata1; /* rev6 */ - u16 backplanedata2; /* rev6 */ - u16 backplanedata3; /* rev6 */ - u16 PAD[31]; - - /* sprom "size" & "blank" info */ - u16 spromstatus; /* 0x7BE, rev2 */ - u32 PAD[464]; - - u16 PAD[0x80]; -}; - -/* Register/deregister interrupt handler. */ -int brcmf_sdiod_intr_register(struct brcmf_sdio_dev *sdiodev); -int brcmf_sdiod_intr_unregister(struct brcmf_sdio_dev *sdiodev); - -/* sdio device register access interface */ -u8 brcmf_sdiod_regrb(struct brcmf_sdio_dev *sdiodev, u32 addr, int *ret); -u32 brcmf_sdiod_regrl(struct brcmf_sdio_dev *sdiodev, u32 addr, int *ret); -void brcmf_sdiod_regwb(struct brcmf_sdio_dev *sdiodev, u32 addr, u8 data, - int *ret); -void brcmf_sdiod_regwl(struct brcmf_sdio_dev *sdiodev, u32 addr, u32 data, - int *ret); - -/* Buffer transfer to/from device (client) core via cmd53. - * fn: function number - * flags: backplane width, address increment, sync/async - * buf: pointer to memory data buffer - * nbytes: number of bytes to transfer to/from buf - * pkt: pointer to packet associated with buf (if any) - * complete: callback function for command completion (async only) - * handle: handle for completion callback (first arg in callback) - * Returns 0 or error code. - * NOTE: Async operation is not currently supported. - */ -int brcmf_sdiod_send_pkt(struct brcmf_sdio_dev *sdiodev, - struct sk_buff_head *pktq); -int brcmf_sdiod_send_buf(struct brcmf_sdio_dev *sdiodev, u8 *buf, uint nbytes); - -int brcmf_sdiod_recv_pkt(struct brcmf_sdio_dev *sdiodev, struct sk_buff *pkt); -int brcmf_sdiod_recv_buf(struct brcmf_sdio_dev *sdiodev, u8 *buf, uint nbytes); -int brcmf_sdiod_recv_chain(struct brcmf_sdio_dev *sdiodev, - struct sk_buff_head *pktq, uint totlen); - -/* Flags bits */ - -/* Four-byte target (backplane) width (vs. two-byte) */ -#define SDIO_REQ_4BYTE 0x1 -/* Fixed address (FIFO) (vs. incrementing address) */ -#define SDIO_REQ_FIXED 0x2 - -/* Read/write to memory block (F1, no FIFO) via CMD53 (sync only). - * rw: read or write (0/1) - * addr: direct SDIO address - * buf: pointer to memory data buffer - * nbytes: number of bytes to transfer to/from buf - * Returns 0 or error code. - */ -int brcmf_sdiod_ramrw(struct brcmf_sdio_dev *sdiodev, bool write, u32 address, - u8 *data, uint size); - -/* Issue an abort to the specified function */ -int brcmf_sdiod_abort(struct brcmf_sdio_dev *sdiodev, uint fn); - -struct brcmf_sdio *brcmf_sdio_probe(struct brcmf_sdio_dev *sdiodev); -void brcmf_sdio_remove(struct brcmf_sdio *bus); -void brcmf_sdio_isr(struct brcmf_sdio *bus); - -void brcmf_sdio_wd_timer(struct brcmf_sdio *bus, uint wdtick); -void brcmf_sdio_wowl_config(struct device *dev, bool enabled); - -#endif /* _BRCM_SDH_H_ */ -- cgit v1.2.3 From 122d3d04d769455efba396f516f61c5a55791519 Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Tue, 28 Oct 2014 14:56:18 +0100 Subject: brcmfmac: (clean) Rename sdio related files. Rename sdio_host.h to sdio.h and dhd_sdio.c to sdio.c. Reviewed-by: Arend Van Spriel Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/Makefile | 2 +- drivers/net/wireless/brcm80211/brcmfmac/bcdc.c | 2 +- drivers/net/wireless/brcm80211/brcmfmac/btcoex.c | 2 +- drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c | 2 +- drivers/net/wireless/brcm80211/brcmfmac/common.c | 2 +- .../net/wireless/brcm80211/brcmfmac/commonring.c | 2 +- drivers/net/wireless/brcm80211/brcmfmac/core.c | 1124 ++++++++++++++++++++ drivers/net/wireless/brcm80211/brcmfmac/core.h | 189 ++++ drivers/net/wireless/brcm80211/brcmfmac/debug.c | 2 +- drivers/net/wireless/brcm80211/brcmfmac/dhd.h | 189 ---- .../net/wireless/brcm80211/brcmfmac/dhd_linux.c | 1124 -------------------- drivers/net/wireless/brcm80211/brcmfmac/feature.c | 2 +- drivers/net/wireless/brcm80211/brcmfmac/flowring.c | 2 +- drivers/net/wireless/brcm80211/brcmfmac/fweh.c | 2 +- drivers/net/wireless/brcm80211/brcmfmac/fwil.c | 2 +- drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c | 2 +- drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c | 2 +- drivers/net/wireless/brcm80211/brcmfmac/p2p.c | 2 +- drivers/net/wireless/brcm80211/brcmfmac/proto.c | 2 +- drivers/net/wireless/brcm80211/brcmfmac/vendor.c | 2 +- 20 files changed, 1329 insertions(+), 1329 deletions(-) create mode 100644 drivers/net/wireless/brcm80211/brcmfmac/core.c create mode 100644 drivers/net/wireless/brcm80211/brcmfmac/core.h delete mode 100644 drivers/net/wireless/brcm80211/brcmfmac/dhd.h delete mode 100644 drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c (limited to 'drivers/net/wireless/brcm80211') diff --git a/drivers/net/wireless/brcm80211/brcmfmac/Makefile b/drivers/net/wireless/brcm80211/brcmfmac/Makefile index 40ba46b4809d..dc4c75083085 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/Makefile +++ b/drivers/net/wireless/brcm80211/brcmfmac/Makefile @@ -31,7 +31,7 @@ brcmfmac-objs += \ p2p.o \ proto.o \ common.o \ - dhd_linux.o \ + core.o \ firmware.o \ feature.o \ btcoex.o \ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/bcdc.c b/drivers/net/wireless/brcm80211/brcmfmac/bcdc.c index d5a2d94ffde4..8e0e91c4a0b1 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/bcdc.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/bcdc.c @@ -25,7 +25,7 @@ #include #include -#include "dhd.h" +#include "core.h" #include "bus.h" #include "fwsignal.h" #include "debug.h" diff --git a/drivers/net/wireless/brcm80211/brcmfmac/btcoex.c b/drivers/net/wireless/brcm80211/brcmfmac/btcoex.c index e324414fcf6d..0445163991b7 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/btcoex.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/btcoex.c @@ -20,7 +20,7 @@ #include #include #include -#include +#include "core.h" #include "debug.h" #include "fwil.h" #include "fwil_types.h" diff --git a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c index 0390e57613cd..8822f2b8d74d 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c @@ -26,7 +26,7 @@ #include #include #include -#include "dhd.h" +#include "core.h" #include "debug.h" #include "tracepoint.h" #include "fwil_types.h" diff --git a/drivers/net/wireless/brcm80211/brcmfmac/common.c b/drivers/net/wireless/brcm80211/brcmfmac/common.c index 75642e46c37a..183cec9939ea 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/common.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/common.c @@ -19,7 +19,7 @@ #include #include #include -#include "dhd.h" +#include "core.h" #include "bus.h" #include "debug.h" #include "fwil.h" diff --git a/drivers/net/wireless/brcm80211/brcmfmac/commonring.c b/drivers/net/wireless/brcm80211/brcmfmac/commonring.c index c6d65b8e1e15..77656c711bed 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/commonring.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/commonring.c @@ -19,7 +19,7 @@ #include #include -#include "dhd.h" +#include "core.h" #include "commonring.h" diff --git a/drivers/net/wireless/brcm80211/brcmfmac/core.c b/drivers/net/wireless/brcm80211/brcmfmac/core.c new file mode 100644 index 000000000000..f407665cb1ea --- /dev/null +++ b/drivers/net/wireless/brcm80211/brcmfmac/core.c @@ -0,0 +1,1124 @@ +/* + * Copyright (c) 2010 Broadcom Corporation + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "core.h" +#include "bus.h" +#include "debug.h" +#include "fwil_types.h" +#include "p2p.h" +#include "cfg80211.h" +#include "fwil.h" +#include "fwsignal.h" +#include "feature.h" +#include "proto.h" +#include "pcie.h" + +MODULE_AUTHOR("Broadcom Corporation"); +MODULE_DESCRIPTION("Broadcom 802.11 wireless LAN fullmac driver."); +MODULE_LICENSE("Dual BSD/GPL"); + +#define MAX_WAIT_FOR_8021X_TX 50 /* msecs */ + +/* AMPDU rx reordering definitions */ +#define BRCMF_RXREORDER_FLOWID_OFFSET 0 +#define BRCMF_RXREORDER_MAXIDX_OFFSET 2 +#define BRCMF_RXREORDER_FLAGS_OFFSET 4 +#define BRCMF_RXREORDER_CURIDX_OFFSET 6 +#define BRCMF_RXREORDER_EXPIDX_OFFSET 8 + +#define BRCMF_RXREORDER_DEL_FLOW 0x01 +#define BRCMF_RXREORDER_FLUSH_ALL 0x02 +#define BRCMF_RXREORDER_CURIDX_VALID 0x04 +#define BRCMF_RXREORDER_EXPIDX_VALID 0x08 +#define BRCMF_RXREORDER_NEW_HOLE 0x10 + +/* Error bits */ +int brcmf_msg_level; +module_param_named(debug, brcmf_msg_level, int, S_IRUSR | S_IWUSR); +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 + +char *brcmf_ifname(struct brcmf_pub *drvr, int ifidx) +{ + if (ifidx < 0 || ifidx >= BRCMF_MAX_IFS) { + brcmf_err("ifidx %d out of range\n", ifidx); + return ""; + } + + if (drvr->iflist[ifidx] == NULL) { + brcmf_err("null i/f %d\n", ifidx); + return ""; + } + + if (drvr->iflist[ifidx]->ndev) + return drvr->iflist[ifidx]->ndev->name; + + return ""; +} + +static void _brcmf_set_multicast_list(struct work_struct *work) +{ + struct brcmf_if *ifp; + struct net_device *ndev; + struct netdev_hw_addr *ha; + u32 cmd_value, cnt; + __le32 cnt_le; + char *buf, *bufp; + u32 buflen; + s32 err; + + ifp = container_of(work, struct brcmf_if, multicast_work); + + brcmf_dbg(TRACE, "Enter, idx=%d\n", ifp->bssidx); + + ndev = ifp->ndev; + + /* Determine initial value of allmulti flag */ + cmd_value = (ndev->flags & IFF_ALLMULTI) ? true : false; + + /* Send down the multicast list first. */ + cnt = netdev_mc_count(ndev); + buflen = sizeof(cnt) + (cnt * ETH_ALEN); + buf = kmalloc(buflen, GFP_ATOMIC); + if (!buf) + return; + bufp = buf; + + cnt_le = cpu_to_le32(cnt); + memcpy(bufp, &cnt_le, sizeof(cnt_le)); + bufp += sizeof(cnt_le); + + netdev_for_each_mc_addr(ha, ndev) { + if (!cnt) + break; + memcpy(bufp, ha->addr, ETH_ALEN); + bufp += ETH_ALEN; + cnt--; + } + + err = brcmf_fil_iovar_data_set(ifp, "mcast_list", buf, buflen); + if (err < 0) { + brcmf_err("Setting mcast_list failed, %d\n", err); + cmd_value = cnt ? true : cmd_value; + } + + kfree(buf); + + /* + * Now send the allmulti setting. This is based on the setting in the + * net_device flags, but might be modified above to be turned on if we + * were trying to set some addresses and dongle rejected it... + */ + err = brcmf_fil_iovar_int_set(ifp, "allmulti", cmd_value); + if (err < 0) + brcmf_err("Setting allmulti failed, %d\n", err); + + /*Finally, pick up the PROMISC flag */ + cmd_value = (ndev->flags & IFF_PROMISC) ? true : false; + err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PROMISC, cmd_value); + if (err < 0) + brcmf_err("Setting BRCMF_C_SET_PROMISC failed, %d\n", + err); +} + +static void +_brcmf_set_mac_address(struct work_struct *work) +{ + struct brcmf_if *ifp; + s32 err; + + ifp = container_of(work, struct brcmf_if, setmacaddr_work); + + brcmf_dbg(TRACE, "Enter, idx=%d\n", ifp->bssidx); + + err = brcmf_fil_iovar_data_set(ifp, "cur_etheraddr", ifp->mac_addr, + ETH_ALEN); + if (err < 0) { + brcmf_err("Setting cur_etheraddr failed, %d\n", err); + } else { + brcmf_dbg(TRACE, "MAC address updated to %pM\n", + ifp->mac_addr); + memcpy(ifp->ndev->dev_addr, ifp->mac_addr, ETH_ALEN); + } +} + +static int brcmf_netdev_set_mac_address(struct net_device *ndev, void *addr) +{ + struct brcmf_if *ifp = netdev_priv(ndev); + struct sockaddr *sa = (struct sockaddr *)addr; + + memcpy(&ifp->mac_addr, sa->sa_data, ETH_ALEN); + schedule_work(&ifp->setmacaddr_work); + return 0; +} + +static void brcmf_netdev_set_multicast_list(struct net_device *ndev) +{ + struct brcmf_if *ifp = netdev_priv(ndev); + + schedule_work(&ifp->multicast_work); +} + +static netdev_tx_t brcmf_netdev_start_xmit(struct sk_buff *skb, + struct net_device *ndev) +{ + int ret; + struct brcmf_if *ifp = netdev_priv(ndev); + struct brcmf_pub *drvr = ifp->drvr; + struct ethhdr *eh = (struct ethhdr *)(skb->data); + + brcmf_dbg(DATA, "Enter, idx=%d\n", ifp->bssidx); + + /* Can the device send data? */ + if (drvr->bus_if->state != BRCMF_BUS_DATA) { + brcmf_err("xmit rejected state=%d\n", drvr->bus_if->state); + netif_stop_queue(ndev); + dev_kfree_skb(skb); + ret = -ENODEV; + goto done; + } + + if (!drvr->iflist[ifp->bssidx]) { + brcmf_err("bad ifidx %d\n", ifp->bssidx); + netif_stop_queue(ndev); + dev_kfree_skb(skb); + ret = -ENODEV; + goto done; + } + + /* Make sure there's enough room for any header */ + if (skb_headroom(skb) < drvr->hdrlen) { + struct sk_buff *skb2; + + brcmf_dbg(INFO, "%s: insufficient headroom\n", + brcmf_ifname(drvr, ifp->bssidx)); + drvr->bus_if->tx_realloc++; + skb2 = skb_realloc_headroom(skb, drvr->hdrlen); + dev_kfree_skb(skb); + skb = skb2; + if (skb == NULL) { + brcmf_err("%s: skb_realloc_headroom failed\n", + brcmf_ifname(drvr, ifp->bssidx)); + ret = -ENOMEM; + goto done; + } + } + + /* validate length for ether packet */ + if (skb->len < sizeof(*eh)) { + ret = -EINVAL; + dev_kfree_skb(skb); + goto done; + } + + if (eh->h_proto == htons(ETH_P_PAE)) + atomic_inc(&ifp->pend_8021x_cnt); + + ret = brcmf_fws_process_skb(ifp, skb); + +done: + if (ret) { + ifp->stats.tx_dropped++; + } else { + ifp->stats.tx_packets++; + ifp->stats.tx_bytes += skb->len; + } + + /* Return ok: we always eat the packet */ + return NETDEV_TX_OK; +} + +void brcmf_txflowblock_if(struct brcmf_if *ifp, + enum brcmf_netif_stop_reason reason, bool state) +{ + unsigned long flags; + + if (!ifp || !ifp->ndev) + return; + + brcmf_dbg(TRACE, "enter: idx=%d stop=0x%X reason=%d state=%d\n", + ifp->bssidx, ifp->netif_stop, reason, state); + + spin_lock_irqsave(&ifp->netif_stop_lock, flags); + if (state) { + if (!ifp->netif_stop) + netif_stop_queue(ifp->ndev); + ifp->netif_stop |= reason; + } else { + ifp->netif_stop &= ~reason; + if (!ifp->netif_stop) + netif_wake_queue(ifp->ndev); + } + spin_unlock_irqrestore(&ifp->netif_stop_lock, flags); +} + +void brcmf_txflowblock(struct device *dev, bool state) +{ + struct brcmf_bus *bus_if = dev_get_drvdata(dev); + struct brcmf_pub *drvr = bus_if->drvr; + + brcmf_dbg(TRACE, "Enter\n"); + + brcmf_fws_bus_blocked(drvr, state); +} + +void brcmf_netif_rx(struct brcmf_if *ifp, struct sk_buff *skb) +{ + skb->dev = ifp->ndev; + skb->protocol = eth_type_trans(skb, skb->dev); + + if (skb->pkt_type == PACKET_MULTICAST) + ifp->stats.multicast++; + + /* Process special event packets */ + brcmf_fweh_process_skb(ifp->drvr, skb); + + if (!(ifp->ndev->flags & IFF_UP)) { + brcmu_pkt_buf_free_skb(skb); + return; + } + + ifp->stats.rx_bytes += skb->len; + ifp->stats.rx_packets++; + + brcmf_dbg(DATA, "rx proto=0x%X\n", ntohs(skb->protocol)); + if (in_interrupt()) + netif_rx(skb); + else + /* If the receive is not processed inside an ISR, + * the softirqd must be woken explicitly to service + * the NET_RX_SOFTIRQ. This is handled by netif_rx_ni(). + */ + netif_rx_ni(skb); +} + +static void brcmf_rxreorder_get_skb_list(struct brcmf_ampdu_rx_reorder *rfi, + u8 start, u8 end, + struct sk_buff_head *skb_list) +{ + /* initialize return list */ + __skb_queue_head_init(skb_list); + + if (rfi->pend_pkts == 0) { + brcmf_dbg(INFO, "no packets in reorder queue\n"); + return; + } + + do { + if (rfi->pktslots[start]) { + __skb_queue_tail(skb_list, rfi->pktslots[start]); + rfi->pktslots[start] = NULL; + } + start++; + if (start > rfi->max_idx) + start = 0; + } while (start != end); + rfi->pend_pkts -= skb_queue_len(skb_list); +} + +static void brcmf_rxreorder_process_info(struct brcmf_if *ifp, u8 *reorder_data, + struct sk_buff *pkt) +{ + u8 flow_id, max_idx, cur_idx, exp_idx, end_idx; + struct brcmf_ampdu_rx_reorder *rfi; + struct sk_buff_head reorder_list; + struct sk_buff *pnext; + u8 flags; + u32 buf_size; + + flow_id = reorder_data[BRCMF_RXREORDER_FLOWID_OFFSET]; + flags = reorder_data[BRCMF_RXREORDER_FLAGS_OFFSET]; + + /* validate flags and flow id */ + if (flags == 0xFF) { + brcmf_err("invalid flags...so ignore this packet\n"); + brcmf_netif_rx(ifp, pkt); + return; + } + + rfi = ifp->drvr->reorder_flows[flow_id]; + if (flags & BRCMF_RXREORDER_DEL_FLOW) { + brcmf_dbg(INFO, "flow-%d: delete\n", + flow_id); + + if (rfi == NULL) { + brcmf_dbg(INFO, "received flags to cleanup, but no flow (%d) yet\n", + flow_id); + brcmf_netif_rx(ifp, pkt); + return; + } + + brcmf_rxreorder_get_skb_list(rfi, rfi->exp_idx, rfi->exp_idx, + &reorder_list); + /* add the last packet */ + __skb_queue_tail(&reorder_list, pkt); + kfree(rfi); + ifp->drvr->reorder_flows[flow_id] = NULL; + goto netif_rx; + } + /* from here on we need a flow reorder instance */ + if (rfi == NULL) { + buf_size = sizeof(*rfi); + max_idx = reorder_data[BRCMF_RXREORDER_MAXIDX_OFFSET]; + + buf_size += (max_idx + 1) * sizeof(pkt); + + /* allocate space for flow reorder info */ + brcmf_dbg(INFO, "flow-%d: start, maxidx %d\n", + flow_id, max_idx); + rfi = kzalloc(buf_size, GFP_ATOMIC); + if (rfi == NULL) { + brcmf_err("failed to alloc buffer\n"); + brcmf_netif_rx(ifp, pkt); + return; + } + + ifp->drvr->reorder_flows[flow_id] = rfi; + rfi->pktslots = (struct sk_buff **)(rfi+1); + rfi->max_idx = max_idx; + } + if (flags & BRCMF_RXREORDER_NEW_HOLE) { + if (rfi->pend_pkts) { + brcmf_rxreorder_get_skb_list(rfi, rfi->exp_idx, + rfi->exp_idx, + &reorder_list); + WARN_ON(rfi->pend_pkts); + } else { + __skb_queue_head_init(&reorder_list); + } + rfi->cur_idx = reorder_data[BRCMF_RXREORDER_CURIDX_OFFSET]; + rfi->exp_idx = reorder_data[BRCMF_RXREORDER_EXPIDX_OFFSET]; + rfi->max_idx = reorder_data[BRCMF_RXREORDER_MAXIDX_OFFSET]; + rfi->pktslots[rfi->cur_idx] = pkt; + rfi->pend_pkts++; + brcmf_dbg(DATA, "flow-%d: new hole %d (%d), pending %d\n", + flow_id, rfi->cur_idx, rfi->exp_idx, rfi->pend_pkts); + } else if (flags & BRCMF_RXREORDER_CURIDX_VALID) { + cur_idx = reorder_data[BRCMF_RXREORDER_CURIDX_OFFSET]; + exp_idx = reorder_data[BRCMF_RXREORDER_EXPIDX_OFFSET]; + + if ((exp_idx == rfi->exp_idx) && (cur_idx != rfi->exp_idx)) { + /* still in the current hole */ + /* enqueue the current on the buffer chain */ + if (rfi->pktslots[cur_idx] != NULL) { + brcmf_dbg(INFO, "HOLE: ERROR buffer pending..free it\n"); + brcmu_pkt_buf_free_skb(rfi->pktslots[cur_idx]); + rfi->pktslots[cur_idx] = NULL; + } + rfi->pktslots[cur_idx] = pkt; + rfi->pend_pkts++; + rfi->cur_idx = cur_idx; + brcmf_dbg(DATA, "flow-%d: store pkt %d (%d), pending %d\n", + flow_id, cur_idx, exp_idx, rfi->pend_pkts); + + /* can return now as there is no reorder + * list to process. + */ + return; + } + if (rfi->exp_idx == cur_idx) { + if (rfi->pktslots[cur_idx] != NULL) { + brcmf_dbg(INFO, "error buffer pending..free it\n"); + brcmu_pkt_buf_free_skb(rfi->pktslots[cur_idx]); + rfi->pktslots[cur_idx] = NULL; + } + rfi->pktslots[cur_idx] = pkt; + rfi->pend_pkts++; + + /* got the expected one. flush from current to expected + * and update expected + */ + brcmf_dbg(DATA, "flow-%d: expected %d (%d), pending %d\n", + flow_id, cur_idx, exp_idx, rfi->pend_pkts); + + rfi->cur_idx = cur_idx; + rfi->exp_idx = exp_idx; + + brcmf_rxreorder_get_skb_list(rfi, cur_idx, exp_idx, + &reorder_list); + brcmf_dbg(DATA, "flow-%d: freeing buffers %d, pending %d\n", + flow_id, skb_queue_len(&reorder_list), + rfi->pend_pkts); + } else { + u8 end_idx; + + brcmf_dbg(DATA, "flow-%d (0x%x): both moved, old %d/%d, new %d/%d\n", + flow_id, flags, rfi->cur_idx, rfi->exp_idx, + cur_idx, exp_idx); + if (flags & BRCMF_RXREORDER_FLUSH_ALL) + end_idx = rfi->exp_idx; + else + end_idx = exp_idx; + + /* flush pkts first */ + brcmf_rxreorder_get_skb_list(rfi, rfi->exp_idx, end_idx, + &reorder_list); + + if (exp_idx == ((cur_idx + 1) % (rfi->max_idx + 1))) { + __skb_queue_tail(&reorder_list, pkt); + } else { + rfi->pktslots[cur_idx] = pkt; + rfi->pend_pkts++; + } + rfi->exp_idx = exp_idx; + rfi->cur_idx = cur_idx; + } + } else { + /* explicity window move updating the expected index */ + exp_idx = reorder_data[BRCMF_RXREORDER_EXPIDX_OFFSET]; + + brcmf_dbg(DATA, "flow-%d (0x%x): change expected: %d -> %d\n", + flow_id, flags, rfi->exp_idx, exp_idx); + if (flags & BRCMF_RXREORDER_FLUSH_ALL) + end_idx = rfi->exp_idx; + else + end_idx = exp_idx; + + brcmf_rxreorder_get_skb_list(rfi, rfi->exp_idx, end_idx, + &reorder_list); + __skb_queue_tail(&reorder_list, pkt); + /* set the new expected idx */ + rfi->exp_idx = exp_idx; + } +netif_rx: + skb_queue_walk_safe(&reorder_list, pkt, pnext) { + __skb_unlink(pkt, &reorder_list); + brcmf_netif_rx(ifp, pkt); + } +} + +void brcmf_rx_frame(struct device *dev, struct sk_buff *skb) +{ + struct brcmf_if *ifp; + 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]; + + if (ret || !ifp || !ifp->ndev) { + if ((ret != -ENODATA) && ifp) + ifp->stats.rx_errors++; + brcmu_pkt_buf_free_skb(skb); + return; + } + + rd = (struct brcmf_skb_reorder_data *)skb->cb; + if (rd->reorder) + brcmf_rxreorder_process_info(ifp, rd->reorder, skb); + else + brcmf_netif_rx(ifp, skb); +} + +void brcmf_txfinalize(struct brcmf_pub *drvr, struct sk_buff *txp, u8 ifidx, + 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); + + if (type == ETH_P_PAE) { + atomic_dec(&ifp->pend_8021x_cnt); + if (waitqueue_active(&ifp->pend_8021x_wait)) + wake_up(&ifp->pend_8021x_wait); + } + + if (!success) + ifp->stats.tx_errors++; +done: + brcmu_pkt_buf_free_skb(txp); +} + +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; + + /* 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)) + brcmu_pkt_buf_free_skb(txp); + else + brcmf_txfinalize(drvr, txp, ifidx, success); + } +} + +static struct net_device_stats *brcmf_netdev_get_stats(struct net_device *ndev) +{ + struct brcmf_if *ifp = netdev_priv(ndev); + + brcmf_dbg(TRACE, "Enter, idx=%d\n", ifp->bssidx); + + return &ifp->stats; +} + +static void brcmf_ethtool_get_drvinfo(struct net_device *ndev, + struct ethtool_drvinfo *info) +{ + struct brcmf_if *ifp = netdev_priv(ndev); + struct brcmf_pub *drvr = ifp->drvr; + + strlcpy(info->driver, KBUILD_MODNAME, sizeof(info->driver)); + snprintf(info->version, sizeof(info->version), "n/a"); + strlcpy(info->fw_version, drvr->fwver, sizeof(info->fw_version)); + strlcpy(info->bus_info, dev_name(drvr->bus_if->dev), + sizeof(info->bus_info)); +} + +static const struct ethtool_ops brcmf_ethtool_ops = { + .get_drvinfo = brcmf_ethtool_get_drvinfo, +}; + +static int brcmf_netdev_stop(struct net_device *ndev) +{ + struct brcmf_if *ifp = netdev_priv(ndev); + + brcmf_dbg(TRACE, "Enter, idx=%d\n", ifp->bssidx); + + brcmf_cfg80211_down(ndev); + + /* Set state and stop OS transmissions */ + netif_stop_queue(ndev); + + return 0; +} + +static int brcmf_netdev_open(struct net_device *ndev) +{ + struct brcmf_if *ifp = netdev_priv(ndev); + struct brcmf_pub *drvr = ifp->drvr; + struct brcmf_bus *bus_if = drvr->bus_if; + u32 toe_ol; + + brcmf_dbg(TRACE, "Enter, idx=%d\n", ifp->bssidx); + + /* If bus is not ready, can't continue */ + if (bus_if->state != BRCMF_BUS_DATA) { + brcmf_err("failed bus is not ready\n"); + return -EAGAIN; + } + + atomic_set(&ifp->pend_8021x_cnt, 0); + + /* Get current TOE mode from dongle */ + if (brcmf_fil_iovar_int_get(ifp, "toe_ol", &toe_ol) >= 0 + && (toe_ol & TOE_TX_CSUM_OL) != 0) + ndev->features |= NETIF_F_IP_CSUM; + else + ndev->features &= ~NETIF_F_IP_CSUM; + + if (brcmf_cfg80211_up(ndev)) { + brcmf_err("failed to bring up cfg80211\n"); + return -EIO; + } + + /* Allow transmit calls */ + netif_start_queue(ndev); + return 0; +} + +static const struct net_device_ops brcmf_netdev_ops_pri = { + .ndo_open = brcmf_netdev_open, + .ndo_stop = brcmf_netdev_stop, + .ndo_get_stats = brcmf_netdev_get_stats, + .ndo_start_xmit = brcmf_netdev_start_xmit, + .ndo_set_mac_address = brcmf_netdev_set_mac_address, + .ndo_set_rx_mode = brcmf_netdev_set_multicast_list +}; + +int brcmf_net_attach(struct brcmf_if *ifp, bool rtnl_locked) +{ + struct brcmf_pub *drvr = ifp->drvr; + struct net_device *ndev; + s32 err; + + brcmf_dbg(TRACE, "Enter, idx=%d mac=%pM\n", ifp->bssidx, + ifp->mac_addr); + ndev = ifp->ndev; + + /* set appropriate operations */ + ndev->netdev_ops = &brcmf_netdev_ops_pri; + + ndev->hard_header_len += drvr->hdrlen; + ndev->ethtool_ops = &brcmf_ethtool_ops; + + drvr->rxsz = ndev->mtu + ndev->hard_header_len + + drvr->hdrlen; + + /* set the mac address */ + memcpy(ndev->dev_addr, ifp->mac_addr, ETH_ALEN); + + INIT_WORK(&ifp->setmacaddr_work, _brcmf_set_mac_address); + INIT_WORK(&ifp->multicast_work, _brcmf_set_multicast_list); + + if (rtnl_locked) + err = register_netdevice(ndev); + else + err = register_netdev(ndev); + if (err != 0) { + brcmf_err("couldn't register the net device\n"); + goto fail; + } + + brcmf_dbg(INFO, "%s: Broadcom Dongle Host Driver\n", ndev->name); + + ndev->destructor = brcmf_cfg80211_free_netdev; + return 0; + +fail: + drvr->iflist[ifp->bssidx] = NULL; + ndev->netdev_ops = NULL; + free_netdev(ndev); + return -EBADE; +} + +static int brcmf_net_p2p_open(struct net_device *ndev) +{ + brcmf_dbg(TRACE, "Enter\n"); + + return brcmf_cfg80211_up(ndev); +} + +static int brcmf_net_p2p_stop(struct net_device *ndev) +{ + brcmf_dbg(TRACE, "Enter\n"); + + return brcmf_cfg80211_down(ndev); +} + +static netdev_tx_t brcmf_net_p2p_start_xmit(struct sk_buff *skb, + struct net_device *ndev) +{ + if (skb) + dev_kfree_skb_any(skb); + + return NETDEV_TX_OK; +} + +static const struct net_device_ops brcmf_netdev_ops_p2p = { + .ndo_open = brcmf_net_p2p_open, + .ndo_stop = brcmf_net_p2p_stop, + .ndo_start_xmit = brcmf_net_p2p_start_xmit +}; + +static int brcmf_net_p2p_attach(struct brcmf_if *ifp) +{ + struct net_device *ndev; + + brcmf_dbg(TRACE, "Enter, idx=%d mac=%pM\n", ifp->bssidx, + ifp->mac_addr); + ndev = ifp->ndev; + + ndev->netdev_ops = &brcmf_netdev_ops_p2p; + + /* set the mac address */ + memcpy(ndev->dev_addr, ifp->mac_addr, ETH_ALEN); + + if (register_netdev(ndev) != 0) { + brcmf_err("couldn't register the p2p net device\n"); + goto fail; + } + + brcmf_dbg(INFO, "%s: Broadcom Dongle Host Driver\n", ndev->name); + + return 0; + +fail: + ifp->drvr->iflist[ifp->bssidx] = NULL; + ndev->netdev_ops = NULL; + free_netdev(ndev); + return -EBADE; +} + +struct brcmf_if *brcmf_add_if(struct brcmf_pub *drvr, s32 bssidx, s32 ifidx, + char *name, u8 *mac_addr) +{ + struct brcmf_if *ifp; + struct net_device *ndev; + + brcmf_dbg(TRACE, "Enter, idx=%d, ifidx=%d\n", bssidx, ifidx); + + ifp = drvr->iflist[bssidx]; + /* + * Delete the existing interface before overwriting it + * in case we missed the BRCMF_E_IF_DEL event. + */ + if (ifp) { + brcmf_err("ERROR: netdev:%s already exists\n", + ifp->ndev->name); + if (ifidx) { + netif_stop_queue(ifp->ndev); + unregister_netdev(ifp->ndev); + free_netdev(ifp->ndev); + drvr->iflist[bssidx] = NULL; + } else { + brcmf_err("ignore IF event\n"); + return ERR_PTR(-EINVAL); + } + } + + if (!brcmf_p2p_enable && bssidx == 1) { + /* this is P2P_DEVICE interface */ + brcmf_dbg(INFO, "allocate non-netdev interface\n"); + ifp = kzalloc(sizeof(*ifp), GFP_KERNEL); + if (!ifp) + return ERR_PTR(-ENOMEM); + } 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); + if (!ndev) + return ERR_PTR(-ENOMEM); + + ifp = netdev_priv(ndev); + ifp->ndev = ndev; + } + + ifp->drvr = drvr; + drvr->iflist[bssidx] = ifp; + ifp->ifidx = ifidx; + ifp->bssidx = bssidx; + + init_waitqueue_head(&ifp->pend_8021x_wait); + spin_lock_init(&ifp->netif_stop_lock); + + if (mac_addr != NULL) + memcpy(ifp->mac_addr, mac_addr, ETH_ALEN); + + brcmf_dbg(TRACE, " ==== pid:%x, if:%s (%pM) created ===\n", + current->pid, name, ifp->mac_addr); + + return ifp; +} + +void brcmf_del_if(struct brcmf_pub *drvr, s32 bssidx) +{ + struct brcmf_if *ifp; + + ifp = drvr->iflist[bssidx]; + drvr->iflist[bssidx] = NULL; + if (!ifp) { + brcmf_err("Null interface, idx=%d\n", bssidx); + return; + } + brcmf_dbg(TRACE, "Enter, idx=%d, ifidx=%d\n", bssidx, ifp->ifidx); + if (ifp->ndev) { + if (bssidx == 0) { + if (ifp->ndev->netdev_ops == &brcmf_netdev_ops_pri) { + rtnl_lock(); + brcmf_netdev_stop(ifp->ndev); + rtnl_unlock(); + } + } else { + netif_stop_queue(ifp->ndev); + } + + if (ifp->ndev->netdev_ops == &brcmf_netdev_ops_pri) { + cancel_work_sync(&ifp->setmacaddr_work); + cancel_work_sync(&ifp->multicast_work); + } + /* unregister will take care of freeing it */ + unregister_netdev(ifp->ndev); + } else { + kfree(ifp); + } +} + +int brcmf_attach(struct device *dev) +{ + struct brcmf_pub *drvr = NULL; + int ret = 0; + + brcmf_dbg(TRACE, "Enter\n"); + + /* Allocate primary brcmf_info */ + drvr = kzalloc(sizeof(struct brcmf_pub), GFP_ATOMIC); + if (!drvr) + return -ENOMEM; + + mutex_init(&drvr->proto_block); + + /* Link to bus module */ + drvr->hdrlen = 0; + drvr->bus_if = dev_get_drvdata(dev); + drvr->bus_if->drvr = drvr; + + /* create device debugfs folder */ + brcmf_debugfs_attach(drvr); + + /* Attach and link in the protocol */ + ret = brcmf_proto_attach(drvr); + if (ret != 0) { + brcmf_err("brcmf_prot_attach failed\n"); + goto fail; + } + + /* attach firmware event handler */ + brcmf_fweh_attach(drvr); + + return ret; + +fail: + brcmf_detach(dev); + + return ret; +} + +int brcmf_bus_start(struct device *dev) +{ + int ret = -1; + struct brcmf_bus *bus_if = dev_get_drvdata(dev); + struct brcmf_pub *drvr = bus_if->drvr; + struct brcmf_if *ifp; + struct brcmf_if *p2p_ifp; + + brcmf_dbg(TRACE, "\n"); + + /* add primary networking interface */ + ifp = brcmf_add_if(drvr, 0, 0, "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; + + /* signal bus ready */ + brcmf_bus_change_state(bus_if, BRCMF_BUS_DATA); + + /* Bus is ready, do any initialization */ + ret = brcmf_c_preinit_dcmds(ifp); + if (ret < 0) + goto fail; + + brcmf_feat_attach(drvr); + + ret = brcmf_fws_init(drvr); + if (ret < 0) + goto fail; + + brcmf_fws_add_interface(ifp); + + drvr->config = brcmf_cfg80211_attach(drvr, bus_if->dev); + 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); +fail: + if (ret < 0) { + brcmf_err("failed: %d\n", ret); + brcmf_cfg80211_detach(drvr->config); + 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; + } + return ret; + } + if ((brcmf_p2p_enable) && (p2p_ifp)) + if (brcmf_net_p2p_attach(p2p_ifp) < 0) + brcmf_p2p_enable = 0; + + return 0; +} + +void brcmf_bus_add_txhdrlen(struct device *dev, uint len) +{ + struct brcmf_bus *bus_if = dev_get_drvdata(dev); + struct brcmf_pub *drvr = bus_if->drvr; + + if (drvr) { + drvr->hdrlen += len; + } +} + +static void brcmf_bus_detach(struct brcmf_pub *drvr) +{ + brcmf_dbg(TRACE, "Enter\n"); + + if (drvr) { + /* Stop the bus module */ + brcmf_bus_stop(drvr->bus_if); + } +} + +void brcmf_dev_reset(struct device *dev) +{ + struct brcmf_bus *bus_if = dev_get_drvdata(dev); + struct brcmf_pub *drvr = bus_if->drvr; + + if (drvr == NULL) + return; + + if (drvr->iflist[0]) + brcmf_fil_cmd_int_set(drvr->iflist[0], BRCMF_C_TERMINATED, 1); +} + +void brcmf_detach(struct device *dev) +{ + s32 i; + struct brcmf_bus *bus_if = dev_get_drvdata(dev); + struct brcmf_pub *drvr = bus_if->drvr; + + brcmf_dbg(TRACE, "Enter\n"); + + if (drvr == NULL) + return; + + /* stop firmware event handling */ + brcmf_fweh_detach(drvr); + + brcmf_bus_change_state(bus_if, BRCMF_BUS_DOWN); + + /* make sure primary interface removed last */ + for (i = BRCMF_MAX_IFS-1; i > -1; i--) + if (drvr->iflist[i]) { + brcmf_fws_del_interface(drvr->iflist[i]); + brcmf_del_if(drvr, i); + } + + brcmf_cfg80211_detach(drvr->config); + + brcmf_fws_deinit(drvr); + + brcmf_bus_detach(drvr); + + brcmf_proto_detach(drvr); + + brcmf_debugfs_detach(drvr); + bus_if->drvr = NULL; + kfree(drvr); +} + +s32 brcmf_iovar_data_set(struct device *dev, char *name, void *data, u32 len) +{ + struct brcmf_bus *bus_if = dev_get_drvdata(dev); + struct brcmf_if *ifp = bus_if->drvr->iflist[0]; + + return brcmf_fil_iovar_data_set(ifp, name, data, len); +} + +static int brcmf_get_pend_8021x_cnt(struct brcmf_if *ifp) +{ + return atomic_read(&ifp->pend_8021x_cnt); +} + +int brcmf_netdev_wait_pend8021x(struct net_device *ndev) +{ + struct brcmf_if *ifp = netdev_priv(ndev); + int err; + + err = wait_event_timeout(ifp->pend_8021x_wait, + !brcmf_get_pend_8021x_cnt(ifp), + msecs_to_jiffies(MAX_WAIT_FOR_8021X_TX)); + + WARN_ON(!err); + + return !err; +} + +static void brcmf_driver_register(struct work_struct *work) +{ +#ifdef CONFIG_BRCMFMAC_SDIO + brcmf_sdio_register(); +#endif +#ifdef CONFIG_BRCMFMAC_USB + brcmf_usb_register(); +#endif +#ifdef CONFIG_BRCMFMAC_PCIE + brcmf_pcie_register(); +#endif +} +static DECLARE_WORK(brcmf_driver_work, brcmf_driver_register); + +static int __init brcmfmac_module_init(void) +{ + brcmf_debugfs_init(); +#ifdef CONFIG_BRCMFMAC_SDIO + brcmf_sdio_init(); +#endif + if (!schedule_work(&brcmf_driver_work)) + return -EBUSY; + + return 0; +} + +static void __exit brcmfmac_module_exit(void) +{ + cancel_work_sync(&brcmf_driver_work); + +#ifdef CONFIG_BRCMFMAC_SDIO + brcmf_sdio_exit(); +#endif +#ifdef CONFIG_BRCMFMAC_USB + brcmf_usb_exit(); +#endif +#ifdef CONFIG_BRCMFMAC_PCIE + brcmf_pcie_exit(); +#endif + brcmf_debugfs_exit(); +} + +module_init(brcmfmac_module_init); +module_exit(brcmfmac_module_exit); diff --git a/drivers/net/wireless/brcm80211/brcmfmac/core.h b/drivers/net/wireless/brcm80211/brcmfmac/core.h new file mode 100644 index 000000000000..7df22bdb19a2 --- /dev/null +++ b/drivers/net/wireless/brcm80211/brcmfmac/core.h @@ -0,0 +1,189 @@ +/* + * Copyright (c) 2010 Broadcom Corporation + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/**************** + * Common types * + */ + +#ifndef BRCMFMAC_CORE_H +#define BRCMFMAC_CORE_H + +#include "fweh.h" + +#define TOE_TX_CSUM_OL 0x00000001 +#define TOE_RX_CSUM_OL 0x00000002 + +/* For supporting multiple interfaces */ +#define BRCMF_MAX_IFS 16 + +#define DOT11_MAX_DEFAULT_KEYS 4 + +/* Small, medium and maximum buffer size for dcmd + */ +#define BRCMF_DCMD_SMLEN 256 +#define BRCMF_DCMD_MEDLEN 1536 +#define BRCMF_DCMD_MAXLEN 8192 + +/* IOCTL from host to device are limited in lenght. A device can only handle + * ethernet frame size. This limitation is to be applied by protocol layer. + */ +#define BRCMF_TX_IOCTL_MAX_MSG_SIZE (ETH_FRAME_LEN+ETH_FCS_LEN) + +#define BRCMF_AMPDU_RX_REORDER_MAXFLOWS 256 + +/* Length of firmware version string stored for + * ethtool driver info which uses 32 bytes as well. + */ +#define BRCMF_DRIVER_FIRMWARE_VERSION_LEN 32 + +/** + * struct brcmf_ampdu_rx_reorder - AMPDU receive reorder info + * + * @pktslots: dynamic allocated array for ordering AMPDU packets. + * @flow_id: AMPDU flow identifier. + * @cur_idx: last AMPDU index from firmware. + * @exp_idx: expected next AMPDU index. + * @max_idx: maximum amount of packets per AMPDU. + * @pend_pkts: number of packets currently in @pktslots. + */ +struct brcmf_ampdu_rx_reorder { + struct sk_buff **pktslots; + u8 flow_id; + u8 cur_idx; + u8 exp_idx; + u8 max_idx; + u8 pend_pkts; +}; + +/* Forward decls for struct brcmf_pub (see below) */ +struct brcmf_proto; /* device communication protocol info */ +struct brcmf_cfg80211_dev; /* cfg80211 device info */ +struct brcmf_fws_info; /* firmware signalling info */ + +/* Common structure for module and instance linkage */ +struct brcmf_pub { + /* Linkage ponters */ + struct brcmf_bus *bus_if; + struct brcmf_proto *proto; + struct brcmf_cfg80211_info *config; + + /* Internal brcmf items */ + uint hdrlen; /* Total BRCMF header length (proto + bus) */ + uint rxsz; /* Rx buffer size bus module should use */ + u8 wme_dp; /* wme discard priority */ + + /* Dongle media info */ + char fwver[BRCMF_DRIVER_FIRMWARE_VERSION_LEN]; + u8 mac[ETH_ALEN]; /* MAC address obtained from dongle */ + + /* Multicast data packets sent to dongle */ + unsigned long tx_multicast; + + struct brcmf_if *iflist[BRCMF_MAX_IFS]; + + struct mutex proto_block; + unsigned char proto_buf[BRCMF_DCMD_MAXLEN]; + + struct brcmf_fweh_info fweh; + + struct brcmf_fws_info *fws; + + struct brcmf_ampdu_rx_reorder + *reorder_flows[BRCMF_AMPDU_RX_REORDER_MAXFLOWS]; + + u32 feat_flags; + u32 chip_quirks; + +#ifdef DEBUG + struct dentry *dbgfs_dir; +#endif +}; + +/* forward declarations */ +struct brcmf_cfg80211_vif; +struct brcmf_fws_mac_descriptor; + +/** + * enum brcmf_netif_stop_reason - reason for stopping netif queue. + * + * @BRCMF_NETIF_STOP_REASON_FWS_FC: + * netif stopped due to firmware signalling flow control. + * @BRCMF_NETIF_STOP_REASON_FLOW: + * netif stopped due to flowring full. + */ +enum brcmf_netif_stop_reason { + BRCMF_NETIF_STOP_REASON_FWS_FC = 1, + BRCMF_NETIF_STOP_REASON_FLOW = 2 +}; + +/** + * struct brcmf_if - interface control information. + * + * @drvr: points to device related information. + * @vif: points to cfg80211 specific interface information. + * @ndev: associated network device. + * @stats: interface specific network statistics. + * @setmacaddr_work: worker object for setting mac address. + * @multicast_work: worker object for multicast provisioning. + * @fws_desc: interface specific firmware-signalling descriptor. + * @ifidx: interface index in device firmware. + * @bssidx: index of bss associated with this interface. + * @mac_addr: assigned mac address. + * @netif_stop: bitmap indicates reason why netif queues are stopped. + * @netif_stop_lock: spinlock for update netif_stop from multiple sources. + * @pend_8021x_cnt: tracks outstanding number of 802.1x frames. + * @pend_8021x_wait: used for signalling change in count. + */ +struct brcmf_if { + struct brcmf_pub *drvr; + struct brcmf_cfg80211_vif *vif; + struct net_device *ndev; + struct net_device_stats stats; + struct work_struct setmacaddr_work; + struct work_struct multicast_work; + struct brcmf_fws_mac_descriptor *fws_desc; + int ifidx; + s32 bssidx; + u8 mac_addr[ETH_ALEN]; + u8 netif_stop; + spinlock_t netif_stop_lock; + atomic_t pend_8021x_cnt; + wait_queue_head_t pend_8021x_wait; +}; + +struct brcmf_skb_reorder_data { + u8 *reorder; +}; + +int brcmf_netdev_wait_pend8021x(struct net_device *ndev); + +/* Return pointer to interface name */ +char *brcmf_ifname(struct brcmf_pub *drvr, int idx); + +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_del_if(struct brcmf_pub *drvr, s32 bssidx); +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_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); + +#endif /* BRCMFMAC_CORE_H */ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/debug.c b/drivers/net/wireless/brcm80211/brcmfmac/debug.c index 340b10447ab9..9b473d50b005 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/debug.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/debug.c @@ -19,7 +19,7 @@ #include #include -#include "dhd.h" +#include "core.h" #include "bus.h" #include "debug.h" diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd.h b/drivers/net/wireless/brcm80211/brcmfmac/dhd.h deleted file mode 100644 index 5e4317dbc2b0..000000000000 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd.h +++ /dev/null @@ -1,189 +0,0 @@ -/* - * Copyright (c) 2010 Broadcom Corporation - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY - * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION - * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN - * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -/**************** - * Common types * - */ - -#ifndef _BRCMF_H_ -#define _BRCMF_H_ - -#include "fweh.h" - -#define TOE_TX_CSUM_OL 0x00000001 -#define TOE_RX_CSUM_OL 0x00000002 - -/* For supporting multiple interfaces */ -#define BRCMF_MAX_IFS 16 - -#define DOT11_MAX_DEFAULT_KEYS 4 - -/* Small, medium and maximum buffer size for dcmd - */ -#define BRCMF_DCMD_SMLEN 256 -#define BRCMF_DCMD_MEDLEN 1536 -#define BRCMF_DCMD_MAXLEN 8192 - -/* IOCTL from host to device are limited in lenght. A device can only handle - * ethernet frame size. This limitation is to be applied by protocol layer. - */ -#define BRCMF_TX_IOCTL_MAX_MSG_SIZE (ETH_FRAME_LEN+ETH_FCS_LEN) - -#define BRCMF_AMPDU_RX_REORDER_MAXFLOWS 256 - -/* Length of firmware version string stored for - * ethtool driver info which uses 32 bytes as well. - */ -#define BRCMF_DRIVER_FIRMWARE_VERSION_LEN 32 - -/** - * struct brcmf_ampdu_rx_reorder - AMPDU receive reorder info - * - * @pktslots: dynamic allocated array for ordering AMPDU packets. - * @flow_id: AMPDU flow identifier. - * @cur_idx: last AMPDU index from firmware. - * @exp_idx: expected next AMPDU index. - * @max_idx: maximum amount of packets per AMPDU. - * @pend_pkts: number of packets currently in @pktslots. - */ -struct brcmf_ampdu_rx_reorder { - struct sk_buff **pktslots; - u8 flow_id; - u8 cur_idx; - u8 exp_idx; - u8 max_idx; - u8 pend_pkts; -}; - -/* Forward decls for struct brcmf_pub (see below) */ -struct brcmf_proto; /* device communication protocol info */ -struct brcmf_cfg80211_dev; /* cfg80211 device info */ -struct brcmf_fws_info; /* firmware signalling info */ - -/* Common structure for module and instance linkage */ -struct brcmf_pub { - /* Linkage ponters */ - struct brcmf_bus *bus_if; - struct brcmf_proto *proto; - struct brcmf_cfg80211_info *config; - - /* Internal brcmf items */ - uint hdrlen; /* Total BRCMF header length (proto + bus) */ - uint rxsz; /* Rx buffer size bus module should use */ - u8 wme_dp; /* wme discard priority */ - - /* Dongle media info */ - char fwver[BRCMF_DRIVER_FIRMWARE_VERSION_LEN]; - u8 mac[ETH_ALEN]; /* MAC address obtained from dongle */ - - /* Multicast data packets sent to dongle */ - unsigned long tx_multicast; - - struct brcmf_if *iflist[BRCMF_MAX_IFS]; - - struct mutex proto_block; - unsigned char proto_buf[BRCMF_DCMD_MAXLEN]; - - struct brcmf_fweh_info fweh; - - struct brcmf_fws_info *fws; - - struct brcmf_ampdu_rx_reorder - *reorder_flows[BRCMF_AMPDU_RX_REORDER_MAXFLOWS]; - - u32 feat_flags; - u32 chip_quirks; - -#ifdef DEBUG - struct dentry *dbgfs_dir; -#endif -}; - -/* forward declarations */ -struct brcmf_cfg80211_vif; -struct brcmf_fws_mac_descriptor; - -/** - * enum brcmf_netif_stop_reason - reason for stopping netif queue. - * - * @BRCMF_NETIF_STOP_REASON_FWS_FC: - * netif stopped due to firmware signalling flow control. - * @BRCMF_NETIF_STOP_REASON_FLOW: - * netif stopped due to flowring full. - */ -enum brcmf_netif_stop_reason { - BRCMF_NETIF_STOP_REASON_FWS_FC = 1, - BRCMF_NETIF_STOP_REASON_FLOW = 2 -}; - -/** - * struct brcmf_if - interface control information. - * - * @drvr: points to device related information. - * @vif: points to cfg80211 specific interface information. - * @ndev: associated network device. - * @stats: interface specific network statistics. - * @setmacaddr_work: worker object for setting mac address. - * @multicast_work: worker object for multicast provisioning. - * @fws_desc: interface specific firmware-signalling descriptor. - * @ifidx: interface index in device firmware. - * @bssidx: index of bss associated with this interface. - * @mac_addr: assigned mac address. - * @netif_stop: bitmap indicates reason why netif queues are stopped. - * @netif_stop_lock: spinlock for update netif_stop from multiple sources. - * @pend_8021x_cnt: tracks outstanding number of 802.1x frames. - * @pend_8021x_wait: used for signalling change in count. - */ -struct brcmf_if { - struct brcmf_pub *drvr; - struct brcmf_cfg80211_vif *vif; - struct net_device *ndev; - struct net_device_stats stats; - struct work_struct setmacaddr_work; - struct work_struct multicast_work; - struct brcmf_fws_mac_descriptor *fws_desc; - int ifidx; - s32 bssidx; - u8 mac_addr[ETH_ALEN]; - u8 netif_stop; - spinlock_t netif_stop_lock; - atomic_t pend_8021x_cnt; - wait_queue_head_t pend_8021x_wait; -}; - -struct brcmf_skb_reorder_data { - u8 *reorder; -}; - -int brcmf_netdev_wait_pend8021x(struct net_device *ndev); - -/* Return pointer to interface name */ -char *brcmf_ifname(struct brcmf_pub *drvr, int idx); - -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_del_if(struct brcmf_pub *drvr, s32 bssidx); -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_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); - -#endif /* _BRCMF_H_ */ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c deleted file mode 100644 index 53650b9cb768..000000000000 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c +++ /dev/null @@ -1,1124 +0,0 @@ -/* - * Copyright (c) 2010 Broadcom Corporation - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY - * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION - * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN - * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include -#include -#include -#include -#include -#include -#include - -#include "dhd.h" -#include "bus.h" -#include "debug.h" -#include "fwil_types.h" -#include "p2p.h" -#include "cfg80211.h" -#include "fwil.h" -#include "fwsignal.h" -#include "feature.h" -#include "proto.h" -#include "pcie.h" - -MODULE_AUTHOR("Broadcom Corporation"); -MODULE_DESCRIPTION("Broadcom 802.11 wireless LAN fullmac driver."); -MODULE_LICENSE("Dual BSD/GPL"); - -#define MAX_WAIT_FOR_8021X_TX 50 /* msecs */ - -/* AMPDU rx reordering definitions */ -#define BRCMF_RXREORDER_FLOWID_OFFSET 0 -#define BRCMF_RXREORDER_MAXIDX_OFFSET 2 -#define BRCMF_RXREORDER_FLAGS_OFFSET 4 -#define BRCMF_RXREORDER_CURIDX_OFFSET 6 -#define BRCMF_RXREORDER_EXPIDX_OFFSET 8 - -#define BRCMF_RXREORDER_DEL_FLOW 0x01 -#define BRCMF_RXREORDER_FLUSH_ALL 0x02 -#define BRCMF_RXREORDER_CURIDX_VALID 0x04 -#define BRCMF_RXREORDER_EXPIDX_VALID 0x08 -#define BRCMF_RXREORDER_NEW_HOLE 0x10 - -/* Error bits */ -int brcmf_msg_level; -module_param_named(debug, brcmf_msg_level, int, S_IRUSR | S_IWUSR); -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 - -char *brcmf_ifname(struct brcmf_pub *drvr, int ifidx) -{ - if (ifidx < 0 || ifidx >= BRCMF_MAX_IFS) { - brcmf_err("ifidx %d out of range\n", ifidx); - return ""; - } - - if (drvr->iflist[ifidx] == NULL) { - brcmf_err("null i/f %d\n", ifidx); - return ""; - } - - if (drvr->iflist[ifidx]->ndev) - return drvr->iflist[ifidx]->ndev->name; - - return ""; -} - -static void _brcmf_set_multicast_list(struct work_struct *work) -{ - struct brcmf_if *ifp; - struct net_device *ndev; - struct netdev_hw_addr *ha; - u32 cmd_value, cnt; - __le32 cnt_le; - char *buf, *bufp; - u32 buflen; - s32 err; - - ifp = container_of(work, struct brcmf_if, multicast_work); - - brcmf_dbg(TRACE, "Enter, idx=%d\n", ifp->bssidx); - - ndev = ifp->ndev; - - /* Determine initial value of allmulti flag */ - cmd_value = (ndev->flags & IFF_ALLMULTI) ? true : false; - - /* Send down the multicast list first. */ - cnt = netdev_mc_count(ndev); - buflen = sizeof(cnt) + (cnt * ETH_ALEN); - buf = kmalloc(buflen, GFP_ATOMIC); - if (!buf) - return; - bufp = buf; - - cnt_le = cpu_to_le32(cnt); - memcpy(bufp, &cnt_le, sizeof(cnt_le)); - bufp += sizeof(cnt_le); - - netdev_for_each_mc_addr(ha, ndev) { - if (!cnt) - break; - memcpy(bufp, ha->addr, ETH_ALEN); - bufp += ETH_ALEN; - cnt--; - } - - err = brcmf_fil_iovar_data_set(ifp, "mcast_list", buf, buflen); - if (err < 0) { - brcmf_err("Setting mcast_list failed, %d\n", err); - cmd_value = cnt ? true : cmd_value; - } - - kfree(buf); - - /* - * Now send the allmulti setting. This is based on the setting in the - * net_device flags, but might be modified above to be turned on if we - * were trying to set some addresses and dongle rejected it... - */ - err = brcmf_fil_iovar_int_set(ifp, "allmulti", cmd_value); - if (err < 0) - brcmf_err("Setting allmulti failed, %d\n", err); - - /*Finally, pick up the PROMISC flag */ - cmd_value = (ndev->flags & IFF_PROMISC) ? true : false; - err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PROMISC, cmd_value); - if (err < 0) - brcmf_err("Setting BRCMF_C_SET_PROMISC failed, %d\n", - err); -} - -static void -_brcmf_set_mac_address(struct work_struct *work) -{ - struct brcmf_if *ifp; - s32 err; - - ifp = container_of(work, struct brcmf_if, setmacaddr_work); - - brcmf_dbg(TRACE, "Enter, idx=%d\n", ifp->bssidx); - - err = brcmf_fil_iovar_data_set(ifp, "cur_etheraddr", ifp->mac_addr, - ETH_ALEN); - if (err < 0) { - brcmf_err("Setting cur_etheraddr failed, %d\n", err); - } else { - brcmf_dbg(TRACE, "MAC address updated to %pM\n", - ifp->mac_addr); - memcpy(ifp->ndev->dev_addr, ifp->mac_addr, ETH_ALEN); - } -} - -static int brcmf_netdev_set_mac_address(struct net_device *ndev, void *addr) -{ - struct brcmf_if *ifp = netdev_priv(ndev); - struct sockaddr *sa = (struct sockaddr *)addr; - - memcpy(&ifp->mac_addr, sa->sa_data, ETH_ALEN); - schedule_work(&ifp->setmacaddr_work); - return 0; -} - -static void brcmf_netdev_set_multicast_list(struct net_device *ndev) -{ - struct brcmf_if *ifp = netdev_priv(ndev); - - schedule_work(&ifp->multicast_work); -} - -static netdev_tx_t brcmf_netdev_start_xmit(struct sk_buff *skb, - struct net_device *ndev) -{ - int ret; - struct brcmf_if *ifp = netdev_priv(ndev); - struct brcmf_pub *drvr = ifp->drvr; - struct ethhdr *eh = (struct ethhdr *)(skb->data); - - brcmf_dbg(DATA, "Enter, idx=%d\n", ifp->bssidx); - - /* Can the device send data? */ - if (drvr->bus_if->state != BRCMF_BUS_DATA) { - brcmf_err("xmit rejected state=%d\n", drvr->bus_if->state); - netif_stop_queue(ndev); - dev_kfree_skb(skb); - ret = -ENODEV; - goto done; - } - - if (!drvr->iflist[ifp->bssidx]) { - brcmf_err("bad ifidx %d\n", ifp->bssidx); - netif_stop_queue(ndev); - dev_kfree_skb(skb); - ret = -ENODEV; - goto done; - } - - /* Make sure there's enough room for any header */ - if (skb_headroom(skb) < drvr->hdrlen) { - struct sk_buff *skb2; - - brcmf_dbg(INFO, "%s: insufficient headroom\n", - brcmf_ifname(drvr, ifp->bssidx)); - drvr->bus_if->tx_realloc++; - skb2 = skb_realloc_headroom(skb, drvr->hdrlen); - dev_kfree_skb(skb); - skb = skb2; - if (skb == NULL) { - brcmf_err("%s: skb_realloc_headroom failed\n", - brcmf_ifname(drvr, ifp->bssidx)); - ret = -ENOMEM; - goto done; - } - } - - /* validate length for ether packet */ - if (skb->len < sizeof(*eh)) { - ret = -EINVAL; - dev_kfree_skb(skb); - goto done; - } - - if (eh->h_proto == htons(ETH_P_PAE)) - atomic_inc(&ifp->pend_8021x_cnt); - - ret = brcmf_fws_process_skb(ifp, skb); - -done: - if (ret) { - ifp->stats.tx_dropped++; - } else { - ifp->stats.tx_packets++; - ifp->stats.tx_bytes += skb->len; - } - - /* Return ok: we always eat the packet */ - return NETDEV_TX_OK; -} - -void brcmf_txflowblock_if(struct brcmf_if *ifp, - enum brcmf_netif_stop_reason reason, bool state) -{ - unsigned long flags; - - if (!ifp || !ifp->ndev) - return; - - brcmf_dbg(TRACE, "enter: idx=%d stop=0x%X reason=%d state=%d\n", - ifp->bssidx, ifp->netif_stop, reason, state); - - spin_lock_irqsave(&ifp->netif_stop_lock, flags); - if (state) { - if (!ifp->netif_stop) - netif_stop_queue(ifp->ndev); - ifp->netif_stop |= reason; - } else { - ifp->netif_stop &= ~reason; - if (!ifp->netif_stop) - netif_wake_queue(ifp->ndev); - } - spin_unlock_irqrestore(&ifp->netif_stop_lock, flags); -} - -void brcmf_txflowblock(struct device *dev, bool state) -{ - struct brcmf_bus *bus_if = dev_get_drvdata(dev); - struct brcmf_pub *drvr = bus_if->drvr; - - brcmf_dbg(TRACE, "Enter\n"); - - brcmf_fws_bus_blocked(drvr, state); -} - -void brcmf_netif_rx(struct brcmf_if *ifp, struct sk_buff *skb) -{ - skb->dev = ifp->ndev; - skb->protocol = eth_type_trans(skb, skb->dev); - - if (skb->pkt_type == PACKET_MULTICAST) - ifp->stats.multicast++; - - /* Process special event packets */ - brcmf_fweh_process_skb(ifp->drvr, skb); - - if (!(ifp->ndev->flags & IFF_UP)) { - brcmu_pkt_buf_free_skb(skb); - return; - } - - ifp->stats.rx_bytes += skb->len; - ifp->stats.rx_packets++; - - brcmf_dbg(DATA, "rx proto=0x%X\n", ntohs(skb->protocol)); - if (in_interrupt()) - netif_rx(skb); - else - /* If the receive is not processed inside an ISR, - * the softirqd must be woken explicitly to service - * the NET_RX_SOFTIRQ. This is handled by netif_rx_ni(). - */ - netif_rx_ni(skb); -} - -static void brcmf_rxreorder_get_skb_list(struct brcmf_ampdu_rx_reorder *rfi, - u8 start, u8 end, - struct sk_buff_head *skb_list) -{ - /* initialize return list */ - __skb_queue_head_init(skb_list); - - if (rfi->pend_pkts == 0) { - brcmf_dbg(INFO, "no packets in reorder queue\n"); - return; - } - - do { - if (rfi->pktslots[start]) { - __skb_queue_tail(skb_list, rfi->pktslots[start]); - rfi->pktslots[start] = NULL; - } - start++; - if (start > rfi->max_idx) - start = 0; - } while (start != end); - rfi->pend_pkts -= skb_queue_len(skb_list); -} - -static void brcmf_rxreorder_process_info(struct brcmf_if *ifp, u8 *reorder_data, - struct sk_buff *pkt) -{ - u8 flow_id, max_idx, cur_idx, exp_idx, end_idx; - struct brcmf_ampdu_rx_reorder *rfi; - struct sk_buff_head reorder_list; - struct sk_buff *pnext; - u8 flags; - u32 buf_size; - - flow_id = reorder_data[BRCMF_RXREORDER_FLOWID_OFFSET]; - flags = reorder_data[BRCMF_RXREORDER_FLAGS_OFFSET]; - - /* validate flags and flow id */ - if (flags == 0xFF) { - brcmf_err("invalid flags...so ignore this packet\n"); - brcmf_netif_rx(ifp, pkt); - return; - } - - rfi = ifp->drvr->reorder_flows[flow_id]; - if (flags & BRCMF_RXREORDER_DEL_FLOW) { - brcmf_dbg(INFO, "flow-%d: delete\n", - flow_id); - - if (rfi == NULL) { - brcmf_dbg(INFO, "received flags to cleanup, but no flow (%d) yet\n", - flow_id); - brcmf_netif_rx(ifp, pkt); - return; - } - - brcmf_rxreorder_get_skb_list(rfi, rfi->exp_idx, rfi->exp_idx, - &reorder_list); - /* add the last packet */ - __skb_queue_tail(&reorder_list, pkt); - kfree(rfi); - ifp->drvr->reorder_flows[flow_id] = NULL; - goto netif_rx; - } - /* from here on we need a flow reorder instance */ - if (rfi == NULL) { - buf_size = sizeof(*rfi); - max_idx = reorder_data[BRCMF_RXREORDER_MAXIDX_OFFSET]; - - buf_size += (max_idx + 1) * sizeof(pkt); - - /* allocate space for flow reorder info */ - brcmf_dbg(INFO, "flow-%d: start, maxidx %d\n", - flow_id, max_idx); - rfi = kzalloc(buf_size, GFP_ATOMIC); - if (rfi == NULL) { - brcmf_err("failed to alloc buffer\n"); - brcmf_netif_rx(ifp, pkt); - return; - } - - ifp->drvr->reorder_flows[flow_id] = rfi; - rfi->pktslots = (struct sk_buff **)(rfi+1); - rfi->max_idx = max_idx; - } - if (flags & BRCMF_RXREORDER_NEW_HOLE) { - if (rfi->pend_pkts) { - brcmf_rxreorder_get_skb_list(rfi, rfi->exp_idx, - rfi->exp_idx, - &reorder_list); - WARN_ON(rfi->pend_pkts); - } else { - __skb_queue_head_init(&reorder_list); - } - rfi->cur_idx = reorder_data[BRCMF_RXREORDER_CURIDX_OFFSET]; - rfi->exp_idx = reorder_data[BRCMF_RXREORDER_EXPIDX_OFFSET]; - rfi->max_idx = reorder_data[BRCMF_RXREORDER_MAXIDX_OFFSET]; - rfi->pktslots[rfi->cur_idx] = pkt; - rfi->pend_pkts++; - brcmf_dbg(DATA, "flow-%d: new hole %d (%d), pending %d\n", - flow_id, rfi->cur_idx, rfi->exp_idx, rfi->pend_pkts); - } else if (flags & BRCMF_RXREORDER_CURIDX_VALID) { - cur_idx = reorder_data[BRCMF_RXREORDER_CURIDX_OFFSET]; - exp_idx = reorder_data[BRCMF_RXREORDER_EXPIDX_OFFSET]; - - if ((exp_idx == rfi->exp_idx) && (cur_idx != rfi->exp_idx)) { - /* still in the current hole */ - /* enqueue the current on the buffer chain */ - if (rfi->pktslots[cur_idx] != NULL) { - brcmf_dbg(INFO, "HOLE: ERROR buffer pending..free it\n"); - brcmu_pkt_buf_free_skb(rfi->pktslots[cur_idx]); - rfi->pktslots[cur_idx] = NULL; - } - rfi->pktslots[cur_idx] = pkt; - rfi->pend_pkts++; - rfi->cur_idx = cur_idx; - brcmf_dbg(DATA, "flow-%d: store pkt %d (%d), pending %d\n", - flow_id, cur_idx, exp_idx, rfi->pend_pkts); - - /* can return now as there is no reorder - * list to process. - */ - return; - } - if (rfi->exp_idx == cur_idx) { - if (rfi->pktslots[cur_idx] != NULL) { - brcmf_dbg(INFO, "error buffer pending..free it\n"); - brcmu_pkt_buf_free_skb(rfi->pktslots[cur_idx]); - rfi->pktslots[cur_idx] = NULL; - } - rfi->pktslots[cur_idx] = pkt; - rfi->pend_pkts++; - - /* got the expected one. flush from current to expected - * and update expected - */ - brcmf_dbg(DATA, "flow-%d: expected %d (%d), pending %d\n", - flow_id, cur_idx, exp_idx, rfi->pend_pkts); - - rfi->cur_idx = cur_idx; - rfi->exp_idx = exp_idx; - - brcmf_rxreorder_get_skb_list(rfi, cur_idx, exp_idx, - &reorder_list); - brcmf_dbg(DATA, "flow-%d: freeing buffers %d, pending %d\n", - flow_id, skb_queue_len(&reorder_list), - rfi->pend_pkts); - } else { - u8 end_idx; - - brcmf_dbg(DATA, "flow-%d (0x%x): both moved, old %d/%d, new %d/%d\n", - flow_id, flags, rfi->cur_idx, rfi->exp_idx, - cur_idx, exp_idx); - if (flags & BRCMF_RXREORDER_FLUSH_ALL) - end_idx = rfi->exp_idx; - else - end_idx = exp_idx; - - /* flush pkts first */ - brcmf_rxreorder_get_skb_list(rfi, rfi->exp_idx, end_idx, - &reorder_list); - - if (exp_idx == ((cur_idx + 1) % (rfi->max_idx + 1))) { - __skb_queue_tail(&reorder_list, pkt); - } else { - rfi->pktslots[cur_idx] = pkt; - rfi->pend_pkts++; - } - rfi->exp_idx = exp_idx; - rfi->cur_idx = cur_idx; - } - } else { - /* explicity window move updating the expected index */ - exp_idx = reorder_data[BRCMF_RXREORDER_EXPIDX_OFFSET]; - - brcmf_dbg(DATA, "flow-%d (0x%x): change expected: %d -> %d\n", - flow_id, flags, rfi->exp_idx, exp_idx); - if (flags & BRCMF_RXREORDER_FLUSH_ALL) - end_idx = rfi->exp_idx; - else - end_idx = exp_idx; - - brcmf_rxreorder_get_skb_list(rfi, rfi->exp_idx, end_idx, - &reorder_list); - __skb_queue_tail(&reorder_list, pkt); - /* set the new expected idx */ - rfi->exp_idx = exp_idx; - } -netif_rx: - skb_queue_walk_safe(&reorder_list, pkt, pnext) { - __skb_unlink(pkt, &reorder_list); - brcmf_netif_rx(ifp, pkt); - } -} - -void brcmf_rx_frame(struct device *dev, struct sk_buff *skb) -{ - struct brcmf_if *ifp; - 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]; - - if (ret || !ifp || !ifp->ndev) { - if ((ret != -ENODATA) && ifp) - ifp->stats.rx_errors++; - brcmu_pkt_buf_free_skb(skb); - return; - } - - rd = (struct brcmf_skb_reorder_data *)skb->cb; - if (rd->reorder) - brcmf_rxreorder_process_info(ifp, rd->reorder, skb); - else - brcmf_netif_rx(ifp, skb); -} - -void brcmf_txfinalize(struct brcmf_pub *drvr, struct sk_buff *txp, u8 ifidx, - 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); - - if (type == ETH_P_PAE) { - atomic_dec(&ifp->pend_8021x_cnt); - if (waitqueue_active(&ifp->pend_8021x_wait)) - wake_up(&ifp->pend_8021x_wait); - } - - if (!success) - ifp->stats.tx_errors++; -done: - brcmu_pkt_buf_free_skb(txp); -} - -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; - - /* 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)) - brcmu_pkt_buf_free_skb(txp); - else - brcmf_txfinalize(drvr, txp, ifidx, success); - } -} - -static struct net_device_stats *brcmf_netdev_get_stats(struct net_device *ndev) -{ - struct brcmf_if *ifp = netdev_priv(ndev); - - brcmf_dbg(TRACE, "Enter, idx=%d\n", ifp->bssidx); - - return &ifp->stats; -} - -static void brcmf_ethtool_get_drvinfo(struct net_device *ndev, - struct ethtool_drvinfo *info) -{ - struct brcmf_if *ifp = netdev_priv(ndev); - struct brcmf_pub *drvr = ifp->drvr; - - strlcpy(info->driver, KBUILD_MODNAME, sizeof(info->driver)); - snprintf(info->version, sizeof(info->version), "n/a"); - strlcpy(info->fw_version, drvr->fwver, sizeof(info->fw_version)); - strlcpy(info->bus_info, dev_name(drvr->bus_if->dev), - sizeof(info->bus_info)); -} - -static const struct ethtool_ops brcmf_ethtool_ops = { - .get_drvinfo = brcmf_ethtool_get_drvinfo, -}; - -static int brcmf_netdev_stop(struct net_device *ndev) -{ - struct brcmf_if *ifp = netdev_priv(ndev); - - brcmf_dbg(TRACE, "Enter, idx=%d\n", ifp->bssidx); - - brcmf_cfg80211_down(ndev); - - /* Set state and stop OS transmissions */ - netif_stop_queue(ndev); - - return 0; -} - -static int brcmf_netdev_open(struct net_device *ndev) -{ - struct brcmf_if *ifp = netdev_priv(ndev); - struct brcmf_pub *drvr = ifp->drvr; - struct brcmf_bus *bus_if = drvr->bus_if; - u32 toe_ol; - - brcmf_dbg(TRACE, "Enter, idx=%d\n", ifp->bssidx); - - /* If bus is not ready, can't continue */ - if (bus_if->state != BRCMF_BUS_DATA) { - brcmf_err("failed bus is not ready\n"); - return -EAGAIN; - } - - atomic_set(&ifp->pend_8021x_cnt, 0); - - /* Get current TOE mode from dongle */ - if (brcmf_fil_iovar_int_get(ifp, "toe_ol", &toe_ol) >= 0 - && (toe_ol & TOE_TX_CSUM_OL) != 0) - ndev->features |= NETIF_F_IP_CSUM; - else - ndev->features &= ~NETIF_F_IP_CSUM; - - if (brcmf_cfg80211_up(ndev)) { - brcmf_err("failed to bring up cfg80211\n"); - return -EIO; - } - - /* Allow transmit calls */ - netif_start_queue(ndev); - return 0; -} - -static const struct net_device_ops brcmf_netdev_ops_pri = { - .ndo_open = brcmf_netdev_open, - .ndo_stop = brcmf_netdev_stop, - .ndo_get_stats = brcmf_netdev_get_stats, - .ndo_start_xmit = brcmf_netdev_start_xmit, - .ndo_set_mac_address = brcmf_netdev_set_mac_address, - .ndo_set_rx_mode = brcmf_netdev_set_multicast_list -}; - -int brcmf_net_attach(struct brcmf_if *ifp, bool rtnl_locked) -{ - struct brcmf_pub *drvr = ifp->drvr; - struct net_device *ndev; - s32 err; - - brcmf_dbg(TRACE, "Enter, idx=%d mac=%pM\n", ifp->bssidx, - ifp->mac_addr); - ndev = ifp->ndev; - - /* set appropriate operations */ - ndev->netdev_ops = &brcmf_netdev_ops_pri; - - ndev->hard_header_len += drvr->hdrlen; - ndev->ethtool_ops = &brcmf_ethtool_ops; - - drvr->rxsz = ndev->mtu + ndev->hard_header_len + - drvr->hdrlen; - - /* set the mac address */ - memcpy(ndev->dev_addr, ifp->mac_addr, ETH_ALEN); - - INIT_WORK(&ifp->setmacaddr_work, _brcmf_set_mac_address); - INIT_WORK(&ifp->multicast_work, _brcmf_set_multicast_list); - - if (rtnl_locked) - err = register_netdevice(ndev); - else - err = register_netdev(ndev); - if (err != 0) { - brcmf_err("couldn't register the net device\n"); - goto fail; - } - - brcmf_dbg(INFO, "%s: Broadcom Dongle Host Driver\n", ndev->name); - - ndev->destructor = brcmf_cfg80211_free_netdev; - return 0; - -fail: - drvr->iflist[ifp->bssidx] = NULL; - ndev->netdev_ops = NULL; - free_netdev(ndev); - return -EBADE; -} - -static int brcmf_net_p2p_open(struct net_device *ndev) -{ - brcmf_dbg(TRACE, "Enter\n"); - - return brcmf_cfg80211_up(ndev); -} - -static int brcmf_net_p2p_stop(struct net_device *ndev) -{ - brcmf_dbg(TRACE, "Enter\n"); - - return brcmf_cfg80211_down(ndev); -} - -static netdev_tx_t brcmf_net_p2p_start_xmit(struct sk_buff *skb, - struct net_device *ndev) -{ - if (skb) - dev_kfree_skb_any(skb); - - return NETDEV_TX_OK; -} - -static const struct net_device_ops brcmf_netdev_ops_p2p = { - .ndo_open = brcmf_net_p2p_open, - .ndo_stop = brcmf_net_p2p_stop, - .ndo_start_xmit = brcmf_net_p2p_start_xmit -}; - -static int brcmf_net_p2p_attach(struct brcmf_if *ifp) -{ - struct net_device *ndev; - - brcmf_dbg(TRACE, "Enter, idx=%d mac=%pM\n", ifp->bssidx, - ifp->mac_addr); - ndev = ifp->ndev; - - ndev->netdev_ops = &brcmf_netdev_ops_p2p; - - /* set the mac address */ - memcpy(ndev->dev_addr, ifp->mac_addr, ETH_ALEN); - - if (register_netdev(ndev) != 0) { - brcmf_err("couldn't register the p2p net device\n"); - goto fail; - } - - brcmf_dbg(INFO, "%s: Broadcom Dongle Host Driver\n", ndev->name); - - return 0; - -fail: - ifp->drvr->iflist[ifp->bssidx] = NULL; - ndev->netdev_ops = NULL; - free_netdev(ndev); - return -EBADE; -} - -struct brcmf_if *brcmf_add_if(struct brcmf_pub *drvr, s32 bssidx, s32 ifidx, - char *name, u8 *mac_addr) -{ - struct brcmf_if *ifp; - struct net_device *ndev; - - brcmf_dbg(TRACE, "Enter, idx=%d, ifidx=%d\n", bssidx, ifidx); - - ifp = drvr->iflist[bssidx]; - /* - * Delete the existing interface before overwriting it - * in case we missed the BRCMF_E_IF_DEL event. - */ - if (ifp) { - brcmf_err("ERROR: netdev:%s already exists\n", - ifp->ndev->name); - if (ifidx) { - netif_stop_queue(ifp->ndev); - unregister_netdev(ifp->ndev); - free_netdev(ifp->ndev); - drvr->iflist[bssidx] = NULL; - } else { - brcmf_err("ignore IF event\n"); - return ERR_PTR(-EINVAL); - } - } - - if (!brcmf_p2p_enable && bssidx == 1) { - /* this is P2P_DEVICE interface */ - brcmf_dbg(INFO, "allocate non-netdev interface\n"); - ifp = kzalloc(sizeof(*ifp), GFP_KERNEL); - if (!ifp) - return ERR_PTR(-ENOMEM); - } 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); - if (!ndev) - return ERR_PTR(-ENOMEM); - - ifp = netdev_priv(ndev); - ifp->ndev = ndev; - } - - ifp->drvr = drvr; - drvr->iflist[bssidx] = ifp; - ifp->ifidx = ifidx; - ifp->bssidx = bssidx; - - init_waitqueue_head(&ifp->pend_8021x_wait); - spin_lock_init(&ifp->netif_stop_lock); - - if (mac_addr != NULL) - memcpy(ifp->mac_addr, mac_addr, ETH_ALEN); - - brcmf_dbg(TRACE, " ==== pid:%x, if:%s (%pM) created ===\n", - current->pid, name, ifp->mac_addr); - - return ifp; -} - -void brcmf_del_if(struct brcmf_pub *drvr, s32 bssidx) -{ - struct brcmf_if *ifp; - - ifp = drvr->iflist[bssidx]; - drvr->iflist[bssidx] = NULL; - if (!ifp) { - brcmf_err("Null interface, idx=%d\n", bssidx); - return; - } - brcmf_dbg(TRACE, "Enter, idx=%d, ifidx=%d\n", bssidx, ifp->ifidx); - if (ifp->ndev) { - if (bssidx == 0) { - if (ifp->ndev->netdev_ops == &brcmf_netdev_ops_pri) { - rtnl_lock(); - brcmf_netdev_stop(ifp->ndev); - rtnl_unlock(); - } - } else { - netif_stop_queue(ifp->ndev); - } - - if (ifp->ndev->netdev_ops == &brcmf_netdev_ops_pri) { - cancel_work_sync(&ifp->setmacaddr_work); - cancel_work_sync(&ifp->multicast_work); - } - /* unregister will take care of freeing it */ - unregister_netdev(ifp->ndev); - } else { - kfree(ifp); - } -} - -int brcmf_attach(struct device *dev) -{ - struct brcmf_pub *drvr = NULL; - int ret = 0; - - brcmf_dbg(TRACE, "Enter\n"); - - /* Allocate primary brcmf_info */ - drvr = kzalloc(sizeof(struct brcmf_pub), GFP_ATOMIC); - if (!drvr) - return -ENOMEM; - - mutex_init(&drvr->proto_block); - - /* Link to bus module */ - drvr->hdrlen = 0; - drvr->bus_if = dev_get_drvdata(dev); - drvr->bus_if->drvr = drvr; - - /* create device debugfs folder */ - brcmf_debugfs_attach(drvr); - - /* Attach and link in the protocol */ - ret = brcmf_proto_attach(drvr); - if (ret != 0) { - brcmf_err("brcmf_prot_attach failed\n"); - goto fail; - } - - /* attach firmware event handler */ - brcmf_fweh_attach(drvr); - - return ret; - -fail: - brcmf_detach(dev); - - return ret; -} - -int brcmf_bus_start(struct device *dev) -{ - int ret = -1; - struct brcmf_bus *bus_if = dev_get_drvdata(dev); - struct brcmf_pub *drvr = bus_if->drvr; - struct brcmf_if *ifp; - struct brcmf_if *p2p_ifp; - - brcmf_dbg(TRACE, "\n"); - - /* add primary networking interface */ - ifp = brcmf_add_if(drvr, 0, 0, "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; - - /* signal bus ready */ - brcmf_bus_change_state(bus_if, BRCMF_BUS_DATA); - - /* Bus is ready, do any initialization */ - ret = brcmf_c_preinit_dcmds(ifp); - if (ret < 0) - goto fail; - - brcmf_feat_attach(drvr); - - ret = brcmf_fws_init(drvr); - if (ret < 0) - goto fail; - - brcmf_fws_add_interface(ifp); - - drvr->config = brcmf_cfg80211_attach(drvr, bus_if->dev); - 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); -fail: - if (ret < 0) { - brcmf_err("failed: %d\n", ret); - brcmf_cfg80211_detach(drvr->config); - 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; - } - return ret; - } - if ((brcmf_p2p_enable) && (p2p_ifp)) - if (brcmf_net_p2p_attach(p2p_ifp) < 0) - brcmf_p2p_enable = 0; - - return 0; -} - -void brcmf_bus_add_txhdrlen(struct device *dev, uint len) -{ - struct brcmf_bus *bus_if = dev_get_drvdata(dev); - struct brcmf_pub *drvr = bus_if->drvr; - - if (drvr) { - drvr->hdrlen += len; - } -} - -static void brcmf_bus_detach(struct brcmf_pub *drvr) -{ - brcmf_dbg(TRACE, "Enter\n"); - - if (drvr) { - /* Stop the bus module */ - brcmf_bus_stop(drvr->bus_if); - } -} - -void brcmf_dev_reset(struct device *dev) -{ - struct brcmf_bus *bus_if = dev_get_drvdata(dev); - struct brcmf_pub *drvr = bus_if->drvr; - - if (drvr == NULL) - return; - - if (drvr->iflist[0]) - brcmf_fil_cmd_int_set(drvr->iflist[0], BRCMF_C_TERMINATED, 1); -} - -void brcmf_detach(struct device *dev) -{ - s32 i; - struct brcmf_bus *bus_if = dev_get_drvdata(dev); - struct brcmf_pub *drvr = bus_if->drvr; - - brcmf_dbg(TRACE, "Enter\n"); - - if (drvr == NULL) - return; - - /* stop firmware event handling */ - brcmf_fweh_detach(drvr); - - brcmf_bus_change_state(bus_if, BRCMF_BUS_DOWN); - - /* make sure primary interface removed last */ - for (i = BRCMF_MAX_IFS-1; i > -1; i--) - if (drvr->iflist[i]) { - brcmf_fws_del_interface(drvr->iflist[i]); - brcmf_del_if(drvr, i); - } - - brcmf_cfg80211_detach(drvr->config); - - brcmf_fws_deinit(drvr); - - brcmf_bus_detach(drvr); - - brcmf_proto_detach(drvr); - - brcmf_debugfs_detach(drvr); - bus_if->drvr = NULL; - kfree(drvr); -} - -s32 brcmf_iovar_data_set(struct device *dev, char *name, void *data, u32 len) -{ - struct brcmf_bus *bus_if = dev_get_drvdata(dev); - struct brcmf_if *ifp = bus_if->drvr->iflist[0]; - - return brcmf_fil_iovar_data_set(ifp, name, data, len); -} - -static int brcmf_get_pend_8021x_cnt(struct brcmf_if *ifp) -{ - return atomic_read(&ifp->pend_8021x_cnt); -} - -int brcmf_netdev_wait_pend8021x(struct net_device *ndev) -{ - struct brcmf_if *ifp = netdev_priv(ndev); - int err; - - err = wait_event_timeout(ifp->pend_8021x_wait, - !brcmf_get_pend_8021x_cnt(ifp), - msecs_to_jiffies(MAX_WAIT_FOR_8021X_TX)); - - WARN_ON(!err); - - return !err; -} - -static void brcmf_driver_register(struct work_struct *work) -{ -#ifdef CONFIG_BRCMFMAC_SDIO - brcmf_sdio_register(); -#endif -#ifdef CONFIG_BRCMFMAC_USB - brcmf_usb_register(); -#endif -#ifdef CONFIG_BRCMFMAC_PCIE - brcmf_pcie_register(); -#endif -} -static DECLARE_WORK(brcmf_driver_work, brcmf_driver_register); - -static int __init brcmfmac_module_init(void) -{ - brcmf_debugfs_init(); -#ifdef CONFIG_BRCMFMAC_SDIO - brcmf_sdio_init(); -#endif - if (!schedule_work(&brcmf_driver_work)) - return -EBUSY; - - return 0; -} - -static void __exit brcmfmac_module_exit(void) -{ - cancel_work_sync(&brcmf_driver_work); - -#ifdef CONFIG_BRCMFMAC_SDIO - brcmf_sdio_exit(); -#endif -#ifdef CONFIG_BRCMFMAC_USB - brcmf_usb_exit(); -#endif -#ifdef CONFIG_BRCMFMAC_PCIE - brcmf_pcie_exit(); -#endif - brcmf_debugfs_exit(); -} - -module_init(brcmfmac_module_init); -module_exit(brcmfmac_module_exit); diff --git a/drivers/net/wireless/brcm80211/brcmfmac/feature.c b/drivers/net/wireless/brcm80211/brcmfmac/feature.c index d4be4c416912..931f68aefaa4 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/feature.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/feature.c @@ -17,7 +17,7 @@ #include #include -#include "dhd.h" +#include "core.h" #include "bus.h" #include "debug.h" #include "fwil.h" diff --git a/drivers/net/wireless/brcm80211/brcmfmac/flowring.c b/drivers/net/wireless/brcm80211/brcmfmac/flowring.c index e9cb550f5c5a..44f3a84d1999 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/flowring.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/flowring.c @@ -19,7 +19,7 @@ #include #include -#include "dhd.h" +#include "core.h" #include "debug.h" #include "bus.h" #include "proto.h" diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fweh.c b/drivers/net/wireless/brcm80211/brcmfmac/fweh.c index ce98ce55685b..7338b335e153 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/fweh.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/fweh.c @@ -18,7 +18,7 @@ #include "brcmu_wifi.h" #include "brcmu_utils.h" -#include "dhd.h" +#include "core.h" #include "debug.h" #include "tracepoint.h" #include "fwsignal.h" diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fwil.c b/drivers/net/wireless/brcm80211/brcmfmac/fwil.c index 70db41f51b7b..51f88c11e642 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/fwil.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/fwil.c @@ -22,7 +22,7 @@ #include #include #include -#include "dhd.h" +#include "core.h" #include "bus.h" #include "debug.h" #include "tracepoint.h" diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c index 6d33331dd2b0..f0dda0ecd23b 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c @@ -26,7 +26,7 @@ #include #include -#include "dhd.h" +#include "core.h" #include "debug.h" #include "bus.h" #include "fwil.h" diff --git a/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c b/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c index 18e1770b8d8c..02d39ce8dbca 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c @@ -24,7 +24,7 @@ #include #include -#include "dhd.h" +#include "core.h" #include "debug.h" #include "proto.h" #include "msgbuf.h" diff --git a/drivers/net/wireless/brcm80211/brcmfmac/p2p.c b/drivers/net/wireless/brcm80211/brcmfmac/p2p.c index ae504b40e554..effb48ebd864 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/p2p.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/p2p.c @@ -21,7 +21,7 @@ #include #include #include -#include +#include "core.h" #include "debug.h" #include "fwil.h" #include "fwil_types.h" diff --git a/drivers/net/wireless/brcm80211/brcmfmac/proto.c b/drivers/net/wireless/brcm80211/brcmfmac/proto.c index 5e83b2bee9d8..26b68c367f57 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/proto.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/proto.c @@ -20,7 +20,7 @@ #include #include -#include "dhd.h" +#include "core.h" #include "bus.h" #include "debug.h" #include "proto.h" diff --git a/drivers/net/wireless/brcm80211/brcmfmac/vendor.c b/drivers/net/wireless/brcm80211/brcmfmac/vendor.c index b86a08369517..222f26a39642 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/vendor.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/vendor.c @@ -20,7 +20,7 @@ #include #include "fwil_types.h" -#include "dhd.h" +#include "core.h" #include "p2p.h" #include "debug.h" #include "cfg80211.h" -- cgit v1.2.3 From 84936626efbfacaf53505020610ae275fae2bf88 Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Tue, 28 Oct 2014 14:56:19 +0100 Subject: brcmfmac: (clean) Move sdio related function. prec_enq is a sdio specific function. Move it to sdio.c. Reviewed-by: Arend Van Spriel Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/common.c | 51 ------------------------ drivers/net/wireless/brcm80211/brcmfmac/core.h | 1 - drivers/net/wireless/brcm80211/brcmfmac/sdio.c | 46 ++++++++++++++++++++- 3 files changed, 44 insertions(+), 54 deletions(-) (limited to 'drivers/net/wireless/brcm80211') diff --git a/drivers/net/wireless/brcm80211/brcmfmac/common.c b/drivers/net/wireless/brcm80211/brcmfmac/common.c index 183cec9939ea..1861a13e8d03 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/common.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/common.c @@ -33,57 +33,6 @@ /* boost value for RSSI_DELTA in preferred join selection */ #define BRCMF_JOIN_PREF_RSSI_BOOST 8 - -bool brcmf_c_prec_enq(struct device *dev, struct pktq *q, - struct sk_buff *pkt, int prec) -{ - struct sk_buff *p; - int eprec = -1; /* precedence to evict from */ - bool discard_oldest; - struct brcmf_bus *bus_if = dev_get_drvdata(dev); - struct brcmf_pub *drvr = bus_if->drvr; - - /* Fast case, precedence queue is not full and we are also not - * exceeding total queue length - */ - if (!pktq_pfull(q, prec) && !pktq_full(q)) { - brcmu_pktq_penq(q, prec, pkt); - return true; - } - - /* Determine precedence from which to evict packet, if any */ - if (pktq_pfull(q, prec)) { - eprec = prec; - } else if (pktq_full(q)) { - p = brcmu_pktq_peek_tail(q, &eprec); - if (eprec > prec) - return false; - } - - /* Evict if needed */ - if (eprec >= 0) { - /* Detect queueing to unconfigured precedence */ - discard_oldest = ac_bitmap_tst(drvr->wme_dp, eprec); - if (eprec == prec && !discard_oldest) - return false; /* refuse newer (incoming) packet */ - /* Evict packet according to discard policy */ - p = discard_oldest ? brcmu_pktq_pdeq(q, eprec) : - brcmu_pktq_pdeq_tail(q, eprec); - if (p == NULL) - brcmf_err("brcmu_pktq_penq() failed, oldest %d\n", - discard_oldest); - - brcmu_pkt_buf_free_skb(p); - } - - /* Enqueue */ - p = brcmu_pktq_penq(q, prec, pkt); - if (p == NULL) - brcmf_err("brcmu_pktq_penq() failed\n"); - - return p != NULL; -} - int brcmf_c_preinit_dcmds(struct brcmf_if *ifp) { s8 eventmask[BRCMF_EVENTING_MASK_LEN]; diff --git a/drivers/net/wireless/brcm80211/brcmfmac/core.h b/drivers/net/wireless/brcm80211/brcmfmac/core.h index 7df22bdb19a2..98228e922d3a 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/core.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/core.h @@ -83,7 +83,6 @@ struct brcmf_pub { /* Internal brcmf items */ uint hdrlen; /* Total BRCMF header length (proto + bus) */ uint rxsz; /* Rx buffer size bus module should use */ - u8 wme_dp; /* wme discard priority */ /* Dongle media info */ char fwver[BRCMF_DRIVER_FIRMWARE_VERSION_LEN]; diff --git a/drivers/net/wireless/brcm80211/brcmfmac/sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/sdio.c index 73ac0c38a32a..72e87b51f999 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/sdio.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/sdio.c @@ -2763,6 +2763,48 @@ static struct pktq *brcmf_sdio_bus_gettxq(struct device *dev) return &bus->txq; } +static bool brcmf_sdio_prec_enq(struct pktq *q, struct sk_buff *pkt, int prec) +{ + struct sk_buff *p; + int eprec = -1; /* precedence to evict from */ + + /* Fast case, precedence queue is not full and we are also not + * exceeding total queue length + */ + if (!pktq_pfull(q, prec) && !pktq_full(q)) { + brcmu_pktq_penq(q, prec, pkt); + return true; + } + + /* Determine precedence from which to evict packet, if any */ + if (pktq_pfull(q, prec)) { + eprec = prec; + } else if (pktq_full(q)) { + p = brcmu_pktq_peek_tail(q, &eprec); + if (eprec > prec) + return false; + } + + /* Evict if needed */ + if (eprec >= 0) { + /* Detect queueing to unconfigured precedence */ + if (eprec == prec) + return false; /* refuse newer (incoming) packet */ + /* Evict packet according to discard policy */ + p = brcmu_pktq_pdeq_tail(q, eprec); + if (p == NULL) + brcmf_err("brcmu_pktq_pdeq_tail() failed\n"); + brcmu_pkt_buf_free_skb(p); + } + + /* Enqueue */ + p = brcmu_pktq_penq(q, prec, pkt); + if (p == NULL) + brcmf_err("brcmu_pktq_penq() failed\n"); + + return p != NULL; +} + static int brcmf_sdio_bus_txdata(struct device *dev, struct sk_buff *pkt) { int ret = -EBADE; @@ -2788,7 +2830,7 @@ static int brcmf_sdio_bus_txdata(struct device *dev, struct sk_buff *pkt) spin_lock_bh(&bus->txq_lock); /* reset bus_flags in packet cb */ *(u16 *)(pkt->cb) = 0; - if (!brcmf_c_prec_enq(bus->sdiodev->dev, &bus->txq, pkt, prec)) { + if (!brcmf_sdio_prec_enq(&bus->txq, pkt, prec)) { skb_pull(pkt, bus->tx_hdrlen); brcmf_err("out of bus->txq !!!\n"); ret = -ENOSR; @@ -2798,7 +2840,7 @@ static int brcmf_sdio_bus_txdata(struct device *dev, struct sk_buff *pkt) if (pktq_len(&bus->txq) >= TXHI) { bus->txoff = true; - brcmf_txflowblock(bus->sdiodev->dev, true); + brcmf_txflowblock(dev, true); } spin_unlock_bh(&bus->txq_lock); -- cgit v1.2.3 From 9146782b1bba3323ef492614402ab0805b597638 Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Wed, 29 Oct 2014 16:02:50 +0100 Subject: brcmsmac: fix statistic counter update function The 802.11 core statistics are retrieved from the core registers but not stored. So the debug code was never triggered to give a warning message on tx underruns or rx overflows. This patch fixes this and assures the statistics are stored in the snapshot. Reviewed-by: Hante Meuleman Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmsmac/main.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'drivers/net/wireless/brcm80211') diff --git a/drivers/net/wireless/brcm80211/brcmsmac/main.c b/drivers/net/wireless/brcm80211/brcmsmac/main.c index 1b474828d5b8..bc9be78faafa 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/main.c +++ b/drivers/net/wireless/brcm80211/brcmsmac/main.c @@ -3081,7 +3081,7 @@ static bool brcms_c_ps_allowed(struct brcms_c_info *wlc) static void brcms_c_statsupd(struct brcms_c_info *wlc) { int i; - struct macstat macstats; + struct macstat *macstats; #ifdef DEBUG u16 delta; u16 rxf0ovfl; @@ -3092,31 +3092,31 @@ static void brcms_c_statsupd(struct brcms_c_info *wlc) if (!wlc->pub->up) return; + macstats = wlc->core->macstat_snapshot; + #ifdef DEBUG /* save last rx fifo 0 overflow count */ - rxf0ovfl = wlc->core->macstat_snapshot->rxf0ovfl; + rxf0ovfl = macstats->rxf0ovfl; /* save last tx fifo underflow count */ for (i = 0; i < NFIFO; i++) - txfunfl[i] = wlc->core->macstat_snapshot->txfunfl[i]; + txfunfl[i] = macstats->txfunfl[i]; #endif /* DEBUG */ /* Read mac stats from contiguous shared memory */ - brcms_b_copyfrom_objmem(wlc->hw, M_UCODE_MACSTAT, &macstats, - sizeof(struct macstat), OBJADDR_SHM_SEL); + brcms_b_copyfrom_objmem(wlc->hw, M_UCODE_MACSTAT, macstats, + sizeof(*macstats), OBJADDR_SHM_SEL); #ifdef DEBUG /* check for rx fifo 0 overflow */ - delta = (u16) (wlc->core->macstat_snapshot->rxf0ovfl - rxf0ovfl); + delta = (u16)(macstats->rxf0ovfl - rxf0ovfl); if (delta) brcms_err(wlc->hw->d11core, "wl%d: %u rx fifo 0 overflows!\n", wlc->pub->unit, delta); /* check for tx fifo underflows */ for (i = 0; i < NFIFO; i++) { - delta = - (u16) (wlc->core->macstat_snapshot->txfunfl[i] - - txfunfl[i]); + delta = macstats->txfunfl[i] - txfunfl[i]; if (delta) brcms_err(wlc->hw->d11core, "wl%d: %u tx fifo %d underflows!\n", -- cgit v1.2.3 From 3fe33c4cec15c4dec7935c1f08811fab43343bf0 Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Wed, 29 Oct 2014 16:02:51 +0100 Subject: brcmsmac: expose 802.11 core statistics in debugfs The 802.11 statistics obtained from the device can be retrieved dumping the 'macstat' file in debugfs folder. Reviewed-by: Hante Meuleman Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmsmac/debug.c | 166 +++++++++++++++++++----- 1 file changed, 133 insertions(+), 33 deletions(-) (limited to 'drivers/net/wireless/brcm80211') diff --git a/drivers/net/wireless/brcm80211/brcmsmac/debug.c b/drivers/net/wireless/brcm80211/brcmsmac/debug.c index a5d4add26f41..19740c1b1566 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/debug.c +++ b/drivers/net/wireless/brcm80211/brcmsmac/debug.c @@ -71,48 +71,148 @@ struct dentry *brcms_debugfs_get_devdir(struct brcms_pub *drvr) } static -ssize_t brcms_debugfs_hardware_read(struct file *f, char __user *data, - size_t count, loff_t *ppos) +int brcms_debugfs_hardware_read(struct seq_file *s, void *data) { - char buf[128]; - int res; - struct brcms_pub *drvr = f->private_data; - - /* only allow read from start */ - if (*ppos > 0) - return 0; - - res = scnprintf(buf, sizeof(buf), - "board vendor: %x\n" - "board type: %x\n" - "board revision: %x\n" - "board flags: %x\n" - "board flags2: %x\n" - "firmware revision: %x\n", - drvr->wlc->hw->d11core->bus->boardinfo.vendor, - drvr->wlc->hw->d11core->bus->boardinfo.type, - drvr->wlc->hw->boardrev, - drvr->wlc->hw->boardflags, - drvr->wlc->hw->boardflags2, - drvr->wlc->ucode_rev - ); - - return simple_read_from_buffer(data, count, ppos, buf, res); + struct brcms_pub *drvr = s->private; + + seq_printf(s, "board vendor: %x\n" + "board type: %x\n" + "board revision: %x\n" + "board flags: %x\n" + "board flags2: %x\n" + "firmware revision: %x\n", + drvr->wlc->hw->d11core->bus->boardinfo.vendor, + drvr->wlc->hw->d11core->bus->boardinfo.type, + drvr->wlc->hw->boardrev, + drvr->wlc->hw->boardflags, + drvr->wlc->hw->boardflags2, + drvr->wlc->ucode_rev); + + return 0; +} + +static int brcms_debugfs_macstat_read(struct seq_file *s, void *data) +{ + struct brcms_pub *drvr = s->private; + struct brcms_info *wl = drvr->ieee_hw->priv; + struct macstat stats; + int i; + + spin_lock_bh(&wl->lock); + stats = *(drvr->wlc->core->macstat_snapshot); + spin_unlock_bh(&wl->lock); + + seq_printf(s, "txallfrm: %d\n", stats.txallfrm); + seq_printf(s, "txrtsfrm: %d\n", stats.txrtsfrm); + seq_printf(s, "txctsfrm: %d\n", stats.txctsfrm); + seq_printf(s, "txackfrm: %d\n", stats.txackfrm); + seq_printf(s, "txdnlfrm: %d\n", stats.txdnlfrm); + seq_printf(s, "txbcnfrm: %d\n", stats.txbcnfrm); + seq_printf(s, "txfunfl[8]:"); + for (i = 0; i < ARRAY_SIZE(stats.txfunfl); i++) + seq_printf(s, " %d", stats.txfunfl[i]); + seq_printf(s, "\ntxtplunfl: %d\n", stats.txtplunfl); + seq_printf(s, "txphyerr: %d\n", stats.txphyerr); + seq_printf(s, "pktengrxducast: %d\n", stats.pktengrxducast); + seq_printf(s, "pktengrxdmcast: %d\n", stats.pktengrxdmcast); + seq_printf(s, "rxfrmtoolong: %d\n", stats.rxfrmtoolong); + seq_printf(s, "rxfrmtooshrt: %d\n", stats.rxfrmtooshrt); + seq_printf(s, "rxinvmachdr: %d\n", stats.rxinvmachdr); + seq_printf(s, "rxbadfcs: %d\n", stats.rxbadfcs); + seq_printf(s, "rxbadplcp: %d\n", stats.rxbadplcp); + seq_printf(s, "rxcrsglitch: %d\n", stats.rxcrsglitch); + seq_printf(s, "rxstrt: %d\n", stats.rxstrt); + seq_printf(s, "rxdfrmucastmbss: %d\n", stats.rxdfrmucastmbss); + seq_printf(s, "rxmfrmucastmbss: %d\n", stats.rxmfrmucastmbss); + seq_printf(s, "rxcfrmucast: %d\n", stats.rxcfrmucast); + seq_printf(s, "rxrtsucast: %d\n", stats.rxrtsucast); + seq_printf(s, "rxctsucast: %d\n", stats.rxctsucast); + seq_printf(s, "rxackucast: %d\n", stats.rxackucast); + seq_printf(s, "rxdfrmocast: %d\n", stats.rxdfrmocast); + seq_printf(s, "rxmfrmocast: %d\n", stats.rxmfrmocast); + seq_printf(s, "rxcfrmocast: %d\n", stats.rxcfrmocast); + seq_printf(s, "rxrtsocast: %d\n", stats.rxrtsocast); + seq_printf(s, "rxctsocast: %d\n", stats.rxctsocast); + seq_printf(s, "rxdfrmmcast: %d\n", stats.rxdfrmmcast); + seq_printf(s, "rxmfrmmcast: %d\n", stats.rxmfrmmcast); + seq_printf(s, "rxcfrmmcast: %d\n", stats.rxcfrmmcast); + seq_printf(s, "rxbeaconmbss: %d\n", stats.rxbeaconmbss); + seq_printf(s, "rxdfrmucastobss: %d\n", stats.rxdfrmucastobss); + seq_printf(s, "rxbeaconobss: %d\n", stats.rxbeaconobss); + seq_printf(s, "rxrsptmout: %d\n", stats.rxrsptmout); + seq_printf(s, "bcntxcancl: %d\n", stats.bcntxcancl); + seq_printf(s, "rxf0ovfl: %d\n", stats.rxf0ovfl); + seq_printf(s, "rxf1ovfl: %d\n", stats.rxf1ovfl); + seq_printf(s, "rxf2ovfl: %d\n", stats.rxf2ovfl); + seq_printf(s, "txsfovfl: %d\n", stats.txsfovfl); + seq_printf(s, "pmqovfl: %d\n", stats.pmqovfl); + seq_printf(s, "rxcgprqfrm: %d\n", stats.rxcgprqfrm); + seq_printf(s, "rxcgprsqovfl: %d\n", stats.rxcgprsqovfl); + seq_printf(s, "txcgprsfail: %d\n", stats.txcgprsfail); + seq_printf(s, "txcgprssuc: %d\n", stats.txcgprssuc); + seq_printf(s, "prs_timeout: %d\n", stats.prs_timeout); + seq_printf(s, "rxnack: %d\n", stats.rxnack); + seq_printf(s, "frmscons: %d\n", stats.frmscons); + seq_printf(s, "txnack: %d\n", stats.txnack); + seq_printf(s, "txglitch_nack: %d\n", stats.txglitch_nack); + seq_printf(s, "txburst: %d\n", stats.txburst); + seq_printf(s, "bphy_rxcrsglitch: %d\n", stats.bphy_rxcrsglitch); + seq_printf(s, "phywatchdog: %d\n", stats.phywatchdog); + seq_printf(s, "bphy_badplcp: %d\n", stats.bphy_badplcp); + return 0; +} + +struct brcms_debugfs_entry { + int (*read)(struct seq_file *seq, void *data); + struct brcms_pub *drvr; +}; + +static int brcms_debugfs_entry_open(struct inode *inode, struct file *f) +{ + struct brcms_debugfs_entry *entry = inode->i_private; + + return single_open(f, entry->read, entry->drvr); } -static const struct file_operations brcms_debugfs_hardware_ops = { +static const struct file_operations brcms_debugfs_def_ops = { .owner = THIS_MODULE, - .open = simple_open, - .read = brcms_debugfs_hardware_read + .open = brcms_debugfs_entry_open, + .release = single_release, + .read = seq_read, + .llseek = seq_lseek }; +static int +brcms_debugfs_add_entry(struct brcms_pub *drvr, const char *fn, + int (*read_fn)(struct seq_file *seq, void *data)) +{ + struct device *dev = &drvr->wlc->hw->d11core->dev; + struct dentry *dentry = drvr->dbgfs_dir; + struct brcms_debugfs_entry *entry; + + if (IS_ERR_OR_NULL(dentry)) + return -ENOENT; + + entry = devm_kzalloc(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, + &brcms_debugfs_def_ops); + + return PTR_ERR_OR_ZERO(dentry); +} + void brcms_debugfs_create_files(struct brcms_pub *drvr) { - struct dentry *dentry = drvr->dbgfs_dir; + if (IS_ERR_OR_NULL(drvr->dbgfs_dir)) + return; - if (!IS_ERR_OR_NULL(dentry)) - debugfs_create_file("hardware", S_IRUGO, dentry, - drvr, &brcms_debugfs_hardware_ops); + brcms_debugfs_add_entry(drvr, "hardware", brcms_debugfs_hardware_read); + brcms_debugfs_add_entry(drvr, "macstat", brcms_debugfs_macstat_read); } #define __brcms_fn(fn) \ -- cgit v1.2.3 From a344d6778a98e4c19ac871f369e399e6356edcb3 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 12 Jun 2014 22:24:31 +0200 Subject: mac80211: allow drivers to support NL80211_SCAN_FLAG_RANDOM_ADDR Allow drivers to support NL80211_SCAN_FLAG_RANDOM_ADDR with software based scanning and generate a random MAC address for them for every scan request with the flag. Signed-off-by: Johannes Berg --- drivers/net/wireless/ath/ath5k/mac80211-ops.c | 6 ++- drivers/net/wireless/ath/ath9k/channel.c | 2 +- drivers/net/wireless/ath/ath9k/htc_drv_main.c | 7 +++- drivers/net/wireless/ath/ath9k/main.c | 7 +++- drivers/net/wireless/ath/wcn36xx/main.c | 7 +++- drivers/net/wireless/b43/main.c | 7 +++- .../net/wireless/brcm80211/brcmsmac/mac80211_if.c | 7 +++- drivers/net/wireless/cw1200/scan.c | 2 +- drivers/net/wireless/mac80211_hwsim.c | 9 ++-- drivers/net/wireless/mwl8k.c | 7 +++- drivers/net/wireless/rt2x00/rt2x00.h | 7 +++- drivers/net/wireless/rt2x00/rt2x00mac.c | 7 +++- drivers/net/wireless/rtlwifi/core.c | 7 +++- drivers/net/wireless/ti/wl1251/main.c | 2 +- drivers/net/wireless/ti/wlcore/cmd.c | 2 +- drivers/staging/vt6656/main_usb.c | 7 +++- include/net/mac80211.h | 15 ++++--- net/mac80211/driver-ops.h | 15 ++++--- net/mac80211/ieee80211_i.h | 7 +++- net/mac80211/mlme.c | 8 ++-- net/mac80211/scan.c | 48 +++++++++++++++++----- net/mac80211/trace.h | 31 +++++++++++--- net/mac80211/tx.c | 9 ++-- net/mac80211/util.c | 12 +++--- 24 files changed, 166 insertions(+), 72 deletions(-) (limited to 'drivers/net/wireless/brcm80211') diff --git a/drivers/net/wireless/ath/ath5k/mac80211-ops.c b/drivers/net/wireless/ath/ath5k/mac80211-ops.c index ab2709a43768..19eab2a69ad5 100644 --- a/drivers/net/wireless/ath/ath5k/mac80211-ops.c +++ b/drivers/net/wireless/ath/ath5k/mac80211-ops.c @@ -547,7 +547,9 @@ ath5k_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, static void -ath5k_sw_scan_start(struct ieee80211_hw *hw) +ath5k_sw_scan_start(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + const u8 *mac_addr) { struct ath5k_hw *ah = hw->priv; if (!ah->assoc) @@ -556,7 +558,7 @@ ath5k_sw_scan_start(struct ieee80211_hw *hw) static void -ath5k_sw_scan_complete(struct ieee80211_hw *hw) +ath5k_sw_scan_complete(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { struct ath5k_hw *ah = hw->priv; ath5k_hw_set_ledstate(ah, ah->assoc ? diff --git a/drivers/net/wireless/ath/ath9k/channel.c b/drivers/net/wireless/ath/ath9k/channel.c index 794d52016437..206665059d66 100644 --- a/drivers/net/wireless/ath/ath9k/channel.c +++ b/drivers/net/wireless/ath/ath9k/channel.c @@ -963,7 +963,7 @@ static void ath_scan_send_probe(struct ath_softc *sc, struct ieee80211_tx_info *info; int band = sc->offchannel.chan.chandef.chan->band; - skb = ieee80211_probereq_get(sc->hw, vif, + skb = ieee80211_probereq_get(sc->hw, vif->addr, ssid->ssid, ssid->ssid_len, req->ie_len); if (!skb) return; diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_main.c b/drivers/net/wireless/ath/ath9k/htc_drv_main.c index c7d12efaa86a..92d5a6c5a225 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_main.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_main.c @@ -1691,7 +1691,9 @@ static int ath9k_htc_ampdu_action(struct ieee80211_hw *hw, return ret; } -static void ath9k_htc_sw_scan_start(struct ieee80211_hw *hw) +static void ath9k_htc_sw_scan_start(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + const u8 *mac_addr) { struct ath9k_htc_priv *priv = hw->priv; struct ath_common *common = ath9k_hw_common(priv->ah); @@ -1705,7 +1707,8 @@ static void ath9k_htc_sw_scan_start(struct ieee80211_hw *hw) mutex_unlock(&priv->mutex); } -static void ath9k_htc_sw_scan_complete(struct ieee80211_hw *hw) +static void ath9k_htc_sw_scan_complete(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) { struct ath9k_htc_priv *priv = hw->priv; struct ath_common *common = ath9k_hw_common(priv->ah); diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index 5f16630f26ce..027ad715ffb2 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -2180,14 +2180,17 @@ static int ath9k_get_antenna(struct ieee80211_hw *hw, u32 *tx_ant, u32 *rx_ant) return 0; } -static void ath9k_sw_scan_start(struct ieee80211_hw *hw) +static void ath9k_sw_scan_start(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + const u8 *mac_addr) { struct ath_softc *sc = hw->priv; struct ath_common *common = ath9k_hw_common(sc->sc_ah); set_bit(ATH_OP_SCANNING, &common->op_flags); } -static void ath9k_sw_scan_complete(struct ieee80211_hw *hw) +static void ath9k_sw_scan_complete(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) { struct ath_softc *sc = hw->priv; struct ath_common *common = ath9k_hw_common(sc->sc_ah); diff --git a/drivers/net/wireless/ath/wcn36xx/main.c b/drivers/net/wireless/ath/wcn36xx/main.c index b71d2b33532d..267c35d1f699 100644 --- a/drivers/net/wireless/ath/wcn36xx/main.c +++ b/drivers/net/wireless/ath/wcn36xx/main.c @@ -494,7 +494,9 @@ out: return ret; } -static void wcn36xx_sw_scan_start(struct ieee80211_hw *hw) +static void wcn36xx_sw_scan_start(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + const u8 *mac_addr) { struct wcn36xx *wcn = hw->priv; @@ -502,7 +504,8 @@ static void wcn36xx_sw_scan_start(struct ieee80211_hw *hw) wcn36xx_smd_start_scan(wcn); } -static void wcn36xx_sw_scan_complete(struct ieee80211_hw *hw) +static void wcn36xx_sw_scan_complete(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) { struct wcn36xx *wcn = hw->priv; diff --git a/drivers/net/wireless/b43/main.c b/drivers/net/wireless/b43/main.c index 5d4173ee55bc..47731cb0d815 100644 --- a/drivers/net/wireless/b43/main.c +++ b/drivers/net/wireless/b43/main.c @@ -5110,7 +5110,9 @@ static void b43_op_sta_notify(struct ieee80211_hw *hw, B43_WARN_ON(!vif || wl->vif != vif); } -static void b43_op_sw_scan_start_notifier(struct ieee80211_hw *hw) +static void b43_op_sw_scan_start_notifier(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + const u8 *mac_addr) { struct b43_wl *wl = hw_to_b43_wl(hw); struct b43_wldev *dev; @@ -5124,7 +5126,8 @@ static void b43_op_sw_scan_start_notifier(struct ieee80211_hw *hw) mutex_unlock(&wl->mutex); } -static void b43_op_sw_scan_complete_notifier(struct ieee80211_hw *hw) +static void b43_op_sw_scan_complete_notifier(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) { struct b43_wl *wl = hw_to_b43_wl(hw); struct b43_wldev *dev; diff --git a/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c b/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c index 43c71bfaa474..f95b52442281 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c +++ b/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c @@ -764,7 +764,9 @@ brcms_ops_configure_filter(struct ieee80211_hw *hw, return; } -static void brcms_ops_sw_scan_start(struct ieee80211_hw *hw) +static void brcms_ops_sw_scan_start(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + const u8 *mac_addr) { struct brcms_info *wl = hw->priv; spin_lock_bh(&wl->lock); @@ -773,7 +775,8 @@ static void brcms_ops_sw_scan_start(struct ieee80211_hw *hw) return; } -static void brcms_ops_sw_scan_complete(struct ieee80211_hw *hw) +static void brcms_ops_sw_scan_complete(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) { struct brcms_info *wl = hw->priv; spin_lock_bh(&wl->lock); diff --git a/drivers/net/wireless/cw1200/scan.c b/drivers/net/wireless/cw1200/scan.c index b2fb6c632092..f2e276faca70 100644 --- a/drivers/net/wireless/cw1200/scan.c +++ b/drivers/net/wireless/cw1200/scan.c @@ -78,7 +78,7 @@ int cw1200_hw_scan(struct ieee80211_hw *hw, if (req->n_ssids > WSM_SCAN_MAX_NUM_OF_SSIDS) return -EINVAL; - frame.skb = ieee80211_probereq_get(hw, priv->vif, NULL, 0, + frame.skb = ieee80211_probereq_get(hw, priv->vif->addr, NULL, 0, req->ie_len); if (!frame.skb) return -ENOMEM; diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index 58f11bb0896f..6daaad595ea4 100644 --- a/drivers/net/wireless/mac80211_hwsim.c +++ b/drivers/net/wireless/mac80211_hwsim.c @@ -1802,7 +1802,7 @@ static void hw_scan_work(struct work_struct *work) struct sk_buff *probe; probe = ieee80211_probereq_get(hwsim->hw, - hwsim->hw_scan_vif, + hwsim->hw_scan_vif->addr, req->ssids[i].ssid, req->ssids[i].ssid_len, req->ie_len); @@ -1866,7 +1866,9 @@ static void mac80211_hwsim_cancel_hw_scan(struct ieee80211_hw *hw, mutex_unlock(&hwsim->mutex); } -static void mac80211_hwsim_sw_scan(struct ieee80211_hw *hw) +static void mac80211_hwsim_sw_scan(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + const u8 *mac_addr) { struct mac80211_hwsim_data *hwsim = hw->priv; @@ -1884,7 +1886,8 @@ out: mutex_unlock(&hwsim->mutex); } -static void mac80211_hwsim_sw_scan_complete(struct ieee80211_hw *hw) +static void mac80211_hwsim_sw_scan_complete(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) { struct mac80211_hwsim_data *hwsim = hw->priv; diff --git a/drivers/net/wireless/mwl8k.c b/drivers/net/wireless/mwl8k.c index ef1104476bd8..b8d1e04aa9b9 100644 --- a/drivers/net/wireless/mwl8k.c +++ b/drivers/net/wireless/mwl8k.c @@ -5548,7 +5548,9 @@ mwl8k_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, return rc; } -static void mwl8k_sw_scan_start(struct ieee80211_hw *hw) +static void mwl8k_sw_scan_start(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + const u8 *mac_addr) { struct mwl8k_priv *priv = hw->priv; u8 tmp; @@ -5565,7 +5567,8 @@ static void mwl8k_sw_scan_start(struct ieee80211_hw *hw) priv->sw_scan_start = true; } -static void mwl8k_sw_scan_complete(struct ieee80211_hw *hw) +static void mwl8k_sw_scan_complete(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) { struct mwl8k_priv *priv = hw->priv; u8 tmp; diff --git a/drivers/net/wireless/rt2x00/rt2x00.h b/drivers/net/wireless/rt2x00/rt2x00.h index d13f25cd70d5..1ff81afb672c 100644 --- a/drivers/net/wireless/rt2x00/rt2x00.h +++ b/drivers/net/wireless/rt2x00/rt2x00.h @@ -1437,8 +1437,11 @@ int rt2x00mac_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_sta *sta); int rt2x00mac_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_sta *sta); -void rt2x00mac_sw_scan_start(struct ieee80211_hw *hw); -void rt2x00mac_sw_scan_complete(struct ieee80211_hw *hw); +void rt2x00mac_sw_scan_start(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + const u8 *mac_addr); +void rt2x00mac_sw_scan_complete(struct ieee80211_hw *hw, + struct ieee80211_vif *vif); int rt2x00mac_get_stats(struct ieee80211_hw *hw, struct ieee80211_low_level_stats *stats); void rt2x00mac_bss_info_changed(struct ieee80211_hw *hw, diff --git a/drivers/net/wireless/rt2x00/rt2x00mac.c b/drivers/net/wireless/rt2x00/rt2x00mac.c index ad6e5a8d1e10..cb40245a0695 100644 --- a/drivers/net/wireless/rt2x00/rt2x00mac.c +++ b/drivers/net/wireless/rt2x00/rt2x00mac.c @@ -568,7 +568,9 @@ int rt2x00mac_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif, } EXPORT_SYMBOL_GPL(rt2x00mac_sta_remove); -void rt2x00mac_sw_scan_start(struct ieee80211_hw *hw) +void rt2x00mac_sw_scan_start(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + const u8 *mac_addr) { struct rt2x00_dev *rt2x00dev = hw->priv; set_bit(DEVICE_STATE_SCANNING, &rt2x00dev->flags); @@ -576,7 +578,8 @@ void rt2x00mac_sw_scan_start(struct ieee80211_hw *hw) } EXPORT_SYMBOL_GPL(rt2x00mac_sw_scan_start); -void rt2x00mac_sw_scan_complete(struct ieee80211_hw *hw) +void rt2x00mac_sw_scan_complete(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) { struct rt2x00_dev *rt2x00dev = hw->priv; clear_bit(DEVICE_STATE_SCANNING, &rt2x00dev->flags); diff --git a/drivers/net/wireless/rtlwifi/core.c b/drivers/net/wireless/rtlwifi/core.c index f6179bc06086..884d90526f9e 100644 --- a/drivers/net/wireless/rtlwifi/core.c +++ b/drivers/net/wireless/rtlwifi/core.c @@ -1361,7 +1361,9 @@ static int rtl_op_ampdu_action(struct ieee80211_hw *hw, return 0; } -static void rtl_op_sw_scan_start(struct ieee80211_hw *hw) +static void rtl_op_sw_scan_start(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + const u8 *mac_addr) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); @@ -1396,7 +1398,8 @@ static void rtl_op_sw_scan_start(struct ieee80211_hw *hw) rtlpriv->cfg->ops->scan_operation_backup(hw, SCAN_OPT_BACKUP_BAND0); } -static void rtl_op_sw_scan_complete(struct ieee80211_hw *hw) +static void rtl_op_sw_scan_complete(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); diff --git a/drivers/net/wireless/ti/wl1251/main.c b/drivers/net/wireless/ti/wl1251/main.c index 38234851457e..0b30a7b4d663 100644 --- a/drivers/net/wireless/ti/wl1251/main.c +++ b/drivers/net/wireless/ti/wl1251/main.c @@ -1029,7 +1029,7 @@ static int wl1251_op_hw_scan(struct ieee80211_hw *hw, goto out_sleep; } - skb = ieee80211_probereq_get(wl->hw, wl->vif, ssid, ssid_len, + skb = ieee80211_probereq_get(wl->hw, wl->vif->addr, ssid, ssid_len, req->ie_len); if (!skb) { ret = -ENOMEM; diff --git a/drivers/net/wireless/ti/wlcore/cmd.c b/drivers/net/wireless/ti/wlcore/cmd.c index dd2e448c3e2b..b82661962d33 100644 --- a/drivers/net/wireless/ti/wlcore/cmd.c +++ b/drivers/net/wireless/ti/wlcore/cmd.c @@ -1145,7 +1145,7 @@ int wl12xx_cmd_build_probe_req(struct wl1271 *wl, struct wl12xx_vif *wlvif, wl1271_debug(DEBUG_SCAN, "build probe request band %d", band); - skb = ieee80211_probereq_get(wl->hw, vif, ssid, ssid_len, + skb = ieee80211_probereq_get(wl->hw, vif->addr, ssid, ssid_len, ie0_len + ie1_len); if (!skb) { ret = -ENOMEM; diff --git a/drivers/staging/vt6656/main_usb.c b/drivers/staging/vt6656/main_usb.c index 2fbff907ce8a..dbc311c3dc37 100644 --- a/drivers/staging/vt6656/main_usb.c +++ b/drivers/staging/vt6656/main_usb.c @@ -856,7 +856,9 @@ static int vnt_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, return 0; } -static void vnt_sw_scan_start(struct ieee80211_hw *hw) +static void vnt_sw_scan_start(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + const u8 *addr) { struct vnt_private *priv = hw->priv; @@ -865,7 +867,8 @@ static void vnt_sw_scan_start(struct ieee80211_hw *hw) vnt_update_pre_ed_threshold(priv, true); } -static void vnt_sw_scan_complete(struct ieee80211_hw *hw) +static void vnt_sw_scan_complete(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) { struct vnt_private *priv = hw->priv; diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 59166a115aff..7b889e3a2647 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -2622,7 +2622,9 @@ enum ieee80211_reconfig_type { * * @sw_scan_start: Notifier function that is called just before a software scan * is started. Can be NULL, if the driver doesn't need this notification. - * The callback can sleep. + * The mac_addr parameter allows supporting NL80211_SCAN_FLAG_RANDOM_ADDR, + * the driver may set the NL80211_FEATURE_SCAN_RANDOM_MAC_ADDR flag if it + * can use this parameter. The callback can sleep. * * @sw_scan_complete: Notifier function that is called just after a * software scan finished. Can be NULL, if the driver doesn't need @@ -3016,8 +3018,11 @@ struct ieee80211_ops { struct ieee80211_scan_ies *ies); int (*sched_scan_stop)(struct ieee80211_hw *hw, struct ieee80211_vif *vif); - void (*sw_scan_start)(struct ieee80211_hw *hw); - void (*sw_scan_complete)(struct ieee80211_hw *hw); + void (*sw_scan_start)(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + const u8 *mac_addr); + void (*sw_scan_complete)(struct ieee80211_hw *hw, + struct ieee80211_vif *vif); int (*get_stats)(struct ieee80211_hw *hw, struct ieee80211_low_level_stats *stats); void (*get_tkip_seq)(struct ieee80211_hw *hw, u8 hw_key_idx, @@ -3820,7 +3825,7 @@ struct sk_buff *ieee80211_nullfunc_get(struct ieee80211_hw *hw, /** * ieee80211_probereq_get - retrieve a Probe Request template * @hw: pointer obtained from ieee80211_alloc_hw(). - * @vif: &struct ieee80211_vif pointer from the add_interface callback. + * @src_addr: source MAC address * @ssid: SSID buffer * @ssid_len: length of SSID * @tailroom: tailroom to reserve at end of SKB for IEs @@ -3831,7 +3836,7 @@ struct sk_buff *ieee80211_nullfunc_get(struct ieee80211_hw *hw, * Return: The Probe Request template. %NULL on error. */ struct sk_buff *ieee80211_probereq_get(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, + const u8 *src_addr, const u8 *ssid, size_t ssid_len, size_t tailroom); diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h index ba0d2cb5df12..5f5fc3f3ee7c 100644 --- a/net/mac80211/driver-ops.h +++ b/net/mac80211/driver-ops.h @@ -380,23 +380,26 @@ static inline int drv_sched_scan_stop(struct ieee80211_local *local, return ret; } -static inline void drv_sw_scan_start(struct ieee80211_local *local) +static inline void drv_sw_scan_start(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata, + const u8 *mac_addr) { might_sleep(); - trace_drv_sw_scan_start(local); + trace_drv_sw_scan_start(local, sdata, mac_addr); if (local->ops->sw_scan_start) - local->ops->sw_scan_start(&local->hw); + local->ops->sw_scan_start(&local->hw, &sdata->vif, mac_addr); trace_drv_return_void(local); } -static inline void drv_sw_scan_complete(struct ieee80211_local *local) +static inline void drv_sw_scan_complete(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata) { might_sleep(); - trace_drv_sw_scan_complete(local); + trace_drv_sw_scan_complete(local, sdata); if (local->ops->sw_scan_complete) - local->ops->sw_scan_complete(&local->hw); + local->ops->sw_scan_complete(&local->hw, &sdata->vif); trace_drv_return_void(local); } diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index dd27180060b9..cf95d033bcbf 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -1249,6 +1249,7 @@ struct ieee80211_local { struct work_struct sched_scan_stopped_work; struct ieee80211_sub_if_data __rcu *sched_scan_sdata; struct cfg80211_sched_scan_request __rcu *sched_scan_req; + u8 scan_addr[ETH_ALEN]; unsigned long leave_oper_channel_time; enum mac80211_scan_state next_scan_state; @@ -1901,12 +1902,14 @@ int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer, u8 bands_used, u32 *rate_masks, struct cfg80211_chan_def *chandef); struct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata, - u8 *dst, u32 ratemask, + const u8 *src, const u8 *dst, + u32 ratemask, struct ieee80211_channel *chan, const u8 *ssid, size_t ssid_len, const u8 *ie, size_t ie_len, bool directed); -void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst, +void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, + const u8 *src, const u8 *dst, const u8 *ssid, size_t ssid_len, const u8 *ie, size_t ie_len, u32 ratemask, bool directed, u32 tx_flags, diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 45490a202d9c..d29589a09065 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -2225,7 +2225,8 @@ static void ieee80211_mgd_probe_ap_send(struct ieee80211_sub_if_data *sdata) else ssid_len = ssid[1]; - ieee80211_send_probe_req(sdata, dst, ssid + 2, ssid_len, NULL, + ieee80211_send_probe_req(sdata, sdata->vif.addr, NULL, + ssid + 2, ssid_len, NULL, 0, (u32) -1, true, 0, ifmgd->associated->channel, false); rcu_read_unlock(); @@ -2328,7 +2329,7 @@ struct sk_buff *ieee80211_ap_probereq_get(struct ieee80211_hw *hw, else ssid_len = ssid[1]; - skb = ieee80211_build_probe_req(sdata, cbss->bssid, + skb = ieee80211_build_probe_req(sdata, sdata->vif.addr, cbss->bssid, (u32) -1, cbss->channel, ssid + 2, ssid_len, NULL, 0, true); @@ -3649,7 +3650,8 @@ static int ieee80211_probe_auth(struct ieee80211_sub_if_data *sdata) * Direct probe is sent to broadcast address as some APs * will not answer to direct packet in unassociated state. */ - ieee80211_send_probe_req(sdata, NULL, ssidie + 2, ssidie[1], + ieee80211_send_probe_req(sdata, sdata->vif.addr, NULL, + ssidie + 2, ssidie[1], NULL, 0, (u32) -1, true, 0, auth_data->bss->channel, false); rcu_read_unlock(); diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c index e75e64b8042c..ae842678b629 100644 --- a/net/mac80211/scan.c +++ b/net/mac80211/scan.c @@ -184,9 +184,21 @@ void ieee80211_scan_rx(struct ieee80211_local *local, struct sk_buff *skb) return; if (ieee80211_is_probe_resp(mgmt->frame_control)) { - /* ignore ProbeResp to foreign address */ - if ((!sdata1 || !ether_addr_equal(mgmt->da, sdata1->vif.addr)) && - (!sdata2 || !ether_addr_equal(mgmt->da, sdata2->vif.addr))) + struct cfg80211_scan_request *scan_req; + struct cfg80211_sched_scan_request *sched_scan_req; + + scan_req = rcu_dereference(local->scan_req); + sched_scan_req = rcu_dereference(local->sched_scan_req); + + /* ignore ProbeResp to foreign address unless scanning + * with randomised address + */ + if (!(sdata1 && + (ether_addr_equal(mgmt->da, sdata1->vif.addr) || + scan_req->flags & NL80211_SCAN_FLAG_RANDOM_ADDR)) && + !(sdata2 && + (ether_addr_equal(mgmt->da, sdata2->vif.addr) || + sched_scan_req->flags & NL80211_SCAN_FLAG_RANDOM_ADDR))) return; elements = mgmt->u.probe_resp.variable; @@ -284,6 +296,9 @@ static bool ieee80211_prep_hw_scan(struct ieee80211_local *local) bands_used, req->rates, &chandef); local->hw_scan_req->req.ie_len = ielen; local->hw_scan_req->req.no_cck = req->no_cck; + ether_addr_copy(local->hw_scan_req->req.mac_addr, req->mac_addr); + ether_addr_copy(local->hw_scan_req->req.mac_addr_mask, + req->mac_addr_mask); return true; } @@ -294,6 +309,7 @@ static void __ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted) bool hw_scan = local->ops->hw_scan; bool was_scanning = local->scanning; struct cfg80211_scan_request *scan_req; + struct ieee80211_sub_if_data *scan_sdata; lockdep_assert_held(&local->mtx); @@ -332,6 +348,9 @@ static void __ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted) if (scan_req != local->int_scan_req) cfg80211_scan_done(scan_req, aborted); RCU_INIT_POINTER(local->scan_req, NULL); + + scan_sdata = rcu_dereference_protected(local->scan_sdata, + lockdep_is_held(&local->mtx)); RCU_INIT_POINTER(local->scan_sdata, NULL); local->scanning = 0; @@ -342,7 +361,7 @@ static void __ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted) if (!hw_scan) { ieee80211_configure_filter(local); - drv_sw_scan_complete(local); + drv_sw_scan_complete(local, scan_sdata); ieee80211_offchannel_return(local); } @@ -368,7 +387,8 @@ void ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted) } EXPORT_SYMBOL(ieee80211_scan_completed); -static int ieee80211_start_sw_scan(struct ieee80211_local *local) +static int ieee80211_start_sw_scan(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata) { /* Software scan is not supported in multi-channel cases */ if (local->use_chanctx) @@ -387,7 +407,7 @@ static int ieee80211_start_sw_scan(struct ieee80211_local *local) * nullfunc frames and probe requests will be dropped in * ieee80211_tx_h_check_assoc(). */ - drv_sw_scan_start(local); + drv_sw_scan_start(local, sdata, local->scan_addr); local->leave_oper_channel_time = jiffies; local->next_scan_state = SCAN_DECISION; @@ -463,7 +483,7 @@ static void ieee80211_scan_state_send_probe(struct ieee80211_local *local, for (i = 0; i < scan_req->n_ssids; i++) ieee80211_send_probe_req( - sdata, NULL, + sdata, local->scan_addr, NULL, scan_req->ssids[i].ssid, scan_req->ssids[i].ssid_len, scan_req->ie, scan_req->ie_len, scan_req->rates[band], false, @@ -543,6 +563,13 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata, rcu_assign_pointer(local->scan_req, req); rcu_assign_pointer(local->scan_sdata, sdata); + if (req->flags & NL80211_SCAN_FLAG_RANDOM_ADDR) + get_random_mask_addr(local->scan_addr, + req->mac_addr, + req->mac_addr_mask); + else + memcpy(local->scan_addr, sdata->vif.addr, ETH_ALEN); + if (local->ops->hw_scan) { __set_bit(SCAN_HW_SCANNING, &local->scanning); } else if ((req->n_channels == 1) && @@ -559,7 +586,7 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata, /* Notify driver scan is starting, keep order of operations * same as normal software scan, in case that matters. */ - drv_sw_scan_start(local); + drv_sw_scan_start(local, sdata, local->scan_addr); ieee80211_configure_filter(local); /* accept probe-responses */ @@ -589,8 +616,9 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata, if (local->ops->hw_scan) { WARN_ON(!ieee80211_prep_hw_scan(local)); rc = drv_hw_scan(local, sdata, local->hw_scan_req); - } else - rc = ieee80211_start_sw_scan(local); + } else { + rc = ieee80211_start_sw_scan(local, sdata); + } if (rc) { kfree(local->hw_scan_req); diff --git a/net/mac80211/trace.h b/net/mac80211/trace.h index 7f76e2f25744..eb91505eb43e 100644 --- a/net/mac80211/trace.h +++ b/net/mac80211/trace.h @@ -596,14 +596,33 @@ DEFINE_EVENT(local_sdata_evt, drv_sched_scan_stop, TP_ARGS(local, sdata) ); -DEFINE_EVENT(local_only_evt, drv_sw_scan_start, - TP_PROTO(struct ieee80211_local *local), - TP_ARGS(local) +TRACE_EVENT(drv_sw_scan_start, + TP_PROTO(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata, + const u8 *mac_addr), + + TP_ARGS(local, sdata, mac_addr), + + TP_STRUCT__entry( + LOCAL_ENTRY + VIF_ENTRY + __array(char, mac_addr, ETH_ALEN) + ), + + TP_fast_assign( + LOCAL_ASSIGN; + VIF_ASSIGN; + memcpy(__entry->mac_addr, mac_addr, ETH_ALEN); + ), + + TP_printk(LOCAL_PR_FMT ", " VIF_PR_FMT ", addr:%pM", + LOCAL_PR_ARG, VIF_PR_ARG, __entry->mac_addr) ); -DEFINE_EVENT(local_only_evt, drv_sw_scan_complete, - TP_PROTO(struct ieee80211_local *local), - TP_ARGS(local) +DEFINE_EVENT(local_sdata_evt, drv_sw_scan_complete, + TP_PROTO(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata), + TP_ARGS(local, sdata) ); TRACE_EVENT(drv_get_stats, diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 0cb41d1a1f20..66ddbbeccd20 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -2961,19 +2961,16 @@ struct sk_buff *ieee80211_nullfunc_get(struct ieee80211_hw *hw, EXPORT_SYMBOL(ieee80211_nullfunc_get); struct sk_buff *ieee80211_probereq_get(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, + const u8 *src_addr, const u8 *ssid, size_t ssid_len, size_t tailroom) { - struct ieee80211_sub_if_data *sdata; - struct ieee80211_local *local; + struct ieee80211_local *local = hw_to_local(hw); struct ieee80211_hdr_3addr *hdr; struct sk_buff *skb; size_t ie_ssid_len; u8 *pos; - sdata = vif_to_sdata(vif); - local = sdata->local; ie_ssid_len = 2 + ssid_len; skb = dev_alloc_skb(local->hw.extra_tx_headroom + sizeof(*hdr) + @@ -2988,7 +2985,7 @@ struct sk_buff *ieee80211_probereq_get(struct ieee80211_hw *hw, hdr->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_PROBE_REQ); eth_broadcast_addr(hdr->addr1); - memcpy(hdr->addr2, vif->addr, ETH_ALEN); + memcpy(hdr->addr2, src_addr, ETH_ALEN); eth_broadcast_addr(hdr->addr3); pos = skb_put(skb, ie_ssid_len); diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 0ad534abc008..bb9664cb8831 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -1523,7 +1523,8 @@ int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer, }; struct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata, - u8 *dst, u32 ratemask, + const u8 *src, const u8 *dst, + u32 ratemask, struct ieee80211_channel *chan, const u8 *ssid, size_t ssid_len, const u8 *ie, size_t ie_len, @@ -1548,8 +1549,8 @@ struct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata, else chandef.chan = chan; - skb = ieee80211_probereq_get(&local->hw, &sdata->vif, - ssid, ssid_len, 100 + ie_len); + skb = ieee80211_probereq_get(&local->hw, src, ssid, ssid_len, + 100 + ie_len); if (!skb) return NULL; @@ -1571,7 +1572,8 @@ struct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata, return skb; } -void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst, +void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, + const u8 *src, const u8 *dst, const u8 *ssid, size_t ssid_len, const u8 *ie, size_t ie_len, u32 ratemask, bool directed, u32 tx_flags, @@ -1579,7 +1581,7 @@ void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst, { struct sk_buff *skb; - skb = ieee80211_build_probe_req(sdata, dst, ratemask, channel, + skb = ieee80211_build_probe_req(sdata, src, dst, ratemask, channel, ssid, ssid_len, ie, ie_len, directed); if (skb) { -- cgit v1.2.3 From ac96ce83ff21c9338afa0e81baabd902d9350015 Mon Sep 17 00:00:00 2001 From: Markus Elfring Date: Thu, 20 Nov 2014 16:42:51 +0100 Subject: net: brcm80211: Deletion of unnecessary checks before two function calls The functions brcmu_pkt_buf_free_skb() and release_firmware() test whether their argument is NULL and then return immediately. Thus the test around the call is not needed. This issue was detected by using the Coccinelle software. Signed-off-by: Markus Elfring Acked-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/firmware.c | 3 +-- drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c | 3 +-- drivers/net/wireless/brcm80211/brcmsmac/main.c | 3 +-- 3 files changed, 3 insertions(+), 6 deletions(-) (limited to 'drivers/net/wireless/brcm80211') diff --git a/drivers/net/wireless/brcm80211/brcmfmac/firmware.c b/drivers/net/wireless/brcm80211/brcmfmac/firmware.c index 0f157f151282..1ff787d1a36b 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/firmware.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/firmware.c @@ -262,8 +262,7 @@ static void brcmf_fw_request_nvram_done(const struct firmware *fw, void *ctx) fail: brcmf_dbg(TRACE, "failed: dev=%s\n", dev_name(fwctx->dev)); - if (fwctx->code) - release_firmware(fwctx->code); + release_firmware(fwctx->code); device_release_driver(fwctx->dev); kfree(fwctx); } diff --git a/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c b/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c index 02d39ce8dbca..9f783db34ae5 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c @@ -518,8 +518,7 @@ static int brcmf_msgbuf_query_dcmd(struct brcmf_pub *drvr, int ifidx, memcpy(buf, skb->data, (len < msgbuf->ioctl_resp_ret_len) ? len : msgbuf->ioctl_resp_ret_len); } - if (skb) - brcmu_pkt_buf_free_skb(skb); + brcmu_pkt_buf_free_skb(skb); return msgbuf->ioctl_resp_status; } diff --git a/drivers/net/wireless/brcm80211/brcmsmac/main.c b/drivers/net/wireless/brcm80211/brcmsmac/main.c index bc9be78faafa..738cfaca1e0f 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/main.c +++ b/drivers/net/wireless/brcm80211/brcmsmac/main.c @@ -1009,8 +1009,7 @@ brcms_c_dotxstatus(struct brcms_c_info *wlc, struct tx_status *txs) if (txh) trace_brcms_txdesc(&wlc->hw->d11core->dev, txh, sizeof(*txh)); - if (p) - brcmu_pkt_buf_free_skb(p); + brcmu_pkt_buf_free_skb(p); } if (dma && queue < NFIFO) { -- cgit v1.2.3 From c15d789e3cfa94f90f86766bb2302dd643ef4621 Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Thu, 20 Nov 2014 22:26:59 +0100 Subject: brcmfmac: fix static checker warning in pmklist handling The patch fixes a static checker warning: drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c:2965 brcmf_cfg80211_set_pmksa() warn: can 'pmkid_len' be negative? The answer to the question above is likely no so changing its type to unsigned is sufficient. Reported-by: Dan Carpenter Reviewed-by: Hante Meuleman Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Daniel (Deognyoun) Kim Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'drivers/net/wireless/brcm80211') diff --git a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c index f8a9dfa657ba..d1928de895f9 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c @@ -2932,7 +2932,7 @@ brcmf_update_pmklist(struct net_device *ndev, struct brcmf_cfg80211_pmk_list *pmk_list, s32 err) { int i, j; - int pmkid_len; + u32 pmkid_len; pmkid_len = le32_to_cpu(pmk_list->pmkids.npmkid); @@ -2960,8 +2960,7 @@ brcmf_cfg80211_set_pmksa(struct wiphy *wiphy, struct net_device *ndev, struct brcmf_if *ifp = netdev_priv(ndev); struct pmkid_list *pmkids = &cfg->pmk_list->pmkids; s32 err = 0; - int i; - int pmkid_len; + u32 pmkid_len, i; brcmf_dbg(TRACE, "Enter\n"); if (!check_vif_up(ifp->vif)) @@ -3000,7 +2999,7 @@ brcmf_cfg80211_del_pmksa(struct wiphy *wiphy, struct net_device *ndev, struct brcmf_if *ifp = netdev_priv(ndev); struct pmkid_list pmkid; s32 err = 0; - int i, pmkid_len; + u32 pmkid_len, i; brcmf_dbg(TRACE, "Enter\n"); if (!check_vif_up(ifp->vif)) -- cgit v1.2.3 From 4f3fff148679d5850994e53c57eb798cd201c88c Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Thu, 20 Nov 2014 22:27:02 +0100 Subject: brcmfmac: correct .disconnect() callback while connecting When the driver has sent a join iovar to the firmware it waits for the events to report result of the connection. However, the wpa_supplicant will request a .disconnect() after a timeout. So upon calling .disconnect() the interface state may still be CONNECTING. Clear the CONNECTING bit as well. Reviewed-by: Hante Meuleman Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/net/wireless/brcm80211') diff --git a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c index d1928de895f9..0ea312546635 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c @@ -1815,6 +1815,7 @@ brcmf_cfg80211_disconnect(struct wiphy *wiphy, struct net_device *ndev, return -EIO; 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); memcpy(&scbval.ea, &profile->bssid, ETH_ALEN); -- cgit v1.2.3 From 56f0b750fca748b8d13c883d9e947178f3d0162c Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Tue, 25 Nov 2014 20:55:53 +0100 Subject: brcmutil: add helper function to format board revision The board revision that is available in hardware can be translated so it matches the labelling on the board. This is accomplished by this helper function. Reviewed-by: Hante Meuleman Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmutil/utils.c | 16 ++++++++++++++++ drivers/net/wireless/brcm80211/include/brcmu_utils.h | 2 ++ 2 files changed, 18 insertions(+) (limited to 'drivers/net/wireless/brcm80211') diff --git a/drivers/net/wireless/brcm80211/brcmutil/utils.c b/drivers/net/wireless/brcm80211/brcmutil/utils.c index 0f7e1c7b6f58..906e89ddf319 100644 --- a/drivers/net/wireless/brcm80211/brcmutil/utils.c +++ b/drivers/net/wireless/brcm80211/brcmutil/utils.c @@ -261,6 +261,21 @@ struct sk_buff *brcmu_pktq_mdeq(struct pktq *pq, uint prec_bmp, } EXPORT_SYMBOL(brcmu_pktq_mdeq); +/* Produce a human-readable string for boardrev */ +char *brcmu_boardrev_str(u32 brev, char *buf) +{ + char c; + + if (brev < 0x100) { + snprintf(buf, 8, "%d.%d", (brev & 0xf0) >> 4, brev & 0xf); + } else { + c = (brev & 0xf000) == 0x1000 ? 'P' : 'A'; + snprintf(buf, 8, "%c%03x", c, brev & 0xfff); + } + return buf; +} +EXPORT_SYMBOL(brcmu_boardrev_str); + #if defined(DEBUG) /* pretty hex print a pkt buffer chain */ void brcmu_prpkt(const char *msg, struct sk_buff *p0) @@ -292,4 +307,5 @@ void brcmu_dbg_hex_dump(const void *data, size_t size, const char *fmt, ...) print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, data, size); } EXPORT_SYMBOL(brcmu_dbg_hex_dump); + #endif /* defined(DEBUG) */ diff --git a/drivers/net/wireless/brcm80211/include/brcmu_utils.h b/drivers/net/wireless/brcm80211/include/brcmu_utils.h index 8ba445b3fd72..a043e29f07e2 100644 --- a/drivers/net/wireless/brcm80211/include/brcmu_utils.h +++ b/drivers/net/wireless/brcm80211/include/brcmu_utils.h @@ -218,4 +218,6 @@ void brcmu_dbg_hex_dump(const void *data, size_t size, const char *fmt, ...) } #endif +char *brcmu_boardrev_str(u32 brev, char *buf); + #endif /* _BRCMU_UTILS_H_ */ -- cgit v1.2.3 From 4cc2fa0036530fcd07446ab1ba690c9c23fe603d Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Tue, 25 Nov 2014 20:55:54 +0100 Subject: brcmsmac: extend hardware info shown in debugfs The hardware info now also include radio and phy information, which can be helpful in debugging issues. Reviewed-by: Hante Meuleman Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmsmac/debug.c | 40 +++++++++++++++++-------- 1 file changed, 27 insertions(+), 13 deletions(-) (limited to 'drivers/net/wireless/brcm80211') diff --git a/drivers/net/wireless/brcm80211/brcmsmac/debug.c b/drivers/net/wireless/brcm80211/brcmsmac/debug.c index 19740c1b1566..c9a8b9360ab1 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/debug.c +++ b/drivers/net/wireless/brcm80211/brcmsmac/debug.c @@ -30,6 +30,7 @@ #include "main.h" #include "debug.h" #include "brcms_trace_events.h" +#include "phy/phy_int.h" static struct dentry *root_folder; @@ -74,20 +75,33 @@ static int brcms_debugfs_hardware_read(struct seq_file *s, void *data) { struct brcms_pub *drvr = s->private; + struct brcms_hardware *hw = drvr->wlc->hw; + struct bcma_device *core = hw->d11core; + struct bcma_bus *bus = core->bus; + char boardrev[10]; - seq_printf(s, "board vendor: %x\n" - "board type: %x\n" - "board revision: %x\n" - "board flags: %x\n" - "board flags2: %x\n" - "firmware revision: %x\n", - drvr->wlc->hw->d11core->bus->boardinfo.vendor, - drvr->wlc->hw->d11core->bus->boardinfo.type, - drvr->wlc->hw->boardrev, - drvr->wlc->hw->boardflags, - drvr->wlc->hw->boardflags2, - drvr->wlc->ucode_rev); - + seq_printf(s, "chipnum 0x%x\n" + "chiprev 0x%x\n" + "chippackage 0x%x\n" + "corerev 0x%x\n" + "boardid 0x%x\n" + "boardvendor 0x%x\n" + "boardrev %s\n" + "boardflags 0x%x\n" + "boardflags2 0x%x\n" + "ucoderev 0x%x\n" + "radiorev 0x%x\n" + "phytype 0x%x\n" + "phyrev 0x%x\n" + "anarev 0x%x\n" + "nvramrev %d\n", + bus->chipinfo.id, bus->chipinfo.rev, bus->chipinfo.pkg, + core->id.rev, bus->boardinfo.type, bus->boardinfo.vendor, + brcmu_boardrev_str(hw->boardrev, boardrev), + drvr->wlc->hw->boardflags, drvr->wlc->hw->boardflags2, + drvr->wlc->ucode_rev, hw->band->radiorev, + hw->band->phytype, hw->band->phyrev, hw->band->pi->ana_rev, + hw->sromrev); return 0; } -- cgit v1.2.3 From c0fb754bd81fafcd63644d5482ba9669d3e4fe9c Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 28 Nov 2014 12:43:40 +0300 Subject: brcmsmac: NULL dereferences in brcms_c_detach_mfree() The brcms_c_attach_malloc() function can call this with a NULL "wlc->corestate" or "wlc->hw". Also I threw in a bonus cleanup by deleting an obvious comment and a no-op NULL assignment. :) Signed-off-by: Dan Carpenter Acked-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmsmac/main.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/net/wireless/brcm80211') diff --git a/drivers/net/wireless/brcm80211/brcmsmac/main.c b/drivers/net/wireless/brcm80211/brcmsmac/main.c index 738cfaca1e0f..a104d7ac3796 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/main.c +++ b/drivers/net/wireless/brcm80211/brcmsmac/main.c @@ -445,18 +445,18 @@ static void brcms_c_detach_mfree(struct brcms_c_info *wlc) kfree(wlc->protection); kfree(wlc->stf); kfree(wlc->bandstate[0]); - kfree(wlc->corestate->macstat_snapshot); + if (wlc->corestate) + kfree(wlc->corestate->macstat_snapshot); kfree(wlc->corestate); - kfree(wlc->hw->bandstate[0]); + if (wlc->hw) + kfree(wlc->hw->bandstate[0]); kfree(wlc->hw); if (wlc->beacon) dev_kfree_skb_any(wlc->beacon); if (wlc->probe_resp) dev_kfree_skb_any(wlc->probe_resp); - /* free the wlc */ kfree(wlc); - wlc = NULL; } static struct brcms_bss_cfg *brcms_c_bsscfg_malloc(uint unit) -- cgit v1.2.3 From 333c2aa029b847051a2db76a6ca59f699a520030 Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Wed, 3 Dec 2014 21:05:27 +0100 Subject: brcmfmac: Fix bitmap malloc bug in msgbuf. Cc: stable@vger.kernel.org # v3.17, v3.18 Reviewed-by: Arend Van Spriel Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/net/wireless/brcm80211') diff --git a/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c b/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c index 9f783db34ae5..5b0b70fa1aea 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c @@ -1354,6 +1354,7 @@ int brcmf_proto_msgbuf_attach(struct brcmf_pub *drvr) } INIT_WORK(&msgbuf->txflow_work, brcmf_msgbuf_txflow_worker); count = BITS_TO_LONGS(if_msgbuf->nrof_flowrings); + count = count * sizeof(unsigned long); msgbuf->flow_map = kzalloc(count, GFP_KERNEL); if (!msgbuf->flow_map) goto fail; -- cgit v1.2.3 From 94a612086f5e78272e831c04b673778f8546ea73 Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Wed, 3 Dec 2014 21:05:28 +0100 Subject: brcmfmac: Fix ifidx for rx data by msgbuf. The ifidx provided by FW needs to be offsetted when receiving data packets. Cc: stable@vger.kernel.org # v3.17, v3.18 Reviewed-by: Arend Van Spriel Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'drivers/net/wireless/brcm80211') diff --git a/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c b/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c index 5b0b70fa1aea..456944a6a2db 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c @@ -1080,8 +1080,17 @@ 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]; if (!ifp || !ifp->ndev) { + brcmf_err("Received pkt for invalid ifidx %d\n", ifidx); brcmu_pkt_buf_free_skb(skb); return; } -- cgit v1.2.3 From 48fd818f008e1f1fcc630381efa3c06c6320d156 Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Wed, 3 Dec 2014 21:05:29 +0100 Subject: brcmfmac: Add PCIE ids for 43602 devices. Some 43602 devices are band specific and identify themselves with different PCIE device ID. This patch adds support for the 43602 2.4G and 5.0G devices used in for example R8000 router. Reviewed-by: Arend Van Spriel Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Daniel (Deognyoun) Kim Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/pcie.c | 2 ++ drivers/net/wireless/brcm80211/include/brcm_hw_ids.h | 2 ++ 2 files changed, 4 insertions(+) (limited to 'drivers/net/wireless/brcm80211') diff --git a/drivers/net/wireless/brcm80211/brcmfmac/pcie.c b/drivers/net/wireless/brcm80211/brcmfmac/pcie.c index b0ae7993e2e8..b87b727967ea 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/pcie.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/pcie.c @@ -1857,6 +1857,8 @@ static struct pci_device_id brcmf_pcie_devid_table[] = { BRCMF_PCIE_DEVICE(BRCM_PCIE_43567_DEVICE_ID), BRCMF_PCIE_DEVICE(BRCM_PCIE_43570_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), { /* end: all zeroes */ } }; diff --git a/drivers/net/wireless/brcm80211/include/brcm_hw_ids.h b/drivers/net/wireless/brcm80211/include/brcm_hw_ids.h index af26e0de1e5c..6996fcc144cf 100644 --- a/drivers/net/wireless/brcm80211/include/brcm_hw_ids.h +++ b/drivers/net/wireless/brcm80211/include/brcm_hw_ids.h @@ -68,6 +68,8 @@ #define BRCM_PCIE_43567_DEVICE_ID 0x43d3 #define BRCM_PCIE_43570_DEVICE_ID 0x43d9 #define BRCM_PCIE_43602_DEVICE_ID 0x43ba +#define BRCM_PCIE_43602_2G_DEVICE_ID 0x43bb +#define BRCM_PCIE_43602_5G_DEVICE_ID 0x43bc /* brcmsmac IDs */ #define BCM4313_D11N2G_ID 0x4727 /* 4313 802.11n 2.4G device */ -- cgit v1.2.3 From e9efa340c173ccfe641c50ed102f68237f825c2c Mon Sep 17 00:00:00 2001 From: Franky Lin Date: Wed, 3 Dec 2014 21:05:30 +0100 Subject: brcmfmac: switch to single message MSI Use single message MSI to replace legacy interrupt. Reviewed-by: Arend Van Spriel Reviewed-by: Hante Meuleman Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Franky Lin Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/pcie.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers/net/wireless/brcm80211') diff --git a/drivers/net/wireless/brcm80211/brcmfmac/pcie.c b/drivers/net/wireless/brcm80211/brcmfmac/pcie.c index b87b727967ea..a66481976d5c 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/pcie.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/pcie.c @@ -798,12 +798,14 @@ static int brcmf_pcie_request_irq(struct brcmf_pciedev_info *devinfo) brcmf_dbg(PCIE, "Enter\n"); /* is it a v1 or v2 implementation */ devinfo->irq_requested = false; + pci_enable_msi(pdev); if (devinfo->generic_corerev == BRCMF_PCIE_GENREV1) { if (request_threaded_irq(pdev->irq, brcmf_pcie_quick_check_isr_v1, brcmf_pcie_isr_thread_v1, IRQF_SHARED, "brcmf_pcie_intr", devinfo)) { + pci_disable_msi(pdev); brcmf_err("Failed to request IRQ %d\n", pdev->irq); return -EIO; } @@ -813,6 +815,7 @@ static int brcmf_pcie_request_irq(struct brcmf_pciedev_info *devinfo) brcmf_pcie_isr_thread_v2, IRQF_SHARED, "brcmf_pcie_intr", devinfo)) { + pci_disable_msi(pdev); brcmf_err("Failed to request IRQ %d\n", pdev->irq); return -EIO; } @@ -839,6 +842,7 @@ static void brcmf_pcie_release_irq(struct brcmf_pciedev_info *devinfo) return; devinfo->irq_requested = false; free_irq(pdev->irq, devinfo); + pci_disable_msi(pdev); msleep(50); count = 0; -- cgit v1.2.3 From d2cf344d7532eb2fa4f246ec1e29304974e6a902 Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Wed, 3 Dec 2014 21:05:31 +0100 Subject: brcmfmac: Fix vendor cmds used interface. The vendor specific commands was always using main interface, change this to use the by caller supplied interface. Reviewed-by: Arend Van Spriel Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/vendor.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) (limited to 'drivers/net/wireless/brcm80211') diff --git a/drivers/net/wireless/brcm80211/brcmfmac/vendor.c b/drivers/net/wireless/brcm80211/brcmfmac/vendor.c index 222f26a39642..50cdf7090198 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/vendor.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/vendor.c @@ -31,8 +31,8 @@ static int brcmf_cfg80211_vndr_cmds_dcmd_handler(struct wiphy *wiphy, struct wireless_dev *wdev, const void *data, int len) { - struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); - struct net_device *ndev = cfg_to_ndev(cfg); + struct brcmf_cfg80211_vif *vif; + struct brcmf_if *ifp; const struct brcmf_vndr_dcmd_hdr *cmdhdr = data; struct sk_buff *reply; int ret, payload, ret_len; @@ -42,6 +42,9 @@ static int brcmf_cfg80211_vndr_cmds_dcmd_handler(struct wiphy *wiphy, brcmf_dbg(TRACE, "cmd %x set %d len %d\n", cmdhdr->cmd, cmdhdr->set, cmdhdr->len); + vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev); + ifp = vif->ifp; + len -= sizeof(struct brcmf_vndr_dcmd_hdr); ret_len = cmdhdr->len; if (ret_len > 0 || len > 0) { @@ -63,11 +66,11 @@ static int brcmf_cfg80211_vndr_cmds_dcmd_handler(struct wiphy *wiphy, } if (cmdhdr->set) - ret = brcmf_fil_cmd_data_set(netdev_priv(ndev), cmdhdr->cmd, - dcmd_buf, ret_len); + ret = brcmf_fil_cmd_data_set(ifp, cmdhdr->cmd, dcmd_buf, + ret_len); else - ret = brcmf_fil_cmd_data_get(netdev_priv(ndev), cmdhdr->cmd, - dcmd_buf, ret_len); + ret = brcmf_fil_cmd_data_get(ifp, cmdhdr->cmd, dcmd_buf, + ret_len); if (ret != 0) goto exit; -- cgit v1.2.3 From c4034f43e65466ba020a7bec5038640143465c7c Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Wed, 3 Dec 2014 21:05:32 +0100 Subject: brcmfmac: Add ifidx to logging of fwil cmds. Reviewed-by: Arend Van Spriel Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/fwil.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) (limited to 'drivers/net/wireless/brcm80211') diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fwil.c b/drivers/net/wireless/brcm80211/brcmfmac/fwil.c index 51f88c11e642..03f2c406a17b 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/fwil.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/fwil.c @@ -136,7 +136,7 @@ brcmf_fil_cmd_data_set(struct brcmf_if *ifp, u32 cmd, void *data, u32 len) mutex_lock(&ifp->drvr->proto_block); - brcmf_dbg(FIL, "cmd=%d, len=%d\n", cmd, len); + brcmf_dbg(FIL, "ifidx=%d, cmd=%d, len=%d\n", ifp->ifidx, cmd, len); brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data, min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n"); @@ -154,7 +154,7 @@ brcmf_fil_cmd_data_get(struct brcmf_if *ifp, u32 cmd, void *data, u32 len) mutex_lock(&ifp->drvr->proto_block); err = brcmf_fil_cmd_data(ifp, cmd, data, len, false); - brcmf_dbg(FIL, "cmd=%d, len=%d\n", cmd, len); + brcmf_dbg(FIL, "ifidx=%d, cmd=%d, len=%d\n", ifp->ifidx, cmd, len); brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data, min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n"); @@ -171,7 +171,7 @@ brcmf_fil_cmd_int_set(struct brcmf_if *ifp, u32 cmd, u32 data) __le32 data_le = cpu_to_le32(data); mutex_lock(&ifp->drvr->proto_block); - brcmf_dbg(FIL, "cmd=%d, value=%d\n", cmd, data); + brcmf_dbg(FIL, "ifidx=%d, cmd=%d, value=%d\n", ifp->ifidx, cmd, data); err = brcmf_fil_cmd_data(ifp, cmd, &data_le, sizeof(data_le), true); mutex_unlock(&ifp->drvr->proto_block); @@ -188,7 +188,7 @@ brcmf_fil_cmd_int_get(struct brcmf_if *ifp, u32 cmd, u32 *data) err = brcmf_fil_cmd_data(ifp, cmd, &data_le, sizeof(data_le), false); mutex_unlock(&ifp->drvr->proto_block); *data = le32_to_cpu(data_le); - brcmf_dbg(FIL, "cmd=%d, value=%d\n", cmd, *data); + brcmf_dbg(FIL, "ifidx=%d, cmd=%d, value=%d\n", ifp->ifidx, cmd, *data); return err; } @@ -224,7 +224,7 @@ brcmf_fil_iovar_data_set(struct brcmf_if *ifp, char *name, const void *data, mutex_lock(&drvr->proto_block); - brcmf_dbg(FIL, "name=%s, len=%d\n", name, len); + brcmf_dbg(FIL, "ifidx=%d, name=%s, len=%d\n", ifp->ifidx, name, len); brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data, min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n"); @@ -264,7 +264,7 @@ brcmf_fil_iovar_data_get(struct brcmf_if *ifp, char *name, void *data, brcmf_err("Creating iovar failed\n"); } - brcmf_dbg(FIL, "name=%s, len=%d\n", name, len); + brcmf_dbg(FIL, "ifidx=%d, name=%s, len=%d\n", ifp->ifidx, name, len); brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data, min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n"); @@ -347,7 +347,8 @@ brcmf_fil_bsscfg_data_set(struct brcmf_if *ifp, char *name, mutex_lock(&drvr->proto_block); - brcmf_dbg(FIL, "bssidx=%d, name=%s, len=%d\n", ifp->bssidx, name, len); + brcmf_dbg(FIL, "ifidx=%d, bssidx=%d, name=%s, len=%d\n", ifp->ifidx, + ifp->bssidx, name, len); brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data, min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n"); @@ -386,7 +387,8 @@ brcmf_fil_bsscfg_data_get(struct brcmf_if *ifp, char *name, err = -EPERM; brcmf_err("Creating bsscfg failed\n"); } - brcmf_dbg(FIL, "bssidx=%d, name=%s, len=%d\n", ifp->bssidx, name, len); + brcmf_dbg(FIL, "ifidx=%d, bssidx=%d, name=%s, len=%d\n", ifp->ifidx, + ifp->bssidx, name, len); brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data, min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n"); -- cgit v1.2.3 From a44aa4001a86d46f936ca449e5d6c268446bfae2 Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Wed, 3 Dec 2014 21:05:33 +0100 Subject: brcmfmac: add multiple BSS support. This patch adds support for multiple BSS interfaces (AP). In total three AP configurations can be created. In order to use multiple BSS firmware needs to support it. Reviewed-by: Arend Van Spriel Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c | 245 +++++++++++++++++---- drivers/net/wireless/brcm80211/brcmfmac/cfg80211.h | 2 + drivers/net/wireless/brcm80211/brcmfmac/core.c | 39 +++- drivers/net/wireless/brcm80211/brcmfmac/core.h | 3 +- drivers/net/wireless/brcm80211/brcmfmac/feature.c | 23 ++ drivers/net/wireless/brcm80211/brcmfmac/feature.h | 1 + drivers/net/wireless/brcm80211/brcmfmac/fweh.c | 6 +- .../net/wireless/brcm80211/brcmfmac/fwil_types.h | 6 + 8 files changed, 272 insertions(+), 53 deletions(-) (limited to 'drivers/net/wireless/brcm80211') diff --git a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c index 0ea312546635..3aecc5f48719 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c @@ -520,6 +520,95 @@ brcmf_cfg80211_update_proto_addr_mode(struct wireless_dev *wdev) ADDR_INDIRECT); } +static int brcmf_cfg80211_request_ap_if(struct brcmf_if *ifp) +{ + struct brcmf_mbss_ssid_le mbss_ssid_le; + int bsscfgidx; + int err; + + memset(&mbss_ssid_le, 0, sizeof(mbss_ssid_le)); + bsscfgidx = brcmf_get_next_free_bsscfgidx(ifp->drvr); + if (bsscfgidx < 0) + return bsscfgidx; + + mbss_ssid_le.bsscfgidx = cpu_to_le32(bsscfgidx); + mbss_ssid_le.SSID_len = cpu_to_le32(5); + sprintf(mbss_ssid_le.SSID, "ssid%d" , bsscfgidx); + + err = brcmf_fil_bsscfg_data_set(ifp, "bsscfg:ssid", &mbss_ssid_le, + sizeof(mbss_ssid_le)); + if (err < 0) + brcmf_err("setting ssid failed %d\n", err); + + return err; +} + +/** + * brcmf_ap_add_vif() - create a new AP virtual interface for multiple BSS + * + * @wiphy: wiphy device of new interface. + * @name: name of the new interface. + * @flags: not used. + * @params: contains mac address for AP device. + */ +static +struct wireless_dev *brcmf_ap_add_vif(struct wiphy *wiphy, const char *name, + u32 *flags, struct vif_params *params) +{ + struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); + struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg)); + struct brcmf_cfg80211_vif *vif; + int err; + + if (brcmf_cfg80211_vif_event_armed(cfg)) + return ERR_PTR(-EBUSY); + + brcmf_dbg(INFO, "Adding vif \"%s\"\n", name); + + vif = brcmf_alloc_vif(cfg, NL80211_IFTYPE_AP, false); + if (IS_ERR(vif)) + return (struct wireless_dev *)vif; + + brcmf_cfg80211_arm_vif_event(cfg, vif); + + err = brcmf_cfg80211_request_ap_if(ifp); + if (err) { + brcmf_cfg80211_arm_vif_event(cfg, NULL); + goto fail; + } + + /* wait for firmware event */ + err = brcmf_cfg80211_wait_vif_event_timeout(cfg, BRCMF_E_IF_ADD, + msecs_to_jiffies(1500)); + brcmf_cfg80211_arm_vif_event(cfg, NULL); + if (!err) { + brcmf_err("timeout occurred\n"); + err = -EIO; + goto fail; + } + + /* interface created in firmware */ + ifp = vif->ifp; + if (!ifp) { + brcmf_err("no if pointer provided\n"); + err = -ENOENT; + goto fail; + } + + strncpy(ifp->ndev->name, name, sizeof(ifp->ndev->name) - 1); + err = brcmf_net_attach(ifp, true); + if (err) { + brcmf_err("Registering netdevice failed\n"); + goto fail; + } + + return &ifp->vif->wdev; + +fail: + brcmf_free_vif(vif); + return ERR_PTR(err); +} + static bool brcmf_is_apmode(struct brcmf_cfg80211_vif *vif) { enum nl80211_iftype iftype; @@ -545,12 +634,16 @@ static struct wireless_dev *brcmf_cfg80211_add_iface(struct wiphy *wiphy, switch (type) { case NL80211_IFTYPE_ADHOC: case NL80211_IFTYPE_STATION: - case NL80211_IFTYPE_AP: case NL80211_IFTYPE_AP_VLAN: case NL80211_IFTYPE_WDS: case NL80211_IFTYPE_MONITOR: case NL80211_IFTYPE_MESH_POINT: return ERR_PTR(-EOPNOTSUPP); + case NL80211_IFTYPE_AP: + wdev = brcmf_ap_add_vif(wiphy, name, flags, params); + if (!IS_ERR(wdev)) + brcmf_cfg80211_update_proto_addr_mode(wdev); + return wdev; case NL80211_IFTYPE_P2P_CLIENT: case NL80211_IFTYPE_P2P_GO: case NL80211_IFTYPE_P2P_DEVICE: @@ -3361,11 +3454,10 @@ static bool brcmf_valid_wpa_oui(u8 *oui, bool is_rsn_ie) } static s32 -brcmf_configure_wpaie(struct net_device *ndev, +brcmf_configure_wpaie(struct brcmf_if *ifp, const struct brcmf_vs_tlv *wpa_ie, bool is_rsn_ie) { - struct brcmf_if *ifp = netdev_priv(ndev); u32 auth = 0; /* d11 open authentication */ u16 count; s32 err = 0; @@ -3840,6 +3932,7 @@ brcmf_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *ndev, enum nl80211_iftype dev_role; struct brcmf_fil_bss_enable_le bss_enable; u16 chanspec; + bool mbss; brcmf_dbg(TRACE, "ctrlchn=%d, center=%d, bw=%d, beacon_interval=%d, dtim_period=%d,\n", settings->chandef.chan->hw_value, @@ -3850,6 +3943,7 @@ brcmf_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *ndev, settings->inactivity_timeout); dev_role = ifp->vif->wdev.iftype; + mbss = ifp->vif->mbss; memset(&ssid_le, 0, sizeof(ssid_le)); if (settings->ssid == NULL || settings->ssid_len == 0) { @@ -3869,8 +3963,10 @@ brcmf_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *ndev, ssid_le.SSID_len = cpu_to_le32((u32)settings->ssid_len); } - brcmf_set_mpc(ifp, 0); - brcmf_configure_arp_offload(ifp, false); + if (!mbss) { + brcmf_set_mpc(ifp, 0); + brcmf_configure_arp_offload(ifp, false); + } /* find the RSN_IE */ rsn_ie = brcmf_parse_tlvs((u8 *)settings->beacon.tail, @@ -3884,13 +3980,16 @@ brcmf_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *ndev, brcmf_dbg(TRACE, "WPA(2) IE is found\n"); if (wpa_ie != NULL) { /* WPA IE */ - err = brcmf_configure_wpaie(ndev, wpa_ie, false); + err = brcmf_configure_wpaie(ifp, wpa_ie, false); if (err < 0) goto exit; } else { + struct brcmf_vs_tlv *tmp_ie; + + tmp_ie = (struct brcmf_vs_tlv *)rsn_ie; + /* RSN IE */ - err = brcmf_configure_wpaie(ndev, - (struct brcmf_vs_tlv *)rsn_ie, true); + err = brcmf_configure_wpaie(ifp, tmp_ie, true); if (err < 0) goto exit; } @@ -3901,45 +4000,53 @@ brcmf_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *ndev, brcmf_config_ap_mgmt_ie(ifp->vif, &settings->beacon); - chanspec = chandef_to_chanspec(&cfg->d11inf, &settings->chandef); - err = brcmf_fil_iovar_int_set(ifp, "chanspec", chanspec); - if (err < 0) { - brcmf_err("Set Channel failed: chspec=%d, %d\n", chanspec, err); - goto exit; - } - - if (settings->beacon_interval) { - err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_BCNPRD, - settings->beacon_interval); + if (!mbss) { + chanspec = chandef_to_chanspec(&cfg->d11inf, + &settings->chandef); + err = brcmf_fil_iovar_int_set(ifp, "chanspec", chanspec); if (err < 0) { - brcmf_err("Beacon Interval Set Error, %d\n", err); + brcmf_err("Set Channel failed: chspec=%d, %d\n", + chanspec, err); goto exit; } - } - if (settings->dtim_period) { - err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_DTIMPRD, - settings->dtim_period); - if (err < 0) { - brcmf_err("DTIM Interval Set Error, %d\n", err); - goto exit; + + if (settings->beacon_interval) { + err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_BCNPRD, + settings->beacon_interval); + if (err < 0) { + brcmf_err("Beacon Interval Set Error, %d\n", + err); + goto exit; + } + } + if (settings->dtim_period) { + err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_DTIMPRD, + settings->dtim_period); + if (err < 0) { + brcmf_err("DTIM Interval Set Error, %d\n", err); + goto exit; + } } - } - if (dev_role == NL80211_IFTYPE_AP) { - err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_DOWN, 1); + if (dev_role == NL80211_IFTYPE_AP) { + err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_DOWN, 1); + if (err < 0) { + brcmf_err("BRCMF_C_DOWN error %d\n", err); + goto exit; + } + brcmf_fil_iovar_int_set(ifp, "apsta", 0); + } + + err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_INFRA, 1); if (err < 0) { - brcmf_err("BRCMF_C_DOWN error %d\n", err); + brcmf_err("SET INFRA error %d\n", err); goto exit; } - brcmf_fil_iovar_int_set(ifp, "apsta", 0); - } - - err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_INFRA, 1); - if (err < 0) { - brcmf_err("SET INFRA error %d\n", err); - goto exit; } if (dev_role == NL80211_IFTYPE_AP) { + if ((brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MBSS)) && (!mbss)) + brcmf_fil_iovar_int_set(ifp, "mbss", 1); + err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_AP, 1); if (err < 0) { brcmf_err("setting AP mode failed %d\n", err); @@ -3984,7 +4091,7 @@ brcmf_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *ndev, set_bit(BRCMF_VIF_STATUS_AP_CREATED, &ifp->vif->sme_state); exit: - if (err) { + if ((err) && (!mbss)) { brcmf_set_mpc(ifp, 1); brcmf_configure_arp_offload(ifp, true); } @@ -4005,20 +4112,31 @@ static int brcmf_cfg80211_stop_ap(struct wiphy *wiphy, struct net_device *ndev) /* first to make sure they get processed by fw. */ msleep(400); + if (ifp->vif->mbss) { + err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_DOWN, 1); + return err; + } + memset(&join_params, 0, sizeof(join_params)); err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SSID, &join_params, sizeof(join_params)); if (err < 0) brcmf_err("SET SSID error (%d)\n", err); - err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_UP, 0); + err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_DOWN, 1); if (err < 0) - brcmf_err("BRCMF_C_UP error %d\n", err); + brcmf_err("BRCMF_C_DOWN error %d\n", err); err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_AP, 0); if (err < 0) brcmf_err("setting AP mode failed %d\n", err); err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_INFRA, 0); if (err < 0) brcmf_err("setting INFRA mode failed %d\n", err); + if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MBSS)) + brcmf_fil_iovar_int_set(ifp, "mbss", 0); + /* Bring device back up so it can be used again */ + err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_UP, 1); + if (err < 0) + brcmf_err("BRCMF_C_UP error %d\n", err); } else { bss_enable.bsscfg_idx = cpu_to_le32(ifp->bssidx); bss_enable.enable = cpu_to_le32(0); @@ -4370,7 +4488,9 @@ struct brcmf_cfg80211_vif *brcmf_alloc_vif(struct brcmf_cfg80211_info *cfg, enum nl80211_iftype type, bool pm_block) { + struct brcmf_cfg80211_vif *vif_walk; struct brcmf_cfg80211_vif *vif; + bool mbss; brcmf_dbg(TRACE, "allocating virtual interface (size=%zu)\n", sizeof(*vif)); @@ -4386,6 +4506,17 @@ struct brcmf_cfg80211_vif *brcmf_alloc_vif(struct brcmf_cfg80211_info *cfg, brcmf_init_prof(&vif->profile); + if (type == NL80211_IFTYPE_AP) { + mbss = false; + list_for_each_entry(vif_walk, &cfg->vif_list, list) { + if (vif_walk->wdev.iftype == NL80211_IFTYPE_AP) { + mbss = true; + break; + } + } + vif->mbss = mbss; + } + list_add_tail(&vif->list, &cfg->vif_list); return vif; } @@ -4628,6 +4759,7 @@ brcmf_notify_connect_status_ap(struct brcmf_cfg80211_info *cfg, struct net_device *ndev, const struct brcmf_event_msg *e, void *data) { + struct brcmf_if *ifp = netdev_priv(ndev); static int generation; u32 event = e->event_code; u32 reason = e->reason; @@ -4638,6 +4770,8 @@ brcmf_notify_connect_status_ap(struct brcmf_cfg80211_info *cfg, ndev != cfg_to_ndev(cfg)) { brcmf_dbg(CONN, "AP mode link down\n"); complete(&cfg->vif_disabled); + if (ifp->vif->mbss) + brcmf_remove_interface(ifp->drvr, ifp->bssidx); return 0; } @@ -5429,7 +5563,28 @@ static int brcmf_setup_wiphybands(struct wiphy *wiphy) return 0; } -static const struct ieee80211_iface_limit brcmf_iface_limits[] = { +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) | @@ -5450,8 +5605,8 @@ 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), - .limits = brcmf_iface_limits + .n_limits = ARRAY_SIZE(brcmf_iface_limits_sbss), + .limits = brcmf_iface_limits_sbss, } }; @@ -5527,6 +5682,10 @@ static int brcmf_setup_wiphy(struct wiphy *wiphy, struct brcmf_if *ifp) 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); diff --git a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.h b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.h index 2a5b22cb3fef..9e98b8d52757 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.h @@ -183,6 +183,7 @@ struct vif_saved_ie { * @pm_block: power-management blocked. * @list: linked list. * @mgmt_rx_reg: registered rx mgmt frame types. + * @mbss: Multiple BSS type, set if not first AP (not relevant for P2P). */ struct brcmf_cfg80211_vif { struct brcmf_if *ifp; @@ -194,6 +195,7 @@ struct brcmf_cfg80211_vif { struct vif_saved_ie saved_ie; struct list_head list; u16 mgmt_rx_reg; + bool mbss; }; /* association inform */ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/core.c b/drivers/net/wireless/brcm80211/brcmfmac/core.c index f407665cb1ea..effe6d7831d9 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/core.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/core.c @@ -836,7 +836,7 @@ struct brcmf_if *brcmf_add_if(struct brcmf_pub *drvr, s32 bssidx, s32 ifidx, return ifp; } -void brcmf_del_if(struct brcmf_pub *drvr, s32 bssidx) +static void brcmf_del_if(struct brcmf_pub *drvr, s32 bssidx) { struct brcmf_if *ifp; @@ -869,6 +869,38 @@ void brcmf_del_if(struct brcmf_pub *drvr, s32 bssidx) } } +void brcmf_remove_interface(struct brcmf_pub *drvr, u32 bssidx) +{ + if (drvr->iflist[bssidx]) { + brcmf_fws_del_interface(drvr->iflist[bssidx]); + brcmf_del_if(drvr, bssidx); + } +} + +int brcmf_get_next_free_bsscfgidx(struct brcmf_pub *drvr) +{ + int ifidx; + int bsscfgidx; + bool available; + int highest; + + available = false; + bsscfgidx = 2; + highest = 2; + for (ifidx = 0; ifidx < BRCMF_MAX_IFS; ifidx++) { + if (drvr->iflist[ifidx]) { + if (drvr->iflist[ifidx]->bssidx == bsscfgidx) + bsscfgidx = highest + 1; + else if (drvr->iflist[ifidx]->bssidx > highest) + highest = drvr->iflist[ifidx]->bssidx; + } else { + available = true; + } + } + + return available ? bsscfgidx : -ENOMEM; +} + int brcmf_attach(struct device *dev) { struct brcmf_pub *drvr = NULL; @@ -1033,10 +1065,7 @@ void brcmf_detach(struct device *dev) /* make sure primary interface removed last */ for (i = BRCMF_MAX_IFS-1; i > -1; i--) - if (drvr->iflist[i]) { - brcmf_fws_del_interface(drvr->iflist[i]); - brcmf_del_if(drvr, i); - } + brcmf_remove_interface(drvr, i); brcmf_cfg80211_detach(drvr->config); diff --git a/drivers/net/wireless/brcm80211/brcmfmac/core.h b/drivers/net/wireless/brcm80211/brcmfmac/core.h index 98228e922d3a..23f74b139cc8 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/core.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/core.h @@ -175,7 +175,8 @@ char *brcmf_ifname(struct brcmf_pub *drvr, int idx); 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_del_if(struct brcmf_pub *drvr, s32 bssidx); +void brcmf_remove_interface(struct brcmf_pub *drvr, u32 bssidx); +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, diff --git a/drivers/net/wireless/brcm80211/brcmfmac/feature.c b/drivers/net/wireless/brcm80211/brcmfmac/feature.c index 931f68aefaa4..defb7a44e0bc 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/feature.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/feature.c @@ -97,6 +97,28 @@ static void brcmf_feat_iovar_int_get(struct brcmf_if *ifp, } } +/** + * brcmf_feat_iovar_int_set() - determine feature through iovar set. + * + * @ifp: interface to query. + * @id: feature id. + * @name: iovar name. + */ +static void brcmf_feat_iovar_int_set(struct brcmf_if *ifp, + enum brcmf_feat_id id, char *name, u32 val) +{ + int err; + + err = brcmf_fil_iovar_int_set(ifp, name, val); + if (err == 0) { + brcmf_dbg(INFO, "enabling feature: %s\n", brcmf_feat_names[id]); + ifp->drvr->feat_flags |= BIT(id); + } else { + brcmf_dbg(TRACE, "%s feature check failed: %d\n", + brcmf_feat_names[id], err); + } +} + void brcmf_feat_attach(struct brcmf_pub *drvr) { struct brcmf_if *ifp = drvr->iflist[0]; @@ -104,6 +126,7 @@ void brcmf_feat_attach(struct brcmf_pub *drvr) brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_MCHAN, "mchan"); if (drvr->bus_if->wowl_supported) brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_WOWL, "wowl"); + brcmf_feat_iovar_int_set(ifp, BRCMF_FEAT_MBSS, "mbss", 0); /* set chip related quirks */ switch (drvr->bus_if->chip) { diff --git a/drivers/net/wireless/brcm80211/brcmfmac/feature.h b/drivers/net/wireless/brcm80211/brcmfmac/feature.h index b9a796d0a44d..f5832e077bb7 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/feature.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/feature.h @@ -22,6 +22,7 @@ * MCHAN: multi-channel for concurrent P2P. */ #define BRCMF_FEAT_LIST \ + BRCMF_FEAT_DEF(MBSS) \ BRCMF_FEAT_DEF(MCHAN) \ BRCMF_FEAT_DEF(WOWL) /* diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fweh.c b/drivers/net/wireless/brcm80211/brcmfmac/fweh.c index 7338b335e153..ec62492ffa69 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/fweh.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/fweh.c @@ -221,10 +221,8 @@ 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_fws_del_interface(ifp); - brcmf_del_if(drvr, ifevent->bssidx); - } + if (ifp && ifevent->action == BRCMF_E_IF_DEL) + brcmf_remove_interface(drvr, ifevent->bssidx); } /** diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fwil_types.h b/drivers/net/wireless/brcm80211/brcmfmac/fwil_types.h index ba64b292f7a5..50891c02c4c1 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/fwil_types.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/fwil_types.h @@ -519,4 +519,10 @@ struct brcmf_fil_wowl_pattern_le { /* u8 pattern[] - Pattern follows the mask is at 'patternoffset' */ }; +struct brcmf_mbss_ssid_le { + __le32 bsscfgidx; + __le32 SSID_len; + unsigned char SSID[32]; +}; + #endif /* FWIL_TYPES_H_ */ -- cgit v1.2.3