aboutsummaryrefslogtreecommitdiffstats
path: root/package/kernel/mac80211/patches/351-0003-brcmfmac-Fix-kernel-oops-in-failed-chip_attach.patch
diff options
context:
space:
mode:
Diffstat (limited to 'package/kernel/mac80211/patches/351-0003-brcmfmac-Fix-kernel-oops-in-failed-chip_attach.patch')
-rw-r--r--package/kernel/mac80211/patches/351-0003-brcmfmac-Fix-kernel-oops-in-failed-chip_attach.patch88
1 files changed, 88 insertions, 0 deletions
diff --git a/package/kernel/mac80211/patches/351-0003-brcmfmac-Fix-kernel-oops-in-failed-chip_attach.patch b/package/kernel/mac80211/patches/351-0003-brcmfmac-Fix-kernel-oops-in-failed-chip_attach.patch
new file mode 100644
index 0000000000..59f1e42d4d
--- /dev/null
+++ b/package/kernel/mac80211/patches/351-0003-brcmfmac-Fix-kernel-oops-in-failed-chip_attach.patch
@@ -0,0 +1,88 @@
+From: Christian Daudt <csd@broadcom.com>
+Date: Wed, 11 May 2016 15:06:48 -0700
+Subject: [PATCH] brcmfmac: Fix kernel oops in failed chip_attach
+
+When chip attach fails, brcmf_sdiod_intr_unregister is being called
+but that is too early as sdiodev->settings has not been set yet
+nor has brcmf_sdiod_intr_register been called.
+Change to use oob_irq_requested + newly created sd_irq_requested
+to decide on what to unregister at intr_unregister time.
+
+Steps to reproduce problem:
+- modprobe brcmfmac using buggy FW
+- rmmod brcmfmac
+- modprobe brcmfmac again.
+
+If done with a buggy firmware, brcm_chip_attach will fail on the
+2nd modprobe triggering the call to intr_unregister and the
+kernel oops when attempting to de-reference sdiodev->settings->bus.sdio
+which has not yet been set.
+
+Signed-off-by: Christian Daudt <csd@broadcom.com>
+Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
+---
+
+--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c
++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c
+@@ -166,6 +166,7 @@ int brcmf_sdiod_intr_register(struct brc
+ sdio_claim_irq(sdiodev->func[1], brcmf_sdiod_ib_irqhandler);
+ sdio_claim_irq(sdiodev->func[2], brcmf_sdiod_dummy_irqhandler);
+ sdio_release_host(sdiodev->func[1]);
++ sdiodev->sd_irq_requested = true;
+ }
+
+ return 0;
+@@ -173,27 +174,30 @@ int brcmf_sdiod_intr_register(struct brc
+
+ int brcmf_sdiod_intr_unregister(struct brcmf_sdio_dev *sdiodev)
+ {
+- struct brcmfmac_sdio_pd *pdata;
+
+- brcmf_dbg(SDIO, "Entering\n");
++ brcmf_dbg(SDIO, "Entering oob=%d sd=%d\n",
++ sdiodev->oob_irq_requested,
++ sdiodev->sd_irq_requested);
+
+- pdata = &sdiodev->settings->bus.sdio;
+- if (pdata->oob_irq_supported) {
++ if (sdiodev->oob_irq_requested) {
++ struct brcmfmac_sdio_pd *pdata;
++
++ pdata = &sdiodev->settings->bus.sdio;
+ sdio_claim_host(sdiodev->func[1]);
+ brcmf_sdiod_regwb(sdiodev, SDIO_CCCR_BRCM_SEPINT, 0, NULL);
+ brcmf_sdiod_regwb(sdiodev, SDIO_CCCR_IENx, 0, NULL);
+ sdio_release_host(sdiodev->func[1]);
+
+- if (sdiodev->oob_irq_requested) {
+- sdiodev->oob_irq_requested = false;
+- if (sdiodev->irq_wake) {
+- disable_irq_wake(pdata->oob_irq_nr);
+- sdiodev->irq_wake = false;
+- }
+- free_irq(pdata->oob_irq_nr, &sdiodev->func[1]->dev);
+- sdiodev->irq_en = false;
++ sdiodev->oob_irq_requested = false;
++ if (sdiodev->irq_wake) {
++ disable_irq_wake(pdata->oob_irq_nr);
++ sdiodev->irq_wake = false;
+ }
+- } else {
++ free_irq(pdata->oob_irq_nr, &sdiodev->func[1]->dev);
++ sdiodev->irq_en = false;
++ }
++
++ if (sdiodev->sd_irq_requested) {
+ sdio_claim_host(sdiodev->func[1]);
+ sdio_release_irq(sdiodev->func[2]);
+ sdio_release_irq(sdiodev->func[1]);
+--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.h
++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.h
+@@ -186,6 +186,7 @@ struct brcmf_sdio_dev {
+ struct brcmf_bus *bus_if;
+ struct brcmf_mp_device *settings;
+ bool oob_irq_requested;
++ bool sd_irq_requested;
+ bool irq_en; /* irq enable flags */
+ spinlock_t irq_en_lock;
+ bool irq_wake; /* irq wake enable flags */