diff options
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.patch | 88 |
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 */ |