Merge tag 'wireless-drivers-2020-12-22' of git://git.kernel.org/pub/scm/linux/kernel...
authorJakub Kicinski <kuba@kernel.org>
Wed, 23 Dec 2020 03:43:32 +0000 (19:43 -0800)
committerJakub Kicinski <kuba@kernel.org>
Wed, 23 Dec 2020 03:43:33 +0000 (19:43 -0800)
Kalle Valo says:

====================
wireless-drivers fixes for v5.11

First set of fixes for v5.11, more fixes than usual this time. For
ath11k we have several fixes for QCA6390 PCI support and mt76 has
several. Also one build fix for mt76.

mt76
 * fix two NULL pointer dereference
 * fix build error when CONFIG_MAC80211_MESH is disabled

rtlwifi
 * fix use-after-free in firmware handling code

ath11k
 * error handling fixes
 * fix crash found during connect and disconnect test
 * handle HT disable better
 * avoid printing qmi memory failure during firmware bootup
 * disable ASPM during firmware bootup

* tag 'wireless-drivers-2020-12-22' of git://git.kernel.org/pub/scm/linux/kernel/git/kvalo/wireless-drivers:
  MAINTAINERS: switch to different email address
  mt76: mt7915: fix MESH ifdef block
  mt76: mt76s: fix NULL pointer dereference in mt76s_process_tx_queue
  mt76: sdio: remove wake logic in mt76s_process_tx_queue
  mt76: usb: remove wake logic in mt76u_status_worker
  ath11k: pci: disable ASPM L0sLs before downloading firmware
  ath11k: qmi: try to allocate a big block of DMA memory first
  rtlwifi: rise completion at the last step of firmware callback
  mt76: mt76u: fix NULL pointer dereference in mt76u_status_worker
  ath11k: Fix ath11k_pci_fix_l1ss()
  ath11k: Fix error code in ath11k_core_suspend()
  ath11k: start vdev if a bss peer is already created
  ath11k: fix crash caused by NULL rx_channel
  ath11k: add missing null check on allocated skb
====================

Link: https://lore.kernel.org/r/20201222163727.D4336C433C6@smtp.codeaurora.org
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
15 files changed:
MAINTAINERS
drivers/net/wireless/ath/ath11k/core.c
drivers/net/wireless/ath/ath11k/dp_rx.c
drivers/net/wireless/ath/ath11k/mac.c
drivers/net/wireless/ath/ath11k/pci.c
drivers/net/wireless/ath/ath11k/pci.h
drivers/net/wireless/ath/ath11k/peer.c
drivers/net/wireless/ath/ath11k/peer.h
drivers/net/wireless/ath/ath11k/qmi.c
drivers/net/wireless/ath/ath11k/qmi.h
drivers/net/wireless/ath/ath11k/wmi.c
drivers/net/wireless/mediatek/mt76/mt7915/init.c
drivers/net/wireless/mediatek/mt76/sdio.c
drivers/net/wireless/mediatek/mt76/usb.c
drivers/net/wireless/realtek/rtlwifi/core.c

index 20cd4cc08dd10afadff50ed524713b84d83f6c94..80f75ab960cd8ed8ceb00459e0135c3b13df32d4 100644 (file)
@@ -3553,7 +3553,7 @@ S:        Supported
 F:     drivers/net/ethernet/broadcom/bnxt/
 
 BROADCOM BRCM80211 IEEE802.11n WIRELESS DRIVER
-M:     Arend van Spriel <arend.vanspriel@broadcom.com>
+M:     Arend van Spriel <aspriel@gmail.com>
 M:     Franky Lin <franky.lin@broadcom.com>
 M:     Hante Meuleman <hante.meuleman@broadcom.com>
 M:     Chi-hsien Lin <chi-hsien.lin@infineon.com>
index b97c38b9a270135c5e4f25d84ea781885bc918c1..350b7913622cb76517c42fde3306acc89ba4799c 100644 (file)
@@ -185,7 +185,7 @@ int ath11k_core_suspend(struct ath11k_base *ab)
        ath11k_hif_ce_irq_disable(ab);
 
        ret = ath11k_hif_suspend(ab);
