From 3ffddfd1307ad9fc226d8b98cc00d3bce5cca356 Mon Sep 17 00:00:00 2001 From: Alvin Xie Date: Mon, 10 Jul 2023 19:14:57 +0800 Subject: [PATCH 1/2] net: wireless: update broadcom drivers from Infineon Signed-off-by: Alvin Xie --- drivers/net/wireless/broadcom/Makefile | 2 +- .../net/wireless/broadcom/brcm80211/Kconfig | 29 +- .../net/wireless/broadcom/brcm80211/Makefile | 2 +- .../broadcom/brcm80211/brcmfmac/Kconfig | 14 +- .../broadcom/brcm80211/brcmfmac/Makefile | 6 +- .../broadcom/brcm80211/brcmfmac/bcdc.c | 7 +- .../broadcom/brcm80211/brcmfmac/bcmsdh.c | 214 +- .../brcm80211/brcmfmac/bt_shared_sdio.c | 326 +++ .../brcm80211/brcmfmac/bt_shared_sdio.h | 43 + .../broadcom/brcm80211/brcmfmac/bus.h | 60 +- .../broadcom/brcm80211/brcmfmac/cfg80211.c | 2322 ++++++++++++++--- .../broadcom/brcm80211/brcmfmac/cfg80211.h | 49 +- .../broadcom/brcm80211/brcmfmac/chip.c | 379 ++- .../broadcom/brcm80211/brcmfmac/chip.h | 43 +- .../broadcom/brcm80211/brcmfmac/common.c | 112 +- .../broadcom/brcm80211/brcmfmac/common.h | 7 + .../broadcom/brcm80211/brcmfmac/core.c | 420 ++- .../broadcom/brcm80211/brcmfmac/core.h | 29 +- .../broadcom/brcm80211/brcmfmac/debug.c | 83 + .../broadcom/brcm80211/brcmfmac/debug.h | 25 + .../broadcom/brcm80211/brcmfmac/dmi.c | 46 +- .../broadcom/brcm80211/brcmfmac/feature.c | 12 +- .../broadcom/brcm80211/brcmfmac/feature.h | 8 +- .../broadcom/brcm80211/brcmfmac/firmware.c | 25 +- .../broadcom/brcm80211/brcmfmac/firmware.h | 15 +- .../broadcom/brcm80211/brcmfmac/flowring.c | 5 +- .../broadcom/brcm80211/brcmfmac/fweh.c | 32 +- .../broadcom/brcm80211/brcmfmac/fweh.h | 32 +- .../broadcom/brcm80211/brcmfmac/fwil.c | 19 + .../broadcom/brcm80211/brcmfmac/fwil.h | 4 + .../broadcom/brcm80211/brcmfmac/fwil_types.h | 81 +- .../broadcom/brcm80211/brcmfmac/fwsignal.c | 65 +- .../broadcom/brcm80211/brcmfmac/fwsignal.h | 3 +- .../broadcom/brcm80211/brcmfmac/msgbuf.c | 124 +- .../broadcom/brcm80211/brcmfmac/msgbuf.h | 1 + .../wireless/broadcom/brcm80211/brcmfmac/of.c | 14 +- .../broadcom/brcm80211/brcmfmac/p2p.c | 200 +- .../broadcom/brcm80211/brcmfmac/p2p.h | 7 +- .../broadcom/brcm80211/brcmfmac/pcie.c | 1020 +++++++- .../broadcom/brcm80211/brcmfmac/pcie.h | 5 + .../broadcom/brcm80211/brcmfmac/pno.c | 31 + .../broadcom/brcm80211/brcmfmac/sdio.c | 645 ++++- .../broadcom/brcm80211/brcmfmac/sdio.h | 121 + .../broadcom/brcm80211/brcmfmac/trxhdr.h | 38 + .../broadcom/brcm80211/brcmfmac/usb.c | 52 +- .../broadcom/brcm80211/brcmfmac/vendor.c | 135 + .../broadcom/brcm80211/brcmfmac/vendor.h | 20 + .../broadcom/brcm80211/brcmutil/Makefile | 2 +- .../broadcom/brcm80211/brcmutil/d11.c | 19 +- .../broadcom/brcm80211/include/brcm_hw_ids.h | 17 +- .../broadcom/brcm80211/include/brcmu_d11.h | 22 +- .../broadcom/brcm80211/include/brcmu_utils.h | 13 + .../broadcom/brcm80211/include/brcmu_wifi.h | 17 +- .../broadcom/brcm80211/include/chipcommon.h | 195 +- include/linux/mmc/sdio_ids.h | 10 +- 55 files changed, 6459 insertions(+), 768 deletions(-) create mode 100644 drivers/net/wireless/broadcom/brcm80211/brcmfmac/bt_shared_sdio.c create mode 100644 drivers/net/wireless/broadcom/brcm80211/brcmfmac/bt_shared_sdio.h create mode 100644 drivers/net/wireless/broadcom/brcm80211/brcmfmac/trxhdr.h diff --git a/drivers/net/wireless/broadcom/Makefile b/drivers/net/wireless/broadcom/Makefile index 1a8384daed2ca..4138e3c419388 100644 --- a/drivers/net/wireless/broadcom/Makefile +++ b/drivers/net/wireless/broadcom/Makefile @@ -3,4 +3,4 @@ obj-$(CONFIG_B43) += b43/ obj-$(CONFIG_B43LEGACY) += b43legacy/ obj-$(CONFIG_BRCMFMAC) += brcm80211/ -obj-$(CONFIG_BRCMSMAC) += brcm80211/ +#obj-$(CONFIG_BRCMSMAC) += brcm80211/ diff --git a/drivers/net/wireless/broadcom/brcm80211/Kconfig b/drivers/net/wireless/broadcom/brcm80211/Kconfig index 5bf2318763c55..2e664df74ca80 100644 --- a/drivers/net/wireless/broadcom/brcm80211/Kconfig +++ b/drivers/net/wireless/broadcom/brcm80211/Kconfig @@ -1,17 +1,20 @@ # SPDX-License-Identifier: GPL-2.0-only config BRCMUTIL tristate + depends on m config BRCMSMAC + depends on n tristate "Broadcom IEEE802.11n PCIe SoftMAC WLAN driver" + depends on m depends on MAC80211 depends on BCMA_POSSIBLE - select BCMA + depends on BCMA select NEW_LEDS if BCMA_DRIVER_GPIO select LEDS_CLASS if BCMA_DRIVER_GPIO select BRCMUTIL select FW_LOADER - select CORDIC + select BPAUTO_CORDIC help This module adds support for PCIe wireless adapters based on Broadcom IEEE802.11n SoftMAC chipsets. It also has WLAN led support, which will @@ -34,6 +37,26 @@ config BRCM_TRACING config BRCMDBG bool "Broadcom driver debug functions" depends on BRCMSMAC || BRCMFMAC - select WANT_DEV_COREDUMP if BRCMFMAC + select BPAUTO_WANT_DEV_COREDUMP help Selecting this enables additional code for debug purposes. + +config BRCMFMAC_PCIE_BARWIN_SZ + bool "Custom PCIE BAR window size support for FullMAC driver" + depends on BRCMFMAC + depends on PCI + default n + help + If you say Y here, the FMAC driver will use custom PCIE BAR + window size. Say Y to allow developers to use custom PCIE + BAR window size when HOST PCIE IP can support less then 4MB + BAR window. + +config BRCMFMAC_BT_SHARED_SDIO + bool "FMAC shares SDIO bus to Bluetooth" + depends on BRCMFMAC + depends on BRCMFMAC_SDIO + default n + help + Selecting this to enables sharing the SDIO bus interface between + Cypress BT and WiFi host drivers. diff --git a/drivers/net/wireless/broadcom/brcm80211/Makefile b/drivers/net/wireless/broadcom/brcm80211/Makefile index 88115d072624c..6c0a00d46b50f 100644 --- a/drivers/net/wireless/broadcom/brcm80211/Makefile +++ b/drivers/net/wireless/broadcom/brcm80211/Makefile @@ -10,4 +10,4 @@ subdir-ccflags-$(CONFIG_BRCMDBG) += -DDEBUG obj-$(CONFIG_BRCMUTIL) += brcmutil/ obj-$(CONFIG_BRCMFMAC) += brcmfmac/ -obj-$(CONFIG_BRCMSMAC) += brcmsmac/ +#obj-$(CONFIG_BRCMSMAC) += brcmsmac/ diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/Kconfig b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/Kconfig index 32794c1eca231..45d4ffcc13383 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/Kconfig +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/Kconfig @@ -1,5 +1,6 @@ config BRCMFMAC tristate "Broadcom FullMAC WLAN driver" + depends on m depends on CFG80211 select BRCMUTIL help @@ -19,7 +20,7 @@ config BRCMFMAC_SDIO depends on (MMC = y || MMC = BRCMFMAC) depends on BRCMFMAC select BRCMFMAC_PROTO_BCDC - select FW_LOADER + depends on FW_LOADER default y help This option enables the SDIO bus interface support for Broadcom @@ -31,7 +32,7 @@ config BRCMFMAC_USB depends on (USB = y || USB = BRCMFMAC) depends on BRCMFMAC select BRCMFMAC_PROTO_BCDC - select FW_LOADER + depends on FW_LOADER help This option enables the USB bus interface support for Broadcom IEEE802.11n embedded FullMAC WLAN driver. Say Y if you want to @@ -42,9 +43,16 @@ config BRCMFMAC_PCIE depends on BRCMFMAC depends on PCI select BRCMFMAC_PROTO_MSGBUF - select FW_LOADER + depends on FW_LOADER help This option enables the PCIE bus interface support for Broadcom IEEE802.11ac embedded FullMAC WLAN driver. Say Y if you want to use the driver for an PCIE wireless card. +config BRCMFMAC_BT_SHARED_SDIO + bool "FMAC shares SDIO bus to Bluetooth" + depends on BRCMFMAC_SDIO + default n + help + This option enables the feautre of sharing the SDIO bus interface + between Cypress BT and WiFi host drivers. diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/Makefile b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/Makefile index 9b15bc3f60548..2c6d99590d11a 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/Makefile +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/Makefile @@ -6,8 +6,8 @@ # ccflags-y += \ - -I $(srctree)/$(src) \ - -I $(srctree)/$(src)/../include + -I$(src) \ + -I$(src)/../include obj-$(CONFIG_BRCMFMAC) += brcmfmac.o brcmfmac-objs += \ @@ -46,3 +46,5 @@ brcmfmac-$(CONFIG_OF) += \ of.o brcmfmac-$(CONFIG_DMI) += \ dmi.o +brcmfmac-${CONFIG_BRCMFMAC_BT_SHARED_SDIO} += \ + bt_shared_sdio.o diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcdc.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcdc.c index 3984fd7d918e1..4fefd8587e5c6 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcdc.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcdc.c @@ -87,6 +87,8 @@ struct brcmf_proto_bcdc_header { * plus any space that might be needed * for bus alignment padding. */ +#define ROUND_UP_MARGIN 2048 + struct brcmf_bcdc { u16 reqid; u8 bus_header[BUS_HEADER_LEN]; @@ -368,8 +370,7 @@ brcmf_proto_bcdc_txcomplete(struct device *dev, struct sk_buff *txp, /* await txstatus signal for firmware if active */ if (brcmf_fws_fc_active(bcdc->fws)) { - if (!success) - brcmf_fws_bustxfail(bcdc->fws, txp); + brcmf_fws_bustxcomplete(bcdc->fws, txp, success); } else { if (brcmf_proto_bcdc_hdrpull(bus_if->drvr, false, txp, &ifp)) brcmu_pkt_buf_free_skb(txp); @@ -471,7 +472,7 @@ int brcmf_proto_bcdc_attach(struct brcmf_pub *drvr) drvr->hdrlen += BCDC_HEADER_LEN + BRCMF_PROT_FW_SIGNAL_MAX_TXBYTES; drvr->bus_if->maxctl = BRCMF_DCMD_MAXLEN + - sizeof(struct brcmf_proto_bcdc_dcmd); + sizeof(struct brcmf_proto_bcdc_dcmd) + ROUND_UP_MARGIN; return 0; fail: diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c index b6d0bc73923fc..4560eeedb00e9 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c @@ -36,6 +36,7 @@ #include "sdio.h" #include "core.h" #include "common.h" +#include "cfg80211.h" #define SDIOH_API_ACCESS_RETRY_LIMIT 2 @@ -43,9 +44,13 @@ #define SDIO_FUNC1_BLOCKSIZE 64 #define SDIO_FUNC2_BLOCKSIZE 512 -#define SDIO_4373_FUNC2_BLOCKSIZE 256 +#define SDIO_4373_FUNC2_BLOCKSIZE 128 #define SDIO_435X_FUNC2_BLOCKSIZE 256 #define SDIO_4329_FUNC2_BLOCKSIZE 128 +#define SDIO_89459_FUNC2_BLOCKSIZE 256 +#define SDIO_CYW55560_FUNC2_BLOCKSIZE 256 +#define SDIO_CYW43455_FUNC2_BLOCKSIZE 256 + /* Maximum milliseconds to wait for F2 to come up */ #define SDIO_WAIT_F2RDY 3000 @@ -330,6 +335,75 @@ static int brcmf_sdiod_skbuff_write(struct brcmf_sdio_dev *sdiodev, return err; } +#ifdef CONFIG_BRCMF_SDIO_TX_COPY +static int brcmf_sdiod_skbuff_list_write(struct brcmf_sdio_dev *sdiodev, + struct sdio_func *func, + u32 addr, + struct sk_buff_head *pktlist) +{ + int err; + struct sk_buff *pkt_next = NULL; + char *buf = sdiodev->membuf; + u32 cur_addr = addr; + unsigned int blk_size = func->cur_blksize; + unsigned int req_size = 0; + unsigned int pad_size = 0; + + skb_queue_walk(pktlist, pkt_next) { + unsigned int remain_size = pkt_next->len; + unsigned int offset = 0; + + do { + unsigned int cpy_size; + + if ((req_size + remain_size) > BRCMF_SDIO_MAX_MEMBUF_SIZE) { + cpy_size = BRCMF_SDIO_MAX_MEMBUF_SIZE - req_size; + + memcpy(buf + req_size, ((char*)(pkt_next->data)) + offset, cpy_size); + + err = sdio_memcpy_toio(func, cur_addr, buf, BRCMF_SDIO_MAX_MEMBUF_SIZE); + if (err) { + goto done; + } + + req_size = 0; + offset += cpy_size; + remain_size -= cpy_size; + cur_addr += BRCMF_SDIO_MAX_MEMBUF_SIZE; + } else { + cpy_size = remain_size; + + memcpy(buf + req_size, ((char*)(pkt_next->data)) + offset, cpy_size); + + req_size += cpy_size; + offset = 0; + remain_size = 0; + } + } while (remain_size > 0); + } + + pad_size = req_size % blk_size; + if ((req_size + pad_size) > BRCMF_SDIO_MAX_MEMBUF_SIZE) { + pad_size = BRCMF_SDIO_MAX_MEMBUF_SIZE - req_size; + } + + if (pad_size > 0) { + memset(buf + req_size, 0, pad_size); + } + + req_size = (req_size + 3) & ((uint)~3); + + err = sdio_memcpy_toio(func, cur_addr, buf, req_size); + +done: + if (err == -ENOMEDIUM) { + brcmf_sdiod_change_state(sdiodev, BRCMF_SDIOD_NOMEDIUM); + } + + return err; +} +#endif /* CONFIG_BRCMF_SDIO_TX_COPY */ + static int mmc_submit_one(struct mmc_data *md, struct mmc_request *mr, struct mmc_command *mc, int sg_cnt, int req_sz, int func_blk_sz, u32 *addr, @@ -424,23 +498,6 @@ static int brcmf_sdiod_sglist_rw(struct brcmf_sdio_dev *sdiodev, max_seg_cnt = min_t(unsigned short, sdiodev->max_segment_count, target_list->qlen); - memset(&mmc_req, 0, sizeof(struct mmc_request)); - memset(&mmc_cmd, 0, sizeof(struct mmc_command)); - memset(&mmc_dat, 0, sizeof(struct mmc_data)); - - mmc_dat.sg = sdiodev->sgtable.sgl; - mmc_dat.blksz = func_blk_sz; - mmc_dat.flags = write ? MMC_DATA_WRITE : MMC_DATA_READ; - mmc_cmd.opcode = SD_IO_RW_EXTENDED; - mmc_cmd.arg = write ? 1<<31 : 0; /* write flag */ - mmc_cmd.arg |= (func->num & 0x7) << 28; /* SDIO func num */ - mmc_cmd.arg |= 1 << 27; /* block mode */ - /* for function 1 the addr will be incremented */ - mmc_cmd.arg |= (func->num == 1) ? 1 << 26 : 0; - mmc_cmd.flags = MMC_RSP_SPI_R5 | MMC_RSP_R5 | MMC_CMD_ADTC; - mmc_req.cmd = &mmc_cmd; - mmc_req.data = &mmc_dat; - req_sz = 0; sg_cnt = 0; sgl = sdiodev->sgtable.sgl; @@ -461,6 +518,23 @@ static int brcmf_sdiod_sglist_rw(struct brcmf_sdio_dev *sdiodev, req_sz += sg_data_sz; pkt_offset += sg_data_sz; if (req_sz >= max_req_sz || sg_cnt >= max_seg_cnt) { + memset(&mmc_req, 0, sizeof(struct mmc_request)); + memset(&mmc_cmd, 0, sizeof(struct mmc_command)); + memset(&mmc_dat, 0, sizeof(struct mmc_data)); + + mmc_dat.sg = sdiodev->sgtable.sgl; + mmc_dat.blksz = func_blk_sz; + mmc_dat.flags = write ? MMC_DATA_WRITE : MMC_DATA_READ; + mmc_cmd.opcode = SD_IO_RW_EXTENDED; + mmc_cmd.arg = write ? 1<<31 : 0; /* write flag */ + mmc_cmd.arg |= (func->num & 0x7) << 28; /* SDIO func num */ + mmc_cmd.arg |= 1 << 27; /* block mode */ + /* for function 1 the addr will be incremented */ + mmc_cmd.arg |= (func->num == 1) ? 1 << 26 : 0; + mmc_cmd.flags = MMC_RSP_SPI_R5 | MMC_RSP_R5 | MMC_CMD_ADTC; + mmc_req.cmd = &mmc_cmd; + mmc_req.data = &mmc_dat; + ret = mmc_submit_one(&mmc_dat, &mmc_req, &mmc_cmd, sg_cnt, req_sz, func_blk_sz, &addr, sdiodev, func, write); @@ -468,17 +542,40 @@ static int brcmf_sdiod_sglist_rw(struct brcmf_sdio_dev *sdiodev, goto exit_queue_walk; req_sz = 0; sg_cnt = 0; + sg_init_table(sdiodev->sgtable.sgl, sdiodev->sgtable.orig_nents); sgl = sdiodev->sgtable.sgl; } } } - if (sg_cnt) + if (sg_cnt) { + memset(&mmc_req, 0, sizeof(struct mmc_request)); + memset(&mmc_cmd, 0, sizeof(struct mmc_command)); + memset(&mmc_dat, 0, sizeof(struct mmc_data)); + + mmc_dat.sg = sdiodev->sgtable.sgl; + mmc_dat.blksz = func_blk_sz; + mmc_dat.flags = write ? MMC_DATA_WRITE : MMC_DATA_READ; + mmc_cmd.opcode = SD_IO_RW_EXTENDED; + mmc_cmd.arg = write ? 1<<31 : 0; /* write flag */ + mmc_cmd.arg |= (func->num & 0x7) << 28; /* SDIO func num */ + mmc_cmd.arg |= 1 << 27; /* block mode */ + /* for function 1 the addr will be incremented */ + mmc_cmd.arg |= (func->num == 1) ? 1 << 26 : 0; + mmc_cmd.flags = MMC_RSP_SPI_R5 | MMC_RSP_R5 | MMC_CMD_ADTC; + mmc_req.cmd = &mmc_cmd; + mmc_req.data = &mmc_dat; + ret = mmc_submit_one(&mmc_dat, &mmc_req, &mmc_cmd, sg_cnt, req_sz, func_blk_sz, &addr, sdiodev, func, write); + } exit_queue_walk: if (!write && sdiodev->settings->bus.sdio.broken_sg_support) { +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 20, 0)) src = __skb_peek(&local_list); +#else /* (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 20, 0)) */ + src = local_list.next; +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 20, 0)) */ src_offset = 0; skb_queue_walk(pktlist, pkt_next) { dst_offset = 0; @@ -574,8 +671,13 @@ int brcmf_sdiod_recv_chain(struct brcmf_sdio_dev *sdiodev, addr |= SBSDIO_SB_ACCESS_2_4B_FLAG; if (pktq->qlen == 1) +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 20, 0)) err = brcmf_sdiod_skbuff_read(sdiodev, sdiodev->func2, addr, __skb_peek(pktq)); +#else /* (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 20, 0)) */ + err = brcmf_sdiod_skbuff_read(sdiodev, sdiodev->func2, addr, + pktq->next); +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 20, 0)) */ else if (!sdiodev->sg_support) { glom_skb = brcmu_pkt_buf_get_skb(totlen); if (!glom_skb) @@ -652,8 +754,12 @@ int brcmf_sdiod_send_pkt(struct brcmf_sdio_dev *sdiodev, break; } } else { +#ifdef CONFIG_BRCMF_SDIO_TX_COPY + err = brcmf_sdiod_skbuff_list_write(sdiodev, sdiodev->func2, addr, pktq); +#else /* CONFIG_BRCMF_SDIO_TX_COPY */ err = brcmf_sdiod_sglist_rw(sdiodev, sdiodev->func2, true, addr, pktq); +#endif /* CONFIG_BRCMF_SDIO_TX_COPY */ } return err; @@ -669,7 +775,7 @@ brcmf_sdiod_ramrw(struct brcmf_sdio_dev *sdiodev, bool write, u32 address, uint dsize; dsize = min_t(uint, SBSDIO_SB_OFT_ADDR_LIMIT, size); - pkt = dev_alloc_skb(dsize); + pkt = __dev_alloc_skb(dsize, GFP_KERNEL); if (!pkt) { brcmf_err("dev_alloc_skb failed: len %d\n", dsize); return -EIO; @@ -888,6 +994,14 @@ int brcmf_sdiod_remove(struct brcmf_sdio_dev *sdiodev) sdiodev->sbwad = 0; pm_runtime_allow(sdiodev->func1->card->host->parent); + +#ifdef CONFIG_BRCMF_SDIO_TX_COPY + if (sdiodev->membuf) { + kfree(sdiodev->membuf); + sdiodev->membuf = NULL; + } +#endif /* CONFIG_BRCMF_SDIO_TX_COPY */ + return 0; } @@ -904,6 +1018,14 @@ int brcmf_sdiod_probe(struct brcmf_sdio_dev *sdiodev) int ret = 0; unsigned int f2_blksz = SDIO_FUNC2_BLOCKSIZE; +#ifdef CONFIG_BRCMF_SDIO_TX_COPY + sdiodev->membuf = kzalloc(BRCMF_SDIO_MAX_MEMBUF_SIZE, GFP_KERNEL); + if(!sdiodev->membuf) { + brcmf_err("alloc membuf failed\n"); + return -ENOMEM; + } +#endif /* CONFIG_BRCMF_SDIO_TX_COPY */ + sdio_claim_host(sdiodev->func1); ret = sdio_set_block_size(sdiodev->func1, SDIO_FUNC1_BLOCKSIZE); @@ -924,6 +1046,19 @@ int brcmf_sdiod_probe(struct brcmf_sdio_dev *sdiodev) case SDIO_DEVICE_ID_BROADCOM_4329: f2_blksz = SDIO_4329_FUNC2_BLOCKSIZE; break; + case SDIO_DEVICE_ID_BROADCOM_CYPRESS_89459: + case SDIO_DEVICE_ID_CYPRESS_54590: + case SDIO_DEVICE_ID_CYPRESS_54591: + case SDIO_DEVICE_ID_CYPRESS_54594: + f2_blksz = SDIO_89459_FUNC2_BLOCKSIZE; + break; + case SDIO_DEVICE_ID_CYPRESS_55560: + f2_blksz = SDIO_CYW55560_FUNC2_BLOCKSIZE; + break; + case SDIO_DEVICE_ID_BROADCOM_43455: + f2_blksz = SDIO_CYW43455_FUNC2_BLOCKSIZE; + break; + default: break; } @@ -969,6 +1104,9 @@ int brcmf_sdiod_probe(struct brcmf_sdio_dev *sdiodev) #define BRCMF_SDIO_DEVICE(dev_id) \ {SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, dev_id)} +#define CYF_SDIO_DEVICE(dev_id) \ + {SDIO_DEVICE(SDIO_VENDOR_ID_CYPRESS, dev_id)} + /* devices we support, null terminated */ static const struct sdio_device_id brcmf_sdmmc_ids[] = { BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_43143), @@ -988,9 +1126,15 @@ static const struct sdio_device_id brcmf_sdmmc_ids[] = { BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4354), BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4356), BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4359), + BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_CYPRESS_43439), BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_CYPRESS_4373), BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_CYPRESS_43012), - BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_CYPRESS_89359), + BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_CYPRESS_89459), + CYF_SDIO_DEVICE(SDIO_DEVICE_ID_CYPRESS_43439), + CYF_SDIO_DEVICE(SDIO_DEVICE_ID_CYPRESS_54590), + CYF_SDIO_DEVICE(SDIO_DEVICE_ID_CYPRESS_54591), + CYF_SDIO_DEVICE(SDIO_DEVICE_ID_CYPRESS_54594), + CYF_SDIO_DEVICE(SDIO_DEVICE_ID_CYPRESS_55560), { /* end: all zeroes */ } }; MODULE_DEVICE_TABLE(sdio, brcmf_sdmmc_ids); @@ -1059,6 +1203,7 @@ static int brcmf_ops_sdio_probe(struct sdio_func *func, dev_set_drvdata(&func->dev, bus_if); dev_set_drvdata(&sdiodev->func1->dev, bus_if); sdiodev->dev = &sdiodev->func1->dev; + dev_set_drvdata(&sdiodev->func2->dev, bus_if); brcmf_sdiod_change_state(sdiodev, BRCMF_SDIOD_DOWN); @@ -1075,6 +1220,7 @@ static int brcmf_ops_sdio_probe(struct sdio_func *func, fail: dev_set_drvdata(&func->dev, NULL); dev_set_drvdata(&sdiodev->func1->dev, NULL); + dev_set_drvdata(&sdiodev->func2->dev, NULL); kfree(sdiodev); kfree(bus_if); return err; @@ -1129,15 +1275,27 @@ static int brcmf_ops_sdio_suspend(struct device *dev) struct brcmf_bus *bus_if; struct brcmf_sdio_dev *sdiodev; mmc_pm_flag_t pm_caps, sdio_flags; + struct brcmf_cfg80211_info *config; + int retry = BRCMF_PM_WAIT_MAXRETRY; int ret = 0; func = container_of(dev, struct sdio_func, dev); + bus_if = dev_get_drvdata(dev); + config = bus_if->drvr->config; + brcmf_dbg(SDIO, "Enter: F%d\n", func->num); + + while (retry && + config->pm_state == BRCMF_CFG80211_PM_STATE_SUSPENDING) { + usleep_range(10000, 20000); + retry--; + } + if (!retry && config->pm_state == BRCMF_CFG80211_PM_STATE_SUSPENDING) + brcmf_err("timed out wait for cfg80211 suspended\n"); + if (func->num != 1) return 0; - - bus_if = dev_get_drvdata(dev); sdiodev = bus_if->bus_priv.sdio; pm_caps = sdio_get_host_pm_caps(func); @@ -1213,13 +1371,19 @@ static struct sdio_driver brcmf_sdmmc_driver = { #ifdef CONFIG_PM_SLEEP .pm = &brcmf_sdio_pm_ops, #endif /* CONFIG_PM_SLEEP */ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 17, 0)) .coredump = brcmf_dev_coredump, +#endif }, }; -int brcmf_sdio_register(void) +void brcmf_sdio_register(void) { - return sdio_register_driver(&brcmf_sdmmc_driver); + int ret; + + ret = sdio_register_driver(&brcmf_sdmmc_driver); + if (ret) + brcmf_err("sdio_register_driver failed: %d\n", ret); } void brcmf_sdio_exit(void) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bt_shared_sdio.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bt_shared_sdio.c new file mode 100644 index 0000000000000..45e53c6ee97d9 --- /dev/null +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bt_shared_sdio.c @@ -0,0 +1,326 @@ +// SPDX-License-Identifier: ISC + +/* Copyright 2019, Cypress Semiconductor Corporation or a subsidiary of + * Cypress Semiconductor Corporation. All rights reserved. + * This software, including source code, documentation and related + * materials ("Software"), is owned by Cypress Semiconductor + * Corporation or one of its subsidiaries ("Cypress") and is protected by + * and subject to worldwide patent protection (United States and foreign), + * United States copyright laws and international treaty provisions. + * Therefore, you may use this Software only as provided in the license + * agreement accompanying the software package from which you + * obtained this Software ("EULA"). If no EULA applies, Cypress hereby grants + * you a personal, nonexclusive, non-transferable license to copy, modify, + * and compile the Software source code solely for use in connection with + * Cypress's integrated circuit products. Any reproduction, modification, + * translation, compilation, or representation of this Software except as + * specified above is prohibited without the express written permission of + * Cypress. + * Disclaimer: THIS SOFTWARE IS PROVIDED AS-IS, WITH NO WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, NONINFRINGEMENT, IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. Cypress + * reserves the right to make changes to the Software without notice. Cypress + * does not assume any liability arising out of the application or use of the + * Software or any product or circuit described in the Software. Cypress does + * not authorize its products for use in any products where a malfunction or + * failure of the Cypress product may reasonably be expected to result in + * significant property damage, injury or death ("High Risk Product"). By + * including Cypress's product in a High Risk Product, the manufacturer + * of such system or application assumes all risk of such use and in doing + * so agrees to indemnify Cypress against all liability. + */ + +#include +#include +#include +#include +#include +#include "bus.h" +#include "chipcommon.h" +#include "core.h" +#include "sdio.h" +#include "soc.h" +#include "fwil.h" + +#define SDIOD_ADDR_BOUND 0x1000 +#define SDIOD_ADDR_BOUND_MASK 0xfff + +struct brcmf_bus *g_bus_if; + +enum bus_owner { + WLAN_MODULE = 0, + BT_MODULE +}; + +struct btsdio_info { + u32 bt_buf_reg_addr; + u32 host_ctrl_reg_addr; + u32 bt_ctrl_reg_addr; + u32 bt_buf_addr; + u32 wlan_buf_addr; +}; + +void brcmf_btsdio_int_handler(struct brcmf_bus *bus_if) +{ + struct brcmf_bt_dev *btdev = bus_if->bt_dev; + + if (btdev && btdev->bt_sdio_int_cb) + btdev->bt_sdio_int_cb(btdev->bt_data); +} + +int brcmf_btsdio_init(struct brcmf_bus *bus_if) +{ + if (!bus_if) + return -EINVAL; + + g_bus_if = bus_if; + return 0; +} + +int brcmf_btsdio_attach(struct brcmf_bus *bus_if, void *btdata, + void (*bt_int_fun)(void *data)) +{ + struct brcmf_bt_dev *btdev; + + /* Allocate bt dev */ + btdev = kzalloc(sizeof(*btdev), GFP_ATOMIC); + if (!btdev) + return -ENOMEM; + + btdev->bt_data = btdata; + btdev->bt_sdio_int_cb = bt_int_fun; + bus_if->bt_dev = btdev; + + return 0; +} + +void brcmf_btsdio_detach(struct brcmf_bus *bus_if) +{ + struct brcmf_bt_dev *btdev = bus_if->bt_dev; + + if (!btdev) + return; + + if (btdev->bt_data) + btdev->bt_data = NULL; + if (btdev->bt_sdio_int_cb) + btdev->bt_sdio_int_cb = NULL; + if (bus_if->bt_dev) { + bus_if->bt_dev = NULL; + kfree(btdev); + } +} + +u8 brcmf_btsdio_bus_count(struct brcmf_bus *bus_if) +{ + struct brcmf_bt_dev *btdev = bus_if->bt_dev; + + if (!btdev) + return 0; + + return btdev->use_count; +} + +void *brcmf_bt_sdio_attach(void *btdata, void (*bt_int_fun)(void *data)) +{ + int err; + + if (!g_bus_if) { + brcmf_err("BTSDIO is not initialized\n"); + return NULL; + } + + err = brcmf_btsdio_attach(g_bus_if, btdata, bt_int_fun); + if (err) { + brcmf_err("BTSDIO attach failed, err=%d\n", err); + return NULL; + } + + return (void *)g_bus_if; +} +EXPORT_SYMBOL(brcmf_bt_sdio_attach); + +int brcmf_get_wlan_info(struct brcmf_bus *bus_if, struct btsdio_info *bs_info) +{ + struct brcmf_if *ifp; + + if (!bus_if || !bs_info) + return -EINVAL; + + ifp = bus_if->drvr->iflist[0]; + + bs_info->bt_buf_reg_addr = SI_ENUM_BASE + 0xC00 + + CHIPGCIREGOFFS(gci_input[6]); + bs_info->host_ctrl_reg_addr = SI_ENUM_BASE + 0xC00 + + CHIPGCIREGOFFS(gci_output[3]); + bs_info->bt_ctrl_reg_addr = SI_ENUM_BASE + 0xC00 + + CHIPGCIREGOFFS(gci_input[7]); + brcmf_dbg(INFO, "BT buf reg addr: 0x%x\n", + bs_info->bt_buf_reg_addr); + brcmf_dbg(INFO, "HOST ctrl reg addr: 0x%x\n", + bs_info->host_ctrl_reg_addr); + brcmf_dbg(INFO, "BT ctrl reg addr: 0x%x\n", + bs_info->bt_ctrl_reg_addr); + return 0; +} +EXPORT_SYMBOL(brcmf_get_wlan_info); + +u32 brcmf_bus_reg_read(struct brcmf_bus *bus_if, u32 addr) +{ + struct brcmf_sdio_dev *sdiodev; + int err = 0; + u32 val; + + if (!bus_if) + return -EINVAL; + + sdiodev = bus_if->bus_priv.sdio; + + sdio_claim_host(sdiodev->func1); + val = brcmf_sdiod_readl(sdiodev, addr, &err); + if (err) { + brcmf_err("sdio reg read failed, err=%d\n", err); + sdio_release_host(sdiodev->func1); + return err; + } + sdio_release_host(sdiodev->func1); + + return val; +} +EXPORT_SYMBOL(brcmf_bus_reg_read); + +void brcmf_bus_reg_write(struct brcmf_bus *bus_if, u32 addr, u32 val) +{ + struct brcmf_sdio_dev *sdiodev; + int err = 0; + + if (!bus_if) + return; + + sdiodev = bus_if->bus_priv.sdio; + + sdio_claim_host(sdiodev->func1); + brcmf_sdiod_writel(sdiodev, addr, val, &err); + if (err) + brcmf_err("sdio reg write failed, err=%d\n", err); + sdio_release_host(sdiodev->func1); +} +EXPORT_SYMBOL(brcmf_bus_reg_write); + +int brcmf_membytes(struct brcmf_bus *bus_if, bool set, u32 address, u8 *data, + unsigned int size) +{ + struct brcmf_sdio_dev *sdiodev; + int err = 0; + u32 block1_offset; + u32 block2_addr; + u16 block1_size; + u16 block2_size; + u8 *block2_data; + + if (!bus_if || !data) + return -EINVAL; + + sdiodev = bus_if->bus_priv.sdio; + /* To avoid SDIO access crosses AXI 4k address boundaries crossing */ + if (((address & SDIOD_ADDR_BOUND_MASK) + size) > SDIOD_ADDR_BOUND) { + brcmf_dbg(SDIO, "data cross 4K boundary\n"); + /* The 1st 4k packet */ + block1_offset = address & SDIOD_ADDR_BOUND_MASK; + block1_size = (SDIOD_ADDR_BOUND - block1_offset); + sdio_claim_host(sdiodev->func1); + err = brcmf_sdiod_ramrw(sdiodev, set, address, + data, block1_size); + if (err) { + brcmf_err("sdio memory access failed, err=%d\n", err); + sdio_release_host(sdiodev->func1); + return err; + } + /* The 2nd 4k packet */ + block2_addr = address + block1_size; + block2_size = size - block1_size; + block2_data = data + block1_size; + err = brcmf_sdiod_ramrw(sdiodev, set, block2_addr, + block2_data, block2_size); + if (err) + brcmf_err("sdio memory access failed, err=%d\n", err); + sdio_release_host(sdiodev->func1); + } else { + sdio_claim_host(sdiodev->func1); + err = brcmf_sdiod_ramrw(sdiodev, set, address, data, size); + if (err) + brcmf_err("sdio memory access failed, err=%d\n", err); + sdio_release_host(sdiodev->func1); + } + return err; +} +EXPORT_SYMBOL(brcmf_membytes); + +/* Function to enable the Bus Clock + * This function is not callable from non-sleepable context + */ +int brcmf_bus_clk_enable(struct brcmf_bus *bus_if, enum bus_owner owner) +{ + struct brcmf_sdio_dev *sdiodev; + struct brcmf_bt_dev *btdev; + int err = 0; + + if (!bus_if) + return -EINVAL; + + btdev = bus_if->bt_dev; + sdiodev = bus_if->bus_priv.sdio; + + sdio_claim_host(sdiodev->func1); + btdev->use_count++; + sdio_release_host(sdiodev->func1); + err = brcmf_sdio_sleep(sdiodev->bus, false); + + return err; +} +EXPORT_SYMBOL(brcmf_bus_clk_enable); + +/* Function to disable the Bus Clock + * This function is not callable from non-sleepable context + */ +int brcmf_bus_clk_disable(struct brcmf_bus *bus_if, enum bus_owner owner) +{ + struct brcmf_sdio_dev *sdiodev; + struct brcmf_bt_dev *btdev; + int err = 0; + + if (!bus_if) + return -EINVAL; + + btdev = bus_if->bt_dev; + sdiodev = bus_if->bus_priv.sdio; + + sdio_claim_host(sdiodev->func1); + if (btdev->use_count != 0) + btdev->use_count--; + sdio_release_host(sdiodev->func1); + err = brcmf_sdio_sleep(sdiodev->bus, true); + + return err; +} +EXPORT_SYMBOL(brcmf_bus_clk_disable); + +/* Function to reset bt_use_count counter to zero. + * This function is not callable from non-sleepable context + */ +void brcmf_bus_reset_bt_use_count(struct brcmf_bus *bus_if) +{ + struct brcmf_sdio_dev *sdiodev; + struct brcmf_bt_dev *btdev; + + if (!bus_if) + return; + + btdev = bus_if->bt_dev; + sdiodev = bus_if->bus_priv.sdio; + + sdio_claim_host(sdiodev->func1); + btdev->use_count = 0; + sdio_release_host(sdiodev->func1); +} +EXPORT_SYMBOL(brcmf_bus_reset_bt_use_count); diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bt_shared_sdio.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bt_shared_sdio.h new file mode 100644 index 0000000000000..f0e6b38cf77f3 --- /dev/null +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bt_shared_sdio.h @@ -0,0 +1,43 @@ +/* SPDX-License-Identifier: ISC */ +/* Copyright 2019, Cypress Semiconductor Corporation or a subsidiary of + * Cypress Semiconductor Corporation. All rights reserved. + * This software, including source code, documentation and related + * materials ("Software"), is owned by Cypress Semiconductor + * Corporation or one of its subsidiaries ("Cypress") and is protected by + * and subject to worldwide patent protection (United States and foreign), + * United States copyright laws and international treaty provisions. + * Therefore, you may use this Software only as provided in the license + * agreement accompanying the software package from which you + * obtained this Software ("EULA"). If no EULA applies, Cypress hereby grants + * you a personal, nonexclusive, non-transferable license to copy, modify, + * and compile the Software source code solely for use in connection with + * Cypress's integrated circuit products. Any reproduction, modification, + * translation, compilation, or representation of this Software except as + * specified above is prohibited without the express written permission of + * Cypress. + * Disclaimer: THIS SOFTWARE IS PROVIDED AS-IS, WITH NO WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, NONINFRINGEMENT, IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. Cypress + * reserves the right to make changes to the Software without notice. Cypress + * does not assume any liability arising out of the application or use of the + * Software or any product or circuit described in the Software. Cypress does + * not authorize its products for use in any products where a malfunction or + * failure of the Cypress product may reasonably be expected to result in + * significant property damage, injury or death ("High Risk Product"). By + * including Cypress's product in a High Risk Product, the manufacturer + * of such system or application assumes all risk of such use and in doing + * so agrees to indemnify Cypress against all liability. + */ + +#ifdef CONFIG_BRCMFMAC_BT_SHARED_SDIO +int brcmf_btsdio_init(struct brcmf_bus *bus_if); +void brcmf_btsdio_detach(struct brcmf_bus *bus_if); +void brcmf_btsdio_int_handler(struct brcmf_bus *bus_if); +u8 brcmf_btsdio_bus_count(struct brcmf_bus *bus_if); +#else +static inline +u8 brcmf_btsdio_bus_count(struct brcmf_bus *bus_if) +{ + return 0; +} +#endif /* CONFIG_BRCMFMAC_BT_SHARED_SDIO */ diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h index 3f5da3bb6aa59..32f4d53bc45e0 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h @@ -6,7 +6,11 @@ #ifndef BRCMFMAC_BUS_H #define BRCMFMAC_BUS_H +#include #include "debug.h" +#include + +#define CONFIG_BRCMF_CMD_TIMEOUT_REBOOT /* IDs of the 6 default common rings of msgbuf protocol */ #define BRCMF_H2D_MSGRING_CONTROL_SUBMIT 0 @@ -22,6 +26,15 @@ #define BRCMF_NROF_COMMON_MSGRINGS (BRCMF_NROF_H2D_COMMON_MSGRINGS + \ BRCMF_NROF_D2H_COMMON_MSGRINGS) +/* The interval to poll console */ +#define BRCMF_CONSOLE 10 + +/* The maximum console interval value (5 mins) */ +#define MAX_CONSOLE_INTERVAL (5 * 60) + +/* The maximum firmware command timeout retry count value */ +#define BRCMF_MAX_CMD_TIMEOUT_RETRIES (1) + /* The level of bus communication with the dongle */ enum brcmf_bus_state { BRCMF_BUS_DOWN, /* Not ready for frame transfers */ @@ -117,6 +130,19 @@ struct brcmf_bus_stats { atomic_t pktcow_failed; }; +/** + * struct brcmf_bt_dev - bt shared SDIO device. + * + * @ bt_data: bt internal structure data + * @ bt_sdio_int_cb: bt registered interrupt callback function + * @ bt_use_count: Counter that tracks whether BT is using the bus + */ +struct brcmf_bt_dev { + void *bt_data; + void (*bt_sdio_int_cb)(void *data); + u32 use_count; /* Counter for tracking if BT is using the bus */ +}; + /** * struct brcmf_bus - interface structure between common and bus layer * @@ -132,6 +158,7 @@ struct brcmf_bus_stats { * @wowl_supported: is wowl supported by bus driver. * @chiprev: revision of the dongle chip. * @msgbuf: msgbuf protocol parameters provided by bus layer. + * @bt_dev: bt shared SDIO device */ struct brcmf_bus { union { @@ -152,6 +179,12 @@ struct brcmf_bus { const struct brcmf_bus_ops *ops; struct brcmf_bus_msgbuf *msgbuf; +#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 16, 0)) + bool allow_skborphan; +#endif +#ifdef CONFIG_BRCMFMAC_BT_SHARED_SDIO + struct brcmf_bt_dev *bt_dev; +#endif /* CONFIG_BRCMFMAC_BT_SHARED_SDIO */ }; /* @@ -256,7 +289,7 @@ void brcmf_rx_event(struct device *dev, struct sk_buff *rxp); int brcmf_alloc(struct device *dev, struct brcmf_mp_device *settings); /* Indication from bus module regarding presence/insertion of dongle. */ -int brcmf_attach(struct device *dev); +int brcmf_attach(struct device *dev, bool start_bus); /* Indication from bus module regarding removal/absence of dongle */ void brcmf_detach(struct device *dev); void brcmf_free(struct device *dev); @@ -270,31 +303,22 @@ void brcmf_fw_crashed(struct device *dev); /* Configure the "global" bus state used by upper layers */ void brcmf_bus_change_state(struct brcmf_bus *bus, enum brcmf_bus_state state); +#ifdef CONFIG_BRCMF_CMD_TIMEOUT_REBOOT +/* Handle firmware command timeout */ +void brcmf_bus_handle_cmd_timeout(struct brcmf_bus *bus); +#endif /* CONFIG_BRCMF_CMD_TIMEOUT_REBOOT */ + s32 brcmf_iovar_data_set(struct device *dev, char *name, void *data, u32 len); void brcmf_bus_add_txhdrlen(struct device *dev, uint len); +int brcmf_fwlog_attach(struct device *dev); #ifdef CONFIG_BRCMFMAC_SDIO void brcmf_sdio_exit(void); -int brcmf_sdio_register(void); -#else -static inline void brcmf_sdio_exit(void) { } -static inline int brcmf_sdio_register(void) { return 0; } +void brcmf_sdio_register(void); #endif - #ifdef CONFIG_BRCMFMAC_USB void brcmf_usb_exit(void); -int brcmf_usb_register(void); -#else -static inline void brcmf_usb_exit(void) { } -static inline int brcmf_usb_register(void) { return 0; } -#endif - -#ifdef CONFIG_BRCMFMAC_PCIE -void brcmf_pcie_exit(void); -int brcmf_pcie_register(void); -#else -static inline void brcmf_pcie_exit(void) { } -static inline int brcmf_pcie_register(void) { return 0; } +void brcmf_usb_register(void); #endif #endif /* BRCMFMAC_BUS_H */ diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c index c2b6e5c966d04..36c6153986d4f 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c @@ -39,6 +39,7 @@ #define RSN_OUI "\x00\x0F\xAC" /* RSN OUI */ #define WME_OUI_TYPE 2 #define WPS_OUI_TYPE 4 +#define WFA_OUI_TYPE_MBO_OCE 0x16 #define VS_IE_FIXED_HDR_LEN 6 #define WPA_IE_VERSION_LEN 2 @@ -63,6 +64,13 @@ #define RSN_CAP_MFPC_MASK BIT(7) #define RSN_PMKID_COUNT_LEN 2 +#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 11, 0)) +#define SUITE(oui, id) (((oui) << 8) | (id)) +#endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(4, 11, 0)) */ + +#define DPP_AKM_SUITE_TYPE 2 +#define WLAN_AKM_SUITE_DPP SUITE(WLAN_OUI_WFA, DPP_AKM_SUITE_TYPE) + #define VNDR_IE_CMD_LEN 4 /* length of the set command * string :"add", "del" (+ NUL) */ @@ -75,8 +83,10 @@ #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_ACTIVE_DWELL_TIME_MS 320 +#define BRCMF_SCAN_JOIN_PASSIVE_DWELL_TIME_MS 400 +#define BRCMF_SCAN_JOIN_ACTIVE_DWELL_TIME_MS_6E 80 +#define BRCMF_SCAN_JOIN_PASSIVE_DWELL_TIME_MS_6E 130 #define BRCMF_SCAN_JOIN_PROBE_INTERVAL_MS 20 #define BRCMF_SCAN_CHANNEL_TIME 40 @@ -85,11 +95,42 @@ #define BRCMF_ND_INFO_TIMEOUT msecs_to_jiffies(2000) -#define BRCMF_PS_MAX_TIMEOUT_MS 2000 +#define MGMT_AUTH_FRAME_DWELL_TIME 4000 +#define MGMT_AUTH_FRAME_WAIT_TIME (MGMT_AUTH_FRAME_DWELL_TIME + 100) + +/* Dump obss definitions */ +#define ACS_MSRMNT_DELAY 80 +#define CHAN_NOISE_DUMMY (-80) +#define OBSS_TOKEN_IDX 15 +#define IBSS_TOKEN_IDX 15 +#define TX_TOKEN_IDX 14 +#define CTG_TOKEN_IDX 13 +#define PKT_TOKEN_IDX 15 +#define IDLE_TOKEN_IDX 12 #define BRCMF_ASSOC_PARAMS_FIXED_SIZE \ (sizeof(struct brcmf_assoc_params_le) - sizeof(u16)) +struct brcmf_dump_survey { + u32 obss; + u32 ibss; + u32 no_ctg; + u32 no_pckt; + u32 tx; + u32 idle; +}; + +struct cca_stats_n_flags { + u32 msrmnt_time; /* Time for Measurement (msec) */ + u32 msrmnt_done; /* flag set when measurement complete */ + char buf[1]; +}; + +struct cca_msrmnt_query { + u32 msrmnt_query; + u32 time_req; +}; + static bool check_vif_up(struct brcmf_cfg80211_vif *vif) { if (!test_bit(BRCMF_VIF_STATUS_READY, &vif->sme_state)) { @@ -144,6 +185,16 @@ static struct ieee80211_rate __wl_rates[] = { .max_power = 30, \ } +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0)) +#define CHAN6G(_channel) { \ + .band = NL80211_BAND_6GHZ, \ + .center_freq = 5950 + (5 * (_channel)), \ + .hw_value = (_channel), \ + .max_antenna_gain = 0, \ + .max_power = 30, \ +} +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0)) */ + static struct ieee80211_channel __wl_2ghz_channels[] = { CHAN2G(1, 2412), CHAN2G(2, 2417), CHAN2G(3, 2422), CHAN2G(4, 2427), CHAN2G(5, 2432), CHAN2G(6, 2437), CHAN2G(7, 2442), CHAN2G(8, 2447), @@ -160,6 +211,27 @@ static struct ieee80211_channel __wl_5ghz_channels[] = { CHAN5G(153), CHAN5G(157), CHAN5G(161), CHAN5G(165) }; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0)) +static struct ieee80211_channel __wl_6ghz_channels[] = { + CHAN6G(1), CHAN6G(5), CHAN6G(9), CHAN6G(13), CHAN6G(17), + CHAN6G(21), CHAN6G(25), CHAN6G(29), CHAN6G(33), CHAN6G(37), + CHAN6G(41), CHAN6G(45), CHAN6G(49), CHAN6G(53), CHAN6G(57), + CHAN6G(61), CHAN6G(65), CHAN6G(69), CHAN6G(73), CHAN6G(77), + CHAN6G(81), CHAN6G(85), CHAN6G(89), CHAN6G(93), CHAN6G(97), + CHAN6G(101), CHAN6G(105), CHAN6G(109), CHAN6G(113), CHAN6G(117), + CHAN6G(121), CHAN6G(125), CHAN6G(129), CHAN6G(133), CHAN6G(137), + CHAN6G(141), CHAN6G(145), CHAN6G(149), CHAN6G(153), CHAN6G(157), + CHAN6G(161), CHAN6G(165), CHAN6G(169), CHAN6G(173), CHAN6G(177), + CHAN6G(181), CHAN6G(185), CHAN6G(189), CHAN6G(193), CHAN6G(197), + CHAN6G(201), CHAN6G(205), CHAN6G(209), CHAN6G(213), CHAN6G(217), + CHAN6G(221), CHAN6G(225), CHAN6G(229), CHAN6G(233) +}; +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0)) */ + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0)) +struct ieee80211_sband_iftype_data sdata[NUM_NL80211_BANDS]; +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0)) */ + /* Band templates duplicated per wiphy. The channel info * above is added to the band during setup. */ @@ -175,6 +247,14 @@ static const struct ieee80211_supported_band __wl_band_5ghz = { .n_bitrates = wl_a_rates_size, }; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0)) +static struct ieee80211_supported_band __wl_band_6ghz = { + .band = NL80211_BAND_6GHZ, + .bitrates = wl_a_rates, + .n_bitrates = wl_a_rates_size, +}; +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0)) */ + /* 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). @@ -183,7 +263,7 @@ static const struct ieee80211_supported_band __wl_band_5ghz = { * domain are to be done here. */ static const struct ieee80211_regdomain brcmf_regdom = { - .n_reg_rules = 4, + .n_reg_rules = 5, .alpha2 = "99", .reg_rules = { /* IEEE 802.11b/g, channels 1..11 */ @@ -196,22 +276,31 @@ static const struct ieee80211_regdomain brcmf_regdom = { /* IEEE 802.11a, channel 36..64 */ REG_RULE(5150-10, 5350+10, 160, 6, 20, 0), /* IEEE 802.11a, channel 100..165 */ - REG_RULE(5470-10, 5850+10, 160, 6, 20, 0), } + REG_RULE(5470-10, 5850+10, 160, 6, 20, 0), + /* IEEE 802.11ax, 6E */ + REG_RULE(5935-10, 7115+10, 160, 6, 20, 0), + } }; /* Note: brcmf_cipher_suites is an array of int defining which cipher suites * are supported. A pointer to this array and the number of entries is passed * on to upper layers. AES_CMAC defines whether or not the driver supports MFP. * So the cipher suite AES_CMAC has to be the last one in the array, and when - * device does not support MFP then the number of suites will be decreased by 1 + * device does not support MFP then the number of suites will be decreased by 4 */ static const u32 brcmf_cipher_suites[] = { WLAN_CIPHER_SUITE_WEP40, WLAN_CIPHER_SUITE_WEP104, WLAN_CIPHER_SUITE_TKIP, WLAN_CIPHER_SUITE_CCMP, + WLAN_CIPHER_SUITE_CCMP_256, + WLAN_CIPHER_SUITE_GCMP, + WLAN_CIPHER_SUITE_GCMP_256, /* Keep as last entry: */ - WLAN_CIPHER_SUITE_AES_CMAC + WLAN_CIPHER_SUITE_AES_CMAC, + WLAN_CIPHER_SUITE_BIP_CMAC_256, + WLAN_CIPHER_SUITE_BIP_GMAC_128, + WLAN_CIPHER_SUITE_BIP_GMAC_256 }; /* Vendor specific ie. id = 221, oui and type defines exact ie */ @@ -233,6 +322,88 @@ struct parsed_vndr_ies { struct parsed_vndr_ie_info ie_info[VNDR_IE_PARSE_LIMIT]; }; +#define WLC_E_IF_ROLE_STA 0 /* Infra STA */ +#define WLC_E_IF_ROLE_AP 1 /* Access Point */ + +#define WL_INTERFACE_CREATE_VER_1 1 +#define WL_INTERFACE_CREATE_VER_2 2 +#define WL_INTERFACE_CREATE_VER_3 3 +#define WL_INTERFACE_CREATE_VER_MAX WL_INTERFACE_CREATE_VER_3 + +#define WL_INTERFACE_MAC_DONT_USE 0x0 +#define WL_INTERFACE_MAC_USE 0x2 + +#define WL_INTERFACE_CREATE_STA 0x0 +#define WL_INTERFACE_CREATE_AP 0x1 + +struct wl_interface_create_v1 { + u16 ver; /* structure version */ + u32 flags; /* flags for operation */ + u8 mac_addr[ETH_ALEN]; /* MAC address */ + u32 wlc_index; /* optional for wlc index */ +}; + +struct wl_interface_create_v2 { + u16 ver; /* structure version */ + u8 pad1[2]; + u32 flags; /* flags for operation */ + u8 mac_addr[ETH_ALEN]; /* MAC address */ + u8 iftype; /* type of interface created */ + u8 pad2; + u32 wlc_index; /* optional for wlc index */ +}; + +struct wl_interface_create_v3 { + u16 ver; /* structure version */ + u16 len; /* length of structure + data */ + u16 fixed_len; /* length of structure */ + u8 iftype; /* type of interface created */ + u8 wlc_index; /* optional for wlc index */ + u32 flags; /* flags for operation */ + u8 mac_addr[ETH_ALEN]; /* MAC address */ + u8 bssid[ETH_ALEN]; /* optional for BSSID */ + u8 if_index; /* interface index request */ + u8 pad[3]; + u8 data[]; /* Optional for specific data */ +}; + +static int brcmf_setup_wiphybands(struct brcmf_cfg80211_info *cfg); +static bool +wl_cfgoce_has_ie(const u8 *ie, const u8 **tlvs, u32 *tlvs_len, + const u8 *oui, u32 oui_len, u8 type); + +/* Check whether the given IE looks like WFA OCE IE. */ +#define wl_cfgoce_is_oce_ie(ie, tlvs, len) \ + wl_cfgoce_has_ie(ie, tlvs, len, \ + (const u8 *)WFA_OUI, TLV_OUI_LEN, WFA_OUI_TYPE_MBO_OCE) + +/* Is any of the tlvs the expected entry? If + * not update the tlvs buffer pointer/length. + */ +static bool +wl_cfgoce_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) + 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; +} + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 7, 0)) static u8 nl80211_band_to_fwil(enum nl80211_band band) { switch (band) { @@ -240,12 +411,17 @@ static u8 nl80211_band_to_fwil(enum nl80211_band band) return WLC_BAND_2G; case NL80211_BAND_5GHZ: return WLC_BAND_5G; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0)) + case NL80211_BAND_6GHZ: + return WLC_BAND_6G; +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0)) */ default: WARN_ON(1); break; } return 0; } +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 7, 0)) */ static u16 chandef_to_chanspec(struct brcmu_d11inf *d11inf, struct cfg80211_chan_def *ch) @@ -313,6 +489,11 @@ static u16 chandef_to_chanspec(struct brcmu_d11inf *d11inf, case NL80211_BAND_5GHZ: ch_inf.band = BRCMU_CHAN_BAND_5G; break; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0)) + case NL80211_BAND_6GHZ: + ch_inf.band = BRCMU_CHAN_BAND_6G; + break; +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0)) */ case NL80211_BAND_60GHZ: default: WARN_ON_ONCE(1); @@ -328,6 +509,22 @@ u16 channel_to_chanspec(struct brcmu_d11inf *d11inf, { struct brcmu_chan ch_inf; + switch (ch->band) { + case NL80211_BAND_2GHZ: + ch_inf.band = BRCMU_CHAN_BAND_2G; + break; + case NL80211_BAND_5GHZ: + ch_inf.band = BRCMU_CHAN_BAND_5G; + break; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0)) + case NL80211_BAND_6GHZ: + ch_inf.band = BRCMU_CHAN_BAND_6G; + break; +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0)) */ + case NL80211_BAND_60GHZ: + default: + WARN_ON_ONCE(1); + } ch_inf.chnum = ieee80211_frequency_to_channel(ch->center_freq); ch_inf.bw = BRCMU_CHAN_BW_20; d11inf->encchspec(&ch_inf); @@ -412,6 +609,8 @@ brcmf_find_wpsie(const u8 *parse, u32 len) return NULL; } +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0)) +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0)) */ static int brcmf_vif_change_validate(struct brcmf_cfg80211_info *cfg, struct brcmf_cfg80211_vif *vif, enum nl80211_iftype new_type) @@ -419,6 +618,7 @@ static int brcmf_vif_change_validate(struct brcmf_cfg80211_info *cfg, struct brcmf_cfg80211_vif *pos; bool check_combos = false; int ret = 0; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0)) struct iface_combination_params params = { .num_different_channels = 1, }; @@ -435,6 +635,23 @@ static int brcmf_vif_change_validate(struct brcmf_cfg80211_info *cfg, if (check_combos) ret = cfg80211_check_combinations(cfg->wiphy, ¶ms); +#else /* (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0)) */ + int iftype_num[NUM_NL80211_IFTYPES]; + + memset(&iftype_num[0], 0, sizeof(iftype_num)); + list_for_each_entry(pos, &cfg->vif_list, list) + if (pos == vif) { + iftype_num[new_type]++; + } else { + /* concurrent interfaces so need check combinations */ + check_combos = true; + iftype_num[pos->wdev.iftype]++; + } + + if (check_combos) + ret = cfg80211_check_combinations(cfg->wiphy, 1, 0, iftype_num); +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0)) */ + return ret; } @@ -442,6 +659,7 @@ static int brcmf_vif_add_validate(struct brcmf_cfg80211_info *cfg, enum nl80211_iftype new_type) { struct brcmf_cfg80211_vif *pos; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0)) struct iface_combination_params params = { .num_different_channels = 1, }; @@ -451,6 +669,16 @@ static int brcmf_vif_add_validate(struct brcmf_cfg80211_info *cfg, params.iftype_num[new_type]++; return cfg80211_check_combinations(cfg->wiphy, ¶ms); +#else /* (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0)) */ + int iftype_num[NUM_NL80211_IFTYPES]; + + memset(&iftype_num[0], 0, sizeof(iftype_num)); + list_for_each_entry(pos, &cfg->vif_list, list) + iftype_num[pos->wdev.iftype]++; + + iftype_num[new_type]++; + return cfg80211_check_combinations(cfg->wiphy, 1, 0, iftype_num); +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0)) */ } static void convert_key_from_CPU(struct brcmf_wsec_key *key, @@ -486,7 +714,7 @@ send_key_to_dongle(struct brcmf_if *ifp, struct brcmf_wsec_key *key) return err; } -static void +void brcmf_cfg80211_update_proto_addr_mode(struct wireless_dev *wdev) { struct brcmf_cfg80211_vif *vif; @@ -520,40 +748,227 @@ static int brcmf_get_first_free_bsscfgidx(struct brcmf_pub *drvr) return -ENOMEM; } +static void brcmf_set_vif_sta_macaddr(struct brcmf_if *ifp, u8 *mac_addr) +{ + u8 mac_idx = ifp->drvr->sta_mac_idx; + + /* set difference MAC address with locally administered bit */ + memcpy(mac_addr, ifp->mac_addr, ETH_ALEN); + mac_addr[0] |= 0x02; + mac_addr[3] ^= mac_idx ? 0xC0 : 0xA0; + mac_idx++; + mac_idx = mac_idx % 2; + ifp->drvr->sta_mac_idx = mac_idx; +} + +static int brcmf_cfg80211_request_sta_if(struct brcmf_if *ifp, u8 *macaddr) +{ + struct wl_interface_create_v1 iface_v1; + struct wl_interface_create_v2 iface_v2; + struct wl_interface_create_v3 iface_v3; + u32 iface_create_ver; + int err; + + /* interface_create version 1 */ + memset(&iface_v1, 0, sizeof(iface_v1)); + iface_v1.ver = WL_INTERFACE_CREATE_VER_1; + iface_v1.flags = WL_INTERFACE_CREATE_STA | + WL_INTERFACE_MAC_USE; + if (!is_zero_ether_addr(macaddr)) + memcpy(iface_v1.mac_addr, macaddr, ETH_ALEN); + else + brcmf_set_vif_sta_macaddr(ifp, iface_v1.mac_addr); + + err = brcmf_fil_iovar_data_get(ifp, "interface_create", + &iface_v1, + sizeof(iface_v1)); + if (err) { + brcmf_dbg(INFO, "failed to create interface(v1), err=%d\n", + err); + } else { + brcmf_dbg(INFO, "interface created(v1)\n"); + return 0; + } + + /* interface_create version 2 */ + memset(&iface_v2, 0, sizeof(iface_v2)); + iface_v2.ver = WL_INTERFACE_CREATE_VER_2; + iface_v2.flags = WL_INTERFACE_MAC_USE; + iface_v2.iftype = WL_INTERFACE_CREATE_STA; + if (!is_zero_ether_addr(macaddr)) + memcpy(iface_v2.mac_addr, macaddr, ETH_ALEN); + else + brcmf_set_vif_sta_macaddr(ifp, iface_v2.mac_addr); + + err = brcmf_fil_iovar_data_get(ifp, "interface_create", + &iface_v2, + sizeof(iface_v2)); + if (err) { + brcmf_dbg(INFO, "failed to create interface(v2), err=%d\n", + err); + } else { + brcmf_dbg(INFO, "interface created(v2)\n"); + return 0; + } + + /* interface_create version 3+ */ + /* get supported version from firmware side */ + iface_create_ver = 0; + err = brcmf_fil_bsscfg_int_get(ifp, "interface_create", + &iface_create_ver); + if (err) { + brcmf_err("fail to get supported version, err=%d\n", err); + return -EOPNOTSUPP; + } + + switch (iface_create_ver) { + case WL_INTERFACE_CREATE_VER_3: + memset(&iface_v3, 0, sizeof(iface_v3)); + iface_v3.ver = WL_INTERFACE_CREATE_VER_3; + iface_v3.flags = WL_INTERFACE_MAC_USE; + iface_v3.iftype = WL_INTERFACE_CREATE_STA; + if (!is_zero_ether_addr(macaddr)) + memcpy(iface_v3.mac_addr, macaddr, ETH_ALEN); + else + brcmf_set_vif_sta_macaddr(ifp, iface_v3.mac_addr); + + err = brcmf_fil_iovar_data_get(ifp, "interface_create", + &iface_v3, + sizeof(iface_v3)); + + if (!err) + brcmf_dbg(INFO, "interface created(v3)\n"); + break; + default: + brcmf_err("not support interface create(v%d)\n", + iface_create_ver); + err = -EOPNOTSUPP; + break; + } + + if (err) { + brcmf_info("station interface creation failed (%d)\n", + err); + return -EIO; + } + + return 0; +} + static int brcmf_cfg80211_request_ap_if(struct brcmf_if *ifp) { + struct wl_interface_create_v1 iface_v1; + struct wl_interface_create_v2 iface_v2; + struct wl_interface_create_v3 iface_v3; + u32 iface_create_ver; struct brcmf_pub *drvr = ifp->drvr; struct brcmf_mbss_ssid_le mbss_ssid_le; int bsscfgidx; int err; - memset(&mbss_ssid_le, 0, sizeof(mbss_ssid_le)); - bsscfgidx = brcmf_get_first_free_bsscfgidx(ifp->drvr); - if (bsscfgidx < 0) - return bsscfgidx; + /* interface_create version 1 */ + memset(&iface_v1, 0, sizeof(iface_v1)); + iface_v1.ver = WL_INTERFACE_CREATE_VER_1; + iface_v1.flags = WL_INTERFACE_CREATE_AP | + WL_INTERFACE_MAC_USE; - 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); + brcmf_set_vif_sta_macaddr(ifp, iface_v1.mac_addr); - err = brcmf_fil_bsscfg_data_set(ifp, "bsscfg:ssid", &mbss_ssid_le, - sizeof(mbss_ssid_le)); - if (err < 0) - bphy_err(drvr, "setting ssid failed %d\n", err); + err = brcmf_fil_iovar_data_get(ifp, "interface_create", + &iface_v1, + sizeof(iface_v1)); + if (err) { + brcmf_dbg(INFO, "failed to create interface(v1), err=%d\n", + err); + } else { + brcmf_dbg(INFO, "interface created(v1)\n"); + return 0; + } + + /* interface_create version 2 */ + memset(&iface_v2, 0, sizeof(iface_v2)); + iface_v2.ver = WL_INTERFACE_CREATE_VER_2; + iface_v2.flags = WL_INTERFACE_MAC_USE; + iface_v2.iftype = WL_INTERFACE_CREATE_AP; + + brcmf_set_vif_sta_macaddr(ifp, iface_v2.mac_addr); + + err = brcmf_fil_iovar_data_get(ifp, "interface_create", + &iface_v2, + sizeof(iface_v2)); + if (err) { + brcmf_dbg(INFO, "failed to create interface(v2), err=%d\n", + err); + } else { + brcmf_dbg(INFO, "interface created(v2)\n"); + return 0; + } + + /* interface_create version 3+ */ + /* get supported version from firmware side */ + iface_create_ver = 0; + err = brcmf_fil_bsscfg_int_get(ifp, "interface_create", + &iface_create_ver); + if (err) { + brcmf_err("fail to get supported version, err=%d\n", err); + return -EOPNOTSUPP; + } + + switch (iface_create_ver) { + case WL_INTERFACE_CREATE_VER_3: + memset(&iface_v3, 0, sizeof(iface_v3)); + iface_v3.ver = WL_INTERFACE_CREATE_VER_3; + iface_v3.flags = WL_INTERFACE_MAC_USE; + iface_v3.iftype = WL_INTERFACE_CREATE_AP; + brcmf_set_vif_sta_macaddr(ifp, iface_v3.mac_addr); + + err = brcmf_fil_iovar_data_get(ifp, "interface_create", + &iface_v3, + sizeof(iface_v3)); + + if (!err) + brcmf_dbg(INFO, "interface created(v3)\n"); + break; + default: + brcmf_err("not support interface create(v%d)\n", + iface_create_ver); + err = -EOPNOTSUPP; + break; + } + + if (err) { + brcmf_info("Does not support interface_create (%d)\n", + err); + memset(&mbss_ssid_le, 0, sizeof(mbss_ssid_le)); + bsscfgidx = brcmf_get_first_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) + bphy_err(drvr, "setting ssid failed %d\n", err); + } return err; } /** - * brcmf_ap_add_vif() - create a new AP virtual interface for multiple BSS + * brcmf_ap_add_vif() - create a new AP or STA virtual interface * * @wiphy: wiphy device of new interface. * @name: name of the new interface. - * @params: contains mac address for AP device. + * @params: contains mac address for AP or STA device. */ static -struct wireless_dev *brcmf_ap_add_vif(struct wiphy *wiphy, const char *name, - struct vif_params *params) +struct wireless_dev *brcmf_apsta_add_vif(struct wiphy *wiphy, const char *name, + struct vif_params *params, + enum nl80211_iftype type) { struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg)); @@ -561,18 +976,24 @@ struct wireless_dev *brcmf_ap_add_vif(struct wiphy *wiphy, const char *name, struct brcmf_cfg80211_vif *vif; int err; + if (type != NL80211_IFTYPE_STATION && type != NL80211_IFTYPE_AP) + return ERR_PTR(-EINVAL); + 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); + vif = brcmf_alloc_vif(cfg, type); if (IS_ERR(vif)) return (struct wireless_dev *)vif; brcmf_cfg80211_arm_vif_event(cfg, vif); - err = brcmf_cfg80211_request_ap_if(ifp); + if (type == NL80211_IFTYPE_STATION) + err = brcmf_cfg80211_request_sta_if(ifp, params->macaddr); + else + err = brcmf_cfg80211_request_ap_if(ifp); if (err) { brcmf_cfg80211_arm_vif_event(cfg, NULL); goto fail; @@ -624,6 +1045,15 @@ static bool brcmf_is_ibssmode(struct brcmf_cfg80211_vif *vif) return vif->wdev.iftype == NL80211_IFTYPE_ADHOC; } +#undef netdev_set_priv_destructor +#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 11, 9)) +#define netdev_set_priv_destructor(_dev, _destructor) \ + (_dev)->destructor = (_destructor) +#else +#define netdev_set_priv_destructor(_dev, _destructor) \ + (_dev)->priv_destructor = (_destructor) +#endif + /** * brcmf_mon_add_vif() - create monitor mode virtual interface * @@ -657,8 +1087,7 @@ static struct wireless_dev *brcmf_mon_add_vif(struct wiphy *wiphy, } ndev->type = ARPHRD_IEEE80211_RADIOTAP; ndev->ieee80211_ptr = &vif->wdev; - ndev->needs_free_netdev = true; - ndev->priv_destructor = brcmf_cfg80211_free_netdev; + netdev_set_priv_destructor(ndev, brcmf_cfg80211_free_netdev); SET_NETDEV_DEV(ndev, wiphy_dev(cfg->wiphy)); ifp = netdev_priv(ndev); @@ -702,8 +1131,13 @@ static int brcmf_mon_del_vif(struct wiphy *wiphy, struct wireless_dev *wdev) static struct wireless_dev *brcmf_cfg80211_add_iface(struct wiphy *wiphy, const char *name, +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 1, 0)) unsigned char name_assign_type, +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 1, 0)) */ enum nl80211_iftype type, +#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 12, 0)) + u32 *flags, +#endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(4, 12, 0)) */ struct vif_params *params) { struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); @@ -719,15 +1153,15 @@ 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_VLAN: case NL80211_IFTYPE_WDS: case NL80211_IFTYPE_MESH_POINT: return ERR_PTR(-EOPNOTSUPP); case NL80211_IFTYPE_MONITOR: return brcmf_mon_add_vif(wiphy, name); + case NL80211_IFTYPE_STATION: case NL80211_IFTYPE_AP: - wdev = brcmf_ap_add_vif(wiphy, name, params); + wdev = brcmf_apsta_add_vif(wiphy, name, params, type); break; case NL80211_IFTYPE_P2P_CLIENT: case NL80211_IFTYPE_P2P_GO: @@ -758,9 +1192,16 @@ void brcmf_set_mpc(struct brcmf_if *ifp, int mpc) { struct brcmf_pub *drvr = ifp->drvr; s32 err = 0; + struct brcmf_cfg80211_info *cfg = ifp->drvr->config; + ifp->drvr->req_mpc = mpc; if (check_vif_up(ifp->vif)) { - err = brcmf_fil_iovar_int_set(ifp, "mpc", mpc); + if (cfg->pwr_save) + err = brcmf_fil_iovar_int_set(ifp, "mpc", + ifp->drvr->req_mpc); + else + err = brcmf_fil_iovar_int_set(ifp, "mpc", 0); + if (err) { bphy_err(drvr, "fail to set mpc\n"); return; @@ -769,6 +1210,21 @@ void brcmf_set_mpc(struct brcmf_if *ifp, int mpc) } } +bool brcmf_is_apmode_operating(struct wiphy *wiphy) +{ + struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); + struct brcmf_cfg80211_vif *vif; + bool ret = false; + + list_for_each_entry(vif, &cfg->vif_list, list) { + if (brcmf_is_apmode(vif) && + test_bit(BRCMF_VIF_STATUS_AP_CREATED, &vif->sme_state)) + ret = true; + } + + return ret; +} + s32 brcmf_notify_escan_complete(struct brcmf_cfg80211_info *cfg, struct brcmf_if *ifp, bool aborted, bool fw_abort) @@ -828,11 +1284,16 @@ s32 brcmf_notify_escan_complete(struct brcmf_cfg80211_info *cfg, if (!aborted) { brcmf_dbg(SCAN, "report results: reqid=%llu\n", reqid); +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)) cfg80211_sched_scan_results(cfg_to_wiphy(cfg), reqid); +#else /* (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)) */ + cfg80211_sched_scan_results(cfg_to_wiphy(cfg)); +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)) */ } } } else if (scan_request) { +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 8, 0)) struct cfg80211_scan_info info = { .aborted = aborted, }; @@ -840,6 +1301,11 @@ s32 brcmf_notify_escan_complete(struct brcmf_cfg80211_info *cfg, brcmf_dbg(SCAN, "ESCAN Completed scan: %s\n", aborted ? "Aborted" : "Done"); cfg80211_scan_done(scan_request, &info); +#else /* (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 8, 0)) */ + brcmf_dbg(SCAN, "ESCAN Completed scan: %s\n", + aborted ? "Aborted" : "Done"); + cfg80211_scan_done(scan_request, aborted); +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 8, 0)) */ } if (!test_and_clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) brcmf_dbg(SCAN, "Scan complete, probably P2P scan\n"); @@ -847,8 +1313,8 @@ s32 brcmf_notify_escan_complete(struct brcmf_cfg80211_info *cfg, return err; } -static int brcmf_cfg80211_del_ap_iface(struct wiphy *wiphy, - struct wireless_dev *wdev) +static int brcmf_cfg80211_del_apsta_iface(struct wiphy *wiphy, + struct wireless_dev *wdev) { struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); struct net_device *ndev = wdev->netdev; @@ -905,15 +1371,15 @@ int brcmf_cfg80211_del_iface(struct wiphy *wiphy, struct wireless_dev *wdev) switch (wdev->iftype) { case NL80211_IFTYPE_ADHOC: - case NL80211_IFTYPE_STATION: case NL80211_IFTYPE_AP_VLAN: case NL80211_IFTYPE_WDS: case NL80211_IFTYPE_MESH_POINT: return -EOPNOTSUPP; case NL80211_IFTYPE_MONITOR: return brcmf_mon_del_vif(wiphy, wdev); + case NL80211_IFTYPE_STATION: case NL80211_IFTYPE_AP: - return brcmf_cfg80211_del_ap_iface(wiphy, wdev); + return brcmf_cfg80211_del_apsta_iface(wiphy, wdev); case NL80211_IFTYPE_P2P_CLIENT: case NL80211_IFTYPE_P2P_GO: case NL80211_IFTYPE_P2P_DEVICE: @@ -928,6 +1394,9 @@ int brcmf_cfg80211_del_iface(struct wiphy *wiphy, struct wireless_dev *wdev) static s32 brcmf_cfg80211_change_iface(struct wiphy *wiphy, struct net_device *ndev, enum nl80211_iftype type, +#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 12, 0)) + u32 *flags, +#endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(4, 12, 0)) */ struct vif_params *params) { struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); @@ -1018,6 +1487,7 @@ brcmf_cfg80211_change_iface(struct wiphy *wiphy, struct net_device *ndev, ndev->ieee80211_ptr->iftype = type; brcmf_cfg80211_update_proto_addr_mode(&vif->wdev); + brcmf_setup_wiphybands(cfg); done: brcmf_dbg(TRACE, "Exit\n"); @@ -1211,11 +1681,6 @@ brcmf_cfg80211_scan(struct wiphy *wiphy, struct cfg80211_scan_request *request) if (err) goto scan_out; - err = brcmf_vif_set_mgmt_ie(vif, BRCMF_VNDR_IE_PRBREQ_FLAG, - request->ie, request->ie_len); - if (err) - goto scan_out; - err = brcmf_do_escan(vif->ifp, request); if (err) goto scan_out; @@ -1365,6 +1830,7 @@ static int brcmf_set_pmk(struct brcmf_if *ifp, const u8 *pmk_data, u16 pmk_len) return err; } +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 3, 0)) static int brcmf_set_sae_password(struct brcmf_if *ifp, const u8 *pwd_data, u16 pwd_len) { @@ -1389,6 +1855,7 @@ static int brcmf_set_sae_password(struct brcmf_if *ifp, const u8 *pwd_data, return err; } +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 3, 0)) */ static void brcmf_link_down(struct brcmf_cfg80211_vif *vif, u16 reason, bool locally_generated) @@ -1416,6 +1883,8 @@ static void brcmf_link_down(struct brcmf_cfg80211_vif *vif, u16 reason, locally_generated, GFP_KERNEL); } clear_bit(BRCMF_VIF_STATUS_CONNECTING, &vif->sme_state); + clear_bit(BRCMF_VIF_STATUS_EAP_SUCCESS, &vif->sme_state); + clear_bit(BRCMF_VIF_STATUS_ASSOC_SUCCESS, &vif->sme_state); clear_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status); brcmf_btcoex_set_mode(vif, BRCMF_BTCOEX_ENABLED, 0); if (vif->profile.use_fwsup != BRCMF_PROFILE_FWSUP_NONE) { @@ -1610,14 +2079,20 @@ static s32 brcmf_set_wpa_version(struct net_device *ndev, s32 val = 0; s32 err = 0; - if (sme->crypto.wpa_versions & NL80211_WPA_VERSION_1) + 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 if (sme->crypto.wpa_versions & NL80211_WPA_VERSION_3) + } else if (sme->crypto.wpa_versions & NL80211_WPA_VERSION_2) { + if (sme->crypto.akm_suites[0] == WLAN_AKM_SUITE_SAE) + val = WPA3_AUTH_SAE_PSK; + else + val = WPA2_AUTH_PSK | WPA2_AUTH_UNSPECIFIED; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 3, 0)) + } else if (sme->crypto.wpa_versions & NL80211_WPA_VERSION_3) { val = WPA3_AUTH_SAE_PSK; - else +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 3, 0)) */ + } else { val = WPA_AUTH_DISABLED; + } brcmf_dbg(CONN, "setting wpa_auth to 0x%0x\n", val); err = brcmf_fil_bsscfg_int_set(ifp, "wpa_auth", val); if (err) { @@ -1696,6 +2171,9 @@ brcmf_set_wsec_mode(struct net_device *ndev, case WLAN_CIPHER_SUITE_AES_CMAC: pval = AES_ENABLED; break; + case WLAN_CIPHER_SUITE_GCMP_256: + pval = AES_ENABLED; + break; default: bphy_err(drvr, "invalid cipher pairwise (%d)\n", sme->crypto.ciphers_pairwise[0]); @@ -1717,6 +2195,9 @@ brcmf_set_wsec_mode(struct net_device *ndev, case WLAN_CIPHER_SUITE_AES_CMAC: gval = AES_ENABLED; break; + case WLAN_CIPHER_SUITE_GCMP_256: + gval = AES_ENABLED; + break; default: bphy_err(drvr, "invalid cipher group (%d)\n", sme->crypto.cipher_group); @@ -1753,6 +2234,7 @@ brcmf_set_key_mgmt(struct net_device *ndev, struct cfg80211_connect_params *sme) struct brcmf_pub *drvr = ifp->drvr; s32 val; s32 err; + s32 okc_enable; const struct brcmf_tlv *rsn_ie; const u8 *ie; u32 ie_len; @@ -1763,6 +2245,7 @@ brcmf_set_key_mgmt(struct net_device *ndev, struct cfg80211_connect_params *sme) profile->use_fwsup = BRCMF_PROFILE_FWSUP_NONE; profile->is_ft = false; + profile->is_okc = false; if (!sme->crypto.n_akm_suites) return 0; @@ -1776,8 +2259,12 @@ brcmf_set_key_mgmt(struct net_device *ndev, struct cfg80211_connect_params *sme) switch (sme->crypto.akm_suites[0]) { case WLAN_AKM_SUITE_8021X: val = WPA_AUTH_UNSPECIFIED; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 13, 0)) if (sme->want_1x) profile->use_fwsup = BRCMF_PROFILE_FWSUP_1X; + else +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 13, 0)) */ + profile->use_fwsup = BRCMF_PROFILE_FWSUP_ROAM; break; case WLAN_AKM_SUITE_PSK: val = WPA_AUTH_PSK; @@ -1791,13 +2278,21 @@ brcmf_set_key_mgmt(struct net_device *ndev, struct cfg80211_connect_params *sme) switch (sme->crypto.akm_suites[0]) { case WLAN_AKM_SUITE_8021X: val = WPA2_AUTH_UNSPECIFIED; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 13, 0)) if (sme->want_1x) profile->use_fwsup = BRCMF_PROFILE_FWSUP_1X; + else +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 13, 0)) */ + profile->use_fwsup = BRCMF_PROFILE_FWSUP_ROAM; break; case WLAN_AKM_SUITE_8021X_SHA256: val = WPA2_AUTH_1X_SHA256; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 13, 0)) if (sme->want_1x) profile->use_fwsup = BRCMF_PROFILE_FWSUP_1X; + else +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 13, 0)) */ + profile->use_fwsup = BRCMF_PROFILE_FWSUP_ROAM; break; case WLAN_AKM_SUITE_PSK_SHA256: val = WPA2_AUTH_PSK_SHA256; @@ -1805,16 +2300,31 @@ brcmf_set_key_mgmt(struct net_device *ndev, struct cfg80211_connect_params *sme) case WLAN_AKM_SUITE_PSK: val = WPA2_AUTH_PSK; break; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)) case WLAN_AKM_SUITE_FT_8021X: val = WPA2_AUTH_UNSPECIFIED | WPA2_AUTH_FT; profile->is_ft = true; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 13, 0)) if (sme->want_1x) profile->use_fwsup = BRCMF_PROFILE_FWSUP_1X; + else +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 13, 0)) */ + profile->use_fwsup = BRCMF_PROFILE_FWSUP_ROAM; break; case WLAN_AKM_SUITE_FT_PSK: val = WPA2_AUTH_PSK | WPA2_AUTH_FT; profile->is_ft = true; break; +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)) */ + case WLAN_AKM_SUITE_DPP: + val = WFA_AUTH_DPP; + profile->use_fwsup = BRCMF_PROFILE_FWSUP_NONE; + break; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)) + case WLAN_AKM_SUITE_8021X_SUITE_B_192: + val = WPA3_AUTH_1X_SUITE_B_SHA384; + break; +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)) */ default: bphy_err(drvr, "invalid cipher group (%d)\n", sme->crypto.cipher_group); @@ -1824,10 +2334,26 @@ brcmf_set_key_mgmt(struct net_device *ndev, struct cfg80211_connect_params *sme) switch (sme->crypto.akm_suites[0]) { case WLAN_AKM_SUITE_SAE: val = WPA3_AUTH_SAE_PSK; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 3, 0)) + if (sme->crypto.sae_pwd) { + brcmf_dbg(INFO, "using SAE offload\n"); + profile->use_fwsup = BRCMF_PROFILE_FWSUP_SAE; + } else +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 3, 0)) */ + if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_FWSUP) && + brcmf_feat_is_enabled(ifp, BRCMF_FEAT_SAE_EXT)) { + brcmf_dbg(INFO, "using EXTSAE with PSK offload\n"); + profile->use_fwsup = BRCMF_PROFILE_FWSUP_PSK; + } + break; + case WLAN_AKM_SUITE_FT_OVER_SAE: + val = WPA3_AUTH_SAE_PSK | WPA2_AUTH_FT; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 3, 0)) if (sme->crypto.sae_pwd) { brcmf_dbg(INFO, "using SAE offload\n"); profile->use_fwsup = BRCMF_PROFILE_FWSUP_SAE; } +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 3, 0)) */ break; default: bphy_err(drvr, "invalid cipher group (%d)\n", @@ -1836,8 +2362,29 @@ brcmf_set_key_mgmt(struct net_device *ndev, struct cfg80211_connect_params *sme) } } - if (profile->use_fwsup == BRCMF_PROFILE_FWSUP_1X) + if ((profile->use_fwsup == BRCMF_PROFILE_FWSUP_1X) || + (profile->use_fwsup == BRCMF_PROFILE_FWSUP_ROAM)) { brcmf_dbg(INFO, "using 1X offload\n"); + err = brcmf_fil_bsscfg_int_get(netdev_priv(ndev), "okc_enable", + &okc_enable); + if (err) { + bphy_err(drvr, "get okc_enable failed (%d)\n", err); + } else { + brcmf_dbg(INFO, "get okc_enable (%d)\n", okc_enable); + profile->is_okc = okc_enable; + } + } else if (profile->use_fwsup != BRCMF_PROFILE_FWSUP_SAE && + (val == WPA3_AUTH_SAE_PSK)) { + brcmf_dbg(INFO, "not using SAE offload\n"); + err = brcmf_fil_bsscfg_int_get(netdev_priv(ndev), "okc_enable", + &okc_enable); + if (err) { + bphy_err(drvr, "get okc_enable failed (%d)\n", err); + } else { + brcmf_dbg(INFO, "get okc_enable (%d)\n", okc_enable); + profile->is_okc = okc_enable; + } + } if (!brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MFP)) goto skip_mfp_config; @@ -1874,7 +2421,7 @@ brcmf_set_key_mgmt(struct net_device *ndev, struct cfg80211_connect_params *sme) brcmf_fil_bsscfg_int_set(netdev_priv(ndev), "mfp", mfp); skip_mfp_config: - brcmf_dbg(CONN, "setting wpa_auth to %d\n", val); + 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) { bphy_err(drvr, "could not set wpa_auth (%d)\n", err); @@ -1905,9 +2452,14 @@ brcmf_set_sharedkey(struct net_device *ndev, brcmf_dbg(CONN, "wpa_versions 0x%x cipher_pairwise 0x%x\n", sec->wpa_versions, sec->cipher_pairwise); +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 3, 0)) if (sec->wpa_versions & (NL80211_WPA_VERSION_1 | NL80211_WPA_VERSION_2 | NL80211_WPA_VERSION_3)) return 0; +#else /* (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 3, 0)) */ + if (sec->wpa_versions & (NL80211_WPA_VERSION_1 | NL80211_WPA_VERSION_2)) + return 0; +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 3, 0)) */ if (!(sec->cipher_pairwise & (WLAN_CIPHER_SUITE_WEP40 | WLAN_CIPHER_SUITE_WEP104))) @@ -1964,6 +2516,7 @@ enum nl80211_auth_type brcmf_war_auth_type(struct brcmf_if *ifp, return type; } +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 7, 0)) static void brcmf_set_join_pref(struct brcmf_if *ifp, struct cfg80211_bss_selection *bss_select) { @@ -2008,6 +2561,44 @@ static void brcmf_set_join_pref(struct brcmf_if *ifp, if (err) bphy_err(drvr, "Set join_pref error (%d)\n", err); } +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 7, 0)) */ + +static bool +wl_cfg80211_is_oce_ap(struct brcmf_if *ifp, + struct wiphy *wiphy, const u8 *bssid_hint) +{ + struct brcmf_pub *drvr = ifp->drvr; + const struct brcmf_tlv *ie; + const struct cfg80211_bss_ies *ies; + struct cfg80211_bss *bss; + const u8 *parse = NULL; + u32 len; + + bss = cfg80211_get_bss(wiphy, NULL, bssid_hint, 0, 0, + IEEE80211_BSS_TYPE_ANY, IEEE80211_PRIVACY_ANY); + if (!bss) { + bphy_err(drvr, "Unable to find AP in the cache"); + return false; + } + + if (rcu_access_pointer(bss->ies)) { + ies = rcu_access_pointer(bss->ies); + parse = ies->data; + len = ies->len; + } else { + bphy_err(drvr, "ies is NULL"); + return false; + } + + while ((ie = brcmf_parse_tlvs(parse, len, WLAN_EID_VENDOR_SPECIFIC))) { + if (wl_cfgoce_is_oce_ie((const u8 *)ie, + (u8 const **)&parse, &len) == true) { + return true; + } + } + brcmf_dbg(TRACE, "OCE IE NOT found"); + return false; +} static s32 brcmf_cfg80211_connect(struct wiphy *wiphy, struct net_device *ndev, @@ -2028,6 +2619,7 @@ brcmf_cfg80211_connect(struct wiphy *wiphy, struct net_device *ndev, u16 chanspec; s32 err = 0; u32 ssid_len; + bool skip_hints = ifp->drvr->settings->fw_ap_select; brcmf_dbg(TRACE, "Enter\n"); if (!check_vif_up(ifp->vif)) @@ -2038,6 +2630,20 @@ brcmf_cfg80211_connect(struct wiphy *wiphy, struct net_device *ndev, return -EOPNOTSUPP; } + /* override bssid_hint for oce networks */ + skip_hints = (skip_hints && + wl_cfg80211_is_oce_ap(ifp, wiphy, sme->bssid_hint)); + if (skip_hints) { + /* Let fw choose the best AP */ + brcmf_dbg(TRACE, "Skipping bssid & channel hint\n"); + } else { + if (sme->channel_hint) + chan = sme->channel_hint; + + if (sme->bssid_hint) + sme->bssid = sme->bssid_hint; + } + if (ifp->vif == cfg->p2p.bss_idx[P2PAPI_BSSCFG_PRIMARY].vif) { /* A normal (non P2P) connection request setup. */ ie = NULL; @@ -2113,44 +2719,56 @@ brcmf_cfg80211_connect(struct wiphy *wiphy, struct net_device *ndev, goto done; } - if (sme->crypto.psk && - profile->use_fwsup != BRCMF_PROFILE_FWSUP_SAE) { - if (WARN_ON(profile->use_fwsup != BRCMF_PROFILE_FWSUP_NONE)) { - err = -EINVAL; - goto done; + if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_FWSUP)) { +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 13, 0)) + if (sme->crypto.psk) { + if ((profile->use_fwsup != BRCMF_PROFILE_FWSUP_SAE) && + (profile->use_fwsup != BRCMF_PROFILE_FWSUP_PSK)) { + if (WARN_ON(profile->use_fwsup != + BRCMF_PROFILE_FWSUP_NONE)) { + err = -EINVAL; + goto done; + } + brcmf_dbg(INFO, "using PSK offload\n"); + profile->use_fwsup = BRCMF_PROFILE_FWSUP_PSK; + } } - brcmf_dbg(INFO, "using PSK offload\n"); - profile->use_fwsup = BRCMF_PROFILE_FWSUP_PSK; - } +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 13, 0)) */ - if (profile->use_fwsup != BRCMF_PROFILE_FWSUP_NONE) { - /* enable firmware supplicant for this interface */ - err = brcmf_fil_iovar_int_set(ifp, "sup_wpa", 1); - if (err < 0) { - bphy_err(drvr, "failed to enable fw supplicant\n"); - goto done; + if (profile->use_fwsup != BRCMF_PROFILE_FWSUP_NONE) { + /* enable firmware supplicant for this interface */ + err = brcmf_fil_iovar_int_set(ifp, "sup_wpa", 1); + if (err < 0) { + bphy_err(drvr, "failed to enable fw supplicant\n"); + goto done; + } + } else { + err = brcmf_fil_iovar_int_set(ifp, "sup_wpa", 0); } - } - if (profile->use_fwsup == BRCMF_PROFILE_FWSUP_PSK) - err = brcmf_set_pmk(ifp, sme->crypto.psk, - BRCMF_WSEC_MAX_PSK_LEN); - else if (profile->use_fwsup == BRCMF_PROFILE_FWSUP_SAE) { - /* clean up user-space RSNE */ - err = brcmf_fil_iovar_data_set(ifp, "wpaie", NULL, 0); - if (err) { - bphy_err(drvr, "failed to clean up user-space RSNE\n"); - goto done; - } - err = brcmf_set_sae_password(ifp, sme->crypto.sae_pwd, - sme->crypto.sae_pwd_len); - if (!err && sme->crypto.psk) +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 13, 0)) + if ((profile->use_fwsup == BRCMF_PROFILE_FWSUP_PSK) && + sme->crypto.psk) err = brcmf_set_pmk(ifp, sme->crypto.psk, BRCMF_WSEC_MAX_PSK_LEN); +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 13, 0)) */ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 3, 0)) + if (profile->use_fwsup == BRCMF_PROFILE_FWSUP_SAE) { + /* clean up user-space RSNE */ + if (brcmf_fil_iovar_data_set(ifp, "wpaie", NULL, 0)) { + bphy_err(drvr, "failed to clean up user-space RSNE\n"); + goto done; + } + err = brcmf_set_sae_password(ifp, sme->crypto.sae_pwd, + sme->crypto.sae_pwd_len); + if (!err && sme->crypto.psk) + err = brcmf_set_pmk(ifp, sme->crypto.psk, + BRCMF_WSEC_MAX_PSK_LEN); + } +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 3, 0)) */ + if (err) + goto done; } - if (err) - goto done; - /* Join with specific BSSID and cached SSID * If SSID is zero join based on BSSID only */ @@ -2181,17 +2799,25 @@ brcmf_cfg80211_connect(struct wiphy *wiphy, struct net_device *ndev, 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); + if (BRCMU_CHSPEC_IS6G(chanspec)) { + ext_join_params->scan_le.active_time = + cpu_to_le32(BRCMF_SCAN_JOIN_ACTIVE_DWELL_TIME_MS_6E); + ext_join_params->scan_le.passive_time = + cpu_to_le32(BRCMF_SCAN_JOIN_PASSIVE_DWELL_TIME_MS_6E); + } else { + 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. @@ -2205,7 +2831,9 @@ brcmf_cfg80211_connect(struct wiphy *wiphy, struct net_device *ndev, ext_join_params->scan_le.nprobes = cpu_to_le32(-1); } +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 7, 0)) brcmf_set_join_pref(ifp, &sme->bss_select); +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 7, 0)) */ err = brcmf_fil_bsscfg_data_set(ifp, "join", ext_join_params, join_params_size); @@ -2260,6 +2888,8 @@ brcmf_cfg80211_disconnect(struct wiphy *wiphy, struct net_device *ndev, clear_bit(BRCMF_VIF_STATUS_CONNECTED, &ifp->vif->sme_state); clear_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state); + clear_bit(BRCMF_VIF_STATUS_EAP_SUCCESS, &ifp->vif->sme_state); + clear_bit(BRCMF_VIF_STATUS_ASSOC_SUCCESS, &ifp->vif->sme_state); cfg80211_disconnected(ndev, reason_code, NULL, 0, true, GFP_KERNEL); memcpy(&scbval.ea, &profile->bssid, ETH_ALEN); @@ -2516,6 +3146,16 @@ brcmf_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev, val = AES_ENABLED; brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_CCMP\n"); break; + case WLAN_CIPHER_SUITE_GCMP_256: + key->algo = CRYPTO_ALGO_AES_GCM256; + val = AES_ENABLED; + brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_GCMP_256\n"); + break; + case WLAN_CIPHER_SUITE_BIP_GMAC_256: + key->algo = CRYPTO_ALGO_BIP_GMAC256; + val = AES_ENABLED; + brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_BIP_GMAC_256\n"); + break; default: bphy_err(drvr, "Invalid cipher (0x%x)\n", params->cipher); err = -EINVAL; @@ -2767,9 +3407,8 @@ brcmf_cfg80211_get_station(struct wiphy *wiphy, struct net_device *ndev, struct brcmf_sta_info_le sta_info_le; u32 sta_flags; u32 is_tdls_peer; - s32 total_rssi_avg = 0; - s32 total_rssi = 0; - s32 count_rssi = 0; + s32 total_rssi; + s32 count_rssi; int rssi; u32 i; @@ -2835,27 +3474,25 @@ brcmf_cfg80211_get_station(struct wiphy *wiphy, struct net_device *ndev, sinfo->filled |= BIT_ULL(NL80211_STA_INFO_RX_BYTES); sinfo->rx_bytes = le64_to_cpu(sta_info_le.rx_tot_bytes); } + total_rssi = 0; + count_rssi = 0; for (i = 0; i < BRCMF_ANT_MAX; i++) { - if (sta_info_le.rssi[i] == 0 || - sta_info_le.rx_lastpkt_rssi[i] == 0) - continue; - sinfo->chains |= BIT(count_rssi); - sinfo->chain_signal[count_rssi] = - sta_info_le.rx_lastpkt_rssi[i]; - sinfo->chain_signal_avg[count_rssi] = - sta_info_le.rssi[i]; - total_rssi += sta_info_le.rx_lastpkt_rssi[i]; - total_rssi_avg += sta_info_le.rssi[i]; - count_rssi++; + if (sta_info_le.rssi[i]) { + sinfo->chain_signal_avg[count_rssi] = + sta_info_le.rssi[i]; + sinfo->chain_signal[count_rssi] = + sta_info_le.rssi[i]; + total_rssi += sta_info_le.rssi[i]; + count_rssi++; + } } if (count_rssi) { - sinfo->filled |= BIT_ULL(NL80211_STA_INFO_SIGNAL); - sinfo->filled |= BIT_ULL(NL80211_STA_INFO_SIGNAL_AVG); sinfo->filled |= BIT_ULL(NL80211_STA_INFO_CHAIN_SIGNAL); - sinfo->filled |= - BIT_ULL(NL80211_STA_INFO_CHAIN_SIGNAL_AVG); - sinfo->signal = total_rssi / count_rssi; - sinfo->signal_avg = total_rssi_avg / count_rssi; + sinfo->chains = count_rssi; + + sinfo->filled |= BIT_ULL(NL80211_STA_INFO_SIGNAL); + total_rssi /= count_rssi; + sinfo->signal = total_rssi; } else if (test_bit(BRCMF_VIF_STATUS_CONNECTED, &ifp->vif->sme_state)) { memset(&scb_val, 0, sizeof(scb_val)); @@ -2934,12 +3571,15 @@ brcmf_cfg80211_set_power_mgmt(struct wiphy *wiphy, struct net_device *ndev, goto done; } - pm = enabled ? PM_FAST : PM_OFF; + pm = enabled ? ifp->drvr->settings->default_pm : PM_OFF; /* Do not enable the power save after assoc if it is a p2p interface */ if (ifp->vif->wdev.iftype == NL80211_IFTYPE_P2P_CLIENT) { brcmf_dbg(INFO, "Do not enable power save for P2P clients\n"); pm = PM_OFF; } + + brcmf_set_mpc(ifp, ifp->drvr->req_mpc); + brcmf_dbg(INFO, "power save %s\n", (pm ? "enabled" : "disabled")); err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM, pm); @@ -2950,11 +3590,6 @@ brcmf_cfg80211_set_power_mgmt(struct wiphy *wiphy, struct net_device *ndev, bphy_err(drvr, "error (%d)\n", err); } - err = brcmf_fil_iovar_int_set(ifp, "pm2_sleep_ret", - min_t(u32, timeout, BRCMF_PS_MAX_TIMEOUT_MS)); - if (err) - bphy_err(drvr, "Unable to set pm timeout, (%d)\n", err); - done: brcmf_dbg(TRACE, "Exit\n"); return err; @@ -2975,24 +3610,20 @@ static s32 brcmf_inform_single_bss(struct brcmf_cfg80211_info *cfg, u8 *notify_ie; size_t notify_ielen; struct cfg80211_inform_bss bss_data = {}; + const struct brcmf_tlv *ssid = NULL; if (le32_to_cpu(bi->length) > WL_BSS_INFO_MAX) { bphy_err(drvr, "Bss info is larger than buffer. Discarding\n"); return -EINVAL; } - if (!bi->ctl_ch) { - ch.chspec = le16_to_cpu(bi->chanspec); - cfg->d11inf.decchspec(&ch); + ch.chspec = le16_to_cpu(bi->chanspec); + cfg->d11inf.decchspec(&ch); + if (!bi->ctl_ch) bi->ctl_ch = ch.control_ch_num; - } - channel = bi->ctl_ch; - - if (channel <= CH_MAX_2G_CHANNEL) - band = NL80211_BAND_2GHZ; - else - band = NL80211_BAND_5GHZ; + channel = bi->ctl_ch; + band = BRCMU_CHAN_BAND_TO_NL80211(ch.band); freq = ieee80211_channel_to_frequency(channel, band); bss_data.chan = ieee80211_get_channel(wiphy, freq); bss_data.scan_width = NL80211_BSS_CHAN_WIDTH_20; @@ -3004,6 +3635,12 @@ static s32 brcmf_inform_single_bss(struct brcmf_cfg80211_info *cfg, notify_ielen = le32_to_cpu(bi->ie_length); bss_data.signal = (s16)le16_to_cpu(bi->RSSI) * 100; + ssid = brcmf_parse_tlvs(notify_ie, notify_ielen, WLAN_EID_SSID); + if (ssid && ssid->data[0] == '\0' && ssid->len == bi->SSID_len) { + /* Update SSID for hidden AP */ + memcpy((u8 *)ssid->data, bi->SSID, bi->SSID_len); + } + 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); @@ -3100,11 +3737,7 @@ static s32 brcmf_inform_ibss(struct brcmf_cfg80211_info *cfg, ch.chspec = le16_to_cpu(bi->chanspec); cfg->d11inf.decchspec(&ch); - if (ch.band == BRCMU_CHAN_BAND_2G) - band = wiphy->bands[NL80211_BAND_2GHZ]; - else - band = wiphy->bands[NL80211_BAND_5GHZ]; - + band = wiphy->bands[BRCMU_CHAN_BAND_TO_NL80211(ch.band)]; freq = ieee80211_channel_to_frequency(ch.control_ch_num, band->band); cfg->channel = freq; notify_channel = ieee80211_get_channel(wiphy, freq); @@ -3146,10 +3779,7 @@ static s32 brcmf_update_bss_info(struct brcmf_cfg80211_info *cfg, struct brcmf_if *ifp) { struct brcmf_pub *drvr = cfg->pub; - struct brcmf_bss_info_le *bi; - const struct brcmf_tlv *tim; - size_t ie_len; - u8 *ie; + struct brcmf_bss_info_le *bi = NULL; s32 err = 0; brcmf_dbg(TRACE, "Enter\n"); @@ -3163,29 +3793,8 @@ static s32 brcmf_update_bss_info(struct brcmf_cfg80211_info *cfg, bphy_err(drvr, "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); - - tim = brcmf_parse_tlvs(ie, ie_len, WLAN_EID_TIM); - if (!tim) { - /* - * 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) { - bphy_err(drvr, "wl dtim_assoc failed (%d)\n", err); - goto update_bss_info_out; - } - } update_bss_info_out: brcmf_dbg(TRACE, "Exit"); @@ -3482,7 +4091,9 @@ brcmf_get_netinfo_array(struct brcmf_pno_scanresults_le *pfn_v1) switch (pfn_v1->version) { default: WARN_ON(1); +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0)) fallthrough; +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0)) */ case cpu_to_le32(1): netinfo = (struct brcmf_pno_net_info_le *)(pfn_v1 + 1); break; @@ -3582,7 +4193,11 @@ brcmf_notify_sched_scan_results(struct brcmf_if *ifp, goto free_req; out_err: +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)) cfg80211_sched_scan_stopped(wiphy, 0); +#else /* (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)) */ + cfg80211_sched_scan_stopped(wiphy); +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)) */ free_req: kfree(request); return err; @@ -3616,13 +4231,21 @@ brcmf_cfg80211_sched_scan_start(struct wiphy *wiphy, } static int brcmf_cfg80211_sched_scan_stop(struct wiphy *wiphy, - struct net_device *ndev, u64 reqid) + struct net_device *ndev +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)) + , u64 reqid +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)) */ + ) { struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); struct brcmf_if *ifp = netdev_priv(ndev); brcmf_dbg(SCAN, "enter\n"); +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)) brcmf_pno_stop_sched_scan(ifp, reqid); +#else /* (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)) */ + brcmf_pno_stop_sched_scan(ifp, 0); +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)) */ if (cfg->int_escan_map) brcmf_notify_escan_complete(cfg, ifp, true, true); return 0; @@ -3809,26 +4432,52 @@ static s32 brcmf_cfg80211_resume(struct wiphy *wiphy) struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); struct net_device *ndev = cfg_to_ndev(cfg); struct brcmf_if *ifp = netdev_priv(ndev); + struct brcmf_pub *drvr = ifp->drvr; + struct brcmf_bus *bus_if = drvr->bus_if; + struct brcmf_cfg80211_info *config = drvr->config; + int retry = BRCMF_PM_WAIT_MAXRETRY; + s32 power_mode; + + power_mode = cfg->pwr_save ? ifp->drvr->settings->default_pm : PM_OFF; brcmf_dbg(TRACE, "Enter\n"); + config->pm_state = BRCMF_CFG80211_PM_STATE_RESUMING; + if (cfg->wowl.active) { + /* wait for bus resumed */ + while (retry && bus_if->state != BRCMF_BUS_UP) { + usleep_range(10000, 20000); + retry--; + } + if (!retry && bus_if->state != BRCMF_BUS_UP) + brcmf_err("timed out wait for bus resume\n"); + brcmf_report_wowl_wakeind(wiphy, ifp); brcmf_fil_iovar_int_set(ifp, "wowl_clear", 0); brcmf_config_wowl_pattern(ifp, "clr", NULL, 0, NULL, 0); if (!brcmf_feat_is_enabled(ifp, BRCMF_FEAT_WOWL_ARP_ND)) brcmf_configure_arp_nd_offload(ifp, true); brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM, - cfg->wowl.pre_pmmode); + power_mode); cfg->wowl.active = false; if (cfg->wowl.nd_enabled) { +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)) brcmf_cfg80211_sched_scan_stop(cfg->wiphy, ifp->ndev, 0); +#else /* (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)) */ + brcmf_cfg80211_sched_scan_stop(cfg->wiphy, ifp->ndev); +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)) */ brcmf_fweh_unregister(cfg->pub, BRCMF_E_PFN_NET_FOUND); brcmf_fweh_register(cfg->pub, BRCMF_E_PFN_NET_FOUND, brcmf_notify_sched_scan_results); cfg->wowl.nd_enabled = false; } + + /* disable packet filters */ + brcmf_pktfilter_enable(ifp->ndev, false); + } + config->pm_state = BRCMF_CFG80211_PM_STATE_RESUMED; return 0; } @@ -3844,7 +4493,6 @@ static void brcmf_configure_wowl(struct brcmf_cfg80211_info *cfg, if (!brcmf_feat_is_enabled(ifp, BRCMF_FEAT_WOWL_ARP_ND)) brcmf_configure_arp_nd_offload(ifp, false); - brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_PM, &cfg->wowl.pre_pmmode); brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM, PM_MAX); wowl_config = 0; @@ -3886,6 +4534,9 @@ static void brcmf_configure_wowl(struct brcmf_cfg80211_info *cfg, brcmf_fil_iovar_int_set(ifp, "wowl_activate", 1); brcmf_bus_wowl_config(cfg->pub->bus_if, true); cfg->wowl.active = true; + + /* enable packet filters */ + brcmf_pktfilter_enable(ifp->ndev, true); } static s32 brcmf_cfg80211_suspend(struct wiphy *wiphy, @@ -3895,9 +4546,12 @@ static s32 brcmf_cfg80211_suspend(struct wiphy *wiphy, struct net_device *ndev = cfg_to_ndev(cfg); struct brcmf_if *ifp = netdev_priv(ndev); struct brcmf_cfg80211_vif *vif; + struct brcmf_cfg80211_info *config = ifp->drvr->config; brcmf_dbg(TRACE, "Enter\n"); + config->pm_state = BRCMF_CFG80211_PM_STATE_SUSPENDING; + /* if the primary net_device is not READY there is nothing * we can do but pray resume goes smoothly. */ @@ -3906,13 +4560,18 @@ static s32 brcmf_cfg80211_suspend(struct wiphy *wiphy, /* Stop scheduled scan */ if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_PNO)) +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)) brcmf_cfg80211_sched_scan_stop(wiphy, ndev, 0); +#else /* (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)) */ + brcmf_cfg80211_sched_scan_stop(wiphy, ndev); +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)) */ /* end any scanning */ if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) brcmf_abort_scanning(cfg); - if (wowl == NULL) { + if (!wowl || !test_bit(BRCMF_VIF_STATUS_CONNECTED, + &ifp->vif->sme_state)) { 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)) @@ -3932,14 +4591,19 @@ static s32 brcmf_cfg80211_suspend(struct wiphy *wiphy, brcmf_set_mpc(ifp, 1); } else { - /* Configure WOWL paramaters */ - brcmf_configure_wowl(cfg, ifp, wowl); + if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_WOWL)) + /* Configure WOWL parameters */ + brcmf_configure_wowl(cfg, ifp, wowl); } exit: - brcmf_dbg(TRACE, "Exit\n"); + /* set cfg80211 pm state to cfg80211 suspended state */ + config->pm_state = BRCMF_CFG80211_PM_STATE_SUSPENDED; + /* clear any scanning activity */ cfg->scan_status = 0; + + brcmf_dbg(TRACE, "Exit\n"); return 0; } @@ -4000,6 +4664,19 @@ brcmf_cfg80211_set_pmksa(struct wiphy *wiphy, struct net_device *ndev, err = brcmf_update_pmklist(cfg, ifp); +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)) + if (pmksa->pmk_len) { + if (ifp->vif->profile.is_okc) { + err = brcmf_fil_iovar_data_set(ifp, "okc_info_pmk", pmksa->pmk, + pmksa->pmk_len); + if (err < 0) + bphy_err(drvr, "okc_info_pmk iovar failed: ret=%d\n", err); + } else { + brcmf_set_pmk(ifp, pmksa->pmk, pmksa->pmk_len); + } + } +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)) */ + brcmf_dbg(TRACE, "Exit\n"); return err; } @@ -4105,6 +4782,12 @@ static bool brcmf_valid_wpa_oui(u8 *oui, bool is_rsn_ie) return (memcmp(oui, WPA_OUI, TLV_OUI_LEN) == 0); } +static bool brcmf_valid_dpp_suite(u8 *oui) +{ + return (memcmp(oui, WFA_OUI, TLV_OUI_LEN) == 0 && + *(oui + TLV_OUI_LEN) == DPP_AKM_SUITE_TYPE); +} + static s32 brcmf_configure_wpaie(struct brcmf_if *ifp, const struct brcmf_vs_tlv *wpa_ie, @@ -4218,42 +4901,47 @@ brcmf_configure_wpaie(struct brcmf_if *ifp, goto exit; } for (i = 0; i < count; i++) { - if (!brcmf_valid_wpa_oui(&data[offset], is_rsn_ie)) { + if (brcmf_valid_dpp_suite(&data[offset])) { + wpa_auth |= WFA_AUTH_DPP; + offset += TLV_OUI_LEN; + } else if (brcmf_valid_wpa_oui(&data[offset], is_rsn_ie)) { + 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; + case RSN_AKM_SHA256_PSK: + brcmf_dbg(TRACE, "RSN_AKM_MFP_PSK\n"); + wpa_auth |= WPA2_AUTH_PSK_SHA256; + break; + case RSN_AKM_SHA256_1X: + brcmf_dbg(TRACE, "RSN_AKM_MFP_1X\n"); + wpa_auth |= WPA2_AUTH_1X_SHA256; + break; + case RSN_AKM_SAE: + brcmf_dbg(TRACE, "RSN_AKM_SAE\n"); + wpa_auth |= WPA3_AUTH_SAE_PSK; + break; + default: + bphy_err(drvr, "Invalid key mgmt info\n"); + } + } else { err = -EINVAL; bphy_err(drvr, "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; - case RSN_AKM_SHA256_PSK: - brcmf_dbg(TRACE, "RSN_AKM_MFP_PSK\n"); - wpa_auth |= WPA2_AUTH_PSK_SHA256; - break; - case RSN_AKM_SHA256_1X: - brcmf_dbg(TRACE, "RSN_AKM_MFP_1X\n"); - wpa_auth |= WPA2_AUTH_1X_SHA256; - break; - case RSN_AKM_SAE: - brcmf_dbg(TRACE, "RSN_AKM_SAE\n"); - wpa_auth |= WPA3_AUTH_SAE_PSK; - break; - default: - bphy_err(drvr, "Invalid key mgmt info\n"); - } offset++; } @@ -4273,10 +4961,12 @@ brcmf_configure_wpaie(struct brcmf_if *ifp, */ if (!(wpa_auth & (WPA2_AUTH_PSK_SHA256 | WPA2_AUTH_1X_SHA256 | + WFA_AUTH_DPP | WPA3_AUTH_SAE_PSK))) { err = -EINVAL; goto exit; } + /* Firmware has requirement that WPA2_AUTH_PSK/ * WPA2_AUTH_UNSPECIFIED be set, if SHA256 OUI * is to be included in the rsn ie. @@ -4690,7 +5380,9 @@ brcmf_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *ndev, struct brcmf_if *ifp = netdev_priv(ndev); struct brcmf_pub *drvr = cfg->pub; struct brcmf_cfg80211_profile *profile = &ifp->vif->profile; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 13, 0)) struct cfg80211_crypto_settings *crypto = &settings->crypto; +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 13, 0)) */ const struct brcmf_tlv *ssid_ie; const struct brcmf_tlv *country_ie; struct brcmf_ssid_le ssid_le; @@ -4712,6 +5404,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; + brcmf_dbg(TRACE, "mbss %s\n", mbss ? "enabled" : "disabled"); /* store current 11d setting */ if (brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_REGULATORY, @@ -4801,7 +5494,7 @@ brcmf_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *ndev, err = -EINVAL; goto exit; } - + ifp->isap = false; /* Interface specific setup */ if (dev_role == NL80211_IFTYPE_AP) { if ((brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MBSS)) && (!mbss)) @@ -4813,23 +5506,24 @@ brcmf_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *ndev, err); goto exit; } - if (!mbss) { - /* Firmware 10.x requires setting channel after enabling - * AP and before bringing interface up. - */ - err = brcmf_fil_iovar_int_set(ifp, "chanspec", chanspec); - if (err < 0) { - bphy_err(drvr, "Set Channel failed: chspec=%d, %d\n", - chanspec, err); - goto exit; - } + + /* Firmware 10.x requires setting channel after enabling + * AP and before bringing interface up. + */ + err = brcmf_fil_iovar_int_set(ifp, "chanspec", chanspec); + if (err < 0) { + bphy_err(drvr, "Set Channel failed: chspec=%d, %d\n", + chanspec, err); + goto exit; } + err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_UP, 1); if (err < 0) { bphy_err(drvr, "BRCMF_C_UP error (%d)\n", err); goto exit; } +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 13, 0)) if (crypto->psk) { brcmf_dbg(INFO, "using PSK offload\n"); profile->use_fwauth |= BIT(BRCMF_PROFILE_FWAUTH_PSK); @@ -4838,6 +5532,8 @@ brcmf_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *ndev, if (err < 0) goto exit; } +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 13, 0)) */ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 3, 0)) if (crypto->sae_pwd) { brcmf_dbg(INFO, "using SAE offload\n"); profile->use_fwauth |= BIT(BRCMF_PROFILE_FWAUTH_SAE); @@ -4846,6 +5542,7 @@ brcmf_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *ndev, if (err < 0) goto exit; } +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 3, 0)) */ if (profile->use_fwauth == 0) profile->use_fwauth = BIT(BRCMF_PROFILE_FWAUTH_NONE); @@ -4881,7 +5578,7 @@ brcmf_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *ndev, err); goto exit; } - + ifp->isap = true; brcmf_dbg(TRACE, "AP mode configuration complete\n"); } else if (dev_role == NL80211_IFTYPE_P2P_GO) { err = brcmf_fil_iovar_int_set(ifp, "chanspec", chanspec); @@ -4913,6 +5610,7 @@ brcmf_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *ndev, goto exit; } + ifp->isap = true; brcmf_dbg(TRACE, "GO mode configuration complete\n"); } else { WARN_ON(1); @@ -4926,6 +5624,9 @@ brcmf_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *ndev, if ((err) && (!mbss)) { brcmf_set_mpc(ifp, 1); brcmf_configure_arp_nd_offload(ifp, true); + } else { + cfg->num_softap++; + brcmf_dbg(TRACE, "Num of SoftAP %u\n", cfg->num_softap); } return err; } @@ -4939,6 +5640,7 @@ static int brcmf_cfg80211_stop_ap(struct wiphy *wiphy, struct net_device *ndev) s32 err; struct brcmf_fil_bss_enable_le bss_enable; struct brcmf_join_params join_params; + s32 apsta = 0; brcmf_dbg(TRACE, "Enter\n"); @@ -4950,31 +5652,50 @@ static int brcmf_cfg80211_stop_ap(struct wiphy *wiphy, struct net_device *ndev) if (profile->use_fwauth != BIT(BRCMF_PROFILE_FWAUTH_NONE)) { if (profile->use_fwauth & BIT(BRCMF_PROFILE_FWAUTH_PSK)) brcmf_set_pmk(ifp, NULL, 0); +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 3, 0)) if (profile->use_fwauth & BIT(BRCMF_PROFILE_FWAUTH_SAE)) brcmf_set_sae_password(ifp, NULL, 0); +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 3, 0)) */ profile->use_fwauth = BIT(BRCMF_PROFILE_FWAUTH_NONE); } - if (ifp->vif->mbss) { - err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_DOWN, 1); - return err; - } + cfg->num_softap--; - /* First BSS doesn't get a full reset */ - if (ifp->bsscfgidx == 0) - brcmf_fil_iovar_int_set(ifp, "closednet", 0); + /* Clear bss configuration and SSID */ + bss_enable.bsscfgidx = cpu_to_le32(ifp->bsscfgidx); + 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); 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) bphy_err(drvr, "SET SSID error (%d)\n", err); - err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_DOWN, 1); - if (err < 0) - bphy_err(drvr, "BRCMF_C_DOWN error %d\n", err); - err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_AP, 0); + + if (cfg->num_softap) { + brcmf_dbg(TRACE, "Num of SoftAP %u\n", cfg->num_softap); + return 0; + } + + /* First BSS doesn't get a full reset */ + if (ifp->bsscfgidx == 0) + brcmf_fil_iovar_int_set(ifp, "closednet", 0); + + err = brcmf_fil_iovar_int_get(ifp, "apsta", &apsta); if (err < 0) - bphy_err(drvr, "setting AP mode failed %d\n", err); + brcmf_err("wl apsta failed (%d)\n", err); + + if (!apsta) { + err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_DOWN, 1); + if (err < 0) + bphy_err(drvr, "BRCMF_C_DOWN error %d\n", err); + err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_AP, 0); + if (err < 0) + bphy_err(drvr, "Set AP mode error %d\n", err); + } if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MBSS)) brcmf_fil_iovar_int_set(ifp, "mbss", 0); brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_REGULATORY, @@ -4994,8 +5715,8 @@ static int brcmf_cfg80211_stop_ap(struct wiphy *wiphy, struct net_device *ndev) bphy_err(drvr, "bss_enable config failed %d\n", err); } brcmf_set_mpc(ifp, 1); - brcmf_configure_arp_nd_offload(ifp, true); clear_bit(BRCMF_VIF_STATUS_AP_CREATED, &ifp->vif->sme_state); + brcmf_configure_arp_nd_offload(ifp, true); brcmf_net_setcarrier(ifp, false); return err; @@ -5078,6 +5799,7 @@ brcmf_cfg80211_change_station(struct wiphy *wiphy, struct net_device *ndev, return err; } +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 8, 0)) static void brcmf_cfg80211_update_mgmt_frame_registrations(struct wiphy *wiphy, struct wireless_dev *wdev, @@ -5089,6 +5811,7 @@ brcmf_cfg80211_update_mgmt_frame_registrations(struct wiphy *wiphy, vif->mgmt_rx_reg = upd->interface_stypes; } +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 8, 0)) */ static int @@ -5107,9 +5830,13 @@ brcmf_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, s32 ie_len; struct brcmf_fil_action_frame_le *action_frame; struct brcmf_fil_af_params_le *af_params; - bool ack; + bool ack = false; s32 chan_nr; u32 freq; + struct brcmf_mf_params_le *mf_params; + u32 mf_params_len; + s32 timeout; + u32 hw_channel; brcmf_dbg(TRACE, "Enter\n"); @@ -5170,13 +5897,16 @@ brcmf_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, /* Add the channel. Use the one specified as parameter if any or * the current one (got from the firmware) otherwise */ - if (chan) + if (chan) { freq = chan->center_freq; - else + chan_nr = ieee80211_frequency_to_channel(freq); + af_params->channel = cpu_to_le32(chan_nr); + } 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); + &hw_channel); + af_params->channel = hw_channel; + } + af_params->dwell_time = cpu_to_le32(params->wait); memcpy(action_frame->data, &buf[DOT11_MGMT_HDR_LEN], le16_to_cpu(action_frame->len)); @@ -5185,11 +5915,79 @@ brcmf_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, *cookie, le16_to_cpu(action_frame->len), freq); ack = brcmf_p2p_send_action_frame(cfg, cfg_to_ndev(cfg), - af_params); + af_params, vif, chan); cfg80211_mgmt_tx_status(wdev, *cookie, buf, len, ack, GFP_KERNEL); kfree(af_params); + } else if (ieee80211_is_auth(mgmt->frame_control)) { + reinit_completion(&vif->mgmt_tx); + clear_bit(BRCMF_MGMT_TX_ACK, &vif->mgmt_tx_status); + clear_bit(BRCMF_MGMT_TX_NOACK, &vif->mgmt_tx_status); + clear_bit(BRCMF_MGMT_TX_OFF_CHAN_COMPLETED, + &vif->mgmt_tx_status); + + mf_params_len = offsetof(struct brcmf_mf_params_le, data) + + (len - DOT11_MGMT_HDR_LEN); + mf_params = kzalloc(mf_params_len, GFP_KERNEL); + if (!mf_params) { + err = -ENOMEM; + goto exit; + } + + mf_params->dwell_time = cpu_to_le32(MGMT_AUTH_FRAME_DWELL_TIME); + mf_params->len = cpu_to_le16(len - DOT11_MGMT_HDR_LEN); + mf_params->frame_control = mgmt->frame_control; + + if (chan) { + freq = chan->center_freq; + chan_nr = ieee80211_frequency_to_channel(freq); + mf_params->channel = cpu_to_le32(chan_nr); + } else { + brcmf_fil_cmd_int_get(vif->ifp, BRCMF_C_GET_CHANNEL, + &hw_channel); + mf_params->channel = hw_channel; + } + + memcpy(&mf_params->da[0], &mgmt->da[0], ETH_ALEN); + memcpy(&mf_params->bssid[0], &mgmt->bssid[0], ETH_ALEN); + mf_params->packet_id = cpu_to_le32(*cookie); + memcpy(mf_params->data, &buf[DOT11_MGMT_HDR_LEN], + le16_to_cpu(mf_params->len)); + + brcmf_dbg(TRACE, "Auth frame, cookie=%d, fc=%04x, len=%d, channel=%d\n", + le32_to_cpu(mf_params->packet_id), + le16_to_cpu(mf_params->frame_control), + le16_to_cpu(mf_params->len), + le32_to_cpu(mf_params->channel)); + + vif->mgmt_tx_id = le32_to_cpu(mf_params->packet_id); + set_bit(BRCMF_MGMT_TX_SEND_FRAME, &vif->mgmt_tx_status); + + err = brcmf_fil_bsscfg_data_set(vif->ifp, "mgmt_frame", + mf_params, mf_params_len); + if (err) { + bphy_err(drvr, "Failed to send Auth frame: err=%d\n", + err); + goto tx_status; + } + + timeout = + wait_for_completion_timeout(&vif->mgmt_tx, + MGMT_AUTH_FRAME_WAIT_TIME); + if (test_bit(BRCMF_MGMT_TX_ACK, &vif->mgmt_tx_status)) { + brcmf_dbg(TRACE, "TX Auth frame operation is success\n"); + ack = true; + } else { + bphy_err(drvr, "TX Auth frame operation is failed: status=%ld)\n", + vif->mgmt_tx_status); + } + +tx_status: + cfg80211_mgmt_tx_status(wdev, *cookie, buf, len, ack, + GFP_KERNEL); + kfree(mf_params); + } else { brcmf_dbg(TRACE, "Unhandled, fc=%04x!!\n", mgmt->frame_control); brcmf_dbg_hex_dump(true, buf, len, "payload, len=%zu\n", len); @@ -5247,15 +6045,7 @@ static int brcmf_cfg80211_get_channel(struct wiphy *wiphy, ch.chspec = chanspec; cfg->d11inf.decchspec(&ch); - - switch (ch.band) { - case BRCMU_CHAN_BAND_2G: - band = NL80211_BAND_2GHZ; - break; - case BRCMU_CHAN_BAND_5G: - band = NL80211_BAND_5GHZ; - break; - } + band = BRCMU_CHAN_BAND_TO_NL80211(ch.band); switch (ch.bw) { case BRCMU_CHAN_BW_80: @@ -5387,6 +6177,7 @@ static int brcmf_cfg80211_tdls_oper(struct wiphy *wiphy, return ret; } +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0)) static int brcmf_cfg80211_update_conn_params(struct wiphy *wiphy, struct net_device *ndev, @@ -5411,6 +6202,7 @@ brcmf_cfg80211_update_conn_params(struct wiphy *wiphy, return err; } +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0)) */ #ifdef CONFIG_PM static int @@ -5439,21 +6231,35 @@ brcmf_cfg80211_set_rekey_data(struct wiphy *wiphy, struct net_device *ndev, } #endif +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 13, 0)) static int brcmf_cfg80211_set_pmk(struct wiphy *wiphy, struct net_device *dev, const struct cfg80211_pmk_conf *conf) { struct brcmf_if *ifp; + struct brcmf_pub *drvr; + int ret; brcmf_dbg(TRACE, "enter\n"); /* expect using firmware supplicant for 1X */ ifp = netdev_priv(dev); - if (WARN_ON(ifp->vif->profile.use_fwsup != BRCMF_PROFILE_FWSUP_1X)) + drvr = ifp->drvr; + if (WARN_ON((ifp->vif->profile.use_fwsup != BRCMF_PROFILE_FWSUP_1X) && + (ifp->vif->profile.use_fwsup != BRCMF_PROFILE_FWSUP_ROAM) && + (ifp->vif->profile.is_ft != true) && + (ifp->vif->profile.is_okc != true))) return -EINVAL; if (conf->pmk_len > BRCMF_WSEC_MAX_PSK_LEN) return -ERANGE; + if (ifp->vif->profile.is_okc) { + ret = brcmf_fil_iovar_data_set(ifp, "okc_info_pmk", conf->pmk, + conf->pmk_len); + if (ret < 0) + bphy_err(drvr, "okc_info_pmk iovar failed: ret=%d\n", ret); + } + return brcmf_set_pmk(ifp, conf->pmk, conf->pmk_len); } @@ -5469,6 +6275,112 @@ static int brcmf_cfg80211_del_pmk(struct wiphy *wiphy, struct net_device *dev, return brcmf_set_pmk(ifp, NULL, 0); } +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 13, 0)) */ + +static int +brcmf_cfg80211_change_bss(struct wiphy *wiphy, struct net_device *dev, + struct bss_parameters *params) +{ + struct brcmf_if *ifp; + int ret = 0; + u32 ap_isolate, val; + + brcmf_dbg(TRACE, "Enter\n"); + ifp = netdev_priv(dev); + if (params->ap_isolate >= 0) { + ap_isolate = (u32)params->ap_isolate; + ret = brcmf_fil_iovar_int_set(ifp, "ap_isolate", ap_isolate); + if (ret < 0) + brcmf_err("ap_isolate iovar failed: ret=%d\n", ret); + } + + /* Get ap_isolate value from firmware to detemine whether fmac */ + /* driver supports packet forwarding. */ + if (brcmf_fil_iovar_int_get(ifp, "ap_isolate", &val) == 0) { + ifp->fmac_pkt_fwd_en = + ((params->ap_isolate == 0) && (val == 1)) ? + true : false; + } else { + brcmf_err("get ap_isolate iovar failed: ret=%d\n", ret); + ifp->fmac_pkt_fwd_en = false; + } + + return ret; +} + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 17, 0)) +static int +brcmf_cfg80211_external_auth(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_external_auth_params *params) +{ + struct brcmf_if *ifp; + struct brcmf_pub *drvr; + struct brcmf_auth_req_status_le auth_status; + int ret = 0; + + brcmf_dbg(TRACE, "Enter\n"); + + ifp = netdev_priv(dev); + drvr = ifp->drvr; + if (params->status == WLAN_STATUS_SUCCESS) { + auth_status.flags = cpu_to_le16(BRCMF_EXTAUTH_SUCCESS); + } else { + bphy_err(drvr, "External authentication failed: status=%d\n", + params->status); + auth_status.flags = cpu_to_le16(BRCMF_EXTAUTH_FAIL); + } + + memcpy(auth_status.peer_mac, params->bssid, ETH_ALEN); + auth_status.ssid_len = cpu_to_le32(min_t(u8, params->ssid.ssid_len, + IEEE80211_MAX_SSID_LEN)); + memcpy(auth_status.ssid, params->ssid.ssid, auth_status.ssid_len); + memset(auth_status.pmkid, 0, WLAN_PMKID_LEN); +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 1, 0)) + if (params->pmkid) + memcpy(auth_status.pmkid, params->pmkid, WLAN_PMKID_LEN); +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 1, 0)) */ + + ret = brcmf_fil_iovar_data_set(ifp, "auth_status", &auth_status, + sizeof(auth_status)); + if (ret < 0) + bphy_err(drvr, "auth_status iovar failed: ret=%d\n", ret); + + return ret; +} +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 17, 0)) */ + +static int +brcmf_cfg80211_set_cqm_rssi_config(struct wiphy *wiphy, struct net_device *dev, + s32 rssi_thold, u32 rssi_hyst) +{ + struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); + struct brcmf_if *ifp; + struct wl_rssi_event rssi = {}; + int err = 0; + + ifp = netdev_priv(dev); + if (rssi_thold == 0) { + cfg->cqm_info.enable = 0; + cfg->cqm_info.rssi_threshold = 0; + } else { + cfg->cqm_info.enable = 1; + cfg->cqm_info.rssi_threshold = rssi_thold; + + rssi.rate_limit_msec = 0; + rssi.rssi_levels[rssi.num_rssi_levels++] = S8_MIN; + rssi.rssi_levels[rssi.num_rssi_levels++] = + cfg->cqm_info.rssi_threshold; + rssi.rssi_levels[rssi.num_rssi_levels++] = S8_MAX; + } + + err = brcmf_fil_iovar_data_set(ifp, "rssi_event", &rssi, sizeof(rssi)); + if (err < 0) + brcmf_err("set rssi_event iovar failed (%d)\n", err); + + brcmf_dbg(TRACE, "enable = %d, rssi_threshold = %d\n", + cfg->cqm_info.enable, cfg->cqm_info.rssi_threshold); + return err; +} static struct cfg80211_ops brcmf_cfg80211_ops = { .add_virtual_intf = brcmf_cfg80211_add_iface, @@ -5502,8 +6414,10 @@ static struct cfg80211_ops brcmf_cfg80211_ops = { .change_station = brcmf_cfg80211_change_station, .sched_scan_start = brcmf_cfg80211_sched_scan_start, .sched_scan_stop = brcmf_cfg80211_sched_scan_stop, +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 8, 0)) .update_mgmt_frame_registrations = brcmf_cfg80211_update_mgmt_frame_registrations, +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 8, 0)) */ .mgmt_tx = brcmf_cfg80211_mgmt_tx, .remain_on_channel = brcmf_p2p_remain_on_channel, .cancel_remain_on_channel = brcmf_cfg80211_cancel_remain_on_channel, @@ -5513,9 +6427,18 @@ static struct cfg80211_ops brcmf_cfg80211_ops = { .crit_proto_start = brcmf_cfg80211_crit_proto_start, .crit_proto_stop = brcmf_cfg80211_crit_proto_stop, .tdls_oper = brcmf_cfg80211_tdls_oper, +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0)) .update_connect_params = brcmf_cfg80211_update_conn_params, +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0)) */ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 13, 0)) .set_pmk = brcmf_cfg80211_set_pmk, .del_pmk = brcmf_cfg80211_del_pmk, +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 13, 0)) */ + .change_bss = brcmf_cfg80211_change_bss, +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 17, 0)) + .external_auth = brcmf_cfg80211_external_auth, +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 17, 0)) */ + .set_cqm_rssi_config = brcmf_cfg80211_set_cqm_rssi_config, }; struct cfg80211_ops *brcmf_cfg80211_get_ops(struct brcmf_mp_device *settings) @@ -5525,8 +6448,10 @@ struct cfg80211_ops *brcmf_cfg80211_get_ops(struct brcmf_mp_device *settings) ops = kmemdup(&brcmf_cfg80211_ops, sizeof(brcmf_cfg80211_ops), GFP_KERNEL); +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0)) if (ops && settings->roamoff) ops->update_connect_params = NULL; +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0)) */ return ops; } @@ -5562,6 +6487,7 @@ struct brcmf_cfg80211_vif *brcmf_alloc_vif(struct brcmf_cfg80211_info *cfg, vif->mbss = mbss; } + init_completion(&vif->mgmt_tx); list_add_tail(&vif->list, &cfg->vif_list); return vif; } @@ -5580,8 +6506,10 @@ void brcmf_cfg80211_free_netdev(struct net_device *ndev) ifp = netdev_priv(ndev); vif = ifp->vif; - if (vif) + if (vif) { brcmf_free_vif(vif); + ifp->vif = NULL; + } } static bool brcmf_is_linkup(struct brcmf_cfg80211_vif *vif, @@ -5614,8 +6542,7 @@ static bool brcmf_is_linkup(struct brcmf_cfg80211_vif *vif, return false; } -static bool brcmf_is_linkdown(struct brcmf_cfg80211_vif *vif, - const struct brcmf_event_msg *e) +static bool brcmf_is_linkdown(const struct brcmf_event_msg *e) { u32 event = e->event_code; u16 flags = e->flags; @@ -5624,8 +6551,6 @@ static bool brcmf_is_linkdown(struct brcmf_cfg80211_vif *vif, (event == BRCMF_E_DISASSOC_IND) || ((event == BRCMF_E_LINK) && (!(flags & BRCMF_EVENT_MSG_LINK)))) { brcmf_dbg(CONN, "Processing link down\n"); - clear_bit(BRCMF_VIF_STATUS_EAP_SUCCESS, &vif->sme_state); - clear_bit(BRCMF_VIF_STATUS_ASSOC_SUCCESS, &vif->sme_state); return true; } return false; @@ -5884,33 +6809,78 @@ static s32 brcmf_get_assoc_ies(struct brcmf_cfg80211_info *cfg, return err; } -static s32 -brcmf_bss_roaming_done(struct brcmf_cfg80211_info *cfg, - struct net_device *ndev, - const struct brcmf_event_msg *e) +#if (0) +static bool +brcmf_has_pmkid(const u8 *parse, u32 len) { - 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; - struct cfg80211_roam_info roam_info = {}; - 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); + const struct brcmf_tlv *rsn_ie; + const u8 *ie; + u32 ie_len; + u32 offset; + u16 count; - buf = kzalloc(WL_BSS_INFO_MAX, GFP_KERNEL); - if (buf == NULL) { - err = -ENOMEM; + rsn_ie = brcmf_parse_tlvs(parse, len, WLAN_EID_RSN); + if (!rsn_ie) + goto done; + ie = (const u8 *)rsn_ie; + ie_len = rsn_ie->len + TLV_HDR_LEN; + /* Skip group data cipher suite */ + offset = TLV_HDR_LEN + WPA_IE_VERSION_LEN + WPA_IE_MIN_OUI_LEN; + if (offset + WPA_IE_SUITE_COUNT_LEN >= ie_len) + goto done; + /* Skip pairwise cipher suite(s) */ + count = ie[offset] + (ie[offset + 1] << 8); + offset += WPA_IE_SUITE_COUNT_LEN + (count * WPA_IE_MIN_OUI_LEN); + if (offset + WPA_IE_SUITE_COUNT_LEN >= ie_len) + goto done; + /* Skip auth key management suite(s) */ + count = ie[offset] + (ie[offset + 1] << 8); + offset += WPA_IE_SUITE_COUNT_LEN + (count * WPA_IE_MIN_OUI_LEN); + if (offset + RSN_CAP_LEN >= ie_len) + goto done; + /* Skip rsn capabilities */ + offset += RSN_CAP_LEN; + if (offset + RSN_PMKID_COUNT_LEN > ie_len) + goto done; + /* Extract PMKID count */ + count = ie[offset] + (ie[offset + 1] << 8); + if (count) + return true; + +done: + return false; +} +#endif + +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; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)) + struct cfg80211_roam_info roam_info = {}; +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)) */ + 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; } @@ -5926,17 +6896,14 @@ brcmf_bss_roaming_done(struct brcmf_cfg80211_info *cfg, ch.chspec = le16_to_cpu(bi->chanspec); cfg->d11inf.decchspec(&ch); - if (ch.band == BRCMU_CHAN_BAND_2G) - band = wiphy->bands[NL80211_BAND_2GHZ]; - else - band = wiphy->bands[NL80211_BAND_5GHZ]; - + band = wiphy->bands[BRCMU_CHAN_BAND_TO_NL80211(ch.band)]; freq = ieee80211_channel_to_frequency(ch.control_ch_num, band->band); notify_channel = ieee80211_get_channel(wiphy, freq); done: kfree(buf); +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)) roam_info.channel = notify_channel; roam_info.bssid = profile->bssid; roam_info.req_ie = conn_info->req_ie; @@ -5944,14 +6911,24 @@ brcmf_bss_roaming_done(struct brcmf_cfg80211_info *cfg, roam_info.resp_ie = conn_info->resp_ie; roam_info.resp_ie_len = conn_info->resp_ie_len; +#if (0) + if ((profile->use_fwsup == BRCMF_PROFILE_FWSUP_1X || + profile->use_fwsup == BRCMF_PROFILE_FWSUP_ROAM) && + (brcmf_has_pmkid(roam_info.req_ie, roam_info.req_ie_len) || + profile->is_ft || profile->is_okc)) + roam_info.authorized = true; +#endif + cfg80211_roamed(ndev, &roam_info, GFP_KERNEL); +#else /* (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)) */ + cfg80211_roamed(ndev, + notify_channel, + profile->bssid, + conn_info->req_ie, conn_info->req_ie_len, + conn_info->resp_ie, conn_info->resp_ie_len, GFP_KERNEL); +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)) */ brcmf_dbg(CONN, "Report roaming result\n"); - if (profile->use_fwsup == BRCMF_PROFILE_FWSUP_1X && profile->is_ft) { - cfg80211_port_authorized(ndev, profile->bssid, GFP_KERNEL); - brcmf_dbg(CONN, "Report port authorized\n"); - } - set_bit(BRCMF_VIF_STATUS_CONNECTED, &ifp->vif->sme_state); brcmf_dbg(TRACE, "Exit\n"); return err; @@ -5965,12 +6942,15 @@ brcmf_bss_connect_done(struct brcmf_cfg80211_info *cfg, struct brcmf_if *ifp = netdev_priv(ndev); struct brcmf_cfg80211_profile *profile = &ifp->vif->profile; struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg); +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)) struct cfg80211_connect_resp_params conn_params; +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)) */ brcmf_dbg(TRACE, "Enter\n"); if (test_and_clear_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state)) { +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)) memset(&conn_params, 0, sizeof(conn_params)); if (completed) { brcmf_get_assoc_ies(cfg, ifp); @@ -5979,6 +6959,10 @@ brcmf_bss_connect_done(struct brcmf_cfg80211_info *cfg, &ifp->vif->sme_state); conn_params.status = WLAN_STATUS_SUCCESS; } else { + clear_bit(BRCMF_VIF_STATUS_EAP_SUCCESS, + &ifp->vif->sme_state); + clear_bit(BRCMF_VIF_STATUS_ASSOC_SUCCESS, + &ifp->vif->sme_state); conn_params.status = WLAN_STATUS_AUTH_TIMEOUT; } conn_params.bssid = profile->bssid; @@ -5986,7 +6970,37 @@ brcmf_bss_connect_done(struct brcmf_cfg80211_info *cfg, conn_params.req_ie_len = conn_info->req_ie_len; conn_params.resp_ie = conn_info->resp_ie; conn_params.resp_ie_len = conn_info->resp_ie_len; + +#if (0) + if (profile->use_fwsup == BRCMF_PROFILE_FWSUP_1X && + brcmf_has_pmkid(conn_params.req_ie, conn_params.req_ie_len)) + conn_params.authorized = true; +#endif + cfg80211_connect_done(ndev, &conn_params, GFP_KERNEL); +#else /* (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)) */ + if (completed) { + brcmf_get_assoc_ies(cfg, ifp); + brcmf_update_bss_info(cfg, ifp); + set_bit(BRCMF_VIF_STATUS_CONNECTED, + &ifp->vif->sme_state); + } else { + clear_bit(BRCMF_VIF_STATUS_EAP_SUCCESS, + &ifp->vif->sme_state); + clear_bit(BRCMF_VIF_STATUS_ASSOC_SUCCESS, + &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); +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)) */ brcmf_dbg(CONN, "Report connect result - connection %s\n", completed ? "succeeded" : "failed"); } @@ -6058,6 +7072,14 @@ brcmf_notify_connect_status(struct brcmf_if *ifp, } if (brcmf_is_apmode(ifp->vif)) { + if (e->event_code == BRCMF_E_ASSOC_IND || + e->event_code == BRCMF_E_REASSOC_IND) { + brcmf_findadd_sta(ifp, e->addr); + } else if ((e->event_code == BRCMF_E_DISASSOC_IND) || + (e->event_code == BRCMF_E_DEAUTH_IND) || + (e->event_code == BRCMF_E_DEAUTH)) { + brcmf_del_sta(ifp, e->addr); + } err = brcmf_notify_connect_status_ap(cfg, ndev, e, data); } else if (brcmf_is_linkup(ifp->vif, e)) { brcmf_dbg(CONN, "Linkup\n"); @@ -6073,12 +7095,16 @@ brcmf_notify_connect_status(struct brcmf_if *ifp, } else brcmf_bss_connect_done(cfg, ndev, e, true); brcmf_net_setcarrier(ifp, true); - } else if (brcmf_is_linkdown(ifp->vif, e)) { + } else if (brcmf_is_linkdown(e)) { brcmf_dbg(CONN, "Linkdown\n"); if (!brcmf_is_ibssmode(ifp->vif) && - test_bit(BRCMF_VIF_STATUS_CONNECTED, - &ifp->vif->sme_state)) { - if (memcmp(profile->bssid, e->addr, ETH_ALEN)) + (test_bit(BRCMF_VIF_STATUS_CONNECTED, + &ifp->vif->sme_state) || + test_bit(BRCMF_VIF_STATUS_CONNECTING, + &ifp->vif->sme_state))) { + if (test_bit(BRCMF_VIF_STATUS_CONNECTED, + &ifp->vif->sme_state) && + memcmp(profile->bssid, e->addr, ETH_ALEN)) return err; brcmf_bss_connect_done(cfg, ndev, e, false); @@ -6150,6 +7176,9 @@ static s32 brcmf_notify_vif_event(struct brcmf_if *ifp, struct brcmf_if_event *ifevent = (struct brcmf_if_event *)data; struct brcmf_cfg80211_vif_event *event = &cfg->vif_event; struct brcmf_cfg80211_vif *vif; + enum nl80211_iftype iftype = NL80211_IFTYPE_UNSPECIFIED; + bool vif_pend = false; + int err; brcmf_dbg(TRACE, "Enter: action %u flags %u ifidx %u bsscfgidx %u\n", ifevent->action, ifevent->flags, ifevent->ifidx, @@ -6162,9 +7191,28 @@ static s32 brcmf_notify_vif_event(struct brcmf_if *ifp, switch (ifevent->action) { case BRCMF_E_IF_ADD: /* waiting process may have timed out */ - if (!cfg->vif_event.vif) { + if (!vif) { + /* handle IF_ADD event from firmware */ spin_unlock(&event->vif_event_lock); - return -EBADF; + vif_pend = true; + if (ifevent->role == WLC_E_IF_ROLE_STA) + iftype = NL80211_IFTYPE_STATION; + else if (ifevent->role == WLC_E_IF_ROLE_AP) + iftype = NL80211_IFTYPE_AP; + else + vif_pend = false; + + if (vif_pend) { + vif = brcmf_alloc_vif(cfg, iftype); + if (IS_ERR(vif)) { + brcmf_err("Role:%d failed to alloc vif\n", + ifevent->role); + return PTR_ERR(vif); + } + } else { + brcmf_err("Invalid Role:%d\n", ifevent->role); + return -EBADF; + } } ifp->vif = vif; @@ -6174,6 +7222,18 @@ static s32 brcmf_notify_vif_event(struct brcmf_if *ifp, ifp->ndev->ieee80211_ptr = &vif->wdev; SET_NETDEV_DEV(ifp->ndev, wiphy_dev(cfg->wiphy)); } + + if (vif_pend) { + err = brcmf_net_attach(ifp, false); + if (err) { + brcmf_err("netdevice register failed with err:%d\n", + err); + brcmf_free_vif(vif); + free_netdev(ifp->ndev); + } + return err; + } + spin_unlock(&event->vif_event_lock); wake_up(&event->vif_wq); return 0; @@ -6197,6 +7257,160 @@ static s32 brcmf_notify_vif_event(struct brcmf_if *ifp, return -EINVAL; } +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 17, 0)) +static s32 +brcmf_notify_ext_auth_request(struct brcmf_if *ifp, + const struct brcmf_event_msg *e, void *data) +{ + struct brcmf_pub *drvr = ifp->drvr; + struct cfg80211_external_auth_params params; + struct brcmf_auth_req_status_le *auth_req = + (struct brcmf_auth_req_status_le *)data; + s32 err = 0; + + brcmf_dbg(INFO, "Enter: event %s (%d) received\n", + brcmf_fweh_event_name(e->event_code), e->event_code); + + if (e->datalen < sizeof(*auth_req)) { + bphy_err(drvr, "Event %s (%d) data too small. Ignore\n", + brcmf_fweh_event_name(e->event_code), e->event_code); + return -EINVAL; + } + + memset(¶ms, 0, sizeof(params)); + params.action = NL80211_EXTERNAL_AUTH_START; + params.key_mgmt_suite = ntohl(WLAN_AKM_SUITE_SAE); + params.status = WLAN_STATUS_SUCCESS; + params.ssid.ssid_len = min_t(u32, 32, le32_to_cpu(auth_req->ssid_len)); + memcpy(params.ssid.ssid, auth_req->ssid, params.ssid.ssid_len); + memcpy(params.bssid, auth_req->peer_mac, ETH_ALEN); + + err = cfg80211_external_auth_request(ifp->ndev, ¶ms, GFP_ATOMIC); + if (err) + bphy_err(drvr, "Ext Auth request to supplicant failed (%d)\n", + err); + + return err; +} +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 17, 0)) */ + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 1, 0)) +static s32 +brcmf_notify_auth_frame_rx(struct brcmf_if *ifp, + const struct brcmf_event_msg *e, void *data) +{ + struct brcmf_pub *drvr = ifp->drvr; + struct brcmf_cfg80211_info *cfg = drvr->config; + struct wireless_dev *wdev; + u32 mgmt_frame_len = e->datalen - sizeof(struct brcmf_rx_mgmt_data); + struct brcmf_rx_mgmt_data *rxframe = (struct brcmf_rx_mgmt_data *)data; + u8 *frame = (u8 *)(rxframe + 1); + struct brcmu_chan ch; + struct ieee80211_mgmt *mgmt_frame; + s32 freq; + + brcmf_dbg(INFO, "Enter: event %s (%d) received\n", + brcmf_fweh_event_name(e->event_code), e->event_code); + + if (e->datalen < sizeof(*rxframe)) { + bphy_err(drvr, "Event %s (%d) data too small. Ignore\n", + brcmf_fweh_event_name(e->event_code), e->event_code); + return -EINVAL; + } + + wdev = &ifp->vif->wdev; + WARN_ON(!wdev); + + ch.chspec = be16_to_cpu(rxframe->chanspec); + cfg->d11inf.decchspec(&ch); + + mgmt_frame = kzalloc(mgmt_frame_len, GFP_KERNEL); + if (!mgmt_frame) + return -ENOMEM; + + mgmt_frame->frame_control = cpu_to_le16(IEEE80211_STYPE_AUTH); + memcpy(mgmt_frame->da, ifp->mac_addr, ETH_ALEN); + memcpy(mgmt_frame->sa, e->addr, ETH_ALEN); + brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BSSID, mgmt_frame->bssid, + ETH_ALEN); + frame += offsetof(struct ieee80211_mgmt, u); + memcpy(&mgmt_frame->u, frame, + mgmt_frame_len - offsetof(struct ieee80211_mgmt, u)); + + freq = ieee80211_channel_to_frequency(ch.control_ch_num, + BRCMU_CHAN_BAND_TO_NL80211(ch.band)); + + cfg80211_rx_mgmt(wdev, freq, 0, (u8 *)mgmt_frame, mgmt_frame_len, + NL80211_RXMGMT_FLAG_EXTERNAL_AUTH); + kfree(mgmt_frame); + return 0; +} +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 1, 0)) */ + +static s32 +brcmf_notify_mgmt_tx_status(struct brcmf_if *ifp, + const struct brcmf_event_msg *e, void *data) +{ + struct brcmf_cfg80211_vif *vif = ifp->vif; + u32 *packet_id = (u32 *)data; + + brcmf_dbg(INFO, "Enter: event %s (%d), status=%d\n", + brcmf_fweh_event_name(e->event_code), e->event_code, + e->status); + + if (!test_bit(BRCMF_MGMT_TX_SEND_FRAME, &vif->mgmt_tx_status) || + (*packet_id != vif->mgmt_tx_id)) + return 0; + + if (e->event_code == BRCMF_E_MGMT_FRAME_TXSTATUS) { + if (e->status == BRCMF_E_STATUS_SUCCESS) + set_bit(BRCMF_MGMT_TX_ACK, &vif->mgmt_tx_status); + else + set_bit(BRCMF_MGMT_TX_NOACK, &vif->mgmt_tx_status); + } else { + set_bit(BRCMF_MGMT_TX_OFF_CHAN_COMPLETED, &vif->mgmt_tx_status); + } + + complete(&vif->mgmt_tx); + return 0; +} + +static s32 +brcmf_notify_rssi_change_ind(struct brcmf_if *ifp, + const struct brcmf_event_msg *e, void *data) +{ + struct brcmf_cfg80211_info *cfg = ifp->drvr->config; + struct wl_event_data_rssi *value = (struct wl_event_data_rssi *)data; + s32 rssi = 0; + + brcmf_dbg(INFO, "Enter: event %s (%d), status=%d\n", + brcmf_fweh_event_name(e->event_code), e->event_code, + e->status); + + if (!cfg->cqm_info.enable) + return 0; + + rssi = ntohl(value->rssi); + brcmf_dbg(TRACE, "rssi: %d, threshold: %d, send event(%s)\n", + rssi, cfg->cqm_info.rssi_threshold, + rssi > cfg->cqm_info.rssi_threshold ? "HIGH" : "LOW"); +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0)) + cfg80211_cqm_rssi_notify(cfg_to_ndev(cfg), + (rssi > cfg->cqm_info.rssi_threshold ? + NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH : + NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW), + rssi, GFP_KERNEL); +#else /* (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0)) */ + cfg80211_cqm_rssi_notify(cfg_to_ndev(cfg), + (rssi > cfg->cqm_info.rssi_threshold ? + NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH : + NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW), + GFP_KERNEL); +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0)) */ + + return 0; +} + static void brcmf_init_conf(struct brcmf_cfg80211_conf *conf) { conf->frag_threshold = (u32)-1; @@ -6241,6 +7455,20 @@ static void brcmf_register_event_handlers(struct brcmf_cfg80211_info *cfg) brcmf_p2p_notify_action_tx_complete); brcmf_fweh_register(cfg->pub, BRCMF_E_PSK_SUP, brcmf_notify_connect_status); +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 17, 0)) + brcmf_fweh_register(cfg->pub, BRCMF_E_EXT_AUTH_REQ, + brcmf_notify_ext_auth_request); +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 17, 0)) */ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 1, 0)) + brcmf_fweh_register(cfg->pub, BRCMF_E_EXT_AUTH_FRAME_RX, + brcmf_notify_auth_frame_rx); +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 1, 0)) */ + brcmf_fweh_register(cfg->pub, BRCMF_E_MGMT_FRAME_TXSTATUS, + brcmf_notify_mgmt_tx_status); + brcmf_fweh_register(cfg->pub, BRCMF_E_MGMT_FRAME_OFF_CHAN_COMPLETE, + brcmf_notify_mgmt_tx_status); + brcmf_fweh_register(cfg->pub, BRCMF_E_RSSI, + brcmf_notify_rssi_change_ind); } static void brcmf_deinit_priv_mem(struct brcmf_cfg80211_info *cfg) @@ -6309,6 +7537,7 @@ 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); + brcmf_clear_assoc_ies(cfg); } static void init_vif_event(struct brcmf_cfg80211_vif_event *event) @@ -6426,15 +7655,17 @@ static int brcmf_construct_chaninfo(struct brcmf_cfg80211_info *cfg, struct brcmf_pub *drvr = cfg->pub; struct brcmf_if *ifp = brcmf_get_ifp(drvr, 0); struct ieee80211_supported_band *band; - struct ieee80211_channel *channel; + struct ieee80211_channel *channel, *cur, *next; struct brcmf_chanspec_list *list; struct brcmu_chan ch; int err; u8 *pbuf; u32 i, j; u32 total; - u32 chaninfo; - + u32 chaninfo, n_2g = 0, n_5g = 0; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0)) + u32 n_6g = 0; +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0)) */ pbuf = kzalloc(BRCMF_DCMD_MEDLEN, GFP_KERNEL); if (pbuf == NULL) @@ -6457,6 +7688,12 @@ static int brcmf_construct_chaninfo(struct brcmf_cfg80211_info *cfg, if (band) for (i = 0; i < band->n_channels; i++) band->channels[i].flags = IEEE80211_CHAN_DISABLED; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0)) + band = wiphy->bands[NL80211_BAND_6GHZ]; + if (band) + for (i = 0; i < band->n_channels; i++) + band->channels[i].flags = IEEE80211_CHAN_DISABLED; +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0)) */ total = le32_to_cpu(list->count); for (i = 0; i < total; i++) { @@ -6467,6 +7704,10 @@ static int brcmf_construct_chaninfo(struct brcmf_cfg80211_info *cfg, band = wiphy->bands[NL80211_BAND_2GHZ]; } else if (ch.band == BRCMU_CHAN_BAND_5G) { band = wiphy->bands[NL80211_BAND_5GHZ]; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0)) + } else if (ch.band == BRCMU_CHAN_BAND_6G) { + band = wiphy->bands[NL80211_BAND_6GHZ]; +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0)) */ } else { bphy_err(drvr, "Invalid channel Spec. 0x%x.\n", ch.chspec); @@ -6516,7 +7757,9 @@ static int brcmf_construct_chaninfo(struct brcmf_cfg80211_info *cfg, default: wiphy_warn(wiphy, "Firmware reported unsupported bandwidth %d\n", ch.bw); +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0)) fallthrough; +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0)) */ case BRCMU_CHAN_BW_20: /* enable the channel and disable other bandwidths * for now as mentioned order assure they are enabled @@ -6542,6 +7785,64 @@ static int brcmf_construct_chaninfo(struct brcmf_cfg80211_info *cfg, } } + /* Remove disabled channels to avoid unexpected restore. */ + band = wiphy->bands[NL80211_BAND_2GHZ]; + if (band) { + n_2g = band->n_channels; + for (i = 0; i < n_2g;) { + cur = &band->channels[i]; + if (cur->flags == IEEE80211_CHAN_DISABLED) { + for (j = i; j < n_2g - 1; j++) { + cur = &band->channels[j]; + next = &band->channels[j + 1]; + memcpy(cur, next, sizeof(*cur)); + } + n_2g--; + } else + i++; + } + wiphy->bands[NL80211_BAND_2GHZ]->n_channels = n_2g; + } + band = wiphy->bands[NL80211_BAND_5GHZ]; + if (band) { + n_5g = band->n_channels; + for (i = 0; i < n_5g;) { + cur = &band->channels[i]; + if (cur->flags == IEEE80211_CHAN_DISABLED) { + for (j = i; j < n_5g - 1; j++) { + cur = &band->channels[j]; + next = &band->channels[j + 1]; + memcpy(cur, next, sizeof(*cur)); + } + n_5g--; + } else + i++; + } + wiphy->bands[NL80211_BAND_5GHZ]->n_channels = n_5g; + } +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0)) + band = wiphy->bands[NL80211_BAND_6GHZ]; + if (band) { + n_6g = band->n_channels; + for (i = 0; i < n_6g;) { + cur = &band->channels[i]; + if (cur->flags == IEEE80211_CHAN_DISABLED) { + for (j = i; j < n_6g - 1; j++) { + cur = &band->channels[j]; + next = &band->channels[j + 1]; + memcpy(cur, next, sizeof(*cur)); + } + /* To avoid fw crash while delete all channels */ + if (n_6g == 1) + break; + n_6g--; + } else + i++; + } + wiphy->bands[NL80211_BAND_6GHZ]->n_channels = n_6g; + } +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0)) */ + fail_pbuf: kfree(pbuf); return err; @@ -6639,9 +7940,20 @@ static void brcmf_get_bwcap(struct brcmf_if *ifp, u32 bw_cap[]) err = brcmf_fil_iovar_int_get(ifp, "bw_cap", &band); if (!err) { bw_cap[NL80211_BAND_5GHZ] = band; - return; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0)) + band = WLC_BAND_6G; + err = brcmf_fil_iovar_int_get(ifp, "bw_cap", &band); + if (!err) { + bw_cap[NL80211_BAND_6GHZ] = band; + return; + } + //WARN_ON(1); +#else /* (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0)) */ + //WARN_ON(1); + return; +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0)) */ } - WARN_ON(1); + //WARN_ON(1); return; } brcmf_dbg(INFO, "fallback to mimo_bw_cap info\n"); @@ -6654,10 +7966,14 @@ static void brcmf_get_bwcap(struct brcmf_if *ifp, u32 bw_cap[]) switch (mimo_bwcap) { case WLC_N_BW_40ALL: bw_cap[NL80211_BAND_2GHZ] |= WLC_BW_40MHZ_BIT; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0)) fallthrough; +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0)) */ case WLC_N_BW_20IN2G_40IN5G: bw_cap[NL80211_BAND_5GHZ] |= WLC_BW_40MHZ_BIT; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0)) fallthrough; +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0)) */ case WLC_N_BW_20ALL: bw_cap[NL80211_BAND_2GHZ] |= WLC_BW_20MHZ_BIT; bw_cap[NL80211_BAND_5GHZ] |= WLC_BW_20MHZ_BIT; @@ -6670,6 +7986,12 @@ static void brcmf_get_bwcap(struct brcmf_if *ifp, u32 bw_cap[]) static void brcmf_update_ht_cap(struct ieee80211_supported_band *band, u32 bw_cap[2], u32 nchain) { +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0)) + /* not allowed in 6G band */ + if (band->band == NL80211_BAND_6GHZ) + return; +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0)) */ + band->ht_cap.ht_supported = true; if (bw_cap[band->band] & WLC_BW_40MHZ_BIT) { band->ht_cap.cap |= IEEE80211_HT_CAP_SGI_40; @@ -6700,9 +8022,11 @@ static void brcmf_update_vht_cap(struct ieee80211_supported_band *band, { __le16 mcs_map; - /* not allowed in 2.4G band */ - if (band->band == NL80211_BAND_2GHZ) +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0)) + /* not allowed in 2.4G & 6G band */ + if (band->band == NL80211_BAND_2GHZ || band->band == NL80211_BAND_6GHZ) return; +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0)) */ band->vht_cap.vht_supported = true; /* 80MHz is mandatory */ @@ -6736,6 +8060,68 @@ static void brcmf_update_vht_cap(struct ieee80211_supported_band *band, } } +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0)) +static void brcmf_update_he_cap(struct ieee80211_supported_band *band, + struct ieee80211_sband_iftype_data *data) +{ + int idx = 1; + struct ieee80211_sta_he_cap *he_cap = &data->he_cap; + struct ieee80211_he_cap_elem *he_cap_elem = &he_cap->he_cap_elem; + struct ieee80211_he_mcs_nss_supp *he_mcs = &he_cap->he_mcs_nss_supp; + + if (data == NULL) { + brcmf_dbg(INFO, "failed to allco mem\n"); + return; + } + + data->types_mask = BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_AP); + he_cap->has_he = true; + he_cap_elem->mac_cap_info[0] = IEEE80211_HE_MAC_CAP0_HTC_HE; + if (band->band == NL80211_BAND_5GHZ) { + he_cap_elem->phy_cap_info[0] = + IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G | + IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G | + IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_80PLUS80_MHZ_IN_5G; + } + he_cap_elem->phy_cap_info[1] = + IEEE80211_HE_PHY_CAP1_LDPC_CODING_IN_PAYLOAD; + he_cap_elem->phy_cap_info[2] = + IEEE80211_HE_PHY_CAP2_NDP_4x_LTF_AND_3_2US; + he_cap_elem->phy_cap_info[3] = + IEEE80211_HE_PHY_CAP3_SU_BEAMFORMER; + he_cap_elem->phy_cap_info[4] = + IEEE80211_HE_PHY_CAP4_SU_BEAMFORMEE | + IEEE80211_HE_PHY_CAP4_BEAMFORMEE_MAX_STS_UNDER_80MHZ_MASK | + IEEE80211_HE_PHY_CAP4_BEAMFORMEE_MAX_STS_ABOVE_80MHZ_4; + he_cap_elem->phy_cap_info[5] = + IEEE80211_HE_PHY_CAP5_BEAMFORMEE_NUM_SND_DIM_UNDER_80MHZ_2; + he_cap_elem->phy_cap_info[6] = + IEEE80211_HE_PHY_CAP6_CODEBOOK_SIZE_42_SU | + IEEE80211_HE_PHY_CAP6_CODEBOOK_SIZE_75_MU | + IEEE80211_HE_PHY_CAP6_TRIG_SU_BEAMFORMER_FB | + IEEE80211_HE_PHY_CAP6_TRIG_MU_BEAMFORMER_FB | + IEEE80211_HE_PHY_CAP6_TRIG_CQI_FB | + IEEE80211_HE_PHY_CAP6_PPE_THRESHOLD_PRESENT; + he_cap_elem->phy_cap_info[7] = + IEEE80211_HE_PHY_CAP7_MAX_NC_1; + he_cap_elem->phy_cap_info[8] = + IEEE80211_HE_PHY_CAP8_20MHZ_IN_160MHZ_HE_PPDU | + IEEE80211_HE_PHY_CAP8_80MHZ_IN_160MHZ_HE_PPDU; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 20, 0)) + he_cap_elem->phy_cap_info[9] = + IEEE80211_HE_PHY_CAP9_TX_1024_QAM_LESS_THAN_242_TONE_RU | + IEEE80211_HE_PHY_CAP9_RX_1024_QAM_LESS_THAN_242_TONE_RU; +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 20, 0)) */ + he_mcs->rx_mcs_80 = cpu_to_le16(0xfffa); + he_mcs->tx_mcs_80 = cpu_to_le16(0xfffa); + he_mcs->rx_mcs_160 = cpu_to_le16((0xfffa)); + he_mcs->tx_mcs_160 = cpu_to_le16((0xfffa)); + + band->n_iftype_data = idx; + band->iftype_data = data; +} +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0)) */ + static int brcmf_setup_wiphybands(struct brcmf_cfg80211_info *cfg) { struct brcmf_pub *drvr = cfg->pub; @@ -6743,7 +8129,10 @@ static int brcmf_setup_wiphybands(struct brcmf_cfg80211_info *cfg) struct wiphy *wiphy = cfg_to_wiphy(cfg); u32 nmode = 0; u32 vhtmode = 0; - u32 bw_cap[2] = { WLC_BW_20MHZ_BIT, WLC_BW_20MHZ_BIT }; + u32 bw_cap[4] = { WLC_BW_20MHZ_BIT, /* 2GHz */ + WLC_BW_20MHZ_BIT, /* 5GHz */ + 0, /* 60GHz */ + WLC_BW_20MHZ_BIT };/* 6GHz */ u32 rxchain; u32 nchain; int err; @@ -6752,6 +8141,7 @@ static int brcmf_setup_wiphybands(struct brcmf_cfg80211_info *cfg) u32 txstreams = 0; u32 txbf_bfe_cap = 0; u32 txbf_bfr_cap = 0; + u32 he[2] = {0, 0}; (void)brcmf_fil_iovar_int_get(ifp, "vhtmode", &vhtmode); err = brcmf_fil_iovar_int_get(ifp, "nmode", &nmode); @@ -6760,8 +8150,10 @@ static int brcmf_setup_wiphybands(struct brcmf_cfg80211_info *cfg) } else { brcmf_get_bwcap(ifp, bw_cap); } - brcmf_dbg(INFO, "nmode=%d, vhtmode=%d, bw_cap=(%d, %d)\n", - nmode, vhtmode, bw_cap[NL80211_BAND_2GHZ], + (void)brcmf_fil_iovar_data_get(ifp, "he", he, sizeof(he)); + + brcmf_dbg(INFO, "nmode=%d, vhtmode=%d, he=%d, bw_cap=(%d, %d)\n", + nmode, vhtmode, he[0], bw_cap[NL80211_BAND_2GHZ], bw_cap[NL80211_BAND_5GHZ]); err = brcmf_fil_iovar_int_get(ifp, "rxchain", &rxchain); @@ -6798,6 +8190,10 @@ static int brcmf_setup_wiphybands(struct brcmf_cfg80211_info *cfg) if (vhtmode) brcmf_update_vht_cap(band, bw_cap, nchain, txstreams, txbf_bfe_cap, txbf_bfr_cap); +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0)) + if (he[0]) + brcmf_update_he_cap(band, &sdata[band->band]); +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0)) */ } return 0; @@ -6808,6 +8204,7 @@ brcmf_txrx_stypes[NUM_NL80211_IFTYPES] = { [NL80211_IFTYPE_STATION] = { .tx = 0xffff, .rx = BIT(IEEE80211_STYPE_ACTION >> 4) | + BIT(IEEE80211_STYPE_AUTH >> 4) | BIT(IEEE80211_STYPE_PROBE_REQ >> 4) }, [NL80211_IFTYPE_P2P_CLIENT] = { @@ -6871,7 +8268,7 @@ brcmf_txrx_stypes[NUM_NL80211_IFTYPES] = { * * p2p, mchan, and mbss: * - * #STA <= 1, #P2P-DEV <= 1, #{P2P-CL, P2P-GO} <= 1, channels = 2, 3 total + * #STA <= 2, #P2P-DEV <= 1, #{P2P-CL, P2P-GO} <= 1, channels = 2, 3 total * #STA <= 1, #P2P-DEV <= 1, #AP <= 1, #P2P-CL <= 1, channels = 1, 4 total * #AP <= 4, matching BI, channels = 1, 4 total * @@ -6917,7 +8314,7 @@ static int brcmf_setup_ifmodes(struct wiphy *wiphy, struct brcmf_if *ifp) goto err; combo[c].num_different_channels = 1 + (rsdb || (p2p && mchan)); - c0_limits[i].max = 1; + c0_limits[i].max = 1 + (p2p && mchan); c0_limits[i++].types = BIT(NL80211_IFTYPE_STATION); if (mon_flag) { c0_limits[i].max = 1; @@ -7017,6 +8414,7 @@ static void brcmf_wiphy_wowl_params(struct wiphy *wiphy, struct brcmf_if *ifp) struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); struct brcmf_pub *drvr = cfg->pub; struct wiphy_wowlan_support *wowl; + struct cfg80211_wowlan *brcmf_wowlan_config = NULL; wowl = kmemdup(&brcmf_wowlan_support, sizeof(brcmf_wowlan_support), GFP_KERNEL); @@ -7039,17 +8437,39 @@ static void brcmf_wiphy_wowl_params(struct wiphy *wiphy, struct brcmf_if *ifp) } wiphy->wowlan = wowl; + + /* wowlan_config structure report for kernels */ + brcmf_wowlan_config = kzalloc(sizeof(*brcmf_wowlan_config), + GFP_KERNEL); + if (brcmf_wowlan_config) { + brcmf_wowlan_config->any = false; + brcmf_wowlan_config->disconnect = true; + brcmf_wowlan_config->eap_identity_req = true; + brcmf_wowlan_config->four_way_handshake = true; + brcmf_wowlan_config->rfkill_release = false; + brcmf_wowlan_config->patterns = NULL; + brcmf_wowlan_config->n_patterns = 0; + brcmf_wowlan_config->tcp = NULL; + if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_WOWL_GTK)) + brcmf_wowlan_config->gtk_rekey_failure = true; + else + brcmf_wowlan_config->gtk_rekey_failure = false; + } else { + brcmf_err("Can not allocate memory for brcm_wowlan_config\n"); + } + wiphy->wowlan_config = brcmf_wowlan_config; #endif } static int brcmf_setup_wiphy(struct wiphy *wiphy, struct brcmf_if *ifp) { struct brcmf_pub *drvr = ifp->drvr; + struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); const struct ieee80211_iface_combination *combo; struct ieee80211_supported_band *band; u16 max_interfaces = 0; bool gscan; - __le32 bandlist[3]; + __le32 bandlist[4]; u32 n_bands; int err, i; @@ -7083,10 +8503,12 @@ static int brcmf_setup_wiphy(struct wiphy *wiphy, struct brcmf_if *ifp) wiphy->cipher_suites = brcmf_cipher_suites; wiphy->n_cipher_suites = ARRAY_SIZE(brcmf_cipher_suites); if (!brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MFP)) - wiphy->n_cipher_suites--; + wiphy->n_cipher_suites -= 4; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 7, 0)) wiphy->bss_select_support = BIT(NL80211_BSS_SELECT_ATTR_RSSI) | BIT(NL80211_BSS_SELECT_ATTR_BAND_PREF) | BIT(NL80211_BSS_SELECT_ATTR_RSSI_ADJUST); +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 7, 0)) */ wiphy->flags |= WIPHY_FLAG_NETNS_OK | WIPHY_FLAG_PS_ON_BY_DEFAULT | @@ -7098,33 +8520,61 @@ static int brcmf_setup_wiphy(struct wiphy *wiphy, struct brcmf_if *ifp) if (!ifp->drvr->settings->roamoff) wiphy->flags |= WIPHY_FLAG_SUPPORTS_FW_ROAM; if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_FWSUP)) { +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 13, 0)) wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_PSK); wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_1X); +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 13, 0)) */ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 3, 0)) if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_SAE)) wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_SAE_OFFLOAD); +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 3, 0)) */ } if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_FWAUTH)) { +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 9, 0)) wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_4WAY_HANDSHAKE_AP_PSK); +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 9, 0)) */ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0)) if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_SAE)) wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_SAE_OFFLOAD_AP); +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0)) */ + } + if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_SAE_EXT)) { + wiphy->features |= NL80211_FEATURE_SAE; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 1, 0)) + wiphy_ext_feature_set(wiphy, + NL80211_EXT_FEATURE_AP_PMKSA_CACHING); +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 1, 0)) */ } +#if (0) + if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_FBT) || + brcmf_feat_is_enabled(ifp, BRCMF_FEAT_OKC)) + wiphy_ext_feature_set(wiphy, + NL80211_EXT_FEATURE_ROAM_OFFLOAD); +#endif +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)) + wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_CQM_RSSI_LIST); +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)) */ + wiphy->mgmt_stypes = brcmf_txrx_stypes; wiphy->max_remain_on_channel_duration = 5000; if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_PNO)) { gscan = brcmf_feat_is_enabled(ifp, BRCMF_FEAT_GSCAN); - brcmf_pno_wiphy_params(wiphy, gscan); +// brcmf_pno_wiphy_params(wiphy, gscan); } /* vendor commands/events support */ wiphy->vendor_commands = brcmf_vendor_cmds; wiphy->n_vendor_commands = BRCMF_VNDR_CMDS_LAST - 1; + wiphy->vendor_events = brcmf_vendor_events; + wiphy->n_vendor_events = BRCMF_VNDR_EVTS_LAST; + brcmf_fweh_register(cfg->pub, BRCMF_E_PHY_TEMP, + brcmf_wiphy_phy_temp_evt_handler); - if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_WOWL)) - brcmf_wiphy_wowl_params(wiphy, ifp); + brcmf_wiphy_wowl_params(wiphy, ifp); err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BANDLIST, &bandlist, sizeof(bandlist)); if (err) { @@ -7168,15 +8618,37 @@ static int brcmf_setup_wiphy(struct wiphy *wiphy, struct brcmf_if *ifp) band->n_channels = ARRAY_SIZE(__wl_5ghz_channels); wiphy->bands[NL80211_BAND_5GHZ] = band; } +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0)) + if (bandlist[i] == cpu_to_le32(WLC_BAND_6G)) { + band = kmemdup(&__wl_band_6ghz, sizeof(__wl_band_6ghz), + GFP_KERNEL); + if (!band) + return -ENOMEM; + + band->channels = kmemdup(&__wl_6ghz_channels, + sizeof(__wl_6ghz_channels), + GFP_KERNEL); + if (!band->channels) { + kfree(band); + return -ENOMEM; + } + + band->n_channels = ARRAY_SIZE(__wl_6ghz_channels); + wiphy->bands[NL80211_BAND_6GHZ] = band; + } +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0)) */ } +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 17, 0)) if (wiphy->bands[NL80211_BAND_5GHZ] && brcmf_feat_is_enabled(ifp, BRCMF_FEAT_DOT11H)) wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_DFS_OFFLOAD); +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 17, 0)) */ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0)) wiphy_read_of_freq_limits(wiphy); - +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0)) */ return 0; } @@ -7187,6 +8659,7 @@ static s32 brcmf_config_dongle(struct brcmf_cfg80211_info *cfg) struct wireless_dev *wdev; struct brcmf_if *ifp; s32 power_mode; + s32 eap_restrict; s32 err = 0; if (cfg->dongle_up) @@ -7201,7 +8674,7 @@ static s32 brcmf_config_dongle(struct brcmf_cfg80211_info *cfg) brcmf_dongle_scantime(ifp); - power_mode = cfg->pwr_save ? PM_FAST : PM_OFF; + power_mode = cfg->pwr_save ? ifp->drvr->settings->default_pm : PM_OFF; err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM, power_mode); if (err) goto default_conf_out; @@ -7211,8 +8684,21 @@ static s32 brcmf_config_dongle(struct brcmf_cfg80211_info *cfg) err = brcmf_dongle_roam(ifp); if (err) goto default_conf_out; + + eap_restrict = ifp->drvr->settings->eap_restrict; + if (eap_restrict) { + err = brcmf_fil_iovar_int_set(ifp, "eap_restrict", + eap_restrict); + if (err) + brcmf_info("eap_restrict error (%d)\n", err); + } +#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 12, 0)) + err = brcmf_cfg80211_change_iface(wdev->wiphy, ndev, wdev->iftype, + NULL, NULL); +#else /* (LINUX_VERSION_CODE < KERNEL_VERSION(4, 12, 0)) */ err = brcmf_cfg80211_change_iface(wdev->wiphy, ndev, wdev->iftype, NULL); +#endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(4, 12, 0)) */ if (err) goto default_conf_out; @@ -7254,6 +8740,7 @@ static s32 __brcmf_cfg80211_down(struct brcmf_if *ifp) the state fw and WPA_Supplicant state consistent */ brcmf_delay(500); + cfg->dongle_up = false; } brcmf_abort_scanning(cfg); @@ -7396,6 +8883,229 @@ static s32 brcmf_translate_country_code(struct brcmf_pub *drvr, char alpha2[2], return 0; } +static int +brcmf_parse_dump_obss(char *buf, struct brcmf_dump_survey *survey) +{ + int i; + char *token; + char delim[] = "\n "; + unsigned long val; + int err = 0; + + token = strsep(&buf, delim); + while (token) { + if (!strcmp(token, "OBSS")) { + for (i = 0; i < OBSS_TOKEN_IDX; i++) + token = strsep(&buf, delim); + err = kstrtoul(token, 10, &val); + survey->obss = val; + } + + if (!strcmp(token, "IBSS")) { + for (i = 0; i < IBSS_TOKEN_IDX; i++) + token = strsep(&buf, delim); + err = kstrtoul(token, 10, &val); + survey->ibss = val; + } + + if (!strcmp(token, "TXDur")) { + for (i = 0; i < TX_TOKEN_IDX; i++) + token = strsep(&buf, delim); + err = kstrtoul(token, 10, &val); + survey->tx = val; + } + + if (!strcmp(token, "Category")) { + for (i = 0; i < CTG_TOKEN_IDX; i++) + token = strsep(&buf, delim); + err = kstrtoul(token, 10, &val); + survey->no_ctg = val; + } + + if (!strcmp(token, "Packet")) { + for (i = 0; i < PKT_TOKEN_IDX; i++) + token = strsep(&buf, delim); + err = kstrtoul(token, 10, &val); + survey->no_pckt = val; + } + + if (!strcmp(token, "Opp(time):")) { + for (i = 0; i < IDLE_TOKEN_IDX; i++) + token = strsep(&buf, delim); + err = kstrtoul(token, 10, &val); + survey->idle = val; + } + + token = strsep(&buf, delim); + + if (err) + return err; + } + + return 0; +} + +static int +brcmf_dump_obss(struct brcmf_if *ifp, struct cca_msrmnt_query req, + struct brcmf_dump_survey *survey) +{ + struct cca_stats_n_flags *results; + char *buf; + int err; + + buf = kzalloc(sizeof(char) * BRCMF_DCMD_MEDLEN, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + memcpy(buf, &req, sizeof(struct cca_msrmnt_query)); + err = brcmf_fil_iovar_data_get(ifp, "dump_obss", + buf, BRCMF_DCMD_MEDLEN); + if (err) { + brcmf_err("dump_obss error (%d)\n", err); + err = -EINVAL; + goto exit; + } + results = (struct cca_stats_n_flags *)(buf); + + if (req.msrmnt_query) + brcmf_parse_dump_obss(results->buf, survey); + +exit: + kfree(buf); + return err; +} + +static s32 +cfg80211_set_channel(struct wiphy *wiphy, struct net_device *dev, + struct ieee80211_channel *chan, + enum nl80211_channel_type channel_type) +{ + u16 chspec = 0; + int err = 0; + struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); + struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg)); + + /* set_channel */ + chspec = channel_to_chanspec(&cfg->d11inf, chan); + if (chspec != INVCHANSPEC) { + err = brcmf_fil_iovar_int_set(ifp, "chanspec", chspec); + if (err) + err = -EINVAL; + } else { + brcmf_err("failed to convert host chanspec to fw chanspec\n"); + err = -EINVAL; + } + + return err; +} + +static int +brcmf_cfg80211_dump_survey(struct wiphy *wiphy, struct net_device *ndev, + int idx, struct survey_info *info) +{ + struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); + struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg)); + struct brcmf_dump_survey *survey; + struct ieee80211_supported_band *band; + struct ieee80211_channel *chan; + struct cca_msrmnt_query req; + u32 noise; + int err; + + brcmf_dbg(TRACE, "Enter: channel idx=%d\n", idx); + + if (!brcmf_is_apmode(ifp->vif)) + return -ENOENT; + + /* Do not run survey when VIF in CONNECTING / CONNECTED states */ + if ((test_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state)) || + (test_bit(BRCMF_VIF_STATUS_CONNECTED, &ifp->vif->sme_state))) { + return -EBUSY; + } + + band = wiphy->bands[NL80211_BAND_2GHZ]; + if (band && idx >= band->n_channels) { + idx -= band->n_channels; + band = NULL; + } + + if (!band || idx >= band->n_channels) { + band = wiphy->bands[NL80211_BAND_5GHZ]; + if (idx >= band->n_channels) + return -ENOENT; + } + + /* Setting current channel to the requested channel */ + chan = &band->channels[idx]; + err = cfg80211_set_channel(wiphy, ndev, chan, NL80211_CHAN_HT20); + if (err) { + info->channel = chan; + info->filled = 0; + return 0; + } + + survey = kzalloc(sizeof(*survey), GFP_KERNEL); + if (!survey) + return -ENOMEM; + + /* Disable mpc */ + brcmf_set_mpc(ifp, 0); + + /* Set interface up, explicitly. */ + err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_UP, 1); + if (err) { + brcmf_err("set interface up failed, err = %d\n", err); + goto exit; + } + + /* Get noise value */ + err = brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_PHY_NOISE, &noise); + if (err) { + brcmf_err("Get Phy Noise failed, use dummy value\n"); + noise = CHAN_NOISE_DUMMY; + } + + + /* Start Measurement for obss stats on current channel */ + req.msrmnt_query = 0; + req.time_req = ACS_MSRMNT_DELAY; + err = brcmf_dump_obss(ifp, req, survey); + if (err) + goto exit; + + /* Add 10 ms for IOVAR completion */ + msleep(ACS_MSRMNT_DELAY + 10); + + /* Issue IOVAR to collect measurement results */ + req.msrmnt_query = 1; + err = brcmf_dump_obss(ifp, req, survey); + if (err) + goto exit; + + info->channel = chan; + info->noise = noise; + info->time = ACS_MSRMNT_DELAY; + info->time_busy = ACS_MSRMNT_DELAY - survey->idle; + info->time_rx = survey->obss + survey->ibss + survey->no_ctg + + survey->no_pckt; + info->time_tx = survey->tx; + info->filled = SURVEY_INFO_NOISE_DBM | SURVEY_INFO_TIME | + SURVEY_INFO_TIME_BUSY | SURVEY_INFO_TIME_RX | + SURVEY_INFO_TIME_TX; + + brcmf_dbg(INFO, "OBSS dump: channel %d: survey duration %d\n", + ieee80211_frequency_to_channel(chan->center_freq), + ACS_MSRMNT_DELAY); + brcmf_dbg(INFO, "noise(%d) busy(%llu) rx(%llu) tx(%llu)\n", + info->noise, info->time_busy, info->time_rx, info->time_tx); + +exit: + if (!brcmf_is_apmode(ifp->vif)) + brcmf_set_mpc(ifp, 1); + kfree(survey); + return err; +} + static void brcmf_cfg80211_reg_notifier(struct wiphy *wiphy, struct regulatory_request *req) { @@ -7459,6 +9169,13 @@ static void brcmf_free_wiphy(struct wiphy *wiphy) kfree(wiphy->bands[NL80211_BAND_5GHZ]->channels); kfree(wiphy->bands[NL80211_BAND_5GHZ]); } +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0)) + if (wiphy->bands[NL80211_BAND_6GHZ]) { + kfree(wiphy->bands[NL80211_BAND_6GHZ]->channels); + kfree(wiphy->bands[NL80211_BAND_6GHZ]); + } +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0)) */ + #if IS_ENABLED(CONFIG_PM) if (wiphy->wowlan != &brcmf_wowlan_support) kfree(wiphy->wowlan); @@ -7491,6 +9208,8 @@ struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr, cfg->wiphy = wiphy; cfg->pub = drvr; + cfg->pm_state = BRCMF_CFG80211_PM_STATE_RESUMED; + cfg->num_softap = 0; init_vif_event(&cfg->vif_event); INIT_LIST_HEAD(&cfg->vif_list); @@ -7547,11 +9266,10 @@ struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr, if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_WOWL_GTK)) ops->set_rekey_data = brcmf_cfg80211_set_rekey_data; #endif - err = wiphy_register(wiphy); - if (err < 0) { - bphy_err(drvr, "Could not register wiphy device (%d)\n", err); - goto priv_out; - } + if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_DUMP_OBSS)) + ops->dump_survey = brcmf_cfg80211_dump_survey; + else + ops->dump_survey = NULL; err = brcmf_setup_wiphybands(cfg); if (err) { @@ -7559,6 +9277,12 @@ struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr, goto wiphy_unreg_out; } + err = wiphy_register(wiphy); + if (err < 0) { + bphy_err(drvr, "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. */ diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h index 17817cdb5de26..715bd43bc003c 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h @@ -92,6 +92,15 @@ #define BRCMF_VIF_EVENT_TIMEOUT msecs_to_jiffies(1500) +#define BRCMF_PM_WAIT_MAXRETRY 100 + +/* cfg80211 wowlan definitions */ +#define WL_WOWLAN_MAX_PATTERNS 8 +#define WL_WOWLAN_MIN_PATTERN_LEN 1 +#define WL_WOWLAN_MAX_PATTERN_LEN 255 +#define WL_WOWLAN_PKT_FILTER_ID_FIRST 201 +#define WL_WOWLAN_PKT_FILTER_ID_LAST (WL_WOWLAN_PKT_FILTER_ID_FIRST + \ + WL_WOWLAN_MAX_PATTERNS - 1) /** * enum brcmf_scan_status - scan engine status * @@ -125,7 +134,8 @@ enum brcmf_profile_fwsup { BRCMF_PROFILE_FWSUP_NONE, BRCMF_PROFILE_FWSUP_PSK, BRCMF_PROFILE_FWSUP_1X, - BRCMF_PROFILE_FWSUP_SAE + BRCMF_PROFILE_FWSUP_SAE, + BRCMF_PROFILE_FWSUP_ROAM }; /** @@ -155,6 +165,7 @@ struct brcmf_cfg80211_profile { enum brcmf_profile_fwsup use_fwsup; u16 use_fwauth; bool is_ft; + bool is_okc; }; /** @@ -178,6 +189,28 @@ enum brcmf_vif_status { BRCMF_VIF_STATUS_ASSOC_SUCCESS, }; +enum brcmf_cfg80211_pm_state { + BRCMF_CFG80211_PM_STATE_RESUMED, + BRCMF_CFG80211_PM_STATE_RESUMING, + BRCMF_CFG80211_PM_STATE_SUSPENDED, + BRCMF_CFG80211_PM_STATE_SUSPENDING, +}; + +/** + * enum brcmf_mgmt_tx_status - mgmt frame tx status + * + * @BRCMF_MGMT_TX_ACK: mgmt frame acked + * @BRCMF_MGMT_TX_NOACK: mgmt frame not acked + * @BRCMF_MGMT_TX_OFF_CHAN_COMPLETED: off-channel complete + * @BRCMF_MGMT_TX_SEND_FRAME: mgmt frame tx is in progres + */ +enum brcmf_mgmt_tx_status { + BRCMF_MGMT_TX_ACK, + BRCMF_MGMT_TX_NOACK, + BRCMF_MGMT_TX_OFF_CHAN_COMPLETED, + BRCMF_MGMT_TX_SEND_FRAME +}; + /** * struct vif_saved_ie - holds saved IEs for a virtual interface. * @@ -221,6 +254,9 @@ struct brcmf_cfg80211_vif { unsigned long sme_state; struct vif_saved_ie saved_ie; struct list_head list; + struct completion mgmt_tx; + unsigned long mgmt_tx_status; + u32 mgmt_tx_id; u16 mgmt_rx_reg; bool mbss; int is_11d; @@ -261,6 +297,11 @@ struct escan_info { struct cfg80211_scan_request *request); }; +struct cqm_rssi_info { + bool enable; + s32 rssi_threshold; +}; + /** * struct brcmf_cfg80211_vif_event - virtual interface event information. * @@ -357,6 +398,7 @@ struct brcmf_cfg80211_info { struct escan_info escan_info; struct timer_list escan_timeout; struct work_struct escan_timeout_work; + struct cqm_rssi_info cqm_info; struct list_head vif_list; struct brcmf_cfg80211_vif_event vif_event; struct completion vif_disabled; @@ -365,6 +407,8 @@ struct brcmf_cfg80211_info { struct brcmf_cfg80211_wowl wowl; struct brcmf_pno_info *pno; u8 ac_priority[MAX_8021D_PRIO]; + u8 pm_state; + u8 num_softap; }; /** @@ -461,5 +505,6 @@ s32 brcmf_notify_escan_complete(struct brcmf_cfg80211_info *cfg, 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); - +bool brcmf_is_apmode_operating(struct wiphy *wiphy); +void brcmf_cfg80211_update_proto_addr_mode(struct wireless_dev *wdev); #endif /* BRCMFMAC_CFG80211_H */ diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c index 5bf11e46fc49a..5dac50415197b 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include @@ -213,6 +214,50 @@ struct sbsocramregs { #define ARMCR4_BSZ_MASK 0x3f #define ARMCR4_BSZ_MULT 8192 +/* Minimum PMU resource mask for 43012C0 */ +#define CY_43012_PMU_MIN_RES_MASK 0xF8BFE77 + +/* PMU STATUS mask for 43012C0 */ +#define CY_43012_PMU_STATUS_MASK 0x1AC + +/* PMU CONTROL EXT mask for 43012C0 */ +#define CY_43012_PMU_CONTROL_EXT_MASK 0x11 + +/* PMU Watchdog Counter Tick value for 43012C0 */ +#define CY_43012_PMU_WATCHDOG_TICK_VAL 0x04 + +/* PMU Watchdog Counter Tick value for 4373 */ +#define CY_4373_PMU_WATCHDOG_TICK_VAL 0x04 + +/* Minimum PMU resource mask for 4373 */ +#define CY_4373_PMU_MIN_RES_MASK 0xFCAFF7F + +/* CYW55560 dedicated space and RAM base */ +#define CYW55560_TCAM_SIZE 0x800 +#define CYW55560_TRXHDR_SIZE 0x2b4 +#define CYW55560_RAM_BASE (0x370000 + \ + CYW55560_TCAM_SIZE + CYW55560_TRXHDR_SIZE) + +#define BRCMF_BLHS_POLL_INTERVAL 10 /* msec */ +#define BRCMF_BLHS_D2H_READY_TIMEOUT 100 /* msec */ +#define BRCMF_BLHS_D2H_TRXHDR_PARSE_DONE_TIMEOUT 50 /* msec */ +#define BRCMF_BLHS_D2H_VALDN_DONE_TIMEOUT 250 /* msec */ + +/* Bootloader handshake flags - dongle to host */ +#define BRCMF_BLHS_D2H_START BIT(0) +#define BRCMF_BLHS_D2H_READY BIT(1) +#define BRCMF_BLHS_D2H_STEADY BIT(2) +#define BRCMF_BLHS_D2H_TRXHDR_PARSE_DONE BIT(3) +#define BRCMF_BLHS_D2H_VALDN_START BIT(4) +#define BRCMF_BLHS_D2H_VALDN_RESULT BIT(5) +#define BRCMF_BLHS_D2H_VALDN_DONE BIT(6) + +/* Bootloader handshake flags - host to dongle */ +#define BRCMF_BLHS_H2D_DL_FW_START BIT(0) +#define BRCMF_BLHS_H2D_DL_FW_DONE BIT(1) +#define BRCMF_BLHS_H2D_DL_NVRAM_DONE BIT(2) +#define BRCMF_BLHS_H2D_BL_RESET_ON_ERROR BIT(3) + struct brcmf_core_priv { struct brcmf_core pub; u32 wrapbase; @@ -639,6 +684,7 @@ static void brcmf_chip_socram_ramsize(struct brcmf_core_priv *sr, u32 *ramsize, *srsize = (32 * 1024); break; case BRCM_CC_43430_CHIP_ID: + case CY_CC_43439_CHIP_ID: /* assume sr for now as we can not check * firmware sr capability at this point. */ @@ -726,6 +772,10 @@ static u32 brcmf_chip_tcm_rambase(struct brcmf_chip_priv *ci) case BRCM_CC_4364_CHIP_ID: case CY_CC_4373_CHIP_ID: return 0x160000; + case CY_CC_89459_CHIP_ID: + return ((ci->pub.chiprev < 9) ? 0x180000 : 0x160000); + case CY_CC_55560_CHIP_ID: + return CYW55560_RAM_BASE; default: brcmf_err("unknown chip: %s\n", ci->pub.name); break; @@ -744,6 +794,9 @@ int brcmf_chip_get_raminfo(struct brcmf_chip *pub) if (mem) { mem_core = container_of(mem, struct brcmf_core_priv, pub); ci->pub.ramsize = brcmf_chip_tcm_ramsize(mem_core); + if (ci->pub.chip == CY_CC_55560_CHIP_ID) + ci->pub.ramsize -= (CYW55560_TCAM_SIZE + + CYW55560_TRXHDR_SIZE); ci->pub.rambase = brcmf_chip_tcm_rambase(ci); if (!ci->pub.rambase) { brcmf_err("RAM base not provided with ARM CR4 core\n"); @@ -941,6 +994,144 @@ int brcmf_chip_dmp_erom_scan(struct brcmf_chip_priv *ci) return 0; } +static void brcmf_blhs_init(struct brcmf_chip *pub) +{ + struct brcmf_chip_priv *chip; + u32 addr; + + chip = container_of(pub, struct brcmf_chip_priv, pub); + addr = pub->blhs->h2d; + pub->blhs->write(chip->ctx, addr, 0); +} + +static int brcmf_blhs_is_bootloader_ready(struct brcmf_chip_priv *chip) +{ + u32 regdata; + u32 addr; + + addr = chip->pub.blhs->d2h; + SPINWAIT_MS((chip->pub.blhs->read(chip->ctx, addr) & + BRCMF_BLHS_D2H_READY) == 0, + BRCMF_BLHS_D2H_READY_TIMEOUT, BRCMF_BLHS_POLL_INTERVAL); + + regdata = chip->pub.blhs->read(chip->ctx, addr); + if (!(regdata & BRCMF_BLHS_D2H_READY)) { + brcmf_err("Timeout waiting for bootloader ready\n"); + return -EPERM; + } + + return 0; +} + +static int brcmf_blhs_prep_fw_download(struct brcmf_chip *pub) +{ + struct brcmf_chip_priv *chip; + u32 addr; + int err; + + /* Host indication for bootloader to start the init */ + brcmf_blhs_init(pub); + + chip = container_of(pub, struct brcmf_chip_priv, pub); + err = brcmf_blhs_is_bootloader_ready(chip); + if (err) + return err; + + /* Host notification about FW download start */ + addr = pub->blhs->h2d; + pub->blhs->write(chip->ctx, addr, BRCMF_BLHS_H2D_DL_FW_START); + + return 0; +} + +static int brcmf_blhs_post_fw_download(struct brcmf_chip *pub) +{ + struct brcmf_chip_priv *chip; + u32 addr; + u32 regdata; + + chip = container_of(pub, struct brcmf_chip_priv, pub); + addr = pub->blhs->h2d; + pub->blhs->write(chip->ctx, addr, BRCMF_BLHS_H2D_DL_FW_DONE); + addr = pub->blhs->d2h; + SPINWAIT_MS((pub->blhs->read(chip->ctx, addr) & + BRCMF_BLHS_D2H_TRXHDR_PARSE_DONE) == 0, + BRCMF_BLHS_D2H_TRXHDR_PARSE_DONE_TIMEOUT, + BRCMF_BLHS_POLL_INTERVAL); + + regdata = pub->blhs->read(chip->ctx, addr); + if (!(regdata & BRCMF_BLHS_D2H_TRXHDR_PARSE_DONE)) { + brcmf_err("TRX header parsing failed\n"); + + /* Host indication for bootloader to get reset on error */ + addr = pub->blhs->h2d; + regdata = pub->blhs->read(chip->ctx, addr); + regdata |= BRCMF_BLHS_H2D_BL_RESET_ON_ERROR; + pub->blhs->write(chip->ctx, addr, regdata); + + return -EPERM; + } + + return 0; +} + +static void brcmf_blhs_post_nvram_download(struct brcmf_chip *pub) +{ + struct brcmf_chip_priv *chip; + u32 addr; + u32 regdata; + + chip = container_of(pub, struct brcmf_chip_priv, pub); + addr = pub->blhs->h2d; + regdata = pub->blhs->read(chip->ctx, addr); + regdata |= BRCMF_BLHS_H2D_DL_NVRAM_DONE; + pub->blhs->write(chip->ctx, addr, regdata); +} + +static int brcmf_blhs_chk_validation(struct brcmf_chip *pub) +{ + struct brcmf_chip_priv *chip; + u32 addr; + u32 regdata; + + chip = container_of(pub, struct brcmf_chip_priv, pub); + addr = pub->blhs->d2h; + SPINWAIT_MS((pub->blhs->read(chip->ctx, addr) & + BRCMF_BLHS_D2H_VALDN_DONE) == 0, + BRCMF_BLHS_D2H_VALDN_DONE_TIMEOUT, + BRCMF_BLHS_POLL_INTERVAL); + + regdata = pub->blhs->read(chip->ctx, addr); + if (!(regdata & BRCMF_BLHS_D2H_VALDN_DONE) || + !(regdata & BRCMF_BLHS_D2H_VALDN_RESULT)) { + brcmf_err("TRX image validation check failed\n"); + + /* Host notification for bootloader to get reset on error */ + addr = pub->blhs->h2d; + regdata = pub->blhs->read(chip->ctx, addr); + regdata |= BRCMF_BLHS_H2D_BL_RESET_ON_ERROR; + pub->blhs->write(chip->ctx, addr, regdata); + + return -EPERM; + } + + return 0; +} + +static int brcmf_blhs_post_watchdog_reset(struct brcmf_chip *pub) +{ + struct brcmf_chip_priv *chip; + int err; + + /* Host indication for bootloader to start the init */ + brcmf_blhs_init(pub); + + chip = container_of(pub, struct brcmf_chip_priv, pub); + err = brcmf_blhs_is_bootloader_ready(chip); + + return err; +} + static int brcmf_chip_recognition(struct brcmf_chip_priv *ci) { struct brcmf_core *core; @@ -1091,6 +1282,7 @@ struct brcmf_chip *brcmf_chip_attach(void *ctx, const struct brcmf_buscore_ops *ops) { struct brcmf_chip_priv *chip; + struct brcmf_blhs *blhs; int err = 0; if (WARN_ON(!ops->read32)) @@ -1117,6 +1309,26 @@ struct brcmf_chip *brcmf_chip_attach(void *ctx, if (err < 0) goto fail; + blhs = NULL; + if (chip->ops->blhs_attach) { + err = chip->ops->blhs_attach(chip->ctx, &blhs, + BRCMF_BLHS_D2H_READY, + BRCMF_BLHS_D2H_READY_TIMEOUT, + BRCMF_BLHS_POLL_INTERVAL); + if (err < 0) + goto fail; + + if (blhs) { + blhs->init = brcmf_blhs_init; + blhs->prep_fwdl = brcmf_blhs_prep_fw_download; + blhs->post_fwdl = brcmf_blhs_post_fw_download; + blhs->post_nvramdl = brcmf_blhs_post_nvram_download; + blhs->chk_validation = brcmf_blhs_chk_validation; + blhs->post_wdreset = brcmf_blhs_post_watchdog_reset; + } + } + chip->pub.blhs = blhs; + err = brcmf_chip_recognition(chip); if (err < 0) goto fail; @@ -1143,6 +1355,7 @@ void brcmf_chip_detach(struct brcmf_chip *pub) list_del(&core->list); kfree(core); } + kfree(pub->blhs); kfree(chip); } @@ -1203,6 +1416,14 @@ struct brcmf_core *brcmf_chip_get_pmu(struct brcmf_chip *pub) return cc; } +struct brcmf_core *brcmf_chip_get_gci(struct brcmf_chip *pub) +{ + struct brcmf_core *gci; + + gci = brcmf_chip_get_core(pub, BCMA_CORE_GCI); + return gci; +} + bool brcmf_chip_iscoreup(struct brcmf_core *pub) { struct brcmf_core_priv *core; @@ -1244,7 +1465,8 @@ brcmf_chip_cm3_set_passive(struct brcmf_chip_priv *chip) brcmf_chip_resetcore(core, 0, 0, 0); /* disable bank #3 remap for this device */ - if (chip->pub.chip == BRCM_CC_43430_CHIP_ID) { + if (chip->pub.chip == BRCM_CC_43430_CHIP_ID || + chip->pub.chip == CY_CC_43439_CHIP_ID) { sr = container_of(core, struct brcmf_core_priv, pub); brcmf_chip_core_write32(sr, SOCRAMREGOFFS(bankidx), 3); brcmf_chip_core_write32(sr, SOCRAMREGOFFS(bankpda), 0); @@ -1274,7 +1496,8 @@ brcmf_chip_cr4_set_passive(struct brcmf_chip_priv *chip) { struct brcmf_core *core; - brcmf_chip_disable_arm(chip, BCMA_CORE_ARM_CR4); + if (!chip->pub.blhs) + brcmf_chip_disable_arm(chip, BCMA_CORE_ARM_CR4); core = brcmf_chip_get_core(&chip->pub, BCMA_CORE_80211); brcmf_chip_resetcore(core, D11_BCMA_IOCTL_PHYRESET | @@ -1390,7 +1613,9 @@ bool brcmf_chip_sr_capable(struct brcmf_chip *pub) case BRCM_CC_4345_CHIP_ID: /* explicitly check SR engine enable bit */ pmu_cc3_mask = BIT(2); +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0)) fallthrough; +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0)) */ case BRCM_CC_43241_CHIP_ID: case BRCM_CC_4335_CHIP_ID: case BRCM_CC_4339_CHIP_ID: @@ -1401,10 +1626,13 @@ bool brcmf_chip_sr_capable(struct brcmf_chip *pub) reg = chip->ops->read32(chip->ctx, addr); return (reg & pmu_cc3_mask) != 0; case BRCM_CC_43430_CHIP_ID: + case CY_CC_43439_CHIP_ID: addr = CORE_CC_REG(base, sr_control1); reg = chip->ops->read32(chip->ctx, addr); return reg != 0; case CY_CC_4373_CHIP_ID: + case CY_CC_55560_CHIP_ID: + case CY_CC_89459_CHIP_ID: /* explicitly check SR engine enable bit */ addr = CORE_CC_REG(base, sr_control0); reg = chip->ops->read32(chip->ctx, addr); @@ -1427,3 +1655,150 @@ bool brcmf_chip_sr_capable(struct brcmf_chip *pub) PMU_RCTL_LOGIC_DISABLE_MASK)) == 0; } } + +void brcmf_chip_reset_pmu_regs(struct brcmf_chip *pub) +{ + struct brcmf_chip_priv *chip; + u32 addr; + u32 base; + + brcmf_dbg(TRACE, "Enter\n"); + + chip = container_of(pub, struct brcmf_chip_priv, pub); + base = brcmf_chip_get_pmu(pub)->base; + + switch (pub->chip) { + case CY_CC_43012_CHIP_ID: + /* SW scratch */ + addr = CORE_CC_REG(base, swscratch); + chip->ops->write32(chip->ctx, addr, 0); + + /* PMU status */ + addr = CORE_CC_REG(base, pmustatus); + chip->ops->write32(chip->ctx, addr, + CY_43012_PMU_STATUS_MASK); + + /* PMU control ext */ + addr = CORE_CC_REG(base, pmucontrol_ext); + chip->ops->write32(chip->ctx, addr, + CY_43012_PMU_CONTROL_EXT_MASK); + break; + + default: + brcmf_err("Unsupported chip id\n"); + break; + } +} + +void brcmf_chip_set_default_min_res_mask(struct brcmf_chip *pub) +{ + struct brcmf_chip_priv *chip; + u32 addr; + u32 base; + + brcmf_dbg(TRACE, "Enter\n"); + + chip = container_of(pub, struct brcmf_chip_priv, pub); + base = brcmf_chip_get_pmu(pub)->base; + switch (pub->chip) { + case CY_CC_43012_CHIP_ID: + addr = CORE_CC_REG(base, min_res_mask); + chip->ops->write32(chip->ctx, addr, + CY_43012_PMU_MIN_RES_MASK); + break; + + default: + brcmf_err("Unsupported chip id\n"); + break; + } +} + +void brcmf_chip_ulp_reset_lhl_regs(struct brcmf_chip *pub) +{ + struct brcmf_chip_priv *chip; + u32 base; + u32 addr; + + brcmf_dbg(TRACE, "Enter\n"); + + chip = container_of(pub, struct brcmf_chip_priv, pub); + base = brcmf_chip_get_gci(pub)->base; + + /* LHL Top Level Power Sequence Control */ + addr = CORE_GCI_REG(base, lhl_top_pwrseq_ctl_adr); + chip->ops->write32(chip->ctx, addr, 0); + + /* GPIO Interrupt Enable0 */ + addr = CORE_GCI_REG(base, gpio_int_en_port_adr[0]); + chip->ops->write32(chip->ctx, addr, 0); + + /* GPIO Interrupt Status0 */ + addr = CORE_GCI_REG(base, gpio_int_st_port_adr[0]); + chip->ops->write32(chip->ctx, addr, ~0); + + /* WL ARM Timer0 Interrupt Mask */ + addr = CORE_GCI_REG(base, lhl_wl_armtim0_intrp_adr); + chip->ops->write32(chip->ctx, addr, 0); + + /* WL ARM Timer0 Interrupt Status */ + addr = CORE_GCI_REG(base, lhl_wl_armtim0_st_adr); + chip->ops->write32(chip->ctx, addr, ~0); + + /* WL ARM Timer */ + addr = CORE_GCI_REG(base, lhl_wl_armtim0_adr); + chip->ops->write32(chip->ctx, addr, 0); + + /* WL MAC Timer0 Interrupt Mask */ + addr = CORE_GCI_REG(base, lhl_wl_mactim0_intrp_adr); + chip->ops->write32(chip->ctx, addr, 0); + + /* WL MAC Timer0 Interrupt Status */ + addr = CORE_GCI_REG(base, lhl_wl_mactim0_st_adr); + chip->ops->write32(chip->ctx, addr, ~0); + + /* WL MAC TimerInt0 */ + addr = CORE_GCI_REG(base, lhl_wl_mactim_int0_adr); + chip->ops->write32(chip->ctx, addr, 0x0); +} + +void brcmf_chip_reset_watchdog(struct brcmf_chip *pub) +{ + struct brcmf_chip_priv *chip; + u32 base; + u32 addr; + + brcmf_dbg(TRACE, "Enter\n"); + + chip = container_of(pub, struct brcmf_chip_priv, pub); + base = brcmf_chip_get_pmu(pub)->base; + + switch (pub->chip) { + case CY_CC_43012_CHIP_ID: + addr = CORE_CC_REG(base, min_res_mask); + chip->ops->write32(chip->ctx, addr, + CY_43012_PMU_MIN_RES_MASK); + /* Watchdog res mask */ + addr = CORE_CC_REG(base, watchdog_res_mask); + chip->ops->write32(chip->ctx, addr, + CY_43012_PMU_MIN_RES_MASK); + /* PMU watchdog */ + addr = CORE_CC_REG(base, pmuwatchdog); + chip->ops->write32(chip->ctx, addr, + CY_43012_PMU_WATCHDOG_TICK_VAL); + break; + case CY_CC_4373_CHIP_ID: + addr = CORE_CC_REG(base, min_res_mask); + chip->ops->write32(chip->ctx, addr, + CY_4373_PMU_MIN_RES_MASK); + addr = CORE_CC_REG(base, watchdog_res_mask); + chip->ops->write32(chip->ctx, addr, + CY_4373_PMU_MIN_RES_MASK); + addr = CORE_CC_REG(base, pmuwatchdog); + chip->ops->write32(chip->ctx, addr, + CY_4373_PMU_WATCHDOG_TICK_VAL); + mdelay(100); + break; + default: + break; + } +} diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.h index 8fa38658e727a..c1a803323f8d0 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/chip.h @@ -8,7 +8,12 @@ #include #define CORE_CC_REG(base, field) \ - (base + offsetof(struct chipcregs, field)) + ((base) + offsetof(struct chipcregs, field)) + +#define CORE_GCI_REG(base, field) \ + ((base) + offsetof(struct chipgciregs, field)) + +struct brcmf_blhs; /** * struct brcmf_chip - chip level information. @@ -23,6 +28,7 @@ * @ramsize: amount of RAM on chip including retention. * @srsize: amount of retention RAM on chip. * @name: string representation of the chip identifier. + * @blhs: bootlooder handshake handle. */ struct brcmf_chip { u32 chip; @@ -35,6 +41,7 @@ struct brcmf_chip { u32 ramsize; u32 srsize; char name[12]; + struct brcmf_blhs *blhs; }; /** @@ -59,6 +66,7 @@ struct brcmf_core { * @setup: bus-specific core setup. * @active: chip becomes active. * The callback should use the provided @rstvec when non-zero. + * @blhs_attach: attach bootloader handshake handle */ struct brcmf_buscore_ops { u32 (*read32)(void *ctx, u32 addr); @@ -67,6 +75,35 @@ struct brcmf_buscore_ops { int (*reset)(void *ctx, struct brcmf_chip *chip); int (*setup)(void *ctx, struct brcmf_chip *chip); void (*activate)(void *ctx, struct brcmf_chip *chip, u32 rstvec); + int (*blhs_attach)(void *ctx, struct brcmf_blhs **blhs, u32 flag, + uint timeout, uint interval); +}; + +/** + * struct brcmf_blhs - bootloader handshake handle related information. + * + * @d2h: offset of dongle to host register for the handshake. + * @h2d: offset of host to dongle register for the handshake. + * @init: bootloader handshake initialization. + * @prep_fwdl: handshake before firmware download. + * @post_fwdl: handshake after firmware download. + * @post_nvramdl: handshake after nvram download. + * @chk_validation: handshake for firmware validation check. + * @post_wdreset: handshake after watchdog reset. + * @read: read value with register offset for the handshake. + * @write: write value with register offset for the handshake. + */ +struct brcmf_blhs { + u32 d2h; + u32 h2d; + void (*init)(struct brcmf_chip *pub); + int (*prep_fwdl)(struct brcmf_chip *pub); + int (*post_fwdl)(struct brcmf_chip *pub); + void (*post_nvramdl)(struct brcmf_chip *pub); + int (*chk_validation)(struct brcmf_chip *pub); + int (*post_wdreset)(struct brcmf_chip *pub); + u32 (*read)(void *ctx, u32 addr); + void (*write)(void *ctx, u32 addr, u32 value); }; int brcmf_chip_get_raminfo(struct brcmf_chip *pub); @@ -85,5 +122,9 @@ void brcmf_chip_set_passive(struct brcmf_chip *ci); bool brcmf_chip_set_active(struct brcmf_chip *ci, u32 rstvec); bool brcmf_chip_sr_capable(struct brcmf_chip *pub); char *brcmf_chip_name(u32 chipid, u32 chiprev, char *buf, uint len); +void brcmf_chip_reset_watchdog(struct brcmf_chip *pub); +void brcmf_chip_ulp_reset_lhl_regs(struct brcmf_chip *pub); +void brcmf_chip_reset_pmu_regs(struct brcmf_chip *pub); +void brcmf_chip_set_default_min_res_mask(struct brcmf_chip *pub); #endif /* BRCMF_AXIDMP_H */ diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c index e3758bd86acf0..cc7de5e926254 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c @@ -20,6 +20,12 @@ #include "of.h" #include "firmware.h" #include "chip.h" +#include "defs.h" +#include "fweh.h" +#include +#include +#include +#include "pcie.h" MODULE_AUTHOR("Broadcom Corporation"); MODULE_DESCRIPTION("Broadcom 802.11 wireless LAN fullmac driver."); @@ -67,6 +73,14 @@ static int brcmf_iapp_enable; module_param_named(iapp, brcmf_iapp_enable, int, 0); MODULE_PARM_DESC(iapp, "Enable partial support for the obsoleted Inter-Access Point Protocol"); +static int brcmf_eap_restrict; +module_param_named(eap_restrict, brcmf_eap_restrict, int, 0400); +MODULE_PARM_DESC(eap_restrict, "Block non-802.1X frames until auth finished"); + +static int brcmf_max_pm; +module_param_named(max_pm, brcmf_max_pm, int, 0); +MODULE_PARM_DESC(max_pm, "Use max power management mode by default"); + #ifdef DEBUG /* always succeed brcmf_bus_started() */ static int brcmf_ignore_probe_fail; @@ -74,9 +88,19 @@ module_param_named(ignore_probe_fail, brcmf_ignore_probe_fail, int, 0); MODULE_PARM_DESC(ignore_probe_fail, "always succeed probe for debugging"); #endif +static int brcmf_fw_ap_select; +module_param_named(fw_ap_select, brcmf_fw_ap_select, int, 0400); +MODULE_PARM_DESC(fw_ap_select, "Allow FW for AP selection"); + static struct brcmfmac_platform_data *brcmfmac_pdata; struct brcmf_mp_global_t brcmf_mp_global; +static int brcmf_reboot_callback(struct notifier_block *this, unsigned long code, void *unused); +static struct notifier_block brcmf_reboot_notifier = { + .notifier_call = brcmf_reboot_callback, + .priority = 1, +}; + void brcmf_c_set_joinpref_default(struct brcmf_if *ifp) { struct brcmf_pub *drvr = ifp->drvr; @@ -139,8 +163,11 @@ static int brcmf_c_process_clm_blob(struct brcmf_if *ifp) bphy_err(drvr, "get CLM blob file name failed (%d)\n", err); return err; } - +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 18, 0)) err = firmware_request_nowarn(&clm, clm_name, bus->dev); +#else /* (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 18, 0)) */ + err = request_firmware(&clm, clm_name, bus->dev); +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 18, 0)) */ if (err) { brcmf_info("no clm_blob available (err=%d), device may have limited channels available\n", err); @@ -201,8 +228,10 @@ int brcmf_c_preinit_dcmds(struct brcmf_if *ifp) char *clmver; char *ptr; s32 err; + struct eventmsgs_ext *eventmask_msg = NULL; + u8 msglen; - /* retreive mac address */ + /* retrieve mac addresses */ err = brcmf_fil_iovar_data_get(ifp, "cur_etheraddr", ifp->mac_addr, sizeof(ifp->mac_addr)); if (err < 0) { @@ -292,6 +321,11 @@ int brcmf_c_preinit_dcmds(struct brcmf_if *ifp) brcmf_dbg(INFO, "CLM version = %s\n", clmver); } + /* set apsta */ + err = brcmf_fil_iovar_int_set(ifp, "apsta", 1); + if (err) + brcmf_info("failed setting apsta, %d\n", err); + /* set mpc */ err = brcmf_fil_iovar_int_set(ifp, "mpc", 1); if (err) { @@ -316,6 +350,43 @@ int brcmf_c_preinit_dcmds(struct brcmf_if *ifp) goto done; } + /* Enable event_msg_ext specific to 43012 chip */ + if (bus->chip == CY_CC_43012_CHIP_ID) { + /* Program event_msg_ext to support event larger than 128 */ + msglen = (roundup(BRCMF_E_LAST, NBBY) / NBBY) + + EVENTMSGS_EXT_STRUCT_SIZE; + /* Allocate buffer for eventmask_msg */ + eventmask_msg = kzalloc(msglen, GFP_KERNEL); + if (!eventmask_msg) { + err = -ENOMEM; + goto done; + } + + /* Read the current programmed event_msgs_ext */ + eventmask_msg->ver = EVENTMSGS_VER; + eventmask_msg->len = roundup(BRCMF_E_LAST, NBBY) / NBBY; + err = brcmf_fil_iovar_data_get(ifp, "event_msgs_ext", + eventmask_msg, + msglen); + + /* Enable ULP event */ + brcmf_dbg(EVENT, "enable event ULP\n"); + setbit(eventmask_msg->mask, BRCMF_E_ULP); + + /* Write updated Event mask */ + eventmask_msg->ver = EVENTMSGS_VER; + eventmask_msg->command = EVENTMSGS_SET_MASK; + eventmask_msg->len = (roundup(BRCMF_E_LAST, NBBY) / NBBY); + + err = brcmf_fil_iovar_data_set(ifp, "event_msgs_ext", + eventmask_msg, msglen); + if (err) { + brcmf_err("Set event_msgs_ext error (%d)\n", err); + kfree(eventmask_msg); + goto done; + } + kfree(eventmask_msg); + } /* Setup default scan channel time */ err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_SCAN_CHANNEL_TIME, BRCMF_DEFAULT_SCAN_CHANNEL_TIME); @@ -336,6 +407,20 @@ int brcmf_c_preinit_dcmds(struct brcmf_if *ifp) /* Enable tx beamforming, errors can be ignored (not supported) */ (void)brcmf_fil_iovar_int_set(ifp, "txbf", 1); + err = brcmf_fil_iovar_int_set(ifp, "chanspec", 0x1001); + if (err < 0) + bphy_err(drvr, "Initial Channel failed %d\n", err); + /* add unicast packet filter */ + err = brcmf_pktfilter_add_remove(ifp->ndev, + BRCMF_UNICAST_FILTER_NUM, true); + if (err == -BRCMF_FW_UNSUPPORTED) { + /* FW not support can be ignored */ + err = 0; + goto done; + } else if (err) { + bphy_err(drvr, "Add unicast filter error (%d)\n", err); + } + done: return err; } @@ -370,7 +455,7 @@ void __brcmf_dbg(u32 level, const char *func, const char *fmt, ...) va_start(args, fmt); vaf.va = &args; if (brcmf_msg_level & level) - pr_debug("%s %pV", func, &vaf); + pr_err("%s %pV", func, &vaf); trace_brcmf_dbg(level, func, &vaf); va_end(args); } @@ -407,15 +492,18 @@ struct brcmf_mp_device *brcmf_get_module_param(struct device *dev, if (!settings) return NULL; - /* start by using the module paramaters */ + /* start by using the module parameters */ settings->p2p_enable = !!brcmf_p2p_enable; settings->feature_disable = brcmf_feature_disable; settings->fcmode = brcmf_fcmode; settings->roamoff = !!brcmf_roamoff; settings->iapp = !!brcmf_iapp_enable; + settings->eap_restrict = !!brcmf_eap_restrict; + settings->default_pm = !!brcmf_max_pm ? PM_MAX : PM_FAST; #ifdef DEBUG settings->ignore_probe_fail = !!brcmf_ignore_probe_fail; #endif + settings->fw_ap_select = !!brcmf_fw_ap_select; if (bus_type == BRCMF_BUSTYPE_SDIO) settings->bus.sdio.txglomsz = brcmf_sdiod_txglomsz; @@ -454,13 +542,22 @@ void brcmf_release_module_param(struct brcmf_mp_device *module_param) kfree(module_param); } +static int +brcmf_reboot_callback(struct notifier_block *this, unsigned long code, void *unused) +{ + brcmf_dbg(INFO, "code = %ld\n", code); + if (code == SYS_RESTART) + brcmf_core_exit(); + return NOTIFY_DONE; +} + static int __init brcmf_common_pd_probe(struct platform_device *pdev) { brcmf_dbg(INFO, "Enter\n"); brcmfmac_pdata = dev_get_platdata(&pdev->dev); - if (brcmfmac_pdata->power_on) + if (brcmfmac_pdata && brcmfmac_pdata->power_on) brcmfmac_pdata->power_on(); return 0; @@ -492,7 +589,7 @@ static int __init brcmfmac_module_init(void) if (err == -ENODEV) brcmf_dbg(INFO, "No platform data available.\n"); - /* Initialize global module paramaters */ + /* Initialize global module parameters */ brcmf_mp_attach(); /* Continue the initialization by registering the different busses */ @@ -500,6 +597,8 @@ static int __init brcmfmac_module_init(void) if (err) { if (brcmfmac_pdata) platform_driver_unregister(&brcmf_pd); + } else { + register_reboot_notifier(&brcmf_reboot_notifier); } return err; @@ -508,6 +607,7 @@ static int __init brcmfmac_module_init(void) static void __exit brcmfmac_module_exit(void) { brcmf_core_exit(); + unregister_reboot_notifier(&brcmf_reboot_notifier); if (brcmfmac_pdata) platform_driver_unregister(&brcmf_pd); } diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.h index 8b5f49997c8b5..48604c25a284e 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.h @@ -7,6 +7,7 @@ #include #include + #include "fwil_types.h" #define BRCMF_FW_ALTPATH_LEN 256 @@ -37,7 +38,10 @@ extern struct brcmf_mp_global_t brcmf_mp_global; * @feature_disable: Feature_disable bitmask. * @fcmode: FWS flow control. * @roamoff: Firmware roaming off? + * @eap_restrict: Not allow data tx/rx until 802.1X auth succeeds + * @default_pm: default power management (PM) mode. * @ignore_probe_fail: Ignore probe failure. + * @fw_ap_select: Allow FW to select AP. * @country_codes: If available, pointer to struct for translating country codes * @bus: Bus specific platform data. Only SDIO at the mmoment. */ @@ -47,7 +51,10 @@ struct brcmf_mp_device { int fcmode; bool roamoff; bool iapp; + bool eap_restrict; + int default_pm; bool ignore_probe_fail; + bool fw_ap_select; struct brcmfmac_pd_cc *country_codes; const char *board_type; union { diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c index c8e1d505f7b5d..c9c842ccb1b3e 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c @@ -5,6 +5,7 @@ #include #include +#include #include #include #include @@ -62,6 +63,14 @@ struct wlc_d11rxhdr { s8 rxpwr[4]; } __packed; +#define BRCMF_IF_STA_LIST_LOCK_INIT(ifp) spin_lock_init(&(ifp)->sta_list_lock) +#define BRCMF_IF_STA_LIST_LOCK(ifp, flags) \ + spin_lock_irqsave(&(ifp)->sta_list_lock, (flags)) +#define BRCMF_IF_STA_LIST_UNLOCK(ifp, flags) \ + spin_unlock_irqrestore(&(ifp)->sta_list_lock, (flags)) + +#define BRCMF_STA_NULL ((struct brcmf_sta *)NULL) + char *brcmf_ifname(struct brcmf_if *ifp) { if (!ifp) @@ -96,6 +105,11 @@ void brcmf_configure_arp_nd_offload(struct brcmf_if *ifp, bool enable) s32 err; u32 mode; + if (enable && brcmf_is_apmode_operating(ifp->drvr->wiphy)) { + brcmf_dbg(TRACE, "Skip ARP/ND offload enable when soft AP is running\n"); + return; + } + if (enable) mode = BRCMF_ARP_OL_AGENT | BRCMF_ARP_OL_PEER_AUTO_REPLY; else @@ -290,7 +304,6 @@ static netdev_tx_t brcmf_netdev_start_xmit(struct sk_buff *skb, struct brcmf_pub *drvr = ifp->drvr; struct ethhdr *eh; int head_delta; - unsigned int tx_bytes = skb->len; brcmf_dbg(DATA, "Enter, bsscfgidx=%d\n", ifp->bsscfgidx); @@ -353,8 +366,10 @@ static netdev_tx_t brcmf_netdev_start_xmit(struct sk_buff *skb, if ((skb->priority == 0) || (skb->priority > 7)) skb->priority = cfg80211_classify8021d(skb, NULL); +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 17, 0)) /* set pacing shift for packet aggregation */ sk_pacing_shift_update(skb->sk, 8); +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 17, 0)) */ ret = brcmf_proto_tx_queue_data(drvr, ifp->ifidx, skb); if (ret < 0) @@ -365,7 +380,7 @@ static netdev_tx_t brcmf_netdev_start_xmit(struct sk_buff *skb, ndev->stats.tx_dropped++; } else { ndev->stats.tx_packets++; - ndev->stats.tx_bytes += tx_bytes; + ndev->stats.tx_bytes += skb->len; } /* Return ok: we always eat the packet */ @@ -453,7 +468,7 @@ void brcmf_netif_mon_rx(struct brcmf_if *ifp, struct sk_buff *skb) skb_pull(skb, offset); /* TODO: use RX header to fill some radiotap data */ - radiotap = skb_push(skb, sizeof(*radiotap)); + radiotap = (struct ieee80211_radiotap_header*)skb_push(skb, sizeof(*radiotap)); memset(radiotap, 0, sizeof(*radiotap)); radiotap->it_len = cpu_to_le16(sizeof(*radiotap)); @@ -463,7 +478,7 @@ void brcmf_netif_mon_rx(struct brcmf_if *ifp, struct sk_buff *skb) struct ieee80211_radiotap_header *radiotap; /* TODO: use RX status to fill some radiotap data */ - radiotap = skb_push(skb, sizeof(*radiotap)); + radiotap = (struct ieee80211_radiotap_header*)skb_push(skb, sizeof(*radiotap)); memset(radiotap, 0, sizeof(*radiotap)); radiotap->it_len = cpu_to_le16(sizeof(*radiotap)); @@ -577,6 +592,9 @@ static void brcmf_ethtool_get_drvinfo(struct net_device *ndev, static const struct ethtool_ops brcmf_ethtool_ops = { .get_drvinfo = brcmf_ethtool_get_drvinfo, +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 5, 0)) + .get_ts_info = ethtool_op_get_ts_info, +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 5, 0)) */ }; static int brcmf_netdev_stop(struct net_device *ndev) @@ -634,6 +652,21 @@ static const struct net_device_ops brcmf_netdev_ops_pri = { .ndo_set_rx_mode = brcmf_netdev_set_multicast_list }; +#undef netdev_set_priv_destructor +#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 11, 9)) +#define netdev_set_priv_destructor(_dev, _destructor) \ + (_dev)->destructor = (_destructor) +#else +#define netdev_set_priv_destructor(_dev, _destructor) \ + (_dev)->priv_destructor = (_destructor) +#endif + +static void __brcmf_cfg80211_free_netdev(struct net_device *ndev) +{ + brcmf_cfg80211_free_netdev(ndev); + free_netdev(ndev); +} + int brcmf_net_attach(struct brcmf_if *ifp, bool rtnl_locked) { struct brcmf_pub *drvr = ifp->drvr; @@ -668,7 +701,7 @@ int brcmf_net_attach(struct brcmf_if *ifp, bool rtnl_locked) netif_carrier_off(ndev); - ndev->priv_destructor = brcmf_cfg80211_free_netdev; + netdev_set_priv_destructor(ndev, brcmf_cfg80211_free_netdev); brcmf_dbg(INFO, "%s: Broadcom Dongle Host Driver\n", ndev->name); return 0; @@ -884,7 +917,7 @@ struct brcmf_if *brcmf_add_if(struct brcmf_pub *drvr, s32 bsscfgidx, s32 ifidx, if (!ndev) return ERR_PTR(-ENOMEM); - ndev->needs_free_netdev = true; + netdev_set_priv_destructor(ndev, __brcmf_cfg80211_free_netdev); ifp = netdev_priv(ndev); ifp->ndev = ndev; /* store mapping ifidx to bsscfgidx */ @@ -899,7 +932,9 @@ struct brcmf_if *brcmf_add_if(struct brcmf_pub *drvr, s32 bsscfgidx, s32 ifidx, init_waitqueue_head(&ifp->pend_8021x_wait); spin_lock_init(&ifp->netif_stop_lock); - + BRCMF_IF_STA_LIST_LOCK_INIT(ifp); + /* Initialize STA info list */ + INIT_LIST_HEAD(&ifp->sta_list); if (mac_addr != NULL) memcpy(ifp->mac_addr, mac_addr, ETH_ALEN); @@ -1131,6 +1166,15 @@ static int brcmf_inet6addr_changed(struct notifier_block *nb, } #endif + +int brcmf_fwlog_attach(struct device *dev) +{ + struct brcmf_bus *bus_if = dev_get_drvdata(dev); + struct brcmf_pub *drvr = bus_if->drvr; + + return brcmf_debug_fwlog_init(drvr); +} + static int brcmf_revinfo_read(struct seq_file *s, void *data) { struct brcmf_bus *bus_if = dev_get_drvdata(s->private); @@ -1320,7 +1364,7 @@ int brcmf_alloc(struct device *dev, struct brcmf_mp_device *settings) return 0; } -int brcmf_attach(struct device *dev) +int brcmf_attach(struct device *dev, bool start_bus) { struct brcmf_bus *bus_if = dev_get_drvdata(dev); struct brcmf_pub *drvr = bus_if->drvr; @@ -1337,6 +1381,7 @@ int brcmf_attach(struct device *dev) /* Link to bus module */ drvr->hdrlen = 0; + drvr->req_mpc = 1; /* Attach and link in the protocol */ ret = brcmf_proto_attach(drvr); if (ret != 0) { @@ -1351,10 +1396,13 @@ int brcmf_attach(struct device *dev) /* attach firmware event handler */ brcmf_fweh_attach(drvr); - ret = brcmf_bus_started(drvr, drvr->ops); - if (ret != 0) { - bphy_err(drvr, "dongle is not responding: err=%d\n", ret); - goto fail; + if (start_bus) { + ret = brcmf_bus_started(drvr, drvr->ops); + if (ret != 0) { + bphy_err(drvr, "dongle is not responding: err=%d\n", + ret); + goto fail; + } } return 0; @@ -1404,14 +1452,28 @@ void brcmf_fw_crashed(struct device *dev) brcmf_dev_coredump(dev); - schedule_work(&drvr->bus_reset); + if (drvr->bus_reset.func) + schedule_work(&drvr->bus_reset); +} + +#ifdef CONFIG_BRCMF_CMD_TIMEOUT_REBOOT +void brcmf_bus_handle_cmd_timeout(struct brcmf_bus *bus) +{ + struct brcmf_pub *drvr = bus->drvr; + + bphy_err(drvr, "try to reset bus\n"); + + if (drvr->bus_reset.func) + schedule_work(&drvr->bus_reset); } +#endif /* CONFIG_BRCMF_CMD_TIMEOUT_REBOOT */ void brcmf_detach(struct device *dev) { s32 i; struct brcmf_bus *bus_if = dev_get_drvdata(dev); struct brcmf_pub *drvr = bus_if->drvr; + struct net_device *ndev; brcmf_dbg(TRACE, "Enter\n"); @@ -1427,6 +1489,23 @@ void brcmf_detach(struct device *dev) #endif brcmf_bus_change_state(bus_if, BRCMF_BUS_DOWN); + + ndev = drvr->iflist[0]->ndev; + + if (ndev) { + rtnl_lock(); + if (ndev->flags & IFF_UP) { + /* If IFF_UP is still up, it indicates that + * "ifconfig wlan0 down" hasn't been called. + * So invoke dev_close explicitly here to + * bring down the interface. + */ + brcmf_dbg(TRACE, "net device is up, bring it down first\n"); + dev_close(ndev); + } + rtnl_unlock(); + } + /* make sure primary interface removed last */ for (i = BRCMF_MAX_IFS - 1; i > -1; i--) { if (drvr->iflist[i]) @@ -1486,8 +1565,10 @@ int brcmf_netdev_wait_pend8021x(struct brcmf_if *ifp) !brcmf_get_pend_8021x_cnt(ifp), MAX_WAIT_FOR_8021X_TX); - if (!err) + if (!err) { bphy_err(drvr, "Timed out waiting for no pending 802.1x packets\n"); + atomic_set(&ifp->pend_8021x_cnt, 0); + } return !err; } @@ -1519,34 +1600,307 @@ void brcmf_bus_change_state(struct brcmf_bus *bus, enum brcmf_bus_state state) } } -int __init brcmf_core_init(void) +static void brcmf_driver_register(struct work_struct *work) { - int err; - - err = brcmf_sdio_register(); - if (err) - return err; +#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); - err = brcmf_usb_register(); - if (err) - goto error_usb_register; +int __init brcmf_core_init(void) +{ + if (!schedule_work(&brcmf_driver_work)) + return -EBUSY; - err = brcmf_pcie_register(); - if (err) - goto error_pcie_register; return 0; - -error_pcie_register: - brcmf_usb_exit(); -error_usb_register: - brcmf_sdio_exit(); - return err; } -void __exit brcmf_core_exit(void) +void brcmf_core_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 } +int +brcmf_pktfilter_add_remove(struct net_device *ndev, int filter_num, bool add) +{ + struct brcmf_if *ifp = netdev_priv(ndev); + struct brcmf_pub *drvr = ifp->drvr; + struct brcmf_pkt_filter_le *pkt_filter; + int filter_fixed_len = offsetof(struct brcmf_pkt_filter_le, u); + int pattern_fixed_len = offsetof(struct brcmf_pkt_filter_pattern_le, + mask_and_pattern); + u16 mask_and_pattern[MAX_PKTFILTER_PATTERN_SIZE]; + int buflen = 0; + int ret = 0; + + brcmf_dbg(INFO, "%s packet filter number %d\n", + (add ? "add" : "remove"), filter_num); + + pkt_filter = kzalloc(sizeof(*pkt_filter) + + (MAX_PKTFILTER_PATTERN_SIZE * 2), GFP_ATOMIC); + if (!pkt_filter) + return -ENOMEM; + + switch (filter_num) { + case BRCMF_UNICAST_FILTER_NUM: + pkt_filter->id = 100; + pkt_filter->type = 0; + pkt_filter->negate_match = 0; + pkt_filter->u.pattern.offset = 0; + pkt_filter->u.pattern.size_bytes = 1; + mask_and_pattern[0] = 0x0001; + break; + case BRCMF_BROADCAST_FILTER_NUM: + //filter_pattern = "101 0 0 0 0xFFFFFFFFFFFF 0xFFFFFFFFFFFF"; + pkt_filter->id = 101; + pkt_filter->type = 0; + pkt_filter->negate_match = 0; + pkt_filter->u.pattern.offset = 0; + pkt_filter->u.pattern.size_bytes = 6; + mask_and_pattern[0] = 0xFFFF; + mask_and_pattern[1] = 0xFFFF; + mask_and_pattern[2] = 0xFFFF; + mask_and_pattern[3] = 0xFFFF; + mask_and_pattern[4] = 0xFFFF; + mask_and_pattern[5] = 0xFFFF; + break; + case BRCMF_MULTICAST4_FILTER_NUM: + //filter_pattern = "102 0 0 0 0xFFFFFF 0x01005E"; + pkt_filter->id = 102; + pkt_filter->type = 0; + pkt_filter->negate_match = 0; + pkt_filter->u.pattern.offset = 0; + pkt_filter->u.pattern.size_bytes = 3; + mask_and_pattern[0] = 0xFFFF; + mask_and_pattern[1] = 0x01FF; + mask_and_pattern[2] = 0x5E00; + break; + case BRCMF_MULTICAST6_FILTER_NUM: + //filter_pattern = "103 0 0 0 0xFFFF 0x3333"; + pkt_filter->id = 103; + pkt_filter->type = 0; + pkt_filter->negate_match = 0; + pkt_filter->u.pattern.offset = 0; + pkt_filter->u.pattern.size_bytes = 2; + mask_and_pattern[0] = 0xFFFF; + mask_and_pattern[1] = 0x3333; + break; + case BRCMF_MDNS_FILTER_NUM: + //filter_pattern = "104 0 0 0 0xFFFFFFFFFFFF 0x01005E0000FB"; + pkt_filter->id = 104; + pkt_filter->type = 0; + pkt_filter->negate_match = 0; + pkt_filter->u.pattern.offset = 0; + pkt_filter->u.pattern.size_bytes = 6; + mask_and_pattern[0] = 0xFFFF; + mask_and_pattern[1] = 0xFFFF; + mask_and_pattern[2] = 0xFFFF; + mask_and_pattern[3] = 0x0001; + mask_and_pattern[4] = 0x005E; + mask_and_pattern[5] = 0xFB00; + break; + case BRCMF_ARP_FILTER_NUM: + //filter_pattern = "105 0 0 12 0xFFFF 0x0806"; + pkt_filter->id = 105; + pkt_filter->type = 0; + pkt_filter->negate_match = 0; + pkt_filter->u.pattern.offset = 12; + pkt_filter->u.pattern.size_bytes = 2; + mask_and_pattern[0] = 0xFFFF; + mask_and_pattern[1] = 0x0608; + break; + case BRCMF_BROADCAST_ARP_FILTER_NUM: + //filter_pattern = "106 0 0 0 + //0xFFFFFFFFFFFF0000000000000806 + //0xFFFFFFFFFFFF0000000000000806"; + pkt_filter->id = 106; + pkt_filter->type = 0; + pkt_filter->negate_match = 0; + pkt_filter->u.pattern.offset = 0; + pkt_filter->u.pattern.size_bytes = 14; + mask_and_pattern[0] = 0xFFFF; + mask_and_pattern[1] = 0xFFFF; + mask_and_pattern[2] = 0xFFFF; + mask_and_pattern[3] = 0x0000; + mask_and_pattern[4] = 0x0000; + mask_and_pattern[5] = 0x0000; + mask_and_pattern[6] = 0x0608; + mask_and_pattern[7] = 0xFFFF; + mask_and_pattern[8] = 0xFFFF; + mask_and_pattern[9] = 0xFFFF; + mask_and_pattern[10] = 0x0000; + mask_and_pattern[11] = 0x0000; + mask_and_pattern[12] = 0x0000; + mask_and_pattern[13] = 0x0608; + break; + default: + ret = -EINVAL; + goto failed; + } + memcpy(pkt_filter->u.pattern.mask_and_pattern, mask_and_pattern, + pkt_filter->u.pattern.size_bytes * 2); + buflen = filter_fixed_len + pattern_fixed_len + + pkt_filter->u.pattern.size_bytes * 2; + + if (add) { + /* Add filter */ + ifp->fwil_fwerr = true; + ret = brcmf_fil_iovar_data_set(ifp, "pkt_filter_add", + pkt_filter, buflen); + ifp->fwil_fwerr = false; + if (ret) + goto failed; + drvr->pkt_filter[filter_num].id = pkt_filter->id; + drvr->pkt_filter[filter_num].enable = 0; + + } else { + /* Delete filter */ + ifp->fwil_fwerr = true; + ret = brcmf_fil_iovar_int_set(ifp, "pkt_filter_delete", + pkt_filter->id); + ifp->fwil_fwerr = false; + if (ret == -BRCMF_FW_BADARG) + ret = 0; + if (ret) + goto failed; + + drvr->pkt_filter[filter_num].id = 0; + drvr->pkt_filter[filter_num].enable = 0; + } +failed: + if (ret) + brcmf_err("%s packet filter failed, ret=%d\n", + (add ? "add" : "remove"), ret); + + kfree(pkt_filter); + return ret; +} + +int brcmf_pktfilter_enable(struct net_device *ndev, bool enable) +{ + struct brcmf_if *ifp = netdev_priv(ndev); + struct brcmf_pub *drvr = ifp->drvr; + int ret = 0; + int idx = 0; + + for (idx = 0; idx < MAX_PKT_FILTER_COUNT; ++idx) { + if (drvr->pkt_filter[idx].id != 0) { + drvr->pkt_filter[idx].enable = enable; + ret = brcmf_fil_iovar_data_set(ifp, "pkt_filter_enable", + &drvr->pkt_filter[idx], + sizeof(struct brcmf_pkt_filter_enable_le)); + if (ret) { + brcmf_err("%s packet filter id(%d) failed, ret=%d\n", + (enable ? "enable" : "disable"), + drvr->pkt_filter[idx].id, ret); + } + } + } + return ret; +} + +/** Find STA with MAC address ea in an interface's STA list. */ +struct brcmf_sta * +brcmf_find_sta(struct brcmf_if *ifp, const u8 *ea) +{ + struct brcmf_sta *sta; + unsigned long flags; + + BRCMF_IF_STA_LIST_LOCK(ifp, flags); + list_for_each_entry(sta, &ifp->sta_list, list) { + if (!memcmp(sta->ea.octet, ea, ETH_ALEN)) { + brcmf_dbg(INFO, "Found STA: 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x into sta list\n", + sta->ea.octet[0], sta->ea.octet[1], + sta->ea.octet[2], sta->ea.octet[3], + sta->ea.octet[4], sta->ea.octet[5]); + BRCMF_IF_STA_LIST_UNLOCK(ifp, flags); + return sta; + } + } + BRCMF_IF_STA_LIST_UNLOCK(ifp, flags); + + return BRCMF_STA_NULL; +} + +/** Add STA into the interface's STA list. */ +struct brcmf_sta * +brcmf_add_sta(struct brcmf_if *ifp, const u8 *ea) +{ + struct brcmf_sta *sta; + unsigned long flags; + + sta = kzalloc(sizeof(*sta), GFP_KERNEL); + if (sta == BRCMF_STA_NULL) { + brcmf_err("Alloc failed\n"); + return BRCMF_STA_NULL; + } + memcpy(sta->ea.octet, ea, ETH_ALEN); + brcmf_dbg(INFO, "Add STA: 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x into sta list\n", + sta->ea.octet[0], sta->ea.octet[1], + sta->ea.octet[2], sta->ea.octet[3], + sta->ea.octet[4], sta->ea.octet[5]); + + /* link the sta and the dhd interface */ + sta->ifp = ifp; + INIT_LIST_HEAD(&sta->list); + + BRCMF_IF_STA_LIST_LOCK(ifp, flags); + + list_add_tail(&sta->list, &ifp->sta_list); + + BRCMF_IF_STA_LIST_UNLOCK(ifp, flags); + return sta; +} + +/** Delete STA from the interface's STA list. */ +void +brcmf_del_sta(struct brcmf_if *ifp, const u8 *ea) +{ + struct brcmf_sta *sta, *next; + unsigned long flags; + + BRCMF_IF_STA_LIST_LOCK(ifp, flags); + list_for_each_entry_safe(sta, next, &ifp->sta_list, list) { + if (!memcmp(sta->ea.octet, ea, ETH_ALEN)) { + brcmf_dbg(INFO, "del STA: 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x from sta list\n", + ea[0], ea[1], ea[2], ea[3], + ea[4], ea[5]); + list_del(&sta->list); + kfree(sta); + } + } + + BRCMF_IF_STA_LIST_UNLOCK(ifp, flags); +} + +/** Add STA if it doesn't exist. Not reentrant. */ +struct brcmf_sta* +brcmf_findadd_sta(struct brcmf_if *ifp, const u8 *ea) +{ + struct brcmf_sta *sta = NULL; + + sta = brcmf_find_sta(ifp, ea); + + if (!sta) { + /* Add entry */ + sta = brcmf_add_sta(ifp, ea); + } + return sta; +} diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.h index 5767d665cee50..a102905b72998 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.h @@ -10,8 +10,10 @@ #ifndef BRCMFMAC_CORE_H #define BRCMFMAC_CORE_H +#include #include #include "fweh.h" +#include "fwil_types.h" #define TOE_TX_CSUM_OL 0x00000001 #define TOE_RX_CSUM_OL 0x00000002 @@ -123,6 +125,7 @@ struct brcmf_pub { u32 feat_flags; u32 chip_quirks; + int req_mpc; struct brcmf_rev_info revinfo; #ifdef DEBUG @@ -136,6 +139,8 @@ struct brcmf_pub { struct work_struct bus_reset; u8 clmver[BRCMF_DCMD_SMLEN]; + struct brcmf_pkt_filter_enable_le pkt_filter[MAX_PKT_FILTER_COUNT]; + u8 sta_mac_idx; }; /* forward declarations */ @@ -185,6 +190,7 @@ struct brcmf_if { struct brcmf_fws_mac_descriptor *fws_desc; int ifidx; s32 bsscfgidx; + bool isap; u8 mac_addr[ETH_ALEN]; u8 netif_stop; spinlock_t netif_stop_lock; @@ -193,6 +199,20 @@ struct brcmf_if { struct in6_addr ipv6_addr_tbl[NDOL_MAX_ENTRIES]; u8 ipv6addr_idx; bool fwil_fwerr; + struct list_head sta_list; /* sll of associated stations */ + spinlock_t sta_list_lock; + bool fmac_pkt_fwd_en; +}; + +struct ether_addr { + u8 octet[ETH_ALEN]; +}; + +/** Per STA params. A list of dhd_sta objects are managed in dhd_if */ +struct brcmf_sta { + void *ifp; /* associated brcm_if */ + struct ether_addr ea; /* stations ethernet mac address */ + struct list_head list; /* link into brcmf_if::sta_list */ }; int brcmf_netdev_wait_pend8021x(struct brcmf_if *ifp); @@ -214,6 +234,11 @@ void brcmf_net_detach(struct net_device *ndev, bool rtnl_locked); int brcmf_net_mon_attach(struct brcmf_if *ifp); void brcmf_net_setcarrier(struct brcmf_if *ifp, bool on); int __init brcmf_core_init(void); -void __exit brcmf_core_exit(void); - +void brcmf_core_exit(void); +int brcmf_pktfilter_add_remove(struct net_device *ndev, int filter_num, + bool add); +int brcmf_pktfilter_enable(struct net_device *ndev, bool enable); +void brcmf_del_sta(struct brcmf_if *ifp, const u8 *ea); +struct brcmf_sta *brcmf_find_sta(struct brcmf_if *ifp, const u8 *ea); +struct brcmf_sta *brcmf_findadd_sta(struct brcmf_if *ifp, const u8 *ea); #endif /* BRCMFMAC_CORE_H */ diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/debug.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/debug.c index eecf8a38d94aa..f5da560bfc124 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/debug.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/debug.c @@ -14,6 +14,82 @@ #include "fweh.h" #include "debug.h" +static int +brcmf_debug_msgtrace_seqchk(u32 *prev, u32 cur) +{ + if ((cur == 0 && *prev == 0xFFFFFFFF) || ((cur - *prev) == 1)) { + goto done; + } else if (cur == *prev) { + brcmf_dbg(FWCON, "duplicate trace\n"); + return -1; + } else if (cur > *prev) { + brcmf_dbg(FWCON, "lost %d packets\n", cur - *prev); + } else { + brcmf_dbg(FWCON, "seq out of order, host %d, dongle %d\n", + *prev, cur); + } +done: + *prev = cur; + return 0; +} + +static int +brcmf_debug_msg_parser(void *event_data) +{ + int err = 0; + struct msgtrace_hdr *hdr; + char *data, *s; + static u32 seqnum_prev; + + hdr = (struct msgtrace_hdr *)event_data; + data = (char *)event_data + MSGTRACE_HDRLEN; + + /* There are 2 bytes available at the end of data */ + data[ntohs(hdr->len)] = '\0'; + + if (ntohl(hdr->discarded_bytes) || ntohl(hdr->discarded_printf)) { + brcmf_dbg(FWCON, "Discarded_bytes %d discarded_printf %d\n", + ntohl(hdr->discarded_bytes), + ntohl(hdr->discarded_printf)); + } + + err = brcmf_debug_msgtrace_seqchk(&seqnum_prev, ntohl(hdr->seqnum)); + if (err) + return err; + + while (*data != '\0' && (s = strstr(data, "\n")) != NULL) { + *s = '\0'; + brcmf_dbg(FWCON, "CONSOLE: %s\n", data); + data = s + 1; + } + if (*data) + brcmf_dbg(FWCON, "CONSOLE: %s", data); + + return err; +} + +static int +brcmf_debug_trace_parser(struct brcmf_if *ifp, + const struct brcmf_event_msg *evtmsg, + void *event_data) +{ + int err = 0; + struct msgtrace_hdr *hdr; + + hdr = (struct msgtrace_hdr *)event_data; + if (hdr->version != MSGTRACE_VERSION) { + brcmf_dbg(FWCON, "trace version mismatch host %d dngl %d\n", + MSGTRACE_VERSION, hdr->version); + err = -EPROTO; + return err; + } + + if (hdr->trace_type == MSGTRACE_HDR_TYPE_MSG) + err = brcmf_debug_msg_parser(event_data); + + return err; +} + int brcmf_debug_create_memdump(struct brcmf_bus *bus, const void *data, size_t len) { @@ -42,6 +118,13 @@ int brcmf_debug_create_memdump(struct brcmf_bus *bus, const void *data, return 0; } + +int brcmf_debug_fwlog_init(struct brcmf_pub *drvr) +{ + return brcmf_fweh_register(drvr, BRCMF_E_TRACE, + brcmf_debug_trace_parser); +} + struct dentry *brcmf_debugfs_get_devdir(struct brcmf_pub *drvr) { return drvr->wiphy->debugfsdir; diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/debug.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/debug.h index 4146faeed3449..95f0f7b2c4a6c 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/debug.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/debug.h @@ -29,6 +29,7 @@ #define BRCMF_MSGBUF_VAL 0x00040000 #define BRCMF_PCIE_VAL 0x00080000 #define BRCMF_FWCON_VAL 0x00100000 +#define BRCMF_ULP_VAL 0x00200000 /* set default print format */ #undef pr_fmt @@ -103,6 +104,10 @@ do { \ #endif /* defined(DEBUG) || defined(CONFIG_BRCM_TRACING) */ +#define MSGTRACE_VERSION 1 +#define MSGTRACE_HDR_TYPE_MSG 0 +#define MSGTRACE_HDR_TYPE_LOG 1 + #define brcmf_dbg_hex_dump(test, data, len, fmt, ...) \ do { \ trace_brcmf_hexdump((void *)data, len); \ @@ -120,6 +125,7 @@ void brcmf_debugfs_add_entry(struct brcmf_pub *drvr, const char *fn, int (*read_fn)(struct seq_file *seq, void *data)); int brcmf_debug_create_memdump(struct brcmf_bus *bus, const void *data, size_t len); +int brcmf_debug_fwlog_init(struct brcmf_pub *drvr); #else static inline struct dentry *brcmf_debugfs_get_devdir(struct brcmf_pub *drvr) { @@ -135,6 +141,25 @@ int brcmf_debug_create_memdump(struct brcmf_bus *bus, const void *data, { return 0; } + +static inline +int brcmf_debug_fwlog_init(struct brcmf_pub *drvr) +{ + return 0; +} #endif +/* Message trace header */ +struct msgtrace_hdr { + u8 version; + u8 trace_type; + u16 len; /* Len of the trace */ + u32 seqnum; /* Sequence number of message */ + /* Number of discarded bytes because of trace overflow */ + u32 discarded_bytes; + /* Number of discarded printf because of trace overflow */ + u32 discarded_printf; +}; + +#define MSGTRACE_HDRLEN sizeof(struct msgtrace_hdr) #endif /* BRCMFMAC_DEBUG_H */ diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/dmi.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/dmi.c index 0af452dca7664..9bbed6f2ce28a 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/dmi.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/dmi.c @@ -40,25 +40,15 @@ static const struct brcmf_dmi_data pov_tab_p1006w_data = { BRCM_CC_43340_CHIP_ID, 2, "pov-tab-p1006w-data" }; -static const struct brcmf_dmi_data predia_basic_data = { - BRCM_CC_43341_CHIP_ID, 2, "predia-basic" -}; - -/* Note the Voyo winpad A15 tablet uses the same Ampak AP6330 module, with the - * exact same nvram file as the Prowise-PT301 tablet. Since the nvram for the - * Prowise-PT301 is already in linux-firmware we just point to that here. - */ -static const struct brcmf_dmi_data voyo_winpad_a15_data = { - BRCM_CC_4330_CHIP_ID, 4, "Prowise-PT301" -}; - static const struct dmi_system_id dmi_platform_data[] = { { /* ACEPC T8 Cherry Trail Z8350 mini PC */ .matches = { DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "To be filled by O.E.M."), DMI_EXACT_MATCH(DMI_BOARD_NAME, "Cherry Trail CR"), +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 18, 0)) DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "T8"), +#endif /* also match on somewhat unique bios-version */ DMI_EXACT_MATCH(DMI_BIOS_VERSION, "1.000"), }, @@ -69,22 +59,14 @@ static const struct dmi_system_id dmi_platform_data[] = { .matches = { DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "To be filled by O.E.M."), DMI_EXACT_MATCH(DMI_BOARD_NAME, "Cherry Trail CR"), +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 18, 0)) DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "T11"), +#endif /* also match on somewhat unique bios-version */ DMI_EXACT_MATCH(DMI_BIOS_VERSION, "1.000"), }, .driver_data = (void *)&acepc_t8_data, }, - { - /* Cyberbook T116 rugged tablet */ - .matches = { - DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "Default string"), - DMI_EXACT_MATCH(DMI_BOARD_NAME, "Cherry Trail CR"), - DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "20170531"), - }, - /* The factory image nvram file is identical to the ACEPC T8 one */ - .driver_data = (void *)&acepc_t8_data, - }, { /* Match for the GPDwin which unfortunately uses somewhat * generic dmi strings, which is why we test for 4 strings. @@ -133,26 +115,6 @@ static const struct dmi_system_id dmi_platform_data[] = { }, .driver_data = (void *)&pov_tab_p1006w_data, }, - { - /* Predia Basic tablet (+ with keyboard dock) */ - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Insyde"), - DMI_MATCH(DMI_PRODUCT_NAME, "CherryTrail"), - /* Mx.WT107.KUBNGEA02 with the version-nr dropped */ - DMI_MATCH(DMI_BIOS_VERSION, "Mx.WT107.KUBNGEA"), - }, - .driver_data = (void *)&predia_basic_data, - }, - { - /* Voyo winpad A15 tablet */ - .matches = { - DMI_MATCH(DMI_BOARD_VENDOR, "AMI Corporation"), - DMI_MATCH(DMI_BOARD_NAME, "Aptio CRB"), - /* Above strings are too generic, also match on BIOS date */ - DMI_MATCH(DMI_BIOS_DATE, "11/20/2014"), - }, - .driver_data = (void *)&voyo_winpad_a15_data, - }, {} }; diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c index 7c68d98493246..f6e1f664c595d 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c @@ -16,7 +16,6 @@ #include "feature.h" #include "common.h" -#define BRCMF_FW_UNSUPPORTED 23 /* * expand feature list to array of feature strings. @@ -41,8 +40,11 @@ static const struct brcmf_feat_fwcap brcmf_fwcap_map[] = { { BRCMF_FEAT_MONITOR_FLAG, "rtap" }, { BRCMF_FEAT_MONITOR_FMT_RADIOTAP, "rtap" }, { BRCMF_FEAT_DOT11H, "802.11h" }, - { BRCMF_FEAT_SAE, "sae" }, + { BRCMF_FEAT_SAE, "sae " }, { BRCMF_FEAT_FWAUTH, "idauth" }, + { BRCMF_FEAT_SAE_EXT, "sae_ext " }, + { BRCMF_FEAT_FBT, "fbt " }, + { BRCMF_FEAT_OKC, "okc" }, }; #ifdef DEBUG @@ -143,7 +145,7 @@ static void brcmf_feat_iovar_int_get(struct brcmf_if *ifp, ifp->fwil_fwerr = true; err = brcmf_fil_iovar_int_get(ifp, name, &data); - if (err == 0) { + if (err != -BRCMF_FW_UNSUPPORTED) { brcmf_dbg(INFO, "enabling feature: %s\n", brcmf_feat_names[id]); ifp->drvr->feat_flags |= BIT(id); } else { @@ -248,7 +250,8 @@ void brcmf_feat_attach(struct brcmf_pub *drvr) brcmf_feat_firmware_capabilities(ifp); memset(&gscan_cfg, 0, sizeof(gscan_cfg)); if (drvr->bus_if->chip != BRCM_CC_43430_CHIP_ID && - drvr->bus_if->chip != BRCM_CC_4345_CHIP_ID) + drvr->bus_if->chip != BRCM_CC_4345_CHIP_ID && + drvr->bus_if->chip != CY_CC_43439_CHIP_ID) brcmf_feat_iovar_data_set(ifp, BRCMF_FEAT_GSCAN, "pfn_gscan_cfg", &gscan_cfg, sizeof(gscan_cfg)); @@ -279,6 +282,7 @@ void brcmf_feat_attach(struct brcmf_pub *drvr) brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_RSDB, "rsdb_mode"); brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_TDLS, "tdls_enable"); brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_MFP, "mfp"); + brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_DUMP_OBSS, "dump_obss"); pfn_mac.version = BRCMF_PFN_MACADDR_CFG_VER; err = brcmf_fil_iovar_data_get(ifp, "pfn_macaddr", &pfn_mac, diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.h index d1f4257af696b..6f9f9e0b60b57 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.h @@ -29,6 +29,8 @@ * DOT11H: firmware supports 802.11h * SAE: simultaneous authentication of equals * FWAUTH: Firmware authenticator + * DUMP_OBSS: Firmware has capable to dump obss info to support ACS + * SAE_EXT: SAE be handled by userspace supplicant */ #define BRCMF_FEAT_LIST \ BRCMF_FEAT_DEF(MBSS) \ @@ -51,7 +53,11 @@ BRCMF_FEAT_DEF(MONITOR_FMT_HW_RX_HDR) \ BRCMF_FEAT_DEF(DOT11H) \ BRCMF_FEAT_DEF(SAE) \ - BRCMF_FEAT_DEF(FWAUTH) + BRCMF_FEAT_DEF(FWAUTH) \ + BRCMF_FEAT_DEF(DUMP_OBSS) \ + BRCMF_FEAT_DEF(SAE_EXT) \ + BRCMF_FEAT_DEF(FBT) \ + BRCMF_FEAT_DEF(OKC) /* * Quirks: diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.c index a2b8d9171af2a..7eaf69813cbd5 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.c @@ -10,6 +10,7 @@ #include #include #include +#include #include "debug.h" #include "firmware.h" @@ -207,8 +208,6 @@ static int brcmf_init_nvram_parser(struct nvram_parser *nvp, size = BRCMF_FW_MAX_NVRAM_SIZE; else size = data_len; - /* Add space for properties we may add */ - size += strlen(BRCMF_FW_DEFAULT_BOARDREV) + 1; /* Alloc for extra 0 byte + roundup by 4 + length field */ size += 1 + 3 + sizeof(u32); nvp->nvram = kzalloc(size, GFP_KERNEL); @@ -512,7 +511,8 @@ static void brcmf_fw_free_request(struct brcmf_fw_request *req) int i; for (i = 0, item = &req->items[0]; i < req->n_items; i++, item++) { - if (item->type == BRCMF_FW_TYPE_BINARY) + if (item->type == BRCMF_FW_TYPE_BINARY || + item->type == BRCMF_FW_TYPE_TRXSE) release_firmware(item->binary); else if (item->type == BRCMF_FW_TYPE_NVRAM) brcmf_fw_nvram_free(item->nv_data.data); @@ -583,6 +583,7 @@ static int brcmf_fw_complete_request(const struct firmware *fw, ret = brcmf_fw_request_nvram_done(fw, fwctx); break; case BRCMF_FW_TYPE_BINARY: + case BRCMF_FW_TYPE_TRXSE: if (fw) cur->binary = fw; else @@ -615,8 +616,11 @@ static int brcmf_fw_request_firmware(const struct firmware **fw, strlcat(alt_path, fwctx->req->board_type, BRCMF_FW_NAME_LEN); strlcat(alt_path, ".txt", BRCMF_FW_NAME_LEN); - ret = request_firmware(fw, alt_path, fwctx->dev); - if (ret == 0) + ret = request_firmware_direct(fw, alt_path, fwctx->dev); + if (ret) + brcmf_info("no board-specific nvram available (ret=%d), device will use %s\n", + ret, cur->path); + else return ret; } @@ -626,8 +630,19 @@ static int brcmf_fw_request_firmware(const struct firmware **fw, static void brcmf_fw_request_done(const struct firmware *fw, void *ctx) { struct brcmf_fw *fwctx = ctx; + struct brcmf_fw_item *cur = &fwctx->req->items[fwctx->curpos]; + char alt_path[BRCMF_FW_NAME_LEN]; int ret; + if (!fw && cur->type == BRCMF_FW_TYPE_TRXSE) { + strlcpy(alt_path, cur->path, BRCMF_FW_NAME_LEN); + /* strip 'se' from .trxse at the end */ + alt_path[strlen(alt_path) - 2] = 0; + ret = request_firmware(&fw, alt_path, fwctx->dev); + if (!ret) + cur->path = alt_path; + } + ret = brcmf_fw_complete_request(fw, fwctx); while (ret == 0 && ++fwctx->curpos < fwctx->req->n_items) { diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.h index 46c66415b4a67..53141a5e853df 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.h @@ -11,6 +11,8 @@ #define BRCMF_FW_DEFAULT_PATH "brcm/" +#define CY_FW_DEFAULT_PATH "cypress/" + /** * struct brcmf_firmware_mapping - Used to map chipid/revmask to firmware * filename and nvram filename. Each bus type implementation should create @@ -32,6 +34,16 @@ static const char BRCM_ ## fw_name ## _FIRMWARE_BASENAME[] = \ BRCMF_FW_DEFAULT_PATH fw_base; \ MODULE_FIRMWARE(BRCMF_FW_DEFAULT_PATH fw_base ".bin") +#define CY_FW_DEF(fw_name, fw_base) \ +static const char BRCM_ ## fw_name ## _FIRMWARE_BASENAME[] = \ + CY_FW_DEFAULT_PATH fw_base; \ +MODULE_FIRMWARE(CY_FW_DEFAULT_PATH fw_base ".bin") + +#define CY_FW_TRXSE_DEF(fw_name, fw_base) \ +static const char BRCM_ ## fw_name ## _FIRMWARE_BASENAME[] = \ + CY_FW_DEFAULT_PATH fw_base; \ +MODULE_FIRMWARE(CY_FW_DEFAULT_PATH fw_base ".trxse") + #define BRCMF_FW_ENTRY(chipid, mask, name) \ { chipid, mask, BRCM_ ## name ## _FIRMWARE_BASENAME } @@ -39,7 +51,8 @@ void brcmf_fw_nvram_free(void *nvram); enum brcmf_fw_type { BRCMF_FW_TYPE_BINARY, - BRCMF_FW_TYPE_NVRAM + BRCMF_FW_TYPE_NVRAM, + BRCMF_FW_TYPE_TRXSE }; struct brcmf_fw_item { diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/flowring.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/flowring.c index 096f6b969dd83..e1127d7e086d5 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/flowring.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/flowring.c @@ -419,7 +419,6 @@ void brcmf_flowring_configure_addr_mode(struct brcmf_flowring *flow, int ifidx, flowid = flow->hash[i].flowid; if (flow->rings[flowid]->status != RING_OPEN) continue; - flow->rings[flowid]->status = RING_CLOSING; brcmf_msgbuf_delete_flowring(drvr, flowid); } } @@ -458,10 +457,8 @@ void brcmf_flowring_delete_peer(struct brcmf_flowring *flow, int ifidx, if ((sta || (memcmp(hash[i].mac, peer, ETH_ALEN) == 0)) && (hash[i].ifidx == ifidx)) { flowid = flow->hash[i].flowid; - if (flow->rings[flowid]->status == RING_OPEN) { - flow->rings[flowid]->status = RING_CLOSING; + if (flow->rings[flowid]->status == RING_OPEN) brcmf_msgbuf_delete_flowring(drvr, flowid); - } } } diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.c index 1285d3685c4f5..6f3eab947bb58 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.c @@ -228,10 +228,6 @@ static void brcmf_fweh_event_worker(struct work_struct *work) brcmf_fweh_event_name(event->code), event->code, event->emsg.ifidx, event->emsg.bsscfgidx, event->emsg.addr); - if (event->emsg.bsscfgidx >= BRCMF_MAX_IFS) { - bphy_err(drvr, "invalid bsscfg index: %u\n", event->emsg.bsscfgidx); - goto event_free; - } /* convert event message */ emsg_be = &event->emsg; @@ -359,26 +355,42 @@ int brcmf_fweh_activate_events(struct brcmf_if *ifp) { struct brcmf_pub *drvr = ifp->drvr; int i, err; - s8 eventmask[BRCMF_EVENTING_MASK_LEN]; + struct eventmsgs_ext *eventmask_msg; + u32 msglen; + + msglen = EVENTMSGS_EXT_STRUCT_SIZE + BRCMF_EVENTING_MASK_LEN; + eventmask_msg = kzalloc(msglen, GFP_KERNEL); + if (!eventmask_msg) + return -ENOMEM; - memset(eventmask, 0, sizeof(eventmask)); for (i = 0; i < BRCMF_E_LAST; i++) { if (ifp->drvr->fweh.evt_handler[i]) { brcmf_dbg(EVENT, "enable event %s\n", brcmf_fweh_event_name(i)); - setbit(eventmask, i); + setbit(eventmask_msg->mask, i); } } /* want to handle IF event as well */ brcmf_dbg(EVENT, "enable event IF\n"); - setbit(eventmask, BRCMF_E_IF); + setbit(eventmask_msg->mask, BRCMF_E_IF); + + eventmask_msg->ver = EVENTMSGS_VER; + eventmask_msg->command = EVENTMSGS_SET_MASK; + eventmask_msg->len = BRCMF_EVENTING_MASK_LEN; + + err = brcmf_fil_iovar_data_set(ifp, "event_msgs_ext", eventmask_msg, + msglen); + if (!err) + goto end; - err = brcmf_fil_iovar_data_set(ifp, "event_msgs", - eventmask, BRCMF_EVENTING_MASK_LEN); + err = brcmf_fil_iovar_data_set(ifp, "event_msgs", eventmask_msg->mask, + BRCMF_EVENTING_MASK_LEN); if (err) bphy_err(drvr, "Set event_msgs error (%d)\n", err); +end: + kfree(eventmask_msg); return err; } diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.h index 48414e8b93890..f92700237d0c8 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.h @@ -90,7 +90,13 @@ struct brcmf_cfg80211_info; BRCMF_ENUM_DEF(FIFO_CREDIT_MAP, 74) \ BRCMF_ENUM_DEF(ACTION_FRAME_RX, 75) \ BRCMF_ENUM_DEF(TDLS_PEER_EVENT, 92) \ - BRCMF_ENUM_DEF(BCMC_CREDIT_SUPPORT, 127) + BRCMF_ENUM_DEF(PHY_TEMP, 111) \ + BRCMF_ENUM_DEF(BCMC_CREDIT_SUPPORT, 127) \ + BRCMF_ENUM_DEF(ULP, 146) \ + BRCMF_ENUM_DEF(EXT_AUTH_REQ, 187) \ + BRCMF_ENUM_DEF(EXT_AUTH_FRAME_RX, 188) \ + BRCMF_ENUM_DEF(MGMT_FRAME_TXSTATUS, 189) \ + BRCMF_ENUM_DEF(MGMT_FRAME_OFF_CHAN_COMPLETE, 190) #define BRCMF_ENUM_DEF(id, val) \ BRCMF_E_##id = (val), @@ -102,7 +108,7 @@ enum brcmf_fweh_event_code { * minimum length check in device firmware so it is * hard-coded here. */ - BRCMF_E_LAST = 139 + BRCMF_E_LAST = 191 }; #undef BRCMF_ENUM_DEF @@ -283,6 +289,28 @@ struct brcmf_if_event { u8 role; }; +enum event_msgs_ext_command { + EVENTMSGS_NONE = 0, + EVENTMSGS_SET_BIT = 1, + EVENTMSGS_RESET_BIT = 2, + EVENTMSGS_SET_MASK = 3 +}; + +#define EVENTMSGS_VER 1 +#define EVENTMSGS_EXT_STRUCT_SIZE offsetof(struct eventmsgs_ext, mask[0]) + +/* len- for SET it would be mask size from the application to the firmware */ +/* for GET it would be actual firmware mask size */ +/* maxgetsize - is only used for GET. indicate max mask size that the */ +/* application can read from the firmware */ +struct eventmsgs_ext { + u8 ver; + u8 command; + u8 len; + u8 maxgetsize; + u8 mask[1]; +}; + typedef int (*brcmf_fweh_handler_t)(struct brcmf_if *ifp, const struct brcmf_event_msg *evtmsg, void *data); diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil.c index 9ed85420f3ca4..f38b1eaaa1e53 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil.c @@ -97,12 +97,19 @@ brcmf_fil_cmd_data(struct brcmf_if *ifp, u32 cmd, void *data, u32 len, bool set) { struct brcmf_pub *drvr = ifp->drvr; s32 err, fwerr; +#ifdef CONFIG_BRCMF_CMD_TIMEOUT_REBOOT + int retries = 0; +#endif /* CONFIG_BRCMF_CMD_TIMEOUT_REBOOT */ if (drvr->bus_if->state != BRCMF_BUS_UP) { bphy_err(drvr, "bus is down. we have nothing to do.\n"); return -EIO; } +#ifdef CONFIG_BRCMF_CMD_TIMEOUT_REBOOT +retry: +#endif /* CONFIG_BRCMF_CMD_TIMEOUT_REBOOT */ + if (data != NULL) len = min_t(uint, len, BRCMF_DCMD_MAXLEN); if (set) @@ -114,6 +121,18 @@ brcmf_fil_cmd_data(struct brcmf_if *ifp, u32 cmd, void *data, u32 len, bool set) if (err) { brcmf_dbg(FIL, "Failed: error=%d\n", err); + +#ifdef CONFIG_BRCMF_CMD_TIMEOUT_REBOOT + if (err == -ETIMEDOUT) { + brcmf_err("cmd timeout, retries=%d\n", retries); + if (retries < BRCMF_MAX_CMD_TIMEOUT_RETRIES) { + retries++; + goto retry; + } else { + brcmf_bus_handle_cmd_timeout(drvr->bus_if); + } + } +#endif /* CONFIG_BRCMF_CMD_TIMEOUT_REBOOT */ } else if (fwerr < 0) { brcmf_dbg(FIL, "Firmware error: %s (%d)\n", brcmf_fil_get_errstr((u32)(-fwerr)), fwerr); diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil.h index ae4cf43729086..9ed7e56bcb9e2 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil.h @@ -71,6 +71,7 @@ #define BRCMF_C_SCB_DEAUTHENTICATE_FOR_REASON 201 #define BRCMF_C_SET_ASSOC_PREFER 205 #define BRCMF_C_GET_VALID_CHANNELS 217 +#define BRCMF_C_GET_FAKEFRAG 218 #define BRCMF_C_SET_FAKEFRAG 219 #define BRCMF_C_GET_KEY_PRIMARY 235 #define BRCMF_C_SET_KEY_PRIMARY 236 @@ -79,6 +80,9 @@ #define BRCMF_C_SET_VAR 263 #define BRCMF_C_SET_WSEC_PMK 268 +#define BRCMF_FW_BADARG 2 +#define BRCMF_FW_UNSUPPORTED 23 + s32 brcmf_fil_cmd_data_set(struct brcmf_if *ifp, u32 cmd, void *data, u32 len); s32 brcmf_fil_cmd_data_get(struct brcmf_if *ifp, u32 cmd, void *data, u32 len); s32 brcmf_fil_cmd_int_set(struct brcmf_if *ifp, u32 cmd, u32 data); diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h index 2e31cc10c1954..4cac2b780c599 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h @@ -59,6 +59,7 @@ #define BRCMF_SCANTYPE_PASSIVE 1 #define BRCMF_WSEC_MAX_PSK_LEN 32 +#define BRCMF_WSEC_MAX_PMK_LEN 64 /* SUITE-B-192's PMK is 48 bytes */ #define BRCMF_WSEC_PASSPHRASE BIT(0) #define BRCMF_WSEC_MAX_SAE_PASSWORD_LEN 128 @@ -135,9 +136,22 @@ /* Link Down indication in WoWL mode: */ #define BRCMF_WOWL_LINKDOWN (1 << 31) -#define BRCMF_WOWL_MAXPATTERNS 8 +#define BRCMF_WOWL_MAXPATTERNS 16 #define BRCMF_WOWL_MAXPATTERNSIZE 128 +enum { + BRCMF_UNICAST_FILTER_NUM = 0, + BRCMF_BROADCAST_FILTER_NUM, + BRCMF_MULTICAST4_FILTER_NUM, + BRCMF_MULTICAST6_FILTER_NUM, + BRCMF_MDNS_FILTER_NUM, + BRCMF_ARP_FILTER_NUM, + BRCMF_BROADCAST_ARP_FILTER_NUM, + MAX_PKT_FILTER_COUNT +}; + +#define MAX_PKTFILTER_PATTERN_SIZE 16 + #define BRCMF_COUNTRY_BUF_SZ 4 #define BRCMF_ANT_MAX 4 @@ -169,6 +183,11 @@ #define BRCMF_HE_CAP_MCS_MAP_NSS_MAX 8 +#define BRCMF_EXTAUTH_START 1 +#define BRCMF_EXTAUTH_ABORT 2 +#define BRCMF_EXTAUTH_FAIL 3 +#define BRCMF_EXTAUTH_SUCCESS 4 + /* MAX_CHUNK_LEN is the maximum length for data passing to firmware in each * ioctl. It is relatively small because firmware has small maximum size input * playload restriction for ioctls. @@ -184,6 +203,8 @@ #define DL_TYPE_CLM 2 +#define MAX_RSSI_LEVELS 8 + /* join preference types for join_pref iovar */ enum brcmf_join_pref_types { BRCMF_JOIN_PREF_RSSI = 1, @@ -517,7 +538,7 @@ struct brcmf_wsec_key_le { struct brcmf_wsec_pmk_le { __le16 key_len; __le16 flags; - u8 key[2 * BRCMF_WSEC_MAX_PSK_LEN + 1]; + u8 key[2 * BRCMF_WSEC_MAX_PMK_LEN + 1]; }; /** @@ -531,6 +552,47 @@ struct brcmf_wsec_sae_pwd_le { u8 key[BRCMF_WSEC_MAX_SAE_PASSWORD_LEN]; }; +/** + * struct brcmf_auth_req_status_le - external auth request and status update + * + * @flags: flags for external auth status + * @peer_mac: peer MAC address + * @ssid_len: length of ssid + * @ssid: ssid characters + */ +struct brcmf_auth_req_status_le { + __le16 flags; + u8 peer_mac[ETH_ALEN]; + __le32 ssid_len; + u8 ssid[IEEE80211_MAX_SSID_LEN]; + u8 pmkid[WLAN_PMKID_LEN]; +}; + +/** + * struct brcmf_mf_params_le - management frame parameters for mgmt_frame iovar + * + * @version: version of the iovar + * @dwell_time: dwell duration in ms + * @len: length of frame data + * @frame_control: frame control + * @channel: channel + * @da: peer MAC address + * @bssid: BSS network identifier + * @packet_id: packet identifier + * @data: frame data + */ +struct brcmf_mf_params_le { + __le32 version; + __le32 dwell_time; + __le16 len; + __le16 frame_control; + __le16 channel; + u8 da[ETH_ALEN]; + u8 bssid[ETH_ALEN]; + __le32 packet_id; + u8 data[1]; +}; + /* Used to get specific STA parameters */ struct brcmf_scb_val_le { __le32 val; @@ -1024,4 +1086,19 @@ struct brcmf_gscan_config { struct brcmf_gscan_bucket_config bucket[1]; }; +/* BRCM_E_RSSI event data */ +struct wl_event_data_rssi { + s32 rssi; + s32 snr; + s32 noise; +}; + +/** RSSI event notification configuration. */ +struct wl_rssi_event { + u32 rate_limit_msec; + u8 num_rssi_levels; + s8 rssi_levels[MAX_RSSI_LEVELS]; + s8 pad[3]; +}; + #endif /* FWIL_TYPES_H_ */ diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.c index 437e83ea8902d..8c06160a18bab 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.c @@ -502,6 +502,9 @@ struct brcmf_fws_info { bool creditmap_received; u8 mode; bool avoid_queueing; +#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 16, 0)) + int fifo_init_credit[BRCMF_FWS_FIFO_COUNT]; +#endif }; #define BRCMF_FWS_TLV_DEF(name, id, len) \ @@ -621,7 +624,6 @@ static inline int brcmf_fws_hanger_poppkt(struct brcmf_fws_hanger *h, static void brcmf_fws_psq_flush(struct brcmf_fws_info *fws, struct pktq *q, int ifidx) { - struct brcmf_fws_hanger_item *hi; bool (*matchfn)(struct sk_buff *, void *) = NULL; struct sk_buff *skb; int prec; @@ -633,9 +635,6 @@ static void brcmf_fws_psq_flush(struct brcmf_fws_info *fws, struct pktq *q, skb = brcmu_pktq_pdeq_match(q, prec, matchfn, &ifidx); while (skb) { hslot = brcmf_skb_htod_tag_get_field(skb, HSLOT); - hi = &fws->hanger.items[hslot]; - WARN_ON(skb != hi->pkt); - hi->state = BRCMF_FWS_HANGER_ITEM_STATE_FREE; brcmf_fws_hanger_poppkt(&fws->hanger, hslot, &skb, true); brcmu_pkt_buf_free_skb(skb); @@ -1617,9 +1616,13 @@ static int brcmf_fws_notify_credit_map(struct brcmf_if *ifp, fws->fifo_credit_map |= 1 << i; else fws->fifo_credit_map &= ~(1 << i); + WARN_ONCE(fws->fifo_credit[i] < 0, "fifo_credit[%d] is negative(%d)\n", i, fws->fifo_credit[i]); +#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 16, 0)) + fws->fifo_init_credit[i] = fws->fifo_credit[i]; +#endif } brcmf_fws_schedule_deq(fws); brcmf_fws_unlock(fws); @@ -2197,6 +2200,38 @@ void brcmf_fws_del_interface(struct brcmf_if *ifp) brcmf_fws_unlock(fws); } +#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 16, 0)) +static bool brcmf_fws_ismultistream(struct brcmf_fws_info *fws) +{ + bool ret = false; + u8 credit_usage = 0; + + /* Check only for BE, VI and VO traffic */ + u32 delay_map = fws->fifo_delay_map & + ((1 << BRCMF_FWS_FIFO_AC_BE) | + (1 << BRCMF_FWS_FIFO_AC_VI) | + (1 << BRCMF_FWS_FIFO_AC_VO)); + + if (hweight_long(delay_map) > 1) { + ret = true; + } else { + if (fws->fifo_credit[BRCMF_FWS_FIFO_AC_BE] < + fws->fifo_init_credit[BRCMF_FWS_FIFO_AC_BE]) + credit_usage++; + if (fws->fifo_credit[BRCMF_FWS_FIFO_AC_VI] < + fws->fifo_init_credit[BRCMF_FWS_FIFO_AC_VI]) + credit_usage++; + if (fws->fifo_credit[BRCMF_FWS_FIFO_AC_VO] < + fws->fifo_init_credit[BRCMF_FWS_FIFO_AC_VO]) + credit_usage++; + + if (credit_usage > 1) + ret = true; + } + return ret; +} +#endif + static void brcmf_fws_dequeue_worker(struct work_struct *worker) { struct brcmf_fws_info *fws; @@ -2210,6 +2245,13 @@ static void brcmf_fws_dequeue_worker(struct work_struct *worker) fws = container_of(worker, struct brcmf_fws_info, fws_dequeue_work); drvr = fws->drvr; +#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 16, 0)) + if (brcmf_fws_ismultistream(fws)) + drvr->bus_if->allow_skborphan = false; + else + drvr->bus_if->allow_skborphan = true; +#endif + brcmf_fws_lock(fws); for (fifo = BRCMF_FWS_FIFO_BCMC; fifo >= 0 && !fws->bus_flow_blocked; fifo--) { @@ -2475,7 +2517,8 @@ bool brcmf_fws_fc_active(struct brcmf_fws_info *fws) return fws->fcmode != BRCMF_FWS_FCMODE_NONE; } -void brcmf_fws_bustxfail(struct brcmf_fws_info *fws, struct sk_buff *skb) +void brcmf_fws_bustxcomplete(struct brcmf_fws_info *fws, struct sk_buff *skb, + bool success) { u32 hslot; @@ -2483,11 +2526,13 @@ void brcmf_fws_bustxfail(struct brcmf_fws_info *fws, struct sk_buff *skb) brcmu_pkt_buf_free_skb(skb); return; } - brcmf_fws_lock(fws); - hslot = brcmf_skb_htod_tag_get_field(skb, HSLOT); - brcmf_fws_txs_process(fws, BRCMF_FWS_TXSTATUS_HOST_TOSSED, hslot, 0, 0, - 1); - brcmf_fws_unlock(fws); + if (!success) { + brcmf_fws_lock(fws); + hslot = brcmf_skb_htod_tag_get_field(skb, HSLOT); + brcmf_fws_txs_process(fws, BRCMF_FWS_TXSTATUS_HOST_TOSSED, + hslot, 0, 0, 1); + brcmf_fws_unlock(fws); + } } void brcmf_fws_bus_blocked(struct brcmf_pub *drvr, bool flow_blocked) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.h index 50e424b5880de..c3b2d3691c43c 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.h @@ -40,7 +40,8 @@ int brcmf_fws_process_skb(struct brcmf_if *ifp, struct sk_buff *skb); void brcmf_fws_reset_interface(struct brcmf_if *ifp); void brcmf_fws_add_interface(struct brcmf_if *ifp); void brcmf_fws_del_interface(struct brcmf_if *ifp); -void brcmf_fws_bustxfail(struct brcmf_fws_info *fws, struct sk_buff *skb); +void brcmf_fws_bustxcomplete(struct brcmf_fws_info *fws, struct sk_buff *skb, + bool success); void brcmf_fws_bus_blocked(struct brcmf_pub *drvr, bool flow_blocked); void brcmf_fws_rxreorder(struct brcmf_if *ifp, struct sk_buff *skb, bool inirq); diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.c index 7c8e08ee8f0ff..920ec9314e323 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include @@ -23,6 +24,7 @@ #include "flowring.h" #include "bus.h" #include "tracepoint.h" +#include "pcie.h" #define MSGBUF_IOCTL_RESP_TIMEOUT msecs_to_jiffies(2000) @@ -47,6 +49,8 @@ #define MSGBUF_TYPE_RX_CMPLT 0x12 #define MSGBUF_TYPE_LPBK_DMAXFER 0x13 #define MSGBUF_TYPE_LPBK_DMAXFER_CMPLT 0x14 +#define MSGBUF_TYPE_H2D_MAILBOX_DATA 0x23 +#define MSGBUF_TYPE_D2H_MAILBOX_DATA 0x24 #define NR_TX_PKTIDS 2048 #define NR_RX_PKTIDS 1024 @@ -71,6 +75,7 @@ #define BRCMF_MSGBUF_TRICKLE_TXWORKER_THRS 32 #define BRCMF_MSGBUF_UPDATE_RX_PTR_THRS 48 +#define BRCMF_MAX_TXSTATUS_WAIT_RETRIES 10 struct msgbuf_common_hdr { u8 msgtype; @@ -103,6 +108,12 @@ struct msgbuf_tx_msghdr { __le32 rsvd0; }; +struct msgbuf_h2d_mbdata { + struct msgbuf_common_hdr msg; + __le32 mbdata; + __le16 rsvd0[7]; +}; + struct msgbuf_rx_bufpost { struct msgbuf_common_hdr msg; __le16 metadata_buf_len; @@ -217,6 +228,13 @@ struct msgbuf_flowring_flush_resp { __le32 rsvd0[3]; }; +struct msgbuf_d2h_mailbox_data { + struct msgbuf_common_hdr msg; + struct msgbuf_completion_hdr compl_hdr; + __le32 mbdata; + __le32 rsvd0[2]; +} d2h_mailbox_data_t; + struct brcmf_msgbuf_work_item { struct list_head queue; u32 flowid; @@ -289,6 +307,8 @@ struct brcmf_msgbuf_pktids { }; static void brcmf_msgbuf_rxbuf_ioctlresp_post(struct brcmf_msgbuf *msgbuf); +static void brcmf_msgbuf_process_d2h_mbdata(struct brcmf_msgbuf *msgbuf, + void *buf); static struct brcmf_msgbuf_pktids * @@ -423,6 +443,34 @@ static void brcmf_msgbuf_release_pktids(struct brcmf_msgbuf *msgbuf) msgbuf->tx_pktids); } +int brcmf_msgbuf_tx_mbdata(struct brcmf_pub *drvr, u32 mbdata) +{ + struct brcmf_msgbuf *msgbuf = (struct brcmf_msgbuf *)drvr->proto->pd; + struct brcmf_commonring *commonring; + struct msgbuf_h2d_mbdata *h2d_mbdata; + void *ret_ptr; + int err; + + commonring = msgbuf->commonrings[BRCMF_H2D_MSGRING_CONTROL_SUBMIT]; + brcmf_commonring_lock(commonring); + ret_ptr = brcmf_commonring_reserve_for_write(commonring); + if (!ret_ptr) { + brcmf_err("Failed to reserve space in commonring\n"); + brcmf_commonring_unlock(commonring); + return -ENOMEM; + } + h2d_mbdata = (struct msgbuf_h2d_mbdata *)ret_ptr; + memset(h2d_mbdata, 0, sizeof(*h2d_mbdata)); + + h2d_mbdata->msg.msgtype = MSGBUF_TYPE_H2D_MAILBOX_DATA; + h2d_mbdata->mbdata = cpu_to_le32(mbdata); + + err = brcmf_commonring_write_complete(commonring); + brcmf_commonring_unlock(commonring); + + return err; +} + static int brcmf_msgbuf_tx_ioctl(struct brcmf_pub *drvr, int ifidx, uint cmd, void *buf, uint len) @@ -719,6 +767,7 @@ static void brcmf_msgbuf_txflow(struct brcmf_msgbuf *msgbuf, u16 flowid) brcmf_flowring_qlen(flow, flowid)); break; } + skb_tx_timestamp(skb); skb_orphan(skb); if (brcmf_msgbuf_alloc_pktid(msgbuf->drvr->bus_if->dev, msgbuf->tx_pktids, skb, ETH_HLEN, @@ -807,8 +856,12 @@ static int brcmf_msgbuf_tx_queue_data(struct brcmf_pub *drvr, int ifidx, flowid = brcmf_flowring_lookup(flow, eh->h_dest, skb->priority, ifidx); if (flowid == BRCMF_FLOWRING_INVALID_ID) { flowid = brcmf_msgbuf_flowring_create(msgbuf, ifidx, skb); - if (flowid == BRCMF_FLOWRING_INVALID_ID) + if (flowid == BRCMF_FLOWRING_INVALID_ID) { return -ENOMEM; + } else { + brcmf_flowring_enqueue(flow, flowid, skb); + return 0; + } } queue_count = brcmf_flowring_enqueue(flow, flowid, skb); force = ((queue_count % BRCMF_MSGBUF_TRICKLE_TXWORKER_THRS) == 0); @@ -1141,7 +1194,8 @@ brcmf_msgbuf_process_rx_complete(struct brcmf_msgbuf *msgbuf, void *buf) { struct brcmf_pub *drvr = msgbuf->drvr; struct msgbuf_rx_complete *rx_complete; - struct sk_buff *skb; + struct sk_buff *skb, *cpskb = NULL; + struct ethhdr *eh; u16 data_offset; u16 buflen; u16 flags; @@ -1190,6 +1244,34 @@ brcmf_msgbuf_process_rx_complete(struct brcmf_msgbuf *msgbuf, void *buf) return; } + if (ifp->isap && ifp->fmac_pkt_fwd_en) { + eh = (struct ethhdr *)(skb->data); + skb_set_network_header(skb, sizeof(struct ethhdr)); + skb->protocol = eh->h_proto; + skb->priority = cfg80211_classify8021d(skb, NULL); + if (is_unicast_ether_addr(eh->h_dest)) { + if (brcmf_find_sta(ifp, eh->h_dest)) { + /* determine the priority */ + if (skb->priority == 0 || skb->priority > 7) { + skb->priority = + cfg80211_classify8021d(skb, + NULL); + } + brcmf_proto_tx_queue_data(ifp->drvr, + ifp->ifidx, skb); + return; + } + } else { + cpskb = pskb_copy(skb, GFP_ATOMIC); + if (cpskb) { + brcmf_proto_tx_queue_data(ifp->drvr, + ifp->ifidx, + cpskb); + } else { + brcmf_err("Unable to do skb copy\n"); + } + } + } skb->protocol = eth_type_trans(skb, ifp->ndev); brcmf_netif_rx(ifp, skb, false); } @@ -1277,6 +1359,21 @@ brcmf_msgbuf_process_flow_ring_delete_response(struct brcmf_msgbuf *msgbuf, brcmf_msgbuf_remove_flowring(msgbuf, flowid); } +static void +brcmf_msgbuf_process_d2h_mbdata(struct brcmf_msgbuf *msgbuf, + void *buf) +{ + struct msgbuf_d2h_mailbox_data *d2h_mbdata; + + d2h_mbdata = (struct msgbuf_d2h_mailbox_data *)buf; + + if (!d2h_mbdata) { + brcmf_err("d2h_mbdata is null\n"); + return; + } + + brcmf_pcie_handle_mb_data(msgbuf->drvr->bus_if, d2h_mbdata->mbdata); +} static void brcmf_msgbuf_process_msgtype(struct brcmf_msgbuf *msgbuf, void *buf) { @@ -1320,6 +1417,11 @@ static void brcmf_msgbuf_process_msgtype(struct brcmf_msgbuf *msgbuf, void *buf) brcmf_dbg(MSGBUF, "MSGBUF_TYPE_RX_CMPLT\n"); brcmf_msgbuf_process_rx_complete(msgbuf, buf); break; + case MSGBUF_TYPE_D2H_MAILBOX_DATA: + brcmf_dbg(MSGBUF, "MSGBUF_TYPE_D2H_MAILBOX_DATA\n"); + brcmf_msgbuf_process_d2h_mbdata(msgbuf, buf); + break; + default: bphy_err(drvr, "Unsupported msgtype %d\n", msg->msgtype); break; @@ -1396,9 +1498,27 @@ void brcmf_msgbuf_delete_flowring(struct brcmf_pub *drvr, u16 flowid) struct brcmf_msgbuf *msgbuf = (struct brcmf_msgbuf *)drvr->proto->pd; struct msgbuf_tx_flowring_delete_req *delete; struct brcmf_commonring *commonring; + struct brcmf_commonring *commonring_del = msgbuf->flowrings[flowid]; + struct brcmf_flowring *flow = msgbuf->flow; void *ret_ptr; u8 ifidx; int err; + int retry = BRCMF_MAX_TXSTATUS_WAIT_RETRIES; + + /* make sure it is not in txflow */ + brcmf_commonring_lock(commonring_del); + flow->rings[flowid]->status = RING_CLOSING; + brcmf_commonring_unlock(commonring_del); + + /* wait for commonring txflow finished */ + while (retry && atomic_read(&commonring_del->outstanding_tx)) { + usleep_range(5000, 10000); + retry--; + } + if (!retry) { + brcmf_err("timed out waiting for txstatus\n"); + atomic_set(&commonring_del->outstanding_tx, 0); + } /* no need to submit if firmware can not be reached */ if (drvr->bus_if->state != BRCMF_BUS_UP) { diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.h index 2e322edbb9070..ff0b5c1aa8741 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.h @@ -39,5 +39,6 @@ static inline int brcmf_proto_msgbuf_attach(struct brcmf_pub *drvr) } static inline void brcmf_proto_msgbuf_detach(struct brcmf_pub *drvr) {} #endif +int brcmf_msgbuf_tx_mbdata(struct brcmf_pub *drvr, u32 mbdata); #endif /* BRCMFMAC_MSGBUF_H */ diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/of.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/of.c index a7554265f95f8..a1d6c80b74d85 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/of.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/of.c @@ -19,7 +19,8 @@ void brcmf_of_probe(struct device *dev, enum brcmf_bus_type bus_type, struct device_node *root, *np = dev->of_node; int irq; u32 irqf; - u32 val; + u32 val32; + u16 val16; /* Set board-type to the first string of the machine compatible prop */ root = of_find_node_by_path("/"); @@ -47,8 +48,15 @@ void brcmf_of_probe(struct device *dev, enum brcmf_bus_type bus_type, !of_device_is_compatible(np, "brcm,bcm4329-fmac")) return; - if (of_property_read_u32(np, "brcm,drive-strength", &val) == 0) - sdio->drive_strength = val; + if (of_property_read_u32(np, "brcm,drive-strength", &val32) == 0) + sdio->drive_strength = val32; + + sdio->broken_sg_support = of_property_read_bool(np, + "brcm,broken_sg_support"); + if (of_property_read_u16(np, "brcm,sd_head_align", &val16) == 0) + sdio->sd_head_align = val16; + if (of_property_read_u16(np, "brcm,sd_sgentry_align", &val16) == 0) + sdio->sd_sgentry_align = val16; /* make sure there are interrupts defined in the node */ if (!of_find_property(np, "interrupts", NULL)) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c index ec6fc7a150a6a..ae1b81b7c0e91 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c @@ -63,6 +63,7 @@ #define P2P_AF_TX_MAX_RETRY 5 #define P2P_AF_MAX_WAIT_TIME msecs_to_jiffies(2000) #define P2P_INVALID_CHANNEL -1 +#define P2P_INVALID_CHANSPEC 0 #define P2P_CHANNEL_SYNC_RETRY 5 #define P2P_AF_FRM_SCAN_MAX_WAIT msecs_to_jiffies(450) #define P2P_DEFAULT_SLEEP_TIME_VSDB 200 @@ -231,12 +232,42 @@ static bool brcmf_p2p_is_pub_action(void *frame, u32 frame_len) if (pact_frm->category == P2P_PUB_AF_CATEGORY && pact_frm->action == P2P_PUB_AF_ACTION && pact_frm->oui_type == P2P_VER && - memcmp(pact_frm->oui, P2P_OUI, P2P_OUI_LEN) == 0) + memcmp(pact_frm->oui, WFA_OUI, P2P_OUI_LEN) == 0) return true; return false; } +/** + * brcmf_p2p_is_dpp_pub_action() - true if dpp public type frame. + * + * @frame: action frame data. + * @frame_len: length of action frame data. + * + * Determine if action frame is dpp public action type + */ +static bool brcmf_p2p_is_dpp_pub_action(void *frame, u32 frame_len) +{ + struct brcmf_p2p_pub_act_frame *pact_frm; + + if (!frame) + return false; + + pact_frm = (struct brcmf_p2p_pub_act_frame *)frame; + if (frame_len < sizeof(struct brcmf_p2p_pub_act_frame) - 1) + return false; + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 13, 0)) + if (pact_frm->category == WLAN_CATEGORY_PUBLIC && + pact_frm->action == WLAN_PUB_ACTION_VENDOR_SPECIFIC && + pact_frm->oui_type == DPP_VER && + memcmp(pact_frm->oui, WFA_OUI, TLV_OUI_LEN) == 0) + return true; +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 13, 0)) */ + + return false; +} + /** * brcmf_p2p_is_p2p_action() - true if p2p action type frame. * @@ -894,7 +925,8 @@ int brcmf_p2p_scan_prep(struct wiphy *wiphy, { struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); struct brcmf_p2p_info *p2p = &cfg->p2p; - int err; + int err = 0; + struct brcmu_chan ch; if (brcmf_p2p_scan_is_p2p_request(request)) { /* find my listen channel */ @@ -903,7 +935,12 @@ int brcmf_p2p_scan_prep(struct wiphy *wiphy, if (err < 0) return err; - p2p->afx_hdl.my_listen_chan = err; + ch.band = BRCMU_CHAN_BAND_2G; + ch.bw = BRCMU_CHAN_BW_20; + ch.sb = BRCMU_CHAN_SB_NONE; + ch.chnum = err; + p2p->cfg->d11inf.encchspec(&ch); + p2p->afx_hdl.my_listen_chan = ch.chspec; clear_bit(BRCMF_P2P_STATUS_GO_NEG_PHASE, &p2p->status); brcmf_dbg(INFO, "P2P: GO_NEG_PHASE status cleared\n"); @@ -912,10 +949,14 @@ int brcmf_p2p_scan_prep(struct wiphy *wiphy, if (err) return err; + vif = p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif; + /* override .run_escan() callback. */ cfg->escan_info.run = brcmf_p2p_run_escan; } - return 0; + err = brcmf_vif_set_mgmt_ie(vif, BRCMF_VNDR_IE_PRBREQ_FLAG, + request->ie, request->ie_len); + return err; } @@ -923,16 +964,15 @@ int brcmf_p2p_scan_prep(struct wiphy *wiphy, * brcmf_p2p_discover_listen() - set firmware to discover listen state. * * @p2p: p2p device. - * @channel: channel nr for discover listen. + * @chspec: chspec for discover listen. * @duration: time in ms to stay on channel. * */ static s32 -brcmf_p2p_discover_listen(struct brcmf_p2p_info *p2p, u16 channel, u32 duration) +brcmf_p2p_discover_listen(struct brcmf_p2p_info *p2p, u16 chspec, u32 duration) { struct brcmf_pub *drvr = p2p->cfg->pub; struct brcmf_cfg80211_vif *vif; - struct brcmu_chan ch; s32 err = 0; vif = p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif; @@ -948,11 +988,8 @@ brcmf_p2p_discover_listen(struct brcmf_p2p_info *p2p, u16 channel, u32 duration) goto exit; } - ch.chnum = channel; - ch.bw = BRCMU_CHAN_BW_20; - p2p->cfg->d11inf.encchspec(&ch); err = brcmf_p2p_set_discover_state(vif->ifp, WL_P2P_DISC_ST_LISTEN, - ch.chspec, (u16)duration); + chspec, (u16)duration); if (!err) { set_bit(BRCMF_P2P_STATUS_DISCOVER_LISTEN, &p2p->status); p2p->remain_on_channel_cookie++; @@ -978,19 +1015,17 @@ int brcmf_p2p_remain_on_channel(struct wiphy *wiphy, struct wireless_dev *wdev, struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); struct brcmf_p2p_info *p2p = &cfg->p2p; s32 err; - u16 channel_nr; - - channel_nr = ieee80211_frequency_to_channel(channel->center_freq); - brcmf_dbg(TRACE, "Enter, channel: %d, duration ms (%d)\n", channel_nr, - duration); err = brcmf_p2p_enable_discovery(p2p); if (err) goto exit; - err = brcmf_p2p_discover_listen(p2p, channel_nr, duration); + err = brcmf_p2p_discover_listen(p2p, + channel_to_chanspec(&cfg->d11inf, channel), duration); if (err) goto exit; + p2p->remin_on_channel_wdev = wdev; + memcpy(&p2p->remain_on_channel, channel, sizeof(*channel)); *cookie = p2p->remain_on_channel_cookie; cfg80211_ready_on_channel(wdev, *cookie, channel, duration, GFP_KERNEL); @@ -1014,6 +1049,7 @@ int brcmf_p2p_notify_listen_complete(struct brcmf_if *ifp, { struct brcmf_cfg80211_info *cfg = ifp->drvr->config; struct brcmf_p2p_info *p2p = &cfg->p2p; + struct wireless_dev *wdev = p2p->remin_on_channel_wdev; brcmf_dbg(TRACE, "Enter\n"); if (test_and_clear_bit(BRCMF_P2P_STATUS_DISCOVER_LISTEN, @@ -1026,10 +1062,16 @@ int brcmf_p2p_notify_listen_complete(struct brcmf_if *ifp, complete(&p2p->wait_next_af); } - cfg80211_remain_on_channel_expired(&ifp->vif->wdev, + wdev = p2p->remin_on_channel_wdev ? + p2p->remin_on_channel_wdev : + &ifp->vif->wdev; + + cfg80211_remain_on_channel_expired(wdev, p2p->remain_on_channel_cookie, &p2p->remain_on_channel, GFP_KERNEL); + p2p->remin_on_channel_wdev = NULL; + } return 0; } @@ -1054,12 +1096,12 @@ void brcmf_p2p_cancel_remain_on_channel(struct brcmf_if *ifp) * brcmf_p2p_act_frm_search() - search function for action frame. * * @p2p: p2p device. - * @channel: channel on which action frame is to be trasmitted. + * @chspec: chspec on which action frame is to be trasmitted. * * search function to reach at common channel to send action frame. When * channel is 0 then all social channels will be used to send af */ -static s32 brcmf_p2p_act_frm_search(struct brcmf_p2p_info *p2p, u16 channel) +static s32 brcmf_p2p_act_frm_search(struct brcmf_p2p_info *p2p, u16 chspec) { struct brcmf_pub *drvr = p2p->cfg->pub; s32 err; @@ -1070,7 +1112,7 @@ static s32 brcmf_p2p_act_frm_search(struct brcmf_p2p_info *p2p, u16 channel) brcmf_dbg(TRACE, "Enter\n"); - if (channel) + if (chspec) channel_cnt = AF_PEER_SEARCH_CNT; else channel_cnt = SOCIAL_CHAN_CNT; @@ -1081,14 +1123,13 @@ static s32 brcmf_p2p_act_frm_search(struct brcmf_p2p_info *p2p, u16 channel) err = -ENOMEM; goto exit; } - ch.bw = BRCMU_CHAN_BW_20; - if (channel) { - ch.chnum = channel; - p2p->cfg->d11inf.encchspec(&ch); - /* insert same channel to the chan_list */ + + if (chspec) { for (i = 0; i < channel_cnt; i++) - default_chan_list[i] = ch.chspec; + default_chan_list[i] = chspec; } else { + ch.band = BRCMU_CHAN_BAND_2G; + ch.bw = BRCMU_CHAN_BW_20; ch.chnum = SOCIAL_CHAN_1; p2p->cfg->d11inf.encchspec(&ch); default_chan_list[0] = ch.chspec; @@ -1147,7 +1188,7 @@ static void brcmf_p2p_afx_handler(struct work_struct *work) * @p2p: p2p device info struct. * */ -static s32 brcmf_p2p_af_searching_channel(struct brcmf_p2p_info *p2p) +static u16 brcmf_p2p_af_searching_channel(struct brcmf_p2p_info *p2p) { struct afx_hdl *afx_hdl = &p2p->afx_hdl; struct brcmf_cfg80211_vif *pri_vif; @@ -1160,14 +1201,14 @@ static s32 brcmf_p2p_af_searching_channel(struct brcmf_p2p_info *p2p) reinit_completion(&afx_hdl->act_frm_scan); set_bit(BRCMF_P2P_STATUS_FINDING_COMMON_CHANNEL, &p2p->status); afx_hdl->is_active = true; - afx_hdl->peer_chan = P2P_INVALID_CHANNEL; + afx_hdl->peer_chan = P2P_INVALID_CHANSPEC; /* Loop to wait until we find a peer's channel or the * pending action frame tx is cancelled. */ retry = 0; while ((retry < P2P_CHANNEL_SYNC_RETRY) && - (afx_hdl->peer_chan == P2P_INVALID_CHANNEL)) { + (afx_hdl->peer_chan == P2P_INVALID_CHANSPEC)) { afx_hdl->is_listen = false; brcmf_dbg(TRACE, "Scheduling action frame for sending.. (%d)\n", retry); @@ -1175,13 +1216,13 @@ static s32 brcmf_p2p_af_searching_channel(struct brcmf_p2p_info *p2p) schedule_work(&afx_hdl->afx_work); wait_for_completion_timeout(&afx_hdl->act_frm_scan, P2P_AF_FRM_SCAN_MAX_WAIT); - if ((afx_hdl->peer_chan != P2P_INVALID_CHANNEL) || + if ((afx_hdl->peer_chan != P2P_INVALID_CHANSPEC) || (!test_bit(BRCMF_P2P_STATUS_FINDING_COMMON_CHANNEL, &p2p->status))) break; if (afx_hdl->my_listen_chan) { - brcmf_dbg(TRACE, "Scheduling listen peer, channel=%d\n", + brcmf_dbg(TRACE, "Scheduling listen peer, chanspec=0x%04x\n", afx_hdl->my_listen_chan); /* listen on my listen channel */ afx_hdl->is_listen = true; @@ -1189,7 +1230,7 @@ static s32 brcmf_p2p_af_searching_channel(struct brcmf_p2p_info *p2p) wait_for_completion_timeout(&afx_hdl->act_frm_scan, P2P_AF_FRM_SCAN_MAX_WAIT); } - if ((afx_hdl->peer_chan != P2P_INVALID_CHANNEL) || + if ((afx_hdl->peer_chan != P2P_INVALID_CHANSPEC) || (!test_bit(BRCMF_P2P_STATUS_FINDING_COMMON_CHANNEL, &p2p->status))) break; @@ -1203,7 +1244,7 @@ static s32 brcmf_p2p_af_searching_channel(struct brcmf_p2p_info *p2p) msleep(P2P_DEFAULT_SLEEP_TIME_VSDB); } - brcmf_dbg(TRACE, "Completed search/listen peer_chan=%d\n", + brcmf_dbg(TRACE, "Completed search/listen peer_chan=0x%4x\n", afx_hdl->peer_chan); afx_hdl->is_active = false; @@ -1226,7 +1267,6 @@ bool brcmf_p2p_scan_finding_common_channel(struct brcmf_cfg80211_info *cfg, { struct brcmf_p2p_info *p2p = &cfg->p2p; struct afx_hdl *afx_hdl = &p2p->afx_hdl; - struct brcmu_chan ch; u8 *ie; s32 err; u8 p2p_dev_addr[ETH_ALEN]; @@ -1236,7 +1276,7 @@ bool brcmf_p2p_scan_finding_common_channel(struct brcmf_cfg80211_info *cfg, if (bi == NULL) { brcmf_dbg(TRACE, "ACTION FRAME SCAN Done\n"); - if (afx_hdl->peer_chan == P2P_INVALID_CHANNEL) + if (afx_hdl->peer_chan == P2P_INVALID_CHANSPEC) complete(&afx_hdl->act_frm_scan); return true; } @@ -1252,13 +1292,8 @@ bool brcmf_p2p_scan_finding_common_channel(struct brcmf_cfg80211_info *cfg, p2p_dev_addr, sizeof(p2p_dev_addr)); if ((err >= 0) && (ether_addr_equal(p2p_dev_addr, afx_hdl->tx_dst_addr))) { - if (!bi->ctl_ch) { - ch.chspec = le16_to_cpu(bi->chanspec); - cfg->d11inf.decchspec(&ch); - bi->ctl_ch = ch.control_ch_num; - } - afx_hdl->peer_chan = bi->ctl_ch; - brcmf_dbg(TRACE, "ACTION FRAME SCAN : Peer %pM found, channel : %d\n", + afx_hdl->peer_chan = le16_to_cpu(bi->chanspec); + brcmf_dbg(TRACE, "ACTION FRAME SCAN : Peer %pM found, chanspec : 0x%04x\n", afx_hdl->tx_dst_addr, afx_hdl->peer_chan); complete(&afx_hdl->act_frm_scan); } @@ -1281,6 +1316,10 @@ static s32 brcmf_p2p_abort_action_frame(struct brcmf_cfg80211_info *cfg) brcmf_dbg(TRACE, "Enter\n"); vif = p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif; + + if (!vif) + vif = p2p->bss_idx[P2PAPI_BSSCFG_PRIMARY].vif; + err = brcmf_fil_bsscfg_data_set(vif->ifp, "actframe_abort", &int_val, sizeof(s32)); if (err) @@ -1426,8 +1465,8 @@ int brcmf_p2p_notify_action_frame_rx(struct brcmf_if *ifp, if (test_bit(BRCMF_P2P_STATUS_FINDING_COMMON_CHANNEL, &p2p->status) && (ether_addr_equal(afx_hdl->tx_dst_addr, e->addr))) { - afx_hdl->peer_chan = ch.control_ch_num; - brcmf_dbg(INFO, "GON request: Peer found, channel=%d\n", + afx_hdl->peer_chan = be16_to_cpu(rxframe->chanspec); + brcmf_dbg(INFO, "GON request: Peer found, chanspec=0x%04x\n", afx_hdl->peer_chan); complete(&afx_hdl->act_frm_scan); } @@ -1470,9 +1509,7 @@ int brcmf_p2p_notify_action_frame_rx(struct brcmf_if *ifp, mgmt_frame_len += offsetof(struct ieee80211_mgmt, u); freq = ieee80211_channel_to_frequency(ch.control_ch_num, - ch.band == BRCMU_CHAN_BAND_2G ? - NL80211_BAND_2GHZ : - NL80211_BAND_5GHZ); + BRCMU_CHAN_BAND_TO_NL80211(ch.band)); wdev = &ifp->vif->wdev; cfg80211_rx_mgmt(wdev, freq, 0, (u8 *)mgmt_frame, mgmt_frame_len, 0); @@ -1531,6 +1568,7 @@ int brcmf_p2p_notify_action_tx_complete(struct brcmf_if *ifp, * * @p2p: p2p info struct for vif. * @af_params: action frame data/info. + * @vif: vif to send * * Send an action frame immediately without doing channel synchronization. * @@ -1539,12 +1577,17 @@ int brcmf_p2p_notify_action_tx_complete(struct brcmf_if *ifp, * frame is transmitted. */ static s32 brcmf_p2p_tx_action_frame(struct brcmf_p2p_info *p2p, - struct brcmf_fil_af_params_le *af_params) + struct brcmf_fil_af_params_le *af_params, + struct brcmf_cfg80211_vif *vif + ) { struct brcmf_pub *drvr = p2p->cfg->pub; - struct brcmf_cfg80211_vif *vif; - struct brcmf_p2p_action_frame *p2p_af; s32 err = 0; + struct brcmf_fil_action_frame_le *action_frame; + u16 action_frame_len; + + action_frame = &af_params->action_frame; + action_frame_len = le16_to_cpu(action_frame->len); brcmf_dbg(TRACE, "Enter\n"); @@ -1552,13 +1595,6 @@ static s32 brcmf_p2p_tx_action_frame(struct brcmf_p2p_info *p2p, clear_bit(BRCMF_P2P_STATUS_ACTION_TX_COMPLETED, &p2p->status); clear_bit(BRCMF_P2P_STATUS_ACTION_TX_NOACK, &p2p->status); - /* check if it is a p2p_presence response */ - p2p_af = (struct brcmf_p2p_action_frame *)af_params->action_frame.data; - if (p2p_af->subtype == P2P_AF_PRESENCE_RSP) - vif = p2p->bss_idx[P2PAPI_BSSCFG_CONNECTION].vif; - else - vif = p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif; - err = brcmf_fil_bsscfg_data_set(vif->ifp, "actframe", af_params, sizeof(*af_params)); if (err) { @@ -1714,10 +1750,14 @@ static bool brcmf_p2p_check_dwell_overflow(u32 requested_dwell, * @cfg: driver private data for cfg80211 interface. * @ndev: net device to transmit on. * @af_params: configuration data for action frame. + * @vif: virtual interface to send */ bool brcmf_p2p_send_action_frame(struct brcmf_cfg80211_info *cfg, struct net_device *ndev, - struct brcmf_fil_af_params_le *af_params) + struct brcmf_fil_af_params_le *af_params, + struct brcmf_cfg80211_vif *vif, + struct ieee80211_channel *peer_listen_chan + ) { struct brcmf_p2p_info *p2p = &cfg->p2p; struct brcmf_if *ifp = netdev_priv(ndev); @@ -1725,6 +1765,7 @@ bool brcmf_p2p_send_action_frame(struct brcmf_cfg80211_info *cfg, struct brcmf_config_af_params config_af_params; struct afx_hdl *afx_hdl = &p2p->afx_hdl; struct brcmf_pub *drvr = cfg->pub; + struct brcmu_chan ch; u16 action_frame_len; bool ack = false; u8 category; @@ -1789,7 +1830,9 @@ bool brcmf_p2p_send_action_frame(struct brcmf_cfg80211_info *cfg, goto exit; } } else if (brcmf_p2p_is_p2p_action(action_frame->data, - action_frame_len)) { + action_frame_len) || + brcmf_p2p_is_dpp_pub_action(action_frame->data, + action_frame_len)) { /* do not configure anything. it will be */ /* sent with a default configuration */ } else { @@ -1826,12 +1869,13 @@ bool brcmf_p2p_send_action_frame(struct brcmf_cfg80211_info *cfg, /* validate channel and p2p ies */ if (config_af_params.search_channel && IS_P2P_SOCIAL_CHANNEL(le32_to_cpu(af_params->channel)) && + p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif && p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif->saved_ie.probe_req_ie_len) { afx_hdl = &p2p->afx_hdl; - afx_hdl->peer_listen_chan = le32_to_cpu(af_params->channel); + afx_hdl->peer_listen_chan = channel_to_chanspec(&cfg->d11inf, peer_listen_chan); if (brcmf_p2p_af_searching_channel(p2p) == - P2P_INVALID_CHANNEL) { + P2P_INVALID_CHANSPEC) { bphy_err(drvr, "Couldn't find peer's channel.\n"); goto exit; } @@ -1844,7 +1888,9 @@ bool brcmf_p2p_send_action_frame(struct brcmf_cfg80211_info *cfg, brcmf_notify_escan_complete(cfg, ifp, true, true); /* update channel */ - af_params->channel = cpu_to_le32(afx_hdl->peer_chan); + ch.chspec = afx_hdl->peer_chan; + cfg->d11inf.decchspec(&ch); + af_params->channel = cpu_to_le32(ch.control_ch_num); } dwell_jiffies = jiffies; dwell_overflow = brcmf_p2p_check_dwell_overflow(requested_dwell, @@ -1857,7 +1903,7 @@ bool brcmf_p2p_send_action_frame(struct brcmf_cfg80211_info *cfg, if (af_params->channel) msleep(P2P_AF_RETRY_DELAY_TIME); - ack = !brcmf_p2p_tx_action_frame(p2p, af_params); + ack = !brcmf_p2p_tx_action_frame(p2p, af_params, vif); tx_retry++; dwell_overflow = brcmf_p2p_check_dwell_overflow(requested_dwell, dwell_jiffies); @@ -1876,9 +1922,11 @@ bool brcmf_p2p_send_action_frame(struct brcmf_cfg80211_info *cfg, * not keep the dwell time, go to listen state again to get next action * response frame. */ + ch.chspec = afx_hdl->my_listen_chan; + cfg->d11inf.decchspec(&ch); if (ack && config_af_params.extra_listen && !p2p->block_gon_req_tx && test_bit(BRCMF_P2P_STATUS_WAITING_NEXT_ACT_FRAME, &p2p->status) && - p2p->af_sent_channel == afx_hdl->my_listen_chan) { + p2p->af_sent_channel == ch.control_ch_num) { delta_ms = jiffies_to_msecs(jiffies - p2p->af_tx_sent_jiffies); if (le32_to_cpu(af_params->dwell_time) > delta_ms) extra_listen_time = le32_to_cpu(af_params->dwell_time) - @@ -1893,7 +1941,7 @@ bool brcmf_p2p_send_action_frame(struct brcmf_cfg80211_info *cfg, extra_listen_time); extra_listen_time += 100; if (!brcmf_p2p_discover_listen(p2p, - p2p->af_sent_channel, + afx_hdl->my_listen_chan, extra_listen_time)) { unsigned long duration; @@ -1958,8 +2006,8 @@ s32 brcmf_p2p_notify_rx_mgmt_p2p_probereq(struct brcmf_if *ifp, if (test_bit(BRCMF_P2P_STATUS_FINDING_COMMON_CHANNEL, &p2p->status) && (ether_addr_equal(afx_hdl->tx_dst_addr, e->addr))) { - afx_hdl->peer_chan = ch.control_ch_num; - brcmf_dbg(INFO, "PROBE REQUEST: Peer found, channel=%d\n", + afx_hdl->peer_chan = be16_to_cpu(rxframe->chanspec); + brcmf_dbg(INFO, "PROBE REQUEST: Peer found, chanspec=0x%04x\n", afx_hdl->peer_chan); complete(&afx_hdl->act_frm_scan); } @@ -1984,9 +2032,7 @@ s32 brcmf_p2p_notify_rx_mgmt_p2p_probereq(struct brcmf_if *ifp, mgmt_frame = (u8 *)(rxframe + 1); mgmt_frame_len = e->datalen - sizeof(*rxframe); freq = ieee80211_channel_to_frequency(ch.control_ch_num, - ch.band == BRCMU_CHAN_BAND_2G ? - NL80211_BAND_2GHZ : - NL80211_BAND_5GHZ); + BRCMU_CHAN_BAND_TO_NL80211(ch.band)); cfg80211_rx_mgmt(&vif->wdev, freq, 0, mgmt_frame, mgmt_frame_len, 0); @@ -2030,6 +2076,7 @@ static void brcmf_p2p_get_current_chanspec(struct brcmf_p2p_info *p2p, } } /* Use default channel for P2P */ + ch.band = BRCMU_CHAN_BAND_2G; ch.chnum = BRCMF_P2P_TEMP_CHAN; ch.bw = BRCMU_CHAN_BW_20; p2p->cfg->d11inf.encchspec(&ch); @@ -2335,7 +2382,9 @@ struct wireless_dev *brcmf_p2p_add_vif(struct wiphy *wiphy, const char *name, } strncpy(ifp->ndev->name, name, sizeof(ifp->ndev->name) - 1); +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 17, 0)) ifp->ndev->name_assign_type = name_assign_type; +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 17, 0)) */ err = brcmf_net_attach(ifp, true); if (err) { bphy_err(drvr, "Registering netdevice failed\n"); @@ -2424,8 +2473,12 @@ int brcmf_p2p_del_vif(struct wiphy *wiphy, struct wireless_dev *wdev) brcmf_remove_interface(vif->ifp, true); brcmf_cfg80211_arm_vif_event(cfg, NULL); - if (iftype != NL80211_IFTYPE_P2P_DEVICE) - p2p->bss_idx[P2PAPI_BSSCFG_CONNECTION].vif = NULL; + if (iftype != NL80211_IFTYPE_P2P_DEVICE) { + if (vif == p2p->bss_idx[P2PAPI_BSSCFG_CONNECTION].vif) + p2p->bss_idx[P2PAPI_BSSCFG_CONNECTION].vif = NULL; + if (vif == p2p->bss_idx[P2PAPI_BSSCFG_CONNECTION2].vif) + p2p->bss_idx[P2PAPI_BSSCFG_CONNECTION2].vif = NULL; + } return err; } @@ -2504,6 +2557,7 @@ s32 brcmf_p2p_attach(struct brcmf_cfg80211_info *cfg, bool p2pdev_forced) pri_ifp = brcmf_get_ifp(cfg->pub, 0); p2p->bss_idx[P2PAPI_BSSCFG_PRIMARY].vif = pri_ifp->vif; + init_completion(&p2p->send_af_done); if (p2pdev_forced) { err_ptr = brcmf_p2p_create_p2pdev(p2p, NULL, NULL); diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.h index d2ecee565bf2e..d71709aae7abc 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.h @@ -87,7 +87,7 @@ struct afx_hdl { struct work_struct afx_work; struct completion act_frm_scan; bool is_active; - s32 peer_chan; + u16 peer_chan; bool is_listen; u16 my_listen_chan; u16 peer_listen_chan; @@ -138,6 +138,7 @@ struct brcmf_p2p_info { bool block_gon_req_tx; bool p2pdev_dynamically; bool wait_for_offchan_complete; + struct wireless_dev *remin_on_channel_wdev; }; s32 brcmf_p2p_attach(struct brcmf_cfg80211_info *cfg, bool p2pdev_forced); @@ -170,7 +171,9 @@ int brcmf_p2p_notify_action_tx_complete(struct brcmf_if *ifp, void *data); bool brcmf_p2p_send_action_frame(struct brcmf_cfg80211_info *cfg, struct net_device *ndev, - struct brcmf_fil_af_params_le *af_params); + struct brcmf_fil_af_params_le *af_params, + struct brcmf_cfg80211_vif *vif, + struct ieee80211_channel *peer_listen_chan); bool brcmf_p2p_scan_finding_common_channel(struct brcmf_cfg80211_info *cfg, struct brcmf_bss_info_le *bi); s32 brcmf_p2p_notify_rx_mgmt_p2p_probereq(struct brcmf_if *ifp, diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c index 61febc9bfa14a..66e86a2aeeb00 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c @@ -12,7 +12,11 @@ #include #include #include -#include +#include +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0)) +#include +#endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(4, 11, 0)) */ +#include #include #include @@ -39,6 +43,8 @@ #include "chip.h" #include "core.h" #include "common.h" +#include "cfg80211.h" +#include "trxhdr.h" enum brcmf_pcie_state { @@ -49,16 +55,19 @@ enum brcmf_pcie_state { BRCMF_FW_DEF(43602, "brcmfmac43602-pcie"); BRCMF_FW_DEF(4350, "brcmfmac4350-pcie"); BRCMF_FW_DEF(4350C, "brcmfmac4350c2-pcie"); -BRCMF_FW_DEF(4356, "brcmfmac4356-pcie"); -BRCMF_FW_DEF(43570, "brcmfmac43570-pcie"); +CY_FW_DEF(4356, "cyfmac4356-pcie"); +CY_FW_DEF(43570, "cyfmac43570-pcie"); BRCMF_FW_DEF(4358, "brcmfmac4358-pcie"); -BRCMF_FW_DEF(4359, "brcmfmac4359-pcie"); +CY_FW_DEF(4359, "cyfmac4359-pcie"); BRCMF_FW_DEF(4364, "brcmfmac4364-pcie"); BRCMF_FW_DEF(4365B, "brcmfmac4365b-pcie"); BRCMF_FW_DEF(4365C, "brcmfmac4365c-pcie"); BRCMF_FW_DEF(4366B, "brcmfmac4366b-pcie"); BRCMF_FW_DEF(4366C, "brcmfmac4366c-pcie"); BRCMF_FW_DEF(4371, "brcmfmac4371-pcie"); +CY_FW_DEF(4355, "cyfmac54591-pcie"); +CY_FW_TRXSE_DEF(55560, "cyfmac55560-pcie"); +CY_FW_DEF(4373, "cyfmac4373-pcie"); static const struct brcmf_firmware_mapping brcmf_pcie_fwnames[] = { BRCMF_FW_ENTRY(BRCM_CC_43602_CHIP_ID, 0xFFFFFFFF, 43602), @@ -79,8 +88,15 @@ static const struct brcmf_firmware_mapping brcmf_pcie_fwnames[] = { BRCMF_FW_ENTRY(BRCM_CC_4366_CHIP_ID, 0xFFFFFFF0, 4366C), BRCMF_FW_ENTRY(BRCM_CC_43664_CHIP_ID, 0xFFFFFFF0, 4366C), BRCMF_FW_ENTRY(BRCM_CC_4371_CHIP_ID, 0xFFFFFFFF, 4371), + BRCMF_FW_ENTRY(CY_CC_89459_CHIP_ID, 0xFFFFFFFF, 4355), + BRCMF_FW_ENTRY(CY_CC_55560_CHIP_ID, 0xFFFFFFFF, 55560), + BRCMF_FW_ENTRY(CY_CC_4373_CHIP_ID, 0xFFFFFFFF, 4373), }; +#define BRCMF_PCIE_REV_GE64(dev) (brcmf_chip_get_core((dev)->ci, \ + BCMA_CORE_PCIE2)->rev >= 64) + +#define BRCMF_PCIE_READ_SHARED_TIMEOUT 5000 /* msec */ #define BRCMF_PCIE_FW_UP_TIMEOUT 5000 /* msec */ #define BRCMF_PCIE_REG_MAP_SIZE (32 * 1024) @@ -91,7 +107,8 @@ static const struct brcmf_firmware_mapping brcmf_pcie_fwnames[] = { #define BRCMF_PCIE_BAR0_WRAPPERBASE 0x70 #define BRCMF_PCIE_BAR0_WRAPBASE_DMP_OFFSET 0x1000 -#define BRCMF_PCIE_BARO_PCIE_ENUM_OFFSET 0x2000 +#define BRCMF_PCIE_BAR0_PCIE_ENUM_OFFSET 0x2000 +#define BRCMF_CYW55560_PCIE_BAR0_PCIE_ENUM_OFFSET 0x3000 #define BRCMF_PCIE_ARMCR4REG_BANKIDX 0x40 #define BRCMF_PCIE_ARMCR4REG_BANKPDA 0x4C @@ -103,12 +120,16 @@ static const struct brcmf_firmware_mapping brcmf_pcie_fwnames[] = { #define BRCMF_PCIE_REG_LINK_STATUS_CTRL 0xBC #define BRCMF_PCIE_PCIE2REG_INTMASK 0x24 -#define BRCMF_PCIE_PCIE2REG_MAILBOXINT 0x48 -#define BRCMF_PCIE_PCIE2REG_MAILBOXMASK 0x4C +#define BRCMF_PCIE_PCIE2REG_MAILBOXINT(dev) (BRCMF_PCIE_REV_GE64(dev) ? \ + 0xC30 : 0x48) +#define BRCMF_PCIE_PCIE2REG_MAILBOXMASK(dev) (BRCMF_PCIE_REV_GE64(dev) ? \ + 0xC34 : 0x4C) #define BRCMF_PCIE_PCIE2REG_CONFIGADDR 0x120 #define BRCMF_PCIE_PCIE2REG_CONFIGDATA 0x124 #define BRCMF_PCIE_PCIE2REG_H2D_MAILBOX_0 0x140 #define BRCMF_PCIE_PCIE2REG_H2D_MAILBOX_1 0x144 +#define BRCMF_PCIE_PCIE2REG_DAR_D2H_MSG_0 0xA80 +#define BRCMF_PCIE_PCIE2REG_DAR_H2D_MSG_0 0xA90 #define BRCMF_PCIE2_INTA 0x01 #define BRCMF_PCIE2_INTB 0x02 @@ -120,30 +141,69 @@ static const struct brcmf_firmware_mapping brcmf_pcie_fwnames[] = { #define BRCMF_PCIE_MB_INT_FN0_0 0x0100 #define BRCMF_PCIE_MB_INT_FN0_1 0x0200 -#define BRCMF_PCIE_MB_INT_D2H0_DB0 0x10000 -#define BRCMF_PCIE_MB_INT_D2H0_DB1 0x20000 -#define BRCMF_PCIE_MB_INT_D2H1_DB0 0x40000 -#define BRCMF_PCIE_MB_INT_D2H1_DB1 0x80000 -#define BRCMF_PCIE_MB_INT_D2H2_DB0 0x100000 -#define BRCMF_PCIE_MB_INT_D2H2_DB1 0x200000 -#define BRCMF_PCIE_MB_INT_D2H3_DB0 0x400000 -#define BRCMF_PCIE_MB_INT_D2H3_DB1 0x800000 - -#define BRCMF_PCIE_MB_INT_D2H_DB (BRCMF_PCIE_MB_INT_D2H0_DB0 | \ - BRCMF_PCIE_MB_INT_D2H0_DB1 | \ - BRCMF_PCIE_MB_INT_D2H1_DB0 | \ - BRCMF_PCIE_MB_INT_D2H1_DB1 | \ - BRCMF_PCIE_MB_INT_D2H2_DB0 | \ - BRCMF_PCIE_MB_INT_D2H2_DB1 | \ - BRCMF_PCIE_MB_INT_D2H3_DB0 | \ - BRCMF_PCIE_MB_INT_D2H3_DB1) - +#define BRCMF_PCIE_MB_INT_D2H0_DB0 0x10000 +#define BRCMF_PCIE_MB_INT_D2H0_DB1 0x20000 +#define BRCMF_PCIE_MB_INT_D2H1_DB0 0x40000 +#define BRCMF_PCIE_MB_INT_D2H1_DB1 0x80000 +#define BRCMF_PCIE_MB_INT_D2H2_DB0 0x100000 +#define BRCMF_PCIE_MB_INT_D2H2_DB1 0x200000 +#define BRCMF_PCIE_MB_INT_D2H3_DB0 0x400000 +#define BRCMF_PCIE_MB_INT_D2H3_DB1 0x800000 +#define BRCMF_PCIE_MB_INT_D2H_DB_ALL (BRCMF_PCIE_MB_INT_D2H0_DB0 | \ + BRCMF_PCIE_MB_INT_D2H0_DB1 | \ + BRCMF_PCIE_MB_INT_D2H1_DB0 | \ + BRCMF_PCIE_MB_INT_D2H1_DB1 | \ + BRCMF_PCIE_MB_INT_D2H2_DB0 | \ + BRCMF_PCIE_MB_INT_D2H2_DB1 | \ + BRCMF_PCIE_MB_INT_D2H3_DB0 | \ + BRCMF_PCIE_MB_INT_D2H3_DB1) + +#define BRCMF_PCIE_MB_INT_D2H0_DB0_GE64 0x0001 +#define BRCMF_PCIE_MB_INT_D2H0_DB1_GE64 0x0002 +#define BRCMF_PCIE_MB_INT_D2H1_DB0_GE64 0x0004 +#define BRCMF_PCIE_MB_INT_D2H1_DB1_GE64 0x0008 +#define BRCMF_PCIE_MB_INT_D2H2_DB0_GE64 0x0010 +#define BRCMF_PCIE_MB_INT_D2H2_DB1_GE64 0x0020 +#define BRCMF_PCIE_MB_INT_D2H3_DB0_GE64 0x0040 +#define BRCMF_PCIE_MB_INT_D2H3_DB1_GE64 0x0080 +#define BRCMF_PCIE_MB_INT_D2H4_DB0_GE64 0x0100 +#define BRCMF_PCIE_MB_INT_D2H4_DB1_GE64 0x0200 +#define BRCMF_PCIE_MB_INT_D2H5_DB0_GE64 0x0400 +#define BRCMF_PCIE_MB_INT_D2H5_DB1_GE64 0x0800 +#define BRCMF_PCIE_MB_INT_D2H6_DB0_GE64 0x1000 +#define BRCMF_PCIE_MB_INT_D2H6_DB1_GE64 0x2000 +#define BRCMF_PCIE_MB_INT_D2H7_DB0_GE64 0x4000 +#define BRCMF_PCIE_MB_INT_D2H7_DB1_GE64 0x8000 +#define BRCMF_PCIE_MB_INT_D2H_DB_ALL_GE64 \ + (BRCMF_PCIE_MB_INT_D2H0_DB0_GE64 | \ + BRCMF_PCIE_MB_INT_D2H0_DB1_GE64 | \ + BRCMF_PCIE_MB_INT_D2H1_DB0_GE64 | \ + BRCMF_PCIE_MB_INT_D2H1_DB1_GE64 | \ + BRCMF_PCIE_MB_INT_D2H2_DB0_GE64 | \ + BRCMF_PCIE_MB_INT_D2H2_DB1_GE64 | \ + BRCMF_PCIE_MB_INT_D2H3_DB0_GE64 | \ + BRCMF_PCIE_MB_INT_D2H3_DB1_GE64 | \ + BRCMF_PCIE_MB_INT_D2H4_DB0_GE64 | \ + BRCMF_PCIE_MB_INT_D2H4_DB1_GE64 | \ + BRCMF_PCIE_MB_INT_D2H5_DB0_GE64 | \ + BRCMF_PCIE_MB_INT_D2H5_DB1_GE64 | \ + BRCMF_PCIE_MB_INT_D2H6_DB0_GE64 | \ + BRCMF_PCIE_MB_INT_D2H6_DB1_GE64 | \ + BRCMF_PCIE_MB_INT_D2H7_DB0_GE64 | \ + BRCMF_PCIE_MB_INT_D2H7_DB1_GE64) + +#define BRCMF_PCIE_MB_INT_D2H_DB(dev) (BRCMF_PCIE_REV_GE64(dev) ? \ + BRCMF_PCIE_MB_INT_D2H_DB_ALL_GE64 : \ + BRCMF_PCIE_MB_INT_D2H_DB_ALL) + +#define BRCMF_PCIE_SHARED_VERSION_6 6 #define BRCMF_PCIE_SHARED_VERSION_7 7 #define BRCMF_PCIE_MIN_SHARED_VERSION 5 #define BRCMF_PCIE_MAX_SHARED_VERSION BRCMF_PCIE_SHARED_VERSION_7 #define BRCMF_PCIE_SHARED_VERSION_MASK 0x00FF #define BRCMF_PCIE_SHARED_DMA_INDEX 0x10000 #define BRCMF_PCIE_SHARED_DMA_2B_IDX 0x100000 +#define BRCMF_PCIE_SHARED_USE_MAILBOX 0x2000000 #define BRCMF_PCIE_SHARED_HOSTRDY_DB1 0x10000000 #define BRCMF_PCIE_FLAGS_HTOD_SPLIT 0x4000 @@ -160,6 +220,7 @@ static const struct brcmf_firmware_mapping brcmf_pcie_fwnames[] = { #define BRCMF_SHARED_DMA_SCRATCH_ADDR_OFFSET 56 #define BRCMF_SHARED_DMA_RINGUPD_LEN_OFFSET 64 #define BRCMF_SHARED_DMA_RINGUPD_ADDR_OFFSET 68 +#define BRCMF_SHARED_HOST_CAP_OFFSET 84 #define BRCMF_RING_H2D_RING_COUNT_OFFSET 0 #define BRCMF_RING_D2H_RING_COUNT_OFFSET 1 @@ -174,6 +235,9 @@ static const struct brcmf_firmware_mapping brcmf_pcie_fwnames[] = { #define BRCMF_DEF_MAX_RXBUFPOST 255 +#define BRCMF_HOSTCAP_H2D_ENABLE_HOSTRDY 0x400 +#define BRCMF_HOSTCAP_DS_NO_OOB_DW 0x1000 + #define BRCMF_CONSOLE_BUFADDR_OFFSET 8 #define BRCMF_CONSOLE_BUFSIZE_OFFSET 12 #define BRCMF_CONSOLE_WRITEIDX_OFFSET 16 @@ -199,18 +263,32 @@ static const struct brcmf_firmware_mapping brcmf_pcie_fwnames[] = { #define BRCMF_PCIE_CFGREG_MSI_ADDR_L 0x5C #define BRCMF_PCIE_CFGREG_MSI_ADDR_H 0x60 #define BRCMF_PCIE_CFGREG_MSI_DATA 0x64 +#define BRCMF_PCIE_CFGREG_REVID 0x6C #define BRCMF_PCIE_CFGREG_LINK_STATUS_CTRL 0xBC #define BRCMF_PCIE_CFGREG_LINK_STATUS_CTRL2 0xDC #define BRCMF_PCIE_CFGREG_RBAR_CTRL 0x228 #define BRCMF_PCIE_CFGREG_PML1_SUB_CTRL1 0x248 #define BRCMF_PCIE_CFGREG_REG_BAR2_CONFIG 0x4E0 #define BRCMF_PCIE_CFGREG_REG_BAR3_CONFIG 0x4F4 +#define BRCMF_PCIE_CFGREG_REVID_SECURE_MODE BIT(31) #define BRCMF_PCIE_LINK_STATUS_CTRL_ASPM_ENAB 3 /* Magic number at a magic location to find RAM size */ #define BRCMF_RAMSIZE_MAGIC 0x534d4152 /* SMAR */ #define BRCMF_RAMSIZE_OFFSET 0x6c +#define BRCMF_ENTROPY_SEED_LEN 64u +#define BRCMF_ENTROPY_NONCE_LEN 16u +#define BRCMF_ENTROPY_HOST_LEN (BRCMF_ENTROPY_SEED_LEN + \ + BRCMF_ENTROPY_NONCE_LEN) +#define BRCMF_NVRAM_OFFSET_TCM 4u +#define BRCMF_NVRAM_COMPRS_FACTOR 4u +#define BRCMF_NVRAM_RNG_SIGNATURE 0xFEEDC0DEu + +struct brcmf_rand_metadata { + u32 signature; + u32 count; +}; struct brcmf_pcie_console { u32 base_addr; @@ -260,6 +338,9 @@ struct brcmf_pciedev_info { struct brcmf_chip *ci; u32 coreid; struct brcmf_pcie_shared_info shared; + u8 hostready; + bool use_mailbox; + bool use_d0_inform; wait_queue_head_t mbdata_resp_wait; bool mbdata_completed; bool irq_allocated; @@ -272,6 +353,14 @@ struct brcmf_pciedev_info { void (*write_ptr)(struct brcmf_pciedev_info *devinfo, u32 mem_offset, u16 value); struct brcmf_mp_device *settings; +#ifdef CONFIG_BRCMFMAC_PCIE_BARWIN_SZ + ulong bar1_size; +#endif /* CONFIG_BRCMFMAC_PCIE_BARWIN_SZ */ +#ifdef DEBUG + u32 console_interval; + bool console_active; + struct timer_list timer; +#endif }; struct brcmf_pcie_ringbuf { @@ -342,6 +431,17 @@ static void brcmf_pcie_setup(struct device *dev, int ret, struct brcmf_fw_request *fwreq); static struct brcmf_fw_request * brcmf_pcie_prepare_fw_request(struct brcmf_pciedev_info *devinfo); +static void brcmf_pcie_bus_console_init(struct brcmf_pciedev_info *devinfo); +static void brcmf_pcie_bus_console_read(struct brcmf_pciedev_info *devinfo, + bool error); + +static void +brcmf_pcie_fwcon_timer(struct brcmf_pciedev_info *devinfo, bool active); +static void brcmf_pcie_debugfs_create(struct device *dev); + +#ifdef CONFIG_BRCMFMAC_PCIE_BARWIN_SZ +DEFINE_RAW_SPINLOCK(pcie_lock); +#endif /* CONFIG_BRCMFMAC_PCIE_BARWIN_SZ */ static u32 brcmf_pcie_read_reg32(struct brcmf_pciedev_info *devinfo, u32 reg_offset) @@ -366,8 +466,24 @@ static u8 brcmf_pcie_read_tcm8(struct brcmf_pciedev_info *devinfo, u32 mem_offset) { void __iomem *address = devinfo->tcm + mem_offset; +#ifdef CONFIG_BRCMFMAC_PCIE_BARWIN_SZ + unsigned long flags; + u8 value; + + raw_spin_lock_irqsave(&pcie_lock, flags); + if ((address - devinfo->tcm) >= devinfo->bar1_size) { + pci_write_config_dword(devinfo->pdev, BCMA_PCI_BAR1_WIN, + devinfo->bar1_size); + address = address - devinfo->bar1_size; + } + value = ioread8(address); + pci_write_config_dword(devinfo->pdev, BCMA_PCI_BAR1_WIN, 0x0); + raw_spin_unlock_irqrestore(&pcie_lock, flags); + return value; +#else return (ioread8(address)); +#endif /* CONFIG_BRCMFMAC_PCIE_BARWIN_SZ */ } @@ -375,8 +491,24 @@ static u16 brcmf_pcie_read_tcm16(struct brcmf_pciedev_info *devinfo, u32 mem_offset) { void __iomem *address = devinfo->tcm + mem_offset; +#ifdef CONFIG_BRCMFMAC_PCIE_BARWIN_SZ + u16 value; + unsigned long flags; + + raw_spin_lock_irqsave(&pcie_lock, flags); + if ((address - devinfo->tcm) >= devinfo->bar1_size) { + pci_write_config_dword(devinfo->pdev, BCMA_PCI_BAR1_WIN, + devinfo->bar1_size); + address = address - devinfo->bar1_size; + } + value = ioread16(address); + pci_write_config_dword(devinfo->pdev, BCMA_PCI_BAR1_WIN, 0x0); + raw_spin_unlock_irqrestore(&pcie_lock, flags); + return value; +#else return (ioread16(address)); +#endif /* CONFIG_BRCMFMAC_PCIE_BARWIN_SZ */ } @@ -385,8 +517,22 @@ brcmf_pcie_write_tcm16(struct brcmf_pciedev_info *devinfo, u32 mem_offset, u16 value) { void __iomem *address = devinfo->tcm + mem_offset; +#ifdef CONFIG_BRCMFMAC_PCIE_BARWIN_SZ + unsigned long flags; + + raw_spin_lock_irqsave(&pcie_lock, flags); + if ((address - devinfo->tcm) >= devinfo->bar1_size) { + pci_write_config_dword(devinfo->pdev, BCMA_PCI_BAR1_WIN, + devinfo->bar1_size); + address = address - devinfo->bar1_size; + } iowrite16(value, address); + pci_write_config_dword(devinfo->pdev, BCMA_PCI_BAR1_WIN, 0x0); + raw_spin_unlock_irqrestore(&pcie_lock, flags); +#else + iowrite16(value, address); +#endif /* CONFIG_BRCMFMAC_PCIE_BARWIN_SZ */ } @@ -413,8 +559,24 @@ static u32 brcmf_pcie_read_tcm32(struct brcmf_pciedev_info *devinfo, u32 mem_offset) { void __iomem *address = devinfo->tcm + mem_offset; +#ifdef CONFIG_BRCMFMAC_PCIE_BARWIN_SZ + u32 value; + unsigned long flags; + + raw_spin_lock_irqsave(&pcie_lock, flags); + if ((address - devinfo->tcm) >= devinfo->bar1_size) { + pci_write_config_dword(devinfo->pdev, BCMA_PCI_BAR1_WIN, + devinfo->bar1_size); + address = address - devinfo->bar1_size; + } + value = ioread32(address); + pci_write_config_dword(devinfo->pdev, BCMA_PCI_BAR1_WIN, 0x0); + raw_spin_unlock_irqrestore(&pcie_lock, flags); + return value; +#else return (ioread32(address)); +#endif /* CONFIG_BRCMFMAC_PCIE_BARWIN_SZ */ } @@ -423,17 +585,47 @@ brcmf_pcie_write_tcm32(struct brcmf_pciedev_info *devinfo, u32 mem_offset, u32 value) { void __iomem *address = devinfo->tcm + mem_offset; - +#ifdef CONFIG_BRCMFMAC_PCIE_BARWIN_SZ + unsigned long flags; + + raw_spin_lock_irqsave(&pcie_lock, flags); + if ((address - devinfo->tcm) >= devinfo->bar1_size) { + pci_write_config_dword(devinfo->pdev, BCMA_PCI_BAR1_WIN, + devinfo->bar1_size); + address = address - devinfo->bar1_size; + } iowrite32(value, address); + pci_write_config_dword(devinfo->pdev, BCMA_PCI_BAR1_WIN, 0x0); + raw_spin_unlock_irqrestore(&pcie_lock, flags); +#else + iowrite32(value, address); +#endif /* CONFIG_BRCMFMAC_PCIE_BARWIN_SZ */ } static u32 brcmf_pcie_read_ram32(struct brcmf_pciedev_info *devinfo, u32 mem_offset) { - void __iomem *addr = devinfo->tcm + devinfo->ci->rambase + mem_offset; + void __iomem *address = devinfo->tcm + devinfo->ci->rambase + + mem_offset; +#ifdef CONFIG_BRCMFMAC_PCIE_BARWIN_SZ + u32 value; + unsigned long flags; + + raw_spin_lock_irqsave(&pcie_lock, flags); + if ((address - devinfo->tcm) >= devinfo->bar1_size) { + pci_write_config_dword(devinfo->pdev, BCMA_PCI_BAR1_WIN, + devinfo->bar1_size); + address = address - devinfo->bar1_size; + } + value = ioread32(address); + pci_write_config_dword(devinfo->pdev, BCMA_PCI_BAR1_WIN, 0x0); + raw_spin_unlock_irqrestore(&pcie_lock, flags); - return (ioread32(addr)); + return value; +#else + return (ioread32(address)); +#endif /* CONFIG_BRCMFMAC_PCIE_BARWIN_SZ */ } @@ -441,9 +633,114 @@ static void brcmf_pcie_write_ram32(struct brcmf_pciedev_info *devinfo, u32 mem_offset, u32 value) { - void __iomem *addr = devinfo->tcm + devinfo->ci->rambase + mem_offset; + void __iomem *address = devinfo->tcm + devinfo->ci->rambase + + mem_offset; +#ifdef CONFIG_BRCMFMAC_PCIE_BARWIN_SZ + unsigned long flags; - iowrite32(value, addr); + raw_spin_lock_irqsave(&pcie_lock, flags); + if ((address - devinfo->tcm) >= devinfo->bar1_size) { + pci_write_config_dword(devinfo->pdev, BCMA_PCI_BAR1_WIN, + devinfo->bar1_size); + address = address - devinfo->bar1_size; + } + iowrite32(value, address); + pci_write_config_dword(devinfo->pdev, BCMA_PCI_BAR1_WIN, 0x0); + raw_spin_unlock_irqrestore(&pcie_lock, flags); +#else + iowrite32(value, address); +#endif /* CONFIG_BRCMFMAC_PCIE_BARWIN_SZ */ +} + + +static void +brcmf_pcie_copy_mem_todev(struct brcmf_pciedev_info *devinfo, u32 mem_offset, + void *srcaddr, u32 len) +{ + void __iomem *address = devinfo->tcm + mem_offset; + __le32 *src32; + __le16 *src16; + u8 *src8; +#ifdef CONFIG_BRCMFMAC_PCIE_BARWIN_SZ + unsigned long flags; +#endif /* CONFIG_BRCMFMAC_PCIE_BARWIN_SZ */ + + if (((ulong)address & 4) || ((ulong)srcaddr & 4) || (len & 4)) { + if (((ulong)address & 2) || ((ulong)srcaddr & 2) || (len & 2)) { + src8 = (u8 *)srcaddr; + while (len) { +#ifdef CONFIG_BRCMFMAC_PCIE_BARWIN_SZ + raw_spin_lock_irqsave(&pcie_lock, flags); + if ((address - devinfo->tcm) >= + devinfo->bar1_size) { + pci_write_config_dword + (devinfo->pdev, + BCMA_PCI_BAR1_WIN, + devinfo->bar1_size); + address = address - + devinfo->bar1_size; + } +#endif /* CONFIG_BRCMFMAC_PCIE_BARWIN_SZ */ + iowrite8(*src8, address); +#ifdef CONFIG_BRCMFMAC_PCIE_BARWIN_SZ + raw_spin_unlock_irqrestore(&pcie_lock, flags); +#endif /* CONFIG_BRCMFMAC_PCIE_BARWIN_SZ */ + address++; + src8++; + len--; + } + } else { + len = len / 2; + src16 = (__le16 *)srcaddr; + while (len) { +#ifdef CONFIG_BRCMFMAC_PCIE_BARWIN_SZ + raw_spin_lock_irqsave(&pcie_lock, flags); + if ((address - devinfo->tcm) >= + devinfo->bar1_size) { + pci_write_config_dword + (devinfo->pdev, + BCMA_PCI_BAR1_WIN, + devinfo->bar1_size); + address = address - + devinfo->bar1_size; + } +#endif /* CONFIG_BRCMFMAC_PCIE_BARWIN_SZ */ + iowrite16(le16_to_cpu(*src16), address); +#ifdef CONFIG_BRCMFMAC_PCIE_BARWIN_SZ + raw_spin_unlock_irqrestore(&pcie_lock, flags); +#endif /* CONFIG_BRCMFMAC_PCIE_BARWIN_SZ */ + address += 2; + src16++; + len--; + } + } + } else { + len = len / 4; + src32 = (__le32 *)srcaddr; + while (len) { +#ifdef CONFIG_BRCMFMAC_PCIE_BARWIN_SZ + raw_spin_lock_irqsave(&pcie_lock, flags); + if ((address - devinfo->tcm) >= + devinfo->bar1_size) { + pci_write_config_dword + (devinfo->pdev, + BCMA_PCI_BAR1_WIN, + devinfo->bar1_size); + address = address - devinfo->bar1_size; + } +#endif /* CONFIG_BRCMFMAC_PCIE_BARWIN_SZ */ + iowrite32(le32_to_cpu(*src32), address); +#ifdef CONFIG_BRCMFMAC_PCIE_BARWIN_SZ + raw_spin_unlock_irqrestore(&pcie_lock, flags); +#endif /* CONFIG_BRCMFMAC_PCIE_BARWIN_SZ */ + address += 4; + src32++; + len--; + } + } +#ifdef CONFIG_BRCMFMAC_PCIE_BARWIN_SZ + pci_write_config_dword(devinfo->pdev, BCMA_PCI_BAR1_WIN, 0x0); +#endif /* CONFIG_BRCMFMAC_PCIE_BARWIN_SZ */ } @@ -455,12 +752,30 @@ brcmf_pcie_copy_dev_tomem(struct brcmf_pciedev_info *devinfo, u32 mem_offset, __le32 *dst32; __le16 *dst16; u8 *dst8; +#ifdef CONFIG_BRCMFMAC_PCIE_BARWIN_SZ + unsigned long flags; +#endif /* CONFIG_BRCMFMAC_PCIE_BARWIN_SZ */ if (((ulong)address & 4) || ((ulong)dstaddr & 4) || (len & 4)) { if (((ulong)address & 2) || ((ulong)dstaddr & 2) || (len & 2)) { dst8 = (u8 *)dstaddr; while (len) { +#ifdef CONFIG_BRCMFMAC_PCIE_BARWIN_SZ + raw_spin_lock_irqsave(&pcie_lock, flags); + if ((address - devinfo->tcm) >= + devinfo->bar1_size) { + pci_write_config_dword + (devinfo->pdev, + BCMA_PCI_BAR1_WIN, + devinfo->bar1_size); + address = address - + devinfo->bar1_size; + } +#endif /* CONFIG_BRCMFMAC_PCIE_BARWIN_SZ */ *dst8 = ioread8(address); +#ifdef CONFIG_BRCMFMAC_PCIE_BARWIN_SZ + raw_spin_unlock_irqrestore(&pcie_lock, flags); +#endif /* CONFIG_BRCMFMAC_PCIE_BARWIN_SZ */ address++; dst8++; len--; @@ -469,7 +784,22 @@ brcmf_pcie_copy_dev_tomem(struct brcmf_pciedev_info *devinfo, u32 mem_offset, len = len / 2; dst16 = (__le16 *)dstaddr; while (len) { +#ifdef CONFIG_BRCMFMAC_PCIE_BARWIN_SZ + raw_spin_lock_irqsave(&pcie_lock, flags); + if ((address - devinfo->tcm) >= + devinfo->bar1_size) { + pci_write_config_dword + (devinfo->pdev, + BCMA_PCI_BAR1_WIN, + devinfo->bar1_size); + address = address - + devinfo->bar1_size; + } +#endif /* CONFIG_BRCMFMAC_PCIE_BARWIN_SZ */ *dst16 = cpu_to_le16(ioread16(address)); +#ifdef CONFIG_BRCMFMAC_PCIE_BARWIN_SZ + raw_spin_unlock_irqrestore(&pcie_lock, flags); +#endif /* CONFIG_BRCMFMAC_PCIE_BARWIN_SZ */ address += 2; dst16++; len--; @@ -479,12 +809,29 @@ brcmf_pcie_copy_dev_tomem(struct brcmf_pciedev_info *devinfo, u32 mem_offset, len = len / 4; dst32 = (__le32 *)dstaddr; while (len) { +#ifdef CONFIG_BRCMFMAC_PCIE_BARWIN_SZ + raw_spin_lock_irqsave(&pcie_lock, flags); + if ((address - devinfo->tcm) >= + devinfo->bar1_size) { + pci_write_config_dword + (devinfo->pdev, + BCMA_PCI_BAR1_WIN, + devinfo->bar1_size); + address = address - devinfo->bar1_size; + } +#endif /* CONFIG_BRCMFMAC_PCIE_BARWIN_SZ */ *dst32 = cpu_to_le32(ioread32(address)); +#ifdef CONFIG_BRCMFMAC_PCIE_BARWIN_SZ + raw_spin_unlock_irqrestore(&pcie_lock, flags); +#endif /* CONFIG_BRCMFMAC_PCIE_BARWIN_SZ */ address += 4; dst32++; len--; } } +#ifdef CONFIG_BRCMFMAC_PCIE_BARWIN_SZ + pci_write_config_dword(devinfo->pdev, BCMA_PCI_BAR1_WIN, 0x0); +#endif /* CONFIG_BRCMFMAC_PCIE_BARWIN_SZ */ } @@ -528,6 +875,9 @@ static void brcmf_pcie_reset_device(struct brcmf_pciedev_info *devinfo) BRCMF_PCIE_CFGREG_MSI_ADDR_L, BRCMF_PCIE_CFGREG_MSI_ADDR_H, BRCMF_PCIE_CFGREG_MSI_DATA, +#ifdef CONFIG_BRCMFMAC_PCIE_BARWIN_SZ + BCMA_PCI_BAR1_WIN, +#endif /* CONFIG_BRCMFMAC_PCIE_BARWIN_SZ */ BRCMF_PCIE_CFGREG_LINK_STATUS_CTRL2, BRCMF_PCIE_CFGREG_RBAR_CTRL, BRCMF_PCIE_CFGREG_PML1_SUB_CTRL1, @@ -549,9 +899,15 @@ static void brcmf_pcie_reset_device(struct brcmf_pciedev_info *devinfo) val); /* Watchdog reset */ + if (devinfo->ci->blhs) + devinfo->ci->blhs->init(devinfo->ci); brcmf_pcie_select_core(devinfo, BCMA_CORE_CHIPCOMMON); WRITECC32(devinfo, watchdog, 4); msleep(100); + if (devinfo->ci->blhs) + if (devinfo->ci->blhs->post_wdreset(devinfo->ci)) + return; + /* Restore ASPM */ brcmf_pcie_select_core(devinfo, BCMA_CORE_PCIE2); @@ -590,8 +946,39 @@ static void brcmf_pcie_attach(struct brcmf_pciedev_info *devinfo) } +static int brcmf_pcie_bus_readshared(struct brcmf_pciedev_info *devinfo, + u32 nvram_csm) +{ + struct brcmf_bus *bus = dev_get_drvdata(&devinfo->pdev->dev); + u32 loop_counter; + u32 addr_le; + u32 addr = 0; + + loop_counter = BRCMF_PCIE_READ_SHARED_TIMEOUT / 50; + while ((addr == 0 || addr == nvram_csm) && (loop_counter)) { + msleep(50); + addr_le = brcmf_pcie_read_ram32(devinfo, + devinfo->ci->ramsize - 4); + addr = le32_to_cpu(addr_le); + loop_counter--; + } + if (addr == 0 || addr == nvram_csm || addr < devinfo->ci->rambase || + addr >= devinfo->ci->rambase + devinfo->ci->ramsize) { + brcmf_err(bus, "Invalid shared RAM address 0x%08x\n", addr); + return -ENODEV; + } + devinfo->shared.tcm_base_address = addr; + brcmf_dbg(PCIE, "Shared RAM addr: 0x%08x\n", addr); + + brcmf_pcie_bus_console_init(devinfo); + return 0; +} + static int brcmf_pcie_enter_download_state(struct brcmf_pciedev_info *devinfo) { + struct brcmf_bus *bus = dev_get_drvdata(&devinfo->pdev->dev); + int err = 0; + if (devinfo->ci->chip == BRCM_CC_43602_CHIP_ID) { brcmf_pcie_select_core(devinfo, BCMA_CORE_ARM_CR4); brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_ARMCR4REG_BANKIDX, @@ -603,7 +990,19 @@ static int brcmf_pcie_enter_download_state(struct brcmf_pciedev_info *devinfo) brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_ARMCR4REG_BANKPDA, 0); } - return 0; + + if (devinfo->ci->blhs) { + err = devinfo->ci->blhs->prep_fwdl(devinfo->ci); + if (err) { + brcmf_err(bus, "FW download preparation failed"); + return err; + } + + if (!brcmf_pcie_bus_readshared(devinfo, 0)) + brcmf_pcie_bus_console_read(devinfo, false); + } + + return err; } @@ -617,8 +1016,14 @@ static int brcmf_pcie_exit_download_state(struct brcmf_pciedev_info *devinfo, brcmf_chip_resetcore(core, 0, 0, 0); } - if (!brcmf_chip_set_active(devinfo->ci, resetintr)) - return -EINVAL; + if (devinfo->ci->blhs) { + brcmf_pcie_bus_console_read(devinfo, false); + devinfo->ci->blhs->post_nvramdl(devinfo->ci); + } else { + if (!brcmf_chip_set_active(devinfo->ci, resetintr)) + return -EINVAL; + } + return 0; } @@ -627,41 +1032,53 @@ static int brcmf_pcie_send_mb_data(struct brcmf_pciedev_info *devinfo, u32 htod_mb_data) { struct brcmf_pcie_shared_info *shared; + struct brcmf_bus *bus; + int err; struct brcmf_core *core; u32 addr; u32 cur_htod_mb_data; u32 i; shared = &devinfo->shared; - addr = shared->htod_mb_data_addr; - cur_htod_mb_data = brcmf_pcie_read_tcm32(devinfo, addr); - - if (cur_htod_mb_data != 0) - brcmf_dbg(PCIE, "MB transaction is already pending 0x%04x\n", - cur_htod_mb_data); - - i = 0; - while (cur_htod_mb_data != 0) { - msleep(10); - i++; - if (i > 100) - return -EIO; + bus = dev_get_drvdata(&devinfo->pdev->dev); + if (shared->version >= BRCMF_PCIE_SHARED_VERSION_6 && + !devinfo->use_mailbox) { + err = brcmf_msgbuf_tx_mbdata(bus->drvr, htod_mb_data); + if (err) { + brcmf_err(bus, "sendimg mbdata failed err=%d\n", err); + return err; + } + } else { + addr = shared->htod_mb_data_addr; cur_htod_mb_data = brcmf_pcie_read_tcm32(devinfo, addr); - } - brcmf_pcie_write_tcm32(devinfo, addr, htod_mb_data); - pci_write_config_dword(devinfo->pdev, BRCMF_PCIE_REG_SBMBX, 1); + if (cur_htod_mb_data != 0) + brcmf_dbg(PCIE, "MB transaction is already pending 0x%04x\n", + cur_htod_mb_data); + + i = 0; + while (cur_htod_mb_data != 0) { + msleep(10); + i++; + if (i > 100) + return -EIO; + cur_htod_mb_data = brcmf_pcie_read_tcm32(devinfo, addr); + } - /* Send mailbox interrupt twice as a hardware workaround */ - core = brcmf_chip_get_core(devinfo->ci, BCMA_CORE_PCIE2); - if (core->rev <= 13) + brcmf_pcie_write_tcm32(devinfo, addr, htod_mb_data); pci_write_config_dword(devinfo->pdev, BRCMF_PCIE_REG_SBMBX, 1); + /* Send mailbox interrupt twice as a hardware workaround */ + core = brcmf_chip_get_core(devinfo->ci, BCMA_CORE_PCIE2); + if (core->rev <= 13) + pci_write_config_dword(devinfo->pdev, + BRCMF_PCIE_REG_SBMBX, 1); + } return 0; } -static void brcmf_pcie_handle_mb_data(struct brcmf_pciedev_info *devinfo) +static u32 brcmf_pcie_read_mb_data(struct brcmf_pciedev_info *devinfo) { struct brcmf_pcie_shared_info *shared; u32 addr; @@ -670,36 +1087,42 @@ static void brcmf_pcie_handle_mb_data(struct brcmf_pciedev_info *devinfo) shared = &devinfo->shared; addr = shared->dtoh_mb_data_addr; dtoh_mb_data = brcmf_pcie_read_tcm32(devinfo, addr); + brcmf_pcie_write_tcm32(devinfo, addr, 0); + return dtoh_mb_data; +} - if (!dtoh_mb_data) - return; +void brcmf_pcie_handle_mb_data(struct brcmf_bus *bus_if, u32 d2h_mb_data) +{ + struct brcmf_pciedev *buspub = bus_if->bus_priv.pcie; + struct brcmf_pciedev_info *devinfo = buspub->devinfo; - brcmf_pcie_write_tcm32(devinfo, addr, 0); + brcmf_dbg(INFO, "D2H_MB_DATA: 0x%04x\n", d2h_mb_data); - brcmf_dbg(PCIE, "D2H_MB_DATA: 0x%04x\n", dtoh_mb_data); - if (dtoh_mb_data & BRCMF_D2H_DEV_DS_ENTER_REQ) { - brcmf_dbg(PCIE, "D2H_MB_DATA: DEEP SLEEP REQ\n"); + if (d2h_mb_data & BRCMF_D2H_DEV_DS_ENTER_REQ) { + brcmf_dbg(INFO, "D2H_MB_DATA: DEEP SLEEP REQ\n"); brcmf_pcie_send_mb_data(devinfo, BRCMF_H2D_HOST_DS_ACK); - brcmf_dbg(PCIE, "D2H_MB_DATA: sent DEEP SLEEP ACK\n"); + brcmf_dbg(INFO, "D2H_MB_DATA: sent DEEP SLEEP ACK\n"); } - if (dtoh_mb_data & BRCMF_D2H_DEV_DS_EXIT_NOTE) - brcmf_dbg(PCIE, "D2H_MB_DATA: DEEP SLEEP EXIT\n"); - if (dtoh_mb_data & BRCMF_D2H_DEV_D3_ACK) { - brcmf_dbg(PCIE, "D2H_MB_DATA: D3 ACK\n"); + + if (d2h_mb_data & BRCMF_D2H_DEV_DS_EXIT_NOTE) + brcmf_dbg(INFO, "D2H_MB_DATA: DEEP SLEEP EXIT\n"); + if (d2h_mb_data & BRCMF_D2H_DEV_D3_ACK) { + brcmf_dbg(INFO, "D2H_MB_DATA: D3 ACK\n"); devinfo->mbdata_completed = true; wake_up(&devinfo->mbdata_resp_wait); } - if (dtoh_mb_data & BRCMF_D2H_DEV_FWHALT) { - brcmf_dbg(PCIE, "D2H_MB_DATA: FW HALT\n"); + + if (d2h_mb_data & BRCMF_D2H_DEV_FWHALT) { + brcmf_dbg(INFO, "D2H_MB_DATA: FW HALT\n"); brcmf_fw_crashed(&devinfo->pdev->dev); } } - static void brcmf_pcie_bus_console_init(struct brcmf_pciedev_info *devinfo) { struct brcmf_pcie_shared_info *shared; struct brcmf_pcie_console *console; + u32 buf_addr; u32 addr; shared = &devinfo->shared; @@ -708,7 +1131,12 @@ static void brcmf_pcie_bus_console_init(struct brcmf_pciedev_info *devinfo) console->base_addr = brcmf_pcie_read_tcm32(devinfo, addr); addr = console->base_addr + BRCMF_CONSOLE_BUFADDR_OFFSET; - console->buf_addr = brcmf_pcie_read_tcm32(devinfo, addr); + buf_addr = brcmf_pcie_read_tcm32(devinfo, addr); + /* reset console index when buffer address is updated */ + if (console->buf_addr != buf_addr) { + console->buf_addr = buf_addr; + console->read_idx = 0; + } addr = console->base_addr + BRCMF_CONSOLE_BUFSIZE_OFFSET; console->bufsize = brcmf_pcie_read_tcm32(devinfo, addr); @@ -768,14 +1196,16 @@ static void brcmf_pcie_bus_console_read(struct brcmf_pciedev_info *devinfo, static void brcmf_pcie_intr_disable(struct brcmf_pciedev_info *devinfo) { - brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_MAILBOXMASK, 0); + brcmf_pcie_write_reg32(devinfo, + BRCMF_PCIE_PCIE2REG_MAILBOXMASK(devinfo), 0); } static void brcmf_pcie_intr_enable(struct brcmf_pciedev_info *devinfo) { - brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_MAILBOXMASK, - BRCMF_PCIE_MB_INT_D2H_DB | + brcmf_pcie_write_reg32(devinfo, + BRCMF_PCIE_PCIE2REG_MAILBOXMASK(devinfo), + BRCMF_PCIE_MB_INT_D2H_DB(devinfo) | BRCMF_PCIE_MB_INT_FN0_0 | BRCMF_PCIE_MB_INT_FN0_1); } @@ -791,7 +1221,8 @@ static irqreturn_t brcmf_pcie_quick_check_isr(int irq, void *arg) { struct brcmf_pciedev_info *devinfo = (struct brcmf_pciedev_info *)arg; - if (brcmf_pcie_read_reg32(devinfo, BRCMF_PCIE_PCIE2REG_MAILBOXINT)) { + if (brcmf_pcie_read_reg32(devinfo, + BRCMF_PCIE_PCIE2REG_MAILBOXINT(devinfo))) { brcmf_pcie_intr_disable(devinfo); brcmf_dbg(PCIE, "Enter\n"); return IRQ_WAKE_THREAD; @@ -804,17 +1235,25 @@ static irqreturn_t brcmf_pcie_isr_thread(int irq, void *arg) { struct brcmf_pciedev_info *devinfo = (struct brcmf_pciedev_info *)arg; u32 status; + u32 d2h_mbdata; + struct pci_dev *pdev = devinfo->pdev; + struct brcmf_bus *bus = dev_get_drvdata(&pdev->dev); devinfo->in_irq = true; - status = brcmf_pcie_read_reg32(devinfo, BRCMF_PCIE_PCIE2REG_MAILBOXINT); + status = brcmf_pcie_read_reg32(devinfo, + BRCMF_PCIE_PCIE2REG_MAILBOXINT(devinfo)); brcmf_dbg(PCIE, "Enter %x\n", status); if (status) { - brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_MAILBOXINT, + brcmf_pcie_write_reg32(devinfo, + BRCMF_PCIE_PCIE2REG_MAILBOXINT(devinfo), status); if (status & (BRCMF_PCIE_MB_INT_FN0_0 | - BRCMF_PCIE_MB_INT_FN0_1)) - brcmf_pcie_handle_mb_data(devinfo); - if (status & BRCMF_PCIE_MB_INT_D2H_DB) { + BRCMF_PCIE_MB_INT_FN0_1)) { + d2h_mbdata = brcmf_pcie_read_mb_data(devinfo); + brcmf_pcie_handle_mb_data(bus, d2h_mbdata); + } + + if (status & BRCMF_PCIE_MB_INT_D2H_DB(devinfo)) { if (devinfo->state == BRCMFMAC_PCIE_STATE_UP) brcmf_proto_msgbuf_rx_trigger( &devinfo->pdev->dev); @@ -873,8 +1312,11 @@ static void brcmf_pcie_release_irq(struct brcmf_pciedev_info *devinfo) if (devinfo->in_irq) brcmf_err(bus, "Still in IRQ (processing) !!!\n"); - status = brcmf_pcie_read_reg32(devinfo, BRCMF_PCIE_PCIE2REG_MAILBOXINT); - brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_MAILBOXINT, status); + status = brcmf_pcie_read_reg32(devinfo, + BRCMF_PCIE_PCIE2REG_MAILBOXINT(devinfo)); + brcmf_pcie_write_reg32(devinfo, + BRCMF_PCIE_PCIE2REG_MAILBOXINT(devinfo), + status); devinfo->irq_allocated = false; } @@ -1096,9 +1538,14 @@ static int brcmf_pcie_init_ringbuffers(struct brcmf_pciedev_info *devinfo) u16 max_flowrings; u16 max_submissionrings; u16 max_completionrings; - +#ifdef CONFIG_BRCMFMAC_PCIE_BARWIN_SZ + brcmf_pcie_copy_dev_tomem(devinfo, devinfo->shared.ring_info_addr, + &ringinfo, sizeof(ringinfo)); +#else memcpy_fromio(&ringinfo, devinfo->tcm + devinfo->shared.ring_info_addr, sizeof(ringinfo)); +#endif /* CONFIG_BRCMFMAC_PCIE_BARWIN_SZ */ + if (devinfo->shared.version >= 6) { max_submissionrings = le16_to_cpu(ringinfo.max_submissionrings); max_flowrings = le16_to_cpu(ringinfo.max_flowrings); @@ -1109,6 +1556,10 @@ static int brcmf_pcie_init_ringbuffers(struct brcmf_pciedev_info *devinfo) BRCMF_NROF_H2D_COMMON_MSGRINGS; max_completionrings = BRCMF_NROF_D2H_COMMON_MSGRINGS; } + if (max_flowrings > 256) { + brcmf_err(bus, "invalid max_flowrings(%d)\n", max_flowrings); + return -EIO; + } if (devinfo->dma_idx_sz != 0) { bufsz = (max_submissionrings + max_completionrings) * @@ -1167,8 +1618,14 @@ static int brcmf_pcie_init_ringbuffers(struct brcmf_pciedev_info *devinfo) ringinfo.d2h_r_idx_hostaddr.high_addr = cpu_to_le32(address >> 32); +#ifdef CONFIG_BRCMFMAC_PCIE_BARWIN_SZ + brcmf_pcie_copy_mem_todev(devinfo, + devinfo->shared.ring_info_addr, + &ringinfo, sizeof(ringinfo)); +#else memcpy_toio(devinfo->tcm + devinfo->shared.ring_info_addr, &ringinfo, sizeof(ringinfo)); +#endif /* CONFIG_BRCMFMAC_PCIE_BARWIN_SZ */ brcmf_dbg(PCIE, "Using host memory indices\n"); } @@ -1303,22 +1760,15 @@ static int brcmf_pcie_init_scratchbuffers(struct brcmf_pciedev_info *devinfo) static void brcmf_pcie_down(struct device *dev) -{ -} - -static int brcmf_pcie_preinit(struct device *dev) { struct brcmf_bus *bus_if = dev_get_drvdata(dev); - struct brcmf_pciedev *buspub = bus_if->bus_priv.pcie; - - brcmf_dbg(PCIE, "Enter\n"); - - brcmf_pcie_intr_enable(buspub->devinfo); - brcmf_pcie_hostready(buspub->devinfo); + struct brcmf_pciedev *pcie_bus_dev = bus_if->bus_priv.pcie; + struct brcmf_pciedev_info *devinfo = pcie_bus_dev->devinfo; - return 0; + brcmf_pcie_fwcon_timer(devinfo, false); } + static int brcmf_pcie_tx(struct device *dev, struct sk_buff *skb) { return 0; @@ -1379,8 +1829,10 @@ int brcmf_pcie_get_fwname(struct device *dev, const char *ext, u8 *fw_name) struct brcmf_fw_name fwnames[] = { { ext, fw_name }, }; + u32 chip; - fwreq = brcmf_fw_alloc_request(bus_if->chip, bus_if->chiprev, + chip = bus_if->chip; + fwreq = brcmf_fw_alloc_request(chip, bus_if->chiprev, brcmf_pcie_fwnames, ARRAY_SIZE(brcmf_pcie_fwnames), fwnames, ARRAY_SIZE(fwnames)); @@ -1426,7 +1878,6 @@ static int brcmf_pcie_reset(struct device *dev) } static const struct brcmf_bus_ops brcmf_pcie_bus_ops = { - .preinit = brcmf_pcie_preinit, .txdata = brcmf_pcie_tx, .stop = brcmf_pcie_down, .txctl = brcmf_pcie_tx_ctlpkt, @@ -1436,6 +1887,7 @@ static const struct brcmf_bus_ops brcmf_pcie_bus_ops = { .get_memdump = brcmf_pcie_get_memdump, .get_fwname = brcmf_pcie_get_fwname, .reset = brcmf_pcie_reset, + .debugfs_create = brcmf_pcie_debugfs_create, }; @@ -1461,6 +1913,28 @@ brcmf_pcie_adjust_ramsize(struct brcmf_pciedev_info *devinfo, u8 *data, } +static void +brcmf_pcie_write_rand(struct brcmf_pciedev_info *devinfo, u32 nvram_csm) +{ + struct brcmf_rand_metadata rand_data; + u8 rand_buf[BRCMF_ENTROPY_HOST_LEN]; + u32 count = BRCMF_ENTROPY_HOST_LEN; + u32 address; + + address = devinfo->ci->rambase + + (devinfo->ci->ramsize - BRCMF_NVRAM_OFFSET_TCM) - + ((nvram_csm & 0xffff) * BRCMF_NVRAM_COMPRS_FACTOR) - + sizeof(rand_data); + memset(rand_buf, 0, BRCMF_ENTROPY_HOST_LEN); + rand_data.signature = cpu_to_le32(BRCMF_NVRAM_RNG_SIGNATURE); + rand_data.count = cpu_to_le32(count); + brcmf_pcie_copy_mem_todev(devinfo, address, &rand_data, + sizeof(rand_data)); + address -= count; + get_random_bytes(rand_buf, count); + brcmf_pcie_copy_mem_todev(devinfo, address, rand_buf, count); +} + static int brcmf_pcie_init_share_ram_info(struct brcmf_pciedev_info *devinfo, u32 sharedram_addr) @@ -1468,6 +1942,7 @@ brcmf_pcie_init_share_ram_info(struct brcmf_pciedev_info *devinfo, struct brcmf_bus *bus = dev_get_drvdata(&devinfo->pdev->dev); struct brcmf_pcie_shared_info *shared; u32 addr; + u32 host_cap; shared = &devinfo->shared; shared->tcm_base_address = sharedram_addr; @@ -1507,6 +1982,30 @@ brcmf_pcie_init_share_ram_info(struct brcmf_pciedev_info *devinfo, addr = sharedram_addr + BRCMF_SHARED_RING_INFO_ADDR_OFFSET; shared->ring_info_addr = brcmf_pcie_read_tcm32(devinfo, addr); + if (shared->version >= BRCMF_PCIE_SHARED_VERSION_6) { + host_cap = shared->version; + + /* Disable OOB Device Wake based DeepSleep State Machine */ + host_cap |= BRCMF_HOSTCAP_DS_NO_OOB_DW; + + devinfo->hostready = + ((shared->flags & BRCMF_PCIE_SHARED_HOSTRDY_DB1) + == BRCMF_PCIE_SHARED_HOSTRDY_DB1); + if (devinfo->hostready) { + brcmf_dbg(PCIE, "HostReady supported by dongle.\n"); + host_cap |= BRCMF_HOSTCAP_H2D_ENABLE_HOSTRDY; + } + devinfo->use_mailbox = + ((shared->flags & BRCMF_PCIE_SHARED_USE_MAILBOX) + == BRCMF_PCIE_SHARED_USE_MAILBOX); + devinfo->use_d0_inform = false; + addr = sharedram_addr + BRCMF_SHARED_HOST_CAP_OFFSET; + + brcmf_pcie_write_tcm32(devinfo, addr, host_cap); + } else { + devinfo->use_d0_inform = true; + } + brcmf_dbg(PCIE, "max rx buf post %d, rx dataoffset %d\n", shared->max_rxbufpost, shared->rx_dataoffset); @@ -1521,12 +2020,16 @@ static int brcmf_pcie_download_fw_nvram(struct brcmf_pciedev_info *devinfo, u32 nvram_len) { struct brcmf_bus *bus = dev_get_drvdata(&devinfo->pdev->dev); + struct trx_header_le *trx = (struct trx_header_le *)fw->data; + u32 fw_size; u32 sharedram_addr; u32 sharedram_addr_written; u32 loop_counter; int err; u32 address; u32 resetintr; + u32 nvram_lenw; + u32 nvram_csm; brcmf_dbg(PCIE, "Halt ARM.\n"); err = brcmf_pcie_enter_download_state(devinfo); @@ -1534,28 +2037,64 @@ static int brcmf_pcie_download_fw_nvram(struct brcmf_pciedev_info *devinfo, return err; brcmf_dbg(PCIE, "Download FW %s\n", devinfo->fw_name); - memcpy_toio(devinfo->tcm + devinfo->ci->rambase, - (void *)fw->data, fw->size); + address = devinfo->ci->rambase; + fw_size = fw->size; + if (trx->magic == cpu_to_le32(TRX_MAGIC)) { + address -= sizeof(struct trx_header_le); + fw_size = le32_to_cpu(trx->len); + } + brcmf_pcie_copy_mem_todev(devinfo, address, (void *)fw->data, fw_size); resetintr = get_unaligned_le32(fw->data); release_firmware(fw); - /* reset last 4 bytes of RAM address. to be used for shared - * area. This identifies when FW is running - */ - brcmf_pcie_write_ram32(devinfo, devinfo->ci->ramsize - 4, 0); + if (devinfo->ci->blhs) { + brcmf_pcie_bus_console_read(devinfo, false); + err = devinfo->ci->blhs->post_fwdl(devinfo->ci); + if (err) { + brcmf_err(bus, "FW download failed, err=%d\n", err); + return err; + } + + err = devinfo->ci->blhs->chk_validation(devinfo->ci); + if (err) { + brcmf_err(bus, "FW valication failed, err=%d\n", err); + return err; + } + } else { + /* reset last 4 bytes of RAM address. to be used for shared + * area. This identifies when FW is running + */ + brcmf_pcie_write_ram32(devinfo, devinfo->ci->ramsize - 4, 0); + } if (nvram) { brcmf_dbg(PCIE, "Download NVRAM %s\n", devinfo->nvram_name); address = devinfo->ci->rambase + devinfo->ci->ramsize - nvram_len; - memcpy_toio(devinfo->tcm + address, nvram, nvram_len); + if (devinfo->ci->blhs) + address -= 4; + brcmf_pcie_copy_mem_todev(devinfo, address, nvram, nvram_len); + + /* Convert nvram_len to words to determine the length token */ + nvram_lenw = nvram_len / 4; + nvram_csm = (~nvram_lenw << 16) | (nvram_lenw & 0x0000FFFF); brcmf_fw_nvram_free(nvram); } else { + nvram_csm = 0; brcmf_dbg(PCIE, "No matching NVRAM file found %s\n", devinfo->nvram_name); } + if (devinfo->ci->chip == CY_CC_55560_CHIP_ID) { + /* Write the length token to the last word of RAM address */ + brcmf_pcie_write_ram32(devinfo, devinfo->ci->ramsize - 4, + cpu_to_le32(nvram_csm)); + + /* Write random numbers to TCM for randomizing heap address */ + brcmf_pcie_write_rand(devinfo, nvram_csm); + } + sharedram_addr_written = brcmf_pcie_read_ram32(devinfo, devinfo->ci->ramsize - 4); @@ -1564,6 +2103,9 @@ static int brcmf_pcie_download_fw_nvram(struct brcmf_pciedev_info *devinfo, if (err) return err; + if (!brcmf_pcie_bus_readshared(devinfo, nvram_csm)) + brcmf_pcie_bus_console_read(devinfo, false); + brcmf_dbg(PCIE, "Wait for FW init\n"); sharedram_addr = sharedram_addr_written; loop_counter = BRCMF_PCIE_FW_UP_TIMEOUT / 50; @@ -1620,6 +2162,9 @@ static int brcmf_pcie_get_resource(struct brcmf_pciedev_info *devinfo) devinfo->regs = ioremap(bar0_addr, BRCMF_PCIE_REG_MAP_SIZE); devinfo->tcm = ioremap(bar1_addr, bar1_size); +#ifdef CONFIG_BRCMFMAC_PCIE_BARWIN_SZ + devinfo->bar1_size = bar1_size; +#endif /* CONFIG_BRCMFMAC_PCIE_BARWIN_SZ */ if (!devinfo->regs || !devinfo->tcm) { brcmf_err(bus, "ioremap() failed (%p,%p)\n", devinfo->regs, @@ -1646,6 +2191,21 @@ static void brcmf_pcie_release_resource(struct brcmf_pciedev_info *devinfo) pci_disable_device(devinfo->pdev); } +static u32 brcmf_pcie_buscore_blhs_read(void *ctx, u32 reg_offset) +{ + struct brcmf_pciedev_info *devinfo = (struct brcmf_pciedev_info *)ctx; + + brcmf_pcie_select_core(devinfo, BCMA_CORE_PCIE2); + return brcmf_pcie_read_reg32(devinfo, reg_offset); +} + +static void brcmf_pcie_buscore_blhs_write(void *ctx, u32 reg_offset, u32 value) +{ + struct brcmf_pciedev_info *devinfo = (struct brcmf_pciedev_info *)ctx; + + brcmf_pcie_select_core(devinfo, BCMA_CORE_PCIE2); + brcmf_pcie_write_reg32(devinfo, reg_offset, value); +} static u32 brcmf_pcie_buscore_prep_addr(const struct pci_dev *pdev, u32 addr) { @@ -1691,9 +2251,11 @@ static int brcmf_pcie_buscore_reset(void *ctx, struct brcmf_chip *chip) devinfo->ci = chip; brcmf_pcie_reset_device(devinfo); - val = brcmf_pcie_read_reg32(devinfo, BRCMF_PCIE_PCIE2REG_MAILBOXINT); + val = brcmf_pcie_read_reg32(devinfo, + BRCMF_PCIE_PCIE2REG_MAILBOXINT(devinfo)); if (val != 0xffffffff) - brcmf_pcie_write_reg32(devinfo, BRCMF_PCIE_PCIE2REG_MAILBOXINT, + brcmf_pcie_write_reg32(devinfo, + BRCMF_PCIE_PCIE2REG_MAILBOXINT(devinfo), val); return 0; @@ -1709,12 +2271,63 @@ static void brcmf_pcie_buscore_activate(void *ctx, struct brcmf_chip *chip, } +static int brcmf_pcie_buscore_blhs_attach(void *ctx, struct brcmf_blhs **blhs, + u32 flag, uint timeout, uint interval) +{ + struct brcmf_pciedev_info *devinfo = (struct brcmf_pciedev_info *)ctx; + struct brcmf_bus *bus = dev_get_drvdata(&devinfo->pdev->dev); + struct brcmf_blhs *blhsh; + u32 regdata; + u32 pcie_enum; + u32 addr; + + if (devinfo->pdev->vendor != CY_PCIE_VENDOR_ID_CYPRESS) + return 0; + + pci_read_config_dword(devinfo->pdev, BRCMF_PCIE_CFGREG_REVID, ®data); + if (regdata & BRCMF_PCIE_CFGREG_REVID_SECURE_MODE) { + blhsh = kzalloc(sizeof(*blhsh), GFP_KERNEL); + if (!blhsh) + return -ENOMEM; + + blhsh->d2h = BRCMF_PCIE_PCIE2REG_DAR_D2H_MSG_0; + blhsh->h2d = BRCMF_PCIE_PCIE2REG_DAR_H2D_MSG_0; + blhsh->read = brcmf_pcie_buscore_blhs_read; + blhsh->write = brcmf_pcie_buscore_blhs_write; + + /* Host indication for bootloarder to start the init */ + if (devinfo->pdev->device == CY_PCIE_55560_DEVICE_ID) + pcie_enum = BRCMF_CYW55560_PCIE_BAR0_PCIE_ENUM_OFFSET; + else + pcie_enum = BRCMF_PCIE_BAR0_PCIE_ENUM_OFFSET; + + pci_read_config_dword(devinfo->pdev, PCI_BASE_ADDRESS_0, + ®data); + addr = regdata + pcie_enum + blhsh->h2d; + brcmf_pcie_buscore_write32(ctx, addr, 0); + + addr = regdata + pcie_enum + blhsh->d2h; + SPINWAIT_MS((brcmf_pcie_buscore_read32(ctx, addr) & flag) == 0, + timeout, interval); + regdata = brcmf_pcie_buscore_read32(ctx, addr); + if (!(regdata & flag)) { + brcmf_err(bus, "Timeout waiting for bootloader ready\n"); + kfree(blhsh); + return -EPERM; + } + *blhs = blhsh; + } + + return 0; +} + static const struct brcmf_buscore_ops brcmf_pcie_buscore_ops = { .prepare = brcmf_pcie_buscoreprep, .reset = brcmf_pcie_buscore_reset, .activate = brcmf_pcie_buscore_activate, .read32 = brcmf_pcie_buscore_read32, .write32 = brcmf_pcie_buscore_write32, + .blhs_attach = brcmf_pcie_buscore_blhs_attach, }; #define BRCMF_PCIE_FW_CODE 0 @@ -1731,13 +2344,14 @@ static void brcmf_pcie_setup(struct device *dev, int ret, struct brcmf_commonring **flowrings; u32 i, nvram_len; + bus = dev_get_drvdata(dev); + pcie_bus_dev = bus->bus_priv.pcie; + devinfo = pcie_bus_dev->devinfo; + /* check firmware loading result */ if (ret) goto fail; - bus = dev_get_drvdata(dev); - pcie_bus_dev = bus->bus_priv.pcie; - devinfo = pcie_bus_dev->devinfo; brcmf_pcie_attach(devinfo); fw = fwreq->items[BRCMF_PCIE_FW_CODE].binary; @@ -1748,8 +2362,6 @@ static void brcmf_pcie_setup(struct device *dev, int ret, ret = brcmf_chip_get_raminfo(devinfo->ci); if (ret) { brcmf_err(bus, "Failed to get RAM info\n"); - release_firmware(fw); - brcmf_fw_nvram_free(nvram); goto fail; } @@ -1761,8 +2373,11 @@ static void brcmf_pcie_setup(struct device *dev, int ret, brcmf_pcie_adjust_ramsize(devinfo, (u8 *)fw->data, fw->size); ret = brcmf_pcie_download_fw_nvram(devinfo, fw, nvram, nvram_len); - if (ret) + if (ret) { + if (devinfo->ci->blhs && !brcmf_pcie_bus_readshared(devinfo, 0)) + brcmf_pcie_bus_console_read(devinfo, true); goto fail; + } devinfo->state = BRCMFMAC_PCIE_STATE_UP; @@ -1799,15 +2414,23 @@ static void brcmf_pcie_setup(struct device *dev, int ret, init_waitqueue_head(&devinfo->mbdata_resp_wait); - ret = brcmf_attach(&devinfo->pdev->dev); + brcmf_pcie_intr_enable(devinfo); + brcmf_pcie_hostready(devinfo); + + ret = brcmf_attach(&devinfo->pdev->dev, true); if (ret) goto fail; brcmf_pcie_bus_console_read(devinfo, false); + brcmf_pcie_fwcon_timer(devinfo, true); + return; fail: + brcmf_err(bus, "Dongle setup failed\n"); + brcmf_pcie_bus_console_read(devinfo, true); + brcmf_fw_crashed(dev); device_release_driver(dev); } @@ -1819,15 +2442,23 @@ brcmf_pcie_prepare_fw_request(struct brcmf_pciedev_info *devinfo) { ".bin", devinfo->fw_name }, { ".txt", devinfo->nvram_name }, }; + u32 chip; - fwreq = brcmf_fw_alloc_request(devinfo->ci->chip, devinfo->ci->chiprev, + if (devinfo->ci->blhs) + fwnames[BRCMF_PCIE_FW_CODE].extension = ".trxse"; + + chip = devinfo->ci->chip; + fwreq = brcmf_fw_alloc_request(chip, devinfo->ci->chiprev, brcmf_pcie_fwnames, ARRAY_SIZE(brcmf_pcie_fwnames), fwnames, ARRAY_SIZE(fwnames)); if (!fwreq) return NULL; - fwreq->items[BRCMF_PCIE_FW_CODE].type = BRCMF_FW_TYPE_BINARY; + if (devinfo->ci->blhs) + fwreq->items[BRCMF_PCIE_FW_CODE].type = BRCMF_FW_TYPE_TRXSE; + else + fwreq->items[BRCMF_PCIE_FW_CODE].type = BRCMF_FW_TYPE_BINARY; fwreq->items[BRCMF_PCIE_FW_NVRAM].type = BRCMF_FW_TYPE_NVRAM; fwreq->items[BRCMF_PCIE_FW_NVRAM].flags = BRCMF_FW_REQF_OPTIONAL; fwreq->board_type = devinfo->settings->board_type; @@ -1838,6 +2469,105 @@ brcmf_pcie_prepare_fw_request(struct brcmf_pciedev_info *devinfo) return fwreq; } +#ifdef DEBUG +static void +brcmf_pcie_fwcon_timer(struct brcmf_pciedev_info *devinfo, bool active) +{ + if (!active) { + if (devinfo->console_active) { + del_timer_sync(&devinfo->timer); + devinfo->console_active = false; + } + return; + } + + /* don't start the timer */ + if (devinfo->state != BRCMFMAC_PCIE_STATE_UP || + !devinfo->console_interval || !BRCMF_FWCON_ON()) + return; + + if (!devinfo->console_active) { + devinfo->timer.expires = jiffies + devinfo->console_interval; + add_timer(&devinfo->timer); + devinfo->console_active = true; + } else { + /* Reschedule the timer */ + mod_timer(&devinfo->timer, jiffies + devinfo->console_interval); + } +} + +static void +brcmf_pcie_fwcon(struct timer_list *t) +{ + struct brcmf_pciedev_info *devinfo = from_timer(devinfo, t, timer); + + if (!devinfo->console_active) + return; + + brcmf_pcie_bus_console_read(devinfo, false); + + /* Reschedule the timer if console interval is not zero */ + mod_timer(&devinfo->timer, jiffies + devinfo->console_interval); +} + +static int brcmf_pcie_console_interval_get(void *data, u64 *val) +{ + struct brcmf_pciedev_info *devinfo = data; + + *val = devinfo->console_interval; + + return 0; +} + +static int brcmf_pcie_console_interval_set(void *data, u64 val) +{ + struct brcmf_pciedev_info *devinfo = data; + + if (val > MAX_CONSOLE_INTERVAL) + return -EINVAL; + + devinfo->console_interval = val; + + if (!val && devinfo->console_active) + brcmf_pcie_fwcon_timer(devinfo, false); + else if (val) + brcmf_pcie_fwcon_timer(devinfo, true); + + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(brcmf_pcie_console_interval_fops, + brcmf_pcie_console_interval_get, + brcmf_pcie_console_interval_set, + "%llu\n"); + +static void brcmf_pcie_debugfs_create(struct device *dev) +{ + struct brcmf_bus *bus_if = dev_get_drvdata(dev); + struct brcmf_pub *drvr = bus_if->drvr; + struct brcmf_pciedev *pcie_bus_dev = bus_if->bus_priv.pcie; + struct brcmf_pciedev_info *devinfo = pcie_bus_dev->devinfo; + struct dentry *dentry = brcmf_debugfs_get_devdir(drvr); + + if (IS_ERR_OR_NULL(dentry)) + return; + + devinfo->console_interval = BRCMF_CONSOLE; + + debugfs_create_file("console_interval", 0644, dentry, devinfo, + &brcmf_pcie_console_interval_fops); +} + +#else +void brcmf_pcie_fwcon_timer(struct brcmf_pciedev_info *devinfo, bool active) +{ +} + +static void brcmf_pcie_debugfs_create(struct device *dev) +{ +} +#endif + static int brcmf_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id) { @@ -1905,6 +2635,10 @@ brcmf_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id) if (ret) goto fail_bus; +#ifdef DEBUG + /* Set up the fwcon timer */ + timer_setup(&devinfo->timer, brcmf_pcie_fwcon, 0); +#endif fwreq = brcmf_pcie_prepare_fw_request(devinfo); if (!fwreq) { ret = -ENOMEM; @@ -1950,6 +2684,8 @@ brcmf_pcie_remove(struct pci_dev *pdev) devinfo = bus->bus_priv.pcie->devinfo; + brcmf_pcie_fwcon_timer(devinfo, false); + devinfo->state = BRCMFMAC_PCIE_STATE_DOWN; if (devinfo->ci) brcmf_pcie_intr_disable(devinfo); @@ -1985,11 +2721,24 @@ static int brcmf_pcie_pm_enter_D3(struct device *dev) { struct brcmf_pciedev_info *devinfo; struct brcmf_bus *bus; + struct brcmf_cfg80211_info *config; + int retry = BRCMF_PM_WAIT_MAXRETRY; brcmf_dbg(PCIE, "Enter\n"); bus = dev_get_drvdata(dev); devinfo = bus->bus_priv.pcie->devinfo; + config = bus->drvr->config; + + while (retry && + config->pm_state == BRCMF_CFG80211_PM_STATE_SUSPENDING) { + usleep_range(10000, 20000); + retry--; + } + if (!retry && config->pm_state == BRCMF_CFG80211_PM_STATE_SUSPENDING) + brcmf_err(bus, "timed out wait for cfg80211 suspended\n"); + + brcmf_pcie_fwcon_timer(devinfo, false); brcmf_bus_change_state(bus, BRCMF_BUS_DOWN); @@ -2026,14 +2775,26 @@ static int brcmf_pcie_pm_leave_D3(struct device *dev) /* Check if device is still up and running, if so we are ready */ if (brcmf_pcie_read_reg32(devinfo, BRCMF_PCIE_PCIE2REG_INTMASK) != 0) { brcmf_dbg(PCIE, "Try to wakeup device....\n"); - if (brcmf_pcie_send_mb_data(devinfo, BRCMF_H2D_HOST_D0_INFORM)) - goto cleanup; + if (devinfo->use_d0_inform) { + if (brcmf_pcie_send_mb_data(devinfo, + BRCMF_H2D_HOST_D0_INFORM)) + goto cleanup; + } else { + brcmf_pcie_hostready(devinfo); + } + brcmf_dbg(PCIE, "Hot resume, continue....\n"); devinfo->state = BRCMFMAC_PCIE_STATE_UP; brcmf_pcie_select_core(devinfo, BCMA_CORE_PCIE2); brcmf_bus_change_state(bus, BRCMF_BUS_UP); brcmf_pcie_intr_enable(devinfo); - brcmf_pcie_hostready(devinfo); + if (devinfo->use_d0_inform) { + brcmf_dbg(TRACE, "sending brcmf_pcie_hostready since use_d0_inform=%d\n", + devinfo->use_d0_inform); + brcmf_pcie_hostready(devinfo); + } + + brcmf_pcie_fwcon_timer(devinfo, true); return 0; } @@ -2045,7 +2806,7 @@ static int brcmf_pcie_pm_leave_D3(struct device *dev) err = brcmf_pcie_probe(pdev, NULL); if (err) - __brcmf_err(NULL, __func__, "probe after resume failed, err=%d\n", err); + brcmf_err(bus, "probe after resume failed, err=%d\n", err); return err; } @@ -2068,6 +2829,9 @@ static const struct dev_pm_ops brcmf_pciedrvr_pm = { BRCM_PCIE_VENDOR_ID_BROADCOM, dev_id,\ subvend, subdev, PCI_CLASS_NETWORK_OTHER << 8, 0xffff00, 0 } +#define BRCMF_PCIE_DEVICE_CY(dev_id) { CY_PCIE_VENDOR_ID_CYPRESS, dev_id,\ + PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_NETWORK_OTHER << 8, 0xffff00, 0 } + static const struct pci_device_id brcmf_pcie_devid_table[] = { BRCMF_PCIE_DEVICE(BRCM_PCIE_4350_DEVICE_ID), BRCMF_PCIE_DEVICE_SUB(0x4355, BRCM_PCIE_VENDOR_ID_BROADCOM, 0x4355), @@ -2075,6 +2839,7 @@ static const struct pci_device_id brcmf_pcie_devid_table[] = { BRCMF_PCIE_DEVICE(BRCM_PCIE_4356_DEVICE_ID), BRCMF_PCIE_DEVICE(BRCM_PCIE_43567_DEVICE_ID), BRCMF_PCIE_DEVICE(BRCM_PCIE_43570_DEVICE_ID), + BRCMF_PCIE_DEVICE(BRCM_PCIE_43570_RAW_DEVICE_ID), BRCMF_PCIE_DEVICE(BRCM_PCIE_4358_DEVICE_ID), BRCMF_PCIE_DEVICE(BRCM_PCIE_4359_DEVICE_ID), BRCMF_PCIE_DEVICE(BRCM_PCIE_43602_DEVICE_ID), @@ -2090,13 +2855,21 @@ static const struct pci_device_id brcmf_pcie_devid_table[] = { BRCMF_PCIE_DEVICE(BRCM_PCIE_4366_2G_DEVICE_ID), BRCMF_PCIE_DEVICE(BRCM_PCIE_4366_5G_DEVICE_ID), BRCMF_PCIE_DEVICE(BRCM_PCIE_4371_DEVICE_ID), + BRCMF_PCIE_DEVICE(CY_PCIE_89459_DEVICE_ID), + BRCMF_PCIE_DEVICE(CY_PCIE_89459_RAW_DEVICE_ID), + BRCMF_PCIE_DEVICE(CY_PCIE_54591_DEVICE_ID), + BRCMF_PCIE_DEVICE(CY_PCIE_54590_DEVICE_ID), + BRCMF_PCIE_DEVICE(CY_PCIE_54594_DEVICE_ID), + BRCMF_PCIE_DEVICE_CY(CY_PCIE_55560_DEVICE_ID), + BRCMF_PCIE_DEVICE(CY_PCIE_4373_RAW_DEVICE_ID), + BRCMF_PCIE_DEVICE(CY_PCIE_4373_DUAL_DEVICE_ID), + BRCMF_PCIE_DEVICE(CY_PCIE_4373_2G_DEVICE_ID), + BRCMF_PCIE_DEVICE(CY_PCIE_4373_5G_DEVICE_ID), { /* end: all zeroes */ } }; - MODULE_DEVICE_TABLE(pci, brcmf_pcie_devid_table); - static struct pci_driver brcmf_pciedrvr = { .node = {}, .name = KBUILD_MODNAME, @@ -2106,17 +2879,22 @@ static struct pci_driver brcmf_pciedrvr = { #ifdef CONFIG_PM .driver.pm = &brcmf_pciedrvr_pm, #endif +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 17, 0)) .driver.coredump = brcmf_dev_coredump, +#endif }; - -int brcmf_pcie_register(void) +void brcmf_pcie_register(void) { + int err; + brcmf_dbg(PCIE, "Enter\n"); - return pci_register_driver(&brcmf_pciedrvr); + err = pci_register_driver(&brcmf_pciedrvr); + if (err) + brcmf_err(NULL, "PCIE driver registration failed, err=%d\n", + err); } - void brcmf_pcie_exit(void) { brcmf_dbg(PCIE, "Enter\n"); diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.h index 8e6c227e8315c..4a15975fe8c95 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.h @@ -11,4 +11,9 @@ struct brcmf_pciedev { struct brcmf_pciedev_info *devinfo; }; + +void brcmf_pcie_exit(void); +void brcmf_pcie_register(void); +void brcmf_pcie_handle_mb_data(struct brcmf_bus *bus_if, u32 d2h_mb_data); + #endif /* BRCMFMAC_PCIE_H */ diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.c index d0a7465be586d..bbb6d3ca0615a 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pno.c @@ -44,7 +44,11 @@ static int brcmf_pno_store_request(struct brcmf_pno_info *pi, "pno request storage full\n")) return -ENOSPC; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)) brcmf_dbg(SCAN, "reqid=%llu\n", req->reqid); +#else /* (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)) */ + brcmf_dbg(SCAN, "reqid=0"); +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)) */ mutex_lock(&pi->req_lock); pi->reqs[pi->n_reqs++] = req; mutex_unlock(&pi->req_lock); @@ -61,11 +65,15 @@ static int brcmf_pno_remove_request(struct brcmf_pno_info *pi, u64 reqid) if (pi->n_reqs == 0) goto done; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)) /* find request */ for (i = 0; i < pi->n_reqs; i++) { if (pi->reqs[i]->reqid == reqid) break; } +#else /* (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)) */ + i = reqid; +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)) */ /* request not found */ if (WARN(i == pi->n_reqs, "reqid not found\n")) { err = -ENOENT; @@ -184,8 +192,13 @@ static int brcmf_pno_set_random(struct brcmf_if *ifp, struct brcmf_pno_info *pi) /* Set locally administered */ pfn_mac.mac[0] |= 0x02; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)) brcmf_dbg(SCAN, "enabling random mac: reqid=%llu mac=%pM\n", pi->reqs[ri]->reqid, pfn_mac.mac); +#else /* (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)) */ + brcmf_dbg(SCAN, "enabling random mac: reqid=0 mac=%pM\n", + pfn_mac.mac); +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)) */ err = brcmf_fil_iovar_data_set(ifp, "pfn_macaddr", &pfn_mac, sizeof(pfn_mac)); if (err) @@ -218,6 +231,7 @@ static int brcmf_pno_add_ssid(struct brcmf_if *ifp, struct cfg80211_ssid *ssid, return err; } +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)) static int brcmf_pno_add_bssid(struct brcmf_if *ifp, const u8 *bssid) { struct brcmf_pub *drvr = ifp->drvr; @@ -234,6 +248,7 @@ static int brcmf_pno_add_bssid(struct brcmf_if *ifp, const u8 *bssid) bphy_err(drvr, "adding failed: err=%d\n", err); return err; } +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)) */ static bool brcmf_is_ssid_active(struct cfg80211_ssid *ssid, struct cfg80211_sched_scan_request *req) @@ -378,8 +393,10 @@ static int brcmf_pno_config_networks(struct brcmf_if *ifp, err = brcmf_pno_add_ssid(ifp, &ms->ssid, active); } +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)) if (!err && is_valid_ether_addr(ms->bssid)) err = brcmf_pno_add_bssid(ifp, ms->bssid); +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)) */ if (err < 0) return err; @@ -470,7 +487,11 @@ int brcmf_pno_start_sched_scan(struct brcmf_if *ifp, struct brcmf_pno_info *pi; int ret; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)) brcmf_dbg(TRACE, "reqid=%llu\n", req->reqid); +#else /* (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)) */ + brcmf_dbg(TRACE, "reqid=0"); +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)) */ pi = ifp_to_pno(ifp); ret = brcmf_pno_store_request(pi, req); @@ -479,7 +500,11 @@ int brcmf_pno_start_sched_scan(struct brcmf_if *ifp, ret = brcmf_pno_config_sched_scans(ifp); if (ret < 0) { +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)) brcmf_pno_remove_request(pi, req->reqid); +#else /* (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)) */ + brcmf_pno_remove_request(pi, 0); +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)) */ if (pi->n_reqs) (void)brcmf_pno_config_sched_scans(ifp); return ret; @@ -542,7 +567,9 @@ void brcmf_pno_detach(struct brcmf_cfg80211_info *cfg) void brcmf_pno_wiphy_params(struct wiphy *wiphy, bool gscan) { /* scheduled scan settings */ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)) wiphy->max_sched_scan_reqs = gscan ? BRCMF_PNO_MAX_BUCKETS : 1; +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)) */ 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; @@ -553,12 +580,14 @@ u64 brcmf_pno_find_reqid_by_bucket(struct brcmf_pno_info *pi, u32 bucket) { u64 reqid = 0; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)) mutex_lock(&pi->req_lock); if (bucket < pi->n_reqs) reqid = pi->reqs[bucket]->reqid; mutex_unlock(&pi->req_lock); +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)) */ return reqid; } @@ -583,11 +612,13 @@ u32 brcmf_pno_get_bucket_map(struct brcmf_pno_info *pi, bucket_map |= BIT(i); break; } +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)) if (is_valid_ether_addr(ms->bssid) && !memcmp(ms->bssid, ni->bssid, ETH_ALEN)) { bucket_map |= BIT(i); break; } +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)) */ } } mutex_unlock(&pi->req_lock); diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c index 9929e90866f04..7fc2129c0bfc9 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c @@ -3,6 +3,7 @@ * Copyright (c) 2010 Broadcom Corporation */ +#include #include #include #include @@ -11,7 +12,12 @@ #include #include #include +#include +#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 11, 0)) +#include +#else /* (LINUX_VERSION_CODE < KERNEL_VERSION(4, 11, 0)) */ #include +#endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(4, 11, 0)) */ #include #include #include @@ -23,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -35,14 +42,20 @@ #include "core.h" #include "common.h" #include "bcdc.h" +#include "fwil.h" +#include "bt_shared_sdio.h" +#include "trxhdr.h" #define DCMD_RESP_TIMEOUT msecs_to_jiffies(2500) #define CTL_DONE_TIMEOUT msecs_to_jiffies(2500) +#define ULP_HUDI_PROC_DONE_TIME msecs_to_jiffies(2500) /* watermark expressed in number of words */ #define DEFAULT_F2_WATERMARK 0x8 -#define CY_4373_F2_WATERMARK 0x40 -#define CY_4373_F1_MESBUSYCTRL (CY_4373_F2_WATERMARK | SBSDIO_MESBUSYCTRL_ENAB) +#define CY_4373_F2_WATERMARK 0x4C +#define CY_4373_MES_WATERMARK 0x44 +#define CY_4373_MESBUSYCTRL (CY_4373_MES_WATERMARK | \ + SBSDIO_MESBUSYCTRL_ENAB) #define CY_43012_F2_WATERMARK 0x60 #define CY_43012_MES_WATERMARK 0x50 #define CY_43012_MESBUSYCTRL (CY_43012_MES_WATERMARK | \ @@ -51,13 +64,21 @@ #define CY_4339_MES_WATERMARK 80 #define CY_4339_MESBUSYCTRL (CY_4339_MES_WATERMARK | \ SBSDIO_MESBUSYCTRL_ENAB) -#define CY_43455_F2_WATERMARK 0x60 -#define CY_43455_MES_WATERMARK 0x50 +#define CY_43455_F2_WATERMARK 0x40 +#define CY_43455_MES_WATERMARK 0x40 #define CY_43455_MESBUSYCTRL (CY_43455_MES_WATERMARK | \ SBSDIO_MESBUSYCTRL_ENAB) #define CY_435X_F2_WATERMARK 0x40 #define CY_435X_F1_MESBUSYCTRL (CY_435X_F2_WATERMARK | \ SBSDIO_MESBUSYCTRL_ENAB) +#define CY_89459_F2_WATERMARK 0x40 +#define CY_89459_MES_WATERMARK 0x40 +#define CY_89459_MESBUSYCTRL (CY_89459_MES_WATERMARK | \ + SBSDIO_MESBUSYCTRL_ENAB) +#define CYW55560_F2_WATERMARK 0x40 +#define CYW55560_MES_WATERMARK 0x40 +#define CYW55560_F1_MESBUSYCTRL (CYW55560_MES_WATERMARK | \ + SBSDIO_MESBUSYCTRL_ENAB) #ifdef DEBUG @@ -135,8 +156,6 @@ struct rte_console { #define BRCMF_FIRSTREAD (1 << 6) -#define BRCMF_CONSOLE 10 /* watchdog interval to poll console */ - /* SBSDIO_DEVICE_CTL */ /* 1: device will assert busy signal when receiving CMD53 */ @@ -327,7 +346,16 @@ struct rte_console { #define KSO_WAIT_US 50 #define MAX_KSO_ATTEMPTS (PMU_MAX_TRANSITION_DLY/KSO_WAIT_US) -#define BRCMF_SDIO_MAX_ACCESS_ERRORS 5 +#define BRCMF_SDIO_MAX_ACCESS_ERRORS 20 + +static void brcmf_sdio_firmware_callback(struct device *dev, int err, + struct brcmf_fw_request *fwreq); +static struct brcmf_fw_request * + brcmf_sdio_prepare_fw_request(struct brcmf_sdio *bus); +static int brcmf_sdio_f2_ready(struct brcmf_sdio *bus); +static int brcmf_ulp_event_notify(struct brcmf_if *ifp, + const struct brcmf_event_msg *evtmsg, + void *data); #ifdef DEBUG /* Device console log buffer state */ @@ -557,7 +585,7 @@ enum brcmf_sdio_frmtype { BRCMF_SDIO_FT_SUB, }; -#define SDIOD_DRVSTR_KEY(chip, pmu) (((unsigned int)(chip) << 16) | (pmu)) +#define SDIOD_DRVSTR_KEY(chip, pmu) (((chip) << 16) | (pmu)) /* SDIO Pad drive strength to select value mappings */ struct sdiod_drive_str { @@ -610,20 +638,23 @@ BRCMF_FW_DEF(43241B5, "brcmfmac43241b5-sdio"); BRCMF_FW_DEF(4329, "brcmfmac4329-sdio"); BRCMF_FW_DEF(4330, "brcmfmac4330-sdio"); BRCMF_FW_DEF(4334, "brcmfmac4334-sdio"); -BRCMF_FW_DEF(43340, "brcmfmac43340-sdio"); +CY_FW_DEF(43340, "cyfmac43340-sdio"); BRCMF_FW_DEF(4335, "brcmfmac4335-sdio"); -BRCMF_FW_DEF(43362, "brcmfmac43362-sdio"); -BRCMF_FW_DEF(4339, "brcmfmac4339-sdio"); +CY_FW_DEF(43362, "cyfmac43362-sdio"); +CY_FW_DEF(4339, "cyfmac4339-sdio"); BRCMF_FW_DEF(43430A0, "brcmfmac43430a0-sdio"); /* Note the names are not postfixed with a1 for backward compatibility */ -BRCMF_FW_DEF(43430A1, "brcmfmac43430-sdio"); -BRCMF_FW_DEF(43455, "brcmfmac43455-sdio"); +CY_FW_DEF(43430A1, "cyfmac43430-sdio"); +CY_FW_DEF(43439, "cyfmac43439-sdio"); +CY_FW_DEF(43455, "cyfmac43455-sdio"); BRCMF_FW_DEF(43456, "brcmfmac43456-sdio"); -BRCMF_FW_DEF(4354, "brcmfmac4354-sdio"); -BRCMF_FW_DEF(4356, "brcmfmac4356-sdio"); -BRCMF_FW_DEF(4359, "brcmfmac4359-sdio"); -BRCMF_FW_DEF(4373, "brcmfmac4373-sdio"); -BRCMF_FW_DEF(43012, "brcmfmac43012-sdio"); +CY_FW_DEF(4354, "cyfmac4354-sdio"); +CY_FW_DEF(4356, "cyfmac4356-sdio"); +CY_FW_DEF(4359, "cyfmac4359-sdio"); +CY_FW_DEF(4373, "cyfmac4373-sdio"); +CY_FW_DEF(43012, "cyfmac43012-sdio"); +CY_FW_DEF(89459, "cyfmac54591-sdio"); +CY_FW_TRXSE_DEF(55560, "cyfmac55560-sdio"); static const struct brcmf_firmware_mapping brcmf_sdio_fwnames[] = { BRCMF_FW_ENTRY(BRCM_CC_43143_CHIP_ID, 0xFFFFFFFF, 43143), @@ -639,14 +670,18 @@ static const struct brcmf_firmware_mapping brcmf_sdio_fwnames[] = { BRCMF_FW_ENTRY(BRCM_CC_43362_CHIP_ID, 0xFFFFFFFE, 43362), BRCMF_FW_ENTRY(BRCM_CC_4339_CHIP_ID, 0xFFFFFFFF, 4339), BRCMF_FW_ENTRY(BRCM_CC_43430_CHIP_ID, 0x00000001, 43430A0), - BRCMF_FW_ENTRY(BRCM_CC_43430_CHIP_ID, 0xFFFFFFFE, 43430A1), + BRCMF_FW_ENTRY(BRCM_CC_43430_CHIP_ID, 0x0000001E, 43430A1), + BRCMF_FW_ENTRY(BRCM_CC_43430_CHIP_ID, 0xFFFFFFE0, 43439), BRCMF_FW_ENTRY(BRCM_CC_4345_CHIP_ID, 0x00000200, 43456), BRCMF_FW_ENTRY(BRCM_CC_4345_CHIP_ID, 0xFFFFFDC0, 43455), BRCMF_FW_ENTRY(BRCM_CC_4354_CHIP_ID, 0xFFFFFFFF, 4354), BRCMF_FW_ENTRY(BRCM_CC_4356_CHIP_ID, 0xFFFFFFFF, 4356), BRCMF_FW_ENTRY(BRCM_CC_4359_CHIP_ID, 0xFFFFFFFF, 4359), + BRCMF_FW_ENTRY(CY_CC_43439_CHIP_ID, 0xFFFFFFFF, 43439), BRCMF_FW_ENTRY(CY_CC_4373_CHIP_ID, 0xFFFFFFFF, 4373), - BRCMF_FW_ENTRY(CY_CC_43012_CHIP_ID, 0xFFFFFFFF, 43012) + BRCMF_FW_ENTRY(CY_CC_43012_CHIP_ID, 0xFFFFFFFF, 43012), + BRCMF_FW_ENTRY(CY_CC_89459_CHIP_ID, 0xFFFFFFFF, 89459), + BRCMF_FW_ENTRY(CY_CC_55560_CHIP_ID, 0xFFFFFFFF, 55560) }; #define TXCTL_CREDITS 2 @@ -672,7 +707,6 @@ static bool data_ok(struct brcmf_sdio *bus) return (bus->tx_max - bus->tx_seq - tx_rsv) != 0 && ((bus->tx_max - bus->tx_seq - tx_rsv) & 0x80) == 0; - } /* To check if there's window offered */ @@ -692,11 +726,13 @@ brcmf_sdio_kso_control(struct brcmf_sdio *bus, bool on) brcmf_dbg(TRACE, "Enter: on=%d\n", on); +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 1, 15)) sdio_retune_crc_disable(bus->sdiodev->func1); /* Cannot re-tune if device is asleep; defer till we're awake */ if (on) sdio_retune_hold_now(bus->sdiodev->func1); +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 1, 15)) */ wr_val = (on << SBSDIO_FUNC1_SLEEPCSR_KSO_SHIFT); /* 1st KSO write goes to AOS wake up core if device is asleep */ @@ -707,7 +743,8 @@ brcmf_sdio_kso_control(struct brcmf_sdio *bus, bool on) * fail. Thereby just bailing out immediately after clearing KSO * bit, to avoid polling of KSO bit. */ - if (!on && bus->ci->chip == CY_CC_43012_CHIP_ID) + if (!on && ((bus->ci->chip == CY_CC_43012_CHIP_ID) || + (bus->ci->chip == CY_CC_55560_CHIP_ID))) return err; if (on) { @@ -758,10 +795,12 @@ brcmf_sdio_kso_control(struct brcmf_sdio *bus, bool on) if (try_cnt > MAX_KSO_ATTEMPTS) brcmf_err("max tries: rd_val=0x%x err=%d\n", rd_val, err); +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 1, 15)) if (on) sdio_retune_release(bus->sdiodev->func1); sdio_retune_crc_enable(bus->sdiodev->func1); +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 1, 15)) */ return err; } @@ -924,6 +963,20 @@ static int brcmf_sdio_clkctl(struct brcmf_sdio *bus, uint target, bool pendok) break; case CLK_SDONLY: +#ifdef CONFIG_BRCMFMAC_BT_SHARED_SDIO + /* If the request is to switch off backplane clock, + * confirm that BT is inactive before doing so. + * If this call had come from Non Watchdog context any way + * the Watchdog would switch off the clock again when + * nothing is to be done & BT has finished using the bus. + */ + if (brcmf_btsdio_bus_count(bus->sdiodev->bus_if)) { + brcmf_dbg(SDIO, "BT is active, not switching off\n"); + brcmf_sdio_wd_timer(bus, true); + break; + } + +#endif /* CONFIG_BRCMFMAC_BT_SHARED_SDIO */ /* Remove HT request, or bring up SD clock */ if (bus->clkstate == CLK_NONE) brcmf_sdio_sdclk(bus, true); @@ -935,6 +988,19 @@ static int brcmf_sdio_clkctl(struct brcmf_sdio *bus, uint target, bool pendok) break; case CLK_NONE: +#ifdef CONFIG_BRCMFMAC_BT_SHARED_SDIO + /* If the request is to switch off backplane clock, + * confirm that BT is inactive before doing so. + * If this call had come from non-watchdog context any way + * the watchdog would switch off the clock again when + * nothing is to be done & BT has finished using the bus. + */ + if (brcmf_btsdio_bus_count(bus->sdiodev->bus_if)) { + brcmf_dbg(SDIO, "BT is active, not switching off\n"); + break; + } +#endif /* CONFIG_BRCMFMAC_BT_SHARED_SDIO */ + /* Make sure to remove HT request */ if (bus->clkstate == CLK_AVAIL) brcmf_sdio_htclk(bus, false, false); @@ -959,6 +1025,29 @@ brcmf_sdio_bus_sleep(struct brcmf_sdio *bus, bool sleep, bool pendok) (sleep ? "SLEEP" : "WAKE"), (bus->sleeping ? "SLEEP" : "WAKE")); +#ifdef CONFIG_BRCMFMAC_BT_SHARED_SDIO + /* The following is the assumption based on which the hook is placed. + * From WLAN driver, either from the active contexts OR from the + * watchdog contexts, we will be attempting to go to sleep. At that + * moment if we see that BT is still actively using the bus, we will + * return -EBUSY from here, and the bus sleep state would not have + * changed, so the caller can then schedule the watchdog again + * which will come and attempt to sleep at a later point. + * + * In case if BT is the only one and is the last user, we don't switch + * off the clock immediately, we allow the WLAN to decide when to sleep + * i.e from the watchdog. + * Now if the watchdog becomes active and attempts to switch off the + * clock and if another WLAN context is active they are any way + * serialized with sdlock. + */ + if (brcmf_btsdio_bus_count(bus->sdiodev->bus_if)) { + brcmf_dbg(SDIO, "Cannot sleep when BT is active\n"); + err = -EBUSY; + goto done; + } +#endif /* CONFIG_BRCMFMAC_BT_SHARED_SDIO */ + /* If SR is enabled control bus state with KSO */ if (bus->sr_enabled) { /* Done if we're already in the requested state */ @@ -1093,7 +1182,7 @@ static void brcmf_sdio_get_console_addr(struct brcmf_sdio *bus) } #endif /* DEBUG */ -static u32 brcmf_sdio_hostmail(struct brcmf_sdio *bus) +static u32 brcmf_sdio_hostmail(struct brcmf_sdio *bus, u32 *hmbd) { struct brcmf_sdio_dev *sdiod = bus->sdiodev; struct brcmf_core *core = bus->sdio_core; @@ -1182,6 +1271,9 @@ static u32 brcmf_sdio_hostmail(struct brcmf_sdio *bus) HMB_DATA_FCDATA_MASK | HMB_DATA_VERSION_MASK)) brcmf_err("Unknown mailbox data content: 0x%02x\n", hmb_data); + /* Populate hmb_data if argument is passed for DS1 check later */ + if (hmbd) + *hmbd = hmb_data; return intstatus; } @@ -2235,7 +2327,11 @@ brcmf_sdio_txpkt_prep(struct brcmf_sdio *bus, struct sk_buff_head *pktq, * length of the chain (including padding) */ if (bus->txglom) +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 20, 0)) brcmf_sdio_update_hwhdr(__skb_peek(pktq)->data, total_len); +#else /* (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 20, 0)) */ + brcmf_sdio_update_hwhdr(pktq->next->data, total_len); +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 20, 0)) */ return 0; } @@ -2345,6 +2441,9 @@ static uint brcmf_sdio_sendfromq(struct brcmf_sdio *bus, uint maxframes) &prec_out); if (pkt == NULL) break; +#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 16, 0)) + skb_orphan(pkt); +#endif __skb_queue_tail(&pktq, pkt); } spin_unlock_bh(&bus->txq_lock); @@ -2451,6 +2550,15 @@ static bool brcmf_chip_is_ulp(struct brcmf_chip *ci) return false; } +static bool brcmf_sdio_use_ht_avail(struct brcmf_chip *ci) +{ + if (ci->chip == CY_CC_4373_CHIP_ID || + ci->chip == CY_CC_55560_CHIP_ID) + return true; + else + return false; +} + static void brcmf_sdio_bus_stop(struct device *dev) { struct brcmf_bus *bus_if = dev_get_drvdata(dev); @@ -2487,7 +2595,8 @@ static void brcmf_sdio_bus_stop(struct device *dev) &err); if (!err) { bpreq = saveclk; - bpreq |= brcmf_chip_is_ulp(bus->ci) ? + bpreq |= (brcmf_sdio_use_ht_avail(bus->ci) || + brcmf_chip_is_ulp(bus->ci)) ? SBSDIO_HT_AVAIL_REQ : SBSDIO_FORCE_HT; brcmf_sdiod_writeb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, @@ -2568,6 +2677,182 @@ static int brcmf_sdio_intr_rstatus(struct brcmf_sdio *bus) return ret; } +/* This Function is used to retrieve important + * details from dongle related to ULP mode Mostly + * values/SHM details that will be vary depending + * on the firmware branches + */ +static void +brcmf_sdio_ulp_preinit(struct device *dev) +{ + struct brcmf_bus *bus_if = dev_get_drvdata(dev); + struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio; + struct brcmf_if *ifp = bus_if->drvr->iflist[0]; + + brcmf_dbg(ULP, "Enter\n"); + + /* Query ulp_sdioctrl iovar to get the ULP related SHM offsets */ + brcmf_fil_iovar_data_get(ifp, "ulp_sdioctrl", + &sdiodev->fmac_ulp.ulp_shm_offset, + sizeof(sdiodev->fmac_ulp.ulp_shm_offset)); + + sdiodev->ulp = false; + + brcmf_dbg(ULP, "m_ulp_ctrl_sdio[%x] m_ulp_wakeevt_ind [%x]\n", + M_DS1_CTRL_SDIO(sdiodev->fmac_ulp), + M_WAKEEVENT_IND(sdiodev->fmac_ulp)); + brcmf_dbg(ULP, "m_ulp_wakeind [%x]\n", + M_ULP_WAKE_IND(sdiodev->fmac_ulp)); +} + +/* Reinitialize ARM because In DS1 mode ARM got off */ +static int +brcmf_sdio_ulp_reinit_fw(struct brcmf_sdio *bus) +{ + struct brcmf_sdio_dev *sdiodev = bus->sdiodev; + struct brcmf_fw_request *fwreq; + int err = 0; + + /* After firmware redownload tx/rx seq are reset accordingly + * these values are reset on FMAC side tx_max is initially set to 4, + * which later is updated by FW. + */ + bus->tx_seq = 0; + bus->rx_seq = 0; + bus->tx_max = 4; + + fwreq = brcmf_sdio_prepare_fw_request(bus); + if (!fwreq) + return -ENOMEM; + + err = brcmf_fw_get_firmwares(sdiodev->dev, fwreq, + brcmf_sdio_firmware_callback); + if (err != 0) { + brcmf_err("async firmware request failed: %d\n", err); + kfree(fwreq); + } + + return err; +} + +/* Check if device is in DS1 mode and handshake with ULP UCODE */ +static bool +brcmf_sdio_ulp_pre_redownload_check(struct brcmf_sdio *bus, u32 hmb_data) +{ + struct brcmf_sdio_dev *sdiod = bus->sdiodev; + int err = 0; + u32 value = 0; + u32 val32, ulp_wake_ind, wowl_wake_ind; + int reg_addr; + unsigned long timeout; + struct brcmf_ulp *fmac_ulp = &bus->sdiodev->fmac_ulp; + int i = 0; + + /* If any host mail box data is present, ignore DS1 exit sequence */ + if (hmb_data) + return false; + /* Skip if DS1 Exit is already in progress + * This can happen if firmware download is taking more time + */ + if (fmac_ulp->ulp_state == FMAC_ULP_TRIGGERED) + return false; + + value = brcmf_sdiod_func0_rb(sdiod, SDIO_CCCR_IOEx, &err); + + if (value == SDIO_FUNC_ENABLE_1) { + brcmf_dbg(ULP, "GOT THE INTERRUPT FROM UCODE\n"); + sdiod->ulp = true; + fmac_ulp->ulp_state = FMAC_ULP_TRIGGERED; + ulp_wake_ind = D11SHM_RDW(sdiod, + M_ULP_WAKE_IND(sdiod->fmac_ulp), + &err); + wowl_wake_ind = D11SHM_RDW(sdiod, + M_WAKEEVENT_IND(sdiod->fmac_ulp), + &err); + + brcmf_dbg(ULP, "wowl_wake_ind: 0x%08x, ulp_wake_ind: 0x%08x state %s\n", + wowl_wake_ind, ulp_wake_ind, (fmac_ulp->ulp_state) ? + "DS1 Exit Triggered" : "IDLE State"); + + if (wowl_wake_ind || ulp_wake_ind) { + /* RX wake Don't do anything. + * Just bail out and re-download firmware. + */ + /* Print out PHY TX error block when bit 9 set */ + if ((ulp_wake_ind & C_DS1_PHY_TXERR) && + M_DS1_PHYTX_ERR_BLK(sdiod->fmac_ulp)) { + brcmf_err("Dump PHY TX Error SHM Locations\n"); + for (i = 0; i < PHYTX_ERR_BLK_SIZE; i++) { + pr_err("0x%x", + D11SHM_RDW(sdiod, + (M_DS1_PHYTX_ERR_BLK(sdiod->fmac_ulp) + + (i * 2)), &err)); + } + brcmf_err("\n"); + } + } else { + /* TX wake negotiate with MAC */ + brcmf_dbg(ULP, "M_DS1_CTRL_SDIO: 0x%08x\n", + (u32)D11SHM_RDW(sdiod, + M_DS1_CTRL_SDIO(sdiod->fmac_ulp), + &err)); + val32 = D11SHM_RD(sdiod, + M_DS1_CTRL_SDIO(sdiod->fmac_ulp), + &err); + D11SHM_WR(sdiod, M_DS1_CTRL_SDIO(sdiod->fmac_ulp), + val32, (C_DS1_CTRL_SDIO_DS1_EXIT | + C_DS1_CTRL_REQ_VALID), &err); + val32 = D11REG_RD(sdiod, D11_MACCONTROL_REG, &err); + val32 = val32 | D11_MACCONTROL_REG_WAKE; + D11REG_WR(sdiod, D11_MACCONTROL_REG, val32, &err); + + /* Poll for PROC_DONE to be set by ucode */ + value = D11SHM_RDW(sdiod, + M_DS1_CTRL_SDIO(sdiod->fmac_ulp), + &err); + /* Wait here (polling) for C_DS1_CTRL_PROC_DONE */ + timeout = jiffies + ULP_HUDI_PROC_DONE_TIME; + while (!(value & C_DS1_CTRL_PROC_DONE)) { + value = D11SHM_RDW(sdiod, + M_DS1_CTRL_SDIO(sdiod->fmac_ulp), + &err); + if (time_after(jiffies, timeout)) + break; + usleep_range(1000, 2000); + } + brcmf_dbg(ULP, "M_DS1_CTRL_SDIO: 0x%08x\n", + (u32)D11SHM_RDW(sdiod, + M_DS1_CTRL_SDIO(sdiod->fmac_ulp), &err)); + value = D11SHM_RDW(sdiod, + M_DS1_CTRL_SDIO(sdiod->fmac_ulp), + &err); + if (!(value & C_DS1_CTRL_PROC_DONE)) { + brcmf_err("Timeout Failed to enter DS1 Exit state!\n"); + return false; + } + } + + ulp_wake_ind = D11SHM_RDW(sdiod, + M_ULP_WAKE_IND(sdiod->fmac_ulp), + &err); + wowl_wake_ind = D11SHM_RDW(sdiod, + M_WAKEEVENT_IND(sdiod->fmac_ulp), + &err); + brcmf_dbg(ULP, "wowl_wake_ind: 0x%08x, ulp_wake_ind: 0x%08x\n", + wowl_wake_ind, ulp_wake_ind); + reg_addr = CORE_CC_REG( + brcmf_chip_get_pmu(bus->ci)->base, min_res_mask); + brcmf_sdiod_writel(sdiod, reg_addr, + DEFAULT_43012_MIN_RES_MASK, &err); + if (err) + brcmf_err("min_res_mask failed\n"); + + return true; + } + + return false; +} + static void brcmf_sdio_dpc(struct brcmf_sdio *bus) { struct brcmf_sdio_dev *sdiod = bus->sdiodev; @@ -2635,12 +2920,18 @@ static void brcmf_sdio_dpc(struct brcmf_sdio *bus) atomic_set(&bus->fcstate, !!(newstatus & (I_HMB_FC_STATE | I_HMB_FC_CHANGE))); intstatus |= (newstatus & bus->hostintmask); +#ifdef CONFIG_BRCMFMAC_BT_SHARED_SDIO + brcmf_btsdio_int_handler(bus->sdiodev->bus_if); +#endif /* CONFIG_BRCMFMAC_BT_SHARED_SDIO */ } /* Handle host mailbox indication */ if (intstatus & I_HMB_HOST_INT) { + u32 hmb_data = 0; intstatus &= ~I_HMB_HOST_INT; - intstatus |= brcmf_sdio_hostmail(bus); + intstatus |= brcmf_sdio_hostmail(bus, &hmb_data); + if (brcmf_sdio_ulp_pre_redownload_check(bus, hmb_data)) + brcmf_sdio_ulp_reinit_fw(bus); } sdio_release_host(bus->sdiodev->func1); @@ -2685,7 +2976,7 @@ static void brcmf_sdio_dpc(struct brcmf_sdio *bus) brcmf_sdio_clrintr(bus); if (bus->ctrl_frame_stat && (bus->clkstate == CLK_AVAIL) && - txctl_ok(bus)) { + txctl_ok(bus) && brcmf_sdio_f2_ready(bus)) { sdio_claim_host(bus->sdiodev->func1); if (bus->ctrl_frame_stat) { err = brcmf_sdio_tx_ctrlframe(bus, bus->ctrl_frame_buf, @@ -2722,13 +3013,19 @@ static void brcmf_sdio_dpc(struct brcmf_sdio *bus) } sdio_release_host(bus->sdiodev->func1); } - } else if (atomic_read(&bus->intstatus) || + } else { + sdio_claim_host(bus->sdiodev->func1); + err = brcmf_sdio_intr_rstatus(bus); + sdio_release_host(bus->sdiodev->func1); + + if (atomic_read(&bus->intstatus) || atomic_read(&bus->ipend) > 0 || (!atomic_read(&bus->fcstate) && brcmu_pktq_mlen(&bus->txq, ~bus->flowcontrol) && data_ok(bus))) { - bus->dpc_triggered = true; - } + bus->dpc_triggered = true; + } + } } static struct pktq *brcmf_sdio_bus_gettxq(struct device *dev) @@ -2811,6 +3108,8 @@ static int brcmf_sdio_bus_txdata(struct device *dev, struct sk_buff *pkt) brcmf_dbg(TRACE, "deferring pktq len %d\n", pktq_len(&bus->txq)); bus->sdcnt.fcqueued++; + skb_tx_timestamp(pkt); + /* Priority based enq */ spin_lock_bh(&bus->txq_lock); /* reset bus_flags in packet cb */ @@ -2909,7 +3208,7 @@ static int brcmf_sdio_readconsole(struct brcmf_sdio *bus) if (line[n - 1] == '\r') n--; line[n] = 0; - pr_debug("CONSOLE: %s\n", line); + pr_err("CONSOLE: %s\n", line); } } break2: @@ -3335,17 +3634,26 @@ brcmf_sdio_verifymemory(struct brcmf_sdio_dev *sdiodev, u32 ram_addr, static int brcmf_sdio_download_code_file(struct brcmf_sdio *bus, const struct firmware *fw) { + struct trx_header_le *trx = (struct trx_header_le *)fw->data; + u32 fw_size; + u32 address; int err; brcmf_dbg(TRACE, "Enter\n"); - err = brcmf_sdiod_ramrw(bus->sdiodev, true, bus->ci->rambase, - (u8 *)fw->data, fw->size); + address = bus->ci->rambase; + fw_size = fw->size; + if (trx->magic == cpu_to_le32(TRX_MAGIC)) { + address -= sizeof(struct trx_header_le); + fw_size = le32_to_cpu(trx->len); + } + err = brcmf_sdiod_ramrw(bus->sdiodev, true, address, + (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, (int)fw_size, address); + else if (!brcmf_sdio_verifymemory(bus->sdiodev, address, + (u8 *)fw->data, fw_size)) err = -EIO; return err; @@ -3383,6 +3691,16 @@ static int brcmf_sdio_download_firmware(struct brcmf_sdio *bus, rstvec = get_unaligned_le32(fw->data); brcmf_dbg(SDIO, "firmware rstvec: %x\n", rstvec); + if (bus->ci->blhs) { + bcmerror = bus->ci->blhs->prep_fwdl(bus->ci); + if (bcmerror) { + brcmf_err("FW download preparation failed\n"); + release_firmware(fw); + brcmf_fw_nvram_free(nvram); + goto err; + } + } + bcmerror = brcmf_sdio_download_code_file(bus, fw); release_firmware(fw); if (bcmerror) { @@ -3391,6 +3709,22 @@ static int brcmf_sdio_download_firmware(struct brcmf_sdio *bus, goto err; } + if (bus->ci->blhs) { + bcmerror = bus->ci->blhs->post_fwdl(bus->ci); + if (bcmerror) { + brcmf_err("FW download failed, err=%d\n", bcmerror); + brcmf_fw_nvram_free(nvram); + goto err; + } + + bcmerror = bus->ci->blhs->chk_validation(bus->ci); + if (bcmerror) { + brcmf_err("FW valication failed, err=%d\n", bcmerror); + brcmf_fw_nvram_free(nvram); + goto err; + } + } + bcmerror = brcmf_sdio_download_nvram(bus, nvram, nvlen); brcmf_fw_nvram_free(nvram); if (bcmerror) { @@ -3398,10 +3732,14 @@ static int brcmf_sdio_download_firmware(struct brcmf_sdio *bus, goto err; } - /* Take arm out of reset */ - if (!brcmf_chip_set_active(bus->ci, rstvec)) { - brcmf_err("error getting out of ARM core reset\n"); - goto err; + if (bus->ci->blhs) { + bus->ci->blhs->post_nvramdl(bus->ci); + } else { + /* Take arm out of reset */ + if (!brcmf_chip_set_active(bus->ci, rstvec)) { + brcmf_err("error getting out of ARM core reset\n"); + goto err; + } } err: @@ -3412,7 +3750,13 @@ static int brcmf_sdio_download_firmware(struct brcmf_sdio *bus, static bool brcmf_sdio_aos_no_decode(struct brcmf_sdio *bus) { - if (bus->ci->chip == CY_CC_43012_CHIP_ID) + if (bus->ci->chip == CY_CC_43012_CHIP_ID || + bus->ci->chip == CY_CC_4373_CHIP_ID || + bus->ci->chip == CY_CC_55560_CHIP_ID || + bus->ci->chip == BRCM_CC_4339_CHIP_ID || + bus->ci->chip == BRCM_CC_4345_CHIP_ID || + bus->ci->chip == BRCM_CC_4354_CHIP_ID || + bus->ci->chip == BRCM_CC_4356_CHIP_ID) return true; else return false; @@ -3428,7 +3772,8 @@ static void brcmf_sdio_sr_init(struct brcmf_sdio *bus) brcmf_dbg(TRACE, "Enter\n"); - if (brcmf_chip_is_ulp(bus->ci)) { + if (brcmf_sdio_use_ht_avail(bus->ci) || + brcmf_chip_is_ulp(bus->ci)) { wakeupctrl = SBSDIO_FUNC1_WCTRL_ALPWAIT_SHIFT; chipclkcsr = SBSDIO_HT_AVAIL_REQ; } else { @@ -3552,6 +3897,10 @@ static int brcmf_sdio_bus_preinit(struct device *dev) if (err < 0) goto done; + /* initialize SHM address from firmware for DS1 */ + if (!bus->sdiodev->ulp) + brcmf_sdio_ulp_preinit(dev); + bus->tx_hdrlen = SDPCM_HWHDR_LEN + SDPCM_SWHDR_LEN; if (sdiodev->sg_support) { bus->txglom = false; @@ -3619,7 +3968,7 @@ static int brcmf_sdio_bus_get_memdump(struct device *dev, void *data, void brcmf_sdio_trigger_dpc(struct brcmf_sdio *bus) { - if (!bus->dpc_triggered) { + /* if (!bus->dpc_triggered) */ { bus->dpc_triggered = true; queue_work(bus->brcmf_wq, &bus->datawork); } @@ -3656,7 +4005,7 @@ static void brcmf_sdio_bus_watchdog(struct brcmf_sdio *bus) brcmf_dbg(TIMER, "Enter\n"); /* Poll period: check device if appropriate. */ - if (!bus->sr_enabled && + if (!bus->sr_enabled && bus->poll && (++bus->polltick >= bus->pollrate)) { u32 intstatus = 0; @@ -3711,7 +4060,8 @@ static void brcmf_sdio_bus_watchdog(struct brcmf_sdio *bus) #endif /* DEBUG */ /* On idle timeout clear activity flag and/or turn off clock */ - if (!bus->dpc_triggered) { + if (!bus->dpc_triggered && + brcmf_btsdio_bus_count(bus->sdiodev->bus_if) == 0) { rmb(); if ((!bus->dpc_running) && (bus->idletime > 0) && (bus->clkstate == CLK_AVAIL)) { @@ -3826,6 +4176,20 @@ brcmf_sdio_drivestrengthinit(struct brcmf_sdio_dev *sdiodev, } } +static u32 brcmf_sdio_buscore_blhs_read(void *ctx, u32 reg_offset) +{ + struct brcmf_sdio_dev *sdiodev = (struct brcmf_sdio_dev *)ctx; + + return (u32)brcmf_sdiod_readb(sdiodev, reg_offset, NULL); +} + +static void brcmf_sdio_buscore_blhs_write(void *ctx, u32 reg_offset, u32 value) +{ + struct brcmf_sdio_dev *sdiodev = (struct brcmf_sdio_dev *)ctx; + + brcmf_sdiod_writeb(sdiodev, reg_offset, (u8)value, NULL); +} + static int brcmf_sdio_buscoreprep(void *ctx) { struct brcmf_sdio_dev *sdiodev = ctx; @@ -3922,11 +4286,39 @@ static void brcmf_sdio_buscore_write32(void *ctx, u32 addr, u32 val) brcmf_sdiod_writel(sdiodev, addr, val, NULL); } +static int brcmf_sdio_buscore_blhs_attach(void *ctx, struct brcmf_blhs **blhs, + u32 flag, uint timeout, uint interval) +{ + struct brcmf_sdio_dev *sdiodev = (struct brcmf_sdio_dev *)ctx; + struct brcmf_blhs *blhsh; + u8 cardcap; + + if (sdiodev->func1->vendor != SDIO_VENDOR_ID_CYPRESS) + return 0; + + cardcap = brcmf_sdiod_func0_rb(sdiodev, SDIO_CCCR_BRCM_CARDCAP, NULL); + if (cardcap & SDIO_CCCR_BRCM_CARDCAP_SECURE_MODE) { + blhsh = kzalloc(sizeof(*blhsh), GFP_KERNEL); + if (!blhsh) + return -ENOMEM; + + blhsh->d2h = BRCMF_SDIO_REG_DAR_D2H_MSG_0; + blhsh->h2d = BRCMF_SDIO_REG_DAR_H2D_MSG_0; + blhsh->read = brcmf_sdio_buscore_blhs_read; + blhsh->write = brcmf_sdio_buscore_blhs_write; + + *blhs = blhsh; + } + + return 0; +} + static const struct brcmf_buscore_ops brcmf_sdio_buscore_ops = { .prepare = brcmf_sdio_buscoreprep, .activate = brcmf_sdio_buscore_activate, .read32 = brcmf_sdio_buscore_read32, .write32 = brcmf_sdio_buscore_write32, + .blhs_attach = brcmf_sdio_buscore_blhs_attach, }; static bool @@ -4035,17 +4427,21 @@ brcmf_sdio_probe_attach(struct brcmf_sdio *bus) if (err) goto fail; - /* set PMUControl so a backplane reset does PMU state reload */ - reg_addr = CORE_CC_REG(brcmf_chip_get_pmu(bus->ci)->base, pmucontrol); - reg_val = brcmf_sdiod_readl(sdiodev, reg_addr, &err); - if (err) - goto fail; + if (!bus->ci->blhs) { + /* set PMUControl so a backplane reset does PMU state reload */ + reg_addr = CORE_CC_REG(brcmf_chip_get_pmu(bus->ci)->base, + pmucontrol); + reg_val = brcmf_sdiod_readl(sdiodev, reg_addr, &err); + if (err) + goto fail; - reg_val |= (BCMA_CC_PMU_CTL_RES_RELOAD << BCMA_CC_PMU_CTL_RES_SHIFT); + reg_val |= (BCMA_CC_PMU_CTL_RES_RELOAD << + BCMA_CC_PMU_CTL_RES_SHIFT); - brcmf_sdiod_writel(sdiodev, reg_addr, reg_val, &err); - if (err) - goto fail; + brcmf_sdiod_writel(sdiodev, reg_addr, reg_val, &err); + if (err) + goto fail; + } sdio_release_host(sdiodev->func1); @@ -4141,23 +4537,28 @@ static int brcmf_sdio_bus_reset(struct device *dev) brcmf_dbg(SDIO, "Enter\n"); +#ifndef CONFIG_BRCMF_CMD_TIMEOUT_REBOOT /* start by unregistering irqs */ brcmf_sdiod_intr_unregister(sdiodev); brcmf_sdiod_remove(sdiodev); +#endif /* CONFIG_BRCMF_CMD_TIMEOUT_REBOOT */ + + brcmf_bus_change_state(sdiodev->bus_if, BRCMF_BUS_DOWN); /* reset the adapter */ sdio_claim_host(sdiodev->func1); mmc_hw_reset(sdiodev->func1->card->host); sdio_release_host(sdiodev->func1); - brcmf_bus_change_state(sdiodev->bus_if, BRCMF_BUS_DOWN); - +#ifndef CONFIG_BRCMF_CMD_TIMEOUT_REBOOT ret = brcmf_sdiod_probe(sdiodev); if (ret) { brcmf_err("Failed to probe after sdio device reset: ret %d\n", ret); + brcmf_sdiod_remove(sdiodev); } +#endif /* CONFIG_BRCMF_CMD_TIMEOUT_REBOOT */ return ret; } @@ -4193,7 +4594,7 @@ static void brcmf_sdio_firmware_callback(struct device *dev, int err, u8 saveclk, bpreq; u8 devctl; - brcmf_dbg(TRACE, "Enter: dev=%s, err=%d\n", dev_name(dev), err); + brcmf_dbg(ULP, "Enter: dev=%s, err=%d\n", dev_name(dev), err); if (err) goto fail; @@ -4210,10 +4611,6 @@ static void brcmf_sdio_firmware_callback(struct device *dev, int err, goto fail; bus->alp_only = false; - /* Start the watchdog timer */ - bus->sdcnt.tickcnt = 0; - brcmf_sdio_wd_timer(bus, true); - sdio_claim_host(sdiod->func1); /* Make sure backplane clock is on, needed to generate F2 interrupt */ @@ -4225,7 +4622,8 @@ static void brcmf_sdio_firmware_callback(struct device *dev, int err, saveclk = brcmf_sdiod_readb(sdiod, SBSDIO_FUNC1_CHIPCLKCSR, &err); if (!err) { bpreq = saveclk; - bpreq |= brcmf_chip_is_ulp(bus->ci) ? + bpreq |= (brcmf_sdio_use_ht_avail(bus->ci) || + brcmf_chip_is_ulp(bus->ci)) ? SBSDIO_HT_AVAIL_REQ : SBSDIO_FORCE_HT; brcmf_sdiod_writeb(sdiod, SBSDIO_FUNC1_CHIPCLKCSR, bpreq, &err); @@ -4262,7 +4660,7 @@ static void brcmf_sdio_firmware_callback(struct device *dev, int err, brcmf_sdiod_writeb(sdiod, SBSDIO_DEVICE_CTL, devctl, &err); brcmf_sdiod_writeb(sdiod, SBSDIO_FUNC1_MESBUSYCTRL, - CY_4373_F1_MESBUSYCTRL, &err); + CY_4373_MESBUSYCTRL, &err); break; case SDIO_DEVICE_ID_BROADCOM_CYPRESS_43012: brcmf_dbg(INFO, "set F2 watermark to 0x%x*4 bytes\n", @@ -4319,6 +4717,35 @@ static void brcmf_sdio_firmware_callback(struct device *dev, int err, brcmf_sdiod_writeb(sdiod, SBSDIO_FUNC1_MESBUSYCTRL, CY_435X_F1_MESBUSYCTRL, &err); break; + case SDIO_DEVICE_ID_BROADCOM_CYPRESS_89459: + case SDIO_DEVICE_ID_CYPRESS_54590: + case SDIO_DEVICE_ID_CYPRESS_54591: + case SDIO_DEVICE_ID_CYPRESS_54594: + brcmf_dbg(INFO, "set F2/MES watermark to 0x%x*4 / 0x%x bytes for 89459\n", + CY_89459_F2_WATERMARK, CY_89459_MESBUSYCTRL); + brcmf_sdiod_writeb(sdiod, SBSDIO_WATERMARK, + CY_89459_F2_WATERMARK, &err); + devctl = brcmf_sdiod_readb(sdiod, SBSDIO_DEVICE_CTL, + &err); + devctl |= SBSDIO_DEVCTL_F2WM_ENAB; + brcmf_sdiod_writeb(sdiod, SBSDIO_DEVICE_CTL, devctl, + &err); + brcmf_sdiod_writeb(sdiod, SBSDIO_FUNC1_MESBUSYCTRL, + CY_89459_MESBUSYCTRL, &err); + break; + case SDIO_DEVICE_ID_CYPRESS_55560: + brcmf_dbg(INFO, "set F2 watermark to 0x%x*4 bytes\n", + CYW55560_F2_WATERMARK); + brcmf_sdiod_writeb(sdiod, SBSDIO_WATERMARK, + CYW55560_F2_WATERMARK, &err); + devctl = brcmf_sdiod_readb(sdiod, SBSDIO_DEVICE_CTL, + &err); + devctl |= SBSDIO_DEVCTL_F2WM_ENAB; + brcmf_sdiod_writeb(sdiod, SBSDIO_DEVICE_CTL, devctl, + &err); + brcmf_sdiod_writeb(sdiod, SBSDIO_FUNC1_MESBUSYCTRL, + CYW55560_F1_MESBUSYCTRL, &err); + break; default: brcmf_sdiod_writeb(sdiod, SBSDIO_WATERMARK, DEFAULT_F2_WATERMARK, &err); @@ -4361,19 +4788,44 @@ static void brcmf_sdio_firmware_callback(struct device *dev, int err, sdio_release_host(sdiod->func1); + /* Start the watchdog timer */ + bus->sdcnt.tickcnt = 0; + brcmf_sdio_wd_timer(bus, true); + err = brcmf_alloc(sdiod->dev, sdiod->settings); if (err) { brcmf_err("brcmf_alloc failed\n"); goto claim; } +#ifdef CONFIG_BRCMFMAC_BT_SHARED_SDIO + err = brcmf_btsdio_init(bus_if); + if (err) { + brcmf_err("brcmf_btsdio_init failed\n"); + goto free; + } +#endif /* CONFIG_BRCMFMAC_BT_SHARED_SDIO */ + /* Attach to the common layer, reserve hdr space */ - err = brcmf_attach(sdiod->dev); + err = brcmf_attach(sdiod->dev, !bus->sdiodev->ulp); if (err != 0) { brcmf_err("brcmf_attach failed\n"); goto free; } + /* Register for ULP events */ + if (sdiod->func1->device == SDIO_DEVICE_ID_BROADCOM_CYPRESS_43012) + brcmf_fweh_register(bus_if->drvr, BRCMF_E_ULP, + brcmf_ulp_event_notify); + + if (bus->sdiodev->ulp) { + /* For ULP, after firmware redownload complete + * set ULP state to IDLE + */ + if (bus->sdiodev->fmac_ulp.ulp_state == FMAC_ULP_TRIGGERED) + bus->sdiodev->fmac_ulp.ulp_state = FMAC_ULP_IDLE; + } + /* ready */ return; @@ -4400,6 +4852,9 @@ brcmf_sdio_prepare_fw_request(struct brcmf_sdio *bus) { ".txt", bus->sdiodev->nvram_name }, }; + if (bus->ci->blhs) + fwnames[BRCMF_SDIO_FW_CODE].extension = ".trxse"; + fwreq = brcmf_fw_alloc_request(bus->ci->chip, bus->ci->chiprev, brcmf_sdio_fwnames, ARRAY_SIZE(brcmf_sdio_fwnames), @@ -4407,7 +4862,10 @@ brcmf_sdio_prepare_fw_request(struct brcmf_sdio *bus) if (!fwreq) return NULL; - fwreq->items[BRCMF_SDIO_FW_CODE].type = BRCMF_FW_TYPE_BINARY; + if (bus->ci->blhs) + fwreq->items[BRCMF_SDIO_FW_CODE].type = BRCMF_FW_TYPE_TRXSE; + else + fwreq->items[BRCMF_SDIO_FW_CODE].type = BRCMF_FW_TYPE_BINARY; fwreq->items[BRCMF_SDIO_FW_NVRAM].type = BRCMF_FW_TYPE_NVRAM; fwreq->board_type = bus->sdiodev->settings->board_type; @@ -4437,7 +4895,7 @@ struct brcmf_sdio *brcmf_sdio_probe(struct brcmf_sdio_dev *sdiodev) bus->tx_seq = SDPCM_SEQ_WRAP - 1; /* single-threaded workqueue */ - wq = alloc_ordered_workqueue("brcmf_wq/%s", WQ_MEM_RECLAIM, + wq = alloc_ordered_workqueue("brcmf_wq/%s", WQ_MEM_RECLAIM | WQ_HIGHPRI, dev_name(&sdiodev->func1->dev)); if (!wq) { brcmf_err("insufficient memory to create txworkqueue\n"); @@ -4556,7 +5014,23 @@ void brcmf_sdio_remove(struct brcmf_sdio *bus) * necessary cores. */ msleep(20); - brcmf_chip_set_passive(bus->ci); + if (bus->sdiodev->fmac_ulp.ulp_state == + FMAC_ULP_ENTRY_RECV) { + brcmf_chip_ulp_reset_lhl_regs(bus->ci); + brcmf_chip_reset_pmu_regs(bus->ci); + } else { + brcmf_chip_set_passive(bus->ci); + } + + if (bus->ci->blhs) + bus->ci->blhs->init(bus->ci); + /* Reset the PMU, backplane and all the + * cores by using the PMUWatchdogCounter. + */ + brcmf_chip_reset_watchdog(bus->ci); + if (bus->ci->blhs) + bus->ci->blhs->post_wdreset(bus->ci); + brcmf_sdio_clkctl(bus, CLK_NONE, false); sdio_release_host(bus->sdiodev->func1); } @@ -4564,6 +5038,9 @@ void brcmf_sdio_remove(struct brcmf_sdio *bus) } if (bus->sdiodev->settings) brcmf_release_module_param(bus->sdiodev->settings); +#ifdef CONFIG_BRCMFMAC_BT_SHARED_SDIO + brcmf_btsdio_detach(bus->sdiodev->bus_if); +#endif /* CONFIG_BRCMFMAC_BT_SHARED_SDIO */ kfree(bus->rxbuf); kfree(bus->hdrbuf); @@ -4612,3 +5089,39 @@ int brcmf_sdio_sleep(struct brcmf_sdio *bus, bool sleep) return ret; } +/* Check F2 Ready bit before sending data to Firmware */ +static int +brcmf_sdio_f2_ready(struct brcmf_sdio *bus) +{ + int ret = -1; + int iordy_status = 0; + + sdio_claim_host(bus->sdiodev->func1); + /* Read the status of IOR2 */ + iordy_status = brcmf_sdiod_func0_rb(bus->sdiodev, SDIO_CCCR_IORx, NULL); + + sdio_release_host(bus->sdiodev->func1); + ret = iordy_status & SDIO_FUNC_ENABLE_2; + return ret; +} + +static int brcmf_ulp_event_notify(struct brcmf_if *ifp, + const struct brcmf_event_msg *evtmsg, + void *data) +{ + int err = 0; + struct brcmf_bus *bus_if = ifp->drvr->bus_if; + struct brcmf_sdio_dev *sdiodev; + struct brcmf_sdio *bus; + struct brcmf_ulp_event *ulp_event = (struct brcmf_ulp_event *)data; + + sdiodev = bus_if->bus_priv.sdio; + bus = sdiodev->bus; + + brcmf_dbg(ULP, "Chip went to DS1 state : action %d\n", + ulp_event->ulp_dongle_action); + if (ulp_event->ulp_dongle_action == FMAC_ULP_ENTRY) + bus->sdiodev->fmac_ulp.ulp_state = FMAC_ULP_ENTRY_RECV; + + return err; +} diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.h index 15d2c02fa3ec8..95c210de19fe0 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.h @@ -10,6 +10,10 @@ #include #include "firmware.h" +#define CONFIG_BRCMF_SDIO_TX_COPY + +#define BRCMF_SDIO_MAX_MEMBUF_SIZE (64 * 1024) + #define SDIOD_FBR_SIZE 0x100 /* io_en */ @@ -28,12 +32,16 @@ #define REG_F0_REG_MASK 0x7FF #define REG_F1_MISC_MASK 0x1FFFF +#define BRCMF_SDIO_REG_DAR_H2D_MSG_0 0x10030 +#define BRCMF_SDIO_REG_DAR_D2H_MSG_0 0x10038 + /* function 0 vendor specific CCCR registers */ #define SDIO_CCCR_BRCM_CARDCAP 0xf0 #define SDIO_CCCR_BRCM_CARDCAP_CMD14_SUPPORT BIT(1) #define SDIO_CCCR_BRCM_CARDCAP_CMD14_EXT BIT(2) #define SDIO_CCCR_BRCM_CARDCAP_CMD_NODEC BIT(3) +#define SDIO_CCCR_BRCM_CARDCAP_SECURE_MODE BIT(7) /* Interrupt enable bits for each function */ #define SDIO_CCCR_IEN_FUNC0 BIT(0) @@ -165,6 +173,35 @@ struct brcmf_sdreg { struct brcmf_sdio; struct brcmf_sdiod_freezer; +/* ULP SHM Offsets info */ +struct ulp_shm_info { + u32 m_ulp_ctrl_sdio; + u32 m_ulp_wakeevt_ind; + u32 m_ulp_wakeind; + u32 m_ulp_phytxblk; +}; + +/* FMAC ULP state machine */ +#define FMAC_ULP_IDLE (0) +#define FMAC_ULP_ENTRY_RECV (1) +#define FMAC_ULP_TRIGGERED (2) + +/* BRCMF_E_ULP event data */ +#define FMAC_ULP_EVENT_VERSION 1 +#define FMAC_ULP_DISABLE_CONSOLE 1 /* Disable console */ +#define FMAC_ULP_UCODE_DOWNLOAD 2 /* Download ULP ucode file */ +#define FMAC_ULP_ENTRY 3 /* Inform ulp entry to Host */ + +struct brcmf_ulp { + uint ulp_state; + struct ulp_shm_info ulp_shm_offset; +}; + +struct brcmf_ulp_event { + u16 version; + u16 ulp_dongle_action; +}; + struct brcmf_sdio_dev { struct sdio_func *func1; struct sdio_func *func2; @@ -189,6 +226,11 @@ struct brcmf_sdio_dev { bool wowl_enabled; enum brcmf_sdiod_state state; struct brcmf_sdiod_freezer *freezer; + struct brcmf_ulp fmac_ulp; + bool ulp; +#ifdef CONFIG_BRCMF_SDIO_TX_COPY + char *membuf; +#endif /* CONFIG_BRCMF_SDIO_TX_COPY */ }; /* sdio core registers */ @@ -379,4 +421,83 @@ void brcmf_sdio_wowl_config(struct device *dev, bool enabled); int brcmf_sdio_sleep(struct brcmf_sdio *bus, bool sleep); void brcmf_sdio_trigger_dpc(struct brcmf_sdio *bus); +/* SHM offsets */ +#define M_DS1_CTRL_SDIO(ptr) ((ptr).ulp_shm_offset.m_ulp_ctrl_sdio) +#define M_WAKEEVENT_IND(ptr) ((ptr).ulp_shm_offset.m_ulp_wakeevt_ind) +#define M_ULP_WAKE_IND(ptr) ((ptr).ulp_shm_offset.m_ulp_wakeind) +#define M_DS1_PHYTX_ERR_BLK(ptr) ((ptr).ulp_shm_offset.m_ulp_phytxblk) + +#define D11_BASE_ADDR 0x18001000 +#define D11_AXI_BASE_ADDR 0xE8000000 +#define D11_SHM_BASE_ADDR (D11_AXI_BASE_ADDR + 0x4000) + +#define D11REG_ADDR(offset) (D11_BASE_ADDR + (offset)) +#define D11IHR_ADDR(offset) (D11_AXI_BASE_ADDR + 0x400 + (2 * (offset))) +#define D11SHM_ADDR(offset) (D11_SHM_BASE_ADDR + (offset)) + +/* MacControl register */ +#define D11_MACCONTROL_REG D11REG_ADDR(0x120) +#define D11_MACCONTROL_REG_WAKE 0x4000000 + +/* HUDI Sequence SHM bits */ +#define C_DS1_CTRL_SDIO_DS1_SLEEP 0x1 +#define C_DS1_CTRL_SDIO_MAC_ON 0x2 +#define C_DS1_CTRL_SDIO_RADIO_PHY_ON 0x4 +#define C_DS1_CTRL_SDIO_DS1_EXIT 0x8 +#define C_DS1_CTRL_PROC_DONE 0x100 +#define C_DS1_CTRL_REQ_VALID 0x200 + +/* M_ULP_WAKEIND bits */ +#define C_WATCHDOG_EXPIRY BIT(0) +#define C_FCBS_ERROR BIT(1) +#define C_RETX_FAILURE BIT(2) +#define C_HOST_WAKEUP BIT(3) +#define C_INVALID_FCBS_BLOCK BIT(4) +#define C_HUDI_DS1_EXIT BIT(5) +#define C_LOB_SLEEP BIT(6) +#define C_DS1_PHY_TXERR BIT(9) +#define C_DS1_WAKE_TIMER BIT(10) + +#define PHYTX_ERR_BLK_SIZE 18 +#define D11SHM_FIRST2BYTE_MASK 0xFFFF0000 +#define D11SHM_SECOND2BYTE_MASK 0x0000FFFF +#define D11SHM_2BYTE_SHIFT 16 + +#define D11SHM_RD(sdh, offset, ret) \ + brcmf_sdiod_readl(sdh, D11SHM_ADDR(offset), ret) + +/* SHM Read is motified based on SHM 4 byte alignment as SHM size is 2 bytes and + * 2 byte is currently not working on FMAC + * If SHM address is not 4 byte aligned, then right shift by 16 + * otherwise, mask the first two MSB bytes + * Suppose data in address 7260 is 0x440002 and it is 4 byte aligned + * Correct SHM value is 0x2 for this SHM offset and next SHM value is 0x44 + */ +#define D11SHM_RDW(sdh, offset, ret) \ + ((offset % 4) ? \ + (brcmf_sdiod_readl(sdh, D11SHM_ADDR(offset), ret) \ + >> D11SHM_2BYTE_SHIFT) : \ + (brcmf_sdiod_readl(sdh, D11SHM_ADDR(offset), ret) \ + & D11SHM_SECOND2BYTE_MASK)) + +/* SHM is of size 2 bytes, 4 bytes write will overwrite other SHM's + * First read 4 bytes and then clear the required two bytes based on + * 4 byte alignment, then update the required value and write the + * 4 byte value now + */ +#define D11SHM_WR(sdh, offset, val, mask, ret) \ + do { \ + if ((offset) % 4) \ + val = (val & D11SHM_SECOND2BYTE_MASK) | \ + ((mask) << D11SHM_2BYTE_SHIFT); \ + else \ + val = (mask) | (val & D11SHM_FIRST2BYTE_MASK); \ + brcmf_sdiod_writel(sdh, D11SHM_ADDR(offset), val, ret); \ + } while (0) +#define D11REG_WR(sdh, addr, val, ret) \ + brcmf_sdiod_writel(sdh, addr, val, ret) + +#define D11REG_RD(sdh, addr, ret) \ + brcmf_sdiod_readl(sdh, addr, ret) + #endif /* BRCMFMAC_SDIO_H */ diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/trxhdr.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/trxhdr.h new file mode 100644 index 0000000000000..0411c7c7ffb99 --- /dev/null +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/trxhdr.h @@ -0,0 +1,38 @@ +/* SPDX-License-Identifier: ISC */ +/* Copyright (c) 2020 Cypress Semiconductor Corporation */ + +#ifndef BRCMFMAC_TRXHDR_H +#define BRCMFMAC_TRXHDR_H + +/* Bootloader makes special use of trx header "offsets" array */ +enum { + TRX_OFFSET_SIGN_INFO_IDX = 0, + TRX_OFFSET_DATA_FOR_SIGN1_IDX = 1, + TRX_OFFSET_DATA_FOR_SIGN2_IDX = 2, + TRX_OFFSET_ROOT_MODULUS_IDX = 3, + TRX_OFFSET_ROOT_EXPONENT_IDX = 67, + TRX_OFFSET_CONT_MODULUS_IDX = 68, + TRX_OFFSET_CONT_EXPONENT_IDX = 132, + TRX_OFFSET_HASH_FW_IDX = 133, + TRX_OFFSET_FW_LEN_IDX = 149, + TRX_OFFSET_TR_RST_IDX = 150, + TRX_OFFSET_FW_VER_FOR_ANTIROOLBACK_IDX = 151, + TRX_OFFSET_IV_IDX = 152, + TRX_OFFSET_NONCE_IDX = 160, + TRX_OFFSET_SIGN_INFO2_IDX = 168, + TRX_OFFSET_MAX_IDX +}; + +#define TRX_MAGIC 0x30524448 /* "HDR0" */ +#define TRX_VERSION 4 /* Version 4 */ +#define TRX_MAX_OFFSET TRX_OFFSET_MAX_IDX /* Max number of file offsets */ + +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 */ +}; + +#endif /* BRCMFMAC_TRXHDR_H */ diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c index 9fb68c2dc7e39..03bbeb103bec2 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c @@ -19,6 +19,7 @@ #include "core.h" #include "common.h" #include "bcdc.h" +#include "cfg80211.h" #define IOCTL_RESP_TIMEOUT msecs_to_jiffies(2000) @@ -39,7 +40,7 @@ BRCMF_FW_DEF(43143, "brcmfmac43143"); BRCMF_FW_DEF(43236B, "brcmfmac43236b"); BRCMF_FW_DEF(43242A, "brcmfmac43242a"); BRCMF_FW_DEF(43569, "brcmfmac43569"); -BRCMF_FW_DEF(4373, "brcmfmac4373"); +CY_FW_DEF(4373, "cyfmac4373"); static const struct brcmf_firmware_mapping brcmf_usb_fwnames[] = { BRCMF_FW_ENTRY(BRCM_CC_43143_CHIP_ID, 0xFFFFFFFF, 43143), @@ -638,6 +639,10 @@ static int brcmf_usb_tx(struct device *dev, struct sk_buff *skb) goto fail; } +#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 16, 0)) + if (devinfo->bus_pub.bus->allow_skborphan) + skb_orphan(skb); +#endif req->skb = skb; req->devinfo = devinfo; usb_fill_bulk_urb(req->urb, devinfo->usbdev, devinfo->tx_pipe, @@ -1219,8 +1224,14 @@ static void brcmf_usb_probe_phase2(struct device *dev, int ret, if (ret) goto error; + if (BRCMF_FWCON_ON()) { + ret = brcmf_fwlog_attach(devinfo->dev); + if (ret) + goto error; + } + /* Attach to the common driver interface */ - ret = brcmf_attach(devinfo->dev); + ret = brcmf_attach(devinfo->dev, true); if (ret) goto error; @@ -1279,6 +1290,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; +#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 16, 0)) + bus->allow_skborphan = true; +#endif #ifdef CONFIG_PM bus->wowl_supported = true; #endif @@ -1295,9 +1309,17 @@ static int brcmf_usb_probe_cb(struct brcmf_usbdev_info *devinfo) ret = brcmf_alloc(devinfo->dev, devinfo->settings); if (ret) goto fail; - ret = brcmf_attach(devinfo->dev); + + if (BRCMF_FWCON_ON()) { + ret = brcmf_fwlog_attach(devinfo->dev); + if (ret) + goto fail; + } + + ret = brcmf_attach(devinfo->dev, true); if (ret) goto fail; + /* we are done */ complete(&devinfo->dev_init_done); return 0; @@ -1480,8 +1502,22 @@ static int brcmf_usb_suspend(struct usb_interface *intf, pm_message_t state) { struct usb_device *usb = interface_to_usbdev(intf); struct brcmf_usbdev_info *devinfo = brcmf_usb_get_businfo(&usb->dev); + struct brcmf_bus *bus; + struct brcmf_cfg80211_info *config; + int retry = BRCMF_PM_WAIT_MAXRETRY; brcmf_dbg(USB, "Enter\n"); + + bus = devinfo->bus_pub.bus; + config = bus->drvr->config; + while (retry && + config->pm_state == BRCMF_CFG80211_PM_STATE_SUSPENDING) { + usleep_range(10000, 20000); + retry--; + } + if (!retry && config->pm_state == BRCMF_CFG80211_PM_STATE_SUSPENDING) + brcmf_err("timed out wait for cfg80211 suspended\n"); + devinfo->bus_pub.state = BRCMFMAC_USB_STATE_SLEEP; brcmf_cancel_all_urbs(devinfo); device_set_wakeup_enable(devinfo->dev, true); @@ -1558,7 +1594,9 @@ static struct usb_driver brcmf_usbdrvr = { .resume = brcmf_usb_resume, .reset_resume = brcmf_usb_reset_resume, .supports_autosuspend = true, +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 5, 0)) .disable_hub_initiated_lpm = 1, +#endif }; static int brcmf_usb_reset_device(struct device *dev, void *notused) @@ -1584,8 +1622,12 @@ void brcmf_usb_exit(void) usb_deregister(&brcmf_usbdrvr); } -int brcmf_usb_register(void) +void brcmf_usb_register(void) { + int ret; + brcmf_dbg(USB, "Enter\n"); - return usb_register(&brcmf_usbdrvr); + ret = usb_register(&brcmf_usbdrvr); + if (ret) + brcmf_err("usb_register failed %d\n", ret); } diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/vendor.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/vendor.c index d07e7c7355d9d..08a327d9d3e11 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/vendor.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/vendor.c @@ -64,6 +64,16 @@ static int brcmf_cfg80211_vndr_cmds_dcmd_handler(struct wiphy *wiphy, *(char *)(dcmd_buf + len) = '\0'; } + if (cmdhdr->cmd == BRCMF_C_SET_AP) { + if (*(int *)(dcmd_buf) == 1) { + ifp->vif->wdev.iftype = NL80211_IFTYPE_AP; + brcmf_net_setcarrier(ifp, true); + } else { + ifp->vif->wdev.iftype = NL80211_IFTYPE_STATION; + } + brcmf_cfg80211_update_proto_addr_mode(&vif->wdev); + } + if (cmdhdr->set) ret = brcmf_fil_cmd_data_set(ifp, cmdhdr->cmd, dcmd_buf, ret_len); @@ -104,6 +114,110 @@ static int brcmf_cfg80211_vndr_cmds_dcmd_handler(struct wiphy *wiphy, return ret; } +static int brcmf_cfg80211_vndr_cmds_int_get(struct brcmf_if *ifp, + u32 cmd, struct wiphy *wiphy) +{ + struct sk_buff *reply; + int get_value = 0; + int ret; + + ret = brcmf_fil_cmd_int_get(ifp, cmd, &get_value); + if (ret) + brcmf_err("Command %u get failure. Error : %d\n", cmd, ret); + + reply = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, sizeof(int)); + nla_put_nohdr(reply, sizeof(int), &get_value); + ret = cfg80211_vendor_cmd_reply(reply); + if (ret) + brcmf_err("Command %u failure. Error : %d\n", cmd, ret); + return ret; +} + +static int brcmf_cfg80211_vndr_cmds_int_set(struct brcmf_if *ifp, int val, u32 cmd) +{ + int ret; + + ret = brcmf_fil_cmd_int_set(ifp, cmd, val); + if (ret < 0) + brcmf_err("Command %u set failure. Error : %d\n", cmd, ret); + return ret; +} + +static int brcmf_cfg80211_vndr_cmds_frameburst(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, int len) +{ + int ret; + int val = *(int *)data; + struct brcmf_cfg80211_vif *vif; + struct brcmf_if *ifp; + + vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev); + ifp = vif->ifp; + + if (val == 0x0 || val == 0x1) + ret = brcmf_cfg80211_vndr_cmds_int_set(ifp, val, + BRCMF_C_SET_FAKEFRAG); + else if (val == 0xff) + ret = brcmf_cfg80211_vndr_cmds_int_get(ifp, + BRCMF_C_GET_FAKEFRAG, + wiphy); + else + brcmf_err("Invalid Input\n"); + + return ret; +} + +s32 +brcmf_wiphy_phy_temp_evt_handler(struct brcmf_if *ifp, + const struct brcmf_event_msg *e, void *data) + +{ + struct brcmf_cfg80211_info *cfg = ifp->drvr->config; + struct wiphy *wiphy = cfg_to_wiphy(cfg); + struct sk_buff *skb; + struct nlattr *phy_temp_data; + u32 version, temp, tempdelta; + struct brcmf_phy_temp_evt *phy_temp_evt; + + phy_temp_evt = (struct brcmf_phy_temp_evt *)data; + + version = le32_to_cpu(phy_temp_evt->version); + temp = le32_to_cpu(phy_temp_evt->temp); + tempdelta = le32_to_cpu(phy_temp_evt->tempdelta); + + skb = cfg80211_vendor_event_alloc(wiphy, NULL, + sizeof(*phy_temp_evt), + BRCMF_VNDR_EVTS_PHY_TEMP, + GFP_KERNEL); + + if (!skb) { + brcmf_dbg(EVENT, "NO MEM: can't allocate skb for vendor PHY_TEMP_EVENT\n"); + return -ENOMEM; + } + + phy_temp_data = nla_nest_start(skb, NL80211_ATTR_VENDOR_EVENTS); + if (!phy_temp_data) { + nla_nest_cancel(skb, phy_temp_data); + kfree_skb(skb); + brcmf_dbg(EVENT, "skb could not nest vendor attributes\n"); + return -EMSGSIZE; + } + + if (nla_put_u32(skb, BRCMF_NLATTR_VERS, version) || + nla_put_u32(skb, BRCMF_NLATTR_PHY_TEMP, temp) || + nla_put_u32(skb, BRCMF_NLATTR_PHY_TEMPDELTA, tempdelta)) { + kfree_skb(skb); + brcmf_dbg(EVENT, "NO ROOM in skb for vendor PHY_TEMP_EVENT\n"); + return -EMSGSIZE; + } + + nla_nest_end(skb, phy_temp_data); + + cfg80211_vendor_event(skb, GFP_KERNEL); + return 0; +} + const struct wiphy_vendor_command brcmf_vendor_cmds[] = { { { @@ -112,7 +226,28 @@ const struct wiphy_vendor_command brcmf_vendor_cmds[] = { }, .flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_NETDEV, +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 3, 0)) .policy = VENDOR_CMD_RAW_DATA, +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 3, 0)) */ .doit = brcmf_cfg80211_vndr_cmds_dcmd_handler }, + { + { + .vendor_id = BROADCOM_OUI, + .subcmd = BRCMF_VNDR_CMDS_FRAMEBURST + }, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | + WIPHY_VENDOR_CMD_NEED_NETDEV, +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 3, 0)) + .policy = VENDOR_CMD_RAW_DATA, +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 3, 0)) */ + .doit = brcmf_cfg80211_vndr_cmds_frameburst + }, +}; + +const struct nl80211_vendor_cmd_info brcmf_vendor_events[] = { + { + .vendor_id = BROADCOM_OUI, + .subcmd = BRCMF_VNDR_EVTS_PHY_TEMP, + }, }; diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/vendor.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/vendor.h index 418f33ea6fd3f..3c691bbe0baec 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/vendor.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/vendor.h @@ -11,9 +11,15 @@ enum brcmf_vndr_cmds { BRCMF_VNDR_CMDS_UNSPEC, BRCMF_VNDR_CMDS_DCMD, + BRCMF_VNDR_CMDS_FRAMEBURST, BRCMF_VNDR_CMDS_LAST }; +enum brcmf_vndr_evts { + BRCMF_VNDR_EVTS_PHY_TEMP, + BRCMF_VNDR_EVTS_LAST +}; + /** * enum brcmf_nlattrs - nl80211 message attributes * @@ -25,11 +31,21 @@ enum brcmf_nlattrs { BRCMF_NLATTR_LEN, BRCMF_NLATTR_DATA, + BRCMF_NLATTR_VERS, + BRCMF_NLATTR_PHY_TEMP, + BRCMF_NLATTR_PHY_TEMPDELTA, __BRCMF_NLATTR_AFTER_LAST, BRCMF_NLATTR_MAX = __BRCMF_NLATTR_AFTER_LAST - 1 }; +/* structure of event sent up by firmware: is this the right place for it? */ +struct brcmf_phy_temp_evt { + __le32 version; + __le32 temp; + __le32 tempdelta; +} __packed; + /** * struct brcmf_vndr_dcmd_hdr - message header for cfg80211 vendor command dcmd * support @@ -49,5 +65,9 @@ struct brcmf_vndr_dcmd_hdr { }; extern const struct wiphy_vendor_command brcmf_vendor_cmds[]; +extern const struct nl80211_vendor_cmd_info brcmf_vendor_events[]; +s32 brcmf_wiphy_phy_temp_evt_handler(struct brcmf_if *ifp, + const struct brcmf_event_msg *e, + void *data); #endif /* _vendor_h_ */ diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmutil/Makefile b/drivers/net/wireless/broadcom/brcm80211/brcmutil/Makefile index 7a82d615ba2ae..6e999552dcff7 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmutil/Makefile +++ b/drivers/net/wireless/broadcom/brcm80211/brcmutil/Makefile @@ -4,7 +4,7 @@ # # Copyright (c) 2011 Broadcom Corporation # -ccflags-y := -I $(srctree)/$(src)/../include +ccflags-y := -I$(src)/../include obj-$(CONFIG_BRCMUTIL) += brcmutil.o brcmutil-objs = utils.o d11.o diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmutil/d11.c b/drivers/net/wireless/broadcom/brcm80211/brcmutil/d11.c index 1e2b1e487eb76..5effa27542e95 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmutil/d11.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmutil/d11.c @@ -87,10 +87,20 @@ static void brcmu_d11ac_encchspec(struct brcmu_chan *ch) 0, d11ac_bw(ch->bw)); ch->chspec &= ~BRCMU_CHSPEC_D11AC_BND_MASK; - if (ch->chnum <= CH_MAX_2G_CHANNEL) - ch->chspec |= BRCMU_CHSPEC_D11AC_BND_2G; - else + switch (ch->band) { + case BRCMU_CHAN_BAND_6G: + ch->chspec |= BRCMU_CHSPEC_D11AC_BND_6G; + break; + case BRCMU_CHAN_BAND_5G: ch->chspec |= BRCMU_CHSPEC_D11AC_BND_5G; + break; + case BRCMU_CHAN_BAND_2G: + ch->chspec |= BRCMU_CHSPEC_D11AC_BND_2G; + break; + default: + WARN_ONCE(1, "Invalid band 0x%04x\n", ch->band); + break; + } } static void brcmu_d11n_decchspec(struct brcmu_chan *ch) @@ -222,6 +232,9 @@ static void brcmu_d11ac_decchspec(struct brcmu_chan *ch) } switch (ch->chspec & BRCMU_CHSPEC_D11AC_BND_MASK) { + case BRCMU_CHSPEC_D11AC_BND_6G: + ch->band = BRCMU_CHAN_BAND_6G; + break; case BRCMU_CHSPEC_D11AC_BND_5G: ch->band = BRCMU_CHAN_BAND_5G; break; diff --git a/drivers/net/wireless/broadcom/brcm80211/include/brcm_hw_ids.h b/drivers/net/wireless/broadcom/brcm80211/include/brcm_hw_ids.h index c6c4be05159d4..91c6b38c68959 100644 --- a/drivers/net/wireless/broadcom/brcm80211/include/brcm_hw_ids.h +++ b/drivers/net/wireless/broadcom/brcm80211/include/brcm_hw_ids.h @@ -14,6 +14,7 @@ #define BRCM_USB_VENDOR_ID_LINKSYS 0x13b1 #define CY_USB_VENDOR_ID_CYPRESS 0x04b4 #define BRCM_PCIE_VENDOR_ID_BROADCOM PCI_VENDOR_ID_BROADCOM +#define CY_PCIE_VENDOR_ID_CYPRESS 0x12be /* Chipcommon Core Chip IDs */ #define BRCM_CC_43143_CHIP_ID 43143 @@ -49,8 +50,12 @@ #define BRCM_CC_4366_CHIP_ID 0x4366 #define BRCM_CC_43664_CHIP_ID 43664 #define BRCM_CC_4371_CHIP_ID 0x4371 +#define CY_CC_43430_CHIP_ID 43430 +#define CY_CC_43439_CHIP_ID 43439 #define CY_CC_4373_CHIP_ID 0x4373 #define CY_CC_43012_CHIP_ID 43012 +#define CY_CC_89459_CHIP_ID 0x4355 +#define CY_CC_55560_CHIP_ID 0xd908 /* USB Device IDs */ #define BRCM_USB_43143_DEVICE_ID 0xbd1e @@ -69,6 +74,7 @@ #define BRCM_PCIE_4356_DEVICE_ID 0x43ec #define BRCM_PCIE_43567_DEVICE_ID 0x43d3 #define BRCM_PCIE_43570_DEVICE_ID 0x43d9 +#define BRCM_PCIE_43570_RAW_DEVICE_ID 0xaa31 #define BRCM_PCIE_4358_DEVICE_ID 0x43e9 #define BRCM_PCIE_4359_DEVICE_ID 0x43ef #define BRCM_PCIE_43602_DEVICE_ID 0x43ba @@ -83,7 +89,16 @@ #define BRCM_PCIE_4366_2G_DEVICE_ID 0x43c4 #define BRCM_PCIE_4366_5G_DEVICE_ID 0x43c5 #define BRCM_PCIE_4371_DEVICE_ID 0x440d - +#define CY_PCIE_89459_DEVICE_ID 0x4415 +#define CY_PCIE_89459_RAW_DEVICE_ID 0x4355 +#define CY_PCIE_54591_DEVICE_ID 0x4417 +#define CY_PCIE_54590_DEVICE_ID 0x4416 +#define CY_PCIE_54594_DEVICE_ID 0x441a +#define CY_PCIE_55560_DEVICE_ID 0xbd31 +#define CY_PCIE_4373_RAW_DEVICE_ID 0x4373 +#define CY_PCIE_4373_DUAL_DEVICE_ID 0x4418 +#define CY_PCIE_4373_2G_DEVICE_ID 0x4419 +#define CY_PCIE_4373_5G_DEVICE_ID 0x441a /* brcmsmac IDs */ #define BCM4313_D11N2G_ID 0x4727 /* 4313 802.11n 2.4G device */ diff --git a/drivers/net/wireless/broadcom/brcm80211/include/brcmu_d11.h b/drivers/net/wireless/broadcom/brcm80211/include/brcmu_d11.h index f6344023855c3..d2554c9383828 100644 --- a/drivers/net/wireless/broadcom/brcm80211/include/brcmu_d11.h +++ b/drivers/net/wireless/broadcom/brcm80211/include/brcmu_d11.h @@ -6,6 +6,8 @@ #ifndef _BRCMU_D11_H_ #define _BRCMU_D11_H_ +#include + /* d11 io type */ #define BRCMU_D11N_IOTYPE 1 #define BRCMU_D11AC_IOTYPE 2 @@ -82,11 +84,23 @@ #define BRCMU_CHSPEC_D11AC_BND_SHIFT 14 #define BRCMU_CHSPEC_D11AC_BND_2G 0x0000 #define BRCMU_CHSPEC_D11AC_BND_3G 0x4000 -#define BRCMU_CHSPEC_D11AC_BND_4G 0x8000 +#define BRCMU_CHSPEC_D11AC_BND_6G 0x8000 #define BRCMU_CHSPEC_D11AC_BND_5G 0xc000 - -#define BRCMU_CHAN_BAND_2G 0 -#define BRCMU_CHAN_BAND_5G 1 +#define BRCMU_CHSPEC_IS5G(chspec) \ + (((chspec) & BRCMU_CHSPEC_D11AC_BND_MASK) == BRCMU_CHSPEC_D11AC_BND_5G) +#define BRCMU_CHSPEC_IS6G(chspec) \ + (((chspec) & BRCMU_CHSPEC_D11AC_BND_MASK) == BRCMU_CHSPEC_D11AC_BND_6G) +#define BRCMU_CHAN_BAND_2G 1 +#define BRCMU_CHAN_BAND_5G 2 +#define BRCMU_CHAN_BAND_6G 3 +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0)) +#define BRCMU_CHAN_BAND_TO_NL80211(band) \ + ((band) == BRCMU_CHAN_BAND_2G ? NL80211_BAND_2GHZ : \ + ((band) == BRCMU_CHAN_BAND_5G ? NL80211_BAND_5GHZ : NL80211_BAND_6GHZ)) +#else /* (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0)) */ +#define BRCMU_CHAN_BAND_TO_NL80211(band) \ + ((band) == BRCMU_CHAN_BAND_2G ? NL80211_BAND_2GHZ : NL80211_BAND_5GHZ) +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0)) */ enum brcmu_chan_bw { BRCMU_CHAN_BW_20, diff --git a/drivers/net/wireless/broadcom/brcm80211/include/brcmu_utils.h b/drivers/net/wireless/broadcom/brcm80211/include/brcmu_utils.h index 9465323286673..48791ac87496d 100644 --- a/drivers/net/wireless/broadcom/brcm80211/include/brcmu_utils.h +++ b/drivers/net/wireless/broadcom/brcm80211/include/brcmu_utils.h @@ -21,6 +21,19 @@ } \ } +/* Spin at most 'ms' milliseconds with polling interval 'interval' milliseconds + * while 'exp' is true. Caller should explicitly test 'exp' when this completes + * and take appropriate error action if 'exp' is still true. + */ +#define SPINWAIT_MS(exp, ms, interval) { \ + typeof(interval) interval_ = (interval); \ + uint countdown = (ms) + (interval_ - 1U); \ + while ((exp) && (countdown >= interval_)) { \ + msleep(interval_); \ + countdown -= interval_; \ + } \ +} + /* osl multi-precedence packet queue */ #define PKTQ_LEN_DEFAULT 128 /* Max 128 packets */ #define PKTQ_MAX_PREC 16 /* Maximum precedence levels */ diff --git a/drivers/net/wireless/broadcom/brcm80211/include/brcmu_wifi.h b/drivers/net/wireless/broadcom/brcm80211/include/brcmu_wifi.h index 7552bdb91991c..04bf1c0b3b06f 100644 --- a/drivers/net/wireless/broadcom/brcm80211/include/brcmu_wifi.h +++ b/drivers/net/wireless/broadcom/brcm80211/include/brcmu_wifi.h @@ -92,7 +92,8 @@ #define WLC_BAND_AUTO 0 /* auto-select */ #define WLC_BAND_5G 1 /* 5 Ghz */ #define WLC_BAND_2G 2 /* 2.4 Ghz */ -#define WLC_BAND_ALL 3 /* all bands */ +#define WLC_BAND_6G 3 /* 6 Ghz */ +#define WLC_BAND_ALL 4 /* all bands */ #define CHSPEC_CHANNEL(chspec) ((u8)((chspec) & WL_CHANSPEC_CHAN_MASK)) #define CHSPEC_BAND(chspec) ((chspec) & WL_CHANSPEC_BAND_MASK) @@ -201,6 +202,13 @@ static inline bool ac_bitmap_tst(u8 bitmap, int prec) #define CRYPTO_ALGO_AES_RESERVED2 6 #define CRYPTO_ALGO_NALG 7 +#define CRYPTO_ALGO_AES_GCM 14 /* 128 bit GCM */ +#define CRYPTO_ALGO_AES_CCM256 15 /* 256 bit CCM */ +#define CRYPTO_ALGO_AES_GCM256 16 /* 256 bit GCM */ +#define CRYPTO_ALGO_BIP_CMAC256 17 /* 256 bit BIP CMAC */ +#define CRYPTO_ALGO_BIP_GMAC 18 /* 128 bit BIP GMAC */ +#define CRYPTO_ALGO_BIP_GMAC256 19 /* 256 bit BIP GMAC */ + /* wireless security bitvec */ #define WEP_ENABLED 0x0001 @@ -233,6 +241,13 @@ static inline bool ac_bitmap_tst(u8 bitmap, int prec) #define WPA3_AUTH_SAE_PSK 0x40000 /* SAE with 4-way handshake */ +#define WFA_AUTH_DPP 0x200000 /* WFA DPP AUTH */ +#define WPA3_AUTH_1X_SUITE_B_SHA384 0x400000 /* Suite B-192 SHA384 */ + + +#define WFA_OUI "\x50\x6F\x9A" /* WFA OUI */ +#define DPP_VER 0x1A /* WFA DPP v1.0 */ + #define DOT11_DEFAULT_RTS_LEN 2347 #define DOT11_DEFAULT_FRAG_LEN 2346 diff --git a/drivers/net/wireless/broadcom/brcm80211/include/chipcommon.h b/drivers/net/wireless/broadcom/brcm80211/include/chipcommon.h index 0340bba968688..39cd34c226281 100644 --- a/drivers/net/wireless/broadcom/brcm80211/include/chipcommon.h +++ b/drivers/net/wireless/broadcom/brcm80211/include/chipcommon.h @@ -214,8 +214,197 @@ struct chipcregs { u32 PAD[3]; u32 retention_grpidx; /* 0x680 */ u32 retention_grpctl; /* 0x684 */ - u32 PAD[94]; - u16 sromotp[768]; + u32 mac_res_req_timer; /* 0x688 */ + u32 mac_res_req_mask; /* 0x68c */ + u32 PAD[18]; + u32 pmucontrol_ext; /* 0x6d8 */ + u32 slowclkperiod; /* 0x6dc */ + u32 PAD[8]; + u32 pmuintmask0; /* 0x700 */ + u32 pmuintmask1; /* 0x704 */ + u32 PAD[14]; + u32 pmuintstatus; /* 0x740 */ + u32 extwakeupstatus; /* 0x744 */ + u32 watchdog_res_mask; /* 0x748 */ + u32 swscratch; /* 0x750 */ + u32 PAD[3]; + u32 extwakemask[2]; /* 0x760-0x764 */ + u32 PAD[2]; + u32 extwakereqmask[2]; /* 0x770-0x774 */ + u32 PAD[2]; + u32 pmuintctrl0; /* 0x780 */ + u32 pmuintctrl1; /* 0x784 */ + u32 PAD[2]; + u32 extwakectrl[2]; /* 0x790 */ +}; + +#define CHIPGCIREGOFFS(field) offsetof(struct chipgciregs, field) + +struct chipgciregs { + u32 gci_corecaps0; /* 0x000 */ + u32 gci_corecaps1; /* 0x004 */ + u32 gci_corecaps2; /* 0x008 */ + u32 gci_corectrl; /* 0x00c */ + u32 gci_corestat; /* 0x010 */ + u32 gci_intstat; /* 0x014 */ + u32 gci_intmask; /* 0x018 */ + u32 gci_wakemask; /* 0x01c */ + u32 gci_levelintstat; /* 0x020 */ + u32 gci_eventintstat; /* 0x024 */ + u32 gci_wakelevelintstat; /* 0x028 */ + u32 gci_wakeeventintstat; /* 0x02c */ + u32 semaphoreintstatus; /* 0x030 */ + u32 semaphoreintmask; /* 0x034 */ + u32 semaphorerequest; /* 0x038 */ + u32 semaphorereserve; /* 0x03c */ + u32 gci_indirect_addr; /* 0x040 */ + u32 gci_gpioctl; /* 0x044 */ + u32 gci_gpiostatus; /* 0x048 */ + u32 gci_gpiomask; /* 0x04c */ + u32 eventsummary; /* 0x050 */ + u32 gci_miscctl; /* 0x054 */ + u32 gci_gpiointmask; /* 0x058 */ + u32 gci_gpiowakemask; /* 0x05c */ + u32 gci_input[32]; /* 0x060 */ + u32 gci_event[32]; /* 0x0e0 */ + u32 gci_output[4]; /* 0x160 */ + u32 gci_control_0; /* 0x170 */ + u32 gci_control_1; /* 0x174 */ + u32 gci_intpolreg; /* 0x178 */ + u32 gci_levelintmask; /* 0x17c */ + u32 gci_eventintmask; /* 0x180 */ + u32 wakelevelintmask; /* 0x184 */ + u32 wakeeventintmask; /* 0x188 */ + u32 hwmask; /* 0x18c */ + u32 PAD; + u32 gci_inbandeventintmask; /* 0x194 */ + u32 PAD; + u32 gci_inbandeventstatus; /* 0x19c */ + u32 gci_seciauxtx; /* 0x1a0 */ + u32 gci_seciauxrx; /* 0x1a4 */ + u32 gci_secitx_datatag; /* 0x1a8 */ + u32 gci_secirx_datatag; /* 0x1ac */ + u32 gci_secitx_datamask; /* 0x1b0 */ + u32 gci_seciusef0tx_reg; /* 0x1b4 */ + u32 gci_secif0tx_offset; /* 0x1b8 */ + u32 gci_secif0rx_offset; /* 0x1bc */ + u32 gci_secif1tx_offset; /* 0x1c0 */ + u32 gci_rxfifo_common_ctrl; /* 0x1c4 */ + u32 gci_rxfifoctrl; /* 0x1c8 */ + u32 gci_hw_sema_status; /* 0x1cc */ + u32 gci_seciuartescval; /* 0x1d0 */ + u32 gic_seciuartautobaudctr; /* 0x1d4 */ + u32 gci_secififolevel; /* 0x1d8 */ + u32 gci_seciuartdata; /* 0x1dc */ + u32 gci_secibauddiv; /* 0x1e0 */ + u32 gci_secifcr; /* 0x1e4 */ + u32 gci_secilcr; /* 0x1e8 */ + u32 gci_secimcr; /* 0x1ec */ + u32 gci_secilsr; /* 0x1f0 */ + u32 gci_secimsr; /* 0x1f4 */ + u32 gci_baudadj; /* 0x1f8 */ + u32 gci_inbandintmask; /* 0x1fc */ + u32 gci_chipctrl; /* 0x200 */ + u32 gci_chipsts; /* 0x204 */ + u32 gci_gpioout; /* 0x208 */ + u32 gci_gpioout_read; /* 0x20C */ + u32 gci_mpwaketx; /* 0x210 */ + u32 gci_mpwakedetect; /* 0x214 */ + u32 gci_seciin_ctrl; /* 0x218 */ + u32 gci_seciout_ctrl; /* 0x21C */ + u32 gci_seciin_auxfifo_en; /* 0x220 */ + u32 gci_seciout_txen_txbr; /* 0x224 */ + u32 gci_seciin_rxbrstatus; /* 0x228 */ + u32 gci_seciin_rxerrstatus; /* 0x22C */ + u32 gci_seciin_fcstatus; /* 0x230 */ + u32 gci_seciout_txstatus; /* 0x234 */ + u32 gci_seciout_txbrstatus; /* 0x238 */ + u32 wlan_mem_info; /* 0x23C */ + u32 wlan_bankxinfo; /* 0x240 */ + u32 bt_smem_select; /* 0x244 */ + u32 bt_smem_stby; /* 0x248 */ + u32 bt_smem_status; /* 0x24C */ + u32 wlan_bankxactivepda; /* 0x250 */ + u32 wlan_bankxsleeppda; /* 0x254 */ + u32 wlan_bankxkill; /* 0x258 */ + u32 PAD[41]; + u32 gci_chipid; /* 0x300 */ + u32 PAD[3]; + u32 otpstatus; /* 0x310 */ + u32 otpcontrol; /* 0x314 */ + u32 otpprog; /* 0x318 */ + u32 otplayout; /* 0x31c */ + u32 otplayoutextension; /* 0x320 */ + u32 otpcontrol1; /* 0x324 */ + u32 otpprogdata; /* 0x328 */ + u32 PAD[52]; + u32 otpECCstatus; /* 0x3FC */ + u32 PAD[512]; + u32 lhl_core_capab_adr; /* 0xC00 */ + u32 lhl_main_ctl_adr; /* 0xC04 */ + u32 lhl_pmu_ctl_adr; /* 0xC08 */ + u32 lhl_extlpo_ctl_adr; /* 0xC0C */ + u32 lpo_ctl_adr; /* 0xC10 */ + u32 lhl_lpo2_ctl_adr; /* 0xC14 */ + u32 lhl_osc32k_ctl_adr; /* 0xC18 */ + u32 lhl_clk_status_adr; /* 0xC1C */ + u32 lhl_clk_det_ctl_adr; /* 0xC20 */ + u32 lhl_clk_sel_adr; /* 0xC24 */ + u32 hidoff_cnt_adr[2]; /* 0xC28-0xC2C */ + u32 lhl_autoclk_ctl_adr; /* 0xC30 */ + u32 PAD; + u32 lhl_hibtim_adr; /* 0xC38 */ + u32 lhl_wl_ilp_val_adr; /* 0xC3C */ + u32 lhl_wl_armtim0_intrp_adr; /* 0xC40 */ + u32 lhl_wl_armtim0_st_adr; /* 0xC44 */ + u32 lhl_wl_armtim0_adr; /* 0xC48 */ + u32 PAD[9]; + u32 lhl_wl_mactim0_intrp_adr; /* 0xC70 */ + u32 lhl_wl_mactim0_st_adr; /* 0xC74 */ + u32 lhl_wl_mactim_int0_adr; /* 0xC78 */ + u32 lhl_wl_mactim_frac0_adr; /* 0xC7C */ + u32 lhl_wl_mactim1_intrp_adr; /* 0xC80 */ + u32 lhl_wl_mactim1_st_adr; /* 0xC84 */ + u32 lhl_wl_mactim_int1_adr; /* 0xC88 */ + u32 lhl_wl_mactim_frac1_adr; /* 0xC8C */ + u32 PAD[8]; + u32 gpio_int_en_port_adr[4]; /* 0xCB0-0xCBC */ + u32 gpio_int_st_port_adr[4]; /* 0xCC0-0xCCC */ + u32 gpio_ctrl_iocfg_p_adr[64]; /* 0xCD0-0xDCC */ + u32 gpio_gctrl_iocfg_p0_p39_adr; /* 0xDD0 */ + u32 gpio_gdsctrl_iocfg_p0_p25_p30_p39_adr; /* 0xDD4 */ + u32 gpio_gdsctrl_iocfg_p26_p29_adr; /* 0xDD8 */ + u32 PAD[8]; + u32 lhl_gpio_din0_adr; /* 0xDFC */ + u32 lhl_gpio_din1_adr; /* 0xE00 */ + u32 lhl_wkup_status_adr; /* 0xE04 */ + u32 lhl_ctl_adr; /* 0xE08 */ + u32 lhl_adc_ctl_adr; /* 0xE0C */ + u32 lhl_qdxyz_in_dly_adr; /* 0xE10 */ + u32 lhl_optctl_adr; /* 0xE14 */ + u32 lhl_optct2_adr; /* 0xE18 */ + u32 lhl_scanp_cntr_init_val_adr; /* 0xE1C */ + u32 lhl_opt_togg_val_adr[6]; /* 0xE20-0xE34 */ + u32 lhl_optx_smp_val_adr; /* 0xE38 */ + u32 lhl_opty_smp_val_adr; /* 0xE3C */ + u32 lhl_optz_smp_val_adr; /* 0xE40 */ + u32 lhl_hidoff_keepstate_adr[3]; /* 0xE44-0xE4C */ + u32 lhl_bt_slmboot_ctl0_adr[4]; /* 0xE50-0xE5C */ + u32 lhl_wl_fw_ctl; /* 0xE60 */ + u32 lhl_wl_hw_ctl_adr[2]; /* 0xE64-0xE68 */ + u32 lhl_bt_hw_ctl_adr; /* 0xE6C */ + u32 lhl_top_pwrseq_en_adr; /* 0xE70 */ + u32 lhl_top_pwrdn_ctl_adr; /* 0xE74 */ + u32 lhl_top_pwrup_ctl_adr; /* 0xE78 */ + u32 lhl_top_pwrseq_ctl_adr; /* 0xE7C */ + u32 lhl_top_pwrdn2_ctl_adr; /* 0xE80 */ + u32 lhl_top_pwrup2_ctl_adr; /* 0xE84 */ + u32 wpt_regon_intrp_cfg_adr; /* 0xE88 */ + u32 bt_regon_intrp_cfg_adr; /* 0xE8C */ + u32 wl_regon_intrp_cfg_adr; /* 0xE90 */ + u32 regon_intrp_st_adr; /* 0xE94 */ + u32 regon_intrp_en_adr; /* 0xE98 */ + }; /* chipid */ @@ -308,4 +497,6 @@ struct chipcregs { */ #define PMU_MAX_TRANSITION_DLY 15000 +#define DEFAULT_43012_MIN_RES_MASK 0x0f8bfe77 + #endif /* _SBCHIPC_H */ diff --git a/include/linux/mmc/sdio_ids.h b/include/linux/mmc/sdio_ids.h index 12036619346cf..9da2c103bda48 100644 --- a/include/linux/mmc/sdio_ids.h +++ b/include/linux/mmc/sdio_ids.h @@ -63,9 +63,10 @@ #define SDIO_DEVICE_ID_BROADCOM_4339 0x4339 #define SDIO_DEVICE_ID_BROADCOM_4345 0x4345 #define SDIO_DEVICE_ID_BROADCOM_4354 0x4354 -#define SDIO_DEVICE_ID_BROADCOM_CYPRESS_89359 0x4355 +#define SDIO_DEVICE_ID_BROADCOM_CYPRESS_89459 0x4355 #define SDIO_DEVICE_ID_BROADCOM_4356 0x4356 #define SDIO_DEVICE_ID_BROADCOM_4359 0x4359 +#define SDIO_DEVICE_ID_BROADCOM_CYPRESS_43439 0xa9af #define SDIO_DEVICE_ID_BROADCOM_CYPRESS_4373 0x4373 #define SDIO_DEVICE_ID_BROADCOM_CYPRESS_43012 0xa804 #define SDIO_DEVICE_ID_BROADCOM_43143 0xa887 @@ -76,6 +77,13 @@ #define SDIO_DEVICE_ID_BROADCOM_43430 0xa9a6 #define SDIO_DEVICE_ID_BROADCOM_43455 0xa9bf +#define SDIO_VENDOR_ID_CYPRESS 0x04b4 +#define SDIO_DEVICE_ID_CYPRESS_54590 0xbd3a +#define SDIO_DEVICE_ID_CYPRESS_54591 0xbd3b +#define SDIO_DEVICE_ID_CYPRESS_54594 0xbd3c +#define SDIO_DEVICE_ID_CYPRESS_43439 0xbd3d +#define SDIO_DEVICE_ID_CYPRESS_55560 0xbd31 + #define SDIO_VENDOR_ID_MARVELL 0x02df #define SDIO_DEVICE_ID_MARVELL_LIBERTAS 0x9103 #define SDIO_DEVICE_ID_MARVELL_8688_WLAN 0x9104 From 0388493e4a058b2859ba10a6c150e2794cbfc2a2 Mon Sep 17 00:00:00 2001 From: Alvin Xie Date: Mon, 10 Jul 2023 19:24:52 +0800 Subject: [PATCH 2/2] net: wireless: modify ap6212 firmware select Signed-off-by: Alvin Xie --- drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c index 7fc2129c0bfc9..84cc0213c6c3d 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c @@ -644,7 +644,7 @@ CY_FW_DEF(43362, "cyfmac43362-sdio"); CY_FW_DEF(4339, "cyfmac4339-sdio"); BRCMF_FW_DEF(43430A0, "brcmfmac43430a0-sdio"); /* Note the names are not postfixed with a1 for backward compatibility */ -CY_FW_DEF(43430A1, "cyfmac43430-sdio"); +BRCMF_FW_DEF(43430A1, "brcmfmac43430-sdio"); CY_FW_DEF(43439, "cyfmac43439-sdio"); CY_FW_DEF(43455, "cyfmac43455-sdio"); BRCMF_FW_DEF(43456, "brcmfmac43456-sdio");