From aa538aca9fb4a197464f5926c7b5d7050e9144b1 Mon Sep 17 00:00:00 2001 From: Rajkumar Manoharan Date: Wed, 16 Dec 2015 16:52:19 +0200 Subject: [PATCH 01/52] ath10k: wake up device before accessing registers commit 1aaf8efba0ae ("ath10k: disable PCI PS for QCA988X and QCA99X0") partially reverts pci soc powersave support added by commit 77258d409ce4 ("ath10k: enable pci soc powersaving"). While reverting the change, pci wake up function is called after accessing pci registers instead of prior to access. The assumption is that chip is woken up before accessing its registers.Though this change does not fix any known issues, this might help to avoid unknown or low power platform specific issues. Signed-off-by: Rajkumar Manoharan Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/pci.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/pci.c b/drivers/net/wireless/ath/ath10k/pci.c index 14fd73ec1c96..d77ba4c09e78 100644 --- a/drivers/net/wireless/ath/ath10k/pci.c +++ b/drivers/net/wireless/ath/ath10k/pci.c @@ -3071,9 +3071,6 @@ static int ath10k_pci_probe(struct pci_dev *pdev, goto err_sleep; } - ath10k_pci_ce_deinit(ar); - ath10k_pci_irq_disable(ar); - if (ar_pci->pci_ps == 0) { ret = ath10k_pci_force_wake(ar); if (ret) { @@ -3082,6 +3079,9 @@ static int ath10k_pci_probe(struct pci_dev *pdev, } } + ath10k_pci_ce_deinit(ar); + ath10k_pci_irq_disable(ar); + ret = ath10k_pci_init_irq(ar); if (ret) { ath10k_err(ar, "failed to init irqs: %d\n", ret); From d9d6a5ae2171576aa8f4a9669d1d02e99d830b77 Mon Sep 17 00:00:00 2001 From: Rajkumar Manoharan Date: Wed, 16 Dec 2015 16:52:19 +0200 Subject: [PATCH 02/52] ath10k: reduce indentation by moving powersave check within function For devices that does not support PCI power save, force wake up API is used. So move powersave check inside force wakeup to reduce one level indentation. Signed-off-by: Rajkumar Manoharan Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/pci.c | 34 ++++++++++++--------------- 1 file changed, 15 insertions(+), 19 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/pci.c b/drivers/net/wireless/ath/ath10k/pci.c index d77ba4c09e78..ee925c618535 100644 --- a/drivers/net/wireless/ath/ath10k/pci.c +++ b/drivers/net/wireless/ath/ath10k/pci.c @@ -487,6 +487,9 @@ static int ath10k_pci_force_wake(struct ath10k *ar) unsigned long flags; int ret = 0; + if (ar_pci->pci_ps) + return ret; + spin_lock_irqsave(&ar_pci->ps_lock, flags); if (!ar_pci->ps_awake) { @@ -2480,12 +2483,10 @@ static int ath10k_pci_hif_resume(struct ath10k *ar) u32 val; int ret = 0; - if (ar_pci->pci_ps == 0) { - ret = ath10k_pci_force_wake(ar); - if (ret) { - ath10k_err(ar, "failed to wake up target: %d\n", ret); - return ret; - } + ret = ath10k_pci_force_wake(ar); + if (ret) { + ath10k_err(ar, "failed to wake up target: %d\n", ret); + return ret; } /* Suspend/Resume resets the PCI configuration space, so we have to @@ -2592,13 +2593,10 @@ static irqreturn_t ath10k_pci_interrupt_handler(int irq, void *arg) struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); int ret; - if (ar_pci->pci_ps == 0) { - ret = ath10k_pci_force_wake(ar); - if (ret) { - ath10k_warn(ar, "failed to wake device up on irq: %d\n", - ret); - return IRQ_NONE; - } + ret = ath10k_pci_force_wake(ar); + if (ret) { + ath10k_warn(ar, "failed to wake device up on irq: %d\n", ret); + return IRQ_NONE; } if (ar_pci->num_msi_intrs == 0) { @@ -3071,12 +3069,10 @@ static int ath10k_pci_probe(struct pci_dev *pdev, goto err_sleep; } - if (ar_pci->pci_ps == 0) { - ret = ath10k_pci_force_wake(ar); - if (ret) { - ath10k_warn(ar, "failed to wake up device : %d\n", ret); - goto err_free_pipes; - } + ret = ath10k_pci_force_wake(ar); + if (ret) { + ath10k_warn(ar, "failed to wake up device : %d\n", ret); + goto err_free_pipes; } ath10k_pci_ce_deinit(ar); From 5c352bf0880655e3a782f55a86fb172542c19a00 Mon Sep 17 00:00:00 2001 From: Andrzej Hajda Date: Mon, 14 Dec 2015 11:06:01 +0100 Subject: [PATCH 03/52] ath9k_htc: fix handling return value of ath9k_hw_calibrate The function can return negative values in case of error. Its result should be then tested for such case. The problem has been detected using proposed semantic patch scripts/coccinelle/tests/assign_signed_to_unsigned.cocci [1]. [1]: http://permalink.gmane.org/gmane.linux.kernel/2046107 Signed-off-by: Andrzej Hajda Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath9k/htc_drv_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_main.c b/drivers/net/wireless/ath/ath9k/htc_drv_main.c index a680a970b7f7..fe1fd1a5ae15 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_main.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_main.c @@ -834,7 +834,7 @@ void ath9k_htc_ani_work(struct work_struct *work) if (longcal || shortcal) common->ani.caldone = ath9k_hw_calibrate(ah, ah->curchan, - ah->rxchainmask, longcal); + ah->rxchainmask, longcal) > 0; ath9k_htc_ps_restore(priv); } From f94c48d6c315a580b60b062c0360561b61faa311 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Wed, 16 Dec 2015 14:10:00 +0300 Subject: [PATCH 04/52] wil6210: fix a warning message condition "iter" is -1 at the end of the loop and not zero. It means we don't print a warning message. Signed-off-by: Dan Carpenter Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/wil6210/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c index 09b4daebab9d..b39f0bfc591e 100644 --- a/drivers/net/wireless/ath/wil6210/main.c +++ b/drivers/net/wireless/ath/wil6210/main.c @@ -987,7 +987,7 @@ int __wil_down(struct wil6210_priv *wil) } mutex_lock(&wil->mutex); - if (!iter) + if (iter < 0) wil_err(wil, "timeout waiting for idle FW/HW\n"); wil_reset(wil, false); From 9ec855cc9e17fe111e1f868027a75ac7ae849997 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Wed, 30 Dec 2015 12:20:49 +0100 Subject: [PATCH 05/52] iwlegacy: 4965-mac: constify il_sensitivity_ranges structure The il_sensitivity_ranges is never modified, so declare it as const. Done with the help of Coccinelle. Signed-off-by: Julia Lawall Acked-by: Stanislaw Gruszka Signed-off-by: Kalle Valo --- drivers/net/wireless/intel/iwlegacy/4965-mac.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/intel/iwlegacy/4965-mac.c b/drivers/net/wireless/intel/iwlegacy/4965-mac.c index 6656215a13a9..fd38aa0763e4 100644 --- a/drivers/net/wireless/intel/iwlegacy/4965-mac.c +++ b/drivers/net/wireless/intel/iwlegacy/4965-mac.c @@ -6416,7 +6416,7 @@ il4965_hw_detect(struct il_priv *il) D_INFO("HW Revision ID = 0x%X\n", il->rev_id); } -static struct il_sensitivity_ranges il4965_sensitivity = { +static const struct il_sensitivity_ranges il4965_sensitivity = { .min_nrg_cck = 97, .max_nrg_cck = 0, /* not used, set to 0 */ From 50f85e220f6f51798429a3cc74e7c76d27513bef Mon Sep 17 00:00:00 2001 From: Insu Yun Date: Wed, 30 Dec 2015 11:01:44 -0500 Subject: [PATCH 06/52] mwifiex: correctly handling kzalloc Since kzalloc can be failed in memory pressure, it needs to be handled, otherwise NULL dereference could be happened Signed-off-by: Insu Yun Signed-off-by: Kalle Valo --- drivers/net/wireless/marvell/mwifiex/sdio.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/drivers/net/wireless/marvell/mwifiex/sdio.c b/drivers/net/wireless/marvell/mwifiex/sdio.c index 78a8474e1a3d..a8af72d02c44 100644 --- a/drivers/net/wireless/marvell/mwifiex/sdio.c +++ b/drivers/net/wireless/marvell/mwifiex/sdio.c @@ -2053,8 +2053,19 @@ static int mwifiex_init_sdio(struct mwifiex_adapter *adapter) /* Allocate skb pointer buffers */ card->mpa_rx.skb_arr = kzalloc((sizeof(void *)) * card->mp_agg_pkt_limit, GFP_KERNEL); + if (!card->mpa_rx.skb_arr) { + kfree(card->mp_regs); + return -ENOMEM; + } + card->mpa_rx.len_arr = kzalloc(sizeof(*card->mpa_rx.len_arr) * card->mp_agg_pkt_limit, GFP_KERNEL); + if (!card->mpa_rx.len_arr) { + kfree(card->mp_regs); + kfree(card->mpa_rx.skb_arr); + return -ENOMEM; + } + ret = mwifiex_alloc_sdio_mpa_buffers(adapter, card->mp_tx_agg_buf_size, card->mp_rx_agg_buf_size); From fdb1e28e05c9d1d6d9c3226e5443ad12e2032e00 Mon Sep 17 00:00:00 2001 From: Xinming Hu Date: Thu, 31 Dec 2015 06:24:12 -0800 Subject: [PATCH 07/52] mwifiex: fix missing debug messages Some critical messages are missed until "adapter->dev" gets initialized in mwifiex_register_dev(). We will use pr_* print message instead of mwifiex_dbg at those places to resolve the problem. Signed-off-by: Xinming Hu Signed-off-by: Amitkumar Karwar Signed-off-by: Kalle Valo --- drivers/net/wireless/marvell/mwifiex/pcie.c | 26 ++++++++------------- 1 file changed, 10 insertions(+), 16 deletions(-) diff --git a/drivers/net/wireless/marvell/mwifiex/pcie.c b/drivers/net/wireless/marvell/mwifiex/pcie.c index 9703848ba9f8..eb0b386f1a33 100644 --- a/drivers/net/wireless/marvell/mwifiex/pcie.c +++ b/drivers/net/wireless/marvell/mwifiex/pcie.c @@ -2473,50 +2473,44 @@ static int mwifiex_pcie_init(struct mwifiex_adapter *adapter) pci_set_master(pdev); - mwifiex_dbg(adapter, INFO, - "try set_consistent_dma_mask(32)\n"); + pr_notice("try set_consistent_dma_mask(32)\n"); ret = pci_set_dma_mask(pdev, DMA_BIT_MASK(32)); if (ret) { - mwifiex_dbg(adapter, ERROR, - "set_dma_mask(32) failed\n"); + pr_err("set_dma_mask(32) failed\n"); goto err_set_dma_mask; } ret = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32)); if (ret) { - mwifiex_dbg(adapter, ERROR, - "set_consistent_dma_mask(64) failed\n"); + pr_err("set_consistent_dma_mask(64) failed\n"); goto err_set_dma_mask; } ret = pci_request_region(pdev, 0, DRV_NAME); if (ret) { - mwifiex_dbg(adapter, ERROR, - "req_reg(0) error\n"); + pr_err("req_reg(0) error\n"); goto err_req_region0; } card->pci_mmap = pci_iomap(pdev, 0, 0); if (!card->pci_mmap) { - mwifiex_dbg(adapter, ERROR, "iomap(0) error\n"); + pr_err("iomap(0) error\n"); ret = -EIO; goto err_iomap0; } ret = pci_request_region(pdev, 2, DRV_NAME); if (ret) { - mwifiex_dbg(adapter, ERROR, "req_reg(2) error\n"); + pr_err("req_reg(2) error\n"); goto err_req_region2; } card->pci_mmap1 = pci_iomap(pdev, 2, 0); if (!card->pci_mmap1) { - mwifiex_dbg(adapter, ERROR, - "iomap(2) error\n"); + pr_err("iomap(2) error\n"); ret = -EIO; goto err_iomap2; } - mwifiex_dbg(adapter, INFO, - "PCI memory map Virt0: %p PCI memory map Virt2: %p\n", - card->pci_mmap, card->pci_mmap1); + pr_notice("PCI memory map Virt0: %p PCI memory map Virt2: %p\n", + card->pci_mmap, card->pci_mmap1); card->cmdrsp_buf = NULL; ret = mwifiex_pcie_create_txbd_ring(adapter); @@ -2635,11 +2629,11 @@ static int mwifiex_register_dev(struct mwifiex_adapter *adapter) /* save adapter pointer in card */ card->adapter = adapter; + adapter->dev = &pdev->dev; if (mwifiex_pcie_request_irq(adapter)) return -1; - adapter->dev = &pdev->dev; adapter->tx_buf_size = card->pcie.tx_buf_size; adapter->mem_type_mapping_tbl = mem_type_mapping_tbl; adapter->num_mem_types = ARRAY_SIZE(mem_type_mapping_tbl); From 91442431c3f90d69e73f776847058d1ab2efdad1 Mon Sep 17 00:00:00 2001 From: Xinming Hu Date: Thu, 31 Dec 2015 06:24:14 -0800 Subject: [PATCH 08/52] mwifiex: increase priority for critical message This patch increase the priority for some critical messages. Signed-off-by: Xinming Hu Signed-off-by: Amitkumar Karwar Signed-off-by: Kalle Valo --- drivers/net/wireless/marvell/mwifiex/pcie.c | 6 +++--- drivers/net/wireless/marvell/mwifiex/sdio.c | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/marvell/mwifiex/pcie.c b/drivers/net/wireless/marvell/mwifiex/pcie.c index eb0b386f1a33..6d0dc40e20e5 100644 --- a/drivers/net/wireless/marvell/mwifiex/pcie.c +++ b/drivers/net/wireless/marvell/mwifiex/pcie.c @@ -2129,14 +2129,14 @@ static irqreturn_t mwifiex_pcie_interrupt(int irq, void *context) struct mwifiex_adapter *adapter; if (!pdev) { - pr_debug("info: %s: pdev is NULL\n", (u8 *)pdev); + pr_err("info: %s: pdev is NULL\n", __func__); goto exit; } card = pci_get_drvdata(pdev); if (!card || !card->adapter) { - pr_debug("info: %s: card=%p adapter=%p\n", __func__, card, - card ? card->adapter : NULL); + pr_err("info: %s: card=%p adapter=%p\n", __func__, card, + card ? card->adapter : NULL); goto exit; } adapter = card->adapter; diff --git a/drivers/net/wireless/marvell/mwifiex/sdio.c b/drivers/net/wireless/marvell/mwifiex/sdio.c index a8af72d02c44..4c8cae682c89 100644 --- a/drivers/net/wireless/marvell/mwifiex/sdio.c +++ b/drivers/net/wireless/marvell/mwifiex/sdio.c @@ -796,8 +796,8 @@ mwifiex_sdio_interrupt(struct sdio_func *func) card = sdio_get_drvdata(func); if (!card || !card->adapter) { - pr_debug("int: func=%p card=%p adapter=%p\n", - func, card, card ? card->adapter : NULL); + pr_err("int: func=%p card=%p adapter=%p\n", + func, card, card ? card->adapter : NULL); return; } adapter = card->adapter; From ee548d4b1036aaba290a4eec68ba11590ad9cc73 Mon Sep 17 00:00:00 2001 From: Amitkumar Karwar Date: Thu, 31 Dec 2015 06:24:15 -0800 Subject: [PATCH 09/52] mwifiex: reduce cloned skb queue size Driver supports Tx status ack feature. When hostapd/ wpa_supplicant asks for ack status of an EAPOL/ACTION frame, driver maintains a cloned skb for the packet until TX_STATUS event is received from firmware. Cloned skb queue gets flushed when connection is terminated or driver is unloaded. Let's reduce the queue size to avoid unnecessarily keeping memory allocated when environment is busy. Signed-off-by: Amitkumar Karwar Signed-off-by: Kalle Valo --- drivers/net/wireless/marvell/mwifiex/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/marvell/mwifiex/main.c b/drivers/net/wireless/marvell/mwifiex/main.c index 969ca1e1f3e9..79c16de8743e 100644 --- a/drivers/net/wireless/marvell/mwifiex/main.c +++ b/drivers/net/wireless/marvell/mwifiex/main.c @@ -763,7 +763,7 @@ mwifiex_clone_skb_for_tx_status(struct mwifiex_private *priv, spin_lock_irqsave(&priv->ack_status_lock, flags); id = idr_alloc(&priv->ack_status_frames, orig_skb, - 1, 0xff, GFP_ATOMIC); + 1, 0x10, GFP_ATOMIC); spin_unlock_irqrestore(&priv->ack_status_lock, flags); if (id >= 0) { From f7b7caa488eb209f1c63cc98104407c62753e406 Mon Sep 17 00:00:00 2001 From: Alexey Khoroshilov Date: Sat, 2 Jan 2016 02:12:38 +0300 Subject: [PATCH 10/52] ipw2x00: add checks for dma mapping errors ipw2100_alloc_skb() and ipw2100_tx_send_data() do not check if mapping dma memory succeed. The patch adds the checks and failure handling. Found by Linux Driver Verification project (linuxtesting.org). Signed-off-by: Alexey Khoroshilov Signed-off-by: Kalle Valo --- drivers/net/wireless/intel/ipw2x00/ipw2100.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/intel/ipw2x00/ipw2100.c b/drivers/net/wireless/intel/ipw2x00/ipw2100.c index 36818c7f30b9..f93a7f71c047 100644 --- a/drivers/net/wireless/intel/ipw2x00/ipw2100.c +++ b/drivers/net/wireless/intel/ipw2x00/ipw2100.c @@ -2311,8 +2311,10 @@ static int ipw2100_alloc_skb(struct ipw2100_priv *priv, packet->dma_addr = pci_map_single(priv->pci_dev, packet->skb->data, sizeof(struct ipw2100_rx), PCI_DMA_FROMDEVICE); - /* NOTE: pci_map_single does not return an error code, and 0 is a valid - * dma_addr */ + if (pci_dma_mapping_error(priv->pci_dev, packet->dma_addr)) { + dev_kfree_skb(packet->skb); + return -ENOMEM; + } return 0; } @@ -3183,6 +3185,11 @@ static void ipw2100_tx_send_data(struct ipw2100_priv *priv) LIBIPW_3ADDR_LEN, tbd->buf_length, PCI_DMA_TODEVICE); + if (pci_dma_mapping_error(priv->pci_dev, + tbd->host_addr)) { + IPW_DEBUG_TX("dma mapping error\n"); + break; + } IPW_DEBUG_TX("data frag tbd TX%d P=%08x L=%d\n", txq->next, tbd->host_addr, From 44129ed04b2b01c3ce9421bd3f530bd6d558e8cd Mon Sep 17 00:00:00 2001 From: Franky Lin Date: Sat, 2 Jan 2016 09:41:36 +0100 Subject: [PATCH 11/52] brcmfmac: add arp offload ip address table configuration support Obtain ipv4 address through inetaddr notification for ARP offload host ip table configuration. Reviewed-by: Arend Van Spriel Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Hante Meuleman Signed-off-by: Franky Lin Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo --- .../broadcom/brcm80211/brcmfmac/core.c | 108 ++++++++++++++++++ .../broadcom/brcm80211/brcmfmac/core.h | 2 + 2 files changed, 110 insertions(+) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c index 3a39192e3f14..4c8f7bf4227c 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -620,6 +621,8 @@ static int brcmf_netdev_stop(struct net_device *ndev) brcmf_cfg80211_down(ndev); + brcmf_fil_iovar_data_set(ifp, "arp_hostip_clear", NULL, 0); + brcmf_net_setcarrier(ifp, false); return 0; @@ -940,6 +943,98 @@ int brcmf_get_next_free_bsscfgidx(struct brcmf_pub *drvr) return available ? bsscfgidx : -ENOMEM; } +#ifdef CONFIG_INET +#define ARPOL_MAX_ENTRIES 8 +static int brcmf_inetaddr_changed(struct notifier_block *nb, + unsigned long action, void *data) +{ + struct brcmf_pub *drvr = container_of(nb, struct brcmf_pub, + inetaddr_notifier); + struct in_ifaddr *ifa = data; + struct net_device *ndev = ifa->ifa_dev->dev; + struct brcmf_if *ifp; + int idx, i, ret; + u32 val; + __be32 addr_table[ARPOL_MAX_ENTRIES] = {0}; + + /* Find out if the notification is meant for us */ + for (idx = 0; idx < BRCMF_MAX_IFS; idx++) { + ifp = drvr->iflist[idx]; + if (ifp && ifp->ndev == ndev) + break; + if (idx == BRCMF_MAX_IFS - 1) + return NOTIFY_DONE; + } + + /* check if arp offload is supported */ + ret = brcmf_fil_iovar_int_get(ifp, "arpoe", &val); + if (ret) + return NOTIFY_OK; + + /* old version only support primary index */ + ret = brcmf_fil_iovar_int_get(ifp, "arp_version", &val); + if (ret) + val = 1; + if (val == 1) + ifp = drvr->iflist[0]; + + /* retrieve the table from firmware */ + ret = brcmf_fil_iovar_data_get(ifp, "arp_hostip", addr_table, + sizeof(addr_table)); + if (ret) { + brcmf_err("fail to get arp ip table err:%d\n", ret); + return NOTIFY_OK; + } + + for (i = 0; i < ARPOL_MAX_ENTRIES; i++) + if (ifa->ifa_address == addr_table[i]) + break; + + switch (action) { + case NETDEV_UP: + if (i == ARPOL_MAX_ENTRIES) { + brcmf_dbg(TRACE, "add %pI4 to arp table\n", + &ifa->ifa_address); + /* set it directly */ + ret = brcmf_fil_iovar_data_set(ifp, "arp_hostip", + &ifa->ifa_address, sizeof(ifa->ifa_address)); + if (ret) + brcmf_err("add arp ip err %d\n", ret); + } + break; + case NETDEV_DOWN: + if (i < ARPOL_MAX_ENTRIES) { + addr_table[i] = 0; + brcmf_dbg(TRACE, "remove %pI4 from arp table\n", + &ifa->ifa_address); + /* clear the table in firmware */ + ret = brcmf_fil_iovar_data_set(ifp, "arp_hostip_clear", + NULL, 0); + if (ret) { + brcmf_err("fail to clear arp ip table err:%d\n", + ret); + return NOTIFY_OK; + } + for (i = 0; i < ARPOL_MAX_ENTRIES; i++) { + if (addr_table[i] != 0) { + brcmf_fil_iovar_data_set(ifp, + "arp_hostip", &addr_table[i], + sizeof(addr_table[i])); + if (ret) + brcmf_err("add arp ip err %d\n", + ret); + } + } + } + break; + default: + break; + } + + return NOTIFY_OK; +} +#endif + int brcmf_attach(struct device *dev) { struct brcmf_pub *drvr = NULL; @@ -1068,6 +1163,15 @@ int brcmf_bus_start(struct device *dev) if (p2p_ifp) ret = brcmf_net_p2p_attach(p2p_ifp); } + + if (ret) + goto fail; + +#ifdef CONFIG_INET + drvr->inetaddr_notifier.notifier_call = brcmf_inetaddr_changed; + ret = register_inetaddr_notifier(&drvr->inetaddr_notifier); +#endif + fail: if (ret < 0) { brcmf_err("failed: %d\n", ret); @@ -1133,6 +1237,10 @@ void brcmf_detach(struct device *dev) if (drvr == NULL) return; +#ifdef CONFIG_INET + unregister_inetaddr_notifier(&drvr->inetaddr_notifier); +#endif + /* stop firmware event handling */ brcmf_fweh_detach(drvr); if (drvr->config) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.h index 77d8239d9536..6018af72bab1 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.h @@ -141,6 +141,8 @@ struct brcmf_pub { #ifdef DEBUG struct dentry *dbgfs_dir; #endif + + struct notifier_block inetaddr_notifier; }; /* forward declarations */ From 3f5893d1b30a92f3fac1587750b2402c40d66651 Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Sat, 2 Jan 2016 09:41:37 +0100 Subject: [PATCH 12/52] brcmfmac: Add get_station support for IBSS When get_station is requested for IBSS then an error will be printed and no information will be returned. This patch adds IBSS specific get station information function. Reviewed-by: Arend Van Spriel Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo --- .../broadcom/brcm80211/brcmfmac/cfg80211.c | 51 +++++++++++++++++++ .../broadcom/brcm80211/brcmfmac/fwil.h | 1 + .../broadcom/brcm80211/brcmfmac/fwil_types.h | 17 +++++++ 3 files changed, 69 insertions(+) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c index 17658b326a6c..6b9339b83abc 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c @@ -2428,6 +2428,54 @@ static void brcmf_fill_bss_param(struct brcmf_if *ifp, struct station_info *si) si->bss_param.flags |= BSS_PARAM_FLAGS_SHORT_SLOT_TIME; } +static s32 +brcmf_cfg80211_get_station_ibss(struct brcmf_if *ifp, + struct station_info *sinfo) +{ + struct brcmf_scb_val_le scbval; + struct brcmf_pktcnt_le pktcnt; + s32 err; + u32 rate; + u32 rssi; + + /* Get the current tx rate */ + err = brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_RATE, &rate); + if (err < 0) { + brcmf_err("BRCMF_C_GET_RATE error (%d)\n", err); + return err; + } + sinfo->filled |= BIT(NL80211_STA_INFO_TX_BITRATE); + sinfo->txrate.legacy = rate * 5; + + memset(&scbval, 0, sizeof(scbval)); + err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_RSSI, &scbval, + sizeof(scbval)); + if (err) { + brcmf_err("BRCMF_C_GET_RSSI error (%d)\n", err); + return err; + } + rssi = le32_to_cpu(scbval.val); + sinfo->filled |= BIT(NL80211_STA_INFO_SIGNAL); + sinfo->signal = rssi; + + err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_GET_PKTCNTS, &pktcnt, + sizeof(pktcnt)); + if (err) { + brcmf_err("BRCMF_C_GET_GET_PKTCNTS error (%d)\n", err); + return err; + } + sinfo->filled |= BIT(NL80211_STA_INFO_RX_PACKETS) | + BIT(NL80211_STA_INFO_RX_DROP_MISC) | + BIT(NL80211_STA_INFO_TX_PACKETS) | + BIT(NL80211_STA_INFO_TX_FAILED); + sinfo->rx_packets = le32_to_cpu(pktcnt.rx_good_pkt); + sinfo->rx_dropped_misc = le32_to_cpu(pktcnt.rx_bad_pkt); + sinfo->tx_packets = le32_to_cpu(pktcnt.tx_good_pkt); + sinfo->tx_failed = le32_to_cpu(pktcnt.tx_bad_pkt); + + return 0; +} + static s32 brcmf_cfg80211_get_station(struct wiphy *wiphy, struct net_device *ndev, const u8 *mac, struct station_info *sinfo) @@ -2445,6 +2493,9 @@ brcmf_cfg80211_get_station(struct wiphy *wiphy, struct net_device *ndev, if (!check_vif_up(ifp->vif)) return -EIO; + if (brcmf_is_ibssmode(ifp->vif)) + return brcmf_cfg80211_get_station_ibss(ifp, sinfo); + memset(&sta_info_le, 0, sizeof(sta_info_le)); memcpy(&sta_info_le, mac, ETH_ALEN); err = brcmf_fil_iovar_data_get(ifp, "tdls_sta_info", diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil.h index b20fc0f82a48..6b72df17744e 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil.h @@ -70,6 +70,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_GET_PKTCNTS 137 #define BRCMF_C_GET_BANDLIST 140 #define BRCMF_C_SET_SCB_TIMEOUT 158 #define BRCMF_C_GET_ASSOCLIST 159 diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h index 94d34ad3a6e6..0b1e46d67e84 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h @@ -751,4 +751,21 @@ struct brcmf_pno_scanresults_le { __le32 count; }; +/** + * struct brcmf_pktcnt_le - packet counters. + * + * @rx_good_pkt: packets (MSDUs & MMPDUs) received from this station + * @rx_bad_pkt: failed rx packets + * @tx_good_pkt: packets (MSDUs & MMPDUs) transmitted to this station + * @tx_bad_pkt: failed tx packets + * @rx_ocast_good_pkt: unicast packets destined for others + */ +struct brcmf_pktcnt_le { + __le32 rx_good_pkt; + __le32 rx_bad_pkt; + __le32 tx_good_pkt; + __le32 tx_bad_pkt; + __le32 rx_ocast_good_pkt; +}; + #endif /* FWIL_TYPES_H_ */ From 48ed16e86b282309afa1b911271cb39fbcfb9f06 Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Sat, 2 Jan 2016 09:41:38 +0100 Subject: [PATCH 13/52] brcmfmac: Add support for scheduled scan mac randomization Scheduled scan be requested to use mac randomization. This patch checks the flags and enables the randomization if desired. Reviewed-by: Arend Van Spriel Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo --- .../broadcom/brcm80211/brcmfmac/cfg80211.c | 54 ++++++++++++++++--- .../broadcom/brcm80211/brcmfmac/feature.c | 10 ++++ .../broadcom/brcm80211/brcmfmac/feature.h | 4 +- .../broadcom/brcm80211/brcmfmac/fwil_types.h | 17 ++++++ 4 files changed, 77 insertions(+), 8 deletions(-) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c index 6b9339b83abc..05843b783d7f 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c @@ -3544,9 +3544,14 @@ static int brcmf_dev_pno_clean(struct net_device *ndev) return ret; } -static int brcmf_dev_pno_config(struct net_device *ndev) +static int brcmf_dev_pno_config(struct brcmf_if *ifp, + struct cfg80211_sched_scan_request *request) { struct brcmf_pno_param_le pfn_param; + struct brcmf_pno_macaddr_le pfn_mac; + s32 err; + u8 *mac_mask; + int i; memset(&pfn_param, 0, sizeof(pfn_param)); pfn_param.version = cpu_to_le32(BRCMF_PNO_VERSION); @@ -3559,8 +3564,37 @@ static int brcmf_dev_pno_config(struct net_device *ndev) /* set up pno scan fr */ pfn_param.scan_freq = cpu_to_le32(BRCMF_PNO_TIME); - return brcmf_fil_iovar_data_set(netdev_priv(ndev), "pfn_set", - &pfn_param, sizeof(pfn_param)); + err = brcmf_fil_iovar_data_set(ifp, "pfn_set", &pfn_param, + sizeof(pfn_param)); + if (err) { + brcmf_err("pfn_set failed, err=%d\n", err); + return err; + } + + /* Find out if mac randomization should be turned on */ + if (!(request->flags & NL80211_SCAN_FLAG_RANDOM_ADDR)) + return 0; + + pfn_mac.version = BRCMF_PFN_MACADDR_CFG_VER; + pfn_mac.flags = BRCMF_PFN_MAC_OUI_ONLY | BRCMF_PFN_SET_MAC_UNASSOC; + + memcpy(pfn_mac.mac, request->mac_addr, ETH_ALEN); + mac_mask = request->mac_addr_mask; + for (i = 0; i < ETH_ALEN; i++) { + pfn_mac.mac[i] &= mac_mask[i]; + pfn_mac.mac[i] |= get_random_int() & ~(mac_mask[i]); + } + /* Clear multi bit */ + pfn_mac.mac[0] &= 0xFE; + /* Set locally administered */ + pfn_mac.mac[0] |= 0x02; + + err = brcmf_fil_iovar_data_set(ifp, "pfn_macaddr", &pfn_mac, + sizeof(pfn_mac)); + if (err) + brcmf_err("pfn_macaddr failed, err=%d\n", err); + + return err; } static int @@ -3614,11 +3648,8 @@ brcmf_cfg80211_sched_scan_start(struct wiphy *wiphy, } /* configure pno */ - ret = brcmf_dev_pno_config(ndev); - if (ret < 0) { - brcmf_err("PNO setup failed!! ret=%d\n", ret); + if (brcmf_dev_pno_config(ifp, request)) return -EINVAL; - } /* configure each match set */ for (i = 0; i < request->n_match_sets; i++) { @@ -6455,6 +6486,15 @@ struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr, goto wiphy_unreg_out; } + /* Fill in some of the advertised nl80211 supported features */ + if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_SCAN_RANDOM_MAC)) { + wiphy->features |= NL80211_FEATURE_SCHED_SCAN_RANDOM_MAC_ADDR; +#ifdef CONFIG_PM + if (wiphy->wowlan->flags & WIPHY_WOWLAN_NET_DETECT) + wiphy->features |= NL80211_FEATURE_ND_RANDOM_MAC_ADDR; +#endif + } + return cfg; wiphy_unreg_out: diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c index d9d1ca4b93ec..e7ac8a294bb7 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c @@ -18,10 +18,12 @@ #include #include +#include #include "core.h" #include "bus.h" #include "debug.h" #include "fwil.h" +#include "fwil_types.h" #include "feature.h" @@ -129,6 +131,8 @@ static void brcmf_feat_iovar_int_set(struct brcmf_if *ifp, void brcmf_feat_attach(struct brcmf_pub *drvr) { struct brcmf_if *ifp = brcmf_get_ifp(drvr, 0); + struct brcmf_pno_macaddr_le pfn_mac; + s32 err; brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_MCHAN, "mchan"); brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_PNO, "pfn"); @@ -140,6 +144,12 @@ void brcmf_feat_attach(struct brcmf_pub *drvr) brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_RSDB, "rsdb_mode"); brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_TDLS, "tdls_enable"); + pfn_mac.version = BRCMF_PFN_MACADDR_CFG_VER; + err = brcmf_fil_iovar_data_get(ifp, "pfn_macaddr", &pfn_mac, + sizeof(pfn_mac)); + if (!err) + ifp->drvr->feat_flags |= BIT(BRCMF_FEAT_SCAN_RANDOM_MAC); + if (brcmf_feature_disable) { brcmf_dbg(INFO, "Features: 0x%02x, disable: 0x%02x\n", ifp->drvr->feat_flags, brcmf_feature_disable); diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.h index 13906568bc2d..2e2479d41337 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.h @@ -26,6 +26,7 @@ * P2P: peer-to-peer * RSDB: Real Simultaneous Dual Band * TDLS: Tunneled Direct Link Setup + * SCAN_RANDOM_MAC: Random MAC during (net detect) scheduled scan. */ #define BRCMF_FEAT_LIST \ BRCMF_FEAT_DEF(MBSS) \ @@ -34,7 +35,8 @@ BRCMF_FEAT_DEF(WOWL) \ BRCMF_FEAT_DEF(P2P) \ BRCMF_FEAT_DEF(RSDB) \ - BRCMF_FEAT_DEF(TDLS) + BRCMF_FEAT_DEF(TDLS) \ + BRCMF_FEAT_DEF(SCAN_RANDOM_MAC) /* * Quirks: diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h index 0b1e46d67e84..bf2df49d7098 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h @@ -128,6 +128,10 @@ #define BRCMF_MAXPMKID 16 /* max # PMKID cache entries */ +#define BRCMF_PFN_MACADDR_CFG_VER 1 +#define BRCMF_PFN_MAC_OUI_ONLY BIT(0) +#define BRCMF_PFN_SET_MAC_UNASSOC BIT(1) + /* join preference types for join_pref iovar */ enum brcmf_join_pref_types { BRCMF_JOIN_PREF_RSSI = 1, @@ -751,6 +755,19 @@ struct brcmf_pno_scanresults_le { __le32 count; }; +/** + * struct brcmf_pno_macaddr_le - to configure PNO macaddr randomization. + * + * @version: PNO version identifier. + * @flags: Flags defining how mac addrss should be used. + * @mac: MAC address. + */ +struct brcmf_pno_macaddr_le { + u8 version; + u8 flags; + u8 mac[ETH_ALEN]; +}; + /** * struct brcmf_pktcnt_le - packet counters. * From ec64241c9fd2873979cf9c05eeaaa4cabe12032a Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Sat, 2 Jan 2016 09:41:39 +0100 Subject: [PATCH 14/52] brcmfmac: obtain feature info using 'cap' firmware command Several features in the driver directly map to a firmware feature listed in response of the 'cap' firmware command. For those features this response will be examined instead of attempting individual firmware commands. Reviewed-by: Hante Meuleman Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Arend van Spriel Reviewed-by: Mathy Vanhoef Signed-off-by: Kalle Valo --- .../broadcom/brcm80211/brcmfmac/feature.c | 51 +++++++++++-------- 1 file changed, 30 insertions(+), 21 deletions(-) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c index e7ac8a294bb7..d41f343f4838 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c @@ -42,6 +42,17 @@ static const char *brcmf_feat_names[] = { }; #undef BRCMF_FEAT_DEF +struct brcmf_feat_fwcap { + enum brcmf_feat_id feature; + const char * const fwcap_id; +}; + +static const struct brcmf_feat_fwcap brcmf_fwcap_map[] = { + { BRCMF_FEAT_MBSS, "mbss" }, + { BRCMF_FEAT_MCHAN, "mchan" }, + { BRCMF_FEAT_P2P, "p2p" }, +}; + #ifdef DEBUG /* * expand quirk list to array of quirk strings. @@ -106,25 +117,22 @@ static void brcmf_feat_iovar_int_get(struct brcmf_if *ifp, } } -/** - * brcmf_feat_iovar_int_set() - determine feature through iovar set. - * - * @ifp: interface to query. - * @id: feature id. - * @name: iovar name. - */ -static void brcmf_feat_iovar_int_set(struct brcmf_if *ifp, - enum brcmf_feat_id id, char *name, u32 val) +static void brcmf_feat_firmware_capabilities(struct brcmf_if *ifp) { - int err; + char caps[256]; + enum brcmf_feat_id id; + int i; - err = brcmf_fil_iovar_int_set(ifp, name, val); - if (err == 0) { - brcmf_dbg(INFO, "enabling feature: %s\n", brcmf_feat_names[id]); - ifp->drvr->feat_flags |= BIT(id); - } else { - brcmf_dbg(TRACE, "%s feature check failed: %d\n", - brcmf_feat_names[id], err); + brcmf_fil_iovar_data_get(ifp, "cap", caps, sizeof(caps)); + brcmf_dbg(INFO, "[ %s]\n", caps); + + for (i = 0; i < ARRAY_SIZE(brcmf_fwcap_map); i++) { + if (strnstr(caps, brcmf_fwcap_map[i].fwcap_id, sizeof(caps))) { + id = brcmf_fwcap_map[i].feature; + brcmf_dbg(INFO, "enabling feature: %s\n", + brcmf_feat_names[id]); + ifp->drvr->feat_flags |= BIT(id); + } } } @@ -134,13 +142,14 @@ void brcmf_feat_attach(struct brcmf_pub *drvr) struct brcmf_pno_macaddr_le pfn_mac; s32 err; - brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_MCHAN, "mchan"); + brcmf_feat_firmware_capabilities(ifp); + brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_PNO, "pfn"); if (drvr->bus_if->wowl_supported) brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_WOWL, "wowl"); - if (drvr->bus_if->chip != BRCM_CC_43362_CHIP_ID) - brcmf_feat_iovar_int_set(ifp, BRCMF_FEAT_MBSS, "mbss", 0); - brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_P2P, "p2p"); + /* MBSS does not work for 43362 */ + if (drvr->bus_if->chip == BRCM_CC_43362_CHIP_ID) + ifp->drvr->feat_flags &= ~BIT(BRCMF_FEAT_MBSS); brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_RSDB, "rsdb_mode"); brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_TDLS, "tdls_enable"); From 6a98d64a27de16f88b97a493577a5fb9d18ec000 Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Sat, 2 Jan 2016 09:41:40 +0100 Subject: [PATCH 15/52] brcmfmac: Fix warn trace on module unload while in ibss mode When the driver is being unloaded a situation can occur where the wirelesss core (cfg80211) wants to remove the ibss, but the state of brcmfmac has already been set to down. When an error is returned in that situation then that will result in a stack trace on removal of the wiphy object. This is avoided by returning 0 when device is down on a leave_ibss call. Reviewed-by: Arend Van Spriel Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo --- .../net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c index 05843b783d7f..dc14dd483779 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c @@ -1448,8 +1448,13 @@ brcmf_cfg80211_leave_ibss(struct wiphy *wiphy, struct net_device *ndev) struct brcmf_if *ifp = netdev_priv(ndev); brcmf_dbg(TRACE, "Enter\n"); - if (!check_vif_up(ifp->vif)) - return -EIO; + if (!check_vif_up(ifp->vif)) { + /* When driver is being unloaded, it can end up here. If an + * error is returned then later on a debug trace in the wireless + * core module will be printed. To avoid this 0 is returned. + */ + return 0; + } brcmf_link_down(ifp->vif, WLAN_REASON_DEAUTH_LEAVING); From 7d34b05605676736c6695ccdcec547055d07468f Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Sat, 2 Jan 2016 09:41:41 +0100 Subject: [PATCH 16/52] brcmfmac: Move all module parameters to one place Module parameters are defined in several files. Move them in one place and make them device specific or global. This makes it easier to override device specific settings by external data like platform data in the future. Reviewed-by: Arend Van Spriel Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo --- .../broadcom/brcm80211/brcmfmac/bcmsdh.c | 12 ++-- .../broadcom/brcm80211/brcmfmac/cfg80211.c | 13 ++-- .../broadcom/brcm80211/brcmfmac/common.c | 63 +++++++++++++++++++ .../broadcom/brcm80211/brcmfmac/common.h | 43 +++++++++++++ .../broadcom/brcm80211/brcmfmac/core.c | 22 +++---- .../broadcom/brcm80211/brcmfmac/core.h | 5 +- .../broadcom/brcm80211/brcmfmac/feature.c | 13 ++-- .../broadcom/brcm80211/brcmfmac/firmware.c | 16 ++--- .../broadcom/brcm80211/brcmfmac/fwsignal.c | 9 +-- 9 files changed, 144 insertions(+), 52 deletions(-) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c index 410a6645d316..53637399bb99 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c @@ -47,6 +47,8 @@ #include "debug.h" #include "sdio.h" #include "of.h" +#include "core.h" +#include "common.h" #define SDIOH_API_ACCESS_RETRY_LIMIT 2 @@ -57,7 +59,6 @@ /* Maximum milliseconds to wait for F2 to come up */ #define SDIO_WAIT_F2RDY 3000 -#define BRCMF_DEFAULT_TXGLOM_SIZE 32 /* max tx frames in glom chain */ #define BRCMF_DEFAULT_RXGLOM_SIZE 32 /* max rx frames in glom chain */ struct brcmf_sdiod_freezer { @@ -68,10 +69,6 @@ struct brcmf_sdiod_freezer { struct completion resumed; }; -static int brcmf_sdiod_txglomsz = BRCMF_DEFAULT_TXGLOM_SIZE; -module_param_named(txglomsz, brcmf_sdiod_txglomsz, int, 0); -MODULE_PARM_DESC(txglomsz, "maximum tx packet chain size [SDIO]"); - static irqreturn_t brcmf_sdiod_oob_irqhandler(int irq, void *dev_id) { struct brcmf_bus *bus_if = dev_get_drvdata(dev_id); @@ -890,7 +887,8 @@ static void brcmf_sdiod_sgtable_alloc(struct brcmf_sdio_dev *sdiodev) if (!sdiodev->sg_support) return; - nents = max_t(uint, BRCMF_DEFAULT_RXGLOM_SIZE, brcmf_sdiod_txglomsz); + nents = max_t(uint, BRCMF_DEFAULT_RXGLOM_SIZE, + sdiodev->bus_if->drvr->settings->sdiod_txglomsz); nents += (nents >> 4) + 1; WARN_ON(nents > sdiodev->max_segment_count); @@ -902,7 +900,7 @@ static void brcmf_sdiod_sgtable_alloc(struct brcmf_sdio_dev *sdiodev) sdiodev->sg_support = false; } - sdiodev->txglomsz = brcmf_sdiod_txglomsz; + sdiodev->txglomsz = sdiodev->bus_if->drvr->settings->sdiod_txglomsz; } #ifdef CONFIG_PM_SLEEP diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c index dc14dd483779..6a7759fcbd86 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c @@ -236,10 +236,6 @@ struct parsed_vndr_ies { struct parsed_vndr_ie_info ie_info[VNDR_IE_PARSE_LIMIT]; }; -static int brcmf_roamoff; -module_param_named(roamoff, brcmf_roamoff, int, S_IRUSR); -MODULE_PARM_DESC(roamoff, "do not use internal roaming engine"); - static u16 chandef_to_chanspec(struct brcmu_d11inf *d11inf, struct cfg80211_chan_def *ch) @@ -5395,7 +5391,7 @@ static s32 brcmf_dongle_roam(struct brcmf_if *ifp) __le32 roam_delta[2]; /* Configure beacon timeout value based upon roaming setting */ - if (brcmf_roamoff) + if (ifp->drvr->settings->roamoff) bcn_timeout = BRCMF_DEFAULT_BCN_TIMEOUT_ROAM_OFF; else bcn_timeout = BRCMF_DEFAULT_BCN_TIMEOUT_ROAM_ON; @@ -5409,8 +5405,9 @@ static s32 brcmf_dongle_roam(struct brcmf_if *ifp) * roaming. */ brcmf_dbg(INFO, "Internal Roaming = %s\n", - brcmf_roamoff ? "Off" : "On"); - err = brcmf_fil_iovar_int_set(ifp, "roam_off", !!(brcmf_roamoff)); + ifp->drvr->settings->roamoff ? "Off" : "On"); + err = brcmf_fil_iovar_int_set(ifp, "roam_off", + ifp->drvr->settings->roamoff); if (err) { brcmf_err("roam_off error (%d)\n", err); goto roam_setup_done; @@ -6082,7 +6079,7 @@ static int brcmf_setup_wiphy(struct wiphy *wiphy, struct brcmf_if *ifp) WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL; if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_TDLS)) wiphy->flags |= WIPHY_FLAG_SUPPORTS_TDLS; - if (!brcmf_roamoff) + if (!ifp->drvr->settings->roamoff) wiphy->flags |= WIPHY_FLAG_SUPPORTS_FW_ROAM; wiphy->mgmt_stypes = brcmf_txrx_stypes; wiphy->max_remain_on_channel_duration = 5000; diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c index 474de118d0b4..bb9e2b3f5012 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c @@ -35,6 +35,40 @@ const u8 ALLFFMAC[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; /* boost value for RSSI_DELTA in preferred join selection */ #define BRCMF_JOIN_PREF_RSSI_BOOST 8 +#define BRCMF_DEFAULT_TXGLOM_SIZE 32 /* max tx frames in glom chain */ + +static int brcmf_sdiod_txglomsz = BRCMF_DEFAULT_TXGLOM_SIZE; +module_param_named(txglomsz, brcmf_sdiod_txglomsz, int, 0); +MODULE_PARM_DESC(txglomsz, "Maximum tx packet chain size [SDIO]"); + +/* Debug level configuration. See debug.h for bits, sysfs modifiable */ +int brcmf_msg_level; +module_param_named(debug, brcmf_msg_level, int, S_IRUSR | S_IWUSR); +MODULE_PARM_DESC(debug, "Level of debug output"); + +static int brcmf_p2p_enable; +module_param_named(p2pon, brcmf_p2p_enable, int, 0); +MODULE_PARM_DESC(p2pon, "Enable legacy p2p management functionality"); + +static int brcmf_feature_disable; +module_param_named(feature_disable, brcmf_feature_disable, int, 0); +MODULE_PARM_DESC(feature_disable, "Disable features"); + +static char brcmf_firmware_path[BRCMF_FW_ALTPATH_LEN]; +module_param_string(alternative_fw_path, brcmf_firmware_path, + BRCMF_FW_ALTPATH_LEN, S_IRUSR); +MODULE_PARM_DESC(alternative_fw_path, "Alternative firmware path"); + +static int brcmf_fcmode; +module_param_named(fcmode, brcmf_fcmode, int, 0); +MODULE_PARM_DESC(fcmode, "Mode of firmware signalled flow control"); + +static int brcmf_roamoff; +module_param_named(roamoff, brcmf_roamoff, int, S_IRUSR); +MODULE_PARM_DESC(roamoff, "Do not use internal roaming engine"); + +struct brcmf_mp_global_t brcmf_mp_global; + int brcmf_c_preinit_dcmds(struct brcmf_if *ifp) { s8 eventmask[BRCMF_EVENTING_MASK_LEN]; @@ -178,3 +212,32 @@ void __brcmf_dbg(u32 level, const char *func, const char *fmt, ...) va_end(args); } #endif + +void brcmf_mp_attach(void) +{ + strlcpy(brcmf_mp_global.firmware_path, brcmf_firmware_path, + BRCMF_FW_ALTPATH_LEN); +} + +int brcmf_mp_device_attach(struct brcmf_pub *drvr) +{ + drvr->settings = kzalloc(sizeof(*drvr->settings), GFP_ATOMIC); + if (!drvr->settings) { + brcmf_err("Failed to alloca storage space for settings\n"); + return -ENOMEM; + } + + drvr->settings->sdiod_txglomsz = brcmf_sdiod_txglomsz; + drvr->settings->p2p_enable = !!brcmf_p2p_enable; + drvr->settings->feature_disable = brcmf_feature_disable; + drvr->settings->fcmode = brcmf_fcmode; + drvr->settings->roamoff = !!brcmf_roamoff; + + return 0; +} + +void brcmf_mp_device_detach(struct brcmf_pub *drvr) +{ + kfree(drvr->settings); +} + diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.h index 21c7488b4732..abe3764669a6 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.h @@ -17,6 +17,49 @@ extern const u8 ALLFFMAC[ETH_ALEN]; +#define BRCMF_FW_ALTPATH_LEN 256 + +/* Definitions for the module global and device specific settings are defined + * here. Two structs are used for them. brcmf_mp_global_t and brcmf_mp_device. + * The mp_global is instantiated once in a global struct and gets initialized + * by the common_attach function which should be called before any other + * (module) initiliazation takes place. The device specific settings is part + * of the drvr struct and should be initialized on every brcmf_attach. + */ + +/** + * struct brcmf_mp_global_t - Global module paramaters. + * + * @firmware_path: Alternative firmware path. + */ +struct brcmf_mp_global_t { + char firmware_path[BRCMF_FW_ALTPATH_LEN]; +}; + +extern struct brcmf_mp_global_t brcmf_mp_global; + +/** + * struct brcmf_mp_device - Device module paramaters. + * + * @sdiod_txglomsz: SDIO txglom size. + * @joinboost_5g_rssi: 5g rssi booost for preferred join selection. + * @p2p_enable: Legacy P2P0 enable (old wpa_supplicant). + * @feature_disable: Feature_disable bitmask. + * @fcmode: FWS flow control. + * @roamoff: Firmware roaming off? + */ +struct brcmf_mp_device { + int sdiod_txglomsz; + int joinboost_5g_rssi; + bool p2p_enable; + int feature_disable; + int fcmode; + bool roamoff; +}; + +void brcmf_mp_attach(void); +int brcmf_mp_device_attach(struct brcmf_pub *drvr); +void brcmf_mp_device_detach(struct brcmf_pub *drvr); /* Sets dongle media info (drv_version, mac address). */ int brcmf_c_preinit_dcmds(struct brcmf_if *ifp); diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c index 4c8f7bf4227c..3fa7bc5ce4b7 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c @@ -57,16 +57,6 @@ MODULE_LICENSE("Dual BSD/GPL"); #define BRCMF_BSSIDX_INVALID -1 -/* Error bits */ -int brcmf_msg_level; -module_param_named(debug, brcmf_msg_level, int, S_IRUSR | S_IWUSR); -MODULE_PARM_DESC(debug, "level of debug output"); - -/* P2P0 enable */ -static int brcmf_p2p_enable; -module_param_named(p2pon, brcmf_p2p_enable, int, 0); -MODULE_PARM_DESC(p2pon, "enable legacy p2p management functionality"); - char *brcmf_ifname(struct brcmf_if *ifp) { if (!ifp) @@ -827,7 +817,7 @@ struct brcmf_if *brcmf_add_if(struct brcmf_pub *drvr, s32 bsscfgidx, s32 ifidx, } } - if (!brcmf_p2p_enable && is_p2pdev) { + if (!drvr->settings->p2p_enable && is_p2pdev) { /* this is P2P_DEVICE interface */ brcmf_dbg(INFO, "allocate non-netdev interface\n"); ifp = kzalloc(sizeof(*ifp), GFP_KERNEL); @@ -1058,6 +1048,10 @@ int brcmf_attach(struct device *dev) drvr->bus_if = dev_get_drvdata(dev); drvr->bus_if->drvr = drvr; + /* Initialize device specific settings */ + if (brcmf_mp_device_attach(drvr)) + goto fail; + /* attach debug facilities */ brcmf_debug_attach(drvr); @@ -1150,7 +1144,7 @@ int brcmf_bus_start(struct device *dev) brcmf_fws_add_interface(ifp); drvr->config = brcmf_cfg80211_attach(drvr, bus_if->dev, - brcmf_p2p_enable); + drvr->settings->p2p_enable); if (drvr->config == NULL) { ret = -ENOMEM; goto fail; @@ -1158,7 +1152,7 @@ int brcmf_bus_start(struct device *dev) ret = brcmf_net_attach(ifp, false); - if ((!ret) && (brcmf_p2p_enable)) { + if ((!ret) && (drvr->settings->p2p_enable)) { p2p_ifp = drvr->iflist[1]; if (p2p_ifp) ret = brcmf_net_p2p_attach(p2p_ifp); @@ -1260,6 +1254,8 @@ void brcmf_detach(struct device *dev) brcmf_proto_detach(drvr); + brcmf_mp_device_detach(drvr); + brcmf_debug_detach(drvr); bus_if->drvr = NULL; kfree(drvr); diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.h index 6018af72bab1..8f39435f976f 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.h @@ -69,8 +69,8 @@ struct brcmf_ampdu_rx_reorder { /* Forward decls for struct brcmf_pub (see below) */ struct brcmf_proto; /* device communication protocol info */ -struct brcmf_cfg80211_dev; /* cfg80211 device info */ -struct brcmf_fws_info; /* firmware signalling info */ +struct brcmf_fws_info; /* firmware signalling info */ +struct brcmf_mp_device; /* module paramateres, device specific */ /* * struct brcmf_rev_info @@ -143,6 +143,7 @@ struct brcmf_pub { #endif struct notifier_block inetaddr_notifier; + struct brcmf_mp_device *settings; }; /* forward declarations */ diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c index d41f343f4838..1ffa95f1b8d2 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c @@ -25,13 +25,9 @@ #include "fwil.h" #include "fwil_types.h" #include "feature.h" +#include "common.h" -/* Module param feature_disable (global for all devices) */ -static int brcmf_feature_disable; -module_param_named(feature_disable, brcmf_feature_disable, int, 0); -MODULE_PARM_DESC(feature_disable, "Disable features"); - /* * expand feature list to array of feature strings. */ @@ -159,10 +155,11 @@ void brcmf_feat_attach(struct brcmf_pub *drvr) if (!err) ifp->drvr->feat_flags |= BIT(BRCMF_FEAT_SCAN_RANDOM_MAC); - if (brcmf_feature_disable) { + if (drvr->settings->feature_disable) { brcmf_dbg(INFO, "Features: 0x%02x, disable: 0x%02x\n", - ifp->drvr->feat_flags, brcmf_feature_disable); - ifp->drvr->feat_flags &= ~brcmf_feature_disable; + ifp->drvr->feat_flags, + drvr->settings->feature_disable); + ifp->drvr->feat_flags &= ~drvr->settings->feature_disable; } /* set chip related quirks */ diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.c index 1e4d5f663036..1365c12b78fc 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/firmware.c @@ -23,15 +23,13 @@ #include "debug.h" #include "firmware.h" +#include "core.h" +#include "common.h" #define BRCMF_FW_MAX_NVRAM_SIZE 64000 #define BRCMF_FW_NVRAM_DEVPATH_LEN 19 /* devpath0=pcie/1/4/ */ #define BRCMF_FW_NVRAM_PCIEDEV_LEN 10 /* pcie/1/4/ + \0 */ -static char brcmf_firmware_path[BRCMF_FW_NAME_LEN]; -module_param_string(alternative_fw_path, brcmf_firmware_path, - BRCMF_FW_NAME_LEN, 0440); - enum nvram_parser_state { IDLE, KEY, @@ -559,13 +557,15 @@ int brcmf_fw_map_chip_to_name(u32 chip, u32 chiprev, } /* check if firmware path is provided by module parameter */ - if (brcmf_firmware_path[0] != '\0') { - strlcpy(fw_name, brcmf_firmware_path, BRCMF_FW_NAME_LEN); + if (brcmf_mp_global.firmware_path[0] != '\0') { + strlcpy(fw_name, brcmf_mp_global.firmware_path, + BRCMF_FW_NAME_LEN); if ((nvram_name) && (mapping_table[i].nvram)) - strlcpy(nvram_name, brcmf_firmware_path, + strlcpy(nvram_name, brcmf_mp_global.firmware_path, BRCMF_FW_NAME_LEN); - end = brcmf_firmware_path[strlen(brcmf_firmware_path) - 1]; + end = brcmf_mp_global.firmware_path[ + strlen(brcmf_mp_global.firmware_path) - 1]; if (end != '/') { strlcat(fw_name, "/", BRCMF_FW_NAME_LEN); if ((nvram_name) && (mapping_table[i].nvram)) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.c index e5f5fac9f9b3..f82c9ab5480b 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.c @@ -36,6 +36,7 @@ #include "p2p.h" #include "cfg80211.h" #include "proto.h" +#include "common.h" /** * DOC: Firmware Signalling @@ -521,10 +522,6 @@ static const int brcmf_fws_prio2fifo[] = { 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; @@ -2134,10 +2131,10 @@ int brcmf_fws_init(struct brcmf_pub *drvr) /* set linkage back */ fws->drvr = drvr; - fws->fcmode = fcmode; + fws->fcmode = drvr->settings->fcmode; if ((drvr->bus_if->always_use_fws_queue == false) && - (fcmode == BRCMF_FWS_FCMODE_NONE)) { + (fws->fcmode == BRCMF_FWS_FCMODE_NONE)) { fws->avoid_queueing = true; brcmf_dbg(INFO, "FWS queueing will be avoided\n"); return 0; From 8ba83d4daab9b71dd27da3765f34d1ffa1fee2ec Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Sat, 2 Jan 2016 09:41:42 +0100 Subject: [PATCH 17/52] brcmfmac: introduce module parameter to force successful probe The module parameter can be used to ensure the probe succeeds thus claiming the device and allowing post-mortem debugging in case of firmware crash. It is only available when select CONFIG_BRCMDBG. Reviewed-by: Hante Meuleman Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo --- .../wireless/broadcom/brcm80211/brcmfmac/common.c | 11 ++++++++++- .../wireless/broadcom/brcm80211/brcmfmac/common.h | 13 +++++++++++++ .../net/wireless/broadcom/brcm80211/brcmfmac/core.c | 2 ++ 3 files changed, 25 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c index bb9e2b3f5012..4265b50faa98 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c @@ -67,6 +67,13 @@ static int brcmf_roamoff; module_param_named(roamoff, brcmf_roamoff, int, S_IRUSR); MODULE_PARM_DESC(roamoff, "Do not use internal roaming engine"); +#ifdef DEBUG +/* always succeed brcmf_bus_start() */ +static int brcmf_ignore_probe_fail; +module_param_named(ignore_probe_fail, brcmf_ignore_probe_fail, int, 0); +MODULE_PARM_DESC(ignore_probe_fail, "always succeed probe for debugging"); +#endif + struct brcmf_mp_global_t brcmf_mp_global; int brcmf_c_preinit_dcmds(struct brcmf_if *ifp) @@ -232,7 +239,9 @@ int brcmf_mp_device_attach(struct brcmf_pub *drvr) drvr->settings->feature_disable = brcmf_feature_disable; drvr->settings->fcmode = brcmf_fcmode; drvr->settings->roamoff = !!brcmf_roamoff; - +#ifdef DEBUG + drvr->settings->ignore_probe_fail = !!brcmf_ignore_probe_fail; +#endif return 0; } diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.h index abe3764669a6..3b0a63b98e99 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.h @@ -55,11 +55,24 @@ struct brcmf_mp_device { int feature_disable; int fcmode; bool roamoff; + bool ignore_probe_fail; }; void brcmf_mp_attach(void); int brcmf_mp_device_attach(struct brcmf_pub *drvr); void brcmf_mp_device_detach(struct brcmf_pub *drvr); +#ifdef DEBUG +static inline bool brcmf_ignoring_probe_fail(struct brcmf_pub *drvr) +{ + return drvr->settings->ignore_probe_fail; +} +#else +static inline bool brcmf_ignoring_probe_fail(struct brcmf_pub *drvr) +{ + return false; +} +#endif + /* Sets dongle media info (drv_version, mac address). */ int brcmf_c_preinit_dcmds(struct brcmf_if *ifp); diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c index 3fa7bc5ce4b7..7c75b1acdf00 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c @@ -1183,6 +1183,8 @@ int brcmf_bus_start(struct device *dev) brcmf_net_detach(p2p_ifp->ndev); drvr->iflist[0] = NULL; drvr->iflist[1] = NULL; + if (brcmf_ignoring_probe_fail(drvr)) + ret = 0; return ret; } return 0; From 7683fe016c010a20b5f2d88b7d8dad198506f5f7 Mon Sep 17 00:00:00 2001 From: Jia-Ju Bai Date: Mon, 4 Jan 2016 15:55:38 +0800 Subject: [PATCH 18/52] rt2x00pci: Disable memory-write-invalidate when the driver exits The driver calls pci_set_mwi to enable memory-write-invalidate when it is initialized, but does not call pci_clear_mwi when it is removed. Many other drivers calls pci_clear_mwi when pci_set_mwi is called, such as r8169, 8139cp and e1000. This patch adds pci_clear_mwi in error handling and removal procedure, which can fix the problem. Signed-off-by: Jia-Ju Bai Acked-by: Helmut Schaa Signed-off-by: Kalle Valo --- drivers/net/wireless/ralink/rt2x00/rt2x00pci.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/wireless/ralink/rt2x00/rt2x00pci.c b/drivers/net/wireless/ralink/rt2x00/rt2x00pci.c index d93db4b0371b..eb6dbcd4fddf 100644 --- a/drivers/net/wireless/ralink/rt2x00/rt2x00pci.c +++ b/drivers/net/wireless/ralink/rt2x00/rt2x00pci.c @@ -149,6 +149,7 @@ int rt2x00pci_probe(struct pci_dev *pci_dev, const struct rt2x00_ops *ops) ieee80211_free_hw(hw); exit_release_regions: + pci_clear_mwi(pci_dev); pci_release_regions(pci_dev); exit_disable_device: @@ -173,6 +174,7 @@ void rt2x00pci_remove(struct pci_dev *pci_dev) /* * Free the PCI device data. */ + pci_clear_mwi(pci_dev); pci_disable_device(pci_dev); pci_release_regions(pci_dev); } From e33a99e227e430a788467e5a85dc29f6df16b983 Mon Sep 17 00:00:00 2001 From: Peter Oh Date: Thu, 31 Dec 2015 15:26:20 +0200 Subject: [PATCH 19/52] ath10k: set SM power save disabled to default value MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use SMPS disabled as default because FW does not indicate any support of SMPS. This change will help STAs out that don’t support SMPS from sticking on 1SS, since they don’t have method to change it back to multiple chains. This change also should not affect power consumption of STAs supporting SMPS, because they are capable to switch the mode to dynamic or static either at the end of frame sequence or by using SMPS action frame. Signed-off-by: Peter Oh Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/mac.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index b4bdeb07a012..6146a293601a 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -3860,7 +3860,8 @@ static struct ieee80211_sta_ht_cap ath10k_get_ht_cap(struct ath10k *ar) ht_cap.ampdu_density = IEEE80211_HT_MPDU_DENSITY_8; ht_cap.cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40; ht_cap.cap |= IEEE80211_HT_CAP_DSSSCCK40; - ht_cap.cap |= WLAN_HT_CAP_SM_PS_STATIC << IEEE80211_HT_CAP_SM_PS_SHIFT; + ht_cap.cap |= + WLAN_HT_CAP_SM_PS_DISABLED << IEEE80211_HT_CAP_SM_PS_SHIFT; if (ar->ht_cap_info & WMI_HT_CAP_HT20_SGI) ht_cap.cap |= IEEE80211_HT_CAP_SGI_20; From dea16eddb4753129dbcd8dc8d1a58ff0cc4ea38c Mon Sep 17 00:00:00 2001 From: Hamad Kadmany Date: Wed, 16 Dec 2015 17:51:45 +0200 Subject: [PATCH 20/52] wil6210: fix kernel OOPS when stopping interface during Rx traffic When network interface is stopping, some resources may be already released by the network stack, and Rx frames cause kernel OOPS (observed one is in netfilter code) Proper solution is to drop packets pending in reorder buffer. Signed-off-by: Hamad Kadmany Signed-off-by: Vladimir Kondratiev Signed-off-by: Maya Erez Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/wil6210/rx_reorder.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/wil6210/rx_reorder.c b/drivers/net/wireless/ath/wil6210/rx_reorder.c index e3d1be82f314..32031e7a11d5 100644 --- a/drivers/net/wireless/ath/wil6210/rx_reorder.c +++ b/drivers/net/wireless/ath/wil6210/rx_reorder.c @@ -261,9 +261,19 @@ struct wil_tid_ampdu_rx *wil_tid_ampdu_rx_alloc(struct wil6210_priv *wil, void wil_tid_ampdu_rx_free(struct wil6210_priv *wil, struct wil_tid_ampdu_rx *r) { + int i; + if (!r) return; - wil_release_reorder_frames(wil, r, r->head_seq_num + r->buf_size); + + /* Do not pass remaining frames to the network stack - it may be + * not expecting to get any more Rx. Rx from here may lead to + * kernel OOPS since some per-socket accounting info was already + * released. + */ + for (i = 0; i < r->buf_size; i++) + kfree_skb(r->reorder_buf[i]); + kfree(r->reorder_buf); kfree(r->reorder_time); kfree(r); From ea3ade75db690dc47c78a77d71dfd7c2df3bb15d Mon Sep 17 00:00:00 2001 From: Lior David Date: Wed, 16 Dec 2015 17:51:46 +0200 Subject: [PATCH 21/52] wil6210: support for platform specific crash recovery Added a simple interface for platform to perform crash recovery. When firmware crashes, wil driver can notify the platform which can trigger a crash recovery process. During the process the platform can request a ram dump from the wil driver as well as control when firmware recovery will start. This interface allows the platform to implement a more advanced crash recovery, for example to reset dependent subsystems in proper order, or to provide its own notifications during the recovery process. Signed-off-by: Lior David Signed-off-by: Maya Erez Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/wil6210/interrupt.c | 8 +++- drivers/net/wireless/ath/wil6210/pcie_bus.c | 30 ++++++++++++++- drivers/net/wireless/ath/wil6210/wil6210.h | 1 + .../net/wireless/ath/wil6210/wil_crash_dump.c | 3 +- .../net/wireless/ath/wil6210/wil_platform.c | 3 +- .../net/wireless/ath/wil6210/wil_platform.h | 38 +++++++++++++++++-- 6 files changed, 73 insertions(+), 10 deletions(-) diff --git a/drivers/net/wireless/ath/wil6210/interrupt.c b/drivers/net/wireless/ath/wil6210/interrupt.c index 50c136e843c4..4f2ffa5c6e17 100644 --- a/drivers/net/wireless/ath/wil6210/interrupt.c +++ b/drivers/net/wireless/ath/wil6210/interrupt.c @@ -394,9 +394,13 @@ static irqreturn_t wil6210_irq_misc_thread(int irq, void *cookie) wil_fw_core_dump(wil); wil_notify_fw_error(wil); isr &= ~ISR_MISC_FW_ERROR; - wil_fw_error_recovery(wil); + if (wil->platform_ops.notify_crash) { + wil_err(wil, "notify platform driver about FW crash"); + wil->platform_ops.notify_crash(wil->platform_handle); + } else { + wil_fw_error_recovery(wil); + } } - if (isr & ISR_MISC_MBOX_EVT) { wil_dbg_irq(wil, "MBOX event\n"); wmi_recv_cmd(wil); diff --git a/drivers/net/wireless/ath/wil6210/pcie_bus.c b/drivers/net/wireless/ath/wil6210/pcie_bus.c index 1a3142c332e1..e36f2a0c8cb6 100644 --- a/drivers/net/wireless/ath/wil6210/pcie_bus.c +++ b/drivers/net/wireless/ath/wil6210/pcie_bus.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2014 Qualcomm Atheros, Inc. + * Copyright (c) 2012-2015 Qualcomm Atheros, Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -125,11 +125,37 @@ static int wil_if_pcie_disable(struct wil6210_priv *wil) return 0; } +static int wil_platform_rop_ramdump(void *wil_handle, void *buf, uint32_t size) +{ + struct wil6210_priv *wil = wil_handle; + + if (!wil) + return -EINVAL; + + return wil_fw_copy_crash_dump(wil, buf, size); +} + +static int wil_platform_rop_fw_recovery(void *wil_handle) +{ + struct wil6210_priv *wil = wil_handle; + + if (!wil) + return -EINVAL; + + wil_fw_error_recovery(wil); + + return 0; +} + static int wil_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id) { struct wil6210_priv *wil; struct device *dev = &pdev->dev; int rc; + const struct wil_platform_rops rops = { + .ramdump = wil_platform_rop_ramdump, + .fw_recovery = wil_platform_rop_fw_recovery, + }; /* check HW */ dev_info(&pdev->dev, WIL_NAME @@ -154,7 +180,7 @@ static int wil_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id) /* rollback to if_free */ wil->platform_handle = - wil_platform_init(&pdev->dev, &wil->platform_ops); + wil_platform_init(&pdev->dev, &wil->platform_ops, &rops, wil); if (!wil->platform_handle) { rc = -ENODEV; wil_err(wil, "wil_platform_init failed\n"); diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h index ade5f3b8274b..235e205ce2bc 100644 --- a/drivers/net/wireless/ath/wil6210/wil6210.h +++ b/drivers/net/wireless/ath/wil6210/wil6210.h @@ -828,6 +828,7 @@ int wil_can_suspend(struct wil6210_priv *wil, bool is_runtime); int wil_suspend(struct wil6210_priv *wil, bool is_runtime); int wil_resume(struct wil6210_priv *wil, bool is_runtime); +int wil_fw_copy_crash_dump(struct wil6210_priv *wil, void *dest, u32 size); void wil_fw_core_dump(struct wil6210_priv *wil); #endif /* __WIL6210_H__ */ diff --git a/drivers/net/wireless/ath/wil6210/wil_crash_dump.c b/drivers/net/wireless/ath/wil6210/wil_crash_dump.c index 7e70934990ae..b57d280946e0 100644 --- a/drivers/net/wireless/ath/wil6210/wil_crash_dump.c +++ b/drivers/net/wireless/ath/wil6210/wil_crash_dump.c @@ -51,8 +51,7 @@ static int wil_fw_get_crash_dump_bounds(struct wil6210_priv *wil, return 0; } -static int wil_fw_copy_crash_dump(struct wil6210_priv *wil, void *dest, - u32 size) +int wil_fw_copy_crash_dump(struct wil6210_priv *wil, void *dest, u32 size) { int i; const struct fw_map *map; diff --git a/drivers/net/wireless/ath/wil6210/wil_platform.c b/drivers/net/wireless/ath/wil6210/wil_platform.c index 2e831bf20117..4eed05bddb60 100644 --- a/drivers/net/wireless/ath/wil6210/wil_platform.c +++ b/drivers/net/wireless/ath/wil6210/wil_platform.c @@ -33,7 +33,8 @@ void wil_platform_modexit(void) * It returns a handle which is used with the rest of the API * */ -void *wil_platform_init(struct device *dev, struct wil_platform_ops *ops) +void *wil_platform_init(struct device *dev, struct wil_platform_ops *ops, + const struct wil_platform_rops *rops, void *wil_handle) { void *handle = ops; /* to return some non-NULL for 'void' impl. */ diff --git a/drivers/net/wireless/ath/wil6210/wil_platform.h b/drivers/net/wireless/ath/wil6210/wil_platform.h index d7fa19b7886d..9a949d910343 100644 --- a/drivers/net/wireless/ath/wil6210/wil_platform.h +++ b/drivers/net/wireless/ath/wil6210/wil_platform.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014 Qualcomm Atheros, Inc. + * Copyright (c) 2014-2015 Qualcomm Atheros, Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -20,16 +20,48 @@ struct device; /** - * struct wil_platform_ops - wil platform module callbacks + * struct wil_platform_ops - wil platform module calls from this + * driver to platform driver */ struct wil_platform_ops { int (*bus_request)(void *handle, uint32_t kbps /* KBytes/Sec */); int (*suspend)(void *handle); int (*resume)(void *handle); void (*uninit)(void *handle); + int (*notify_crash)(void *handle); }; -void *wil_platform_init(struct device *dev, struct wil_platform_ops *ops); +/** + * struct wil_platform_rops - wil platform module callbacks from + * platform driver to this driver + * @ramdump: store a ramdump from the wil firmware. The platform + * driver may add additional data to the ramdump to + * generate the final crash dump. + * @fw_recovery: start a firmware recovery process. Called as + * part of a crash recovery process which may include other + * related platform subsystems. + */ +struct wil_platform_rops { + int (*ramdump)(void *wil_handle, void *buf, uint32_t size); + int (*fw_recovery)(void *wil_handle); +}; + +/** + * wil_platform_init - initialize the platform driver + * + * @dev - pointer to the wil6210 device + * @ops - structure with platform driver operations. Platform + * driver will fill this structure with function pointers. + * @rops - structure with callbacks from platform driver to + * this driver. The platform driver copies the structure to + * its own storage. Can be NULL if this driver does not + * support crash recovery. + * @wil_handle - context for this driver that will be passed + * when platform driver invokes one of the callbacks in + * rops. May be NULL if rops is NULL. + */ +void *wil_platform_init(struct device *dev, struct wil_platform_ops *ops, + const struct wil_platform_rops *rops, void *wil_handle); int __init wil_platform_modinit(void); void wil_platform_modexit(void); From 50e81e2f0b0a9fdb9e5bd515270680c7c281ce1f Mon Sep 17 00:00:00 2001 From: Pawel Kulakowski Date: Fri, 18 Dec 2015 10:48:57 +0100 Subject: [PATCH 22/52] ath9k: Enable support for cloned SKBS Ath9k driver does not modify tx skbs, so SUPPORTS_CLONED_SKBS flag can be set. Enabling this flag significant reduce number of copy operation during TCP Tx. This is especially noticeable on platforms with slower CPU (lower CPU usage brings profits in better TCP Tx troughput results). Tested on MIPS with 560 MHz clock Without CLONED_SKBS flag: TCP Tx 145 Mb/s (iperf result) __copy_user_common consumes 12.9% of CPU (result from perf tool) 0% CPU Idle With CLONED_SKBS flag: TCP Tx 170 Mb/s (iperf result) __copy_user_common consumes 1.8% of CPU (result from perf tool) 12% CPU Idle Signed-off-by: Pawel Kulakowski Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath9k/init.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c index 2e2b92ba96b8..ab7a1ac37849 100644 --- a/drivers/net/wireless/ath/ath9k/init.c +++ b/drivers/net/wireless/ath/ath9k/init.c @@ -828,6 +828,7 @@ static void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw) ieee80211_hw_set(hw, RX_INCLUDES_FCS); ieee80211_hw_set(hw, HOST_BROADCAST_PS_BUFFERING); ieee80211_hw_set(hw, SUPPORT_FAST_XMIT); + ieee80211_hw_set(hw, SUPPORTS_CLONED_SKBS); if (ath9k_ps_enable) ieee80211_hw_set(hw, SUPPORTS_PS); From 19f2ce3f1114c5d59e5e4457ccdd15464070dc5f Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Sat, 19 Dec 2015 13:59:19 +0300 Subject: [PATCH 23/52] ath9k: fix ath9k_hw_nvram_check_version() There is a type bug so it always returns success. Fixes: 6fa658fd5ab2 ('ath9k: Simplify and fix eeprom endianness swapping') Signed-off-by: Dan Carpenter Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath9k/eeprom.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath9k/eeprom.c b/drivers/net/wireless/ath/ath9k/eeprom.c index f8c5065e5f5f..a7afdeee698c 100644 --- a/drivers/net/wireless/ath/ath9k/eeprom.c +++ b/drivers/net/wireless/ath/ath9k/eeprom.c @@ -206,7 +206,7 @@ bool ath9k_hw_nvram_check_version(struct ath_hw *ah, int version, int minrev) ath_err(common, "Bad EEPROM VER 0x%04x or REV 0x%04x\n", ah->eep_ops->get_eeprom_ver(ah), ah->eep_ops->get_eeprom_rev(ah)); - return -EINVAL; + return false; } return true; From 2ec7752fd9b9fe5924973390b784584e7481f52c Mon Sep 17 00:00:00 2001 From: Fengwei Yin Date: Sun, 20 Dec 2015 21:20:40 +0800 Subject: [PATCH 24/52] wcn36xx: handle rx skb allocation failure to avoid system crash Lawrence reported that git clone could make system crash on a Qualcomm ARM soc based device (DragonBoard, 1G memory without swap) running 64bit Debian. It's turned out the crash is related with rx skb allocation failure. git could consume more than 600MB anonymous memory. And system is in extremely memory shortage case. But driver didn't handle the rx allocation failure case. This patch doesn't submit skb to upper layer if rx skb allocation fails. Instead, it reuse the old skb for rx DMA again. It's more like drop the packets if system is in memory shortage case. With this change, git clone is OOMed instead of system crash. Reported-by: King, Lawrence Signed-off-by: Fengwei Yin Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/wcn36xx/dxe.c | 41 +++++++++++++------------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/drivers/net/wireless/ath/wcn36xx/dxe.c b/drivers/net/wireless/ath/wcn36xx/dxe.c index f8dfa05b290a..473381f483bf 100644 --- a/drivers/net/wireless/ath/wcn36xx/dxe.c +++ b/drivers/net/wireless/ath/wcn36xx/dxe.c @@ -474,36 +474,37 @@ static int wcn36xx_rx_handle_packets(struct wcn36xx *wcn, struct wcn36xx_dxe_desc *dxe = ctl->desc; dma_addr_t dma_addr; struct sk_buff *skb; + int ret = 0, int_mask; + u32 value; + + if (ch->ch_type == WCN36XX_DXE_CH_RX_L) { + value = WCN36XX_DXE_CTRL_RX_L; + int_mask = WCN36XX_DXE_INT_CH1_MASK; + } else { + value = WCN36XX_DXE_CTRL_RX_H; + int_mask = WCN36XX_DXE_INT_CH3_MASK; + } while (!(dxe->ctrl & WCN36XX_DXE_CTRL_VALID_MASK)) { skb = ctl->skb; dma_addr = dxe->dst_addr_l; - wcn36xx_dxe_fill_skb(wcn->dev, ctl); + ret = wcn36xx_dxe_fill_skb(wcn->dev, ctl); + if (0 == ret) { + /* new skb allocation ok. Use the new one and queue + * the old one to network system. + */ + dma_unmap_single(wcn->dev, dma_addr, WCN36XX_PKT_SIZE, + DMA_FROM_DEVICE); + wcn36xx_rx_skb(wcn, skb); + } /* else keep old skb not submitted and use it for rx DMA */ - switch (ch->ch_type) { - case WCN36XX_DXE_CH_RX_L: - dxe->ctrl = WCN36XX_DXE_CTRL_RX_L; - wcn36xx_dxe_write_register(wcn, WCN36XX_DXE_ENCH_ADDR, - WCN36XX_DXE_INT_CH1_MASK); - break; - case WCN36XX_DXE_CH_RX_H: - dxe->ctrl = WCN36XX_DXE_CTRL_RX_H; - wcn36xx_dxe_write_register(wcn, WCN36XX_DXE_ENCH_ADDR, - WCN36XX_DXE_INT_CH3_MASK); - break; - default: - wcn36xx_warn("Unknown channel\n"); - } - - dma_unmap_single(wcn->dev, dma_addr, WCN36XX_PKT_SIZE, - DMA_FROM_DEVICE); - wcn36xx_rx_skb(wcn, skb); + wcn36xx_dxe_write_register(wcn, WCN36XX_DXE_ENCH_ADDR, int_mask); + dxe->ctrl = value; ctl = ctl->next; dxe = ctl->desc; } ch->head_blk_ctl = ctl; - return 0; } From 9d5db23eecdb001ac26610d7a87ccdea566feee9 Mon Sep 17 00:00:00 2001 From: Fengwei Yin Date: Sun, 20 Dec 2015 21:20:41 +0800 Subject: [PATCH 25/52] wcn36xx: split DMA mask register writing. Per comments from Bjorn Andersson , split DMA mask register writing as seperate patch in case we need bi-sect in the furture. Signed-off-by: Fengwei Yin Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/wcn36xx/dxe.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/wcn36xx/dxe.c b/drivers/net/wireless/ath/wcn36xx/dxe.c index 473381f483bf..8643801f31b6 100644 --- a/drivers/net/wireless/ath/wcn36xx/dxe.c +++ b/drivers/net/wireless/ath/wcn36xx/dxe.c @@ -498,11 +498,11 @@ static int wcn36xx_rx_handle_packets(struct wcn36xx *wcn, wcn36xx_rx_skb(wcn, skb); } /* else keep old skb not submitted and use it for rx DMA */ - wcn36xx_dxe_write_register(wcn, WCN36XX_DXE_ENCH_ADDR, int_mask); dxe->ctrl = value; ctl = ctl->next; dxe = ctl->desc; } + wcn36xx_dxe_write_register(wcn, WCN36XX_DXE_ENCH_ADDR, int_mask); ch->head_blk_ctl = ctl; return 0; From a1cdb1c59c8c203de2731fc6910598ed19c97e41 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Sun, 20 Dec 2015 08:45:40 +0200 Subject: [PATCH 26/52] iwlwifi: dvm: fix WoWLAN My commit below introduced a mutex in the transport to prevent concurrent operations. To do so, it added a flag (is_down) to make sure the transport is in the right state. This uncoverred an bug that didn't cause any harm until now: iwldvm calls stop_device and then starts the firmware without calling start_hw in between. While this flow is fine from the device configuration point of view (register, etc...), it is now forbidden by the new is_down flag. This led to this error to appear: iwlwifi 0000:05:00.0: Can't start_fw since the HW hasn't been started and the suspend would fail. This fixes: https://bugzilla.kernel.org/show_bug.cgi?id=109591 CC: [4.3+] Reported-by: Bogdan Bogush Fixes=fa9f3281cbb1 ("iwlwifi: pcie: lock start_hw / start_fw / stop_device") Signed-off-by: Emmanuel Grumbach Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/intel/iwlwifi/dvm/lib.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/lib.c b/drivers/net/wireless/intel/iwlwifi/dvm/lib.c index bee1c03ee259..4841be2aa499 100644 --- a/drivers/net/wireless/intel/iwlwifi/dvm/lib.c +++ b/drivers/net/wireless/intel/iwlwifi/dvm/lib.c @@ -1154,6 +1154,9 @@ int iwlagn_suspend(struct iwl_priv *priv, struct cfg80211_wowlan *wowlan) priv->ucode_loaded = false; iwl_trans_stop_device(priv->trans); + ret = iwl_trans_start_hw(priv->trans); + if (ret) + goto out; priv->wowlan = true; From 006bda75d81fd27a583a3b310e9444fea2aa6ef2 Mon Sep 17 00:00:00 2001 From: Oren Givon Date: Thu, 17 Dec 2015 14:17:00 +0200 Subject: [PATCH 27/52] iwlwifi: update and fix 7265 series PCI IDs Update and fix some 7265 PCI IDs entries. CC: [3.13+] Signed-off-by: Oren Givon Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/intel/iwlwifi/pcie/drv.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/drv.c b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c index af106513d38e..6261a68cae90 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/drv.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c @@ -388,6 +388,7 @@ static const struct pci_device_id iwl_hw_card_ids[] = { {IWL_PCI_DEVICE(0x095B, 0x5310, iwl7265_2ac_cfg)}, {IWL_PCI_DEVICE(0x095B, 0x5302, iwl7265_n_cfg)}, {IWL_PCI_DEVICE(0x095B, 0x5210, iwl7265_2ac_cfg)}, + {IWL_PCI_DEVICE(0x095A, 0x5C10, iwl7265_2ac_cfg)}, {IWL_PCI_DEVICE(0x095A, 0x5012, iwl7265_2ac_cfg)}, {IWL_PCI_DEVICE(0x095A, 0x5412, iwl7265_2ac_cfg)}, {IWL_PCI_DEVICE(0x095A, 0x5410, iwl7265_2ac_cfg)}, @@ -405,10 +406,10 @@ static const struct pci_device_id iwl_hw_card_ids[] = { {IWL_PCI_DEVICE(0x095A, 0x900A, iwl7265_2ac_cfg)}, {IWL_PCI_DEVICE(0x095A, 0x9110, iwl7265_2ac_cfg)}, {IWL_PCI_DEVICE(0x095A, 0x9112, iwl7265_2ac_cfg)}, - {IWL_PCI_DEVICE(0x095A, 0x9210, iwl7265_2ac_cfg)}, + {IWL_PCI_DEVICE(0x095B, 0x9210, iwl7265_2ac_cfg)}, {IWL_PCI_DEVICE(0x095B, 0x9200, iwl7265_2ac_cfg)}, {IWL_PCI_DEVICE(0x095A, 0x9510, iwl7265_2ac_cfg)}, - {IWL_PCI_DEVICE(0x095A, 0x9310, iwl7265_2ac_cfg)}, + {IWL_PCI_DEVICE(0x095B, 0x9310, iwl7265_2ac_cfg)}, {IWL_PCI_DEVICE(0x095A, 0x9410, iwl7265_2ac_cfg)}, {IWL_PCI_DEVICE(0x095A, 0x5020, iwl7265_2n_cfg)}, {IWL_PCI_DEVICE(0x095A, 0x502A, iwl7265_2n_cfg)}, From 1e3c3c3529a7d0455b38f74d287d296cb9c786aa Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Sun, 13 Dec 2015 09:35:30 +0200 Subject: [PATCH 28/52] iwlwifi: mvm: let the firmware choose the antenna for beacons The firmware knows better what antenna to choose. Old firmware still need the setting, so use a flag to know if the driver should choose the antenna or if the firmware can do it iself. Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/intel/iwlwifi/iwl-fw-file.h | 3 +++ drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c | 9 ++++++--- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-fw-file.h b/drivers/net/wireless/intel/iwlwifi/iwl-fw-file.h index d2294ad67023..9f24f990b705 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-fw-file.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-fw-file.h @@ -309,6 +309,8 @@ typedef unsigned int __bitwise__ iwl_ucode_tlv_capa_t; * @IWL_UCODE_TLV_CAPA_EXTENDED_DTS_MEASURE: extended DTS measurement * @IWL_UCODE_TLV_CAPA_SHORT_PM_TIMEOUTS: supports short PM timeouts * @IWL_UCODE_TLV_CAPA_BT_MPLUT_SUPPORT: supports bt-coex Multi-priority LUT + * @IWL_UCODE_TLV_CAPA_BEACON_ANT_SELECTION: firmware will decide on what + * antenna the beacon should be transmitted * * @NUM_IWL_UCODE_TLV_CAPA: number of bits used */ @@ -336,6 +338,7 @@ enum iwl_ucode_tlv_capa { IWL_UCODE_TLV_CAPA_EXTENDED_DTS_MEASURE = (__force iwl_ucode_tlv_capa_t)64, IWL_UCODE_TLV_CAPA_SHORT_PM_TIMEOUTS = (__force iwl_ucode_tlv_capa_t)65, IWL_UCODE_TLV_CAPA_BT_MPLUT_SUPPORT = (__force iwl_ucode_tlv_capa_t)67, + IWL_UCODE_TLV_CAPA_BEACON_ANT_SELECTION = (__force iwl_ucode_tlv_capa_t)71, NUM_IWL_UCODE_TLV_CAPA #ifdef __CHECKER__ diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c index 5e3a7582885b..448b9c927b51 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c @@ -1012,9 +1012,12 @@ static int iwl_mvm_mac_ctxt_send_beacon(struct iwl_mvm *mvm, TX_CMD_FLG_BT_PRIO_POS; beacon_cmd.tx.tx_flags = cpu_to_le32(tx_flags); - mvm->mgmt_last_antenna_idx = - iwl_mvm_next_antenna(mvm, iwl_mvm_get_valid_tx_ant(mvm), - mvm->mgmt_last_antenna_idx); + if (!fw_has_capa(&mvm->fw->ucode_capa, + IWL_UCODE_TLV_CAPA_BEACON_ANT_SELECTION)) { + mvm->mgmt_last_antenna_idx = + iwl_mvm_next_antenna(mvm, iwl_mvm_get_valid_tx_ant(mvm), + mvm->mgmt_last_antenna_idx); + } beacon_cmd.tx.rate_n_flags = cpu_to_le32(BIT(mvm->mgmt_last_antenna_idx) << From 6fa52430f0b3a45a31fb706084288884532e857c Mon Sep 17 00:00:00 2001 From: Matti Gottlieb Date: Mon, 4 Jan 2016 13:38:41 +0200 Subject: [PATCH 29/52] iwlwifi: mvm: change mcc update API New functionality for testing that is not relevant for this driver has been added. This required an API change. Add new cmd & response versions for the MCC update cmd & response. Add new TLV indicating that the FW is using the new API. Signed-off-by: Matti Gottlieb Signed-off-by: Emmanuel Grumbach --- .../net/wireless/intel/iwlwifi/iwl-fw-file.h | 4 ++ .../net/wireless/intel/iwlwifi/mvm/fw-api.h | 66 +++++++++++++++++-- drivers/net/wireless/intel/iwlwifi/mvm/nvm.c | 60 ++++++++++++----- 3 files changed, 107 insertions(+), 23 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-fw-file.h b/drivers/net/wireless/intel/iwlwifi/iwl-fw-file.h index 9f24f990b705..8d37e330f153 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-fw-file.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-fw-file.h @@ -7,6 +7,7 @@ * * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * Copyright(c) 2016 Intel Deutschland GmbH * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -33,6 +34,7 @@ * * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * Copyright(c) 2016 Intel Deutschland GmbH * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -311,6 +313,7 @@ typedef unsigned int __bitwise__ iwl_ucode_tlv_capa_t; * @IWL_UCODE_TLV_CAPA_BT_MPLUT_SUPPORT: supports bt-coex Multi-priority LUT * @IWL_UCODE_TLV_CAPA_BEACON_ANT_SELECTION: firmware will decide on what * antenna the beacon should be transmitted + * @IWL_UCODE_TLV_CAPA_LAR_SUPPORT_V2: support LAR API V2 * * @NUM_IWL_UCODE_TLV_CAPA: number of bits used */ @@ -339,6 +342,7 @@ enum iwl_ucode_tlv_capa { IWL_UCODE_TLV_CAPA_SHORT_PM_TIMEOUTS = (__force iwl_ucode_tlv_capa_t)65, IWL_UCODE_TLV_CAPA_BT_MPLUT_SUPPORT = (__force iwl_ucode_tlv_capa_t)67, IWL_UCODE_TLV_CAPA_BEACON_ANT_SELECTION = (__force iwl_ucode_tlv_capa_t)71, + IWL_UCODE_TLV_CAPA_LAR_SUPPORT_V2 = (__force iwl_ucode_tlv_capa_t)73, NUM_IWL_UCODE_TLV_CAPA #ifdef __CHECKER__ diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api.h b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api.h index 995898c5d017..82049bb139c2 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api.h @@ -7,6 +7,7 @@ * * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * Copyright(c) 2016 Intel Deutschland GmbH * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -33,6 +34,7 @@ * * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * Copyright(c) 2016 Intel Deutschland GmbH * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -1452,7 +1454,7 @@ struct iwl_sf_cfg_cmd { ***********************************/ /** - * struct iwl_mcc_update_cmd - Request the device to update geographic + * struct iwl_mcc_update_cmd_v1 - Request the device to update geographic * regulatory profile according to the given MCC (Mobile Country Code). * The MCC is two letter-code, ascii upper case[A-Z] or '00' for world domain. * 'ZZ' MCC will be used to switch to NVM default profile; in this case, the @@ -1461,14 +1463,34 @@ struct iwl_sf_cfg_cmd { * @source_id: the source from where we got the MCC, see iwl_mcc_source * @reserved: reserved for alignment */ +struct iwl_mcc_update_cmd_v1 { + __le16 mcc; + u8 source_id; + u8 reserved; +} __packed; /* LAR_UPDATE_MCC_CMD_API_S_VER_1 */ + +/** + * struct iwl_mcc_update_cmd - Request the device to update geographic + * regulatory profile according to the given MCC (Mobile Country Code). + * The MCC is two letter-code, ascii upper case[A-Z] or '00' for world domain. + * 'ZZ' MCC will be used to switch to NVM default profile; in this case, the + * MCC in the cmd response will be the relevant MCC in the NVM. + * @mcc: given mobile country code + * @source_id: the source from where we got the MCC, see iwl_mcc_source + * @reserved: reserved for alignment + * @key: integrity key for MCC API OEM testing + * @reserved2: reserved + */ struct iwl_mcc_update_cmd { __le16 mcc; u8 source_id; u8 reserved; -} __packed; /* LAR_UPDATE_MCC_CMD_API_S */ + __le32 key; + __le32 reserved2[5]; +} __packed; /* LAR_UPDATE_MCC_CMD_API_S_VER_2 */ /** - * iwl_mcc_update_resp - response to MCC_UPDATE_CMD. + * iwl_mcc_update_resp_v1 - response to MCC_UPDATE_CMD. * Contains the new channel control profile map, if changed, and the new MCC * (mobile country code). * The new MCC may be different than what was requested in MCC_UPDATE_CMD. @@ -1481,14 +1503,41 @@ struct iwl_mcc_update_cmd { * @channels: channel control data map, DWORD for each channel. Only the first * 16bits are used. */ -struct iwl_mcc_update_resp { +struct iwl_mcc_update_resp_v1 { __le32 status; __le16 mcc; u8 cap; u8 source_id; __le32 n_channels; __le32 channels[0]; -} __packed; /* LAR_UPDATE_MCC_CMD_RESP_S */ +} __packed; /* LAR_UPDATE_MCC_CMD_RESP_S_VER_1 */ + +/** + * iwl_mcc_update_resp - response to MCC_UPDATE_CMD. + * Contains the new channel control profile map, if changed, and the new MCC + * (mobile country code). + * The new MCC may be different than what was requested in MCC_UPDATE_CMD. + * @status: see &enum iwl_mcc_update_status + * @mcc: the new applied MCC + * @cap: capabilities for all channels which matches the MCC + * @source_id: the MCC source, see iwl_mcc_source + * @time: time elapsed from the MCC test start (in 30 seconds TU) + * @reserved: reserved. + * @n_channels: number of channels in @channels_data (may be 14, 39, 50 or 51 + * channels, depending on platform) + * @channels: channel control data map, DWORD for each channel. Only the first + * 16bits are used. + */ +struct iwl_mcc_update_resp { + __le32 status; + __le16 mcc; + u8 cap; + u8 source_id; + __le16 time; + __le16 reserved; + __le32 n_channels; + __le32 channels[0]; +} __packed; /* LAR_UPDATE_MCC_CMD_RESP_S_VER_2 */ /** * struct iwl_mcc_chub_notif - chub notifies of mcc change @@ -1518,6 +1567,9 @@ enum iwl_mcc_update_status { MCC_RESP_NVM_DISABLED, MCC_RESP_ILLEGAL, MCC_RESP_LOW_PRIORITY, + MCC_RESP_TEST_MODE_ACTIVE, + MCC_RESP_TEST_MODE_NOT_ACTIVE, + MCC_RESP_TEST_MODE_DENIAL_OF_SERVICE, }; enum iwl_mcc_source { @@ -1530,7 +1582,9 @@ enum iwl_mcc_source { MCC_SOURCE_RESERVED = 6, MCC_SOURCE_DEFAULT = 7, MCC_SOURCE_UNINITIALIZED = 8, - MCC_SOURCE_GET_CURRENT = 0x10 + MCC_SOURCE_MCC_API = 9, + MCC_SOURCE_GET_CURRENT = 0x10, + MCC_SOURCE_GETTING_MCC_TEST_MODE = 0x11, }; /* DTS measurements */ diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/nvm.c b/drivers/net/wireless/intel/iwlwifi/mvm/nvm.c index d8dcb67b7ff9..e4fe8a66a3a0 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/nvm.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/nvm.c @@ -7,6 +7,7 @@ * * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * Copyright(c) 2016 Intel Deutschland GmbH * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -33,6 +34,7 @@ * * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * Copyright(c) 2016 Intel Deutschland GmbH * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -670,6 +672,7 @@ iwl_mvm_update_mcc(struct iwl_mvm *mvm, const char *alpha2, .source_id = (u8)src_id, }; struct iwl_mcc_update_resp *mcc_resp, *resp_cp = NULL; + struct iwl_mcc_update_resp_v1 *mcc_resp_v1 = NULL; struct iwl_rx_packet *pkt; struct iwl_host_cmd cmd = { .id = MCC_UPDATE_CMD, @@ -681,11 +684,15 @@ iwl_mvm_update_mcc(struct iwl_mvm *mvm, const char *alpha2, u32 status; int resp_len, n_channels; u16 mcc; + bool resp_v2 = fw_has_capa(&mvm->fw->ucode_capa, + IWL_UCODE_TLV_CAPA_LAR_SUPPORT_V2); if (WARN_ON_ONCE(!iwl_mvm_is_lar_supported(mvm))) return ERR_PTR(-EOPNOTSUPP); cmd.len[0] = sizeof(struct iwl_mcc_update_cmd); + if (!resp_v2) + cmd.len[0] = sizeof(struct iwl_mcc_update_cmd_v1); IWL_DEBUG_LAR(mvm, "send MCC update to FW with '%c%c' src = %d\n", alpha2[0], alpha2[1], src_id); @@ -697,31 +704,50 @@ iwl_mvm_update_mcc(struct iwl_mvm *mvm, const char *alpha2, pkt = cmd.resp_pkt; /* Extract MCC response */ - mcc_resp = (void *)pkt->data; - status = le32_to_cpu(mcc_resp->status); - - mcc = le16_to_cpu(mcc_resp->mcc); - - /* W/A for a FW/NVM issue - returns 0x00 for the world domain */ - if (mcc == 0) { - mcc = 0x3030; /* "00" - world */ - mcc_resp->mcc = cpu_to_le16(mcc); + if (resp_v2) { + mcc_resp = (void *)pkt->data; + n_channels = __le32_to_cpu(mcc_resp->n_channels); + } else { + mcc_resp_v1 = (void *)pkt->data; + n_channels = __le32_to_cpu(mcc_resp_v1->n_channels); } - n_channels = __le32_to_cpu(mcc_resp->n_channels); - IWL_DEBUG_LAR(mvm, - "MCC response status: 0x%x. new MCC: 0x%x ('%c%c') change: %d n_chans: %d\n", - status, mcc, mcc >> 8, mcc & 0xff, - !!(status == MCC_RESP_NEW_CHAN_PROFILE), n_channels); + resp_len = sizeof(struct iwl_mcc_update_resp) + n_channels * + sizeof(__le32); - resp_len = sizeof(*mcc_resp) + n_channels * sizeof(__le32); - resp_cp = kmemdup(mcc_resp, resp_len, GFP_KERNEL); + resp_cp = kzalloc(resp_len, GFP_KERNEL); if (!resp_cp) { ret = -ENOMEM; goto exit; } - ret = 0; + if (resp_v2) { + memcpy(resp_cp, mcc_resp, resp_len); + } else { + resp_cp->status = mcc_resp_v1->status; + resp_cp->mcc = mcc_resp_v1->mcc; + resp_cp->cap = mcc_resp_v1->cap; + resp_cp->source_id = mcc_resp_v1->source_id; + resp_cp->n_channels = mcc_resp_v1->n_channels; + memcpy(resp_cp->channels, mcc_resp_v1->channels, + n_channels * sizeof(__le32)); + } + + status = le32_to_cpu(resp_cp->status); + + mcc = le16_to_cpu(resp_cp->mcc); + + /* W/A for a FW/NVM issue - returns 0x00 for the world domain */ + if (mcc == 0) { + mcc = 0x3030; /* "00" - world */ + resp_cp->mcc = cpu_to_le16(mcc); + } + + IWL_DEBUG_LAR(mvm, + "MCC response status: 0x%x. new MCC: 0x%x ('%c%c') change: %d n_chans: %d\n", + status, mcc, mcc >> 8, mcc & 0xff, + !!(status == MCC_RESP_NEW_CHAN_PROFILE), n_channels); + exit: iwl_free_resp(&cmd); if (ret) From 4ca87a5f58454711cc8cc550a7844efebd9bc001 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Sun, 3 Jan 2016 22:23:40 +0200 Subject: [PATCH 30/52] iwlwifi: mvm: reset mvm->scan_type when firmware is started If we don't reset the scan type when the firmware is started, we will think the firmware is still configured after the interface has been brought down. When we will bring it up again, we will not configure the scan type in firmware and it will crash with the following assert: 0x0000100A | ADVANCED_SYSASSERT Fixes: 355346ba3050 ("iwlwifi: mvm: configure scheduled scan according to traffic conditions") Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/intel/iwlwifi/mvm/fw.c | 1 + drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c index e6e80882d86d..4ed5180c547b 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c @@ -943,6 +943,7 @@ int iwl_mvm_up(struct iwl_mvm *mvm) } if (fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_UMAC_SCAN)) { + mvm->scan_type = IWL_SCAN_TYPE_NOT_SET; ret = iwl_mvm_config_scan(mvm); if (ret) goto error; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c index 296b9c5cd1be..2d65040269d7 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c @@ -1002,7 +1002,6 @@ static void iwl_mvm_restart_cleanup(struct iwl_mvm *mvm) mvm->vif_count = 0; mvm->rx_ba_sessions = 0; mvm->fw_dbg_conf = FW_DBG_INVALID; - mvm->scan_type = IWL_SCAN_TYPE_NOT_SET; /* keep statistics ticking */ iwl_mvm_accu_radio_stats(mvm); From ed0450cef00d2c76bcb8778721df947ba7ff4147 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Mon, 4 Jan 2016 10:19:17 +0200 Subject: [PATCH 31/52] iwlwifi: set max firmware version of 7265 to 17 Just like 7260, 7265 will not have firmware releases newer than iwlwifi-7265-17.ucode. 7265D is still supported in latest firmware releases. Fixes: 628a2918afe4 ("iwlwifi: separate firmware version for 7260 devices") Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/intel/iwlwifi/iwl-7000.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-7000.c b/drivers/net/wireless/intel/iwlwifi/iwl-7000.c index fd9064bf389a..d15117cc1fa4 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-7000.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-7000.c @@ -72,7 +72,7 @@ /* Highest firmware API version supported */ #define IWL7260_UCODE_API_MAX 17 -#define IWL7265_UCODE_API_MAX 19 +#define IWL7265_UCODE_API_MAX 17 #define IWL7265D_UCODE_API_MAX 19 /* Oldest version we won't warn about */ From f370f5cffe7ae8b92026c8d6cb2738b4d3aeed9c Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Tue, 5 Jan 2016 16:30:04 +0200 Subject: [PATCH 32/52] iwlwifi: mvm: bump max API to 20 7265D and up are now able to handle -20.ucode. Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/intel/iwlwifi/iwl-7000.c | 2 +- drivers/net/wireless/intel/iwlwifi/iwl-8000.c | 2 +- drivers/net/wireless/intel/iwlwifi/iwl-9000.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-7000.c b/drivers/net/wireless/intel/iwlwifi/iwl-7000.c index d15117cc1fa4..e60cf141ed79 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-7000.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-7000.c @@ -73,7 +73,7 @@ /* Highest firmware API version supported */ #define IWL7260_UCODE_API_MAX 17 #define IWL7265_UCODE_API_MAX 17 -#define IWL7265D_UCODE_API_MAX 19 +#define IWL7265D_UCODE_API_MAX 20 /* Oldest version we won't warn about */ #define IWL7260_UCODE_API_OK 13 diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-8000.c b/drivers/net/wireless/intel/iwlwifi/iwl-8000.c index dee4458b408d..c84a0299d43e 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-8000.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-8000.c @@ -69,7 +69,7 @@ #include "iwl-agn-hw.h" /* Highest firmware API version supported */ -#define IWL8000_UCODE_API_MAX 19 +#define IWL8000_UCODE_API_MAX 20 /* Oldest version we won't warn about */ #define IWL8000_UCODE_API_OK 13 diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-9000.c b/drivers/net/wireless/intel/iwlwifi/iwl-9000.c index 0d2aa1d9a50f..ecbf4822cd69 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-9000.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-9000.c @@ -55,7 +55,7 @@ #include "iwl-agn-hw.h" /* Highest firmware API version supported */ -#define IWL9000_UCODE_API_MAX 16 +#define IWL9000_UCODE_API_MAX 20 /* Oldest version we won't warn about */ #define IWL9000_UCODE_API_OK 13 From 488c28e110e18466c99ffb1e2342498b42d3344e Mon Sep 17 00:00:00 2001 From: Oren Givon Date: Wed, 25 Nov 2015 11:17:41 +0200 Subject: [PATCH 33/52] iwlwifi: nvm: fix loading default NVM file Fix loading the default NVM file, in the case where the requested NVM file isn't found in the file system. Signed-off-by: Oren Givon Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/intel/iwlwifi/mvm/nvm.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/nvm.c b/drivers/net/wireless/intel/iwlwifi/mvm/nvm.c index e4fe8a66a3a0..7a3da2da6fd0 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/nvm.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/nvm.c @@ -642,7 +642,8 @@ int iwl_nvm_init(struct iwl_mvm *mvm, bool read_nvm_from_nic) else mvm->nvm_file_name = nvm_file_C; - if (ret == -EFAULT && mvm->nvm_file_name) { + if ((ret == -EFAULT || ret == -ENOENT) && + mvm->nvm_file_name) { /* in case nvm file was failed try again */ ret = iwl_mvm_read_external_nvm(mvm); if (ret) From ca95ff3a9f9edea919a53a297a4cba178b6cdf5f Mon Sep 17 00:00:00 2001 From: David Spinadel Date: Sun, 3 Jan 2016 17:08:32 +0200 Subject: [PATCH 34/52] iwlwifi: mvm: fix extended dwell time FW adds 10 msec for every dwell time in low band, so we need to set 10 msec less. Don't use extended dwell time when fragmented scan is needed because FW adds 3 msec per probe and it can easily exceed max out of channel time. Fixes: c3e230b167a9 ("iwlwifi: mvm: add extended dwell time") Signed-off-by: David Spinadel Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/intel/iwlwifi/mvm/scan.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/scan.c b/drivers/net/wireless/intel/iwlwifi/mvm/scan.c index bee3201c7116..9a15642f80dd 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/scan.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/scan.c @@ -92,7 +92,7 @@ static struct iwl_mvm_scan_timing_params scan_timing[] = { .dwell_active = 10, .dwell_passive = 110, .dwell_fragmented = 44, - .dwell_extended = 100, + .dwell_extended = 90, .suspend_time = 0, .max_out_time = 0, }, @@ -100,7 +100,7 @@ static struct iwl_mvm_scan_timing_params scan_timing[] = { .dwell_active = 10, .dwell_passive = 110, .dwell_fragmented = 44, - .dwell_extended = 100, + .dwell_extended = 90, .suspend_time = 30, .max_out_time = 120, }, @@ -108,7 +108,7 @@ static struct iwl_mvm_scan_timing_params scan_timing[] = { .dwell_active = 10, .dwell_passive = 110, .dwell_fragmented = 44, - .dwell_extended = 100, + .dwell_extended = 90, .suspend_time = 120, .max_out_time = 120, }, @@ -116,7 +116,6 @@ static struct iwl_mvm_scan_timing_params scan_timing[] = { .dwell_active = 10, .dwell_passive = 110, .dwell_fragmented = 44, - .dwell_extended = 44, .suspend_time = 95, .max_out_time = 44, }, @@ -790,7 +789,8 @@ static int iwl_mvm_scan_lmac_flags(struct iwl_mvm *mvm, #endif if (iwl_mvm_is_regular_scan(params) && - vif->type != NL80211_IFTYPE_P2P_DEVICE) + vif->type != NL80211_IFTYPE_P2P_DEVICE && + params->type != IWL_SCAN_TYPE_FRAGMENTED) flags |= IWL_MVM_LMAC_SCAN_FLAG_EXTENDED_DWELL; return flags; @@ -1072,7 +1072,8 @@ static u32 iwl_mvm_scan_umac_flags(struct iwl_mvm *mvm, #endif if (iwl_mvm_is_regular_scan(params) && - vif->type != NL80211_IFTYPE_P2P_DEVICE) + vif->type != NL80211_IFTYPE_P2P_DEVICE && + params->type != IWL_SCAN_TYPE_FRAGMENTED) flags |= IWL_UMAC_SCAN_GEN_FLAGS_EXTENDED_DWELL; return flags; From 976f15a8ef53144731f7e431c9498aff9392c9cb Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Mon, 28 Dec 2015 15:22:28 +0200 Subject: [PATCH 35/52] iwlwifi: mvm: dump the radio registers when the firmware crashes Dumping the content of the radio registers greatly helps to debug PHY issues, which can lead to TFD queue hang. Signed-off-by: Emmanuel Grumbach --- .../intel/iwlwifi/iwl-fw-error-dump.h | 2 + drivers/net/wireless/intel/iwlwifi/iwl-prph.h | 6 +++ .../net/wireless/intel/iwlwifi/mvm/fw-dbg.c | 40 ++++++++++++++++++- 3 files changed, 46 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-fw-error-dump.h b/drivers/net/wireless/intel/iwlwifi/iwl-fw-error-dump.h index f08a1319fc04..a5aaf6853704 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-fw-error-dump.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-fw-error-dump.h @@ -88,6 +88,7 @@ * &struct iwl_fw_error_dump_rb * @IWL_FW_ERROR_PAGING: UMAC's image memory segments which were * paged to the DRAM. + * @IWL_FW_ERROR_DUMP_RADIO_REG: Dump the radio registers. */ enum iwl_fw_error_dump_type { /* 0 is deprecated */ @@ -103,6 +104,7 @@ enum iwl_fw_error_dump_type { IWL_FW_ERROR_DUMP_ERROR_INFO = 10, IWL_FW_ERROR_DUMP_RB = 11, IWL_FW_ERROR_DUMP_PAGING = 12, + IWL_FW_ERROR_DUMP_RADIO_REG = 13, IWL_FW_ERROR_DUMP_MAX, }; diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-prph.h b/drivers/net/wireless/intel/iwlwifi/iwl-prph.h index 9da7dc49549c..5bde23a472b4 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-prph.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-prph.h @@ -345,6 +345,12 @@ enum secure_load_status_reg { #define TXF_READ_MODIFY_DATA (0xa00448) #define TXF_READ_MODIFY_ADDR (0xa0044c) +/* Radio registers access */ +#define RSP_RADIO_CMD (0xa02804) +#define RSP_RADIO_RDDAT (0xa02814) +#define RADIO_RSP_ADDR_POS (6) +#define RADIO_RSP_RD_CMD (3) + /* FW monitor */ #define MON_BUFF_SAMPLE_CTL (0xa03c00) #define MON_BUFF_BASE_ADDR (0xa03c3c) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.c index f406c76b4302..59450f177c43 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.c @@ -113,6 +113,35 @@ static void iwl_mvm_free_coredump(const void *data) kfree(fw_error_dump); } +#define RADIO_REG_MAX_READ 0x2ad +static void iwl_mvm_read_radio_reg(struct iwl_mvm *mvm, + struct iwl_fw_error_dump_data **dump_data) +{ + u8 *pos = (void *)(*dump_data)->data; + unsigned long flags; + int i; + + if (!iwl_trans_grab_nic_access(mvm->trans, &flags)) + return; + + (*dump_data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_RADIO_REG); + (*dump_data)->len = cpu_to_le32(RADIO_REG_MAX_READ); + + for (i = 0; i < RADIO_REG_MAX_READ; i++) { + u32 rd_cmd = RADIO_RSP_RD_CMD; + + rd_cmd |= i << RADIO_RSP_ADDR_POS; + iwl_write_prph_no_grab(mvm->trans, RSP_RADIO_CMD, rd_cmd); + *pos = (u8)iwl_read_prph_no_grab(mvm->trans, RSP_RADIO_RDDAT); + + pos++; + } + + *dump_data = iwl_fw_error_next_data(*dump_data); + + iwl_trans_release_nic_access(mvm->trans, &flags); +} + static void iwl_mvm_dump_fifos(struct iwl_mvm *mvm, struct iwl_fw_error_dump_data **dump_data) { @@ -401,7 +430,7 @@ void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm) struct iwl_fw_error_dump_trigger_desc *dump_trig; struct iwl_mvm_dump_ptrs *fw_error_dump; u32 sram_len, sram_ofs; - u32 file_len, fifo_data_len = 0, prph_len = 0; + u32 file_len, fifo_data_len = 0, prph_len = 0, radio_len = 0; u32 smem_len = mvm->cfg->smem_len; u32 sram2_len = mvm->cfg->dccm2_len; bool monitor_dump_only = false; @@ -472,6 +501,9 @@ void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm) sizeof(struct iwl_fw_error_dump_prph) + num_bytes_in_chunk; } + + if (mvm->cfg->device_family == IWL_DEVICE_FAMILY_7000) + radio_len = sizeof(*dump_data) + RADIO_REG_MAX_READ; } file_len = sizeof(*dump_file) + @@ -479,6 +511,7 @@ void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm) sram_len + sizeof(*dump_mem) + fifo_data_len + prph_len + + radio_len + sizeof(*dump_info); /* Make room for the SMEM, if it exists */ @@ -543,8 +576,11 @@ void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm) dump_data = iwl_fw_error_next_data(dump_data); /* We only dump the FIFOs if the FW is in error state */ - if (test_bit(STATUS_FW_ERROR, &mvm->trans->status)) + if (test_bit(STATUS_FW_ERROR, &mvm->trans->status)) { iwl_mvm_dump_fifos(mvm, &dump_data); + if (radio_len) + iwl_mvm_read_radio_reg(mvm, &dump_data); + } if (mvm->fw_dump_desc) { dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_ERROR_INFO); From a977a1507ce133201ac2f11b3fbf102a73a77244 Mon Sep 17 00:00:00 2001 From: Golan Ben-Ami Date: Wed, 25 Nov 2015 11:44:57 +0200 Subject: [PATCH 36/52] iwlwifi: mvm: add a non-trigger window to fw dbg triggers Allow the user to configure a non-trigger session - a window between triggers in which the driver won't collect fw debug data. This can be useful when the frequent collection of fw data has an impact on the performance, such as debugging tx flows. Signed-off-by: Golan Ben-Ami Signed-off-by: Emmanuel Grumbach --- .../net/wireless/intel/iwlwifi/iwl-fw-file.h | 5 +++- .../net/wireless/intel/iwlwifi/mvm/fw-dbg.h | 24 +++++++++++++++++++ drivers/net/wireless/intel/iwlwifi/mvm/mvm.h | 3 +++ 3 files changed, 31 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-fw-file.h b/drivers/net/wireless/intel/iwlwifi/iwl-fw-file.h index 8d37e330f153..84f8aeb926c8 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-fw-file.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-fw-file.h @@ -557,6 +557,8 @@ enum iwl_fw_dbg_trigger_vif_type { * @start_conf_id: if mode is %IWL_FW_DBG_TRIGGER_START, this defines what * configuration should be applied when the triggers kicks in. * @occurrences: number of occurrences. 0 means the trigger will never fire. + * @trig_dis_ms: the time, in milliseconds, after an occurrence of this + * trigger in which another occurrence should be ignored. */ struct iwl_fw_dbg_trigger_tlv { __le32 id; @@ -566,7 +568,8 @@ struct iwl_fw_dbg_trigger_tlv { u8 mode; u8 start_conf_id; __le16 occurrences; - __le32 reserved[2]; + __le16 trig_dis_ms; + __le16 reserved[3]; u8 data[0]; } __packed; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.h b/drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.h index 461acdf497dc..08148b258bc5 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.h @@ -117,6 +117,24 @@ iwl_fw_dbg_trigger_stop_conf_match(struct iwl_mvm *mvm, (BIT(mvm->fw_dbg_conf) & le32_to_cpu(trig->stop_conf_ids)))); } +static inline bool +iwl_fw_dbg_no_trig_window(struct iwl_mvm *mvm, + struct iwl_fw_dbg_trigger_tlv *trig) +{ + unsigned long wind_jiff = + msecs_to_jiffies(le16_to_cpu(trig->trig_dis_ms)); + u32 id = le32_to_cpu(trig->id); + + /* If this is the first event checked, jump to update start ts */ + if (mvm->fw_dbg_non_collect_ts_start[id] && + (time_after(mvm->fw_dbg_non_collect_ts_start[id] + wind_jiff, + jiffies))) + return true; + + mvm->fw_dbg_non_collect_ts_start[id] = jiffies; + return false; +} + static inline bool iwl_fw_dbg_trigger_check_stop(struct iwl_mvm *mvm, struct ieee80211_vif *vif, @@ -125,6 +143,12 @@ iwl_fw_dbg_trigger_check_stop(struct iwl_mvm *mvm, if (vif && !iwl_fw_dbg_trigger_vif_match(trig, vif)) return false; + if (iwl_fw_dbg_no_trig_window(mvm, trig)) { + IWL_WARN(mvm, "Trigger %d occurred while no-collect window.\n", + trig->id); + return false; + } + return iwl_fw_dbg_trigger_stop_conf_match(mvm, trig); } diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h index 287c16250570..7517dc13eb86 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h @@ -658,6 +658,9 @@ struct iwl_mvm { /* max number of simultaneous scans the FW supports */ unsigned int max_scans; + /* ts of the beginning of a non-collect fw dbg data period */ + unsigned long fw_dbg_non_collect_ts_start[FW_DBG_TRIGGER_MAX - 1]; + /* UMAC scan tracking */ u32 scan_uid_status[IWL_MVM_MAX_UMAC_SCANS]; From 909ddf0b812ecd5ff3a9d5a164134a3714101fdb Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 21 Sep 2015 14:09:17 +0200 Subject: [PATCH 37/52] iwlwifi: mvm: support A-MSDU in A-MPDU Since A-MPDU deaggregation is done in hardware, and A-MSDU deaggregation is done in software, there's no reason not to support A-MSDU in A-MPDU; set the flag to support it. Signed-off-by: Johannes Berg Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c index 2d65040269d7..8539dfe9998e 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c @@ -438,6 +438,7 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm) ieee80211_hw_set(hw, CHANCTX_STA_CSA); ieee80211_hw_set(hw, SUPPORT_FAST_XMIT); ieee80211_hw_set(hw, SUPPORTS_CLONED_SKBS); + ieee80211_hw_set(hw, SUPPORTS_AMSDU_IN_AMPDU); if (mvm->trans->max_skb_frags) hw->netdev_features = NETIF_F_HIGHDMA | NETIF_F_SG; From b6c7d7209dc1f128fb5b9e07ee0bb2f808f86bab Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Thu, 24 Dec 2015 08:48:46 +0200 Subject: [PATCH 38/52] iwlwifi: mvm: remove useless WARN_ON and rely on cfg80211's combination We advertise one STATION vif only, so this just can't happen. Remove this useless WARN_ON. Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/intel/iwlwifi/mvm/power.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/power.c b/drivers/net/wireless/intel/iwlwifi/mvm/power.c index 87a9f244e151..9de159f1ef2d 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/power.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/power.c @@ -613,8 +613,6 @@ static void iwl_mvm_power_get_vifs_iterator(void *_data, u8 *mac, break; case NL80211_IFTYPE_STATION: - /* only a single MAC of the same type */ - WARN_ON(power_iterator->bss_vif); power_iterator->bss_vif = vif; if (mvmvif->phy_ctxt) if (mvmvif->phy_ctxt->id < MAX_PHYS) From 30433d3b9850140ceb801c5cc2013c7b408c33de Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 24 Sep 2015 18:14:55 +0200 Subject: [PATCH 39/52] iwlwifi: mvm: prevent multiple stations with the same address As the device (and parts of the driver) cannot deal with having the same MAC address for two stations (on two virtual interfaces), add some explicit code to prevent this case. Note that in practice this cannot happen since the device doesn't support operating with two AP/GO interfaces at the same time either, and other scenarios for this are, while not impossible, not going to happen in practice. Signed-off-by: Johannes Berg Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c index 8539dfe9998e..40b5c5378bd2 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c @@ -439,6 +439,7 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm) ieee80211_hw_set(hw, SUPPORT_FAST_XMIT); ieee80211_hw_set(hw, SUPPORTS_CLONED_SKBS); ieee80211_hw_set(hw, SUPPORTS_AMSDU_IN_AMPDU); + ieee80211_hw_set(hw, NEEDS_UNIQUE_STA_ADDR); if (mvm->trans->max_skb_frags) hw->netdev_features = NETIF_F_HIGHDMA | NETIF_F_SG; From bd6f5bd70012569dc626f50f3272b9f516cf20f8 Mon Sep 17 00:00:00 2001 From: Ayala Beker Date: Sun, 20 Dec 2015 09:27:50 +0200 Subject: [PATCH 40/52] iwlwifi: mvm: don't ask beacons when P2P GO vif and no assoc sta The commit below called iwl_mvm_mac_ctxt_changed() to handle a case that the vif is a P2P GO. However iwl_mvm_mac_ctxt_cmd_go() ignores the number of associated stations and asks the FW to pass beacons anyways. Fix this by checking ap_assoc_sta_count parameter, in iwl_mvm_mac_ctxt_cmd_go() as well, and ask the FW to pass beacons only when there's at least one associated station. Signed-off-by: Ayala Beker Signed-off-by: Emmanuel Grumbach --- .../net/wireless/intel/iwlwifi/mvm/mac-ctxt.c | 44 ++++++++----------- 1 file changed, 19 insertions(+), 25 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c index 448b9c927b51..bf1e5eb5dbdb 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c @@ -717,6 +717,8 @@ static void iwl_mvm_mac_ctxt_cmd_common(struct iwl_mvm *mvm, cpu_to_le32(vif->bss_conf.use_short_slot ? MAC_FLG_SHORT_SLOT : 0); + cmd->filter_flags = cpu_to_le32(MAC_FILTER_ACCEPT_GRP); + for (i = 0; i < IEEE80211_NUM_ACS; i++) { u8 txf = iwl_mvm_ac_to_tx_fifo[i]; @@ -730,11 +732,26 @@ static void iwl_mvm_mac_ctxt_cmd_common(struct iwl_mvm *mvm, cmd->ac[txf].fifos_mask = BIT(txf); } - /* in AP mode, the MCAST FIFO takes the EDCA params from VO */ - if (vif->type == NL80211_IFTYPE_AP) + if (vif->type == NL80211_IFTYPE_AP) { + /* in AP mode, the MCAST FIFO takes the EDCA params from VO */ cmd->ac[IWL_MVM_TX_FIFO_VO].fifos_mask |= BIT(IWL_MVM_TX_FIFO_MCAST); + /* + * in AP mode, pass probe requests and beacons from other APs + * (needed for ht protection); when there're no any associated + * station don't ask FW to pass beacons to prevent unnecessary + * wake-ups. + */ + cmd->filter_flags |= cpu_to_le32(MAC_FILTER_IN_PROBE_REQUEST); + if (mvmvif->ap_assoc_sta_count) { + cmd->filter_flags |= cpu_to_le32(MAC_FILTER_IN_BEACON); + IWL_DEBUG_HC(mvm, "Asking FW to pass beacons\n"); + } else { + IWL_DEBUG_HC(mvm, "No need to receive beacons\n"); + } + } + if (vif->bss_conf.qos) cmd->qos_flags |= cpu_to_le32(MAC_QOS_FLG_UPDATE_EDCA); @@ -748,8 +765,6 @@ static void iwl_mvm_mac_ctxt_cmd_common(struct iwl_mvm *mvm, cmd->qos_flags |= cpu_to_le32(MAC_QOS_FLG_TGN); if (ht_enabled) iwl_mvm_mac_ctxt_set_ht_flags(mvm, vif, cmd); - - cmd->filter_flags = cpu_to_le32(MAC_FILTER_ACCEPT_GRP); } static int iwl_mvm_mac_ctxt_send_cmd(struct iwl_mvm *mvm, @@ -1156,7 +1171,6 @@ static int iwl_mvm_mac_ctxt_cmd_ap(struct iwl_mvm *mvm, struct ieee80211_vif *vif, u32 action) { - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); struct iwl_mac_ctx_cmd cmd = {}; WARN_ON(vif->type != NL80211_IFTYPE_AP || vif->p2p); @@ -1164,19 +1178,6 @@ static int iwl_mvm_mac_ctxt_cmd_ap(struct iwl_mvm *mvm, /* Fill the common data for all mac context types */ iwl_mvm_mac_ctxt_cmd_common(mvm, vif, &cmd, NULL, action); - /* - * pass probe requests and beacons from other APs (needed - * for ht protection); when there're no any associated station - * don't ask FW to pass beacons to prevent unnecessary wake-ups. - */ - cmd.filter_flags |= cpu_to_le32(MAC_FILTER_IN_PROBE_REQUEST); - if (mvmvif->ap_assoc_sta_count) { - cmd.filter_flags |= cpu_to_le32(MAC_FILTER_IN_BEACON); - IWL_DEBUG_HC(mvm, "Asking FW to pass beacons\n"); - } else { - IWL_DEBUG_HC(mvm, "No need to receive beacons\n"); - } - /* Fill the data specific for ap mode */ iwl_mvm_mac_ctxt_cmd_fill_ap(mvm, vif, &cmd.ap, action == FW_CTXT_ACTION_ADD); @@ -1196,13 +1197,6 @@ static int iwl_mvm_mac_ctxt_cmd_go(struct iwl_mvm *mvm, /* Fill the common data for all mac context types */ iwl_mvm_mac_ctxt_cmd_common(mvm, vif, &cmd, NULL, action); - /* - * pass probe requests and beacons from other APs (needed - * for ht protection) - */ - cmd.filter_flags |= cpu_to_le32(MAC_FILTER_IN_PROBE_REQUEST | - MAC_FILTER_IN_BEACON); - /* Fill the data specific for GO mode */ iwl_mvm_mac_ctxt_cmd_fill_ap(mvm, vif, &cmd.go.ap, action == FW_CTXT_ACTION_ADD); From f5e28eac1a890e5a950cacfa3a4942a6d69462e6 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Sun, 6 Dec 2015 14:58:08 +0200 Subject: [PATCH 41/52] iwlwifi: mvm: check PN for CCMP/GCMP in the driver As we're working on multi-queue RX, we want to parallelise checking the PN in order to avoid having to serialise the RX processing. It may seem that doing parallel PN checking is insecure, but it turns out to be OK because queue assignment is done based on the data in the frame (IP/TCP) and thus cannot be manipulated by an attacker, since the data is encrypted and must first have been decrypted successfully. There are some corner cases, in particular when the peer starts using fragmentation which redirects the packet to the default queue. However this redirection is remembered (for the STA, per TID) and thus cannot be exploited by an attacker either. Leave checking on the default queue (queue 0) to mac80211, since we get fragmented packets there and those are subject to stricter checks during reassembly. Signed-off-by: Johannes Berg Signed-off-by: Sara Sharon Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/intel/iwlwifi/mvm/d3.c | 114 +++++++++++++++--- .../net/wireless/intel/iwlwifi/mvm/mac80211.c | 46 +++++++ drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c | 106 ++++++++++++++-- drivers/net/wireless/intel/iwlwifi/mvm/sta.h | 12 ++ 4 files changed, 245 insertions(+), 33 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c index 6ac40727541e..2cd9052899c4 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c @@ -137,6 +137,28 @@ static void iwl_mvm_convert_p1k(u16 *p1k, __le16 *out) out[i] = cpu_to_le16(p1k[i]); } +static const u8 *iwl_mvm_find_max_pn(struct ieee80211_key_conf *key, + struct iwl_mvm_key_pn *ptk_pn, + struct ieee80211_key_seq *seq, + int tid, int queues) +{ + const u8 *ret = seq->ccmp.pn; + int i; + + /* get the PN from mac80211, used on the default queue */ + ieee80211_get_key_rx_seq(key, tid, seq); + + /* and use the internal data for the other queues */ + for (i = 1; i < queues; i++) { + const u8 *tmp = ptk_pn->q[i].pn[tid]; + + if (memcmp(ret, tmp, IEEE80211_CCMP_PN_LEN) <= 0) + ret = tmp; + } + + return ret; +} + struct wowlan_key_data { struct iwl_wowlan_rsc_tsc_params_cmd *rsc_tsc; struct iwl_wowlan_tkip_params_cmd *tkip; @@ -294,18 +316,42 @@ static void iwl_mvm_wowlan_program_keys(struct ieee80211_hw *hw, /* * For non-QoS this relies on the fact that both the uCode and - * mac80211 use TID 0 for checking the IV in the frames. + * mac80211/our RX code use TID 0 for checking the PN. */ - for (i = 0; i < IWL_NUM_RSC; i++) { - u8 *pn = seq.ccmp.pn; + if (sta && iwl_mvm_has_new_rx_api(mvm)) { + struct iwl_mvm_sta *mvmsta; + struct iwl_mvm_key_pn *ptk_pn; + const u8 *pn; - ieee80211_get_key_rx_seq(key, i, &seq); - aes_sc[i].pn = cpu_to_le64((u64)pn[5] | - ((u64)pn[4] << 8) | - ((u64)pn[3] << 16) | - ((u64)pn[2] << 24) | - ((u64)pn[1] << 32) | - ((u64)pn[0] << 40)); + mvmsta = iwl_mvm_sta_from_mac80211(sta); + ptk_pn = rcu_dereference_protected( + mvmsta->ptk_pn[key->keyidx], + lockdep_is_held(&mvm->mutex)); + if (WARN_ON(!ptk_pn)) + break; + + for (i = 0; i < IWL_MAX_TID_COUNT; i++) { + pn = iwl_mvm_find_max_pn(key, ptk_pn, &seq, i, + mvm->trans->num_rx_queues); + aes_sc[i].pn = cpu_to_le64((u64)pn[5] | + ((u64)pn[4] << 8) | + ((u64)pn[3] << 16) | + ((u64)pn[2] << 24) | + ((u64)pn[1] << 32) | + ((u64)pn[0] << 40)); + } + } else { + for (i = 0; i < IWL_NUM_RSC; i++) { + u8 *pn = seq.ccmp.pn; + + ieee80211_get_key_rx_seq(key, i, &seq); + aes_sc[i].pn = cpu_to_le64((u64)pn[5] | + ((u64)pn[4] << 8) | + ((u64)pn[3] << 16) | + ((u64)pn[2] << 24) | + ((u64)pn[1] << 32) | + ((u64)pn[0] << 40)); + } } data->use_rsc_tsc = true; break; @@ -1426,18 +1472,42 @@ static void iwl_mvm_tkip_sc_to_seq(struct tkip_sc *sc, seq->tkip.iv16 = le16_to_cpu(sc->iv16); } -static void iwl_mvm_set_aes_rx_seq(struct aes_sc *scs, +static void iwl_mvm_set_aes_rx_seq(struct iwl_mvm *mvm, struct aes_sc *scs, + struct ieee80211_sta *sta, struct ieee80211_key_conf *key) { int tid; BUILD_BUG_ON(IWL_NUM_RSC != IEEE80211_NUM_TIDS); - for (tid = 0; tid < IWL_NUM_RSC; tid++) { - struct ieee80211_key_seq seq = {}; + if (sta && iwl_mvm_has_new_rx_api(mvm)) { + struct iwl_mvm_sta *mvmsta; + struct iwl_mvm_key_pn *ptk_pn; - iwl_mvm_aes_sc_to_seq(&scs[tid], &seq); - ieee80211_set_key_rx_seq(key, tid, &seq); + mvmsta = iwl_mvm_sta_from_mac80211(sta); + + ptk_pn = rcu_dereference_protected(mvmsta->ptk_pn[key->keyidx], + lockdep_is_held(&mvm->mutex)); + if (WARN_ON(!ptk_pn)) + return; + + for (tid = 0; tid < IWL_MAX_TID_COUNT; tid++) { + struct ieee80211_key_seq seq = {}; + int i; + + iwl_mvm_aes_sc_to_seq(&scs[tid], &seq); + ieee80211_set_key_rx_seq(key, tid, &seq); + for (i = 1; i < mvm->trans->num_rx_queues; i++) + memcpy(ptk_pn->q[i].pn[tid], + seq.ccmp.pn, IEEE80211_CCMP_PN_LEN); + } + } else { + for (tid = 0; tid < IWL_NUM_RSC; tid++) { + struct ieee80211_key_seq seq = {}; + + iwl_mvm_aes_sc_to_seq(&scs[tid], &seq); + ieee80211_set_key_rx_seq(key, tid, &seq); + } } } @@ -1456,14 +1526,15 @@ static void iwl_mvm_set_tkip_rx_seq(struct tkip_sc *scs, } } -static void iwl_mvm_set_key_rx_seq(struct ieee80211_key_conf *key, +static void iwl_mvm_set_key_rx_seq(struct iwl_mvm *mvm, + struct ieee80211_key_conf *key, struct iwl_wowlan_status *status) { union iwl_all_tsc_rsc *rsc = &status->gtk.rsc.all_tsc_rsc; switch (key->cipher) { case WLAN_CIPHER_SUITE_CCMP: - iwl_mvm_set_aes_rx_seq(rsc->aes.multicast_rsc, key); + iwl_mvm_set_aes_rx_seq(mvm, rsc->aes.multicast_rsc, NULL, key); break; case WLAN_CIPHER_SUITE_TKIP: iwl_mvm_set_tkip_rx_seq(rsc->tkip.multicast_rsc, key); @@ -1474,6 +1545,7 @@ static void iwl_mvm_set_key_rx_seq(struct ieee80211_key_conf *key, } struct iwl_mvm_d3_gtk_iter_data { + struct iwl_mvm *mvm; struct iwl_wowlan_status *status; void *last_gtk; u32 cipher; @@ -1522,7 +1594,8 @@ static void iwl_mvm_d3_update_keys(struct ieee80211_hw *hw, switch (key->cipher) { case WLAN_CIPHER_SUITE_CCMP: - iwl_mvm_set_aes_rx_seq(sc->aes.unicast_rsc, key); + iwl_mvm_set_aes_rx_seq(data->mvm, sc->aes.unicast_rsc, + sta, key); atomic64_set(&key->tx_pn, le64_to_cpu(sc->aes.tsc.pn)); break; case WLAN_CIPHER_SUITE_TKIP: @@ -1545,7 +1618,7 @@ static void iwl_mvm_d3_update_keys(struct ieee80211_hw *hw, if (data->status->num_of_gtk_rekeys) ieee80211_remove_key(key); else if (data->last_gtk == key) - iwl_mvm_set_key_rx_seq(key, data->status); + iwl_mvm_set_key_rx_seq(data->mvm, key, data->status); } static bool iwl_mvm_setup_connection_keep(struct iwl_mvm *mvm, @@ -1554,6 +1627,7 @@ static bool iwl_mvm_setup_connection_keep(struct iwl_mvm *mvm, { struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); struct iwl_mvm_d3_gtk_iter_data gtkdata = { + .mvm = mvm, .status = status, }; u32 disconnection_reasons = @@ -1615,7 +1689,7 @@ static bool iwl_mvm_setup_connection_keep(struct iwl_mvm *mvm, key = ieee80211_gtk_rekey_add(vif, &conf.conf); if (IS_ERR(key)) return false; - iwl_mvm_set_key_rx_seq(key, status); + iwl_mvm_set_key_rx_seq(mvm, key, status); } if (status->num_of_gtk_rekeys) { diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c index 40b5c5378bd2..d70a1716f3e0 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c @@ -2568,6 +2568,9 @@ static int iwl_mvm_mac_set_key(struct ieee80211_hw *hw, struct ieee80211_key_conf *key) { struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); + struct iwl_mvm_sta *mvmsta; + struct iwl_mvm_key_pn *ptk_pn; + int keyidx = key->keyidx; int ret; u8 key_offset; @@ -2635,6 +2638,36 @@ static int iwl_mvm_mac_set_key(struct ieee80211_hw *hw, break; } + if (!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status) && + sta && iwl_mvm_has_new_rx_api(mvm) && + key->flags & IEEE80211_KEY_FLAG_PAIRWISE && + (key->cipher == WLAN_CIPHER_SUITE_CCMP || + key->cipher == WLAN_CIPHER_SUITE_GCMP)) { + struct ieee80211_key_seq seq; + int tid, q; + + mvmsta = iwl_mvm_sta_from_mac80211(sta); + WARN_ON(rcu_access_pointer(mvmsta->ptk_pn[keyidx])); + ptk_pn = kzalloc(sizeof(*ptk_pn) + + mvm->trans->num_rx_queues * + sizeof(ptk_pn->q[0]), + GFP_KERNEL); + if (!ptk_pn) { + ret = -ENOMEM; + break; + } + + for (tid = 0; tid < IWL_MAX_TID_COUNT; tid++) { + ieee80211_get_key_rx_seq(key, tid, &seq); + for (q = 0; q < mvm->trans->num_rx_queues; q++) + memcpy(ptk_pn->q[q].pn[tid], + seq.ccmp.pn, + IEEE80211_CCMP_PN_LEN); + } + + rcu_assign_pointer(mvmsta->ptk_pn[keyidx], ptk_pn); + } + /* in HW restart reuse the index, otherwise request a new one */ if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) key_offset = key->hw_key_idx; @@ -2660,6 +2693,19 @@ static int iwl_mvm_mac_set_key(struct ieee80211_hw *hw, break; } + if (sta && iwl_mvm_has_new_rx_api(mvm) && + key->flags & IEEE80211_KEY_FLAG_PAIRWISE && + (key->cipher == WLAN_CIPHER_SUITE_CCMP || + key->cipher == WLAN_CIPHER_SUITE_GCMP)) { + mvmsta = iwl_mvm_sta_from_mac80211(sta); + ptk_pn = rcu_dereference_protected( + mvmsta->ptk_pn[keyidx], + lockdep_is_held(&mvm->mutex)); + RCU_INIT_POINTER(mvmsta->ptk_pn[keyidx], NULL); + if (ptk_pn) + kfree_rcu(ptk_pn, rcu_head); + } + IWL_DEBUG_MAC80211(mvm, "disable hwcrypto key\n"); ret = iwl_mvm_remove_sta_key(mvm, vif, sta, key); break; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c index e2a872deb668..0c073e02fd4c 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c @@ -78,12 +78,83 @@ void iwl_mvm_rx_phy_cmd_mq(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb) #endif } -static void iwl_mvm_pass_packet_to_mac80211(struct iwl_mvm *mvm, - struct napi_struct *napi, - struct sk_buff *skb, - struct ieee80211_hdr *hdr, u16 len, - u32 ampdu_status, u8 crypt_len, - struct iwl_rx_cmd_buffer *rxb) +static inline int iwl_mvm_check_pn(struct iwl_mvm *mvm, struct sk_buff *skb, + int queue, struct ieee80211_sta *sta) +{ + struct iwl_mvm_sta *mvmsta; + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + struct ieee80211_rx_status *stats = IEEE80211_SKB_RXCB(skb); + struct iwl_mvm_key_pn *ptk_pn; + u8 tid, keyidx; + u8 pn[IEEE80211_CCMP_PN_LEN]; + u8 *extiv; + + /* do PN checking */ + + /* multicast and non-data only arrives on default queue */ + if (!ieee80211_is_data(hdr->frame_control) || + is_multicast_ether_addr(hdr->addr1)) + return 0; + + /* do not check PN for open AP */ + if (!(stats->flag & RX_FLAG_DECRYPTED)) + return 0; + + /* + * avoid checking for default queue - we don't want to replicate + * all the logic that's necessary for checking the PN on fragmented + * frames, leave that to mac80211 + */ + if (queue == 0) + return 0; + + /* if we are here - this for sure is either CCMP or GCMP */ + if (IS_ERR_OR_NULL(sta)) { + IWL_ERR(mvm, + "expected hw-decrypted unicast frame for station\n"); + return -1; + } + + mvmsta = iwl_mvm_sta_from_mac80211(sta); + + extiv = (u8 *)hdr + ieee80211_hdrlen(hdr->frame_control); + keyidx = extiv[3] >> 6; + + ptk_pn = rcu_dereference(mvmsta->ptk_pn[keyidx]); + if (!ptk_pn) + return -1; + + if (ieee80211_is_data_qos(hdr->frame_control)) + tid = *ieee80211_get_qos_ctl(hdr) & IEEE80211_QOS_CTL_TID_MASK; + else + tid = 0; + + /* we don't use HCCA/802.11 QoS TSPECs, so drop such frames */ + if (tid >= IWL_MAX_TID_COUNT) + return -1; + + /* load pn */ + pn[0] = extiv[7]; + pn[1] = extiv[6]; + pn[2] = extiv[5]; + pn[3] = extiv[4]; + pn[4] = extiv[1]; + pn[5] = extiv[0]; + + if (memcmp(pn, ptk_pn->q[queue].pn[tid], + IEEE80211_CCMP_PN_LEN) <= 0) + return -1; + + memcpy(ptk_pn->q[queue].pn[tid], pn, IEEE80211_CCMP_PN_LEN); + stats->flag |= RX_FLAG_PN_VALIDATED; + + return 0; +} + +/* iwl_mvm_create_skb Adds the rxb to a new skb */ +static void iwl_mvm_create_skb(struct sk_buff *skb, struct ieee80211_hdr *hdr, + u16 len, u8 crypt_len, + struct iwl_rx_cmd_buffer *rxb) { unsigned int hdrlen, fraglen; @@ -112,8 +183,18 @@ static void iwl_mvm_pass_packet_to_mac80211(struct iwl_mvm *mvm, skb_add_rx_frag(skb, 0, rxb_steal_page(rxb), offset, fraglen, rxb->truesize); } +} - ieee80211_rx_napi(mvm->hw, skb, napi); +/* iwl_mvm_pass_packet_to_mac80211 - passes the packet for mac80211 */ +static void iwl_mvm_pass_packet_to_mac80211(struct iwl_mvm *mvm, + struct napi_struct *napi, + struct sk_buff *skb, int queue, + struct ieee80211_sta *sta) +{ + if (iwl_mvm_check_pn(mvm, skb, queue, sta)) + kfree_skb(skb); + else + ieee80211_rx_napi(mvm->hw, skb, napi); } static void iwl_mvm_get_signal_strength(struct iwl_mvm *mvm, @@ -141,7 +222,7 @@ static void iwl_mvm_get_signal_strength(struct iwl_mvm *mvm, rx_status->chain_signal[2] = energy_c; } -static u32 iwl_mvm_rx_crypto(struct iwl_mvm *mvm, struct ieee80211_hdr *hdr, +static int iwl_mvm_rx_crypto(struct iwl_mvm *mvm, struct ieee80211_hdr *hdr, struct ieee80211_rx_status *stats, struct iwl_rx_mpdu_desc *desc, int queue, u8 *crypt_len) @@ -158,6 +239,7 @@ static u32 iwl_mvm_rx_crypto(struct iwl_mvm *mvm, struct ieee80211_hdr *hdr, switch (status & IWL_RX_MPDU_STATUS_SEC_MASK) { case IWL_RX_MPDU_STATUS_SEC_CCM: case IWL_RX_MPDU_STATUS_SEC_GCM: + BUILD_BUG_ON(IEEE80211_CCMP_PN_LEN != IEEE80211_GCMP_PN_LEN); /* alg is CCM: check MIC only */ if (!(status & IWL_RX_MPDU_STATUS_MIC_OK)) return -1; @@ -217,7 +299,6 @@ void iwl_mvm_rx_mpdu_mq(struct iwl_mvm *mvm, struct napi_struct *napi, u32 rate_n_flags = le32_to_cpu(desc->rate_n_flags); struct ieee80211_sta *sta = NULL; struct sk_buff *skb; - u32 ampdu_status; u8 crypt_len = 0; /* Dont use dev_alloc_skb(), we'll have enough headroom once @@ -311,8 +392,6 @@ void iwl_mvm_rx_mpdu_mq(struct iwl_mvm *mvm, struct napi_struct *napi, iwl_mvm_rx_csum(sta, skb, desc); } - rcu_read_unlock(); - /* * TODO: PHY info. * Verify we don't have the information in the MPDU descriptor and @@ -367,8 +446,9 @@ void iwl_mvm_rx_mpdu_mq(struct iwl_mvm *mvm, struct napi_struct *napi, /* TODO: PHY info - update ampdu queue statistics (for debugfs) */ /* TODO: PHY info - gscan */ - iwl_mvm_pass_packet_to_mac80211(mvm, napi, skb, hdr, len, ampdu_status, - crypt_len, rxb); + iwl_mvm_create_skb(skb, hdr, len, crypt_len, rxb); + iwl_mvm_pass_packet_to_mac80211(mvm, napi, skb, queue, sta); + rcu_read_unlock(); } void iwl_mvm_rx_frame_release(struct iwl_mvm *mvm, diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/sta.h b/drivers/net/wireless/intel/iwlwifi/mvm/sta.h index badf17c7fcca..39fdf5224e81 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/sta.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/sta.h @@ -7,6 +7,7 @@ * * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH + * Copyright(c) 2015 Intel Deutschland GmbH * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -33,6 +34,7 @@ * * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH + * Copyright(c) 2015 Intel Deutschland GmbH * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -284,6 +286,13 @@ static inline u16 iwl_mvm_tid_queued(struct iwl_mvm_tid_data *tid_data) tid_data->next_reclaimed); } +struct iwl_mvm_key_pn { + struct rcu_head rcu_head; + struct { + u8 pn[IWL_MAX_TID_COUNT][IEEE80211_CCMP_PN_LEN]; + } ____cacheline_aligned_in_smp q[]; +}; + /** * struct iwl_mvm_sta - representation of a station in the driver * @sta_id: the index of the station in the fw (will be replaced by id_n_color) @@ -308,6 +317,7 @@ static inline u16 iwl_mvm_tid_queued(struct iwl_mvm_tid_data *tid_data) * gets empty before all the frames were sent, which can happen when * we are sending frames from an AMPDU queue and there was a hole in * the BA window. To be used for UAPSD only. + * @ptk_pn: per-queue PTK PN data structures * * When mac80211 creates a station it reserves some space (hw->sta_data_size) * in the structure for use by driver. This structure is placed in that @@ -328,6 +338,8 @@ struct iwl_mvm_sta { struct iwl_lq_sta lq_sta; struct ieee80211_vif *vif; + struct iwl_mvm_key_pn __rcu *ptk_pn[4]; + /* Temporary, until the new TLC will control the Tx protection */ s8 tx_protection; bool tt_tx_protection; From 0eb1c968f3c1519505d20b62f782b4f4daf4f525 Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Thu, 31 Dec 2015 15:19:32 +0200 Subject: [PATCH 42/52] iwlwifi: mvm: initialize gtkdata->mvm correctly gtkdata->mvm wasn't set in iwl_mvm_d0i3_update_keys, resulting in kernel panic in some flows (when mvm is actually used...) Fixes: a3f7ba5c8825 ("iwlwifi: update key params on d0i3 entrance/exit") Signed-off-by: Eliad Peller Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/intel/iwlwifi/mvm/d3.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c index 2cd9052899c4..d3e21d95cece 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c @@ -1843,6 +1843,7 @@ void iwl_mvm_d0i3_update_keys(struct iwl_mvm *mvm, struct iwl_wowlan_status *status) { struct iwl_mvm_d3_gtk_iter_data gtkdata = { + .mvm = mvm, .status = status, }; From be720d3fc049e14323f2ba637c053fe39d3e0157 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 5 Jan 2016 16:16:31 +0100 Subject: [PATCH 43/52] iwlwifi: mvm: check minimum temperature notification length This notification will be extended with extra data, so just check that it has a minimum length, not the exact length; we might later add handling for the extra fields added and have more code to handle both versions. Signed-off-by: Johannes Berg Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/intel/iwlwifi/mvm/tt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/tt.c b/drivers/net/wireless/intel/iwlwifi/mvm/tt.c index 473975cb34af..fb76004eede4 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/tt.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/tt.c @@ -120,7 +120,7 @@ static int iwl_mvm_temp_notif_parse(struct iwl_mvm *mvm, int len = iwl_rx_packet_payload_len(pkt); int temp; - if (WARN_ON_ONCE(len != sizeof(*notif))) { + if (WARN_ON_ONCE(len < sizeof(*notif))) { IWL_ERR(mvm, "Invalid DTS_MEASUREMENT_NOTIFICATION\n"); return -EINVAL; } From a80c7a696610c537fd6e61489be9da0a995fc880 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Tue, 5 Jan 2016 09:14:08 +0200 Subject: [PATCH 44/52] iwlwifi: mvm: constify the parameters of a few functions in fw-dbg.c The debug functions of fw-dbg.c don't really need to modify the trigger and the description they receive as a parameter. Constify the pointers. Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/intel/iwlwifi/iwl-trans.h | 4 ++-- drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.c | 8 ++++---- drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.h | 6 +++--- drivers/net/wireless/intel/iwlwifi/mvm/mvm.h | 6 +++--- drivers/net/wireless/intel/iwlwifi/pcie/trans.c | 2 +- 5 files changed, 13 insertions(+), 13 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h index 81b7cb71e001..82fb3a97a46d 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h @@ -663,7 +663,7 @@ struct iwl_trans_ops { void (*resume)(struct iwl_trans *trans); struct iwl_trans_dump_data *(*dump_data)(struct iwl_trans *trans, - struct iwl_fw_dbg_trigger_tlv + const struct iwl_fw_dbg_trigger_tlv *trigger); }; @@ -966,7 +966,7 @@ static inline void iwl_trans_resume(struct iwl_trans *trans) static inline struct iwl_trans_dump_data * iwl_trans_dump_data(struct iwl_trans *trans, - struct iwl_fw_dbg_trigger_tlv *trigger) + const struct iwl_fw_dbg_trigger_tlv *trigger) { if (!trans->ops->dump_data) return NULL; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.c index 59450f177c43..b3bc2128d81b 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.c @@ -681,15 +681,15 @@ void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm) clear_bit(IWL_MVM_STATUS_DUMPING_FW_LOG, &mvm->status); } -struct iwl_mvm_dump_desc iwl_mvm_dump_desc_assert = { +const struct iwl_mvm_dump_desc iwl_mvm_dump_desc_assert = { .trig_desc = { .type = cpu_to_le32(FW_DBG_TRIGGER_FW_ASSERT), }, }; int iwl_mvm_fw_dbg_collect_desc(struct iwl_mvm *mvm, - struct iwl_mvm_dump_desc *desc, - struct iwl_fw_dbg_trigger_tlv *trigger) + const struct iwl_mvm_dump_desc *desc, + const struct iwl_fw_dbg_trigger_tlv *trigger) { unsigned int delay = 0; @@ -715,7 +715,7 @@ int iwl_mvm_fw_dbg_collect_desc(struct iwl_mvm *mvm, int iwl_mvm_fw_dbg_collect(struct iwl_mvm *mvm, enum iwl_fw_dbg_trigger trig, const char *str, size_t len, - struct iwl_fw_dbg_trigger_tlv *trigger) + const struct iwl_fw_dbg_trigger_tlv *trigger) { struct iwl_mvm_dump_desc *desc; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.h b/drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.h index 08148b258bc5..f7dff7612c9c 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.h @@ -72,11 +72,11 @@ void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm); void iwl_mvm_free_fw_dump_desc(struct iwl_mvm *mvm); int iwl_mvm_fw_dbg_collect_desc(struct iwl_mvm *mvm, - struct iwl_mvm_dump_desc *desc, - struct iwl_fw_dbg_trigger_tlv *trigger); + const struct iwl_mvm_dump_desc *desc, + const struct iwl_fw_dbg_trigger_tlv *trigger); int iwl_mvm_fw_dbg_collect(struct iwl_mvm *mvm, enum iwl_fw_dbg_trigger trig, const char *str, size_t len, - struct iwl_fw_dbg_trigger_tlv *trigger); + const struct iwl_fw_dbg_trigger_tlv *trigger); int iwl_mvm_fw_dbg_collect_trig(struct iwl_mvm *mvm, struct iwl_fw_dbg_trigger_tlv *trigger, const char *fmt, ...) __printf(3, 4); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h index 7517dc13eb86..5f3ac8cccf49 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h @@ -157,7 +157,7 @@ struct iwl_mvm_dump_desc { struct iwl_fw_error_dump_trigger_desc trig_desc; }; -extern struct iwl_mvm_dump_desc iwl_mvm_dump_desc_assert; +extern const struct iwl_mvm_dump_desc iwl_mvm_dump_desc_assert; struct iwl_mvm_phy_ctxt { u16 id; @@ -732,8 +732,8 @@ struct iwl_mvm { s8 restart_fw; u8 fw_dbg_conf; struct delayed_work fw_dump_wk; - struct iwl_mvm_dump_desc *fw_dump_desc; - struct iwl_fw_dbg_trigger_tlv *fw_dump_trig; + const struct iwl_mvm_dump_desc *fw_dump_desc; + const struct iwl_fw_dbg_trigger_tlv *fw_dump_trig; #ifdef CONFIG_IWLWIFI_LEDS struct led_classdev led; diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c index d44e7afad593..3a4310917978 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c @@ -2364,7 +2364,7 @@ iwl_trans_pcie_dump_monitor(struct iwl_trans *trans, static struct iwl_trans_dump_data *iwl_trans_pcie_dump_data(struct iwl_trans *trans, - struct iwl_fw_dbg_trigger_tlv *trigger) + const struct iwl_fw_dbg_trigger_tlv *trigger) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); struct iwl_fw_error_dump_data *data; From 9fb7807ce7f21cbab2b13db0183f7f71a75add5e Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Tue, 5 Jan 2016 09:35:21 +0200 Subject: [PATCH 45/52] iwlwifi: mvm: fix memory leaks in error paths upon fw error dump When iwl_mvm_fw_error_dump fails, it needs to clear the state in mvm, which includes: * clear IWL_MVM_STATUS_DUMPING_FW_LOG * set mvm->fw_dump_trig to NULL * free the description While at it, remove a NULL check in iwl_mvm_free_fw_dump_desc since kfree is NULL safe. Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.c index b3bc2128d81b..0813f8184e10 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.c @@ -270,8 +270,7 @@ static void iwl_mvm_dump_fifos(struct iwl_mvm *mvm, void iwl_mvm_free_fw_dump_desc(struct iwl_mvm *mvm) { - if (mvm->fw_dump_desc == &iwl_mvm_dump_desc_assert || - !mvm->fw_dump_desc) + if (mvm->fw_dump_desc == &iwl_mvm_dump_desc_assert) return; kfree(mvm->fw_dump_desc); @@ -441,7 +440,7 @@ void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm) /* there's no point in fw dump if the bus is dead */ if (test_bit(STATUS_TRANS_DEAD, &mvm->trans->status)) { IWL_ERR(mvm, "Skip fw error dump since bus is dead\n"); - return; + goto out; } if (mvm->fw_dump_trig && @@ -450,7 +449,7 @@ void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm) fw_error_dump = kzalloc(sizeof(*fw_error_dump), GFP_KERNEL); if (!fw_error_dump) - return; + goto out; /* SRAM - include stack CCM if driver knows the values for it */ if (!mvm->cfg->dccm_offset || !mvm->cfg->dccm_len) { @@ -550,8 +549,7 @@ void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm) dump_file = vzalloc(file_len); if (!dump_file) { kfree(fw_error_dump); - iwl_mvm_free_fw_dump_desc(mvm); - return; + goto out; } fw_error_dump->op_mode_ptr = dump_file; @@ -590,8 +588,6 @@ void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm) memcpy(dump_trig, &mvm->fw_dump_desc->trig_desc, sizeof(*dump_trig) + mvm->fw_dump_desc->len); - /* now we can free this copy */ - iwl_mvm_free_fw_dump_desc(mvm); dump_data = iwl_fw_error_next_data(dump_data); } @@ -677,6 +673,8 @@ void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm) dev_coredumpm(mvm->trans->dev, THIS_MODULE, fw_error_dump, 0, GFP_KERNEL, iwl_mvm_read_coredump, iwl_mvm_free_coredump); +out: + iwl_mvm_free_fw_dump_desc(mvm); mvm->fw_dump_trig = NULL; clear_bit(IWL_MVM_STATUS_DUMPING_FW_LOG, &mvm->status); } From 62d7476d958ce06d7a10b02bdb30006870286fe2 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Tue, 5 Jan 2016 15:25:43 +0200 Subject: [PATCH 46/52] iwlwifi: pcie: properly configure the debug buffer size for 8000 8000 device family has a new debug engine that needs to be configured differently than 7000's. The debug engine's DMA works in chunks of memory and the size of the buffer really means the start of the last chunk. Since one chunk is 256-byte long, we should configure the device to write to buffer_size - 256. This fixes a situation were the device would write to memory it is not allowed to access. CC: [4.1+] Signed-off-by: Emmanuel Grumbach --- drivers/net/wireless/intel/iwlwifi/pcie/trans.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c index 3a4310917978..d60a467a983c 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c @@ -7,6 +7,7 @@ * * Copyright(c) 2007 - 2015 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * Copyright(c) 2016 Intel Deutschland GmbH * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -33,6 +34,7 @@ * * Copyright(c) 2005 - 2015 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * Copyright(c) 2016 Intel Deutschland GmbH * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -924,9 +926,16 @@ static void iwl_pcie_apply_destination(struct iwl_trans *trans) if (dest->monitor_mode == EXTERNAL_MODE && trans_pcie->fw_mon_size) { iwl_write_prph(trans, le32_to_cpu(dest->base_reg), trans_pcie->fw_mon_phys >> dest->base_shift); - iwl_write_prph(trans, le32_to_cpu(dest->end_reg), - (trans_pcie->fw_mon_phys + - trans_pcie->fw_mon_size) >> dest->end_shift); + if (trans->cfg->device_family == IWL_DEVICE_FAMILY_8000) + iwl_write_prph(trans, le32_to_cpu(dest->end_reg), + (trans_pcie->fw_mon_phys + + trans_pcie->fw_mon_size - 256) >> + dest->end_shift); + else + iwl_write_prph(trans, le32_to_cpu(dest->end_reg), + (trans_pcie->fw_mon_phys + + trans_pcie->fw_mon_size) >> + dest->end_shift); } } From 3021ad9a4f009265e6063e617fb91306980af16c Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Tue, 5 Jan 2016 11:05:45 +0100 Subject: [PATCH 47/52] brcmfmac: Add wowl net detect support With wowl net detect it becomes possible to scan for specific ssids and wakeup once found. Reviewed-by: Arend Van Spriel Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo --- .../broadcom/brcm80211/brcmfmac/cfg80211.c | 142 ++++++++++++++++-- .../broadcom/brcm80211/brcmfmac/cfg80211.h | 27 +++- .../broadcom/brcm80211/brcmfmac/fwil_types.h | 2 + 3 files changed, 155 insertions(+), 16 deletions(-) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c index 6a7759fcbd86..fd54ad141965 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c @@ -95,6 +95,8 @@ #define BRCMF_SCAN_UNASSOC_TIME 40 #define BRCMF_SCAN_PASSIVE_TIME 120 +#define BRCMF_ND_INFO_TIMEOUT msecs_to_jiffies(2000) + #define BRCMF_ASSOC_PARAMS_FIXED_SIZE \ (sizeof(struct brcmf_assoc_params_le) - sizeof(u16)) @@ -236,6 +238,17 @@ struct parsed_vndr_ies { struct parsed_vndr_ie_info ie_info[VNDR_IE_PARSE_LIMIT]; }; +/* Function prototype forward declarations */ +static int +brcmf_cfg80211_sched_scan_start(struct wiphy *wiphy, + struct net_device *ndev, + struct cfg80211_sched_scan_request *request); +static int brcmf_cfg80211_sched_scan_stop(struct wiphy *wiphy, + struct net_device *ndev); +static s32 +brcmf_notify_sched_scan_results(struct brcmf_if *ifp, + const struct brcmf_event_msg *e, void *data); + static u16 chandef_to_chanspec(struct brcmu_d11inf *d11inf, struct cfg80211_chan_def *ch) @@ -3116,26 +3129,71 @@ static s32 brcmf_config_wowl_pattern(struct brcmf_if *ifp, u8 cmd[4], return ret; } +static s32 +brcmf_wowl_nd_results(struct brcmf_if *ifp, const struct brcmf_event_msg *e, + void *data) +{ + struct brcmf_cfg80211_info *cfg = ifp->drvr->config; + struct brcmf_pno_scanresults_le *pfn_result; + struct brcmf_pno_net_info_le *netinfo; + + brcmf_dbg(SCAN, "Enter\n"); + + pfn_result = (struct brcmf_pno_scanresults_le *)data; + + if (e->event_code == BRCMF_E_PFN_NET_LOST) { + brcmf_dbg(SCAN, "PFN NET LOST event. Ignore\n"); + return 0; + } + + if (le32_to_cpu(pfn_result->count) < 1) { + brcmf_err("Invalid result count, expected 1 (%d)\n", + le32_to_cpu(pfn_result->count)); + return -EINVAL; + } + + data += sizeof(struct brcmf_pno_scanresults_le); + netinfo = (struct brcmf_pno_net_info_le *)data; + memcpy(cfg->wowl.nd->ssid.ssid, netinfo->SSID, netinfo->SSID_len); + cfg->wowl.nd->ssid.ssid_len = netinfo->SSID_len; + cfg->wowl.nd->n_channels = 1; + cfg->wowl.nd->channels[0] = + ieee80211_channel_to_frequency(netinfo->channel, + netinfo->channel <= CH_MAX_2G_CHANNEL ? + NL80211_BAND_2GHZ : NL80211_BAND_5GHZ); + cfg->wowl.nd_info->n_matches = 1; + cfg->wowl.nd_info->matches[0] = cfg->wowl.nd; + + /* Inform (the resume task) that the net detect information was recvd */ + cfg->wowl.nd_data_completed = true; + wake_up(&cfg->wowl.nd_data_wait); + + return 0; +} + #ifdef CONFIG_PM static void brcmf_report_wowl_wakeind(struct wiphy *wiphy, struct brcmf_if *ifp) { + struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); struct brcmf_wowl_wakeind_le wake_ind_le; struct cfg80211_wowlan_wakeup wakeup_data; struct cfg80211_wowlan_wakeup *wakeup; u32 wakeind; s32 err; + int timeout; err = brcmf_fil_iovar_data_get(ifp, "wowl_wakeind", &wake_ind_le, sizeof(wake_ind_le)); - if (!err) { + if (err) { brcmf_err("Get wowl_wakeind failed, err = %d\n", err); return; } wakeind = le32_to_cpu(wake_ind_le.ucode_wakeind); if (wakeind & (BRCMF_WOWL_MAGIC | BRCMF_WOWL_DIS | BRCMF_WOWL_BCN | - BRCMF_WOWL_RETR | BRCMF_WOWL_NET)) { + BRCMF_WOWL_RETR | BRCMF_WOWL_NET | + BRCMF_WOWL_PFN_FOUND)) { wakeup = &wakeup_data; memset(&wakeup_data, 0, sizeof(wakeup_data)); wakeup_data.pattern_idx = -1; @@ -3163,6 +3221,16 @@ static void brcmf_report_wowl_wakeind(struct wiphy *wiphy, struct brcmf_if *ifp) */ wakeup_data.pattern_idx = 0; } + if (wakeind & BRCMF_WOWL_PFN_FOUND) { + brcmf_dbg(INFO, "WOWL Wake indicator: BRCMF_WOWL_PFN_FOUND\n"); + timeout = wait_event_timeout(cfg->wowl.nd_data_wait, + cfg->wowl.nd_data_completed, + BRCMF_ND_INFO_TIMEOUT); + if (!timeout) + brcmf_err("No result for wowl net detect\n"); + else + wakeup_data.net_detect = cfg->wowl.nd_info; + } } else { wakeup = NULL; } @@ -3185,14 +3253,21 @@ static s32 brcmf_cfg80211_resume(struct wiphy *wiphy) brcmf_dbg(TRACE, "Enter\n"); - if (cfg->wowl_enabled) { + if (cfg->wowl.active) { brcmf_report_wowl_wakeind(wiphy, ifp); brcmf_fil_iovar_int_set(ifp, "wowl_clear", 0); brcmf_config_wowl_pattern(ifp, "clr", NULL, 0, NULL, 0); brcmf_configure_arp_offload(ifp, true); brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM, - cfg->pre_wowl_pmmode); - cfg->wowl_enabled = false; + cfg->wowl.pre_pmmode); + cfg->wowl.active = false; + if (cfg->wowl.nd_enabled) { + brcmf_cfg80211_sched_scan_stop(cfg->wiphy, ifp->ndev); + brcmf_fweh_unregister(cfg->pub, BRCMF_E_PFN_NET_FOUND); + brcmf_fweh_register(cfg->pub, BRCMF_E_PFN_NET_FOUND, + brcmf_notify_sched_scan_results); + cfg->wowl.nd_enabled = false; + } } return 0; } @@ -3207,7 +3282,7 @@ static void brcmf_configure_wowl(struct brcmf_cfg80211_info *cfg, brcmf_dbg(TRACE, "Suspend, wowl config.\n"); brcmf_configure_arp_offload(ifp, false); - brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_PM, &cfg->pre_wowl_pmmode); + brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_PM, &cfg->wowl.pre_pmmode); brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM, PM_MAX); wowl_config = 0; @@ -3225,11 +3300,26 @@ static void brcmf_configure_wowl(struct brcmf_cfg80211_info *cfg, wowl->patterns[i].pkt_offset); } } + if (wowl->nd_config) { + brcmf_cfg80211_sched_scan_start(cfg->wiphy, ifp->ndev, + wowl->nd_config); + wowl_config |= BRCMF_WOWL_PFN_FOUND; + + cfg->wowl.nd_data_completed = false; + cfg->wowl.nd_enabled = true; + /* Now reroute the event for PFN to the wowl function. */ + brcmf_fweh_unregister(cfg->pub, BRCMF_E_PFN_NET_FOUND); + brcmf_fweh_register(cfg->pub, BRCMF_E_PFN_NET_FOUND, + brcmf_wowl_nd_results); + } + if (!test_bit(BRCMF_VIF_STATUS_CONNECTED, &ifp->vif->sme_state)) + wowl_config |= BRCMF_WOWL_UNASSOC; + brcmf_fil_iovar_data_set(ifp, "wowl_wakeind", "clear", strlen("clear")); brcmf_fil_iovar_int_set(ifp, "wowl", wowl_config); brcmf_fil_iovar_int_set(ifp, "wowl_activate", 1); brcmf_bus_wowl_config(cfg->pub->bus_if, true); - cfg->wowl_enabled = true; + cfg->wowl.active = true; } static s32 brcmf_cfg80211_suspend(struct wiphy *wiphy, @@ -3248,6 +3338,10 @@ static s32 brcmf_cfg80211_suspend(struct wiphy *wiphy, if (!check_vif_up(ifp->vif)) goto exit; + /* Stop scheduled scan */ + if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_PNO)) + brcmf_cfg80211_sched_scan_stop(wiphy, ndev); + /* end any scanning */ if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) brcmf_abort_scanning(cfg); @@ -5329,6 +5423,10 @@ static void brcmf_deinit_priv_mem(struct brcmf_cfg80211_info *cfg) cfg->escan_ioctl_buf = NULL; kfree(cfg->extra_buf); cfg->extra_buf = NULL; + kfree(cfg->wowl.nd); + cfg->wowl.nd = NULL; + kfree(cfg->wowl.nd_info); + cfg->wowl.nd_info = NULL; } static s32 brcmf_init_priv_mem(struct brcmf_cfg80211_info *cfg) @@ -5342,6 +5440,14 @@ static s32 brcmf_init_priv_mem(struct brcmf_cfg80211_info *cfg) cfg->extra_buf = kzalloc(WL_EXTRA_BUF_MAX, GFP_KERNEL); if (!cfg->extra_buf) goto init_priv_mem_out; + cfg->wowl.nd = kzalloc(sizeof(*cfg->wowl.nd) + sizeof(u32), GFP_KERNEL); + if (!cfg->wowl.nd) + goto init_priv_mem_out; + cfg->wowl.nd_info = kzalloc(sizeof(*cfg->wowl.nd_info) + + sizeof(struct cfg80211_wowlan_nd_match *), + GFP_KERNEL); + if (!cfg->wowl.nd_info) + goto init_priv_mem_out; return 0; @@ -6018,7 +6124,7 @@ static void brcmf_wiphy_pno_params(struct wiphy *wiphy) } #ifdef CONFIG_PM -static const struct wiphy_wowlan_support brcmf_wowlan_support = { +static struct wiphy_wowlan_support brcmf_wowlan_support = { .flags = WIPHY_WOWLAN_MAGIC_PKT | WIPHY_WOWLAN_DISCONNECT, .n_patterns = BRCMF_WOWL_MAXPATTERNS, .pattern_max_len = BRCMF_WOWL_MAXPATTERNSIZE, @@ -6027,10 +6133,23 @@ static const struct wiphy_wowlan_support brcmf_wowlan_support = { }; #endif -static void brcmf_wiphy_wowl_params(struct wiphy *wiphy) +static void brcmf_wiphy_wowl_params(struct wiphy *wiphy, struct brcmf_if *ifp) { #ifdef CONFIG_PM - /* wowl settings */ + struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); + s32 err; + u32 wowl_cap; + + if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_PNO)) { + err = brcmf_fil_iovar_int_get(ifp, "wowl_cap", &wowl_cap); + if (!err) { + if (wowl_cap & BRCMF_WOWL_PFN_FOUND) { + brcmf_wowlan_support.flags |= + WIPHY_WOWLAN_NET_DETECT; + init_waitqueue_head(&cfg->wowl.nd_data_wait); + } + } + } wiphy->wowlan = &brcmf_wowlan_support; #endif } @@ -6091,8 +6210,7 @@ static int brcmf_setup_wiphy(struct wiphy *wiphy, struct brcmf_if *ifp) wiphy->n_vendor_commands = BRCMF_VNDR_CMDS_LAST - 1; if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_WOWL)) - brcmf_wiphy_wowl_params(wiphy); - + brcmf_wiphy_wowl_params(wiphy, ifp); err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BANDLIST, &bandlist, sizeof(bandlist)); if (err) { diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h index c17b6d584fe0..69af708b43f5 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h @@ -226,6 +226,27 @@ struct brcmf_cfg80211_vif_event { struct brcmf_cfg80211_vif *vif; }; +/** + * struct brcmf_cfg80211_wowl - wowl related information. + * + * @active: set on suspend, cleared on resume. + * @pre_pmmode: firmware PM mode at entering suspend. + * @nd: net dectect data. + * @nd_info: helper struct to pass to cfg80211. + * @nd_data_wait: wait queue to sync net detect data. + * @nd_data_completed: completion for net detect data. + * @nd_enabled: net detect enabled. + */ +struct brcmf_cfg80211_wowl { + bool active; + u32 pre_pmmode; + struct cfg80211_wowlan_nd_match *nd; + struct cfg80211_wowlan_nd_info *nd_info; + wait_queue_head_t nd_data_wait; + bool nd_data_completed; + bool nd_enabled; +}; + /** * struct brcmf_cfg80211_info - dongle private data of cfg80211 interface * @@ -259,8 +280,7 @@ struct brcmf_cfg80211_vif_event { * @vif_list: linked list of vif instances. * @vif_cnt: number of vif instances. * @vif_event: vif event signalling. - * @wowl_enabled; set during suspend, is wowl used. - * @pre_wowl_pmmode: intermediate storage of pm mode during wowl. + * @wowl: wowl related information. */ struct brcmf_cfg80211_info { struct wiphy *wiphy; @@ -292,9 +312,8 @@ struct brcmf_cfg80211_info { struct brcmf_cfg80211_vif_event vif_event; struct completion vif_disabled; struct brcmu_d11inf d11inf; - bool wowl_enabled; - u32 pre_wowl_pmmode; struct brcmf_assoclist_le assoclist; + struct brcmf_cfg80211_wowl wowl; }; /** diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h index bf2df49d7098..1afc2ad83b6c 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h @@ -110,6 +110,8 @@ #define BRCMF_WOWL_UNASSOC (1 << 24) /* Wakeup if received matched secured pattern: */ #define BRCMF_WOWL_SECURE (1 << 25) +/* Wakeup on finding preferred network */ +#define BRCMF_WOWL_PFN_FOUND (1 << 26) /* Link Down indication in WoWL mode: */ #define BRCMF_WOWL_LINKDOWN (1 << 31) From 5419f7f17d5b70a9b65ec849da69bb5f7253b863 Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Tue, 5 Jan 2016 11:05:46 +0100 Subject: [PATCH 48/52] brcmfmac: Reshuffle functions to avoid forward declarations Function prototype forward declarations are to be avoided. This patch shuffles some of the functions so the forward declarations can be removed. Reviewed-by: Arend Van Spriel Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo --- .../broadcom/brcm80211/brcmfmac/cfg80211.c | 596 +++++++++--------- 1 file changed, 290 insertions(+), 306 deletions(-) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c index fd54ad141965..c9657be3d4c3 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c @@ -238,18 +238,6 @@ struct parsed_vndr_ies { struct parsed_vndr_ie_info ie_info[VNDR_IE_PARSE_LIMIT]; }; -/* Function prototype forward declarations */ -static int -brcmf_cfg80211_sched_scan_start(struct wiphy *wiphy, - struct net_device *ndev, - struct cfg80211_sched_scan_request *request); -static int brcmf_cfg80211_sched_scan_stop(struct wiphy *wiphy, - struct net_device *ndev); -static s32 -brcmf_notify_sched_scan_results(struct brcmf_if *ifp, - const struct brcmf_event_msg *e, void *data); - - static u16 chandef_to_chanspec(struct brcmu_d11inf *d11inf, struct cfg80211_chan_def *ch) { @@ -3081,6 +3069,296 @@ static void brcmf_init_escan(struct brcmf_cfg80211_info *cfg) brcmf_cfg80211_escan_timeout_worker); } +/* PFN result doesn't have all the info which are required by the supplicant + * (For e.g IEs) Do a target Escan so that sched scan results are reported + * via wl_inform_single_bss in the required format. Escan does require the + * scan request in the form of cfg80211_scan_request. For timebeing, create + * cfg80211_scan_request one out of the received PNO event. + */ +static s32 +brcmf_notify_sched_scan_results(struct brcmf_if *ifp, + const struct brcmf_event_msg *e, void *data) +{ + struct brcmf_cfg80211_info *cfg = ifp->drvr->config; + struct brcmf_pno_net_info_le *netinfo, *netinfo_start; + struct cfg80211_scan_request *request = NULL; + struct cfg80211_ssid *ssid = NULL; + struct ieee80211_channel *channel = NULL; + struct wiphy *wiphy = cfg_to_wiphy(cfg); + int err = 0; + int channel_req = 0; + int band = 0; + struct brcmf_pno_scanresults_le *pfn_result; + u32 result_count; + u32 status; + + brcmf_dbg(SCAN, "Enter\n"); + + if (e->event_code == BRCMF_E_PFN_NET_LOST) { + brcmf_dbg(SCAN, "PFN NET LOST event. Do Nothing\n"); + return 0; + } + + pfn_result = (struct brcmf_pno_scanresults_le *)data; + result_count = le32_to_cpu(pfn_result->count); + status = le32_to_cpu(pfn_result->status); + + /* PFN event is limited to fit 512 bytes so we may get + * multiple NET_FOUND events. For now place a warning here. + */ + WARN_ON(status != BRCMF_PNO_SCAN_COMPLETE); + brcmf_dbg(SCAN, "PFN NET FOUND event. count: %d\n", result_count); + if (result_count > 0) { + int i; + + request = kzalloc(sizeof(*request), GFP_KERNEL); + ssid = kcalloc(result_count, sizeof(*ssid), GFP_KERNEL); + channel = kcalloc(result_count, sizeof(*channel), GFP_KERNEL); + if (!request || !ssid || !channel) { + err = -ENOMEM; + goto out_err; + } + + request->wiphy = wiphy; + data += sizeof(struct brcmf_pno_scanresults_le); + netinfo_start = (struct brcmf_pno_net_info_le *)data; + + for (i = 0; i < result_count; i++) { + netinfo = &netinfo_start[i]; + if (!netinfo) { + brcmf_err("Invalid netinfo ptr. index: %d\n", + i); + err = -EINVAL; + goto out_err; + } + + brcmf_dbg(SCAN, "SSID:%s Channel:%d\n", + netinfo->SSID, netinfo->channel); + memcpy(ssid[i].ssid, netinfo->SSID, netinfo->SSID_len); + ssid[i].ssid_len = netinfo->SSID_len; + request->n_ssids++; + + channel_req = netinfo->channel; + if (channel_req <= CH_MAX_2G_CHANNEL) + band = NL80211_BAND_2GHZ; + else + band = NL80211_BAND_5GHZ; + channel[i].center_freq = + ieee80211_channel_to_frequency(channel_req, + band); + channel[i].band = band; + channel[i].flags |= IEEE80211_CHAN_NO_HT40; + request->channels[i] = &channel[i]; + request->n_channels++; + } + + /* assign parsed ssid array */ + if (request->n_ssids) + request->ssids = &ssid[0]; + + if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) { + /* Abort any on-going scan */ + brcmf_abort_scanning(cfg); + } + + set_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status); + cfg->escan_info.run = brcmf_run_escan; + err = brcmf_do_escan(cfg, wiphy, ifp, request); + if (err) { + clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status); + goto out_err; + } + cfg->sched_escan = true; + cfg->scan_request = request; + } else { + brcmf_err("FALSE PNO Event. (pfn_count == 0)\n"); + goto out_err; + } + + kfree(ssid); + kfree(channel); + kfree(request); + return 0; + +out_err: + kfree(ssid); + kfree(channel); + kfree(request); + cfg80211_sched_scan_stopped(wiphy); + return err; +} + +static int brcmf_dev_pno_clean(struct net_device *ndev) +{ + int ret; + + /* Disable pfn */ + ret = brcmf_fil_iovar_int_set(netdev_priv(ndev), "pfn", 0); + if (ret == 0) { + /* clear pfn */ + ret = brcmf_fil_iovar_data_set(netdev_priv(ndev), "pfnclear", + NULL, 0); + } + if (ret < 0) + brcmf_err("failed code %d\n", ret); + + return ret; +} + +static int brcmf_dev_pno_config(struct brcmf_if *ifp, + struct cfg80211_sched_scan_request *request) +{ + struct brcmf_pno_param_le pfn_param; + struct brcmf_pno_macaddr_le pfn_mac; + s32 err; + u8 *mac_mask; + int i; + + memset(&pfn_param, 0, sizeof(pfn_param)); + pfn_param.version = cpu_to_le32(BRCMF_PNO_VERSION); + + /* set extra pno params */ + pfn_param.flags = cpu_to_le16(1 << BRCMF_PNO_ENABLE_ADAPTSCAN_BIT); + pfn_param.repeat = BRCMF_PNO_REPEAT; + pfn_param.exp = BRCMF_PNO_FREQ_EXPO_MAX; + + /* set up pno scan fr */ + pfn_param.scan_freq = cpu_to_le32(BRCMF_PNO_TIME); + + err = brcmf_fil_iovar_data_set(ifp, "pfn_set", &pfn_param, + sizeof(pfn_param)); + if (err) { + brcmf_err("pfn_set failed, err=%d\n", err); + return err; + } + + /* Find out if mac randomization should be turned on */ + if (!(request->flags & NL80211_SCAN_FLAG_RANDOM_ADDR)) + return 0; + + pfn_mac.version = BRCMF_PFN_MACADDR_CFG_VER; + pfn_mac.flags = BRCMF_PFN_MAC_OUI_ONLY | BRCMF_PFN_SET_MAC_UNASSOC; + + memcpy(pfn_mac.mac, request->mac_addr, ETH_ALEN); + mac_mask = request->mac_addr_mask; + for (i = 0; i < ETH_ALEN; i++) { + pfn_mac.mac[i] &= mac_mask[i]; + pfn_mac.mac[i] |= get_random_int() & ~(mac_mask[i]); + } + /* Clear multi bit */ + pfn_mac.mac[0] &= 0xFE; + /* Set locally administered */ + pfn_mac.mac[0] |= 0x02; + + err = brcmf_fil_iovar_data_set(ifp, "pfn_macaddr", &pfn_mac, + sizeof(pfn_mac)); + if (err) + brcmf_err("pfn_macaddr failed, err=%d\n", err); + + return err; +} + +static int +brcmf_cfg80211_sched_scan_start(struct wiphy *wiphy, + struct net_device *ndev, + struct cfg80211_sched_scan_request *request) +{ + struct brcmf_if *ifp = netdev_priv(ndev); + struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy); + struct brcmf_pno_net_param_le pfn; + int i; + int ret = 0; + + brcmf_dbg(SCAN, "Enter n_match_sets:%d n_ssids:%d\n", + request->n_match_sets, request->n_ssids); + if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) { + brcmf_err("Scanning already: status (%lu)\n", cfg->scan_status); + return -EAGAIN; + } + if (test_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status)) { + brcmf_err("Scanning suppressed: status (%lu)\n", + cfg->scan_status); + return -EAGAIN; + } + + if (!request->n_ssids || !request->n_match_sets) { + brcmf_dbg(SCAN, "Invalid sched scan req!! n_ssids:%d\n", + request->n_ssids); + return -EINVAL; + } + + if (request->n_ssids > 0) { + for (i = 0; i < request->n_ssids; i++) { + /* Active scan req for ssids */ + brcmf_dbg(SCAN, ">>> Active scan req for ssid (%s)\n", + request->ssids[i].ssid); + + /* match_set ssids is a supert set of n_ssid list, + * so we need not add these set separately. + */ + } + } + + if (request->n_match_sets > 0) { + /* clean up everything */ + ret = brcmf_dev_pno_clean(ndev); + if (ret < 0) { + brcmf_err("failed error=%d\n", ret); + return ret; + } + + /* configure pno */ + if (brcmf_dev_pno_config(ifp, request)) + return -EINVAL; + + /* configure each match set */ + for (i = 0; i < request->n_match_sets; i++) { + struct cfg80211_ssid *ssid; + u32 ssid_len; + + ssid = &request->match_sets[i].ssid; + ssid_len = ssid->ssid_len; + + if (!ssid_len) { + brcmf_err("skip broadcast ssid\n"); + continue; + } + pfn.auth = cpu_to_le32(WLAN_AUTH_OPEN); + pfn.wpa_auth = cpu_to_le32(BRCMF_PNO_WPA_AUTH_ANY); + pfn.wsec = cpu_to_le32(0); + pfn.infra = cpu_to_le32(1); + pfn.flags = cpu_to_le32(1 << BRCMF_PNO_HIDDEN_BIT); + pfn.ssid.SSID_len = cpu_to_le32(ssid_len); + memcpy(pfn.ssid.SSID, ssid->ssid, ssid_len); + ret = brcmf_fil_iovar_data_set(ifp, "pfn_add", &pfn, + sizeof(pfn)); + brcmf_dbg(SCAN, ">>> PNO filter %s for ssid (%s)\n", + ret == 0 ? "set" : "failed", ssid->ssid); + } + /* Enable the PNO */ + if (brcmf_fil_iovar_int_set(ifp, "pfn", 1) < 0) { + brcmf_err("PNO enable failed!! ret=%d\n", ret); + return -EINVAL; + } + } else { + return -EINVAL; + } + + return 0; +} + +static int brcmf_cfg80211_sched_scan_stop(struct wiphy *wiphy, + struct net_device *ndev) +{ + struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); + + brcmf_dbg(SCAN, "enter\n"); + brcmf_dev_pno_clean(ndev); + if (cfg->sched_escan) + brcmf_notify_escan_complete(cfg, netdev_priv(ndev), true, true); + return 0; +} + static __always_inline void brcmf_delay(u32 ms) { if (ms < 1000 / HZ) { @@ -3500,300 +3778,6 @@ brcmf_cfg80211_flush_pmksa(struct wiphy *wiphy, struct net_device *ndev) } -/* - * PFN result doesn't have all the info which are - * required by the supplicant - * (For e.g IEs) Do a target Escan so that sched scan results are reported - * via wl_inform_single_bss in the required format. Escan does require the - * scan request in the form of cfg80211_scan_request. For timebeing, create - * cfg80211_scan_request one out of the received PNO event. - */ -static s32 -brcmf_notify_sched_scan_results(struct brcmf_if *ifp, - const struct brcmf_event_msg *e, void *data) -{ - struct brcmf_cfg80211_info *cfg = ifp->drvr->config; - struct brcmf_pno_net_info_le *netinfo, *netinfo_start; - struct cfg80211_scan_request *request = NULL; - struct cfg80211_ssid *ssid = NULL; - struct ieee80211_channel *channel = NULL; - struct wiphy *wiphy = cfg_to_wiphy(cfg); - int err = 0; - int channel_req = 0; - int band = 0; - struct brcmf_pno_scanresults_le *pfn_result; - u32 result_count; - u32 status; - - brcmf_dbg(SCAN, "Enter\n"); - - if (e->event_code == BRCMF_E_PFN_NET_LOST) { - brcmf_dbg(SCAN, "PFN NET LOST event. Do Nothing\n"); - return 0; - } - - pfn_result = (struct brcmf_pno_scanresults_le *)data; - result_count = le32_to_cpu(pfn_result->count); - status = le32_to_cpu(pfn_result->status); - - /* - * PFN event is limited to fit 512 bytes so we may get - * multiple NET_FOUND events. For now place a warning here. - */ - WARN_ON(status != BRCMF_PNO_SCAN_COMPLETE); - brcmf_dbg(SCAN, "PFN NET FOUND event. count: %d\n", result_count); - if (result_count > 0) { - int i; - - request = kzalloc(sizeof(*request), GFP_KERNEL); - ssid = kcalloc(result_count, sizeof(*ssid), GFP_KERNEL); - channel = kcalloc(result_count, sizeof(*channel), GFP_KERNEL); - if (!request || !ssid || !channel) { - err = -ENOMEM; - goto out_err; - } - - request->wiphy = wiphy; - data += sizeof(struct brcmf_pno_scanresults_le); - netinfo_start = (struct brcmf_pno_net_info_le *)data; - - for (i = 0; i < result_count; i++) { - netinfo = &netinfo_start[i]; - if (!netinfo) { - brcmf_err("Invalid netinfo ptr. index: %d\n", - i); - err = -EINVAL; - goto out_err; - } - - brcmf_dbg(SCAN, "SSID:%s Channel:%d\n", - netinfo->SSID, netinfo->channel); - memcpy(ssid[i].ssid, netinfo->SSID, netinfo->SSID_len); - ssid[i].ssid_len = netinfo->SSID_len; - request->n_ssids++; - - channel_req = netinfo->channel; - if (channel_req <= CH_MAX_2G_CHANNEL) - band = NL80211_BAND_2GHZ; - else - band = NL80211_BAND_5GHZ; - channel[i].center_freq = - ieee80211_channel_to_frequency(channel_req, - band); - channel[i].band = band; - channel[i].flags |= IEEE80211_CHAN_NO_HT40; - request->channels[i] = &channel[i]; - request->n_channels++; - } - - /* assign parsed ssid array */ - if (request->n_ssids) - request->ssids = &ssid[0]; - - if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) { - /* Abort any on-going scan */ - brcmf_abort_scanning(cfg); - } - - set_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status); - cfg->escan_info.run = brcmf_run_escan; - err = brcmf_do_escan(cfg, wiphy, ifp, request); - if (err) { - clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status); - goto out_err; - } - cfg->sched_escan = true; - cfg->scan_request = request; - } else { - brcmf_err("FALSE PNO Event. (pfn_count == 0)\n"); - goto out_err; - } - - kfree(ssid); - kfree(channel); - kfree(request); - return 0; - -out_err: - kfree(ssid); - kfree(channel); - kfree(request); - cfg80211_sched_scan_stopped(wiphy); - return err; -} - -static int brcmf_dev_pno_clean(struct net_device *ndev) -{ - int ret; - - /* Disable pfn */ - ret = brcmf_fil_iovar_int_set(netdev_priv(ndev), "pfn", 0); - if (ret == 0) { - /* clear pfn */ - ret = brcmf_fil_iovar_data_set(netdev_priv(ndev), "pfnclear", - NULL, 0); - } - if (ret < 0) - brcmf_err("failed code %d\n", ret); - - return ret; -} - -static int brcmf_dev_pno_config(struct brcmf_if *ifp, - struct cfg80211_sched_scan_request *request) -{ - struct brcmf_pno_param_le pfn_param; - struct brcmf_pno_macaddr_le pfn_mac; - s32 err; - u8 *mac_mask; - int i; - - memset(&pfn_param, 0, sizeof(pfn_param)); - pfn_param.version = cpu_to_le32(BRCMF_PNO_VERSION); - - /* set extra pno params */ - pfn_param.flags = cpu_to_le16(1 << BRCMF_PNO_ENABLE_ADAPTSCAN_BIT); - pfn_param.repeat = BRCMF_PNO_REPEAT; - pfn_param.exp = BRCMF_PNO_FREQ_EXPO_MAX; - - /* set up pno scan fr */ - pfn_param.scan_freq = cpu_to_le32(BRCMF_PNO_TIME); - - err = brcmf_fil_iovar_data_set(ifp, "pfn_set", &pfn_param, - sizeof(pfn_param)); - if (err) { - brcmf_err("pfn_set failed, err=%d\n", err); - return err; - } - - /* Find out if mac randomization should be turned on */ - if (!(request->flags & NL80211_SCAN_FLAG_RANDOM_ADDR)) - return 0; - - pfn_mac.version = BRCMF_PFN_MACADDR_CFG_VER; - pfn_mac.flags = BRCMF_PFN_MAC_OUI_ONLY | BRCMF_PFN_SET_MAC_UNASSOC; - - memcpy(pfn_mac.mac, request->mac_addr, ETH_ALEN); - mac_mask = request->mac_addr_mask; - for (i = 0; i < ETH_ALEN; i++) { - pfn_mac.mac[i] &= mac_mask[i]; - pfn_mac.mac[i] |= get_random_int() & ~(mac_mask[i]); - } - /* Clear multi bit */ - pfn_mac.mac[0] &= 0xFE; - /* Set locally administered */ - pfn_mac.mac[0] |= 0x02; - - err = brcmf_fil_iovar_data_set(ifp, "pfn_macaddr", &pfn_mac, - sizeof(pfn_mac)); - if (err) - brcmf_err("pfn_macaddr failed, err=%d\n", err); - - return err; -} - -static int -brcmf_cfg80211_sched_scan_start(struct wiphy *wiphy, - struct net_device *ndev, - struct cfg80211_sched_scan_request *request) -{ - struct brcmf_if *ifp = netdev_priv(ndev); - struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy); - struct brcmf_pno_net_param_le pfn; - int i; - int ret = 0; - - brcmf_dbg(SCAN, "Enter n_match_sets:%d n_ssids:%d\n", - request->n_match_sets, request->n_ssids); - if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) { - brcmf_err("Scanning already: status (%lu)\n", cfg->scan_status); - return -EAGAIN; - } - if (test_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status)) { - brcmf_err("Scanning suppressed: status (%lu)\n", - cfg->scan_status); - return -EAGAIN; - } - - if (!request->n_ssids || !request->n_match_sets) { - brcmf_dbg(SCAN, "Invalid sched scan req!! n_ssids:%d\n", - request->n_ssids); - return -EINVAL; - } - - if (request->n_ssids > 0) { - for (i = 0; i < request->n_ssids; i++) { - /* Active scan req for ssids */ - brcmf_dbg(SCAN, ">>> Active scan req for ssid (%s)\n", - request->ssids[i].ssid); - - /* - * match_set ssids is a supert set of n_ssid list, - * so we need not add these set seperately. - */ - } - } - - if (request->n_match_sets > 0) { - /* clean up everything */ - ret = brcmf_dev_pno_clean(ndev); - if (ret < 0) { - brcmf_err("failed error=%d\n", ret); - return ret; - } - - /* configure pno */ - if (brcmf_dev_pno_config(ifp, request)) - return -EINVAL; - - /* configure each match set */ - for (i = 0; i < request->n_match_sets; i++) { - struct cfg80211_ssid *ssid; - u32 ssid_len; - - ssid = &request->match_sets[i].ssid; - ssid_len = ssid->ssid_len; - - if (!ssid_len) { - brcmf_err("skip broadcast ssid\n"); - continue; - } - pfn.auth = cpu_to_le32(WLAN_AUTH_OPEN); - pfn.wpa_auth = cpu_to_le32(BRCMF_PNO_WPA_AUTH_ANY); - pfn.wsec = cpu_to_le32(0); - pfn.infra = cpu_to_le32(1); - pfn.flags = cpu_to_le32(1 << BRCMF_PNO_HIDDEN_BIT); - pfn.ssid.SSID_len = cpu_to_le32(ssid_len); - memcpy(pfn.ssid.SSID, ssid->ssid, ssid_len); - ret = brcmf_fil_iovar_data_set(ifp, "pfn_add", &pfn, - sizeof(pfn)); - brcmf_dbg(SCAN, ">>> PNO filter %s for ssid (%s)\n", - ret == 0 ? "set" : "failed", ssid->ssid); - } - /* Enable the PNO */ - if (brcmf_fil_iovar_int_set(ifp, "pfn", 1) < 0) { - brcmf_err("PNO enable failed!! ret=%d\n", ret); - return -EINVAL; - } - } else { - return -EINVAL; - } - - return 0; -} - -static int brcmf_cfg80211_sched_scan_stop(struct wiphy *wiphy, - struct net_device *ndev) -{ - struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); - - brcmf_dbg(SCAN, "enter\n"); - brcmf_dev_pno_clean(ndev); - if (cfg->sched_escan) - brcmf_notify_escan_complete(cfg, netdev_priv(ndev), true, true); - return 0; -} - static s32 brcmf_configure_opensecurity(struct brcmf_if *ifp) { s32 err; From 4011fc49969019eb5e20824285831d873e885f77 Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Tue, 5 Jan 2016 11:05:47 +0100 Subject: [PATCH 49/52] brcmfmac: change brcmf_sdio_wd_timer() prototype The function brcmf_sdio_wd_timer() has wdtick parameter. However, it is only called with two values and as such the parameter is replaced with boolean value indicating the timer should be active or not. Reviewed-by: Hante Meuleman Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo --- .../broadcom/brcm80211/brcmfmac/sdio.c | 33 +++++++------------ .../broadcom/brcm80211/brcmfmac/sdio.h | 2 +- 2 files changed, 13 insertions(+), 22 deletions(-) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c index ceb2a754308e..2f020201945c 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c @@ -503,8 +503,7 @@ struct brcmf_sdio { struct timer_list timer; struct completion watchdog_wait; struct task_struct *watchdog_tsk; - bool wd_timer_valid; - uint save_ms; + bool wd_active; struct workqueue_struct *brcmf_wq; struct work_struct datawork; @@ -961,7 +960,7 @@ brcmf_sdio_bus_sleep(struct brcmf_sdio *bus, bool sleep, bool pendok) brcmf_sdio_clkctl(bus, CLK_NONE, pendok); } else { brcmf_sdio_clkctl(bus, CLK_AVAIL, pendok); - brcmf_sdio_wd_timer(bus, BRCMF_WD_POLL_MS); + brcmf_sdio_wd_timer(bus, true); } bus->sleeping = sleep; brcmf_dbg(SDIO, "new state %s\n", @@ -3576,7 +3575,7 @@ static void brcmf_sdio_bus_watchdog(struct brcmf_sdio *bus) if (bus->idlecount > bus->idletime) { brcmf_dbg(SDIO, "idle\n"); sdio_claim_host(bus->sdiodev->func[1]); - brcmf_sdio_wd_timer(bus, 0); + brcmf_sdio_wd_timer(bus, false); bus->idlecount = 0; brcmf_sdio_bus_sleep(bus, true, false); sdio_release_host(bus->sdiodev->func[1]); @@ -3908,7 +3907,7 @@ brcmf_sdio_watchdog(unsigned long data) if (bus->watchdog_tsk) { complete(&bus->watchdog_wait); /* Reschedule the watchdog */ - if (bus->wd_timer_valid) + if (bus->wd_active) mod_timer(&bus->timer, jiffies + msecs_to_jiffies(BRCMF_WD_POLL_MS)); } @@ -3950,7 +3949,7 @@ static void brcmf_sdio_firmware_callback(struct device *dev, /* Start the watchdog timer */ bus->sdcnt.tickcnt = 0; - brcmf_sdio_wd_timer(bus, BRCMF_WD_POLL_MS); + brcmf_sdio_wd_timer(bus, true); sdio_claim_host(sdiodev->func[1]); @@ -4195,7 +4194,7 @@ void brcmf_sdio_remove(struct brcmf_sdio *bus) if (bus->ci) { if (bus->sdiodev->state != BRCMF_SDIOD_NOMEDIUM) { sdio_claim_host(bus->sdiodev->func[1]); - brcmf_sdio_wd_timer(bus, 0); + brcmf_sdio_wd_timer(bus, false); brcmf_sdio_clkctl(bus, CLK_AVAIL, false); /* Leave the device in state where it is * 'passive'. This is done by resetting all @@ -4217,13 +4216,12 @@ void brcmf_sdio_remove(struct brcmf_sdio *bus) brcmf_dbg(TRACE, "Disconnected\n"); } -void brcmf_sdio_wd_timer(struct brcmf_sdio *bus, uint wdtick) +void brcmf_sdio_wd_timer(struct brcmf_sdio *bus, bool active) { /* Totally stop the timer */ - if (!wdtick && bus->wd_timer_valid) { + if (!active && bus->wd_active) { del_timer_sync(&bus->timer); - bus->wd_timer_valid = false; - bus->save_ms = wdtick; + bus->wd_active = false; return; } @@ -4231,27 +4229,20 @@ void brcmf_sdio_wd_timer(struct brcmf_sdio *bus, uint wdtick) if (bus->sdiodev->state != BRCMF_SDIOD_DATA) return; - if (wdtick) { - if (bus->save_ms != BRCMF_WD_POLL_MS) { - if (bus->wd_timer_valid) - /* Stop timer and restart at new value */ - del_timer_sync(&bus->timer); - + if (active) { + if (!bus->wd_active) { /* Create timer again when watchdog period is dynamically changed or in the first instance */ bus->timer.expires = jiffies + msecs_to_jiffies(BRCMF_WD_POLL_MS); add_timer(&bus->timer); - + bus->wd_active = true; } else { /* Re arm the timer, at last watchdog period */ mod_timer(&bus->timer, jiffies + msecs_to_jiffies(BRCMF_WD_POLL_MS)); } - - bus->wd_timer_valid = true; - bus->save_ms = wdtick; } } diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.h index d86ecf26f31a..ff47ceedb1f4 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.h @@ -369,7 +369,7 @@ struct brcmf_sdio *brcmf_sdio_probe(struct brcmf_sdio_dev *sdiodev); void brcmf_sdio_remove(struct brcmf_sdio *bus); void brcmf_sdio_isr(struct brcmf_sdio *bus); -void brcmf_sdio_wd_timer(struct brcmf_sdio *bus, uint wdtick); +void brcmf_sdio_wd_timer(struct brcmf_sdio *bus, bool active); void brcmf_sdio_wowl_config(struct device *dev, bool enabled); int brcmf_sdio_sleep(struct brcmf_sdio *bus, bool sleep); void brcmf_sdio_trigger_dpc(struct brcmf_sdio *bus); From 63ce3d5db09324b56b739bfde5704799b0d6fd89 Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Tue, 5 Jan 2016 11:05:48 +0100 Subject: [PATCH 50/52] brcmfmac: use msecs_to_jiffies() in macro definitions Instead to having macro definition for millisecond timeout have the definition directly in jiffies. This makes the unit of the value immediately clear and may result in code that is bit more compact. Reviewed-by: Hante Meuleman Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo --- .../broadcom/brcm80211/brcmfmac/cfg80211.c | 2 +- .../broadcom/brcm80211/brcmfmac/cfg80211.h | 2 ++ .../broadcom/brcm80211/brcmfmac/core.c | 4 ++-- .../broadcom/brcm80211/brcmfmac/msgbuf.c | 4 ++-- .../wireless/broadcom/brcm80211/brcmfmac/p2p.c | 18 +++++++++--------- .../broadcom/brcm80211/brcmfmac/pcie.c | 7 +++---- .../broadcom/brcm80211/brcmfmac/sdio.c | 18 ++++++++---------- .../broadcom/brcm80211/brcmfmac/sdio.h | 4 ++-- .../wireless/broadcom/brcm80211/brcmfmac/usb.c | 5 ++--- 9 files changed, 31 insertions(+), 33 deletions(-) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c index c9657be3d4c3..84788ea9e39e 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c @@ -568,7 +568,7 @@ struct wireless_dev *brcmf_ap_add_vif(struct wiphy *wiphy, const char *name, /* wait for firmware event */ err = brcmf_cfg80211_wait_vif_event_timeout(cfg, BRCMF_E_IF_ADD, - msecs_to_jiffies(1500)); + BRCMF_VIF_EVENT_TIMEOUT); brcmf_cfg80211_arm_vif_event(cfg, NULL); if (!err) { brcmf_err("timeout occurred\n"); diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h index 69af708b43f5..40efb539ac26 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h @@ -75,6 +75,8 @@ #define BRCMF_DEFAULT_BCN_TIMEOUT_ROAM_ON 2 #define BRCMF_DEFAULT_BCN_TIMEOUT_ROAM_OFF 4 +#define BRCMF_VIF_EVENT_TIMEOUT msecs_to_jiffies(1500) + /** * enum brcmf_scan_status - scan engine status * diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c index 7c75b1acdf00..ed9998b69709 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c @@ -40,7 +40,7 @@ MODULE_AUTHOR("Broadcom Corporation"); MODULE_DESCRIPTION("Broadcom 802.11 wireless LAN fullmac driver."); MODULE_LICENSE("Dual BSD/GPL"); -#define MAX_WAIT_FOR_8021X_TX 50 /* msecs */ +#define MAX_WAIT_FOR_8021X_TX msecs_to_jiffies(50) /* AMPDU rx reordering definitions */ #define BRCMF_RXREORDER_FLOWID_OFFSET 0 @@ -1282,7 +1282,7 @@ int brcmf_netdev_wait_pend8021x(struct brcmf_if *ifp) err = wait_event_timeout(ifp->pend_8021x_wait, !brcmf_get_pend_8021x_cnt(ifp), - msecs_to_jiffies(MAX_WAIT_FOR_8021X_TX)); + MAX_WAIT_FOR_8021X_TX); WARN_ON(!err); diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.c index 5df91386e13a..c2bdb91746cf 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/msgbuf.c @@ -34,7 +34,7 @@ #include "tracepoint.h" -#define MSGBUF_IOCTL_RESP_TIMEOUT 2000 +#define MSGBUF_IOCTL_RESP_TIMEOUT msecs_to_jiffies(2000) #define MSGBUF_TYPE_GEN_STATUS 0x1 #define MSGBUF_TYPE_RING_STATUS 0x2 @@ -466,7 +466,7 @@ static int brcmf_msgbuf_ioctl_resp_wait(struct brcmf_msgbuf *msgbuf) { return wait_event_timeout(msgbuf->ioctl_resp_wait, msgbuf->ctl_completed, - msecs_to_jiffies(MSGBUF_IOCTL_RESP_TIMEOUT)); + MSGBUF_IOCTL_RESP_TIMEOUT); } diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c index b23dcbcd505e..821b6494f9d1 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c @@ -71,10 +71,10 @@ #define P2P_AF_MED_DWELL_TIME 400 #define P2P_AF_LONG_DWELL_TIME 1000 #define P2P_AF_TX_MAX_RETRY 1 -#define P2P_AF_MAX_WAIT_TIME 2000 +#define P2P_AF_MAX_WAIT_TIME msecs_to_jiffies(2000) #define P2P_INVALID_CHANNEL -1 #define P2P_CHANNEL_SYNC_RETRY 5 -#define P2P_AF_FRM_SCAN_MAX_WAIT 1500 +#define P2P_AF_FRM_SCAN_MAX_WAIT msecs_to_jiffies(1500) #define P2P_DEFAULT_SLEEP_TIME_VSDB 200 /* WiFi P2P Public Action Frame OUI Subtypes */ @@ -102,6 +102,7 @@ #define P2PSD_ACTION_ID_GAS_CREQ 0x0c /* GAS Comback Request AF */ #define P2PSD_ACTION_ID_GAS_CRESP 0x0d /* GAS Comback Response AF */ +#define BRCMF_P2P_DISABLE_TIMEOUT msecs_to_jiffies(500) /** * struct brcmf_p2p_disc_st_le - set discovery state in firmware. * @@ -1514,7 +1515,7 @@ static s32 brcmf_p2p_tx_action_frame(struct brcmf_p2p_info *p2p, p2p->af_tx_sent_jiffies = jiffies; timeout = wait_for_completion_timeout(&p2p->send_af_done, - msecs_to_jiffies(P2P_AF_MAX_WAIT_TIME)); + P2P_AF_MAX_WAIT_TIME); if (test_bit(BRCMF_P2P_STATUS_ACTION_TX_COMPLETED, &p2p->status)) { brcmf_dbg(TRACE, "TX action frame operation is success\n"); @@ -1988,7 +1989,7 @@ int brcmf_p2p_ifchange(struct brcmf_cfg80211_info *cfg, return err; } err = brcmf_cfg80211_wait_vif_event_timeout(cfg, BRCMF_E_IF_CHANGE, - msecs_to_jiffies(1500)); + BRCMF_VIF_EVENT_TIMEOUT); brcmf_cfg80211_arm_vif_event(cfg, NULL); if (!err) { brcmf_err("No BRCMF_E_IF_CHANGE event received\n"); @@ -2090,7 +2091,7 @@ static struct wireless_dev *brcmf_p2p_create_p2pdev(struct brcmf_p2p_info *p2p, /* wait for firmware event */ err = brcmf_cfg80211_wait_vif_event_timeout(p2p->cfg, BRCMF_E_IF_ADD, - msecs_to_jiffies(1500)); + BRCMF_VIF_EVENT_TIMEOUT); brcmf_cfg80211_arm_vif_event(p2p->cfg, NULL); brcmf_fweh_p2pdev_setup(pri_ifp, false); if (!err) { @@ -2180,7 +2181,7 @@ struct wireless_dev *brcmf_p2p_add_vif(struct wiphy *wiphy, const char *name, /* wait for firmware event */ err = brcmf_cfg80211_wait_vif_event_timeout(cfg, BRCMF_E_IF_ADD, - msecs_to_jiffies(1500)); + BRCMF_VIF_EVENT_TIMEOUT); brcmf_cfg80211_arm_vif_event(cfg, NULL); if (!err) { brcmf_err("timeout occurred\n"); @@ -2230,7 +2231,6 @@ int brcmf_p2p_del_vif(struct wiphy *wiphy, struct wireless_dev *wdev) struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy); struct brcmf_p2p_info *p2p = &cfg->p2p; struct brcmf_cfg80211_vif *vif; - unsigned long jiffie_timeout = msecs_to_jiffies(1500); bool wait_for_disable = false; int err; @@ -2263,7 +2263,7 @@ int brcmf_p2p_del_vif(struct wiphy *wiphy, struct wireless_dev *wdev) if (wait_for_disable) wait_for_completion_timeout(&cfg->vif_disabled, - msecs_to_jiffies(500)); + BRCMF_P2P_DISABLE_TIMEOUT); err = 0; if (vif->wdev.iftype != NL80211_IFTYPE_P2P_DEVICE) { @@ -2273,7 +2273,7 @@ int brcmf_p2p_del_vif(struct wiphy *wiphy, struct wireless_dev *wdev) if (!err) { /* wait for firmware event */ err = brcmf_cfg80211_wait_vif_event_timeout(cfg, BRCMF_E_IF_DEL, - jiffie_timeout); + BRCMF_VIF_EVENT_TIMEOUT); if (!err) err = -EIO; else diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c index 3d2d790d3ad6..0480b70e3eb8 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c @@ -191,7 +191,7 @@ static struct brcmf_firmware_mapping brcmf_pcie_fwnames[] = { #define BRCMF_H2D_HOST_D0_INFORM_IN_USE 0x00000008 #define BRCMF_H2D_HOST_D0_INFORM 0x00000010 -#define BRCMF_PCIE_MBDATA_TIMEOUT 2000 +#define BRCMF_PCIE_MBDATA_TIMEOUT msecs_to_jiffies(2000) #define BRCMF_PCIE_CFGREG_STATUS_CMD 0x4 #define BRCMF_PCIE_CFGREG_PM_CSR 0x4C @@ -1885,9 +1885,8 @@ static int brcmf_pcie_pm_enter_D3(struct device *dev) devinfo->mbdata_completed = false; brcmf_pcie_send_mb_data(devinfo, BRCMF_H2D_HOST_D3_INFORM); - wait_event_timeout(devinfo->mbdata_resp_wait, - devinfo->mbdata_completed, - msecs_to_jiffies(BRCMF_PCIE_MBDATA_TIMEOUT)); + wait_event_timeout(devinfo->mbdata_resp_wait, devinfo->mbdata_completed, + BRCMF_PCIE_MBDATA_TIMEOUT); if (!devinfo->mbdata_completed) { brcmf_err("Timeout on response for entering D3 substate\n"); return -EIO; diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c index 2f020201945c..dd6614332836 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c @@ -45,8 +45,8 @@ #include "chip.h" #include "firmware.h" -#define DCMD_RESP_TIMEOUT 2000 /* In milli second */ -#define CTL_DONE_TIMEOUT 2000 /* In milli second */ +#define DCMD_RESP_TIMEOUT msecs_to_jiffies(2000) +#define CTL_DONE_TIMEOUT msecs_to_jiffies(2000) #ifdef DEBUG @@ -1657,7 +1657,7 @@ static int brcmf_sdio_dcmd_resp_wait(struct brcmf_sdio *bus, uint *condition, bool *pending) { DECLARE_WAITQUEUE(wait, current); - int timeout = msecs_to_jiffies(DCMD_RESP_TIMEOUT); + int timeout = DCMD_RESP_TIMEOUT; /* Wait until control frame is available */ add_wait_queue(&bus->dcmd_resp_wait, &wait); @@ -2842,7 +2842,7 @@ brcmf_sdio_bus_txctl(struct device *dev, unsigned char *msg, uint msglen) brcmf_sdio_trigger_dpc(bus); wait_event_interruptible_timeout(bus->ctrl_wait, !bus->ctrl_frame_stat, - msecs_to_jiffies(CTL_DONE_TIMEOUT)); + CTL_DONE_TIMEOUT); ret = 0; if (bus->ctrl_frame_stat) { sdio_claim_host(bus->sdiodev->func[1]); @@ -3552,7 +3552,7 @@ static void brcmf_sdio_bus_watchdog(struct brcmf_sdio *bus) /* Poll for console output periodically */ if (bus->sdiodev->state == BRCMF_SDIOD_DATA && BRCMF_FWCON_ON() && bus->console_interval != 0) { - bus->console.count += BRCMF_WD_POLL_MS; + bus->console.count += jiffies_to_msecs(BRCMF_WD_POLL); if (bus->console.count >= bus->console_interval) { bus->console.count -= bus->console_interval; sdio_claim_host(bus->sdiodev->func[1]); @@ -3909,7 +3909,7 @@ brcmf_sdio_watchdog(unsigned long data) /* Reschedule the watchdog */ if (bus->wd_active) mod_timer(&bus->timer, - jiffies + msecs_to_jiffies(BRCMF_WD_POLL_MS)); + jiffies + BRCMF_WD_POLL); } } @@ -4234,14 +4234,12 @@ void brcmf_sdio_wd_timer(struct brcmf_sdio *bus, bool active) /* Create timer again when watchdog period is dynamically changed or in the first instance */ - bus->timer.expires = - jiffies + msecs_to_jiffies(BRCMF_WD_POLL_MS); + bus->timer.expires = jiffies + BRCMF_WD_POLL; add_timer(&bus->timer); bus->wd_active = true; } else { /* Re arm the timer, at last watchdog period */ - mod_timer(&bus->timer, - jiffies + msecs_to_jiffies(BRCMF_WD_POLL_MS)); + mod_timer(&bus->timer, jiffies + BRCMF_WD_POLL); } } } diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.h index ff47ceedb1f4..5ec7a6d87672 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.h @@ -152,8 +152,8 @@ /* Packet alignment for most efficient SDIO (can change based on platform) */ #define BRCMF_SDALIGN (1 << 6) -/* watchdog polling interval in ms */ -#define BRCMF_WD_POLL_MS 10 +/* watchdog polling interval */ +#define BRCMF_WD_POLL msecs_to_jiffies(10) /** * enum brcmf_sdiod_state - the state of the bus. diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c index 66c26a92b29c..c72b7b352a77 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c @@ -29,7 +29,7 @@ #include "usb.h" -#define IOCTL_RESP_TIMEOUT 2000 +#define IOCTL_RESP_TIMEOUT msecs_to_jiffies(2000) #define BRCMF_USB_RESET_GETVER_SPINWAIT 100 /* in unit of ms */ #define BRCMF_USB_RESET_GETVER_LOOP_CNT 10 @@ -190,8 +190,7 @@ static struct brcmf_usbdev_info *brcmf_usb_get_businfo(struct device *dev) static int brcmf_usb_ioctl_resp_wait(struct brcmf_usbdev_info *devinfo) { return wait_event_timeout(devinfo->ioctl_resp_wait, - devinfo->ctl_completed, - msecs_to_jiffies(IOCTL_RESP_TIMEOUT)); + devinfo->ctl_completed, IOCTL_RESP_TIMEOUT); } static void brcmf_usb_ioctl_resp_wake(struct brcmf_usbdev_info *devinfo) From 5ce96c0808ac8ad82f70b156ceb77ec2008c82e3 Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Tue, 5 Jan 2016 11:05:49 +0100 Subject: [PATCH 51/52] brcmfmac: use jiffies for timeout in btcoex The btcoex uses a timeout which was in milliseconds and got converted to jiffies upon using timer api. Instead, convert it to jiffies and treat it as such further. Reviewed-by: Hante Meuleman Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo --- .../wireless/broadcom/brcm80211/brcmfmac/btcoex.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/btcoex.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/btcoex.c index 4e33f96b3dd1..14a70d4b4e86 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/btcoex.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/btcoex.c @@ -29,7 +29,7 @@ #include "cfg80211.h" /* T1 start SCO/eSCO priority suppression */ -#define BRCMF_BTCOEX_OPPR_WIN_TIME 2000 +#define BRCMF_BTCOEX_OPPR_WIN_TIME msecs_to_jiffies(2000) /* BT registers values during DHCP */ #define BRCMF_BT_DHCP_REG50 0x8022 @@ -314,8 +314,7 @@ static void brcmf_btcoex_handler(struct work_struct *work) } else { btci->timeout -= BRCMF_BTCOEX_OPPR_WIN_TIME; mod_timer(&btci->timer, - jiffies + - msecs_to_jiffies(BRCMF_BTCOEX_OPPR_WIN_TIME)); + jiffies + BRCMF_BTCOEX_OPPR_WIN_TIME); } btci->timer_on = true; break; @@ -328,12 +327,11 @@ static void brcmf_btcoex_handler(struct work_struct *work) /* DHCP is not over yet, start lowering BT priority */ brcmf_dbg(INFO, "DHCP T1:%d expired\n", - BRCMF_BTCOEX_OPPR_WIN_TIME); + jiffies_to_msecs(BRCMF_BTCOEX_OPPR_WIN_TIME)); brcmf_btcoex_boost_wifi(btci, true); btci->bt_state = BRCMF_BT_DHCP_FLAG_FORCE_TIMEOUT; - mod_timer(&btci->timer, - jiffies + msecs_to_jiffies(btci->timeout)); + mod_timer(&btci->timer, jiffies + btci->timeout); btci->timer_on = true; break; @@ -477,7 +475,7 @@ int brcmf_btcoex_set_mode(struct brcmf_cfg80211_vif *vif, return -EBUSY; /* Start BT timer only for SCO connection */ if (brcmf_btcoex_is_sco_active(ifp)) { - btci->timeout = duration; + btci->timeout = msecs_to_jiffies(duration); btci->vif = vif; brcmf_btcoex_dhcp_start(btci); } From 42e0ed0d454c6ad7be67f2c18828391ecfdc9a62 Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Tue, 5 Jan 2016 11:05:50 +0100 Subject: [PATCH 52/52] brcmfmac: Do not handle link downs for ibss. Sometimes on module reload and reconnect to ibss a deauth from other station can be received. This is treated as a link down but for ibss this is wrong. It will close the interface and no data is possible. Ignore the firmware generated link down events in ibss mode, as ibss is always teared down by cfg80211. Reviewed-by: Arend Van Spriel Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: Kalle Valo --- .../wireless/broadcom/brcm80211/brcmfmac/cfg80211.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c index 84788ea9e39e..7b01e4ddb315 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c @@ -1454,6 +1454,7 @@ brcmf_cfg80211_leave_ibss(struct wiphy *wiphy, struct net_device *ndev) } brcmf_link_down(ifp->vif, WLAN_REASON_DEAUTH_LEAVING); + brcmf_net_setcarrier(ifp, false); brcmf_dbg(TRACE, "Exit\n"); @@ -5248,12 +5249,13 @@ brcmf_notify_connect_status(struct brcmf_if *ifp, brcmf_dbg(CONN, "Linkdown\n"); if (!brcmf_is_ibssmode(ifp->vif)) { brcmf_bss_connect_done(cfg, ndev, e, false); + brcmf_link_down(ifp->vif, + brcmf_map_fw_linkdown_reason(e)); + brcmf_init_prof(ndev_to_prof(ndev)); + if (ndev != cfg_to_ndev(cfg)) + complete(&cfg->vif_disabled); + brcmf_net_setcarrier(ifp, false); } - brcmf_link_down(ifp->vif, brcmf_map_fw_linkdown_reason(e)); - brcmf_init_prof(ndev_to_prof(ndev)); - if (ndev != cfg_to_ndev(cfg)) - complete(&cfg->vif_disabled); - brcmf_net_setcarrier(ifp, false); } else if (brcmf_is_nonetwork(cfg, e)) { if (brcmf_is_ibssmode(ifp->vif)) clear_bit(BRCMF_VIF_STATUS_CONNECTING,