-       if (!ret) {
+       if (ret) {
                ath11k_warn(ab, "failed to suspend hif: %d\n", ret);
                return ret;
        }
index 205c0f1a40e91d33f84b85f0d68cf4fbf1e3bfb9..920e5026a635fceabfa6f3e388f034dbe886d932 100644 (file)
@@ -2294,6 +2294,7 @@ static void ath11k_dp_rx_h_ppdu(struct ath11k *ar, struct hal_rx_desc *rx_desc,
 {
        u8 channel_num;
        u32 center_freq;
+       struct ieee80211_channel *channel;
 
        rx_status->freq = 0;
        rx_status->rate_idx = 0;
@@ -2314,9 +2315,12 @@ static void ath11k_dp_rx_h_ppdu(struct ath11k *ar, struct hal_rx_desc *rx_desc,
                rx_status->band = NL80211_BAND_5GHZ;
        } else {
                spin_lock_bh(&ar->data_lock);
-               rx_status->band = ar->rx_channel->band;
-               channel_num =
-                       ieee80211_frequency_to_channel(ar->rx_channel->center_freq);
+               channel = ar->rx_channel;
+               if (channel) {
+                       rx_status->band = channel->band;
+                       channel_num =
+                               ieee80211_frequency_to_channel(channel->center_freq);
+               }
                spin_unlock_bh(&ar->data_lock);
                ath11k_dbg_dump(ar->ab, ATH11K_DBG_DATA, NULL, "rx_desc: ",
                                rx_desc, sizeof(struct hal_rx_desc));
index 5c175e3e09b2850e9d03959c60d1369c80c7b6a0..c1608f64ea95d6b692701e8cf78c1aa9cd8d3872 100644 (file)
@@ -3021,6 +3021,7 @@ static int ath11k_mac_station_add(struct ath11k *ar,
        }
 
        if (ab->hw_params.vdev_start_delay &&
+           !arvif->is_started &&
            arvif->vdev_type != WMI_VDEV_TYPE_AP) {
                ret = ath11k_start_vdev_delay(ar->hw, vif);
                if (ret) {
@@ -5284,7 +5285,8 @@ ath11k_mac_op_assign_vif_chanctx(struct ieee80211_hw *hw,
        /* for QCA6390 bss peer must be created before vdev_start */
        if (ab->hw_params.vdev_start_delay &&
            arvif->vdev_type != WMI_VDEV_TYPE_AP &&
-           arvif->vdev_type != WMI_VDEV_TYPE_MONITOR) {
+           arvif->vdev_type != WMI_VDEV_TYPE_MONITOR &&
+           !ath11k_peer_find_by_vdev_id(ab, arvif->vdev_id)) {
                memcpy(&arvif->chanctx, ctx, sizeof(*ctx));
                ret = 0;
                goto out;
@@ -5295,7 +5297,9 @@ ath11k_mac_op_assign_vif_chanctx(struct ieee80211_hw *hw,
                goto out;
        }
 
-       if (ab->hw_params.vdev_start_delay) {
+       if (ab->hw_params.vdev_start_delay &&
+           (arvif->vdev_type == WMI_VDEV_TYPE_AP ||
+           arvif->vdev_type == WMI_VDEV_TYPE_MONITOR)) {
                param.vdev_id = arvif->vdev_id;
                param.peer_type = WMI_PEER_TYPE_DEFAULT;
                param.peer_addr = ar->mac_addr;
index 857647aa57c8a7a66c41eaafa430667d062bc88e..20b415cd96c4aadaa233aed4cbabbae6996c4201 100644 (file)
@@ -274,7 +274,7 @@ static int ath11k_pci_fix_l1ss(struct ath11k_base *ab)
                                      PCIE_QSERDES_COM_SYSCLK_EN_SEL_REG,
                                      PCIE_QSERDES_COM_SYSCLK_EN_SEL_VAL,
                                      PCIE_QSERDES_COM_SYSCLK_EN_SEL_MSK);
-       if (!ret) {
+       if (ret) {
                ath11k_warn(ab, "failed to set sysclk: %d\n", ret);
                return ret;
        }
@@ -283,7 +283,7 @@ static int ath11k_pci_fix_l1ss(struct ath11k_base *ab)
                                      PCIE_USB3_PCS_MISC_OSC_DTCT_CONFIG1_REG,
                                      PCIE_USB3_PCS_MISC_OSC_DTCT_CONFIG1_VAL,
                                      PCIE_USB3_PCS_MISC_OSC_DTCT_CONFIG_MSK);
-       if (!ret) {
+       if (ret) {
                ath11k_warn(ab, "failed to set dtct config1 error: %d\n", ret);
                return ret;
        }
@@ -292,7 +292,7 @@ static int ath11k_pci_fix_l1ss(struct ath11k_base *ab)
                                      PCIE_USB3_PCS_MISC_OSC_DTCT_CONFIG2_REG,
                                      PCIE_USB3_PCS_MISC_OSC_DTCT_CONFIG2_VAL,
                                      PCIE_USB3_PCS_MISC_OSC_DTCT_CONFIG_MSK);
-       if (!ret) {
+       if (ret) {
                ath11k_warn(ab, "failed to set dtct config2: %d\n", ret);
                return ret;
        }
@@ -301,7 +301,7 @@ static int ath11k_pci_fix_l1ss(struct ath11k_base *ab)
                                      PCIE_USB3_PCS_MISC_OSC_DTCT_CONFIG4_REG,
                                      PCIE_USB3_PCS_MISC_OSC_DTCT_CONFIG4_VAL,
                                      PCIE_USB3_PCS_MISC_OSC_DTCT_CONFIG_MSK);
-       if (!ret) {
+       if (ret) {
                ath11k_warn(ab, "failed to set dtct config4: %d\n", ret);
                return ret;
        }
@@ -886,6 +886,32 @@ static void ath11k_pci_free_region(struct ath11k_pci *ab_pci)
                pci_disable_device(pci_dev);
 }
 
+static void ath11k_pci_aspm_disable(struct ath11k_pci *ab_pci)
+{
+       struct ath11k_base *ab = ab_pci->ab;
+
+       pcie_capability_read_word(ab_pci->pdev, PCI_EXP_LNKCTL,
+                                 &ab_pci->link_ctl);
+
+       ath11k_dbg(ab, ATH11K_DBG_PCI, "pci link_ctl 0x%04x L0s %d L1 %d\n",
+                  ab_pci->link_ctl,
+                  u16_get_bits(ab_pci->link_ctl, PCI_EXP_LNKCTL_ASPM_L0S),
+                  u16_get_bits(ab_pci->link_ctl, PCI_EXP_LNKCTL_ASPM_L1));
+
+       /* disable L0s and L1 */
+       pcie_capability_write_word(ab_pci->pdev, PCI_EXP_LNKCTL,
+                                  ab_pci->link_ctl & ~PCI_EXP_LNKCTL_ASPMC);
+
+       set_bit(ATH11K_PCI_ASPM_RESTORE, &ab_pci->flags);
+}
+
+static void ath11k_pci_aspm_restore(struct ath11k_pci *ab_pci)
+{
+       if (test_and_clear_bit(ATH11K_PCI_ASPM_RESTORE, &ab_pci->flags))
+               pcie_capability_write_word(ab_pci->pdev, PCI_EXP_LNKCTL,
+                                          ab_pci->link_ctl);
+}
+
 static int ath11k_pci_power_up(struct ath11k_base *ab)
 {
        struct ath11k_pci *ab_pci = ath11k_pci_priv(ab);
@@ -895,6 +921,11 @@ static int ath11k_pci_power_up(struct ath11k_base *ab)
        clear_bit(ATH11K_PCI_FLAG_INIT_DONE, &ab_pci->flags);
        ath11k_pci_sw_reset(ab_pci->ab, true);
 
+       /* Disable ASPM during firmware download due to problems switching
+        * to AMSS state.
+        */
+       ath11k_pci_aspm_disable(ab_pci);
+
        ret = ath11k_mhi_start(ab_pci);
        if (ret) {
                ath11k_err(ab, "failed to start mhi: %d\n", ret);
@@ -908,6 +939,9 @@ static void ath11k_pci_power_down(struct ath11k_base *ab)
 {
        struct ath11k_pci *ab_pci = ath11k_pci_priv(ab);
 
+       /* restore aspm in case firmware bootup fails */
+       ath11k_pci_aspm_restore(ab_pci);
+
        ath11k_pci_force_wake(ab_pci->ab);
        ath11k_mhi_stop(ab_pci);
        clear_bit(ATH11K_PCI_FLAG_INIT_DONE, &ab_pci->flags);
@@ -965,6 +999,8 @@ static int ath11k_pci_start(struct ath11k_base *ab)
 
        set_bit(ATH11K_PCI_FLAG_INIT_DONE, &ab_pci->flags);
 
+       ath11k_pci_aspm_restore(ab_pci);
+
        ath11k_pci_ce_irqs_enable(ab);
        ath11k_ce_rx_post_buf(ab);
 
index 0432a702416b42b6dd6bd3046c5c9db61a44b530..fe44d0dfce1956912474eff47f2a86c6ebd0cbb3 100644 (file)
@@ -63,6 +63,7 @@ struct ath11k_msi_config {
 enum ath11k_pci_flags {
        ATH11K_PCI_FLAG_INIT_DONE,
        ATH11K_PCI_FLAG_IS_MSI_64,
+       ATH11K_PCI_ASPM_RESTORE,
 };
 
 struct ath11k_pci {
@@ -80,6 +81,7 @@ struct ath11k_pci {
 
        /* enum ath11k_pci_flags */
        unsigned long flags;
+       u16 link_ctl;
 };
 
 static inline struct ath11k_pci *ath11k_pci_priv(struct ath11k_base *ab)
index 1866d82678fa9fc981cfea09946f0825111c3738..b69e7ebfa930327fdf8b10260c312d5888cf43bc 100644 (file)
@@ -76,6 +76,23 @@ struct ath11k_peer *ath11k_peer_find_by_id(struct ath11k_base *ab,
        return NULL;
 }
 
+struct ath11k_peer *ath11k_peer_find_by_vdev_id(struct ath11k_base *ab,
+                                               int vdev_id)
+{
+       struct ath11k_peer *peer;
+
+       spin_lock_bh(&ab->base_lock);
+
+       list_for_each_entry(peer, &ab->peers, list) {
+               if (vdev_id == peer->vdev_id) {
+                       spin_unlock_bh(&ab->base_lock);
+                       return peer;
+               }
+       }
+       spin_unlock_bh(&ab->base_lock);
+       return NULL;
+}
+
 void ath11k_peer_unmap_event(struct ath11k_base *ab, u16 peer_id)
 {
        struct ath11k_peer *peer;
index bba2e00b6944aeb00c31f4b80341512528bb52ab..8553ed061aeaaf12b5598a08385cfcc2018fae74 100644 (file)
@@ -43,5 +43,7 @@ int ath11k_peer_create(struct ath11k *ar, struct ath11k_vif *arvif,
                       struct ieee80211_sta *sta, struct peer_create_params *param);
 int ath11k_wait_for_peer_delete_done(struct ath11k *ar, u32 vdev_id,
                                     const u8 *addr);
+struct ath11k_peer *ath11k_peer_find_by_vdev_id(struct ath11k_base *ab,
+                                               int vdev_id);
 
 #endif /* _PEER_H_ */
index f0b5c50974f3e6b6ca04801964c49dfae6c067ae..0db623ff4bb9b33991df8620cb18fb4689e32906 100644 (file)
@@ -1660,6 +1660,7 @@ static int ath11k_qmi_respond_fw_mem_request(struct ath11k_base *ab)
        struct qmi_wlanfw_respond_mem_resp_msg_v01 resp;
        struct qmi_txn txn = {};
        int ret = 0, i;
+       bool delayed;
 
        req = kzalloc(sizeof(*req), GFP_KERNEL);
        if (!req)
@@ -1672,11 +1673,13 @@ static int ath11k_qmi_respond_fw_mem_request(struct ath11k_base *ab)
         * failure to FW and FW will then request mulitple blocks of small
         * chunk size memory.
         */
-       if (!ab->bus_params.fixed_mem_region && ab->qmi.mem_seg_count <= 2) {
+       if (!ab->bus_params.fixed_mem_region && ab->qmi.target_mem_delayed) {
+               delayed = true;
                ath11k_dbg(ab, ATH11K_DBG_QMI, "qmi delays mem_request %d\n",
                           ab->qmi.mem_seg_count);
                memset(req, 0, sizeof(*req));
        } else {
+               delayed = false;
                req->mem_seg_len = ab->qmi.mem_seg_count;
 
                for (i = 0; i < req->mem_seg_len ; i++) {
@@ -1708,6 +1711,12 @@ static int ath11k_qmi_respond_fw_mem_request(struct ath11k_base *ab)
        }
 
        if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
+               /* the error response is expected when
+                * target_mem_delayed is true.
+                */
+               if (delayed && resp.resp.error == 0)
+                       goto out;
+
                ath11k_warn(ab, "Respond mem req failed, result: %d, err: %d\n",
                            resp.resp.result, resp.resp.error);
                ret = -EINVAL;
@@ -1742,6 +1751,8 @@ static int ath11k_qmi_alloc_target_mem_chunk(struct ath11k_base *ab)
        int i;
        struct target_mem_chunk *chunk;
 
+       ab->qmi.target_mem_delayed = false;
+
        for (i = 0; i < ab->qmi.mem_seg_count; i++) {
                chunk = &ab->qmi.target_mem[i];
                chunk->vaddr = dma_alloc_coherent(ab->dev,
@@ -1749,6 +1760,15 @@ static int ath11k_qmi_alloc_target_mem_chunk(struct ath11k_base *ab)
                                                  &chunk->paddr,
                                                  GFP_KERNEL);
                if (!chunk->vaddr) {
+                       if (ab->qmi.mem_seg_count <= 2) {
+                               ath11k_dbg(ab, ATH11K_DBG_QMI,
+                                          "qmi dma allocation failed (%d B type %u), will try later with small size\n",
+                                           chunk->size,
+                                           chunk->type);
+                               ath11k_qmi_free_target_mem_chunk(ab);
+                               ab->qmi.target_mem_delayed = true;
+                               return 0;
+                       }
                        ath11k_err(ab, "failed to alloc memory, size: 0x%x, type: %u\n",
                                   chunk->size,
                                   chunk->type);
@@ -2517,7 +2537,7 @@ static void ath11k_qmi_msg_mem_request_cb(struct qmi_handle *qmi_hdl,
                                    ret);
                        return;
                }
-       } else if (msg->mem_seg_len > 2) {
+       } else {
                ret = ath11k_qmi_alloc_target_mem_chunk(ab);
                if (ret) {
                        ath11k_warn(ab, "qmi failed to alloc target memory: %d\n",
index 92925c9eac67499656f47b7925c17e4bd9f7e634..7bad374cc23a63c12152d7b12ca1772aca1bee29 100644 (file)
@@ -125,6 +125,7 @@ struct ath11k_qmi {
        struct target_mem_chunk target_mem[ATH11K_QMI_WLANFW_MAX_NUM_MEM_SEG_V01];
        u32 mem_seg_count;
        u32 target_mem_mode;
+       bool target_mem_delayed;
        u8 cal_done;
        struct target_info target;
        struct m3_mem_region m3_mem;
index da4b546b62cb5d583d2a28038fba9f4a96f46012..73869d445c5b3a151179539a34cd254c79cb68aa 100644 (file)
@@ -3460,6 +3460,9 @@ int ath11k_wmi_set_hw_mode(struct ath11k_base *ab,
        len = sizeof(*cmd);
 
        skb = ath11k_wmi_alloc_skb(wmi_ab, len);
+       if (!skb)
+               return -ENOMEM;
+
        cmd = (struct wmi_pdev_set_hw_mode_cmd_param *)skb->data;
 
        cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_PDEV_SET_HW_MODE_CMD) |
index ed4635bd151a2deb6fb072a86388c56c97044915..102a8f14c22d4f20ce0126e70311fe10aa9ce419 100644 (file)
@@ -40,9 +40,9 @@ static const struct ieee80211_iface_limit if_limits[] = {
                .types = BIT(NL80211_IFTYPE_ADHOC)
        }, {
                .max = 16,
-               .types = BIT(NL80211_IFTYPE_AP) |
+               .types = BIT(NL80211_IFTYPE_AP)
 #ifdef CONFIG_MAC80211_MESH
-                        BIT(NL80211_IFTYPE_MESH_POINT)
+                        BIT(NL80211_IFTYPE_MESH_POINT)
 #endif
        }, {
                .max = MT7915_MAX_INTERFACES,
index 62b5b912818fa205cbf2c69bf820cceab0b847f7..0b6facb17ff722772981476943c1a0d3a6f5c2d8 100644 (file)
@@ -157,10 +157,14 @@ static void mt76s_net_worker(struct mt76_worker *w)
 
 static int mt76s_process_tx_queue(struct mt76_dev *dev, struct mt76_queue *q)
 {
-       bool wake, mcu = q == dev->q_mcu[MT_MCUQ_WM];
        struct mt76_queue_entry entry;
        int nframes = 0;
+       bool mcu;
 
+       if (!q)
+               return 0;
+
+       mcu = q == dev->q_mcu[MT_MCUQ_WM];
        while (q->queued > 0) {
                if (!q->entry[q->tail].done)
                        break;
@@ -177,21 +181,12 @@ static int mt76s_process_tx_queue(struct mt76_dev *dev, struct mt76_queue *q)
                nframes++;
        }
 
-       wake = q->stopped && q->queued < q->ndesc - 8;
-       if (wake)
-               q->stopped = false;
-
        if (!q->queued)
                wake_up(&dev->tx_wait);
 
-       if (mcu)
-               goto out;
-
-       mt76_txq_schedule(&dev->phy, q->qid);
+       if (!mcu)
+               mt76_txq_schedule(&dev->phy, q->qid);
 
-       if (wake)
-               ieee80211_wake_queue(dev->hw, q->qid);
-out:
        return nframes;
 }
 
index dc850109de22d66a9755a75db5a9164d88dcf557..b95d093728b9b7d0fc9b6a8cd52b87e31ea0bacf 100644 (file)
@@ -811,11 +811,12 @@ static void mt76u_status_worker(struct mt76_worker *w)
        struct mt76_dev *dev = container_of(usb, struct mt76_dev, usb);
        struct mt76_queue_entry entry;
        struct mt76_queue *q;
-       bool wake;
        int i;
 
        for (i = 0; i < IEEE80211_NUM_ACS; i++) {
                q = dev->phy.q_tx[i];
+               if (!q)
+                       continue;
 
                while (q->queued > 0) {
                        if (!q->entry[q->tail].done)
@@ -827,10 +828,6 @@ static void mt76u_status_worker(struct mt76_worker *w)
                        mt76_queue_tx_complete(dev, q, &entry);
                }
 
-               wake = q->stopped && q->queued < q->ndesc - 8;
-               if (wake)
-                       q->stopped = false;
-
                if (!q->queued)
                        wake_up(&dev->tx_wait);
 
@@ -839,8 +836,6 @@ static void mt76u_status_worker(struct mt76_worker *w)
                if (dev->drv->tx_status_data &&
                    !test_and_set_bit(MT76_READING_STATS, &dev->phy.state))
                        queue_work(dev->wq, &dev->usb.stat_work);
-               if (wake)
-                       ieee80211_wake_queue(dev->hw, i);
        }
 }
 
index a7259dbc953da7a0158b6f8398ef8eff4df02e28..965bd95890459313c975dffbc17156f8e660d109 100644 (file)
@@ -78,7 +78,6 @@ static void rtl_fw_do_work(const struct firmware *firmware, void *context,
 
        rtl_dbg(rtlpriv, COMP_ERR, DBG_LOUD,
                "Firmware callback routine entered!\n");
-       complete(&rtlpriv->firmware_loading_complete);
        if (!firmware) {
                if (rtlpriv->cfg->alt_fw_name) {
                        err = request_firmware(&firmware,
@@ -91,13 +90,13 @@ static void rtl_fw_do_work(const struct firmware *firmware, void *context,
                }
                pr_err("Selected firmware is not available\n");
                rtlpriv->max_fw_size = 0;
-               return;
+               goto exit;
        }
 found_alt:
        if (firmware->size > rtlpriv->max_fw_size) {
                pr_err("Firmware is too big!\n");
                release_firmware(firmware);
-               return;
+               goto exit;
        }
        if (!is_wow) {
                memcpy(rtlpriv->rtlhal.pfirmware, firmware->data,
@@ -109,6 +108,9 @@ found_alt:
                rtlpriv->rtlhal.wowlan_fwsize = firmware->size;
        }
        release_firmware(firmware);
+
+exit:
+       complete(&rtlpriv->firmware_loading_complete);
 }
 
 void rtl_fw_cb(const struct firmware *firmware, void *context)