12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788 |
- 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 */
|