diff options
Diffstat (limited to 'drivers/net/wireless/brcm80211/brcmfmac')
18 files changed, 2510 insertions, 485 deletions
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c index 11fd1c73558..f3149debede 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c @@ -65,7 +65,7 @@ int brcmf_sdio_intr_register(struct brcmf_sdio_dev *sdiodev) u8 data; unsigned long flags; - brcmf_dbg(TRACE, "Entering: irq %d\n", sdiodev->irq); + brcmf_dbg(SDIO, "Entering: irq %d\n", sdiodev->irq); ret = request_irq(sdiodev->irq, brcmf_sdio_irqhandler, sdiodev->irq_flags, "brcmf_oob_intr", @@ -102,7 +102,7 @@ int brcmf_sdio_intr_register(struct brcmf_sdio_dev *sdiodev) int brcmf_sdio_intr_unregister(struct brcmf_sdio_dev *sdiodev) { - brcmf_dbg(TRACE, "Entering\n"); + brcmf_dbg(SDIO, "Entering\n"); sdio_claim_host(sdiodev->func[1]); brcmf_sdio_regwb(sdiodev, SDIO_CCCR_BRCM_SEPINT, 0, NULL); @@ -136,7 +136,7 @@ static void brcmf_sdio_dummy_irqhandler(struct sdio_func *func) int brcmf_sdio_intr_register(struct brcmf_sdio_dev *sdiodev) { - brcmf_dbg(TRACE, "Entering\n"); + brcmf_dbg(SDIO, "Entering\n"); sdio_claim_host(sdiodev->func[1]); sdio_claim_irq(sdiodev->func[1], brcmf_sdio_irqhandler); @@ -148,7 +148,7 @@ int brcmf_sdio_intr_register(struct brcmf_sdio_dev *sdiodev) int brcmf_sdio_intr_unregister(struct brcmf_sdio_dev *sdiodev) { - brcmf_dbg(TRACE, "Entering\n"); + brcmf_dbg(SDIO, "Entering\n"); sdio_claim_host(sdiodev->func[1]); sdio_release_irq(sdiodev->func[2]); @@ -253,9 +253,9 @@ u8 brcmf_sdio_regrb(struct brcmf_sdio_dev *sdiodev, u32 addr, int *ret) u8 data; int retval; - brcmf_dbg(INFO, "addr:0x%08x\n", addr); + brcmf_dbg(SDIO, "addr:0x%08x\n", addr); retval = brcmf_sdio_regrw_helper(sdiodev, addr, &data, false); - brcmf_dbg(INFO, "data:0x%02x\n", data); + brcmf_dbg(SDIO, "data:0x%02x\n", data); if (ret) *ret = retval; @@ -268,9 +268,9 @@ u32 brcmf_sdio_regrl(struct brcmf_sdio_dev *sdiodev, u32 addr, int *ret) u32 data; int retval; - brcmf_dbg(INFO, "addr:0x%08x\n", addr); + brcmf_dbg(SDIO, "addr:0x%08x\n", addr); retval = brcmf_sdio_regrw_helper(sdiodev, addr, &data, false); - brcmf_dbg(INFO, "data:0x%08x\n", data); + brcmf_dbg(SDIO, "data:0x%08x\n", data); if (ret) *ret = retval; @@ -283,7 +283,7 @@ void brcmf_sdio_regwb(struct brcmf_sdio_dev *sdiodev, u32 addr, { int retval; - brcmf_dbg(INFO, "addr:0x%08x, data:0x%02x\n", addr, data); + brcmf_dbg(SDIO, "addr:0x%08x, data:0x%02x\n", addr, data); retval = brcmf_sdio_regrw_helper(sdiodev, addr, &data, true); if (ret) @@ -295,7 +295,7 @@ void brcmf_sdio_regwl(struct brcmf_sdio_dev *sdiodev, u32 addr, { int retval; - brcmf_dbg(INFO, "addr:0x%08x, data:0x%08x\n", addr, data); + brcmf_dbg(SDIO, "addr:0x%08x, data:0x%08x\n", addr, data); retval = brcmf_sdio_regrw_helper(sdiodev, addr, &data, true); if (ret) @@ -358,7 +358,7 @@ brcmf_sdcard_recv_pkt(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn, uint width; int err = 0; - brcmf_dbg(INFO, "fun = %d, addr = 0x%x, size = %d\n", + brcmf_dbg(SDIO, "fun = %d, addr = 0x%x, size = %d\n", fn, addr, pkt->len); width = (flags & SDIO_REQ_4BYTE) ? 4 : 2; @@ -381,7 +381,7 @@ int brcmf_sdcard_recv_chain(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn, uint width; int err = 0; - brcmf_dbg(INFO, "fun = %d, addr = 0x%x, size = %d\n", + brcmf_dbg(SDIO, "fun = %d, addr = 0x%x, size = %d\n", fn, addr, pktq->qlen); width = (flags & SDIO_REQ_4BYTE) ? 4 : 2; @@ -428,7 +428,7 @@ brcmf_sdcard_send_pkt(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn, uint bar0 = addr & ~SBSDIO_SB_OFT_ADDR_MASK; int err = 0; - brcmf_dbg(INFO, "fun = %d, addr = 0x%x, size = %d\n", + brcmf_dbg(SDIO, "fun = %d, addr = 0x%x, size = %d\n", fn, addr, pkt->len); /* Async not implemented yet */ @@ -492,13 +492,13 @@ int brcmf_sdcard_rwdata(struct brcmf_sdio_dev *sdiodev, uint rw, u32 addr, int brcmf_sdcard_abort(struct brcmf_sdio_dev *sdiodev, uint fn) { char t_func = (char)fn; - brcmf_dbg(TRACE, "Enter\n"); + brcmf_dbg(SDIO, "Enter\n"); /* issue abort cmd52 command through F0 */ brcmf_sdioh_request_byte(sdiodev, SDIOH_WRITE, SDIO_FUNC_0, SDIO_CCCR_ABORT, &t_func); - brcmf_dbg(TRACE, "Exit\n"); + brcmf_dbg(SDIO, "Exit\n"); return 0; } diff --git a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh_sdmmc.c b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh_sdmmc.c index d92d373733d..716548989e4 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh_sdmmc.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh_sdmmc.c @@ -139,7 +139,7 @@ int brcmf_sdioh_request_byte(struct brcmf_sdio_dev *sdiodev, uint rw, uint func, { int err_ret; - brcmf_dbg(INFO, "rw=%d, func=%d, addr=0x%05x\n", rw, func, regaddr); + brcmf_dbg(SDIO, "rw=%d, func=%d, addr=0x%05x\n", rw, func, regaddr); brcmf_pm_resume_wait(sdiodev, &sdiodev->request_byte_wait); if (brcmf_pm_resume_error(sdiodev)) @@ -179,7 +179,7 @@ int brcmf_sdioh_request_word(struct brcmf_sdio_dev *sdiodev, return -EINVAL; } - brcmf_dbg(INFO, "rw=%d, func=%d, addr=0x%05x, nbytes=%d\n", + brcmf_dbg(SDIO, "rw=%d, func=%d, addr=0x%05x, nbytes=%d\n", rw, func, addr, nbytes); brcmf_pm_resume_wait(sdiodev, &sdiodev->request_word_wait); @@ -252,7 +252,7 @@ brcmf_sdioh_request_chain(struct brcmf_sdio_dev *sdiodev, uint fix_inc, struct sk_buff *pkt; - brcmf_dbg(TRACE, "Enter\n"); + brcmf_dbg(SDIO, "Enter\n"); brcmf_pm_resume_wait(sdiodev, &sdiodev->request_chain_wait); if (brcmf_pm_resume_error(sdiodev)) @@ -270,7 +270,7 @@ brcmf_sdioh_request_chain(struct brcmf_sdio_dev *sdiodev, uint fix_inc, write ? "TX" : "RX", pkt, SGCount, addr, pkt_len, err_ret); } else { - brcmf_dbg(TRACE, "%s xfr'd %p[%d], addr=0x%05x, len=%d\n", + brcmf_dbg(SDIO, "%s xfr'd %p[%d], addr=0x%05x, len=%d\n", write ? "TX" : "RX", pkt, SGCount, addr, pkt_len); } @@ -280,7 +280,7 @@ brcmf_sdioh_request_chain(struct brcmf_sdio_dev *sdiodev, uint fix_inc, SGCount++; } - brcmf_dbg(TRACE, "Exit\n"); + brcmf_dbg(SDIO, "Exit\n"); return err_ret; } @@ -295,7 +295,7 @@ int brcmf_sdioh_request_buffer(struct brcmf_sdio_dev *sdiodev, uint pkt_len; bool fifo = (fix_inc == SDIOH_DATA_FIX); - brcmf_dbg(TRACE, "Enter\n"); + brcmf_dbg(SDIO, "Enter\n"); if (pkt == NULL) return -EINVAL; @@ -314,7 +314,7 @@ int brcmf_sdioh_request_buffer(struct brcmf_sdio_dev *sdiodev, brcmf_err("%s FAILED %p, addr=0x%05x, pkt_len=%d, ERR=0x%08x\n", write ? "TX" : "RX", pkt, addr, pkt_len, status); } else { - brcmf_dbg(TRACE, "%s xfr'd %p, addr=0x%05x, len=%d\n", + brcmf_dbg(SDIO, "%s xfr'd %p, addr=0x%05x, len=%d\n", write ? "TX" : "RX", pkt, addr, pkt_len); } @@ -350,12 +350,12 @@ static int brcmf_sdioh_enablefuncs(struct brcmf_sdio_dev *sdiodev) u32 fbraddr; u8 func; - brcmf_dbg(TRACE, "\n"); + brcmf_dbg(SDIO, "\n"); /* Get the Card's common CIS address */ sdiodev->func_cis_ptr[0] = brcmf_sdioh_get_cisaddr(sdiodev, SDIO_CCCR_CIS); - brcmf_dbg(INFO, "Card's Common CIS Ptr = 0x%x\n", + brcmf_dbg(SDIO, "Card's Common CIS Ptr = 0x%x\n", sdiodev->func_cis_ptr[0]); /* Get the Card's function CIS (for each function) */ @@ -363,7 +363,7 @@ static int brcmf_sdioh_enablefuncs(struct brcmf_sdio_dev *sdiodev) func <= sdiodev->num_funcs; func++, fbraddr += SDIOD_FBR_SIZE) { sdiodev->func_cis_ptr[func] = brcmf_sdioh_get_cisaddr(sdiodev, SDIO_FBR_CIS + fbraddr); - brcmf_dbg(INFO, "Function %d CIS Ptr = 0x%x\n", + brcmf_dbg(SDIO, "Function %d CIS Ptr = 0x%x\n", func, sdiodev->func_cis_ptr[func]); } @@ -382,7 +382,7 @@ int brcmf_sdioh_attach(struct brcmf_sdio_dev *sdiodev) { int err_ret = 0; - brcmf_dbg(TRACE, "\n"); + brcmf_dbg(SDIO, "\n"); sdiodev->num_funcs = 2; @@ -404,13 +404,13 @@ int brcmf_sdioh_attach(struct brcmf_sdio_dev *sdiodev) out: sdio_release_host(sdiodev->func[1]); - brcmf_dbg(TRACE, "Done\n"); + brcmf_dbg(SDIO, "Done\n"); return err_ret; } void brcmf_sdioh_detach(struct brcmf_sdio_dev *sdiodev) { - brcmf_dbg(TRACE, "\n"); + brcmf_dbg(SDIO, "\n"); /* Disable Function 2 */ sdio_claim_host(sdiodev->func[2]); @@ -458,11 +458,11 @@ static int brcmf_ops_sdio_probe(struct sdio_func *func, struct brcmf_sdio_dev *sdiodev; struct brcmf_bus *bus_if; - brcmf_dbg(TRACE, "Enter\n"); - brcmf_dbg(TRACE, "Class=%x\n", func->class); - brcmf_dbg(TRACE, "sdio vendor ID: 0x%04x\n", func->vendor); - brcmf_dbg(TRACE, "sdio device ID: 0x%04x\n", func->device); - brcmf_dbg(TRACE, "Function#: %d\n", func->num); + brcmf_dbg(SDIO, "Enter\n"); + brcmf_dbg(SDIO, "Class=%x\n", func->class); + brcmf_dbg(SDIO, "sdio vendor ID: 0x%04x\n", func->vendor); + brcmf_dbg(SDIO, "sdio device ID: 0x%04x\n", func->device); + brcmf_dbg(SDIO, "Function#: %d\n", func->num); /* Consume func num 1 but dont do anything with it. */ if (func->num == 1) @@ -501,13 +501,13 @@ static int brcmf_ops_sdio_probe(struct sdio_func *func, if (err) goto fail; - brcmf_dbg(TRACE, "F2 found, calling brcmf_sdio_probe...\n"); + brcmf_dbg(SDIO, "F2 found, calling brcmf_sdio_probe...\n"); err = brcmf_sdio_probe(sdiodev); if (err) { brcmf_err("F2 error, probe failed %d...\n", err); goto fail; } - brcmf_dbg(TRACE, "F2 init completed...\n"); + brcmf_dbg(SDIO, "F2 init completed...\n"); return 0; fail: @@ -523,10 +523,10 @@ static void brcmf_ops_sdio_remove(struct sdio_func *func) struct brcmf_bus *bus_if; struct brcmf_sdio_dev *sdiodev; - brcmf_dbg(TRACE, "Enter\n"); - brcmf_dbg(TRACE, "sdio vendor ID: 0x%04x\n", func->vendor); - brcmf_dbg(TRACE, "sdio device ID: 0x%04x\n", func->device); - brcmf_dbg(TRACE, "Function: %d\n", func->num); + brcmf_dbg(SDIO, "Enter\n"); + brcmf_dbg(SDIO, "sdio vendor ID: 0x%04x\n", func->vendor); + brcmf_dbg(SDIO, "sdio device ID: 0x%04x\n", func->device); + brcmf_dbg(SDIO, "Function: %d\n", func->num); if (func->num != 1 && func->num != 2) return; @@ -543,7 +543,7 @@ static void brcmf_ops_sdio_remove(struct sdio_func *func) kfree(sdiodev); } - brcmf_dbg(TRACE, "Exit\n"); + brcmf_dbg(SDIO, "Exit\n"); } #ifdef CONFIG_PM_SLEEP @@ -554,7 +554,7 @@ static int brcmf_sdio_suspend(struct device *dev) struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio; int ret = 0; - brcmf_dbg(TRACE, "\n"); + brcmf_dbg(SDIO, "\n"); atomic_set(&sdiodev->suspend, true); @@ -645,7 +645,7 @@ static struct platform_driver brcmf_sdio_pd = { void brcmf_sdio_exit(void) { - brcmf_dbg(TRACE, "Enter\n"); + brcmf_dbg(SDIO, "Enter\n"); sdio_unregister_driver(&brcmf_sdmmc_driver); @@ -656,7 +656,7 @@ void brcmf_sdio_init(void) { int ret; - brcmf_dbg(TRACE, "Enter\n"); + brcmf_dbg(SDIO, "Enter\n"); ret = platform_driver_register(&brcmf_sdio_pd); @@ -666,7 +666,7 @@ void brcmf_sdio_init(void) #else void brcmf_sdio_exit(void) { - brcmf_dbg(TRACE, "Enter\n"); + brcmf_dbg(SDIO, "Enter\n"); sdio_unregister_driver(&brcmf_sdmmc_driver); } @@ -675,7 +675,7 @@ void brcmf_sdio_init(void) { int ret; - brcmf_dbg(TRACE, "Enter\n"); + brcmf_dbg(SDIO, "Enter\n"); ret = sdio_register_driver(&brcmf_sdmmc_driver); diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd.h b/drivers/net/wireless/brcm80211/brcmfmac/dhd.h index c7fa20846b3..5249c67b466 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd.h @@ -72,6 +72,7 @@ #define BRCMF_C_SET_WSEC 134 #define BRCMF_C_GET_PHY_NOISE 135 #define BRCMF_C_GET_BSS_INFO 136 +#define BRCMF_C_GET_BANDLIST 140 #define BRCMF_C_SET_SCB_TIMEOUT 158 #define BRCMF_C_GET_PHYLIST 180 #define BRCMF_C_SET_SCAN_CHANNEL_TIME 185 @@ -475,6 +476,11 @@ struct brcmf_sta_info_le { __le32 rx_decrypt_failures; /* # of packet decrypted failed */ }; +struct brcmf_chanspec_list { + __le32 count; /* # of entries */ + __le32 element[1]; /* variable length uint32 list */ +}; + /* * WLC_E_PROBRESP_MSG * WLC_E_P2P_PROBREQ_MSG @@ -542,10 +548,25 @@ struct brcmf_if_event { u8 action; u8 flags; u8 bssidx; + u8 role; }; -/* forward declaration */ +/* forward declarations */ struct brcmf_cfg80211_vif; +struct brcmf_fws_mac_descriptor; + +/** + * enum brcmf_netif_stop_reason - reason for stopping netif queue. + * + * @BRCMF_NETIF_STOP_REASON_FWS_FC: + * netif stopped due to firmware signalling flow control. + * @BRCMF_NETIF_STOP_REASON_BLOCK_BUS: + * netif stopped due to bus blocking. + */ +enum brcmf_netif_stop_reason { + BRCMF_NETIF_STOP_REASON_FWS_FC = 1, + BRCMF_NETIF_STOP_REASON_BLOCK_BUS = 2 +}; /** * struct brcmf_if - interface control information. @@ -554,9 +575,13 @@ struct brcmf_cfg80211_vif; * @vif: points to cfg80211 specific interface information. * @ndev: associated network device. * @stats: interface specific network statistics. + * @setmacaddr_work: worker object for setting mac address. + * @multicast_work: worker object for multicast provisioning. + * @fws_desc: interface specific firmware-signalling descriptor. * @ifidx: interface index in device firmware. * @bssidx: index of bss associated with this interface. * @mac_addr: assigned mac address. + * @netif_stop: bitmap indicates reason why netif queues are stopped. * @pend_8021x_cnt: tracks outstanding number of 802.1x frames. * @pend_8021x_wait: used for signalling change in count. */ @@ -567,9 +592,11 @@ struct brcmf_if { struct net_device_stats stats; struct work_struct setmacaddr_work; struct work_struct multicast_work; + struct brcmf_fws_mac_descriptor *fws_desc; int ifidx; s32 bssidx; u8 mac_addr[ETH_ALEN]; + u8 netif_stop; atomic_t pend_8021x_cnt; wait_queue_head_t pend_8021x_wait; }; @@ -594,6 +621,10 @@ extern int brcmf_net_attach(struct brcmf_if *ifp, bool rtnl_locked); extern struct brcmf_if *brcmf_add_if(struct brcmf_pub *drvr, s32 bssidx, s32 ifidx, char *name, u8 *mac_addr); extern void brcmf_del_if(struct brcmf_pub *drvr, s32 bssidx); +void brcmf_txflowblock_if(struct brcmf_if *ifp, + enum brcmf_netif_stop_reason reason, bool state); extern u32 brcmf_get_chip_info(struct brcmf_if *ifp); +extern void brcmf_txfinalize(struct brcmf_pub *drvr, struct sk_buff *txp, + bool success); #endif /* _BRCMF_H_ */ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_bus.h b/drivers/net/wireless/brcm80211/brcmfmac/dhd_bus.h index 883ef9063e8..080395f49fa 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_bus.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_bus.h @@ -39,10 +39,12 @@ struct brcmf_bus_dcmd { * @txdata: send a data frame to the dongle (callee disposes skb). * @txctl: transmit a control request message to dongle. * @rxctl: receive a control response message from dongle. + * @gettxq: obtain a reference of bus transmit queue (optional). * * This structure provides an abstract interface towards the * bus specific driver. For control messages to common driver - * will assure there is only one active transaction. + * will assure there is only one active transaction. Unless + * indicated otherwise these callbacks are mandatory. */ struct brcmf_bus_ops { int (*init)(struct device *dev); @@ -50,6 +52,7 @@ struct brcmf_bus_ops { int (*txdata)(struct device *dev, struct sk_buff *skb); int (*txctl)(struct device *dev, unsigned char *msg, uint len); int (*rxctl)(struct device *dev, unsigned char *msg, uint len); + struct pktq * (*gettxq)(struct device *dev); }; /** @@ -115,6 +118,14 @@ int brcmf_bus_rxctl(struct brcmf_bus *bus, unsigned char *msg, uint len) return bus->ops->rxctl(bus->dev, msg, len); } +static inline +struct pktq *brcmf_bus_gettxq(struct brcmf_bus *bus) +{ + if (!bus->ops->gettxq) + return ERR_PTR(-ENOENT); + + return bus->ops->gettxq(bus->dev); +} /* * interface functions from common layer */ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_cdc.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_cdc.c index e224bcb9002..59c77aa3b95 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_cdc.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_cdc.c @@ -303,8 +303,8 @@ int brcmf_proto_hdrpull(struct brcmf_pub *drvr, bool do_fws, u8 *ifidx, /* Pop BDC header used to convey priority for buses that don't */ - if (pktbuf->len < BDC_HEADER_LEN) { - brcmf_err("rx data too short (%d < %d)\n", + if (pktbuf->len <= BDC_HEADER_LEN) { + brcmf_dbg(INFO, "rx data too short (%d <= %d)\n", pktbuf->len, BDC_HEADER_LEN); return -EBADE; } diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.c index ac792499b46..202869cd093 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.c @@ -130,7 +130,7 @@ ssize_t brcmf_debugfs_fws_stats_read(struct file *f, char __user *data, size_t count, loff_t *ppos) { struct brcmf_fws_stats *fwstats = f->private_data; - char buf[100]; + char buf[650]; int res; /* only allow read from start */ @@ -138,14 +138,52 @@ ssize_t brcmf_debugfs_fws_stats_read(struct file *f, char __user *data, return 0; res = scnprintf(buf, sizeof(buf), - "header_pulls: %u\n" - "header_only_pkt: %u\n" - "tlv_parse_failed: %u\n" - "tlv_invalid_type: %u\n", + "header_pulls: %u\n" + "header_only_pkt: %u\n" + "tlv_parse_failed: %u\n" + "tlv_invalid_type: %u\n" + "mac_update_fails: %u\n" + "ps_update_fails: %u\n" + "if_update_fails: %u\n" + "pkt2bus: %u\n" + "generic_error: %u\n" + "rollback_success: %u\n" + "rollback_failed: %u\n" + "delayq_full: %u\n" + "supprq_full: %u\n" + "txs_indicate: %u\n" + "txs_discard: %u\n" + "txs_suppr_core: %u\n" + "txs_suppr_ps: %u\n" + "txs_tossed: %u\n" + "send_pkts: BK:%u BE:%u VO:%u VI:%u BCMC:%u\n" + "fifo_credits_sent: BK:%u BE:%u VO:%u VI:%u BCMC:%u\n", fwstats->header_pulls, fwstats->header_only_pkt, fwstats->tlv_parse_failed, - fwstats->tlv_invalid_type); + fwstats->tlv_invalid_type, + fwstats->mac_update_failed, + fwstats->mac_ps_update_failed, + fwstats->if_update_failed, + fwstats->pkt2bus, + fwstats->generic_error, + fwstats->rollback_success, + fwstats->rollback_failed, + fwstats->delayq_full_error, + fwstats->supprq_full_error, + fwstats->txs_indicate, + fwstats->txs_discard, + fwstats->txs_supp_core, + fwstats->txs_supp_ps, + fwstats->txs_tossed, + fwstats->send_pkts[0], fwstats->send_pkts[1], + fwstats->send_pkts[2], fwstats->send_pkts[3], + fwstats->send_pkts[4], + fwstats->fifo_credits_sent[0], + fwstats->fifo_credits_sent[1], + fwstats->fifo_credits_sent[2], + fwstats->fifo_credits_sent[3], + fwstats->fifo_credits_sent[4]); return simple_read_from_buffer(data, count, ppos, buf, res); } diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.h b/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.h index 4bc646bde16..009c87bfd9a 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.h @@ -34,6 +34,7 @@ #define BRCMF_SCAN_VAL 0x00004000 #define BRCMF_CONN_VAL 0x00008000 #define BRCMF_CDC_VAL 0x00010000 +#define BRCMF_SDIO_VAL 0x00020000 /* set default print format */ #undef pr_fmt @@ -92,6 +93,7 @@ do { \ #define brcmf_dbg_hex_dump(test, data, len, fmt, ...) \ do { \ + trace_brcmf_hexdump((void *)data, len); \ if (test) \ brcmu_dbg_hex_dump(data, len, fmt, ##__VA_ARGS__); \ } while (0) @@ -137,6 +139,25 @@ struct brcmf_fws_stats { u32 tlv_invalid_type; u32 header_only_pkt; u32 header_pulls; + u32 pkt2bus; + u32 send_pkts[5]; + u32 fifo_credits_sent[5]; + u32 fifo_credits_back[6]; + u32 generic_error; + u32 mac_update_failed; + u32 mac_ps_update_failed; + u32 if_update_failed; + u32 packet_request_failed; + u32 credit_request_failed; + u32 rollback_success; + u32 rollback_failed; + u32 delayq_full_error; + u32 supprq_full_error; + u32 txs_indicate; + u32 txs_discard; + u32 txs_supp_core; + u32 txs_supp_ps; + u32 txs_tossed; }; struct brcmf_pub; diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c index fa5a2af04d4..763a84eba21 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c @@ -223,18 +223,7 @@ static netdev_tx_t brcmf_netdev_start_xmit(struct sk_buff *skb, goto done; } - /* handle ethernet header */ - eh = (struct ethhdr *)(skb->data); - if (is_multicast_ether_addr(eh->h_dest)) - drvr->tx_multicast++; - if (ntohs(eh->h_proto) == ETH_P_PAE) - atomic_inc(&ifp->pend_8021x_cnt); - - /* If the protocol uses a data header, apply it */ - brcmf_proto_hdrpush(drvr, ifp->ifidx, 0, skb); - - /* Use bus module to send data frame */ - ret = brcmf_bus_txdata(drvr->bus_if, skb); + ret = brcmf_fws_process_skb(ifp, skb); done: if (ret) { @@ -248,9 +237,27 @@ done: return NETDEV_TX_OK; } +void brcmf_txflowblock_if(struct brcmf_if *ifp, + enum brcmf_netif_stop_reason reason, bool state) +{ + if (!ifp) + return; + + brcmf_dbg(TRACE, "enter: idx=%d stop=0x%X reason=%d state=%d\n", + ifp->bssidx, ifp->netif_stop, reason, state); + if (state) { + if (!ifp->netif_stop) + netif_stop_queue(ifp->ndev); + ifp->netif_stop |= reason; + } else { + ifp->netif_stop &= ~reason; + if (!ifp->netif_stop) + netif_wake_queue(ifp->ndev); + } +} + void brcmf_txflowblock(struct device *dev, bool state) { - struct net_device *ndev; struct brcmf_bus *bus_if = dev_get_drvdata(dev); struct brcmf_pub *drvr = bus_if->drvr; int i; @@ -258,13 +265,8 @@ void brcmf_txflowblock(struct device *dev, bool state) brcmf_dbg(TRACE, "Enter\n"); for (i = 0; i < BRCMF_MAX_IFS; i++) - if (drvr->iflist[i]) { - ndev = drvr->iflist[i]->ndev; - if (state) - netif_stop_queue(ndev); - else - netif_wake_queue(ndev); - } + brcmf_txflowblock_if(drvr->iflist[i], + BRCMF_NETIF_STOP_REASON_BLOCK_BUS, state); } void brcmf_rx_frames(struct device *dev, struct sk_buff_head *skb_list) @@ -321,13 +323,8 @@ void brcmf_rx_frames(struct device *dev, struct sk_buff_head *skb_list) /* Strip header, count, deliver upward */ skb_pull(skb, ETH_HLEN); - /* Process special event packets and then discard them */ - brcmf_fweh_process_skb(drvr, skb, &ifidx); - - if (drvr->iflist[ifidx]) { - ifp = drvr->iflist[ifidx]; - ifp->ndev->last_rx = jiffies; - } + /* Process special event packets */ + brcmf_fweh_process_skb(drvr, skb); if (!(ifp->ndev->flags & IFF_UP)) { brcmu_pkt_buf_free_skb(skb); @@ -350,14 +347,13 @@ void brcmf_rx_frames(struct device *dev, struct sk_buff_head *skb_list) } } -void brcmf_txcomplete(struct device *dev, struct sk_buff *txp, bool success) +void brcmf_txfinalize(struct brcmf_pub *drvr, struct sk_buff *txp, + bool success) { - u8 ifidx; + struct brcmf_if *ifp; struct ethhdr *eh; + u8 ifidx; u16 type; - struct brcmf_bus *bus_if = dev_get_drvdata(dev); - struct brcmf_pub *drvr = bus_if->drvr; - struct brcmf_if *ifp; int res; res = brcmf_proto_hdrpull(drvr, false, &ifidx, txp); @@ -378,11 +374,24 @@ void brcmf_txcomplete(struct device *dev, struct sk_buff *txp, bool success) } if (!success) ifp->stats.tx_errors++; - done: brcmu_pkt_buf_free_skb(txp); } +void brcmf_txcomplete(struct device *dev, struct sk_buff *txp, bool success) +{ + struct brcmf_bus *bus_if = dev_get_drvdata(dev); + struct brcmf_pub *drvr = bus_if->drvr; + + /* await txstatus signal for firmware if active */ + if (brcmf_fws_fc_active(drvr->fws)) { + if (!success) + brcmf_fws_bustxfail(drvr->fws, txp); + } else { + brcmf_txfinalize(drvr, txp, success); + } +} + static struct net_device_stats *brcmf_netdev_get_stats(struct net_device *ndev) { struct brcmf_if *ifp = netdev_priv(ndev); @@ -741,28 +750,35 @@ struct brcmf_if *brcmf_add_if(struct brcmf_pub *drvr, s32 bssidx, s32 ifidx, } } - /* Allocate netdev, including space for private structure */ - ndev = alloc_netdev(sizeof(struct brcmf_if), name, ether_setup); - if (!ndev) { - brcmf_err("OOM - alloc_netdev\n"); - return ERR_PTR(-ENOMEM); + if (!brcmf_p2p_enable && bssidx == 1) { + /* this is P2P_DEVICE interface */ + brcmf_dbg(INFO, "allocate non-netdev interface\n"); + ifp = kzalloc(sizeof(*ifp), GFP_KERNEL); + } else { + brcmf_dbg(INFO, "allocate netdev interface\n"); + /* Allocate netdev, including space for private structure */ + ndev = alloc_netdev(sizeof(*ifp), name, ether_setup); + if (!ndev) { + brcmf_err("OOM - alloc_netdev\n"); + return ERR_PTR(-ENOMEM); + } + + ifp = netdev_priv(ndev); + ifp->ndev = ndev; } - ifp = netdev_priv(ndev); - ifp->ndev = ndev; ifp->drvr = drvr; drvr->iflist[bssidx] = ifp; ifp->ifidx = ifidx; ifp->bssidx = bssidx; - init_waitqueue_head(&ifp->pend_8021x_wait); if (mac_addr != NULL) memcpy(ifp->mac_addr, mac_addr, ETH_ALEN); brcmf_dbg(TRACE, " ==== pid:%x, if:%s (%pM) created ===\n", - current->pid, ifp->ndev->name, ifp->mac_addr); + current->pid, name, ifp->mac_addr); return ifp; } @@ -794,11 +810,13 @@ void brcmf_del_if(struct brcmf_pub *drvr, s32 bssidx) } unregister_netdev(ifp->ndev); - drvr->iflist[bssidx] = NULL; if (bssidx == 0) brcmf_cfg80211_detach(drvr->config); free_netdev(ifp->ndev); + } else { + kfree(ifp); } + drvr->iflist[bssidx] = NULL; } int brcmf_attach(uint bus_hdrlen, struct device *dev) @@ -882,6 +900,7 @@ int brcmf_bus_start(struct device *dev) drvr->fw_signals = true; (void)brcmf_fws_init(drvr); + brcmf_fws_add_interface(ifp); drvr->config = brcmf_cfg80211_attach(drvr, bus_if->dev); if (drvr->config == NULL) { @@ -899,8 +918,10 @@ fail: brcmf_err("failed: %d\n", ret); if (drvr->config) brcmf_cfg80211_detach(drvr->config); - if (drvr->fws) + if (drvr->fws) { + brcmf_fws_del_interface(ifp); brcmf_fws_deinit(drvr); + } free_netdev(ifp->ndev); drvr->iflist[0] = NULL; if (p2p_ifp) { @@ -956,16 +977,17 @@ void brcmf_detach(struct device *dev) /* make sure primary interface removed last */ for (i = BRCMF_MAX_IFS-1; i > -1; i--) - if (drvr->iflist[i]) + if (drvr->iflist[i]) { + brcmf_fws_del_interface(drvr->iflist[i]); brcmf_del_if(drvr, i); + } brcmf_bus_detach(drvr); if (drvr->prot) brcmf_proto_detach(drvr); - if (drvr->fws) - brcmf_fws_deinit(drvr); + brcmf_fws_deinit(drvr); brcmf_debugfs_detach(drvr); bus_if->drvr = NULL; diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c index 9a2edd3f0a5..4ff2d3c52ee 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c @@ -94,6 +94,7 @@ struct rte_console { #include "dhd_bus.h" #include "dhd_dbg.h" +#include "tracepoint.h" #define TXQLEN 2048 /* bulk tx queue length */ #define TXHI (TXQLEN - 256) /* turn on flow control above TXHI */ @@ -675,7 +676,7 @@ static int brcmf_sdbrcm_htclk(struct brcmf_sdio *bus, bool on, bool pendok) u8 clkctl, clkreq, devctl; unsigned long timeout; - brcmf_dbg(TRACE, "Enter\n"); + brcmf_dbg(SDIO, "Enter\n"); clkctl = 0; @@ -713,7 +714,7 @@ static int brcmf_sdbrcm_htclk(struct brcmf_sdio *bus, bool on, bool pendok) devctl |= SBSDIO_DEVCTL_CA_INT_ONLY; brcmf_sdio_regwb(bus->sdiodev, SBSDIO_DEVICE_CTL, devctl, &err); - brcmf_dbg(INFO, "CLKCTL: set PENDING\n"); + brcmf_dbg(SDIO, "CLKCTL: set PENDING\n"); bus->clkstate = CLK_PENDING; return 0; @@ -750,7 +751,7 @@ static int brcmf_sdbrcm_htclk(struct brcmf_sdio *bus, bool on, bool pendok) /* Mark clock available */ bus->clkstate = CLK_AVAIL; - brcmf_dbg(INFO, "CLKCTL: turned ON\n"); + brcmf_dbg(SDIO, "CLKCTL: turned ON\n"); #if defined(DEBUG) if (!bus->alp_only) { @@ -775,7 +776,7 @@ static int brcmf_sdbrcm_htclk(struct brcmf_sdio *bus, bool on, bool pendok) bus->clkstate = CLK_SDONLY; brcmf_sdio_regwb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, clkreq, &err); - brcmf_dbg(INFO, "CLKCTL: turned OFF\n"); + brcmf_dbg(SDIO, "CLKCTL: turned OFF\n"); if (err) { brcmf_err("Failed access turning clock off: %d\n", err); @@ -788,7 +789,7 @@ static int brcmf_sdbrcm_htclk(struct brcmf_sdio *bus, bool on, bool pendok) /* Change idle/active SD state */ static int brcmf_sdbrcm_sdclk(struct brcmf_sdio *bus, bool on) { - brcmf_dbg(TRACE, "Enter\n"); + brcmf_dbg(SDIO, "Enter\n"); if (on) bus->clkstate = CLK_SDONLY; @@ -805,7 +806,7 @@ static int brcmf_sdbrcm_clkctl(struct brcmf_sdio *bus, uint target, bool pendok) uint oldstate = bus->clkstate; #endif /* DEBUG */ - brcmf_dbg(TRACE, "Enter\n"); + brcmf_dbg(SDIO, "Enter\n"); /* Early exit if we're already there */ if (bus->clkstate == target) { @@ -849,7 +850,7 @@ static int brcmf_sdbrcm_clkctl(struct brcmf_sdio *bus, uint target, bool pendok) break; } #ifdef DEBUG - brcmf_dbg(INFO, "%d -> %d\n", oldstate, bus->clkstate); + brcmf_dbg(SDIO, "%d -> %d\n", oldstate, bus->clkstate); #endif /* DEBUG */ return 0; @@ -862,7 +863,7 @@ static u32 brcmf_sdbrcm_hostmail(struct brcmf_sdio *bus) u8 fcbits; int ret; - brcmf_dbg(TRACE, "Enter\n"); + brcmf_dbg(SDIO, "Enter\n"); /* Read mailbox data and ack that we did so */ ret = r_sdreg32(bus, &hmb_data, @@ -875,7 +876,7 @@ static u32 brcmf_sdbrcm_hostmail(struct brcmf_sdio *bus) /* Dongle recomposed rx frames, accept them again */ if (hmb_data & HMB_DATA_NAKHANDLED) { - brcmf_dbg(INFO, "Dongle reports NAK handled, expect rtx of %d\n", + brcmf_dbg(SDIO, "Dongle reports NAK handled, expect rtx of %d\n", bus->rx_seq); if (!bus->rxskip) brcmf_err("unexpected NAKHANDLED!\n"); @@ -896,7 +897,7 @@ static u32 brcmf_sdbrcm_hostmail(struct brcmf_sdio *bus) "expecting %d\n", bus->sdpcm_ver, SDPCM_PROT_VERSION); else - brcmf_dbg(INFO, "Dongle ready, protocol version %d\n", + brcmf_dbg(SDIO, "Dongle ready, protocol version %d\n", bus->sdpcm_ver); } @@ -970,7 +971,7 @@ static void brcmf_sdbrcm_rxfail(struct brcmf_sdio *bus, bool abort, bool rtx) if (!retries) brcmf_err("count never zeroed: last 0x%04x\n", lastrbc); else - brcmf_dbg(INFO, "flush took %d iterations\n", 0xffff - retries); + brcmf_dbg(SDIO, "flush took %d iterations\n", 0xffff - retries); if (rtx) { bus->sdcnt.rxrtx++; @@ -1173,7 +1174,7 @@ static u8 brcmf_sdbrcm_rxglom(struct brcmf_sdio *bus, u8 rxseq) /* If packets, issue read(s) and send up packet chain */ /* Return sequence numbers consumed? */ - brcmf_dbg(TRACE, "start: glomd %p glom %p\n", + brcmf_dbg(SDIO, "start: glomd %p glom %p\n", bus->glomd, skb_peek(&bus->glom)); /* If there's a descriptor, generate the packet chain */ @@ -1781,7 +1782,6 @@ static int brcmf_sdbrcm_txpkt(struct brcmf_sdio *bus, struct sk_buff *pkt, u8 *frame; u16 len, pad = 0; u32 swheader; - struct sk_buff *new; int i; brcmf_dbg(TRACE, "Enter\n"); @@ -1795,27 +1795,14 @@ static int brcmf_sdbrcm_txpkt(struct brcmf_sdio *bus, struct sk_buff *pkt, brcmf_dbg(INFO, "insufficient headroom %d for %d pad\n", skb_headroom(pkt), pad); bus->sdiodev->bus_if->tx_realloc++; - new = brcmu_pkt_buf_get_skb(pkt->len + BRCMF_SDALIGN); - if (!new) { - brcmf_err("couldn't allocate new %d-byte packet\n", - pkt->len + BRCMF_SDALIGN); - ret = -ENOMEM; + ret = skb_cow(pkt, BRCMF_SDALIGN); + if (ret) goto done; - } - - pkt_align(new, pkt->len, BRCMF_SDALIGN); - memcpy(new->data, pkt->data, pkt->len); - brcmu_pkt_buf_free_skb(pkt); - pkt = new; - frame = (u8 *) (pkt->data); - /* precondition: (frame % BRCMF_SDALIGN) == 0) */ - pad = 0; - } else { - skb_push(pkt, pad); - frame = (u8 *) (pkt->data); - /* precondition: pad + SDPCM_HDRLEN <= pkt->len */ - memset(frame, 0, pad + SDPCM_HDRLEN); + pad = ((unsigned long)frame % BRCMF_SDALIGN); } + skb_push(pkt, pad); + frame = (u8 *) (pkt->data); + memset(frame, 0, pad + SDPCM_HDRLEN); } /* precondition: pad < BRCMF_SDALIGN */ @@ -1830,8 +1817,8 @@ static int brcmf_sdbrcm_txpkt(struct brcmf_sdio *bus, struct sk_buff *pkt, (((pad + SDPCM_HDRLEN) << SDPCM_DOFFSET_SHIFT) & SDPCM_DOFFSET_MASK); - put_unaligned_le32(swheader, frame + SDPCM_FRAMETAG_LEN); - put_unaligned_le32(0, frame + SDPCM_FRAMETAG_LEN + sizeof(swheader)); + *(((__le32 *) frame) + 1) = cpu_to_le32(swheader); + *(((__le32 *) frame) + 2) = 0; #ifdef DEBUG tx_packets[pkt->priority]++; @@ -1897,7 +1884,7 @@ static int brcmf_sdbrcm_txpkt(struct brcmf_sdio *bus, struct sk_buff *pkt, done: /* restore pkt buffer pointer before calling tx complete routine */ skb_pull(pkt, SDPCM_HDRLEN + pad); - brcmf_txcomplete(bus->sdiodev->dev, pkt, ret != 0); + brcmf_txcomplete(bus->sdiodev->dev, pkt, ret == 0); return ret; } @@ -2131,7 +2118,7 @@ static void brcmf_sdbrcm_dpc(struct brcmf_sdio *bus) bus->sdiodev->bus_if->state = BRCMF_BUS_DOWN; } - brcmf_dbg(INFO, "DPC: PENDING, devctl 0x%02x clkctl 0x%02x\n", + brcmf_dbg(SDIO, "DPC: PENDING, devctl 0x%02x clkctl 0x%02x\n", devctl, clkctl); if (SBSDIO_HTAV(clkctl)) { @@ -2307,6 +2294,15 @@ static void brcmf_sdbrcm_dpc(struct brcmf_sdio *bus) } } +static struct pktq *brcmf_sdbrcm_bus_gettxq(struct device *dev) +{ + struct brcmf_bus *bus_if = dev_get_drvdata(dev); + struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio; + struct brcmf_sdio *bus = sdiodev->bus; + + return &bus->txq; +} + static int brcmf_sdbrcm_bus_txdata(struct device *dev, struct sk_buff *pkt) { int ret = -EBADE; @@ -2392,7 +2388,7 @@ brcmf_sdbrcm_membytes(struct brcmf_sdio *bus, bool write, u32 address, u8 *data, /* Do the transfer(s) */ while (size) { - brcmf_dbg(INFO, "%s %d bytes at offset 0x%08x in window 0x%08x\n", + brcmf_dbg(SDIO, "%s %d bytes at offset 0x%08x in window 0x%08x\n", write ? "write" : "read", dsize, sdaddr, address & SBSDIO_SBWINDOW_MASK); bcmerror = brcmf_sdcard_rwdata(bus->sdiodev, write, @@ -2625,10 +2621,10 @@ brcmf_sdbrcm_bus_txctl(struct device *dev, unsigned char *msg, uint msglen) msecs_to_jiffies(2000)); if (!bus->ctrl_frame_stat) { - brcmf_dbg(INFO, "ctrl_frame_stat == false\n"); + brcmf_dbg(SDIO, "ctrl_frame_stat == false\n"); ret = 0; } else { - brcmf_dbg(INFO, "ctrl_frame_stat == true\n"); + brcmf_dbg(SDIO, "ctrl_frame_stat == true\n"); ret = -1; } } @@ -2699,7 +2695,7 @@ static int brcmf_sdio_readshared(struct brcmf_sdio *bus, addr = le32_to_cpu(addr_le); - brcmf_dbg(INFO, "sdpcm_shared address 0x%08X\n", addr); + brcmf_dbg(SDIO, "sdpcm_shared address 0x%08X\n", addr); /* * Check if addr is valid. @@ -2726,8 +2722,8 @@ static int brcmf_sdio_readshared(struct brcmf_sdio *bus, sh->console_addr = le32_to_cpu(sh_le.console_addr); sh->msgtrace_addr = le32_to_cpu(sh_le.msgtrace_addr); - if ((sh->flags & SDPCM_SHARED_VERSION_MASK) != SDPCM_SHARED_VERSION) { - brcmf_err("sdpcm_shared version mismatch: dhd %d dongle %d\n", + if ((sh->flags & SDPCM_SHARED_VERSION_MASK) > SDPCM_SHARED_VERSION) { + brcmf_err("sdpcm shared version unsupported: dhd %d dongle %d\n", SDPCM_SHARED_VERSION, sh->flags & SDPCM_SHARED_VERSION_MASK); return -EPROTO; @@ -2809,21 +2805,18 @@ static int brcmf_sdio_trap_info(struct brcmf_sdio *bus, struct sdpcm_shared *sh, int error, res; char buf[350]; struct brcmf_trap_info tr; - int nbytes; loff_t pos = 0; - if ((sh->flags & SDPCM_SHARED_TRAP) == 0) + if ((sh->flags & SDPCM_SHARED_TRAP) == 0) { + brcmf_dbg(INFO, "no trap in firmware\n"); return 0; + } error = brcmf_sdbrcm_membytes(bus, false, sh->trap_addr, (u8 *)&tr, sizeof(struct brcmf_trap_info)); if (error < 0) return error; - nbytes = brcmf_sdio_dump_console(bus, sh, data, count); - if (nbytes < 0) - return nbytes; - res = scnprintf(buf, sizeof(buf), "dongle trap info: type 0x%x @ epc 0x%08x\n" " cpsr 0x%08x spsr 0x%08x sp 0x%08x\n" @@ -2839,12 +2832,7 @@ static int brcmf_sdio_trap_info(struct brcmf_sdio *bus, struct sdpcm_shared *sh, le32_to_cpu(tr.r4), le32_to_cpu(tr.r5), le32_to_cpu(tr.r6), le32_to_cpu(tr.r7)); - error = simple_read_from_buffer(data+nbytes, count, &pos, buf, res); - if (error < 0) - return error; - - nbytes += error; - return nbytes; + return simple_read_from_buffer(data, count, &pos, buf, res); } static int brcmf_sdio_assert_info(struct brcmf_sdio *bus, @@ -2926,14 +2914,20 @@ static int brcmf_sdbrcm_died_dump(struct brcmf_sdio *bus, char __user *data, error = brcmf_sdio_assert_info(bus, &sh, data, count); if (error < 0) goto done; - nbytes = error; - error = brcmf_sdio_trap_info(bus, &sh, data, count); + + error = brcmf_sdio_trap_info(bus, &sh, data+nbytes, count); + if (error < 0) + goto done; + nbytes += error; + + error = brcmf_sdio_dump_console(bus, &sh, data+nbytes, count); if (error < 0) goto done; + nbytes += error; - error += nbytes; - *ppos += error; + error = nbytes; + *ppos += nbytes; done: return error; } @@ -3309,15 +3303,15 @@ static int _brcmf_sdbrcm_download_firmware(struct brcmf_sdio *bus) goto err; } - /* External image takes precedence if specified */ if (brcmf_sdbrcm_download_code_file(bus)) { brcmf_err("dongle image file download failed\n"); goto err; } - /* External nvram takes precedence if specified */ - if (brcmf_sdbrcm_download_nvram(bus)) + if (brcmf_sdbrcm_download_nvram(bus)) { brcmf_err("dongle nvram file download failed\n"); + goto err; + } /* Take arm out of reset */ if (brcmf_sdbrcm_download_state(bus, false)) { @@ -3848,6 +3842,7 @@ static struct brcmf_bus_ops brcmf_sdio_bus_ops = { .txdata = brcmf_sdbrcm_bus_txdata, .txctl = brcmf_sdbrcm_bus_txctl, .rxctl = brcmf_sdbrcm_bus_rxctl, + .gettxq = brcmf_sdbrcm_bus_gettxq, }; void *brcmf_sdbrcm_probe(u32 regsva, struct brcmf_sdio_dev *sdiodev) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fweh.c b/drivers/net/wireless/brcm80211/brcmfmac/fweh.c index e9d6f91a1f2..5a64280e648 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/fweh.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/fweh.c @@ -20,6 +20,8 @@ #include "dhd.h" #include "dhd_dbg.h" +#include "tracepoint.h" +#include "fwsignal.h" #include "fweh.h" #include "fwil.h" @@ -154,7 +156,7 @@ static int brcmf_fweh_call_event_handler(struct brcmf_if *ifp, fweh = &ifp->drvr->fweh; /* handle the event if valid interface and handler */ - if (ifp->ndev && fweh->evt_handler[code]) + if (fweh->evt_handler[code]) err = fweh->evt_handler[code](ifp, emsg, data); else brcmf_err("unhandled event %d ignored\n", code); @@ -179,9 +181,9 @@ static void brcmf_fweh_handle_if_event(struct brcmf_pub *drvr, struct brcmf_if *ifp; int err = 0; - brcmf_dbg(EVENT, "action: %u idx: %u bsscfg: %u flags: %u\n", - ifevent->action, ifevent->ifidx, - ifevent->bssidx, ifevent->flags); + brcmf_dbg(EVENT, "action: %u idx: %u bsscfg: %u flags: %u role: %u\n", + ifevent->action, ifevent->ifidx, ifevent->bssidx, + ifevent->flags, ifevent->role); if (ifevent->ifidx >= BRCMF_MAX_IFS) { brcmf_err("invalid interface index: %u\n", @@ -198,15 +200,20 @@ static void brcmf_fweh_handle_if_event(struct brcmf_pub *drvr, emsg->ifname, emsg->addr); if (IS_ERR(ifp)) return; - + brcmf_fws_add_interface(ifp); if (!drvr->fweh.evt_handler[BRCMF_E_IF]) err = brcmf_net_attach(ifp, false); } + if (ifevent->action == BRCMF_E_IF_CHANGE) + brcmf_fws_reset_interface(ifp); + err = brcmf_fweh_call_event_handler(ifp, emsg->event_code, emsg, data); - if (ifevent->action == BRCMF_E_IF_DEL) + if (ifevent->action == BRCMF_E_IF_DEL) { + brcmf_fws_del_interface(ifp); brcmf_del_if(drvr, ifevent->bssidx); + } } /** @@ -400,13 +407,12 @@ int brcmf_fweh_activate_events(struct brcmf_if *ifp) * * @drvr: driver information object. * @event_packet: event packet to process. - * @ifidx: index of the firmware interface (may change). * * If the packet buffer contains a firmware event message it will * dispatch the event to a registered handler (using worker). */ void brcmf_fweh_process_event(struct brcmf_pub *drvr, - struct brcmf_event *event_packet, u8 *ifidx) + struct brcmf_event *event_packet) { enum brcmf_fweh_event_code code; struct brcmf_fweh_info *fweh = &drvr->fweh; @@ -418,7 +424,6 @@ void brcmf_fweh_process_event(struct brcmf_pub *drvr, /* get event info */ code = get_unaligned_be32(&event_packet->msg.event_type); datalen = get_unaligned_be32(&event_packet->msg.datalen); - *ifidx = event_packet->msg.ifidx; data = &event_packet[1]; if (code >= BRCMF_E_LAST) @@ -435,7 +440,7 @@ void brcmf_fweh_process_event(struct brcmf_pub *drvr, return; event->code = code; - event->ifidx = *ifidx; + event->ifidx = event_packet->msg.ifidx; /* use memcpy to get aligned event message */ memcpy(&event->emsg, &event_packet->msg, sizeof(event->emsg)); diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fweh.h b/drivers/net/wireless/brcm80211/brcmfmac/fweh.h index 8c39b51dccc..6ec5db9c60a 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/fweh.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/fweh.h @@ -187,10 +187,10 @@ void brcmf_fweh_unregister(struct brcmf_pub *drvr, enum brcmf_fweh_event_code code); int brcmf_fweh_activate_events(struct brcmf_if *ifp); void brcmf_fweh_process_event(struct brcmf_pub *drvr, - struct brcmf_event *event_packet, u8 *ifidx); + struct brcmf_event *event_packet); static inline void brcmf_fweh_process_skb(struct brcmf_pub *drvr, - struct sk_buff *skb, u8 *ifidx) + struct sk_buff *skb) { struct brcmf_event *event_packet; u8 *data; @@ -213,7 +213,7 @@ static inline void brcmf_fweh_process_skb(struct brcmf_pub *drvr, if (usr_stype != BCMILCP_BCM_SUBTYPE_EVENT) return; - brcmf_fweh_process_event(drvr, event_packet, ifidx); + brcmf_fweh_process_event(drvr, event_packet); } #endif /* FWEH_H_ */ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fwil.c b/drivers/net/wireless/brcm80211/brcmfmac/fwil.c index 8d1def935b8..04f395930d8 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/fwil.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/fwil.c @@ -25,6 +25,7 @@ #include "dhd.h" #include "dhd_bus.h" #include "dhd_dbg.h" +#include "tracepoint.h" #include "fwil.h" diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c index 071d55f9cd4..b3c608ee37c 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c @@ -14,17 +14,22 @@ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include <linux/types.h> +#include <linux/module.h> #include <linux/if_ether.h> #include <linux/spinlock.h> #include <linux/skbuff.h> #include <linux/netdevice.h> +#include <linux/etherdevice.h> #include <linux/err.h> #include <uapi/linux/nl80211.h> +#include <net/cfg80211.h> #include <brcmu_utils.h> #include <brcmu_wifi.h> #include "dhd.h" +#include "dhd_proto.h" #include "dhd_dbg.h" +#include "dhd_bus.h" #include "fwil.h" #include "fweh.h" #include "fwsignal.h" @@ -54,7 +59,7 @@ BRCMF_FWS_TLV_DEF(RSSI, 8, 1) \ BRCMF_FWS_TLV_DEF(INTERFACE_OPEN, 9, 1) \ BRCMF_FWS_TLV_DEF(INTERFACE_CLOSE, 10, 1) \ - BRCMF_FWS_TLV_DEF(FIFO_CREDITBACK, 11, 8) \ + BRCMF_FWS_TLV_DEF(FIFO_CREDITBACK, 11, 6) \ BRCMF_FWS_TLV_DEF(PENDING_TRAFFIC_BMP, 12, 2) \ BRCMF_FWS_TLV_DEF(MAC_REQUEST_PACKET, 13, 3) \ BRCMF_FWS_TLV_DEF(HOST_REORDER_RXPKTS, 14, 10) \ @@ -62,7 +67,7 @@ BRCMF_FWS_TLV_DEF(COMP_TXSTATUS, 19, 1) \ BRCMF_FWS_TLV_DEF(FILLER, 255, 0) -/** +/* * enum brcmf_fws_tlv_type - definition of tlv identifiers. */ #define BRCMF_FWS_TLV_DEF(name, id, len) \ @@ -73,18 +78,18 @@ enum brcmf_fws_tlv_type { }; #undef BRCMF_FWS_TLV_DEF -/** - * enum brcmf_fws_tlv_len - length values for tlvs. +/* + * enum brcmf_fws_tlv_len - definition of tlv lengths. */ #define BRCMF_FWS_TLV_DEF(name, id, len) \ - BRCMF_FWS_TYPE_ ## name ## _LEN = len, + BRCMF_FWS_TYPE_ ## name ## _LEN = (len), enum brcmf_fws_tlv_len { BRCMF_FWS_TLV_DEFLIST }; #undef BRCMF_FWS_TLV_DEF #ifdef DEBUG -/** +/* * brcmf_fws_tlv_names - array of tlv names. */ #define BRCMF_FWS_TLV_DEF(name, id, len) \ @@ -114,43 +119,109 @@ static const char *brcmf_fws_get_tlv_name(enum brcmf_fws_tlv_type id) } #endif /* DEBUG */ -/** +/* * flags used to enable tlv signalling from firmware. */ -#define BRCMF_FWS_FLAGS_RSSI_SIGNALS 0x0001 -#define BRCMF_FWS_FLAGS_XONXOFF_SIGNALS 0x0002 -#define BRCMF_FWS_FLAGS_CREDIT_STATUS_SIGNALS 0x0004 -#define BRCMF_FWS_FLAGS_HOST_PROPTXSTATUS_ACTIVE 0x0008 -#define BRCMF_FWS_FLAGS_PSQ_GENERATIONFSM_ENABLE 0x0010 -#define BRCMF_FWS_FLAGS_PSQ_ZERO_BUFFER_ENABLE 0x0020 -#define BRCMF_FWS_FLAGS_HOST_RXREORDER_ACTIVE 0x0040 - -#define BRCMF_FWS_HANGER_MAXITEMS 1024 -#define BRCMF_FWS_HANGER_ITEM_STATE_FREE 1 -#define BRCMF_FWS_HANGER_ITEM_STATE_INUSE 2 -#define BRCMF_FWS_HANGER_ITEM_STATE_INUSE_SUPPRESSED 3 - -#define BRCMF_FWS_STATE_OPEN 1 -#define BRCMF_FWS_STATE_CLOSE 2 - -#define BRCMF_FWS_FCMODE_NONE 0 -#define BRCMF_FWS_FCMODE_IMPLIED_CREDIT 1 -#define BRCMF_FWS_FCMODE_EXPLICIT_CREDIT 2 +#define BRCMF_FWS_FLAGS_RSSI_SIGNALS 0x0001 +#define BRCMF_FWS_FLAGS_XONXOFF_SIGNALS 0x0002 +#define BRCMF_FWS_FLAGS_CREDIT_STATUS_SIGNALS 0x0004 +#define BRCMF_FWS_FLAGS_HOST_PROPTXSTATUS_ACTIVE 0x0008 +#define BRCMF_FWS_FLAGS_PSQ_GENERATIONFSM_ENABLE 0x0010 +#define BRCMF_FWS_FLAGS_PSQ_ZERO_BUFFER_ENABLE 0x0020 +#define BRCMF_FWS_FLAGS_HOST_RXREORDER_ACTIVE 0x0040 #define BRCMF_FWS_MAC_DESC_TABLE_SIZE 32 -#define BRCMF_FWS_MAX_IFNUM 16 #define BRCMF_FWS_MAC_DESC_ID_INVALID 0xff #define BRCMF_FWS_HOSTIF_FLOWSTATE_OFF 0 #define BRCMF_FWS_HOSTIF_FLOWSTATE_ON 1 +#define BRCMF_FWS_FLOWCONTROL_HIWATER 128 +#define BRCMF_FWS_FLOWCONTROL_LOWATER 64 + +#define BRCMF_FWS_PSQ_PREC_COUNT ((NL80211_NUM_ACS + 1) * 2) +#define BRCMF_FWS_PSQ_LEN 256 + +#define BRCMF_FWS_HTOD_FLAG_PKTFROMHOST 0x01 +#define BRCMF_FWS_HTOD_FLAG_PKT_REQUESTED 0x02 /** - * FWFC packet identifier + * enum brcmf_fws_skb_state - indicates processing state of skb. + * + * @BRCMF_FWS_SKBSTATE_NEW: sk_buff is newly arrived in the driver. + * @BRCMF_FWS_SKBSTATE_DELAYED: sk_buff had to wait on queue. + * @BRCMF_FWS_SKBSTATE_SUPPRESSED: sk_buff has been suppressed by firmware. + */ +enum brcmf_fws_skb_state { + BRCMF_FWS_SKBSTATE_NEW, + BRCMF_FWS_SKBSTATE_DELAYED, + BRCMF_FWS_SKBSTATE_SUPPRESSED +}; + +/** + * struct brcmf_skbuff_cb - control buffer associated with skbuff. + * + * @if_flags: holds interface index and packet related flags. + * @htod: host to device packet identifier (used in PKTTAG tlv). + * @state: transmit state of the packet. + * @mac: descriptor related to destination for this packet. + * + * This information is stored in control buffer struct sk_buff::cb, which + * provides 48 bytes of storage so this structure should not exceed that. + */ +struct brcmf_skbuff_cb { + u16 if_flags; + u32 htod; + enum brcmf_fws_skb_state state; + struct brcmf_fws_mac_descriptor *mac; +}; + +/* + * macro casting skbuff control buffer to struct brcmf_skbuff_cb. + */ +#define brcmf_skbcb(skb) ((struct brcmf_skbuff_cb *)((skb)->cb)) + +/* + * sk_buff control if flags + * + * b[11] - packet sent upon firmware request. + * b[10] - packet only contains signalling data. + * b[9] - packet is a tx packet. + * b[8] - packet uses FIFO credit (non-pspoll). + * b[7] - interface in AP mode. + * b[6:4] - AC FIFO number. + * b[3:0] - interface index. + */ +#define BRCMF_SKB_IF_FLAGS_REQUESTED_MASK 0x0800 +#define BRCMF_SKB_IF_FLAGS_REQUESTED_SHIFT 11 +#define BRCMF_SKB_IF_FLAGS_SIGNAL_ONLY_MASK 0x0400 +#define BRCMF_SKB_IF_FLAGS_SIGNAL_ONLY_SHIFT 10 +#define BRCMF_SKB_IF_FLAGS_TRANSMIT_MASK 0x0200 +#define BRCMF_SKB_IF_FLAGS_TRANSMIT_SHIFT 9 +#define BRCMF_SKB_IF_FLAGS_CREDITCHECK_MASK 0x0100 +#define BRCMF_SKB_IF_FLAGS_CREDITCHECK_SHIFT 8 +#define BRCMF_SKB_IF_FLAGS_IF_AP_MASK 0x0080 +#define BRCMF_SKB_IF_FLAGS_IF_AP_SHIFT 7 +#define BRCMF_SKB_IF_FLAGS_FIFO_MASK 0x0070 +#define BRCMF_SKB_IF_FLAGS_FIFO_SHIFT 4 +#define BRCMF_SKB_IF_FLAGS_INDEX_MASK 0x000f +#define BRCMF_SKB_IF_FLAGS_INDEX_SHIFT 0 + +#define brcmf_skb_if_flags_set_field(skb, field, value) \ + brcmu_maskset16(&(brcmf_skbcb(skb)->if_flags), \ + BRCMF_SKB_IF_FLAGS_ ## field ## _MASK, \ + BRCMF_SKB_IF_FLAGS_ ## field ## _SHIFT, (value)) +#define brcmf_skb_if_flags_get_field(skb, field) \ + brcmu_maskget16(brcmf_skbcb(skb)->if_flags, \ + BRCMF_SKB_IF_FLAGS_ ## field ## _MASK, \ + BRCMF_SKB_IF_FLAGS_ ## field ## _SHIFT) + +/* + * sk_buff control packet identifier * * 32-bit packet identifier used in PKTTAG tlv from host to dongle. * * - Generated at the host (e.g. dhd) - * - Seen as a generic sequence number by wlc except the flags field + * - Seen as a generic sequence number by firmware except for the flags field. * * Generation : b[31] => generation number for this packet [host->fw] * OR, current generation number [fw->host] @@ -159,35 +230,1057 @@ static const char *brcmf_fws_get_tlv_name(enum brcmf_fws_tlv_type id) * h-slot : b[23:8] => hanger-slot * freerun : b[7:0] => A free running counter */ -#define BRCMF_FWS_PKTTAG_GENERATION_MASK 0x80000000 -#define BRCMF_FWS_PKTTAG_GENERATION_SHIFT 31 -#define BRCMF_FWS_PKTTAG_FLAGS_MASK 0x78000000 -#define BRCMF_FWS_PKTTAG_FLAGS_SHIFT 27 -#define BRCMF_FWS_PKTTAG_FIFO_MASK 0x07000000 -#define BRCMF_FWS_PKTTAG_FIFO_SHIFT 24 -#define BRCMF_FWS_PKTTAG_HSLOT_MASK 0x00ffff00 -#define BRCMF_FWS_PKTTAG_HSLOT_SHIFT 8 -#define BRCMF_FWS_PKTTAG_FREERUN_MASK 0x000000ff -#define BRCMF_FWS_PKTTAG_FREERUN_SHIFT 0 - -#define brcmf_fws_pkttag_set_field(var, field, value) \ - brcmu_maskset32((var), BRCMF_FWS_PKTTAG_ ## field ## _MASK, \ - BRCMF_FWS_PKTTAG_ ## field ## _SHIFT, (value)) -#define brcmf_fws_pkttag_get_field(var, field) \ - brcmu_maskget32((var), BRCMF_FWS_PKTTAG_ ## field ## _MASK, \ - BRCMF_FWS_PKTTAG_ ## field ## _SHIFT) +#define BRCMF_SKB_HTOD_TAG_GENERATION_MASK 0x80000000 +#define BRCMF_SKB_HTOD_TAG_GENERATION_SHIFT 31 +#define BRCMF_SKB_HTOD_TAG_FLAGS_MASK 0x78000000 +#define BRCMF_SKB_HTOD_TAG_FLAGS_SHIFT 27 +#define BRCMF_SKB_HTOD_TAG_FIFO_MASK 0x07000000 +#define BRCMF_SKB_HTOD_TAG_FIFO_SHIFT 24 +#define BRCMF_SKB_HTOD_TAG_HSLOT_MASK 0x00ffff00 +#define BRCMF_SKB_HTOD_TAG_HSLOT_SHIFT 8 +#define BRCMF_SKB_HTOD_TAG_FREERUN_MASK 0x000000ff +#define BRCMF_SKB_HTOD_TAG_FREERUN_SHIFT 0 + +#define brcmf_skb_htod_tag_set_field(skb, field, value) \ + brcmu_maskset32(&(brcmf_skbcb(skb)->htod), \ + BRCMF_SKB_HTOD_TAG_ ## field ## _MASK, \ + BRCMF_SKB_HTOD_TAG_ ## field ## _SHIFT, (value)) +#define brcmf_skb_htod_tag_get_field(skb, field) \ + brcmu_maskget32(brcmf_skbcb(skb)->htod, \ + BRCMF_SKB_HTOD_TAG_ ## field ## _MASK, \ + BRCMF_SKB_HTOD_TAG_ ## field ## _SHIFT) + +#define BRCMF_FWS_TXSTAT_GENERATION_MASK 0x80000000 +#define BRCMF_FWS_TXSTAT_GENERATION_SHIFT 31 +#define BRCMF_FWS_TXSTAT_FLAGS_MASK 0x78000000 +#define BRCMF_FWS_TXSTAT_FLAGS_SHIFT 27 +#define BRCMF_FWS_TXSTAT_FIFO_MASK 0x07000000 +#define BRCMF_FWS_TXSTAT_FIFO_SHIFT 24 +#define BRCMF_FWS_TXSTAT_HSLOT_MASK 0x00FFFF00 +#define BRCMF_FWS_TXSTAT_HSLOT_SHIFT 8 +#define BRCMF_FWS_TXSTAT_PKTID_MASK 0x00FFFFFF +#define BRCMF_FWS_TXSTAT_PKTID_SHIFT 0 + +#define brcmf_txstatus_get_field(txs, field) \ + brcmu_maskget32(txs, BRCMF_FWS_TXSTAT_ ## field ## _MASK, \ + BRCMF_FWS_TXSTAT_ ## field ## _SHIFT) + +/** + * enum brcmf_fws_fifo - fifo indices used by dongle firmware. + * + * @BRCMF_FWS_FIFO_AC_BK: fifo for background traffic. + * @BRCMF_FWS_FIFO_AC_BE: fifo for best-effort traffic. + * @BRCMF_FWS_FIFO_AC_VI: fifo for video traffic. + * @BRCMF_FWS_FIFO_AC_VO: fifo for voice traffic. + * @BRCMF_FWS_FIFO_BCMC: fifo for broadcast/multicast (AP only). + * @BRCMF_FWS_FIFO_ATIM: fifo for ATIM (AP only). + * @BRCMF_FWS_FIFO_COUNT: number of fifos. + */ +enum brcmf_fws_fifo { + BRCMF_FWS_FIFO_AC_BK, + BRCMF_FWS_FIFO_AC_BE, + BRCMF_FWS_FIFO_AC_VI, + BRCMF_FWS_FIFO_AC_VO, + BRCMF_FWS_FIFO_BCMC, + BRCMF_FWS_FIFO_ATIM, + BRCMF_FWS_FIFO_COUNT +}; + +/** + * enum brcmf_fws_txstatus - txstatus flag values. + * + * @BRCMF_FWS_TXSTATUS_DISCARD: + * host is free to discard the packet. + * @BRCMF_FWS_TXSTATUS_CORE_SUPPRESS: + * 802.11 core suppressed the packet. + * @BRCMF_FWS_TXSTATUS_FW_PS_SUPPRESS: + * firmware suppress the packet as device is already in PS mode. + * @BRCMF_FWS_TXSTATUS_FW_TOSSED: + * firmware tossed the packet. + */ +enum brcmf_fws_txstatus { + BRCMF_FWS_TXSTATUS_DISCARD, + BRCMF_FWS_TXSTATUS_CORE_SUPPRESS, + BRCMF_FWS_TXSTATUS_FW_PS_SUPPRESS, + BRCMF_FWS_TXSTATUS_FW_TOSSED +}; + +enum brcmf_fws_fcmode { + BRCMF_FWS_FCMODE_NONE, + BRCMF_FWS_FCMODE_IMPLIED_CREDIT, + BRCMF_FWS_FCMODE_EXPLICIT_CREDIT +}; + +enum brcmf_fws_mac_desc_state { + BRCMF_FWS_STATE_OPEN = 1, + BRCMF_FWS_STATE_CLOSE +}; + +/** + * struct brcmf_fws_mac_descriptor - firmware signalling data per node/interface + * + * @occupied: slot is in use. + * @mac_handle: handle for mac entry determined by firmware. + * @interface_id: interface index. + * @state: current state. + * @suppressed: mac entry is suppressed. + * @generation: generation bit. + * @ac_bitmap: ac queue bitmap. + * @requested_credit: credits requested by firmware. + * @ea: ethernet address. + * @seq: per-node free-running sequence. + * @psq: power-save queue. + * @transit_count: packet in transit to firmware. + */ +struct brcmf_fws_mac_descriptor { + u8 occupied; + u8 mac_handle; + u8 interface_id; + u8 state; + bool suppressed; + u8 generation; + u8 ac_bitmap; + u8 requested_credit; + u8 requested_packet; + u8 ea[ETH_ALEN]; + u8 seq[BRCMF_FWS_FIFO_COUNT]; + struct pktq psq; + int transit_count; + int suppress_count; + int suppr_transit_count; + bool send_tim_signal; + u8 traffic_pending_bmp; + u8 traffic_lastreported_bmp; +}; + +#define BRCMF_FWS_HANGER_MAXITEMS 1024 + +/** + * enum brcmf_fws_hanger_item_state - state of hanger item. + * + * @BRCMF_FWS_HANGER_ITEM_STATE_FREE: item is free for use. + * @BRCMF_FWS_HANGER_ITEM_STATE_INUSE: item is in use. + * @BRCMF_FWS_HANGER_ITEM_STATE_INUSE_SUPPRESSED: item was suppressed. + */ +enum brcmf_fws_hanger_item_state { + BRCMF_FWS_HANGER_ITEM_STATE_FREE = 1, + BRCMF_FWS_HANGER_ITEM_STATE_INUSE, + BRCMF_FWS_HANGER_ITEM_STATE_INUSE_SUPPRESSED +}; + + +/** + * struct brcmf_fws_hanger_item - single entry for tx pending packet. + * + * @state: entry is either free or occupied. + * @gen: generation. + * @pkt: packet itself. + */ +struct brcmf_fws_hanger_item { + enum brcmf_fws_hanger_item_state state; + u8 gen; + struct sk_buff *pkt; +}; + +/** + * struct brcmf_fws_hanger - holds packets awaiting firmware txstatus. + * + * @pushed: packets pushed to await txstatus. + * @popped: packets popped upon handling txstatus. + * @failed_to_push: packets that could not be pushed. + * @failed_to_pop: packets that could not be popped. + * @failed_slotfind: packets for which failed to find an entry. + * @slot_pos: last returned item index for a free entry. + * @items: array of hanger items. + */ +struct brcmf_fws_hanger { + u32 pushed; + u32 popped; + u32 failed_to_push; + u32 failed_to_pop; + u32 failed_slotfind; + u32 slot_pos; + struct brcmf_fws_hanger_item items[BRCMF_FWS_HANGER_MAXITEMS]; +}; + +struct brcmf_fws_macdesc_table { + struct brcmf_fws_mac_descriptor nodes[BRCMF_FWS_MAC_DESC_TABLE_SIZE]; + struct brcmf_fws_mac_descriptor iface[BRCMF_MAX_IFS]; + struct brcmf_fws_mac_descriptor other; +}; struct brcmf_fws_info { struct brcmf_pub *drvr; struct brcmf_fws_stats stats; + struct brcmf_fws_hanger hanger; + enum brcmf_fws_fcmode fcmode; + struct brcmf_fws_macdesc_table desc; + struct workqueue_struct *fws_wq; + struct work_struct fws_dequeue_work; + u32 fifo_enqpkt[BRCMF_FWS_FIFO_COUNT]; + int fifo_credit[BRCMF_FWS_FIFO_COUNT]; + int deq_node_pos[BRCMF_FWS_FIFO_COUNT]; + u32 fifo_credit_map; + u32 fifo_delay_map; }; +/* + * brcmf_fws_prio2fifo - mapping from 802.1d priority to firmware fifo index. + */ +static const int brcmf_fws_prio2fifo[] = { + BRCMF_FWS_FIFO_AC_BE, + BRCMF_FWS_FIFO_AC_BK, + BRCMF_FWS_FIFO_AC_BK, + BRCMF_FWS_FIFO_AC_BE, + BRCMF_FWS_FIFO_AC_VI, + BRCMF_FWS_FIFO_AC_VI, + BRCMF_FWS_FIFO_AC_VO, + BRCMF_FWS_FIFO_AC_VO +}; + +static int fcmode; +module_param(fcmode, int, S_IRUSR); +MODULE_PARM_DESC(fcmode, "mode of firmware signalled flow control"); + +#define BRCMF_FWS_TLV_DEF(name, id, len) \ + case BRCMF_FWS_TYPE_ ## name: \ + return len; + +/** + * brcmf_fws_get_tlv_len() - returns defined length for given tlv id. + * + * @fws: firmware-signalling information. + * @id: identifier of the TLV. + * + * Return: the specified length for the given TLV; Otherwise -EINVAL. + */ +static int brcmf_fws_get_tlv_len(struct brcmf_fws_info *fws, + enum brcmf_fws_tlv_type id) +{ + switch (id) { + BRCMF_FWS_TLV_DEFLIST + default: + fws->stats.tlv_invalid_type++; + break; + } + return -EINVAL; +} +#undef BRCMF_FWS_TLV_DEF + +static bool brcmf_fws_ifidx_match(struct sk_buff *skb, void *arg) +{ + u32 ifidx = brcmf_skb_if_flags_get_field(skb, INDEX); + return ifidx == *(int *)arg; +} + +static void brcmf_fws_psq_flush(struct brcmf_fws_info *fws, struct pktq *q, + int ifidx) +{ + bool (*matchfn)(struct sk_buff *, void *) = NULL; + struct sk_buff *skb; + int prec; + + if (ifidx != -1) + matchfn = brcmf_fws_ifidx_match; + for (prec = 0; prec < q->num_prec; prec++) { + skb = brcmu_pktq_pdeq_match(q, prec, matchfn, &ifidx); + while (skb) { + brcmu_pkt_buf_free_skb(skb); + skb = brcmu_pktq_pdeq_match(q, prec, matchfn, &ifidx); + } + } +} + +static void brcmf_fws_hanger_init(struct brcmf_fws_hanger *hanger) +{ + int i; + + brcmf_dbg(TRACE, "enter\n"); + memset(hanger, 0, sizeof(*hanger)); + for (i = 0; i < ARRAY_SIZE(hanger->items); i++) + hanger->items[i].state = BRCMF_FWS_HANGER_ITEM_STATE_FREE; +} + +static u32 brcmf_fws_hanger_get_free_slot(struct brcmf_fws_hanger *h) +{ + u32 i; + + brcmf_dbg(TRACE, "enter\n"); + i = (h->slot_pos + 1) % BRCMF_FWS_HANGER_MAXITEMS; + + while (i != h->slot_pos) { + if (h->items[i].state == BRCMF_FWS_HANGER_ITEM_STATE_FREE) { + h->slot_pos = i; + goto done; + } + i++; + if (i == BRCMF_FWS_HANGER_MAXITEMS) + i = 0; + } + brcmf_err("all slots occupied\n"); + h->failed_slotfind++; + i = BRCMF_FWS_HANGER_MAXITEMS; +done: + brcmf_dbg(TRACE, "exit: %d\n", i); + return i; +} + +static int brcmf_fws_hanger_pushpkt(struct brcmf_fws_hanger *h, + struct sk_buff *pkt, u32 slot_id) +{ + brcmf_dbg(TRACE, "enter\n"); + if (slot_id >= BRCMF_FWS_HANGER_MAXITEMS) + return -ENOENT; + + if (h->items[slot_id].state != BRCMF_FWS_HANGER_ITEM_STATE_FREE) { + brcmf_err("slot is not free\n"); + h->failed_to_push++; + return -EINVAL; + } + + h->items[slot_id].state = BRCMF_FWS_HANGER_ITEM_STATE_INUSE; + h->items[slot_id].pkt = pkt; + h->pushed++; + return 0; +} + +static int brcmf_fws_hanger_poppkt(struct brcmf_fws_hanger *h, + u32 slot_id, struct sk_buff **pktout, + bool remove_item) +{ + brcmf_dbg(TRACE, "enter\n"); + if (slot_id >= BRCMF_FWS_HANGER_MAXITEMS) + return -ENOENT; + + if (h->items[slot_id].state == BRCMF_FWS_HANGER_ITEM_STATE_FREE) { + brcmf_err("entry not in use\n"); + h->failed_to_pop++; + return -EINVAL; + } + + *pktout = h->items[slot_id].pkt; + if (remove_item) { + h->items[slot_id].state = BRCMF_FWS_HANGER_ITEM_STATE_FREE; + h->items[slot_id].pkt = NULL; + h->items[slot_id].gen = 0xff; + h->popped++; + } + return 0; +} + +static int brcmf_fws_hanger_mark_suppressed(struct brcmf_fws_hanger *h, + u32 slot_id, u8 gen) +{ + brcmf_dbg(TRACE, "enter\n"); + + if (slot_id >= BRCMF_FWS_HANGER_MAXITEMS) + return -ENOENT; + + h->items[slot_id].gen = gen; + + if (h->items[slot_id].state != BRCMF_FWS_HANGER_ITEM_STATE_INUSE) { + brcmf_err("entry not in use\n"); + return -EINVAL; + } + + h->items[slot_id].state = BRCMF_FWS_HANGER_ITEM_STATE_INUSE_SUPPRESSED; + return 0; +} + +static int brcmf_fws_hanger_get_genbit(struct brcmf_fws_hanger *hanger, + struct sk_buff *pkt, u32 slot_id, + int *gen) +{ + brcmf_dbg(TRACE, "enter\n"); + *gen = 0xff; + + if (slot_id >= BRCMF_FWS_HANGER_MAXITEMS) + return -ENOENT; + + if (hanger->items[slot_id].state == BRCMF_FWS_HANGER_ITEM_STATE_FREE) { + brcmf_err("slot not in use\n"); + return -EINVAL; + } + + *gen = hanger->items[slot_id].gen; + return 0; +} + +static void brcmf_fws_hanger_cleanup(struct brcmf_fws_info *fws, + bool (*fn)(struct sk_buff *, void *), + int ifidx) +{ + struct brcmf_fws_hanger *h = &fws->hanger; + struct sk_buff *skb; + int i; + enum brcmf_fws_hanger_item_state s; + + brcmf_dbg(TRACE, "enter: ifidx=%d\n", ifidx); + for (i = 0; i < ARRAY_SIZE(h->items); i++) { + s = h->items[i].state; + if (s == BRCMF_FWS_HANGER_ITEM_STATE_INUSE || + s == BRCMF_FWS_HANGER_ITEM_STATE_INUSE_SUPPRESSED) { + skb = h->items[i].pkt; + if (fn == NULL || fn(skb, &ifidx)) { + /* suppress packets freed from psq */ + if (s == BRCMF_FWS_HANGER_ITEM_STATE_INUSE) + brcmu_pkt_buf_free_skb(skb); + h->items[i].state = + BRCMF_FWS_HANGER_ITEM_STATE_FREE; + } + } + } +} + +static void brcmf_fws_init_mac_descriptor(struct brcmf_fws_mac_descriptor *desc, + u8 *addr, u8 ifidx) +{ + brcmf_dbg(TRACE, + "enter: desc %p ea=%pM, ifidx=%u\n", desc, addr, ifidx); + desc->occupied = 1; + desc->state = BRCMF_FWS_STATE_OPEN; + desc->requested_credit = 0; + /* depending on use may need ifp->bssidx instead */ + desc->interface_id = ifidx; + desc->ac_bitmap = 0xff; /* update this when handling APSD */ + if (addr) + memcpy(&desc->ea[0], addr, ETH_ALEN); +} + +static +void brcmf_fws_clear_mac_descriptor(struct brcmf_fws_mac_descriptor *desc) +{ + brcmf_dbg(TRACE, + "enter: ea=%pM, ifidx=%u\n", desc->ea, desc->interface_id); + desc->occupied = 0; + desc->state = BRCMF_FWS_STATE_CLOSE; + desc->requested_credit = 0; +} + +static struct brcmf_fws_mac_descriptor * +brcmf_fws_mac_descriptor_lookup(struct brcmf_fws_info *fws, u8 *ea) +{ + struct brcmf_fws_mac_descriptor *entry; + int i; + + brcmf_dbg(TRACE, "enter: ea=%pM\n", ea); + if (ea == NULL) + return ERR_PTR(-EINVAL); + + entry = &fws->desc.nodes[0]; + for (i = 0; i < ARRAY_SIZE(fws->desc.nodes); i++) { + if (entry->occupied && !memcmp(entry->ea, ea, ETH_ALEN)) + return entry; + entry++; + } + + return ERR_PTR(-ENOENT); +} + +static struct brcmf_fws_mac_descriptor* +brcmf_fws_find_mac_desc(struct brcmf_fws_info *fws, int ifidx, u8 *da) +{ + struct brcmf_fws_mac_descriptor *entry = &fws->desc.other; + struct brcmf_if *ifp; + bool multicast; + + brcmf_dbg(TRACE, "enter: ifidx=%d\n", ifidx); + + multicast = is_multicast_ether_addr(da); + ifp = fws->drvr->iflist[ifidx ? ifidx + 1 : 0]; + if (WARN_ON(!ifp)) + goto done; + + /* Multicast destination and P2P clients get the interface entry. + * STA gets the interface entry if there is no exact match. For + * example, TDLS destinations have their own entry. + */ + entry = NULL; + if (multicast && ifp->fws_desc) + entry = ifp->fws_desc; + + if (entry != NULL && multicast) + goto done; + + entry = brcmf_fws_mac_descriptor_lookup(fws, da); + if (IS_ERR(entry)) + entry = &fws->desc.other; + +done: + brcmf_dbg(TRACE, "exit: entry=%p\n", entry); + return entry; +} + +static bool brcmf_fws_mac_desc_ready(struct brcmf_fws_mac_descriptor *entry, + int fifo) +{ + bool ready; + + /* + * destination entry is ready when firmware says it is OPEN + * and there are no packets enqueued for it. + */ + ready = entry->state == BRCMF_FWS_STATE_OPEN && + !entry->suppressed && + brcmu_pktq_mlen(&entry->psq, 3 << (fifo * 2)) == 0; + + /* + * Or when the destination entry is CLOSED, but firmware has + * specifically requested packets for this entry. + */ + ready = ready || (entry->state == BRCMF_FWS_STATE_CLOSE && + (entry->requested_credit + entry->requested_packet)); + return ready; +} + +static void brcmf_fws_mac_desc_cleanup(struct brcmf_fws_info *fws, + struct brcmf_fws_mac_descriptor *entry, + int ifidx) +{ + brcmf_dbg(TRACE, "enter: entry=(ea=%pM, ifid=%d), ifidx=%d\n", + entry->ea, entry->interface_id, ifidx); + if (entry->occupied && (ifidx == -1 || ifidx == entry->interface_id)) { + brcmf_dbg(TRACE, "flush psq: ifidx=%d, qlen=%d\n", + ifidx, entry->psq.len); + brcmf_fws_psq_flush(fws, &entry->psq, ifidx); + entry->occupied = !!(entry->psq.len); + } +} + +static void brcmf_fws_bus_txq_cleanup(struct brcmf_fws_info *fws, + bool (*fn)(struct sk_buff *, void *), + int ifidx) +{ + struct brcmf_fws_hanger_item *hi; + struct pktq *txq; + struct sk_buff *skb; + int prec; + u32 hslot; + + brcmf_dbg(TRACE, "enter: ifidx=%d\n", ifidx); + txq = brcmf_bus_gettxq(fws->drvr->bus_if); + if (IS_ERR(txq)) { + brcmf_dbg(TRACE, "no txq to clean up\n"); + return; + } + + for (prec = 0; prec < txq->num_prec; prec++) { + skb = brcmu_pktq_pdeq_match(txq, prec, fn, &ifidx); + while (skb) { + hslot = brcmf_skb_htod_tag_get_field(skb, HSLOT); + hi = &fws->hanger.items[hslot]; + WARN_ON(skb != hi->pkt); + hi->state = BRCMF_FWS_HANGER_ITEM_STATE_FREE; + brcmu_pkt_buf_free_skb(skb); + skb = brcmu_pktq_pdeq_match(txq, prec, fn, &ifidx); + } + } +} + +static void brcmf_fws_cleanup(struct brcmf_fws_info *fws, int ifidx) +{ + int i; + struct brcmf_fws_mac_descriptor *table; + bool (*matchfn)(struct sk_buff *, void *) = NULL; + + brcmf_dbg(TRACE, "enter: ifidx=%d\n", ifidx); + if (fws == NULL) + return; + + if (ifidx != -1) + matchfn = brcmf_fws_ifidx_match; + + /* cleanup individual nodes */ + table = &fws->desc.nodes[0]; + for (i = 0; i < ARRAY_SIZE(fws->desc.nodes); i++) + brcmf_fws_mac_desc_cleanup(fws, &table[i], ifidx); + + brcmf_fws_mac_desc_cleanup(fws, &fws->desc.other, ifidx); + brcmf_fws_bus_txq_cleanup(fws, matchfn, ifidx); + brcmf_fws_hanger_cleanup(fws, matchfn, ifidx); +} + +static void brcmf_fws_tim_update(struct brcmf_fws_info *ctx, + struct brcmf_fws_mac_descriptor *entry, + int prec) +{ + brcmf_dbg(TRACE, "enter: ea=%pM\n", entry->ea); + if (entry->state == BRCMF_FWS_STATE_CLOSE) { + /* check delayedQ and suppressQ in one call using bitmap */ + if (brcmu_pktq_mlen(&entry->psq, 3 << (prec * 2)) == 0) + entry->traffic_pending_bmp = + entry->traffic_pending_bmp & ~NBITVAL(prec); + else + entry->traffic_pending_bmp = + entry->traffic_pending_bmp | NBITVAL(prec); + } + /* request a TIM update to firmware at the next piggyback opportunity */ + if (entry->traffic_lastreported_bmp != entry->traffic_pending_bmp) + entry->send_tim_signal = true; +} + +static void +brcmf_fws_flow_control_check(struct brcmf_fws_info *fws, struct pktq *pq, + u8 if_id) +{ + struct brcmf_if *ifp = fws->drvr->iflist[if_id]; + + brcmf_dbg(TRACE, + "enter: bssidx=%d, ifidx=%d\n", ifp->bssidx, ifp->ifidx); + if (WARN_ON(!ifp)) + return; + + if ((ifp->netif_stop & BRCMF_NETIF_STOP_REASON_FWS_FC) && + pq->len <= BRCMF_FWS_FLOWCONTROL_LOWATER) + brcmf_txflowblock_if(ifp, + BRCMF_NETIF_STOP_REASON_FWS_FC, false); + if (!(ifp->netif_stop & BRCMF_NETIF_STOP_REASON_FWS_FC) && + pq->len >= BRCMF_FWS_FLOWCONTROL_HIWATER) + brcmf_txflowblock_if(ifp, BRCMF_NETIF_STOP_REASON_FWS_FC, true); + return; +} + static int brcmf_fws_rssi_indicate(struct brcmf_fws_info *fws, s8 rssi) { brcmf_dbg(CTL, "rssi %d\n", rssi); return 0; } +static +int brcmf_fws_macdesc_indicate(struct brcmf_fws_info *fws, u8 type, u8 *data) +{ + struct brcmf_fws_mac_descriptor *entry, *existing; + u8 mac_handle; + u8 ifidx; + u8 *addr; + + mac_handle = *data++; + ifidx = *data++; + addr = data; + + entry = &fws->desc.nodes[mac_handle & 0x1F]; + if (type == BRCMF_FWS_TYPE_MACDESC_DEL) { + brcmf_dbg(TRACE, "deleting mac %pM idx %d\n", addr, ifidx); + if (entry->occupied) + brcmf_fws_clear_mac_descriptor(entry); + else + fws->stats.mac_update_failed++; + return 0; + } + + brcmf_dbg(TRACE, + "add mac %pM handle %u idx %d\n", addr, mac_handle, ifidx); + existing = brcmf_fws_mac_descriptor_lookup(fws, addr); + if (IS_ERR(existing)) { + if (!entry->occupied) { + entry->mac_handle = mac_handle; + brcmf_fws_init_mac_descriptor(entry, addr, ifidx); + brcmu_pktq_init(&entry->psq, BRCMF_FWS_PSQ_PREC_COUNT, + BRCMF_FWS_PSQ_LEN); + } else { + fws->stats.mac_update_failed++; + } + } else { + if (entry != existing) { + brcmf_dbg(TRACE, "relocate mac\n"); + memcpy(entry, existing, + offsetof(struct brcmf_fws_mac_descriptor, psq)); + entry->mac_handle = mac_handle; + brcmf_fws_clear_mac_descriptor(existing); + } else { + brcmf_dbg(TRACE, "use existing\n"); + WARN_ON(entry->mac_handle != mac_handle); + /* TODO: what should we do here: continue, reinit, .. */ + } + } + return 0; +} + +static int brcmf_fws_macdesc_state_indicate(struct brcmf_fws_info *fws, + u8 type, u8 *data) +{ + struct brcmf_fws_mac_descriptor *entry; + u8 mac_handle; + int i; + + mac_handle = data[0]; + entry = &fws->desc.nodes[mac_handle & 0x1F]; + if (!entry->occupied) { + fws->stats.mac_ps_update_failed++; + return -ESRCH; + } + + /* a state update should wipe old credits? */ + entry->requested_credit = 0; + if (type == BRCMF_FWS_TYPE_MAC_OPEN) { + entry->state = BRCMF_FWS_STATE_OPEN; + } else { + entry->state = BRCMF_FWS_STATE_CLOSE; + for (i = BRCMF_FWS_FIFO_AC_BE; i < NL80211_NUM_ACS; i++) + brcmf_fws_tim_update(fws, entry, i); + } + return 0; +} + +static int brcmf_fws_interface_state_indicate(struct brcmf_fws_info *fws, + u8 type, u8 *data) +{ + struct brcmf_fws_mac_descriptor *entry; + u8 ifidx; + int ret; + + ifidx = data[0]; + + brcmf_dbg(TRACE, "enter: ifidx=%d\n", ifidx); + if (ifidx >= BRCMF_MAX_IFS) { + ret = -ERANGE; + goto fail; + } + + entry = &fws->desc.iface[ifidx]; + if (!entry->occupied) { + ret = -ESRCH; + goto fail; + } + + switch (type) { + case BRCMF_FWS_TYPE_INTERFACE_OPEN: + entry->state = BRCMF_FWS_STATE_OPEN; + return 0; + case BRCMF_FWS_TYPE_INTERFACE_CLOSE: + entry->state = BRCMF_FWS_STATE_CLOSE; + return 0; + default: + ret = -EINVAL; + break; + } +fail: + fws->stats.if_update_failed++; + return ret; +} + +static int brcmf_fws_request_indicate(struct brcmf_fws_info *fws, u8 type, + u8 *data) +{ + struct brcmf_fws_mac_descriptor *entry; + + entry = &fws->desc.nodes[data[1] & 0x1F]; + if (!entry->occupied) { + if (type == BRCMF_FWS_TYPE_MAC_REQUEST_CREDIT) + fws->stats.credit_request_failed++; + else + fws->stats.packet_request_failed++; + return -ESRCH; + } + + if (type == BRCMF_FWS_TYPE_MAC_REQUEST_CREDIT) + entry->requested_credit = data[0]; + else + entry->requested_packet = data[0]; + + entry->ac_bitmap = data[2]; + return 0; +} + +static void brcmf_fws_return_credits(struct brcmf_fws_info *fws, + u8 fifo, u8 credits) +{ + if (!credits) + return; + + fws->fifo_credit_map |= 1 << fifo; + fws->fifo_credit[fifo] += credits; +} + +static void brcmf_fws_schedule_deq(struct brcmf_fws_info *fws) +{ + /* only schedule dequeue when there are credits for delayed traffic */ + if (fws->fifo_credit_map & fws->fifo_delay_map) + queue_work(fws->fws_wq, &fws->fws_dequeue_work); +} + +static void brcmf_skb_pick_up_credit(struct brcmf_fws_info *fws, int fifo, + struct sk_buff *p) +{ + struct brcmf_fws_mac_descriptor *entry = brcmf_skbcb(p)->mac; + + if (brcmf_skbcb(p)->if_flags & BRCMF_SKB_IF_FLAGS_CREDITCHECK_MASK) { + if (fws->fcmode != BRCMF_FWS_FCMODE_IMPLIED_CREDIT) + return; + brcmf_fws_return_credits(fws, fifo, 1); + } else { + /* + * if this packet did not count against FIFO credit, it + * must have taken a requested_credit from the destination + * entry (for pspoll etc.) + */ + if (!brcmf_skb_if_flags_get_field(p, REQUESTED)) + entry->requested_credit++; + } + brcmf_fws_schedule_deq(fws); +} + +static int brcmf_fws_enq(struct brcmf_fws_info *fws, + enum brcmf_fws_skb_state state, int fifo, + struct sk_buff *p) +{ + int prec = 2 * fifo; + u32 *qfull_stat = &fws->stats.delayq_full_error; + + struct brcmf_fws_mac_descriptor *entry; + + entry = brcmf_skbcb(p)->mac; + if (entry == NULL) { + brcmf_err("no mac descriptor found for skb %p\n", p); + return -ENOENT; + } + + brcmf_dbg(TRACE, "enter: ea=%pM, qlen=%d\n", entry->ea, entry->psq.len); + if (state == BRCMF_FWS_SKBSTATE_SUPPRESSED) { + prec += 1; + qfull_stat = &fws->stats.supprq_full_error; + } + + if (brcmu_pktq_penq(&entry->psq, prec, p) == NULL) { + *qfull_stat += 1; + return -ENFILE; + } + + /* increment total enqueued packet count */ + fws->fifo_delay_map |= 1 << fifo; + fws->fifo_enqpkt[fifo]++; + + /* update the sk_buff state */ + brcmf_skbcb(p)->state = state; + if (state == BRCMF_FWS_SKBSTATE_SUPPRESSED) + entry->suppress_count++; + + /* + * A packet has been pushed so update traffic + * availability bitmap, if applicable + */ + brcmf_fws_tim_update(fws, entry, fifo); + brcmf_fws_flow_control_check(fws, &entry->psq, + brcmf_skb_if_flags_get_field(p, INDEX)); + return 0; +} + +static struct sk_buff *brcmf_fws_deq(struct brcmf_fws_info *fws, int fifo) +{ + struct brcmf_fws_mac_descriptor *table; + struct brcmf_fws_mac_descriptor *entry; + struct sk_buff *p; + int use_credit = 1; + int num_nodes; + int node_pos; + int prec_out; + int pmsk = 3; + int i; + + table = (struct brcmf_fws_mac_descriptor *)&fws->desc; + num_nodes = sizeof(fws->desc) / sizeof(struct brcmf_fws_mac_descriptor); + node_pos = fws->deq_node_pos[fifo]; + + for (i = 0; i < num_nodes; i++) { + entry = &table[(node_pos + i) % num_nodes]; + if (!entry->occupied) + continue; + + if (entry->suppressed) + pmsk = 2; + p = brcmu_pktq_mdeq(&entry->psq, pmsk << (fifo * 2), &prec_out); + if (p == NULL) { + if (entry->suppressed) { + if (entry->suppr_transit_count > + entry->suppress_count) + return NULL; + entry->suppressed = false; + p = brcmu_pktq_mdeq(&entry->psq, + 1 << (fifo * 2), &prec_out); + } + } + if (p == NULL) + continue; + + /* did the packet come from suppress sub-queue? */ + if (entry->requested_credit > 0) { + entry->requested_credit--; + /* + * if the packet was pulled out while destination is in + * closed state but had a non-zero packets requested, + * then this should not count against the FIFO credit. + * That is due to the fact that the firmware will + * most likely hold onto this packet until a suitable + * time later to push it to the appropriate AC FIFO. + */ + if (entry->state == BRCMF_FWS_STATE_CLOSE) + use_credit = 0; + } else if (entry->requested_packet > 0) { + entry->requested_packet--; + brcmf_skb_if_flags_set_field(p, REQUESTED, 1); + if (entry->state == BRCMF_FWS_STATE_CLOSE) + use_credit = 0; + } + brcmf_skb_if_flags_set_field(p, CREDITCHECK, use_credit); + + /* move dequeue position to ensure fair round-robin */ + fws->deq_node_pos[fifo] = (node_pos + i + 1) % num_nodes; + brcmf_fws_flow_control_check(fws, &entry->psq, + brcmf_skb_if_flags_get_field(p, + INDEX) + ); + /* + * A packet has been picked up, update traffic + * availability bitmap, if applicable + */ + brcmf_fws_tim_update(fws, entry, fifo); + + /* + * decrement total enqueued fifo packets and + * clear delay bitmap if done. + */ + fws->fifo_enqpkt[fifo]--; + if (fws->fifo_enqpkt[fifo] == 0) + fws->fifo_delay_map &= ~(1 << fifo); + goto done; + } + p = NULL; +done: + brcmf_dbg(TRACE, "exit: fifo %d skb %p\n", fifo, p); + return p; +} + +static int brcmf_fws_txstatus_suppressed(struct brcmf_fws_info *fws, int fifo, + struct sk_buff *skb, u32 genbit) +{ + struct brcmf_fws_mac_descriptor *entry = brcmf_skbcb(skb)->mac; + u32 hslot; + int ret; + + hslot = brcmf_skb_htod_tag_get_field(skb, HSLOT); + + /* this packet was suppressed */ + if (!entry->suppressed || entry->generation != genbit) { + entry->suppressed = true; + entry->suppress_count = brcmu_pktq_mlen(&entry->psq, + 1 << (fifo * 2 + 1)); + entry->suppr_transit_count = entry->transit_count; + } + + entry->generation = genbit; + + ret = brcmf_fws_enq(fws, BRCMF_FWS_SKBSTATE_SUPPRESSED, fifo, skb); + if (ret != 0) { + /* suppress q is full, drop this packet */ + brcmf_fws_hanger_poppkt(&fws->hanger, hslot, &skb, + true); + } else { + /* + * Mark suppressed to avoid a double free during + * wlfc cleanup + */ + brcmf_fws_hanger_mark_suppressed(&fws->hanger, hslot, + genbit); + entry->suppress_count++; + } + + return ret; +} + +static int +brcmf_fws_txstatus_process(struct brcmf_fws_info *fws, u8 flags, u32 hslot, + u32 genbit) +{ + u32 fifo; + int ret; + bool remove_from_hanger = true; + struct sk_buff *skb; + struct brcmf_fws_mac_descriptor *entry = NULL; + + brcmf_dbg(TRACE, "status: flags=0x%X, hslot=%d\n", + flags, hslot); + + if (flags == BRCMF_FWS_TXSTATUS_DISCARD) + fws->stats.txs_discard++; + else if (flags == BRCMF_FWS_TXSTATUS_CORE_SUPPRESS) { + fws->stats.txs_supp_core++; + remove_from_hanger = false; + } else if (flags == BRCMF_FWS_TXSTATUS_FW_PS_SUPPRESS) { + fws->stats.txs_supp_ps++; + remove_from_hanger = false; + } else if (flags == BRCMF_FWS_TXSTATUS_FW_TOSSED) + fws->stats.txs_tossed++; + else + brcmf_err("unexpected txstatus\n"); + + ret = brcmf_fws_hanger_poppkt(&fws->hanger, hslot, &skb, + remove_from_hanger); + if (ret != 0) { + brcmf_err("no packet in hanger slot: hslot=%d\n", hslot); + return ret; + } + + entry = brcmf_skbcb(skb)->mac; + if (WARN_ON(!entry)) { + brcmu_pkt_buf_free_skb(skb); + return -EINVAL; + } + + /* pick up the implicit credit from this packet */ + fifo = brcmf_skb_htod_tag_get_field(skb, FIFO); + brcmf_skb_pick_up_credit(fws, fifo, skb); + + if (!remove_from_hanger) + ret = brcmf_fws_txstatus_suppressed(fws, fifo, skb, genbit); + + if (remove_from_hanger || ret) { + entry->transit_count--; + if (entry->suppressed) + entry->suppr_transit_count--; + + brcmf_txfinalize(fws->drvr, skb, true); + } + return 0; +} + +static int brcmf_fws_fifocreditback_indicate(struct brcmf_fws_info *fws, + u8 *data) +{ + int i; + + if (fws->fcmode != BRCMF_FWS_FCMODE_EXPLICIT_CREDIT) { + brcmf_dbg(INFO, "ignored\n"); + return 0; + } + + brcmf_dbg(TRACE, "enter: data %pM\n", data); + for (i = 0; i < BRCMF_FWS_FIFO_COUNT; i++) + brcmf_fws_return_credits(fws, i, data[i]); + + brcmf_dbg(INFO, "map: credit %x delay %x\n", fws->fifo_credit_map, + fws->fifo_delay_map); + brcmf_fws_schedule_deq(fws); + return 0; +} + +static int brcmf_fws_txstatus_indicate(struct brcmf_fws_info *fws, u8 *data) +{ + __le32 status_le; + u32 status; + u32 hslot; + u32 genbit; + u8 flags; + + fws->stats.txs_indicate++; + memcpy(&status_le, data, sizeof(status_le)); + status = le32_to_cpu(status_le); + flags = brcmf_txstatus_get_field(status, FLAGS); + hslot = brcmf_txstatus_get_field(status, HSLOT); + genbit = brcmf_txstatus_get_field(status, GENERATION); + + return brcmf_fws_txstatus_process(fws, flags, hslot, genbit); +} + static int brcmf_fws_dbg_seqnum_check(struct brcmf_fws_info *fws, u8 *data) { __le32 timestamp; @@ -213,51 +1306,32 @@ do { \ #define brcmf_fws_unlock(drvr, flags) \ spin_unlock_irqrestore(&((drvr)->fws_spinlock), (flags)) -int brcmf_fws_init(struct brcmf_pub *drvr) +static int brcmf_fws_notify_credit_map(struct brcmf_if *ifp, + const struct brcmf_event_msg *e, + void *data) { - u32 tlv; - int rc; - - /* enable rssi signals */ - tlv = drvr->fw_signals ? BRCMF_FWS_FLAGS_RSSI_SIGNALS : 0; - - spin_lock_init(&drvr->fws_spinlock); + struct brcmf_fws_info *fws = ifp->drvr->fws; + int i; + ulong flags; + u8 *credits = data; - drvr->fws = kzalloc(sizeof(*(drvr->fws)), GFP_KERNEL); - if (!drvr->fws) { - rc = -ENOMEM; - goto fail; + if (e->datalen < BRCMF_FWS_FIFO_COUNT) { + brcmf_err("event payload too small (%d)\n", e->datalen); + return -EINVAL; } - /* enable proptxtstatus signaling by default */ - rc = brcmf_fil_iovar_int_set(drvr->iflist[0], "tlv", tlv); - if (rc < 0) { - brcmf_err("failed to set bdcv2 tlv signaling\n"); - goto fail; + brcmf_dbg(TRACE, "enter: credits %pM\n", credits); + brcmf_fws_lock(ifp->drvr, flags); + for (i = 0; i < ARRAY_SIZE(fws->fifo_credit); i++) { + if (*credits) + fws->fifo_credit_map |= 1 << i; + else + fws->fifo_credit_map &= ~(1 << i); + fws->fifo_credit[i] = *credits++; } - /* set linkage back */ - drvr->fws->drvr = drvr; - - /* create debugfs file for statistics */ - brcmf_debugfs_create_fws_stats(drvr, &drvr->fws->stats); - - /* TODO: remove upon feature delivery */ - brcmf_err("%s bdcv2 tlv signaling [%x]\n", - drvr->fw_signals ? "enabled" : "disabled", tlv); + brcmf_fws_schedule_deq(fws); + brcmf_fws_unlock(ifp->drvr, flags); return 0; - -fail: - /* disable flow control entirely */ - drvr->fw_signals = false; - brcmf_fws_deinit(drvr); - return rc; -} - -void brcmf_fws_deinit(struct brcmf_pub *drvr) -{ - /* free top structure */ - kfree(drvr->fws); - drvr->fws = NULL; } int brcmf_fws_hdrpull(struct brcmf_pub *drvr, int ifidx, s16 signal_len, @@ -304,57 +1378,50 @@ int brcmf_fws_hdrpull(struct brcmf_pub *drvr, int ifidx, s16 signal_len, len = signal_data[1]; data = signal_data + 2; + brcmf_dbg(INFO, "tlv type=%d (%s), len=%d, data[0]=%d\n", type, + brcmf_fws_get_tlv_name(type), len, *data); + /* abort parsing when length invalid */ if (data_len < len + 2) break; - brcmf_dbg(INFO, "tlv type=%d (%s), len=%d\n", type, - brcmf_fws_get_tlv_name(type), len); - switch (type) { - case BRCMF_FWS_TYPE_MAC_OPEN: - case BRCMF_FWS_TYPE_MAC_CLOSE: - WARN_ON(len != BRCMF_FWS_TYPE_MAC_OPEN_LEN); - break; - case BRCMF_FWS_TYPE_MAC_REQUEST_CREDIT: - WARN_ON(len != BRCMF_FWS_TYPE_MAC_REQUEST_CREDIT_LEN); - break; - case BRCMF_FWS_TYPE_TXSTATUS: - WARN_ON(len != BRCMF_FWS_TYPE_TXSTATUS_LEN); + if (len != brcmf_fws_get_tlv_len(fws, type)) break; - case BRCMF_FWS_TYPE_PKTTAG: - WARN_ON(len != BRCMF_FWS_TYPE_PKTTAG_LEN); + + switch (type) { + case BRCMF_FWS_TYPE_HOST_REORDER_RXPKTS: + case BRCMF_FWS_TYPE_COMP_TXSTATUS: break; case BRCMF_FWS_TYPE_MACDESC_ADD: case BRCMF_FWS_TYPE_MACDESC_DEL: - WARN_ON(len != BRCMF_FWS_TYPE_MACDESC_ADD_LEN); + brcmf_fws_macdesc_indicate(fws, type, data); break; - case BRCMF_FWS_TYPE_RSSI: - WARN_ON(len != BRCMF_FWS_TYPE_RSSI_LEN); - brcmf_fws_rssi_indicate(fws, *(s8 *)data); + case BRCMF_FWS_TYPE_MAC_OPEN: + case BRCMF_FWS_TYPE_MAC_CLOSE: + brcmf_fws_macdesc_state_indicate(fws, type, data); break; case BRCMF_FWS_TYPE_INTERFACE_OPEN: case BRCMF_FWS_TYPE_INTERFACE_CLOSE: - WARN_ON(len != BRCMF_FWS_TYPE_INTERFACE_OPEN_LEN); + brcmf_fws_interface_state_indicate(fws, type, data); break; - case BRCMF_FWS_TYPE_FIFO_CREDITBACK: - WARN_ON(len != BRCMF_FWS_TYPE_FIFO_CREDITBACK_LEN); + case BRCMF_FWS_TYPE_MAC_REQUEST_CREDIT: + case BRCMF_FWS_TYPE_MAC_REQUEST_PACKET: + brcmf_fws_request_indicate(fws, type, data); break; - case BRCMF_FWS_TYPE_PENDING_TRAFFIC_BMP: - WARN_ON(len != BRCMF_FWS_TYPE_PENDING_TRAFFIC_BMP_LEN); + case BRCMF_FWS_TYPE_TXSTATUS: + brcmf_fws_txstatus_indicate(fws, data); break; - case BRCMF_FWS_TYPE_MAC_REQUEST_PACKET: - WARN_ON(len != BRCMF_FWS_TYPE_MAC_REQUEST_PACKET_LEN); + case BRCMF_FWS_TYPE_FIFO_CREDITBACK: + brcmf_fws_fifocreditback_indicate(fws, data); break; - case BRCMF_FWS_TYPE_HOST_REORDER_RXPKTS: - WARN_ON(len != BRCMF_FWS_TYPE_HOST_REORDER_RXPKTS_LEN); + case BRCMF_FWS_TYPE_RSSI: + brcmf_fws_rssi_indicate(fws, *data); break; case BRCMF_FWS_TYPE_TRANS_ID: - WARN_ON(len != BRCMF_FWS_TYPE_TRANS_ID_LEN); brcmf_fws_dbg_seqnum_check(fws, data); break; - case BRCMF_FWS_TYPE_COMP_TXSTATUS: - WARN_ON(len != BRCMF_FWS_TYPE_COMP_TXSTATUS_LEN); - break; + case BRCMF_FWS_TYPE_PKTTAG: + case BRCMF_FWS_TYPE_PENDING_TRAFFIC_BMP: default: fws->stats.tlv_invalid_type++; break; @@ -380,3 +1447,505 @@ int brcmf_fws_hdrpull(struct brcmf_pub *drvr, int ifidx, s16 signal_len, brcmf_fws_unlock(drvr, flags); return 0; } + +static int brcmf_fws_hdrpush(struct brcmf_fws_info *fws, struct sk_buff *skb) +{ + struct brcmf_fws_mac_descriptor *entry = brcmf_skbcb(skb)->mac; + u8 *wlh; + u16 data_offset = 0; + u8 fillers; + __le32 pkttag = cpu_to_le32(brcmf_skbcb(skb)->htod); + + brcmf_dbg(TRACE, "enter: ea=%pM, ifidx=%u, pkttag=0x%08X\n", + entry->ea, entry->interface_id, le32_to_cpu(pkttag)); + if (entry->send_tim_signal) + data_offset += 2 + BRCMF_FWS_TYPE_PENDING_TRAFFIC_BMP_LEN; + + /* +2 is for Type[1] and Len[1] in TLV, plus TIM signal */ + data_offset += 2 + BRCMF_FWS_TYPE_PKTTAG_LEN; + fillers = round_up(data_offset, 4) - data_offset; + data_offset += fillers; + + skb_push(skb, data_offset); + wlh = skb->data; + + wlh[0] = BRCMF_FWS_TYPE_PKTTAG; + wlh[1] = BRCMF_FWS_TYPE_PKTTAG_LEN; + memcpy(&wlh[2], &pkttag, sizeof(pkttag)); + wlh += BRCMF_FWS_TYPE_PKTTAG_LEN + 2; + + if (entry->send_tim_signal) { + entry->send_tim_signal = 0; + wlh[0] = BRCMF_FWS_TYPE_PENDING_TRAFFIC_BMP; + wlh[1] = BRCMF_FWS_TYPE_PENDING_TRAFFIC_BMP_LEN; + wlh[2] = entry->mac_handle; + wlh[3] = entry->traffic_pending_bmp; + wlh += BRCMF_FWS_TYPE_PENDING_TRAFFIC_BMP_LEN + 2; + entry->traffic_lastreported_bmp = entry->traffic_pending_bmp; + } + if (fillers) + memset(wlh, BRCMF_FWS_TYPE_FILLER, fillers); + + brcmf_proto_hdrpush(fws->drvr, brcmf_skb_if_flags_get_field(skb, INDEX), + data_offset >> 2, skb); + return 0; +} + +static int brcmf_fws_precommit_skb(struct brcmf_fws_info *fws, int fifo, + struct sk_buff *p) +{ + struct brcmf_skbuff_cb *skcb = brcmf_skbcb(p); + struct brcmf_fws_mac_descriptor *entry = skcb->mac; + int rc = 0; + bool header_needed; + int hslot = BRCMF_FWS_HANGER_MAXITEMS; + u8 free_ctr; + u8 ifidx; + u8 flags; + + header_needed = skcb->state != BRCMF_FWS_SKBSTATE_SUPPRESSED; + + if (header_needed) { + /* obtaining free slot may fail, but that will be caught + * by the hanger push. This assures the packet has a BDC + * header upon return. + */ + hslot = brcmf_fws_hanger_get_free_slot(&fws->hanger); + free_ctr = entry->seq[fifo]; + brcmf_skb_htod_tag_set_field(p, HSLOT, hslot); + brcmf_skb_htod_tag_set_field(p, FREERUN, free_ctr); + brcmf_skb_htod_tag_set_field(p, GENERATION, 1); + entry->transit_count++; + } + brcmf_skb_if_flags_set_field(p, TRANSMIT, 1); + brcmf_skb_htod_tag_set_field(p, FIFO, fifo); + + flags = BRCMF_FWS_HTOD_FLAG_PKTFROMHOST; + if (!(skcb->if_flags & BRCMF_SKB_IF_FLAGS_CREDITCHECK_MASK)) { + /* + Indicate that this packet is being sent in response to an + explicit request from the firmware side. + */ + flags |= BRCMF_FWS_HTOD_FLAG_PKT_REQUESTED; + } + brcmf_skb_htod_tag_set_field(p, FLAGS, flags); + if (header_needed) { + brcmf_fws_hdrpush(fws, p); + rc = brcmf_fws_hanger_pushpkt(&fws->hanger, p, hslot); + if (rc) + brcmf_err("hanger push failed: rc=%d\n", rc); + } else { + int gen; + + /* remove old header */ + rc = brcmf_proto_hdrpull(fws->drvr, false, &ifidx, p); + if (rc == 0) { + hslot = brcmf_skb_htod_tag_get_field(p, HSLOT); + brcmf_fws_hanger_get_genbit(&fws->hanger, p, + hslot, &gen); + brcmf_skb_htod_tag_set_field(p, GENERATION, gen); + + /* push new header */ + brcmf_fws_hdrpush(fws, p); + } + } + + return rc; +} + +static int +brcmf_fws_rollback_toq(struct brcmf_fws_info *fws, struct sk_buff *skb) +{ + /* + put the packet back to the head of queue + + - suppressed packet goes back to suppress sub-queue + - pull out the header, if new or delayed packet + + Note: hslot is used only when header removal is done. + */ + struct brcmf_fws_mac_descriptor *entry; + enum brcmf_fws_skb_state state; + struct sk_buff *pktout; + int rc = 0; + int fifo; + int hslot; + u8 ifidx; + + fifo = brcmf_skb_if_flags_get_field(skb, FIFO); + state = brcmf_skbcb(skb)->state; + entry = brcmf_skbcb(skb)->mac; + + if (entry != NULL) { + if (state == BRCMF_FWS_SKBSTATE_SUPPRESSED) { + /* wl-header is saved for suppressed packets */ + pktout = brcmu_pktq_penq_head(&entry->psq, 2 * fifo + 1, + skb); + if (pktout == NULL) { + brcmf_err("suppress queue full\n"); + rc = -ENOSPC; + } + } else { + hslot = brcmf_skb_htod_tag_get_field(skb, HSLOT); + + /* remove header first */ + rc = brcmf_proto_hdrpull(fws->drvr, false, &ifidx, skb); + if (rc) { + brcmf_err("header removal failed\n"); + /* free the hanger slot */ + brcmf_fws_hanger_poppkt(&fws->hanger, hslot, + &pktout, true); + brcmf_txfinalize(fws->drvr, skb, false); + rc = -EINVAL; + goto fail; + } + + /* delay-q packets are going to delay-q */ + pktout = brcmu_pktq_penq_head(&entry->psq, + 2 * fifo, skb); + if (pktout == NULL) { + brcmf_err("delay queue full\n"); + rc = -ENOSPC; + } + + /* free the hanger slot */ + brcmf_fws_hanger_poppkt(&fws->hanger, hslot, &pktout, + true); + + /* decrement sequence count */ + entry->seq[fifo]--; + } + /* + if this packet did not count against FIFO credit, it must have + taken a requested_credit from the firmware (for pspoll etc.) + */ + if (!(brcmf_skbcb(skb)->if_flags & + BRCMF_SKB_IF_FLAGS_CREDITCHECK_MASK)) + entry->requested_credit++; + } else { + brcmf_err("no mac entry linked\n"); + rc = -ENOENT; + } + + +fail: + if (rc) + fws->stats.rollback_failed++; + else + fws->stats.rollback_success++; + return rc; +} + +static int brcmf_fws_consume_credit(struct brcmf_fws_info *fws, int fifo, + struct sk_buff *skb) +{ + struct brcmf_fws_mac_descriptor *entry = brcmf_skbcb(skb)->mac; + int *credit = &fws->fifo_credit[fifo]; + int use_credit = 1; + + brcmf_dbg(TRACE, "enter: ac=%d, credits=%d\n", fifo, *credit); + + if (entry->requested_credit > 0) { + /* + * if the packet was pulled out while destination is in + * closed state but had a non-zero packets requested, + * then this should not count against the FIFO credit. + * That is due to the fact that the firmware will + * most likely hold onto this packet until a suitable + * time later to push it to the appropriate AC FIFO. + */ + entry->requested_credit--; + if (entry->state == BRCMF_FWS_STATE_CLOSE) + use_credit = 0; + } else if (entry->requested_packet > 0) { + entry->requested_packet--; + brcmf_skb_if_flags_set_field(skb, REQUESTED, 1); + if (entry->state == BRCMF_FWS_STATE_CLOSE) + use_credit = 0; + } + brcmf_skb_if_flags_set_field(skb, CREDITCHECK, use_credit); + if (!use_credit) { + brcmf_dbg(TRACE, "exit: no creditcheck set\n"); + return 0; + } + + if (!(*credit)) { + brcmf_dbg(TRACE, "exit: credits depleted\n"); + return -ENAVAIL; + } + (*credit)--; + if (!(*credit)) + fws->fifo_credit_map &= ~(1 << fifo); + brcmf_dbg(TRACE, "exit: ac=%d, credits=%d\n", fifo, *credit); + return 0; +} + +static int brcmf_fws_commit_skb(struct brcmf_fws_info *fws, int fifo, + struct sk_buff *skb) +{ + struct brcmf_skbuff_cb *skcb = brcmf_skbcb(skb); + struct brcmf_fws_mac_descriptor *entry; + struct brcmf_bus *bus = fws->drvr->bus_if; + int rc; + + entry = skcb->mac; + if (IS_ERR(entry)) + return PTR_ERR(entry); + + rc = brcmf_fws_precommit_skb(fws, fifo, skb); + if (rc < 0) { + fws->stats.generic_error++; + goto rollback; + } + + rc = brcmf_bus_txdata(bus, skb); + if (rc < 0) + goto rollback; + + entry->seq[fifo]++; + fws->stats.pkt2bus++; + if (brcmf_skbcb(skb)->if_flags & BRCMF_SKB_IF_FLAGS_CREDITCHECK_MASK) { + fws->stats.send_pkts[fifo]++; + fws->stats.fifo_credits_sent[fifo]++; + } + + return rc; + +rollback: + rc = brcmf_fws_rollback_toq(fws, skb); + return rc; +} + +int brcmf_fws_process_skb(struct brcmf_if *ifp, struct sk_buff *skb) +{ + struct brcmf_pub *drvr = ifp->drvr; + struct brcmf_skbuff_cb *skcb = brcmf_skbcb(skb); + struct ethhdr *eh = (struct ethhdr *)(skb->data); + ulong flags; + u8 ifidx = ifp->ifidx; + int fifo = BRCMF_FWS_FIFO_BCMC; + bool multicast = is_multicast_ether_addr(eh->h_dest); + + /* determine the priority */ + if (!skb->priority) + skb->priority = cfg80211_classify8021d(skb); + + drvr->tx_multicast += !!multicast; + if (ntohs(eh->h_proto) == ETH_P_PAE) + atomic_inc(&ifp->pend_8021x_cnt); + + if (!brcmf_fws_fc_active(drvr->fws)) { + /* If the protocol uses a data header, apply it */ + brcmf_proto_hdrpush(drvr, ifidx, 0, skb); + + /* Use bus module to send data frame */ + return brcmf_bus_txdata(drvr->bus_if, skb); + } + + /* set control buffer information */ + skcb->if_flags = 0; + skcb->mac = brcmf_fws_find_mac_desc(drvr->fws, ifidx, eh->h_dest); + skcb->state = BRCMF_FWS_SKBSTATE_NEW; + brcmf_skb_if_flags_set_field(skb, INDEX, ifidx); + if (!multicast) + fifo = brcmf_fws_prio2fifo[skb->priority]; + brcmf_skb_if_flags_set_field(skb, FIFO, fifo); + + brcmf_dbg(TRACE, "ea=%pM, multi=%d, fifo=%d\n", eh->h_dest, + multicast, fifo); + + brcmf_fws_lock(drvr, flags); + if (!brcmf_fws_mac_desc_ready(skcb->mac, fifo) || + (!multicast && + brcmf_fws_consume_credit(drvr->fws, fifo, skb) < 0)) { + /* enqueue the packet in delayQ */ + drvr->fws->fifo_delay_map |= 1 << fifo; + brcmf_fws_enq(drvr->fws, BRCMF_FWS_SKBSTATE_DELAYED, fifo, skb); + } else { + brcmf_fws_commit_skb(drvr->fws, fifo, skb); + } + brcmf_fws_unlock(drvr, flags); + return 0; +} + +void brcmf_fws_reset_interface(struct brcmf_if *ifp) +{ + struct brcmf_fws_mac_descriptor *entry = ifp->fws_desc; + + brcmf_dbg(TRACE, "enter: idx=%d\n", ifp->bssidx); + if (!entry) + return; + + brcmf_fws_init_mac_descriptor(entry, ifp->mac_addr, ifp->ifidx); +} + +void brcmf_fws_add_interface(struct brcmf_if *ifp) +{ + struct brcmf_fws_info *fws = ifp->drvr->fws; + struct brcmf_fws_mac_descriptor *entry; + + brcmf_dbg(TRACE, "enter: idx=%d, mac=%pM\n", + ifp->bssidx, ifp->mac_addr); + if (!ifp->ndev || !ifp->drvr->fw_signals) + return; + + entry = &fws->desc.iface[ifp->ifidx]; + ifp->fws_desc = entry; + brcmf_fws_init_mac_descriptor(entry, ifp->mac_addr, ifp->ifidx); + brcmu_pktq_init(&entry->psq, BRCMF_FWS_PSQ_PREC_COUNT, + BRCMF_FWS_PSQ_LEN); +} + +void brcmf_fws_del_interface(struct brcmf_if *ifp) +{ + struct brcmf_fws_mac_descriptor *entry = ifp->fws_desc; + + brcmf_dbg(TRACE, "enter: idx=%d\n", ifp->bssidx); + if (!entry) + return; + + ifp->fws_desc = NULL; + brcmf_fws_clear_mac_descriptor(entry); + brcmf_fws_cleanup(ifp->drvr->fws, ifp->ifidx); +} + +static void brcmf_fws_dequeue_worker(struct work_struct *worker) +{ + struct brcmf_fws_info *fws; + struct sk_buff *skb; + ulong flags; + int fifo; + int credit; + + fws = container_of(worker, struct brcmf_fws_info, fws_dequeue_work); + + brcmf_dbg(TRACE, "enter: fws=%p\n", fws); + brcmf_fws_lock(fws->drvr, flags); + for (fifo = NL80211_NUM_ACS; fifo >= 0; fifo--) { + brcmf_dbg(TRACE, "fifo %d credit %d\n", fifo, + fws->fifo_credit[fifo]); + for (credit = 0; credit < fws->fifo_credit[fifo]; /* nop */) { + skb = brcmf_fws_deq(fws, fifo); + if (!skb) + break; + if (!brcmf_fws_commit_skb(fws, fifo, skb) && + brcmf_skbcb(skb)->if_flags & + BRCMF_SKB_IF_FLAGS_CREDITCHECK_MASK) + credit++; + } + fws->fifo_credit[fifo] -= credit; + } + brcmf_fws_unlock(fws->drvr, flags); +} + +int brcmf_fws_init(struct brcmf_pub *drvr) +{ + u32 tlv = BRCMF_FWS_FLAGS_RSSI_SIGNALS; + int rc; + + if (!drvr->fw_signals) + return 0; + + spin_lock_init(&drvr->fws_spinlock); + + drvr->fws = kzalloc(sizeof(*(drvr->fws)), GFP_KERNEL); + if (!drvr->fws) { + rc = -ENOMEM; + goto fail; + } + + /* set linkage back */ + drvr->fws->drvr = drvr; + drvr->fws->fcmode = fcmode; + + drvr->fws->fws_wq = create_singlethread_workqueue("brcmf_fws_wq"); + if (drvr->fws->fws_wq == NULL) { + brcmf_err("workqueue creation failed\n"); + rc = -EBADF; + goto fail; + } + INIT_WORK(&drvr->fws->fws_dequeue_work, brcmf_fws_dequeue_worker); + + /* enable firmware signalling if fcmode active */ + if (drvr->fws->fcmode != BRCMF_FWS_FCMODE_NONE) + tlv |= BRCMF_FWS_FLAGS_XONXOFF_SIGNALS | + BRCMF_FWS_FLAGS_CREDIT_STATUS_SIGNALS | + BRCMF_FWS_FLAGS_HOST_PROPTXSTATUS_ACTIVE; + + rc = brcmf_fil_iovar_int_set(drvr->iflist[0], "tlv", tlv); + if (rc < 0) { + brcmf_err("failed to set bdcv2 tlv signaling\n"); + goto fail; + } + + if (brcmf_fweh_register(drvr, BRCMF_E_FIFO_CREDIT_MAP, + brcmf_fws_notify_credit_map)) { + brcmf_err("register credit map handler failed\n"); + goto fail; + } + + brcmf_fws_hanger_init(&drvr->fws->hanger); + brcmf_fws_init_mac_descriptor(&drvr->fws->desc.other, NULL, 0); + brcmu_pktq_init(&drvr->fws->desc.other.psq, BRCMF_FWS_PSQ_PREC_COUNT, + BRCMF_FWS_PSQ_LEN); + + /* create debugfs file for statistics */ + brcmf_debugfs_create_fws_stats(drvr, &drvr->fws->stats); + + /* TODO: remove upon feature delivery */ + brcmf_err("%s bdcv2 tlv signaling [%x]\n", + drvr->fw_signals ? "enabled" : "disabled", tlv); + return 0; + +fail: + /* disable flow control entirely */ + drvr->fw_signals = false; + brcmf_fws_deinit(drvr); + return rc; +} + +void brcmf_fws_deinit(struct brcmf_pub *drvr) +{ + struct brcmf_fws_info *fws = drvr->fws; + ulong flags; + + if (!fws) + return; + + /* cleanup */ + brcmf_fws_lock(drvr, flags); + brcmf_fws_cleanup(fws, -1); + drvr->fws = NULL; + brcmf_fws_unlock(drvr, flags); + + /* free top structure */ + kfree(fws); +} + +bool brcmf_fws_fc_active(struct brcmf_fws_info *fws) +{ + if (!fws) + return false; + + brcmf_dbg(TRACE, "enter: mode=%d\n", fws->fcmode); + return fws->fcmode != BRCMF_FWS_FCMODE_NONE; +} + +void brcmf_fws_bustxfail(struct brcmf_fws_info *fws, struct sk_buff *skb) +{ + ulong flags; + + brcmf_fws_lock(fws->drvr, flags); + brcmf_fws_txstatus_process(fws, BRCMF_FWS_TXSTATUS_FW_TOSSED, + brcmf_skb_htod_tag_get_field(skb, HSLOT), 0); + /* the packet never reached firmware so reclaim credit */ + if (fws->fcmode == BRCMF_FWS_FCMODE_EXPLICIT_CREDIT && + brcmf_skbcb(skb)->if_flags & BRCMF_SKB_IF_FLAGS_CREDITCHECK_MASK) { + brcmf_fws_return_credits(fws, + brcmf_skb_htod_tag_get_field(skb, + FIFO), + 1); + brcmf_fws_schedule_deq(fws); + } + brcmf_fws_unlock(fws->drvr, flags); +} diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.h b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.h index e728eea72bb..fbe483d2375 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.h @@ -20,6 +20,14 @@ int brcmf_fws_init(struct brcmf_pub *drvr); void brcmf_fws_deinit(struct brcmf_pub *drvr); +bool brcmf_fws_fc_active(struct brcmf_fws_info *fws); int brcmf_fws_hdrpull(struct brcmf_pub *drvr, int ifidx, s16 signal_len, struct sk_buff *skb); +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); + #endif /* FWSIGNAL_H_ */ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/p2p.c b/drivers/net/wireless/brcm80211/brcmfmac/p2p.c index 4166e642068..94ff045df2b 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/p2p.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/p2p.c @@ -15,6 +15,7 @@ */ #include <linux/slab.h> #include <linux/netdevice.h> +#include <linux/etherdevice.h> #include <net/cfg80211.h> #include <brcmu_wifi.h> @@ -455,7 +456,9 @@ static int brcmf_p2p_set_firmware(struct brcmf_if *ifp, u8 *p2p_mac) { s32 ret = 0; + brcmf_fil_cmd_int_set(ifp, BRCMF_C_DOWN, 1); brcmf_fil_iovar_int_set(ifp, "apsta", 1); + brcmf_fil_cmd_int_set(ifp, BRCMF_C_UP, 1); /* In case of COB type, firmware has default mac address * After Initializing firmware, we have to set current mac address to @@ -473,28 +476,35 @@ static int brcmf_p2p_set_firmware(struct brcmf_if *ifp, u8 *p2p_mac) * brcmf_p2p_generate_bss_mac() - derive mac addresses for P2P. * * @p2p: P2P specific data. + * @dev_addr: optional device address. * - * P2P needs mac addresses for P2P device and interface. These are - * derived from the primary net device, ie. the permanent ethernet - * address of the device. + * P2P needs mac addresses for P2P device and interface. If no device + * address it specified, these are derived from the primary net device, ie. + * the permanent ethernet address of the device. */ -static void brcmf_p2p_generate_bss_mac(struct brcmf_p2p_info *p2p) +static void brcmf_p2p_generate_bss_mac(struct brcmf_p2p_info *p2p, u8 *dev_addr) { struct brcmf_if *pri_ifp = p2p->bss_idx[P2PAPI_BSSCFG_PRIMARY].vif->ifp; - struct brcmf_if *p2p_ifp = p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif->ifp; + bool local_admin = false; + + if (!dev_addr || is_zero_ether_addr(dev_addr)) { + dev_addr = pri_ifp->mac_addr; + local_admin = true; + } /* Generate the P2P Device Address. This consists of the device's * primary MAC address with the locally administered bit set. */ - memcpy(p2p->dev_addr, pri_ifp->mac_addr, ETH_ALEN); - p2p->dev_addr[0] |= 0x02; - memcpy(p2p_ifp->mac_addr, p2p->dev_addr, ETH_ALEN); + memcpy(p2p->dev_addr, dev_addr, ETH_ALEN); + if (local_admin) + p2p->dev_addr[0] |= 0x02; /* Generate the P2P Interface Address. If the discovery and connection * BSSCFGs need to simultaneously co-exist, then this address must be * different from the P2P Device Address, but also locally administered. */ memcpy(p2p->int_addr, p2p->dev_addr, ETH_ALEN); + p2p->int_addr[0] |= 0x02; p2p->int_addr[4] ^= 0x80; } @@ -773,7 +783,7 @@ exit: * validates the channels in the request. */ static s32 brcmf_p2p_run_escan(struct brcmf_cfg80211_info *cfg, - struct net_device *ndev, + struct brcmf_if *ifp, struct cfg80211_scan_request *request, u16 action) { @@ -1261,7 +1271,7 @@ static void brcmf_p2p_stop_wait_next_action_frame(struct brcmf_cfg80211_info *cfg) { struct brcmf_p2p_info *p2p = &cfg->p2p; - struct net_device *ndev = cfg->escan_info.ndev; + struct brcmf_if *ifp = cfg->escan_info.ifp; if (test_bit(BRCMF_P2P_STATUS_SENDING_ACT_FRAME, &p2p->status) && (test_bit(BRCMF_P2P_STATUS_ACTION_TX_COMPLETED, &p2p->status) || @@ -1271,12 +1281,12 @@ brcmf_p2p_stop_wait_next_action_frame(struct brcmf_cfg80211_info *cfg) * So abort scan for off channel completion. */ if (p2p->af_sent_channel) - brcmf_notify_escan_complete(cfg, ndev, true, true); + brcmf_notify_escan_complete(cfg, ifp, true, true); } else if (test_bit(BRCMF_P2P_STATUS_WAITING_NEXT_AF_LISTEN, &p2p->status)) { brcmf_dbg(TRACE, "*** Wake UP ** abort listen for next af frame\n"); /* So abort scan to cancel listen */ - brcmf_notify_escan_complete(cfg, ndev, true, true); + brcmf_notify_escan_complete(cfg, ifp, true, true); } } @@ -1384,7 +1394,7 @@ int brcmf_p2p_notify_action_frame_rx(struct brcmf_if *ifp, /* After complete GO Negotiation, roll back to mpc mode */ if ((action == P2P_PAF_GON_CONF) || (action == P2P_PAF_PROVDIS_RSP)) - brcmf_set_mpc(ifp->ndev, 1); + brcmf_set_mpc(ifp, 1); if (action == P2P_PAF_GON_CONF) { brcmf_dbg(TRACE, "P2P: GO_NEG_PHASE status cleared\n"); clear_bit(BRCMF_P2P_STATUS_GO_NEG_PHASE, &p2p->status); @@ -1421,7 +1431,8 @@ int brcmf_p2p_notify_action_frame_rx(struct brcmf_if *ifp, CHSPEC_IS2G(chanspec) ? IEEE80211_BAND_2GHZ : IEEE80211_BAND_5GHZ); - wdev = ifp->ndev->ieee80211_ptr; + + wdev = &ifp->vif->wdev; cfg80211_rx_mgmt(wdev, freq, 0, (u8 *)mgmt_frame, mgmt_frame_len, GFP_ATOMIC); @@ -1637,6 +1648,7 @@ bool brcmf_p2p_send_action_frame(struct brcmf_cfg80211_info *cfg, struct brcmf_fil_af_params_le *af_params) { struct brcmf_p2p_info *p2p = &cfg->p2p; + struct brcmf_if *ifp = netdev_priv(ndev); struct brcmf_fil_action_frame_le *action_frame; struct brcmf_config_af_params config_af_params; struct afx_hdl *afx_hdl = &p2p->afx_hdl; @@ -1725,7 +1737,7 @@ bool brcmf_p2p_send_action_frame(struct brcmf_cfg80211_info *cfg, /* To make sure to send successfully action frame, turn off mpc */ if (config_af_params.mpc_onoff == 0) - brcmf_set_mpc(ndev, 0); + brcmf_set_mpc(ifp, 0); /* set status and destination address before sending af */ if (p2p->next_af_subtype != P2P_PAF_SUBTYPE_INVALID) { @@ -1753,7 +1765,7 @@ bool brcmf_p2p_send_action_frame(struct brcmf_cfg80211_info *cfg, * care of current piggback algo, lets abort the scan here * itself. */ - brcmf_notify_escan_complete(cfg, ndev, true, true); + brcmf_notify_escan_complete(cfg, ifp, true, true); /* update channel */ af_params->channel = cpu_to_le32(afx_hdl->peer_chan); @@ -1820,7 +1832,7 @@ exit: clear_bit(BRCMF_P2P_STATUS_WAITING_NEXT_ACT_FRAME, &p2p->status); /* if all done, turn mpc on again */ if (config_af_params.mpc_onoff == 1) - brcmf_set_mpc(ndev, 1); + brcmf_set_mpc(ifp, 1); return ack; } @@ -1839,7 +1851,6 @@ s32 brcmf_p2p_notify_rx_mgmt_p2p_probereq(struct brcmf_if *ifp, struct brcmf_cfg80211_info *cfg = ifp->drvr->config; struct brcmf_p2p_info *p2p = &cfg->p2p; struct afx_hdl *afx_hdl = &p2p->afx_hdl; - struct wireless_dev *wdev; struct brcmf_cfg80211_vif *vif = ifp->vif; struct brcmf_rx_mgmt_data *rxframe = (struct brcmf_rx_mgmt_data *)data; u16 chanspec = be16_to_cpu(rxframe->chanspec); @@ -1882,8 +1893,9 @@ s32 brcmf_p2p_notify_rx_mgmt_p2p_probereq(struct brcmf_if *ifp, CHSPEC_IS2G(chanspec) ? IEEE80211_BAND_2GHZ : IEEE80211_BAND_5GHZ); - wdev = ifp->ndev->ieee80211_ptr; - cfg80211_rx_mgmt(wdev, freq, 0, mgmt_frame, mgmt_frame_len, GFP_ATOMIC); + + cfg80211_rx_mgmt(&vif->wdev, freq, 0, mgmt_frame, mgmt_frame_len, + GFP_ATOMIC); brcmf_dbg(INFO, "mgmt_frame_len (%d) , e->datalen (%d), chanspec (%04x), freq (%d)\n", mgmt_frame_len, e->datalen, chanspec, freq); @@ -1934,7 +1946,8 @@ s32 brcmf_p2p_attach(struct brcmf_cfg80211_info *cfg) p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif = p2p_vif; - brcmf_p2p_generate_bss_mac(p2p); + brcmf_p2p_generate_bss_mac(p2p, NULL); + memcpy(p2p_ifp->mac_addr, p2p->dev_addr, ETH_ALEN); brcmf_p2p_set_firmware(pri_ifp, p2p->dev_addr); /* Initialize P2P Discovery in the firmware */ @@ -2040,13 +2053,13 @@ int brcmf_p2p_ifchange(struct brcmf_cfg80211_info *cfg, brcmf_err("vif for P2PAPI_BSSCFG_PRIMARY does not exist\n"); return -EPERM; } - brcmf_notify_escan_complete(cfg, vif->ifp->ndev, true, true); + brcmf_notify_escan_complete(cfg, vif->ifp, true, true); vif = p2p->bss_idx[P2PAPI_BSSCFG_CONNECTION].vif; if (!vif) { brcmf_err("vif for P2PAPI_BSSCFG_CONNECTION does not exist\n"); return -EPERM; } - brcmf_set_mpc(vif->ifp->ndev, 0); + brcmf_set_mpc(vif->ifp, 0); /* In concurrency case, STA may be already associated in a particular */ /* channel. so retrieve the current channel of primary interface and */ @@ -2124,13 +2137,105 @@ static int brcmf_p2p_release_p2p_if(struct brcmf_cfg80211_vif *vif) } /** + * brcmf_p2p_create_p2pdev() - create a P2P_DEVICE virtual interface. + * + * @p2p: P2P specific data. + * @wiphy: wiphy device of new interface. + * @addr: mac address for this new interface. + */ +static struct wireless_dev *brcmf_p2p_create_p2pdev(struct brcmf_p2p_info *p2p, + struct wiphy *wiphy, + u8 *addr) +{ + struct brcmf_cfg80211_vif *p2p_vif; + struct brcmf_if *p2p_ifp; + struct brcmf_if *pri_ifp; + int err; + u32 bssidx; + + if (p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif) + return ERR_PTR(-ENOSPC); + + p2p_vif = brcmf_alloc_vif(p2p->cfg, NL80211_IFTYPE_P2P_DEVICE, + false); + if (IS_ERR(p2p_vif)) { + brcmf_err("could not create discovery vif\n"); + return (struct wireless_dev *)p2p_vif; + } + + pri_ifp = p2p->bss_idx[P2PAPI_BSSCFG_PRIMARY].vif->ifp; + brcmf_p2p_generate_bss_mac(p2p, addr); + brcmf_p2p_set_firmware(pri_ifp, p2p->dev_addr); + + brcmf_cfg80211_arm_vif_event(p2p->cfg, p2p_vif); + + /* Initialize P2P Discovery in the firmware */ + err = brcmf_fil_iovar_int_set(pri_ifp, "p2p_disc", 1); + if (err < 0) { + brcmf_err("set p2p_disc error\n"); + brcmf_cfg80211_arm_vif_event(p2p->cfg, NULL); + goto fail; + } + + /* wait for firmware event */ + err = brcmf_cfg80211_wait_vif_event_timeout(p2p->cfg, BRCMF_E_IF_ADD, + msecs_to_jiffies(1500)); + brcmf_cfg80211_arm_vif_event(p2p->cfg, NULL); + if (!err) { + brcmf_err("timeout occurred\n"); + err = -EIO; + goto fail; + } + + /* discovery interface created */ + p2p_ifp = p2p_vif->ifp; + p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif = p2p_vif; + memcpy(p2p_ifp->mac_addr, p2p->dev_addr, ETH_ALEN); + memcpy(&p2p_vif->wdev.address, p2p->dev_addr, sizeof(p2p->dev_addr)); + + /* verify bsscfg index for P2P discovery */ + err = brcmf_fil_iovar_int_get(pri_ifp, "p2p_dev", &bssidx); + if (err < 0) { + brcmf_err("retrieving discover bsscfg index failed\n"); + goto fail; + } + + WARN_ON(p2p_ifp->bssidx != bssidx); + + init_completion(&p2p->send_af_done); + INIT_WORK(&p2p->afx_hdl.afx_work, brcmf_p2p_afx_handler); + init_completion(&p2p->afx_hdl.act_frm_scan); + init_completion(&p2p->wait_next_af); + + return &p2p_vif->wdev; + +fail: + brcmf_free_vif(p2p_vif); + return ERR_PTR(err); +} + +/** + * brcmf_p2p_delete_p2pdev() - delete P2P_DEVICE virtual interface. + * + * @vif: virtual interface object to delete. + */ +static void brcmf_p2p_delete_p2pdev(struct brcmf_cfg80211_vif *vif) +{ + struct brcmf_p2p_info *p2p = &vif->ifp->drvr->config->p2p; + + cfg80211_unregister_wdev(&vif->wdev); + p2p->bss_idx[P2PAPI_BSSCFG_DEVICE].vif = NULL; + brcmf_free_vif(vif); +} + +/** * brcmf_p2p_add_vif() - create a new P2P virtual interface. * * @wiphy: wiphy device of new interface. * @name: name of the new interface. * @type: nl80211 interface type. - * @flags: TBD - * @params: TBD + * @flags: not used. + * @params: contains mac address for P2P device. */ struct wireless_dev *brcmf_p2p_add_vif(struct wiphy *wiphy, const char *name, enum nl80211_iftype type, u32 *flags, @@ -2157,6 +2262,9 @@ struct wireless_dev *brcmf_p2p_add_vif(struct wiphy *wiphy, const char *name, iftype = BRCMF_FIL_P2P_IF_GO; mode = WL_MODE_AP; break; + case NL80211_IFTYPE_P2P_DEVICE: + return brcmf_p2p_create_p2pdev(&cfg->p2p, wiphy, + params->macaddr); default: return ERR_PTR(-EOPNOTSUPP); } @@ -2244,6 +2352,8 @@ int brcmf_p2p_del_vif(struct wiphy *wiphy, struct wireless_dev *wdev) break; case NL80211_IFTYPE_P2P_DEVICE: + brcmf_p2p_delete_p2pdev(vif); + return 0; default: return -ENOTSUPP; break; @@ -2275,3 +2385,33 @@ int brcmf_p2p_del_vif(struct wiphy *wiphy, struct wireless_dev *wdev) return err; } + +int brcmf_p2p_start_device(struct wiphy *wiphy, struct wireless_dev *wdev) +{ + struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); + struct brcmf_p2p_info *p2p = &cfg->p2p; + struct brcmf_cfg80211_vif *vif; + int err; + + vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev); + mutex_lock(&cfg->usr_sync); + err = brcmf_p2p_enable_discovery(p2p); + if (!err) + set_bit(BRCMF_VIF_STATUS_READY, &vif->sme_state); + mutex_unlock(&cfg->usr_sync); + return err; +} + +void brcmf_p2p_stop_device(struct wiphy *wiphy, struct wireless_dev *wdev) +{ + struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); + struct brcmf_p2p_info *p2p = &cfg->p2p; + struct brcmf_cfg80211_vif *vif; + + vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev); + mutex_lock(&cfg->usr_sync); + (void)brcmf_p2p_deinit_discovery(p2p); + brcmf_abort_scanning(cfg); + clear_bit(BRCMF_VIF_STATUS_READY, &vif->sme_state); + mutex_unlock(&cfg->usr_sync); +} diff --git a/drivers/net/wireless/brcm80211/brcmfmac/tracepoint.h b/drivers/net/wireless/brcm80211/brcmfmac/tracepoint.h index 35efc7a6764..9df1f7a681e 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/tracepoint.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/tracepoint.h @@ -73,6 +73,20 @@ TRACE_EVENT(brcmf_dbg, TP_printk("%s: %s", __get_str(func), __get_str(msg)) ); +TRACE_EVENT(brcmf_hexdump, + TP_PROTO(void *data, size_t len), + TP_ARGS(data, len), + TP_STRUCT__entry( + __field(unsigned long, len) + __dynamic_array(u8, hdata, len) + ), + TP_fast_assign( + __entry->len = len; + memcpy(__get_dynamic_array(hdata), data, len); + ), + TP_printk("hexdump [length=%lu]", __entry->len) +); + #ifdef CONFIG_BRCM_TRACING #undef TRACE_INCLUDE_PATH diff --git a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c index 804473fc5c5..62699203869 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c @@ -26,6 +26,7 @@ #include <brcmu_wifi.h> #include "dhd.h" #include "dhd_dbg.h" +#include "tracepoint.h" #include "fwil_types.h" #include "p2p.h" #include "wl_cfg80211.h" @@ -182,64 +183,6 @@ static struct ieee80211_channel __wl_5ghz_a_channels[] = { CHAN5G(216, 0), }; -static struct ieee80211_channel __wl_5ghz_n_channels[] = { - CHAN5G(32, 0), CHAN5G(34, 0), - CHAN5G(36, 0), CHAN5G(38, 0), - CHAN5G(40, 0), CHAN5G(42, 0), - CHAN5G(44, 0), CHAN5G(46, 0), - CHAN5G(48, 0), CHAN5G(50, 0), - CHAN5G(52, 0), CHAN5G(54, 0), - CHAN5G(56, 0), CHAN5G(58, 0), - CHAN5G(60, 0), CHAN5G(62, 0), - CHAN5G(64, 0), CHAN5G(66, 0), - CHAN5G(68, 0), CHAN5G(70, 0), - CHAN5G(72, 0), CHAN5G(74, 0), - CHAN5G(76, 0), CHAN5G(78, 0), - CHAN5G(80, 0), CHAN5G(82, 0), - CHAN5G(84, 0), CHAN5G(86, 0), - CHAN5G(88, 0), CHAN5G(90, 0), - CHAN5G(92, 0), CHAN5G(94, 0), - CHAN5G(96, 0), CHAN5G(98, 0), - CHAN5G(100, 0), CHAN5G(102, 0), - CHAN5G(104, 0), CHAN5G(106, 0), - CHAN5G(108, 0), CHAN5G(110, 0), - CHAN5G(112, 0), CHAN5G(114, 0), - CHAN5G(116, 0), CHAN5G(118, 0), - CHAN5G(120, 0), CHAN5G(122, 0), - CHAN5G(124, 0), CHAN5G(126, 0), - CHAN5G(128, 0), CHAN5G(130, 0), - CHAN5G(132, 0), CHAN5G(134, 0), - CHAN5G(136, 0), CHAN5G(138, 0), - CHAN5G(140, 0), CHAN5G(142, 0), - CHAN5G(144, 0), CHAN5G(145, 0), - CHAN5G(146, 0), CHAN5G(147, 0), - CHAN5G(148, 0), CHAN5G(149, 0), - CHAN5G(150, 0), CHAN5G(151, 0), - CHAN5G(152, 0), CHAN5G(153, 0), - CHAN5G(154, 0), CHAN5G(155, 0), - CHAN5G(156, 0), CHAN5G(157, 0), - CHAN5G(158, 0), CHAN5G(159, 0), - CHAN5G(160, 0), CHAN5G(161, 0), - CHAN5G(162, 0), CHAN5G(163, 0), - CHAN5G(164, 0), CHAN5G(165, 0), - CHAN5G(166, 0), CHAN5G(168, 0), - CHAN5G(170, 0), CHAN5G(172, 0), - CHAN5G(174, 0), CHAN5G(176, 0), - CHAN5G(178, 0), CHAN5G(180, 0), - CHAN5G(182, 0), CHAN5G(184, 0), - CHAN5G(186, 0), CHAN5G(188, 0), - CHAN5G(190, 0), CHAN5G(192, 0), - CHAN5G(194, 0), CHAN5G(196, 0), - CHAN5G(198, 0), CHAN5G(200, 0), - CHAN5G(202, 0), CHAN5G(204, 0), - CHAN5G(206, 0), CHAN5G(208, 0), - CHAN5G(210, 0), CHAN5G(212, 0), - CHAN5G(214, 0), CHAN5G(216, 0), - CHAN5G(218, 0), CHAN5G(220, 0), - CHAN5G(222, 0), CHAN5G(224, 0), - CHAN5G(226, 0), CHAN5G(228, 0), -}; - static struct ieee80211_supported_band __wl_band_2ghz = { .band = IEEE80211_BAND_2GHZ, .channels = __wl_2ghz_channels, @@ -256,12 +199,28 @@ static struct ieee80211_supported_band __wl_band_5ghz_a = { .n_bitrates = wl_a_rates_size, }; -static struct ieee80211_supported_band __wl_band_5ghz_n = { - .band = IEEE80211_BAND_5GHZ, - .channels = __wl_5ghz_n_channels, - .n_channels = ARRAY_SIZE(__wl_5ghz_n_channels), - .bitrates = wl_a_rates, - .n_bitrates = wl_a_rates_size, +/* This is to override regulatory domains defined in cfg80211 module (reg.c) + * By default world regulatory domain defined in reg.c puts the flags + * NL80211_RRF_PASSIVE_SCAN and NL80211_RRF_NO_IBSS for 5GHz channels (for + * 36..48 and 149..165). With respect to these flags, wpa_supplicant doesn't + * start p2p operations on 5GHz channels. All the changes in world regulatory + * domain are to be done here. + */ +static const struct ieee80211_regdomain brcmf_regdom = { + .n_reg_rules = 4, + .alpha2 = "99", + .reg_rules = { + /* IEEE 802.11b/g, channels 1..11 */ + REG_RULE(2412-10, 2472+10, 40, 6, 20, 0), + /* If any */ + /* IEEE 802.11 channel 14 - Only JP enables + * this and for 802.11b only + */ + REG_RULE(2484-10, 2484+10, 20, 6, 20, 0), + /* IEEE 802.11a, channel 36..64 */ + REG_RULE(5150-10, 5350+10, 40, 6, 20, 0), + /* IEEE 802.11a, channel 100..165 */ + REG_RULE(5470-10, 5850+10, 40, 6, 20, 0), } }; static const u32 __wl_cipher_suites[] = { @@ -523,17 +482,16 @@ static struct wireless_dev *brcmf_cfg80211_add_iface(struct wiphy *wiphy, return ERR_PTR(-EOPNOTSUPP); case NL80211_IFTYPE_P2P_CLIENT: case NL80211_IFTYPE_P2P_GO: + case NL80211_IFTYPE_P2P_DEVICE: return brcmf_p2p_add_vif(wiphy, name, type, flags, params); case NL80211_IFTYPE_UNSPECIFIED: - case NL80211_IFTYPE_P2P_DEVICE: default: return ERR_PTR(-EINVAL); } } -void brcmf_set_mpc(struct net_device *ndev, int mpc) +void brcmf_set_mpc(struct brcmf_if *ifp, int mpc) { - struct brcmf_if *ifp = netdev_priv(ndev); s32 err = 0; if (check_vif_up(ifp->vif)) { @@ -546,10 +504,9 @@ void brcmf_set_mpc(struct net_device *ndev, int mpc) } } -s32 -brcmf_notify_escan_complete(struct brcmf_cfg80211_info *cfg, - struct net_device *ndev, - bool aborted, bool fw_abort) +s32 brcmf_notify_escan_complete(struct brcmf_cfg80211_info *cfg, + struct brcmf_if *ifp, bool aborted, + bool fw_abort) { struct brcmf_scan_params_le params_le; struct cfg80211_scan_request *scan_request; @@ -580,7 +537,7 @@ brcmf_notify_escan_complete(struct brcmf_cfg80211_info *cfg, /* Scan is aborted by setting channel_list[0] to -1 */ params_le.channel_list[0] = cpu_to_le16(-1); /* E-Scan (or anyother type) can be aborted by SCAN */ - err = brcmf_fil_cmd_data_set(netdev_priv(ndev), BRCMF_C_SCAN, + err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SCAN, ¶ms_le, sizeof(params_le)); if (err) brcmf_err("Scan abort failed\n"); @@ -594,12 +551,12 @@ brcmf_notify_escan_complete(struct brcmf_cfg80211_info *cfg, cfg->sched_escan = false; if (!aborted) cfg80211_sched_scan_results(cfg_to_wiphy(cfg)); - brcmf_set_mpc(ndev, 1); + brcmf_set_mpc(ifp, 1); } else if (scan_request) { brcmf_dbg(SCAN, "ESCAN Completed scan: %s\n", aborted ? "Aborted" : "Done"); cfg80211_scan_done(scan_request, aborted); - brcmf_set_mpc(ndev, 1); + brcmf_set_mpc(ifp, 1); } if (!test_and_clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) brcmf_dbg(SCAN, "Scan complete, probably P2P scan\n"); @@ -619,9 +576,9 @@ int brcmf_cfg80211_del_iface(struct wiphy *wiphy, struct wireless_dev *wdev) if (ndev) { if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status) && - cfg->escan_info.ndev == ndev) - brcmf_notify_escan_complete(cfg, ndev, true, - true); + cfg->escan_info.ifp == netdev_priv(ndev)) + brcmf_notify_escan_complete(cfg, netdev_priv(ndev), + true, true); brcmf_fil_iovar_int_set(netdev_priv(ndev), "mpc", 1); } @@ -637,9 +594,9 @@ int brcmf_cfg80211_del_iface(struct wiphy *wiphy, struct wireless_dev *wdev) return -EOPNOTSUPP; case NL80211_IFTYPE_P2P_CLIENT: case NL80211_IFTYPE_P2P_GO: + case NL80211_IFTYPE_P2P_DEVICE: return brcmf_p2p_del_vif(wiphy, wdev); case NL80211_IFTYPE_UNSPECIFIED: - case NL80211_IFTYPE_P2P_DEVICE: default: return -EINVAL; } @@ -803,7 +760,7 @@ static void brcmf_escan_prep(struct brcmf_scan_params_le *params_le, } static s32 -brcmf_run_escan(struct brcmf_cfg80211_info *cfg, struct net_device *ndev, +brcmf_run_escan(struct brcmf_cfg80211_info *cfg, struct brcmf_if *ifp, struct cfg80211_scan_request *request, u16 action) { s32 params_size = BRCMF_SCAN_PARAMS_FIXED_SIZE + @@ -832,8 +789,7 @@ brcmf_run_escan(struct brcmf_cfg80211_info *cfg, struct net_device *ndev, params->action = cpu_to_le16(action); params->sync_id = cpu_to_le16(0x1234); - err = brcmf_fil_iovar_data_set(netdev_priv(ndev), "escan", - params, params_size); + err = brcmf_fil_iovar_data_set(ifp, "escan", params, params_size); if (err) { if (err == -EBUSY) brcmf_dbg(INFO, "system busy : escan canceled\n"); @@ -848,7 +804,7 @@ exit: static s32 brcmf_do_escan(struct brcmf_cfg80211_info *cfg, struct wiphy *wiphy, - struct net_device *ndev, struct cfg80211_scan_request *request) + struct brcmf_if *ifp, struct cfg80211_scan_request *request) { s32 err; u32 passive_scan; @@ -856,35 +812,35 @@ brcmf_do_escan(struct brcmf_cfg80211_info *cfg, struct wiphy *wiphy, struct escan_info *escan = &cfg->escan_info; brcmf_dbg(SCAN, "Enter\n"); - escan->ndev = ndev; + escan->ifp = ifp; escan->wiphy = wiphy; escan->escan_state = WL_ESCAN_STATE_SCANNING; passive_scan = cfg->active_scan ? 0 : 1; - err = brcmf_fil_cmd_int_set(netdev_priv(ndev), BRCMF_C_SET_PASSIVE_SCAN, + err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PASSIVE_SCAN, passive_scan); if (err) { brcmf_err("error (%d)\n", err); return err; } - brcmf_set_mpc(ndev, 0); + brcmf_set_mpc(ifp, 0); results = (struct brcmf_scan_results *)cfg->escan_info.escan_buf; results->version = 0; results->count = 0; results->buflen = WL_ESCAN_RESULTS_FIXED_SIZE; - err = escan->run(cfg, ndev, request, WL_ESCAN_ACTION_START); + err = escan->run(cfg, ifp, request, WL_ESCAN_ACTION_START); if (err) - brcmf_set_mpc(ndev, 1); + brcmf_set_mpc(ifp, 1); return err; } static s32 -brcmf_cfg80211_escan(struct wiphy *wiphy, struct net_device *ndev, +brcmf_cfg80211_escan(struct wiphy *wiphy, struct brcmf_cfg80211_vif *vif, struct cfg80211_scan_request *request, struct cfg80211_ssid *this_ssid) { - struct brcmf_if *ifp = netdev_priv(ndev); - struct brcmf_cfg80211_info *cfg = ndev_to_cfg(ndev); + struct brcmf_if *ifp = vif->ifp; + struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); struct cfg80211_ssid *ssids; struct brcmf_cfg80211_scan_req *sr = &cfg->scan_req_int; u32 passive_scan; @@ -910,10 +866,8 @@ brcmf_cfg80211_escan(struct wiphy *wiphy, struct net_device *ndev, } /* If scan req comes for p2p0, send it over primary I/F */ - if (ifp->vif == cfg->p2p.bss_idx[P2PAPI_BSSCFG_DEVICE].vif) { - ifp = cfg->p2p.bss_idx[P2PAPI_BSSCFG_PRIMARY].vif->ifp; - ndev = ifp->ndev; - } + if (vif == cfg->p2p.bss_idx[P2PAPI_BSSCFG_DEVICE].vif) + vif = cfg->p2p.bss_idx[P2PAPI_BSSCFG_PRIMARY].vif; /* Arm scan timeout timer */ mod_timer(&cfg->escan_timeout, jiffies + @@ -934,11 +888,11 @@ brcmf_cfg80211_escan(struct wiphy *wiphy, struct net_device *ndev, set_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status); if (escan_req) { cfg->escan_info.run = brcmf_run_escan; - err = brcmf_p2p_scan_prep(wiphy, request, ifp->vif); + err = brcmf_p2p_scan_prep(wiphy, request, vif); if (err) goto scan_out; - err = brcmf_do_escan(cfg, wiphy, ndev, request); + err = brcmf_do_escan(cfg, wiphy, vif->ifp, request); if (err) goto scan_out; } else { @@ -962,7 +916,7 @@ brcmf_cfg80211_escan(struct wiphy *wiphy, struct net_device *ndev, brcmf_err("WLC_SET_PASSIVE_SCAN error (%d)\n", err); goto scan_out; } - brcmf_set_mpc(ndev, 0); + brcmf_set_mpc(ifp, 0); err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SCAN, &sr->ssid_le, sizeof(sr->ssid_le)); if (err) { @@ -972,7 +926,7 @@ brcmf_cfg80211_escan(struct wiphy *wiphy, struct net_device *ndev, else brcmf_err("WLC_SCAN error (%d)\n", err); - brcmf_set_mpc(ndev, 1); + brcmf_set_mpc(ifp, 1); goto scan_out; } } @@ -990,16 +944,15 @@ scan_out: static s32 brcmf_cfg80211_scan(struct wiphy *wiphy, struct cfg80211_scan_request *request) { - struct net_device *ndev = request->wdev->netdev; + struct brcmf_cfg80211_vif *vif; s32 err = 0; brcmf_dbg(TRACE, "Enter\n"); - - if (!check_vif_up(container_of(request->wdev, - struct brcmf_cfg80211_vif, wdev))) + vif = container_of(request->wdev, struct brcmf_cfg80211_vif, wdev); + if (!check_vif_up(vif)) return -EIO; - err = brcmf_cfg80211_escan(wiphy, ndev, request, NULL); + err = brcmf_cfg80211_escan(wiphy, vif, request, NULL); if (err) brcmf_err("scan error (%d)\n", err); @@ -1891,8 +1844,10 @@ static s32 brcmf_add_keyext(struct wiphy *wiphy, struct net_device *ndev, u8 key_idx, const u8 *mac_addr, struct key_params *params) { + struct brcmf_if *ifp = netdev_priv(ndev); struct brcmf_wsec_key key; s32 err = 0; + u8 keybuf[8]; memset(&key, 0, sizeof(key)); key.index = (u32) key_idx; @@ -1916,8 +1871,9 @@ brcmf_add_keyext(struct wiphy *wiphy, struct net_device *ndev, brcmf_dbg(CONN, "Setting the key index %d\n", key.index); memcpy(key.data, params->key, key.len); - if (params->cipher == WLAN_CIPHER_SUITE_TKIP) { - u8 keybuf[8]; + if ((ifp->vif->mode != WL_MODE_AP) && + (params->cipher == WLAN_CIPHER_SUITE_TKIP)) { + brcmf_dbg(CONN, "Swapping RX/TX MIC key\n"); memcpy(keybuf, &key.data[24], sizeof(keybuf)); memcpy(&key.data[24], &key.data[16], sizeof(keybuf)); memcpy(&key.data[16], keybuf, sizeof(keybuf)); @@ -2013,7 +1969,7 @@ brcmf_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev, break; case WLAN_CIPHER_SUITE_TKIP: if (ifp->vif->mode != WL_MODE_AP) { - brcmf_dbg(CONN, "Swapping key\n"); + brcmf_dbg(CONN, "Swapping RX/TX MIC key\n"); memcpy(keybuf, &key.data[24], sizeof(keybuf)); memcpy(&key.data[24], &key.data[16], sizeof(keybuf)); memcpy(&key.data[16], keybuf, sizeof(keybuf)); @@ -2118,8 +2074,7 @@ brcmf_cfg80211_get_key(struct wiphy *wiphy, struct net_device *ndev, err = -EAGAIN; goto done; } - switch (wsec & ~SES_OW_ENABLED) { - case WEP_ENABLED: + if (wsec & WEP_ENABLED) { sec = &profile->sec; if (sec->cipher_pairwise & WLAN_CIPHER_SUITE_WEP40) { params.cipher = WLAN_CIPHER_SUITE_WEP40; @@ -2128,16 +2083,13 @@ brcmf_cfg80211_get_key(struct wiphy *wiphy, struct net_device *ndev, params.cipher = WLAN_CIPHER_SUITE_WEP104; brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP104\n"); } - break; - case TKIP_ENABLED: + } else if (wsec & TKIP_ENABLED) { params.cipher = WLAN_CIPHER_SUITE_TKIP; brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_TKIP\n"); - break; - case AES_ENABLED: + } else if (wsec & AES_ENABLED) { params.cipher = WLAN_CIPHER_SUITE_AES_CMAC; brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_AES_CMAC\n"); - break; - default: + } else { brcmf_err("Invalid algo (0x%x)\n", wsec); err = -EINVAL; goto done; @@ -2511,7 +2463,7 @@ void brcmf_abort_scanning(struct brcmf_cfg80211_info *cfg) set_bit(BRCMF_SCAN_STATUS_ABORT, &cfg->scan_status); if (cfg->scan_request) { escan->escan_state = WL_ESCAN_STATE_IDLE; - brcmf_notify_escan_complete(cfg, escan->ndev, true, true); + brcmf_notify_escan_complete(cfg, escan->ifp, true, true); } clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status); clear_bit(BRCMF_SCAN_STATUS_ABORT, &cfg->scan_status); @@ -2523,7 +2475,7 @@ static void brcmf_cfg80211_escan_timeout_worker(struct work_struct *work) container_of(work, struct brcmf_cfg80211_info, escan_timeout_work); - brcmf_notify_escan_complete(cfg, cfg->escan_info.ndev, true, true); + brcmf_notify_escan_complete(cfg, cfg->escan_info.ifp, true, true); } static void brcmf_escan_timeout(unsigned long data) @@ -2574,7 +2526,6 @@ brcmf_cfg80211_escan_handler(struct brcmf_if *ifp, const struct brcmf_event_msg *e, void *data) { struct brcmf_cfg80211_info *cfg = ifp->drvr->config; - struct net_device *ndev = ifp->ndev; s32 status; s32 err = 0; struct brcmf_escan_result_le *escan_result_le; @@ -2587,9 +2538,8 @@ brcmf_cfg80211_escan_handler(struct brcmf_if *ifp, status = e->status; - if (!ndev || !test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) { - brcmf_err("scan not ready ndev %p drv_status %x\n", ndev, - !test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)); + if (!test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) { + brcmf_err("scan not ready, bssidx=%d\n", ifp->bssidx); return -EPERM; } @@ -2660,7 +2610,7 @@ brcmf_cfg80211_escan_handler(struct brcmf_if *ifp, cfg->escan_info.escan_buf; brcmf_inform_bss(cfg); aborted = status != BRCMF_E_STATUS_SUCCESS; - brcmf_notify_escan_complete(cfg, ndev, aborted, + brcmf_notify_escan_complete(cfg, ifp, aborted, false); } else brcmf_dbg(SCAN, "Ignored scan complete result 0x%x\n", @@ -2738,7 +2688,7 @@ static s32 brcmf_cfg80211_suspend(struct wiphy *wiphy, brcmf_abort_scanning(cfg); /* Turn off watchdog timer */ - brcmf_set_mpc(ndev, 1); + brcmf_set_mpc(netdev_priv(ndev), 1); exit: brcmf_dbg(TRACE, "Exit\n"); @@ -2896,7 +2846,6 @@ brcmf_notify_sched_scan_results(struct brcmf_if *ifp, const struct brcmf_event_msg *e, void *data) { struct brcmf_cfg80211_info *cfg = ifp->drvr->config; - struct net_device *ndev = ifp->ndev; struct brcmf_pno_net_info_le *netinfo, *netinfo_start; struct cfg80211_scan_request *request = NULL; struct cfg80211_ssid *ssid = NULL; @@ -2980,7 +2929,7 @@ brcmf_notify_sched_scan_results(struct brcmf_if *ifp, } set_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status); - err = brcmf_do_escan(cfg, wiphy, ndev, request); + err = brcmf_do_escan(cfg, wiphy, ifp, request); if (err) { clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status); goto out_err; @@ -3137,7 +3086,7 @@ static int brcmf_cfg80211_sched_scan_stop(struct wiphy *wiphy, brcmf_dbg(SCAN, "enter\n"); brcmf_dev_pno_clean(ndev); if (cfg->sched_escan) - brcmf_notify_escan_complete(cfg, ndev, true, true); + brcmf_notify_escan_complete(cfg, netdev_priv(ndev), true, true); return 0; } @@ -3709,7 +3658,7 @@ brcmf_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *ndev, ssid_le.SSID_len = cpu_to_le32((u32)settings->ssid_len); } - brcmf_set_mpc(ndev, 0); + brcmf_set_mpc(ifp, 0); /* find the RSN_IE */ rsn_ie = brcmf_parse_tlvs((u8 *)settings->beacon.tail, @@ -3817,15 +3766,16 @@ brcmf_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *ndev, exit: if (err) - brcmf_set_mpc(ndev, 1); + brcmf_set_mpc(ifp, 1); return err; } static int brcmf_cfg80211_stop_ap(struct wiphy *wiphy, struct net_device *ndev) { struct brcmf_if *ifp = netdev_priv(ndev); - s32 err = -EPERM; + s32 err; struct brcmf_fil_bss_enable_le bss_enable; + struct brcmf_join_params join_params; brcmf_dbg(TRACE, "Enter\n"); @@ -3833,16 +3783,21 @@ static int brcmf_cfg80211_stop_ap(struct wiphy *wiphy, struct net_device *ndev) /* Due to most likely deauths outstanding we sleep */ /* first to make sure they get processed by fw. */ msleep(400); - err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_AP, 0); - if (err < 0) { - brcmf_err("setting AP mode failed %d\n", err); - goto exit; - } + + memset(&join_params, 0, sizeof(join_params)); + err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SSID, + &join_params, sizeof(join_params)); + if (err < 0) + brcmf_err("SET SSID error (%d)\n", err); err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_UP, 0); - if (err < 0) { + if (err < 0) brcmf_err("BRCMF_C_UP error %d\n", err); - goto exit; - } + err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_AP, 0); + if (err < 0) + brcmf_err("setting AP mode failed %d\n", err); + err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_INFRA, 0); + if (err < 0) + brcmf_err("setting INFRA mode failed %d\n", err); } else { bss_enable.bsscfg_idx = cpu_to_le32(ifp->bssidx); bss_enable.enable = cpu_to_le32(0); @@ -3851,11 +3806,10 @@ static int brcmf_cfg80211_stop_ap(struct wiphy *wiphy, struct net_device *ndev) if (err < 0) brcmf_err("bss_enable config failed %d\n", err); } - brcmf_set_mpc(ndev, 1); + brcmf_set_mpc(ifp, 1); set_bit(BRCMF_VIF_STATUS_AP_CREATING, &ifp->vif->sme_state); clear_bit(BRCMF_VIF_STATUS_AP_CREATED, &ifp->vif->sme_state); -exit: return err; } @@ -3909,13 +3863,13 @@ brcmf_cfg80211_mgmt_frame_register(struct wiphy *wiphy, struct wireless_dev *wdev, u16 frame_type, bool reg) { - struct brcmf_if *ifp = netdev_priv(wdev->netdev); - struct brcmf_cfg80211_vif *vif = ifp->vif; + struct brcmf_cfg80211_vif *vif; u16 mgmt_type; brcmf_dbg(TRACE, "Enter, frame_type %04x, reg=%d\n", frame_type, reg); mgmt_type = (frame_type & IEEE80211_FCTL_STYPE) >> 4; + vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev); if (reg) vif->mgmt_rx_reg |= BIT(mgmt_type); else @@ -3931,7 +3885,6 @@ brcmf_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, { struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); const struct ieee80211_mgmt *mgmt; - struct brcmf_if *ifp; struct brcmf_cfg80211_vif *vif; s32 err = 0; s32 ie_offset; @@ -3967,8 +3920,7 @@ brcmf_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, ie_offset = DOT11_MGMT_HDR_LEN + DOT11_BCN_PRB_FIXED_LEN; ie_len = len - ie_offset; - ifp = netdev_priv(wdev->netdev); - vif = ifp->vif; + vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev); if (vif == cfg->p2p.bss_idx[P2PAPI_BSSCFG_PRIMARY].vif) vif = cfg->p2p.bss_idx[P2PAPI_BSSCFG_DEVICE].vif; err = brcmf_vif_set_mgmt_ie(vif, @@ -4003,7 +3955,7 @@ brcmf_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, *cookie, le16_to_cpu(action_frame->len), chan->center_freq); - ack = brcmf_p2p_send_action_frame(cfg, wdev->netdev, + ack = brcmf_p2p_send_action_frame(cfg, cfg_to_ndev(cfg), af_params); cfg80211_mgmt_tx_status(wdev, *cookie, buf, len, ack, @@ -4075,6 +4027,8 @@ static struct cfg80211_ops wl_cfg80211_ops = { .mgmt_tx = brcmf_cfg80211_mgmt_tx, .remain_on_channel = brcmf_p2p_remain_on_channel, .cancel_remain_on_channel = brcmf_cfg80211_cancel_remain_on_channel, + .start_p2p_device = brcmf_p2p_start_device, + .stop_p2p_device = brcmf_p2p_stop_device, #ifdef CONFIG_NL80211_TESTMODE .testmode_cmd = brcmf_cfg80211_testmode #endif @@ -4162,6 +4116,11 @@ brcmf_txrx_stypes[NUM_NL80211_IFTYPES] = { BIT(IEEE80211_STYPE_AUTH >> 4) | BIT(IEEE80211_STYPE_DEAUTH >> 4) | BIT(IEEE80211_STYPE_ACTION >> 4) + }, + [NL80211_IFTYPE_P2P_DEVICE] = { + .tx = 0xffff, + .rx = BIT(IEEE80211_STYPE_ACTION >> 4) | + BIT(IEEE80211_STYPE_PROBE_REQ >> 4) } }; @@ -4188,13 +4147,6 @@ static struct wiphy *brcmf_setup_wiphy(struct device *phydev) wiphy->iface_combinations = brcmf_iface_combos; wiphy->n_iface_combinations = ARRAY_SIZE(brcmf_iface_combos); wiphy->bands[IEEE80211_BAND_2GHZ] = &__wl_band_2ghz; - wiphy->bands[IEEE80211_BAND_5GHZ] = &__wl_band_5ghz_a; /* Set - * it as 11a by default. - * This will be updated with - * 11n phy tables in - * "ifconfig up" - * if phy has 11n capability - */ wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM; wiphy->cipher_suites = __wl_cipher_suites; wiphy->n_cipher_suites = ARRAY_SIZE(__wl_cipher_suites); @@ -4204,6 +4156,9 @@ static struct wiphy *brcmf_setup_wiphy(struct device *phydev) wiphy->mgmt_stypes = brcmf_txrx_stypes; wiphy->max_remain_on_channel_duration = 5000; brcmf_wiphy_pno_params(wiphy); + brcmf_dbg(INFO, "Registering custom regulatory\n"); + wiphy->flags |= WIPHY_FLAG_CUSTOM_REGULATORY; + wiphy_apply_custom_regulatory(wiphy, &brcmf_regdom); err = wiphy_register(wiphy); if (err < 0) { brcmf_err("Could not register wiphy device (%d)\n", err); @@ -4622,9 +4577,11 @@ static s32 brcmf_notify_vif_event(struct brcmf_if *ifp, ifp->vif = vif; vif->ifp = ifp; - vif->wdev.netdev = ifp->ndev; - ifp->ndev->ieee80211_ptr = &vif->wdev; - SET_NETDEV_DEV(ifp->ndev, wiphy_dev(cfg->wiphy)); + if (ifp->ndev) { + vif->wdev.netdev = ifp->ndev; + ifp->ndev->ieee80211_ptr = &vif->wdev; + SET_NETDEV_DEV(ifp->ndev, wiphy_dev(cfg->wiphy)); + } mutex_unlock(&event->vif_event_lock); wake_up(&event->vif_wq); return 0; @@ -4927,34 +4884,248 @@ dongle_scantime_out: return err; } -static s32 wl_update_wiphybands(struct brcmf_cfg80211_info *cfg) + +static s32 brcmf_construct_reginfo(struct brcmf_cfg80211_info *cfg, u32 bw_cap) +{ + struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg)); + struct ieee80211_channel *band_chan_arr; + struct brcmf_chanspec_list *list; + s32 err; + u8 *pbuf; + u32 i, j; + u32 total; + u16 chanspec; + enum ieee80211_band band; + u32 channel; + u32 *n_cnt; + bool ht40_allowed; + u32 index; + u32 ht40_flag; + bool update; + u32 array_size; + + pbuf = kzalloc(BRCMF_DCMD_MEDLEN, GFP_KERNEL); + + if (pbuf == NULL) + return -ENOMEM; + + list = (struct brcmf_chanspec_list *)pbuf; + + err = brcmf_fil_iovar_data_get(ifp, "chanspecs", pbuf, + BRCMF_DCMD_MEDLEN); + if (err) { + brcmf_err("get chanspecs error (%d)\n", err); + goto exit; + } + + __wl_band_2ghz.n_channels = 0; + __wl_band_5ghz_a.n_channels = 0; + + total = le32_to_cpu(list->count); + for (i = 0; i < total; i++) { + chanspec = (u16)le32_to_cpu(list->element[i]); + channel = CHSPEC_CHANNEL(chanspec); + + if (CHSPEC_IS40(chanspec)) { + if (CHSPEC_SB_UPPER(chanspec)) + channel += CH_10MHZ_APART; + else + channel -= CH_10MHZ_APART; + } else if (CHSPEC_IS80(chanspec)) { + brcmf_dbg(INFO, "HT80 center channel : %d\n", + channel); + continue; + } + if (CHSPEC_IS2G(chanspec) && (channel >= CH_MIN_2G_CHANNEL) && + (channel <= CH_MAX_2G_CHANNEL)) { + band_chan_arr = __wl_2ghz_channels; + array_size = ARRAY_SIZE(__wl_2ghz_channels); + n_cnt = &__wl_band_2ghz.n_channels; + band = IEEE80211_BAND_2GHZ; + ht40_allowed = (bw_cap == WLC_N_BW_40ALL); + } else if (CHSPEC_IS5G(chanspec) && + channel >= CH_MIN_5G_CHANNEL) { + band_chan_arr = __wl_5ghz_a_channels; + array_size = ARRAY_SIZE(__wl_5ghz_a_channels); + n_cnt = &__wl_band_5ghz_a.n_channels; + band = IEEE80211_BAND_5GHZ; + ht40_allowed = !(bw_cap == WLC_N_BW_20ALL); + } else { + brcmf_err("Invalid channel Sepc. 0x%x.\n", chanspec); + continue; + } + if (!ht40_allowed && CHSPEC_IS40(chanspec)) + continue; + update = false; + for (j = 0; (j < *n_cnt && (*n_cnt < array_size)); j++) { + if (band_chan_arr[j].hw_value == channel) { + update = true; + break; + } + } + if (update) + index = j; + else + index = *n_cnt; + if (index < array_size) { + band_chan_arr[index].center_freq = + ieee80211_channel_to_frequency(channel, band); + band_chan_arr[index].hw_value = channel; + + if (CHSPEC_IS40(chanspec) && ht40_allowed) { + /* assuming the order is HT20, HT40 Upper, + * HT40 lower from chanspecs + */ + ht40_flag = band_chan_arr[index].flags & + IEEE80211_CHAN_NO_HT40; + if (CHSPEC_SB_UPPER(chanspec)) { + if (ht40_flag == IEEE80211_CHAN_NO_HT40) + band_chan_arr[index].flags &= + ~IEEE80211_CHAN_NO_HT40; + band_chan_arr[index].flags |= + IEEE80211_CHAN_NO_HT40PLUS; + } else { + /* It should be one of + * IEEE80211_CHAN_NO_HT40 or + * IEEE80211_CHAN_NO_HT40PLUS + */ + band_chan_arr[index].flags &= + ~IEEE80211_CHAN_NO_HT40; + if (ht40_flag == IEEE80211_CHAN_NO_HT40) + band_chan_arr[index].flags |= + IEEE80211_CHAN_NO_HT40MINUS; + } + } else { + band_chan_arr[index].flags = + IEEE80211_CHAN_NO_HT40; + if (band == IEEE80211_BAND_2GHZ) + channel |= WL_CHANSPEC_BAND_2G; + else + channel |= WL_CHANSPEC_BAND_5G; + channel |= WL_CHANSPEC_BW_20; + err = brcmf_fil_bsscfg_int_get(ifp, + "per_chan_info", + &channel); + if (!err) { + if (channel & WL_CHAN_RADAR) + band_chan_arr[index].flags |= + (IEEE80211_CHAN_RADAR | + IEEE80211_CHAN_NO_IBSS); + if (channel & WL_CHAN_PASSIVE) + band_chan_arr[index].flags |= + IEEE80211_CHAN_PASSIVE_SCAN; + } + } + if (!update) + (*n_cnt)++; + } + } +exit: + kfree(pbuf); + return err; +} + + +static s32 brcmf_update_wiphybands(struct brcmf_cfg80211_info *cfg) { struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg)); struct wiphy *wiphy; s32 phy_list; + u32 band_list[3]; + u32 nmode; + u32 bw_cap = 0; s8 phy; - s32 err = 0; + s32 err; + u32 nband; + s32 i; + struct ieee80211_supported_band *bands[IEEE80211_NUM_BANDS]; + s32 index; err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_PHYLIST, &phy_list, sizeof(phy_list)); if (err) { - brcmf_err("error (%d)\n", err); + brcmf_err("BRCMF_C_GET_PHYLIST error (%d)\n", err); return err; } phy = ((char *)&phy_list)[0]; - brcmf_dbg(INFO, "%c phy\n", phy); - if (phy == 'n' || phy == 'a') { - wiphy = cfg_to_wiphy(cfg); - wiphy->bands[IEEE80211_BAND_5GHZ] = &__wl_band_5ghz_n; + brcmf_dbg(INFO, "BRCMF_C_GET_PHYLIST reported: %c phy\n", phy); + + + err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BANDLIST, + &band_list, sizeof(band_list)); + if (err) { + brcmf_err("BRCMF_C_GET_BANDLIST error (%d)\n", err); + return err; + } + brcmf_dbg(INFO, "BRCMF_C_GET_BANDLIST reported: 0x%08x 0x%08x 0x%08x phy\n", + band_list[0], band_list[1], band_list[2]); + + err = brcmf_fil_iovar_int_get(ifp, "nmode", &nmode); + if (err) { + brcmf_err("nmode error (%d)\n", err); + } else { + err = brcmf_fil_iovar_int_get(ifp, "mimo_bw_cap", &bw_cap); + if (err) + brcmf_err("mimo_bw_cap error (%d)\n", err); + } + brcmf_dbg(INFO, "nmode=%d, mimo_bw_cap=%d\n", nmode, bw_cap); + + err = brcmf_construct_reginfo(cfg, bw_cap); + if (err) { + brcmf_err("brcmf_construct_reginfo failed (%d)\n", err); + return err; } + nband = band_list[0]; + memset(bands, 0, sizeof(bands)); + + for (i = 1; i <= nband && i < ARRAY_SIZE(band_list); i++) { + index = -1; + if ((band_list[i] == WLC_BAND_5G) && + (__wl_band_5ghz_a.n_channels > 0)) { + index = IEEE80211_BAND_5GHZ; + bands[index] = &__wl_band_5ghz_a; + if ((bw_cap == WLC_N_BW_40ALL) || + (bw_cap == WLC_N_BW_20IN2G_40IN5G)) + bands[index]->ht_cap.cap |= + IEEE80211_HT_CAP_SGI_40; + } else if ((band_list[i] == WLC_BAND_2G) && + (__wl_band_2ghz.n_channels > 0)) { + index = IEEE80211_BAND_2GHZ; + bands[index] = &__wl_band_2ghz; + if (bw_cap == WLC_N_BW_40ALL) + bands[index]->ht_cap.cap |= + IEEE80211_HT_CAP_SGI_40; + } + + if ((index >= 0) && nmode) { + bands[index]->ht_cap.cap |= IEEE80211_HT_CAP_SGI_20; + bands[index]->ht_cap.cap |= IEEE80211_HT_CAP_DSSSCCK40; + bands[index]->ht_cap.ht_supported = true; + bands[index]->ht_cap.ampdu_factor = + IEEE80211_HT_MAX_AMPDU_64K; + bands[index]->ht_cap.ampdu_density = + IEEE80211_HT_MPDU_DENSITY_16; + /* An HT shall support all EQM rates for one spatial + * stream + */ + bands[index]->ht_cap.mcs.rx_mask[0] = 0xff; + } + } + + wiphy = cfg_to_wiphy(cfg); + wiphy->bands[IEEE80211_BAND_2GHZ] = bands[IEEE80211_BAND_2GHZ]; + wiphy->bands[IEEE80211_BAND_5GHZ] = bands[IEEE80211_BAND_5GHZ]; + wiphy_apply_custom_regulatory(wiphy, &brcmf_regdom); + return err; } + static s32 brcmf_dongle_probecap(struct brcmf_cfg80211_info *cfg) { - return wl_update_wiphybands(cfg); + return brcmf_update_wiphybands(cfg); } static s32 brcmf_config_dongle(struct brcmf_cfg80211_info *cfg) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.h b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.h index 8b5d4989906..2907437ef43 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.h @@ -238,9 +238,8 @@ struct escan_info { u32 escan_state; u8 escan_buf[WL_ESCAN_BUF_SIZE]; struct wiphy *wiphy; - struct net_device *ndev; - s32 (*run)(struct brcmf_cfg80211_info *cfg, - struct net_device *ndev, + struct brcmf_if *ifp; + s32 (*run)(struct brcmf_cfg80211_info *cfg, struct brcmf_if *ifp, struct cfg80211_scan_request *request, u16 action); }; @@ -493,9 +492,9 @@ bool brcmf_cfg80211_vif_event_armed(struct brcmf_cfg80211_info *cfg); int brcmf_cfg80211_wait_vif_event_timeout(struct brcmf_cfg80211_info *cfg, u8 action, ulong timeout); s32 brcmf_notify_escan_complete(struct brcmf_cfg80211_info *cfg, - struct net_device *ndev, - bool aborted, bool fw_abort); -void brcmf_set_mpc(struct net_device *ndev, int mpc); + struct brcmf_if *ifp, bool aborted, + bool fw_abort); +void brcmf_set_mpc(struct brcmf_if *ndev, int mpc); void brcmf_abort_scanning(struct brcmf_cfg80211_info *cfg); #endif /* _wl_cfg80211_h_ */ |