351-0003-brcmfmac-Fix-kernel-oops-in-failed-chip_attach.patch 3.0 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788
  1. From: Christian Daudt <csd@broadcom.com>
  2. Date: Wed, 11 May 2016 15:06:48 -0700
  3. Subject: [PATCH] brcmfmac: Fix kernel oops in failed chip_attach
  4. When chip attach fails, brcmf_sdiod_intr_unregister is being called
  5. but that is too early as sdiodev->settings has not been set yet
  6. nor has brcmf_sdiod_intr_register been called.
  7. Change to use oob_irq_requested + newly created sd_irq_requested
  8. to decide on what to unregister at intr_unregister time.
  9. Steps to reproduce problem:
  10. - modprobe brcmfmac using buggy FW
  11. - rmmod brcmfmac
  12. - modprobe brcmfmac again.
  13. If done with a buggy firmware, brcm_chip_attach will fail on the
  14. 2nd modprobe triggering the call to intr_unregister and the
  15. kernel oops when attempting to de-reference sdiodev->settings->bus.sdio
  16. which has not yet been set.
  17. Signed-off-by: Christian Daudt <csd@broadcom.com>
  18. Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
  19. ---
  20. --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c
  21. +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c
  22. @@ -166,6 +166,7 @@ int brcmf_sdiod_intr_register(struct brc
  23. sdio_claim_irq(sdiodev->func[1], brcmf_sdiod_ib_irqhandler);
  24. sdio_claim_irq(sdiodev->func[2], brcmf_sdiod_dummy_irqhandler);
  25. sdio_release_host(sdiodev->func[1]);
  26. + sdiodev->sd_irq_requested = true;
  27. }
  28. return 0;
  29. @@ -173,27 +174,30 @@ int brcmf_sdiod_intr_register(struct brc
  30. int brcmf_sdiod_intr_unregister(struct brcmf_sdio_dev *sdiodev)
  31. {
  32. - struct brcmfmac_sdio_pd *pdata;
  33. - brcmf_dbg(SDIO, "Entering\n");
  34. + brcmf_dbg(SDIO, "Entering oob=%d sd=%d\n",
  35. + sdiodev->oob_irq_requested,
  36. + sdiodev->sd_irq_requested);
  37. - pdata = &sdiodev->settings->bus.sdio;
  38. - if (pdata->oob_irq_supported) {
  39. + if (sdiodev->oob_irq_requested) {
  40. + struct brcmfmac_sdio_pd *pdata;
  41. +
  42. + pdata = &sdiodev->settings->bus.sdio;
  43. sdio_claim_host(sdiodev->func[1]);
  44. brcmf_sdiod_regwb(sdiodev, SDIO_CCCR_BRCM_SEPINT, 0, NULL);
  45. brcmf_sdiod_regwb(sdiodev, SDIO_CCCR_IENx, 0, NULL);
  46. sdio_release_host(sdiodev->func[1]);
  47. - if (sdiodev->oob_irq_requested) {
  48. - sdiodev->oob_irq_requested = false;
  49. - if (sdiodev->irq_wake) {
  50. - disable_irq_wake(pdata->oob_irq_nr);
  51. - sdiodev->irq_wake = false;
  52. - }
  53. - free_irq(pdata->oob_irq_nr, &sdiodev->func[1]->dev);
  54. - sdiodev->irq_en = false;
  55. + sdiodev->oob_irq_requested = false;
  56. + if (sdiodev->irq_wake) {
  57. + disable_irq_wake(pdata->oob_irq_nr);
  58. + sdiodev->irq_wake = false;
  59. }
  60. - } else {
  61. + free_irq(pdata->oob_irq_nr, &sdiodev->func[1]->dev);
  62. + sdiodev->irq_en = false;
  63. + }
  64. +
  65. + if (sdiodev->sd_irq_requested) {
  66. sdio_claim_host(sdiodev->func[1]);
  67. sdio_release_irq(sdiodev->func[2]);
  68. sdio_release_irq(sdiodev->func[1]);
  69. --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.h
  70. +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.h
  71. @@ -186,6 +186,7 @@ struct brcmf_sdio_dev {
  72. struct brcmf_bus *bus_if;
  73. struct brcmf_mp_device *settings;
  74. bool oob_irq_requested;
  75. + bool sd_irq_requested;
  76. bool irq_en; /* irq enable flags */
  77. spinlock_t irq_en_lock;
  78. bool irq_wake; /* irq wake enable flags */