--- /dev/null
+From b0302aba812bcc39291cdab9ad7e37008f352a91 Mon Sep 17 00:00:00 2001
+From: Larry Finger <Larry.Finger@lwfinger.net>
+Date: Mon, 30 Jan 2012 09:54:49 -0600
+Subject: rtlwifi: Convert to asynchronous firmware load
+
+From: Larry Finger <Larry.Finger@lwfinger.net>
+
+commit b0302aba812bcc39291cdab9ad7e37008f352a91 upstream.
+
+This patch addresses a kernel bugzilla report and two recent mail threads.
+
+The kernel bugzilla report is https://bugzilla.kernel.org/show_bug.cgi?id=42632,
+which reports a udev timeout on boot.
+
+The first mail thread, which was on LKML (http://lkml.indiana.edu/hypermail/
+linux/kernel/1112.3/00965.html) was for a WARNING that occurs after a
+suspend/resume cycle for rtl8192cu.
+
+The scond mail thread (http://marc.info/?l=linux-wireless&m=132655490826766&w=2)
+concerned changes in udev that break drivers that delay while firmware is loaded
+on modprobe.
+
+This patch converts all rtlwifi-based drivers to use the asynchronous firmware
+loading mechanism. Drivers rtl8192ce, rtl8192cu and rtl8192de share a common
+callback routine. Driver rtl8192se needs different handling of the firmware,
+thus it has its own code.
+
+Signed-off-by: Larry Finger <Larry.Finger@lwfinger.net>
+Signed-off-by: John W. Linville <linville@tuxdriver.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+
+---
+ drivers/net/wireless/rtlwifi/base.c | 1
+ drivers/net/wireless/rtlwifi/core.c | 42 ++++++++++++++
+ drivers/net/wireless/rtlwifi/core.h | 4 -
+ drivers/net/wireless/rtlwifi/pci.c | 28 +++------
+ drivers/net/wireless/rtlwifi/pci.h | 1
+ drivers/net/wireless/rtlwifi/ps.c | 3 -
+ drivers/net/wireless/rtlwifi/rtl8192c/fw_common.c | 10 ---
+ drivers/net/wireless/rtlwifi/rtl8192ce/hw.c | 6 --
+ drivers/net/wireless/rtlwifi/rtl8192ce/sw.c | 24 ++------
+ drivers/net/wireless/rtlwifi/rtl8192cu/hw.c | 33 ++++-------
+ drivers/net/wireless/rtlwifi/rtl8192cu/sw.c | 29 +++------
+ drivers/net/wireless/rtlwifi/rtl8192de/fw.c | 8 --
+ drivers/net/wireless/rtlwifi/rtl8192de/hw.c | 3 -
+ drivers/net/wireless/rtlwifi/rtl8192de/sw.c | 38 +++++-------
+ drivers/net/wireless/rtlwifi/rtl8192se/fw.c | 2
+ drivers/net/wireless/rtlwifi/rtl8192se/hw.c | 16 +++--
+ drivers/net/wireless/rtlwifi/rtl8192se/led.c | 5 +
+ drivers/net/wireless/rtlwifi/rtl8192se/sw.c | 65 ++++++++++++++++------
+ drivers/net/wireless/rtlwifi/usb.c | 36 +++++-------
+ drivers/net/wireless/rtlwifi/wifi.h | 4 +
+ 20 files changed, 192 insertions(+), 166 deletions(-)
+
+--- a/drivers/net/wireless/rtlwifi/base.c
++++ b/drivers/net/wireless/rtlwifi/base.c
+@@ -413,6 +413,7 @@ void rtl_init_rfkill(struct ieee80211_hw
+
+ wiphy_rfkill_start_polling(hw->wiphy);
+ }
++EXPORT_SYMBOL(rtl_init_rfkill);
+
+ void rtl_deinit_rfkill(struct ieee80211_hw *hw)
+ {
+--- a/drivers/net/wireless/rtlwifi/core.c
++++ b/drivers/net/wireless/rtlwifi/core.c
+@@ -31,8 +31,50 @@
+ #include "core.h"
+ #include "cam.h"
+ #include "base.h"
++#include "pci.h"
+ #include "ps.h"
+
++#include <linux/export.h>
++
++void rtl_fw_cb(const struct firmware *firmware, void *context)
++{
++ struct ieee80211_hw *hw = context;
++ struct rtl_priv *rtlpriv = rtl_priv(hw);
++ int err;
++
++ RT_TRACE(rtlpriv, COMP_ERR, DBG_LOUD,
++ ("Firmware callback routine entered!\n"));
++ complete(&rtlpriv->firmware_loading_complete);
++ if (!firmware) {
++ pr_err("Firmware %s not available\n", rtlpriv->cfg->fw_name);
++ rtlpriv->max_fw_size = 0;
++ return;
++ }
++ if (firmware->size > rtlpriv->max_fw_size) {
++ RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
++ ("Firmware is too big!\n"));
++ release_firmware(firmware);
++ return;
++ }
++ memcpy(rtlpriv->rtlhal.pfirmware, firmware->data, firmware->size);
++ rtlpriv->rtlhal.fwsize = firmware->size;
++ release_firmware(firmware);
++
++ err = ieee80211_register_hw(hw);
++ if (err) {
++ RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
++ ("Can't register mac80211 hw\n"));
++ return;
++ } else {
++ rtlpriv->mac80211.mac80211_registered = 1;
++ }
++ set_bit(RTL_STATUS_INTERFACE_START, &rtlpriv->status);
++
++ /*init rfkill */
++ rtl_init_rfkill(hw);
++}
++EXPORT_SYMBOL(rtl_fw_cb);
++
+ /*mutex for start & stop is must here. */
+ static int rtl_op_start(struct ieee80211_hw *hw)
+ {
+--- a/drivers/net/wireless/rtlwifi/core.h
++++ b/drivers/net/wireless/rtlwifi/core.h
+@@ -30,8 +30,6 @@
+ #ifndef __RTL_CORE_H__
+ #define __RTL_CORE_H__
+
+-#include <net/mac80211.h>
+-
+ #define RTL_SUPPORTED_FILTERS \
+ (FIF_PROMISC_IN_BSS | \
+ FIF_ALLMULTI | FIF_CONTROL | \
+@@ -42,4 +40,6 @@
+ #define RTL_SUPPORTED_CTRL_FILTER 0xFF
+
+ extern const struct ieee80211_ops rtl_ops;
++void rtl_fw_cb(const struct firmware *firmware, void *context);
++
+ #endif
+--- a/drivers/net/wireless/rtlwifi/pci.c
++++ b/drivers/net/wireless/rtlwifi/pci.c
+@@ -28,8 +28,8 @@
+ *****************************************************************************/
+
+ #include <linux/export.h>
+-#include "core.h"
+ #include "wifi.h"
++#include "core.h"
+ #include "pci.h"
+ #include "base.h"
+ #include "ps.h"
+@@ -1579,6 +1579,9 @@ static void rtl_pci_stop(struct ieee8021
+
+ rtlpci->driver_is_goingto_unload = true;
+ rtlpriv->cfg->ops->hw_disable(hw);
++ /* some things are not needed if firmware not available */
++ if (!rtlpriv->max_fw_size)
++ return;
+ rtlpriv->cfg->ops->led_control(hw, LED_CTL_POWER_OFF);
+
+ spin_lock_irqsave(&rtlpriv->locks.rf_ps_lock, flags);
+@@ -1797,6 +1800,7 @@ int __devinit rtl_pci_probe(struct pci_d
+ rtlpriv = hw->priv;
+ pcipriv = (void *)rtlpriv->priv;
+ pcipriv->dev.pdev = pdev;
++ init_completion(&rtlpriv->firmware_loading_complete);
+
+ /* init cfg & intf_ops */
+ rtlpriv->rtlhal.interface = INTF_PCI;
+@@ -1817,7 +1821,7 @@ int __devinit rtl_pci_probe(struct pci_d
+ err = pci_request_regions(pdev, KBUILD_MODNAME);
+ if (err) {
+ RT_ASSERT(false, ("Can't obtain PCI resources\n"));
+- return err;
++ goto fail2;
+ }
+
+ pmem_start = pci_resource_start(pdev, rtlpriv->cfg->bar_id);
+@@ -1883,15 +1887,6 @@ int __devinit rtl_pci_probe(struct pci_d
+ goto fail3;
+ }
+
+- err = ieee80211_register_hw(hw);
+- if (err) {
+- RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
+- ("Can't register mac80211 hw.\n"));
+- goto fail3;
+- } else {
+- rtlpriv->mac80211.mac80211_registered = 1;
+- }
+-
+ err = sysfs_create_group(&pdev->dev.kobj, &rtl_attribute_group);
+ if (err) {
+ RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
+@@ -1899,9 +1894,6 @@ int __devinit rtl_pci_probe(struct pci_d
+ goto fail3;
+ }
+
+- /*init rfkill */
+- rtl_init_rfkill(hw);
+-
+ rtlpci = rtl_pcidev(pcipriv);
+ err = request_irq(rtlpci->pdev->irq, &_rtl_pci_interrupt,
+ IRQF_SHARED, KBUILD_MODNAME, hw);
+@@ -1910,24 +1902,22 @@ int __devinit rtl_pci_probe(struct pci_d
+ ("%s: failed to register IRQ handler\n",
+ wiphy_name(hw->wiphy)));
+ goto fail3;
+- } else {
+- rtlpci->irq_alloc = 1;
+ }
++ rtlpci->irq_alloc = 1;
+
+- set_bit(RTL_STATUS_INTERFACE_START, &rtlpriv->status);
+ return 0;
+
+ fail3:
+ pci_set_drvdata(pdev, NULL);
+ rtl_deinit_core(hw);
+ _rtl_pci_io_handler_release(hw);
+- ieee80211_free_hw(hw);
+
+ if (rtlpriv->io.pci_mem_start != 0)
+ pci_iounmap(pdev, (void __iomem *)rtlpriv->io.pci_mem_start);
+
+ fail2:
+ pci_release_regions(pdev);
++ complete(&rtlpriv->firmware_loading_complete);
+
+ fail1:
+
+@@ -1946,6 +1936,8 @@ void rtl_pci_disconnect(struct pci_dev *
+ struct rtl_pci *rtlpci = rtl_pcidev(pcipriv);
+ struct rtl_mac *rtlmac = rtl_mac(rtlpriv);
+
++ /* just in case driver is removed before firmware callback */
++ wait_for_completion(&rtlpriv->firmware_loading_complete);
+ clear_bit(RTL_STATUS_INTERFACE_START, &rtlpriv->status);
+
+ sysfs_remove_group(&pdev->dev.kobj, &rtl_attribute_group);
+--- a/drivers/net/wireless/rtlwifi/pci.h
++++ b/drivers/net/wireless/rtlwifi/pci.h
+@@ -239,7 +239,6 @@ int __devinit rtl_pci_probe(struct pci_d
+ void rtl_pci_disconnect(struct pci_dev *pdev);
+ int rtl_pci_suspend(struct device *dev);
+ int rtl_pci_resume(struct device *dev);
+-
+ static inline u8 pci_read8_sync(struct rtl_priv *rtlpriv, u32 addr)
+ {
+ return readb((u8 __iomem *) rtlpriv->io.pci_mem_start + addr);
+--- a/drivers/net/wireless/rtlwifi/ps.c
++++ b/drivers/net/wireless/rtlwifi/ps.c
+@@ -47,7 +47,8 @@ bool rtl_ps_enable_nic(struct ieee80211_
+ ("Driver is already down!\n"));
+
+ /*<2> Enable Adapter */
+- rtlpriv->cfg->ops->hw_init(hw);
++ if (rtlpriv->cfg->ops->hw_init(hw))
++ return 1;
+ RT_CLEAR_PS_LEVEL(ppsc, RT_RF_OFF_LEVL_HALT_NIC);
+
+ /*<3> Enable Interrupt */
+--- a/drivers/net/wireless/rtlwifi/rtl8192c/fw_common.c
++++ b/drivers/net/wireless/rtlwifi/rtl8192c/fw_common.c
+@@ -262,10 +262,9 @@ int rtl92c_download_fw(struct ieee80211_
+ u32 fwsize;
+ enum version_8192c version = rtlhal->version;
+
+- if (!rtlhal->pfirmware)
++ if (rtlpriv->max_fw_size == 0 || !rtlhal->pfirmware)
+ return 1;
+
+- pr_info("Loading firmware file %s\n", rtlpriv->cfg->fw_name);
+ pfwheader = (struct rtl92c_firmware_header *)rtlhal->pfirmware;
+ pfwdata = (u8 *) rtlhal->pfirmware;
+ fwsize = rtlhal->fwsize;
+@@ -518,15 +517,8 @@ static void _rtl92c_fill_h2c_command(str
+ void rtl92c_fill_h2c_cmd(struct ieee80211_hw *hw,
+ u8 element_id, u32 cmd_len, u8 *p_cmdbuffer)
+ {
+- struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
+ u32 tmp_cmdbuf[2];
+
+- if (rtlhal->fw_ready == false) {
+- RT_ASSERT(false, ("return H2C cmd because of Fw "
+- "download fail!!!\n"));
+- return;
+- }
+-
+ memset(tmp_cmdbuf, 0, 8);
+ memcpy(tmp_cmdbuf, p_cmdbuffer, cmd_len);
+ _rtl92c_fill_h2c_command(hw, element_id, cmd_len, (u8 *)&tmp_cmdbuf);
+--- a/drivers/net/wireless/rtlwifi/rtl8192ce/hw.c
++++ b/drivers/net/wireless/rtlwifi/rtl8192ce/hw.c
+@@ -921,10 +921,7 @@ int rtl92ce_hw_init(struct ieee80211_hw
+ ("Failed to download FW. Init HW "
+ "without FW now..\n"));
+ err = 1;
+- rtlhal->fw_ready = false;
+ return err;
+- } else {
+- rtlhal->fw_ready = true;
+ }
+
+ rtlhal->last_hmeboxnum = 0;
+@@ -1199,7 +1196,6 @@ static void _rtl92ce_poweroff_adapter(st
+ {
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_pci_priv *rtlpcipriv = rtl_pcipriv(hw);
+- struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
+ u8 u1b_tmp;
+ u32 u4b_tmp;
+
+@@ -1210,7 +1206,7 @@ static void _rtl92ce_poweroff_adapter(st
+ rtl_write_byte(rtlpriv, REG_APSD_CTRL, 0x40);
+ rtl_write_byte(rtlpriv, REG_SYS_FUNC_EN, 0xE2);
+ rtl_write_byte(rtlpriv, REG_SYS_FUNC_EN, 0xE0);
+- if ((rtl_read_byte(rtlpriv, REG_MCUFWDL) & BIT(7)) && rtlhal->fw_ready)
++ if (rtl_read_byte(rtlpriv, REG_MCUFWDL) & BIT(7))
+ rtl92c_firmware_selfreset(hw);
+ rtl_write_byte(rtlpriv, REG_SYS_FUNC_EN + 1, 0x51);
+ rtl_write_byte(rtlpriv, REG_MCUFWDL, 0x00);
+--- a/drivers/net/wireless/rtlwifi/rtl8192ce/sw.c
++++ b/drivers/net/wireless/rtlwifi/rtl8192ce/sw.c
+@@ -92,9 +92,7 @@ int rtl92c_init_sw_vars(struct ieee80211
+ int err;
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw));
+- const struct firmware *firmware;
+ struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
+- char *fw_name = NULL;
+
+ rtl8192ce_bt_reg_init(hw);
+
+@@ -166,26 +164,20 @@ int rtl92c_init_sw_vars(struct ieee80211
+ /* request fw */
+ if (IS_VENDOR_UMC_A_CUT(rtlhal->version) &&
+ !IS_92C_SERIAL(rtlhal->version))
+- fw_name = "rtlwifi/rtl8192cfwU.bin";
++ rtlpriv->cfg->fw_name = "rtlwifi/rtl8192cfwU.bin";
+ else if (IS_81xxC_VENDOR_UMC_B_CUT(rtlhal->version))
+- fw_name = "rtlwifi/rtl8192cfwU_B.bin";
+- else
+- fw_name = rtlpriv->cfg->fw_name;
+- err = request_firmware(&firmware, fw_name, rtlpriv->io.dev);
++ rtlpriv->cfg->fw_name = "rtlwifi/rtl8192cfwU_B.bin";
++
++ rtlpriv->max_fw_size = 0x4000;
++ pr_info("Using firmware %s\n", rtlpriv->cfg->fw_name);
++ err = request_firmware_nowait(THIS_MODULE, 1, rtlpriv->cfg->fw_name,
++ rtlpriv->io.dev, GFP_KERNEL, hw,
++ rtl_fw_cb);
+ if (err) {
+ RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
+ ("Failed to request firmware!\n"));
+ return 1;
+ }
+- if (firmware->size > 0x4000) {
+- RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
+- ("Firmware is too big!\n"));
+- release_firmware(firmware);
+- return 1;
+- }
+- memcpy(rtlpriv->rtlhal.pfirmware, firmware->data, firmware->size);
+- rtlpriv->rtlhal.fwsize = firmware->size;
+- release_firmware(firmware);
+
+ return 0;
+ }
+--- a/drivers/net/wireless/rtlwifi/rtl8192cu/hw.c
++++ b/drivers/net/wireless/rtlwifi/rtl8192cu/hw.c
+@@ -1171,10 +1171,7 @@ int rtl92cu_hw_init(struct ieee80211_hw
+ RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING,
+ ("Failed to download FW. Init HW without FW now..\n"));
+ err = 1;
+- rtlhal->fw_ready = false;
+ return err;
+- } else {
+- rtlhal->fw_ready = true;
+ }
+ rtlhal->last_hmeboxnum = 0; /* h2c */
+ _rtl92cu_phy_param_tab_init(hw);
+@@ -1270,24 +1267,22 @@ static void _ResetDigitalProcedure1(str
+ if (rtl_read_byte(rtlpriv, REG_MCUFWDL) & BIT(1)) {
+ /* reset MCU ready status */
+ rtl_write_byte(rtlpriv, REG_MCUFWDL, 0);
+- if (rtlhal->fw_ready) {
+- /* 8051 reset by self */
+- rtl_write_byte(rtlpriv, REG_HMETFR+3, 0x20);
+- while ((retry_cnts++ < 100) &&
+- (FEN_CPUEN & rtl_read_word(rtlpriv,
+- REG_SYS_FUNC_EN))) {
+- udelay(50);
+- }
+- if (retry_cnts >= 100) {
+- RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
++ /* 8051 reset by self */
++ rtl_write_byte(rtlpriv, REG_HMETFR+3, 0x20);
++ while ((retry_cnts++ < 100) &&
++ (FEN_CPUEN & rtl_read_word(rtlpriv,
++ REG_SYS_FUNC_EN))) {
++ udelay(50);
++ }
++ if (retry_cnts >= 100) {
++ RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
+ ("#####=> 8051 reset failed!.."
+ ".......................\n"););
+- /* if 8051 reset fail, reset MAC. */
+- rtl_write_byte(rtlpriv,
+- REG_SYS_FUNC_EN + 1,
+- 0x50);
+- udelay(100);
+- }
++ /* if 8051 reset fail, reset MAC. */
++ rtl_write_byte(rtlpriv,
++ REG_SYS_FUNC_EN + 1,
++ 0x50);
++ udelay(100);
+ }
+ }
+ /* Reset MAC and Enable 8051 */
+--- a/drivers/net/wireless/rtlwifi/rtl8192cu/sw.c
++++ b/drivers/net/wireless/rtlwifi/rtl8192cu/sw.c
+@@ -54,7 +54,6 @@ MODULE_FIRMWARE("rtlwifi/rtl8192cufw.bin
+ static int rtl92cu_init_sw_vars(struct ieee80211_hw *hw)
+ {
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+- const struct firmware *firmware;
+ int err;
+
+ rtlpriv->dm.dm_initialgain_enable = true;
+@@ -62,29 +61,21 @@ static int rtl92cu_init_sw_vars(struct i
+ rtlpriv->dm.disable_framebursting = false;
+ rtlpriv->dm.thermalvalue = 0;
+ rtlpriv->dbg.global_debuglevel = rtlpriv->cfg->mod_params->debug;
+- rtlpriv->rtlhal.pfirmware = vmalloc(0x4000);
++
++ /* for firmware buf */
++ rtlpriv->rtlhal.pfirmware = vzalloc(0x4000);
+ if (!rtlpriv->rtlhal.pfirmware) {
+ RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
+ ("Can't alloc buffer for fw.\n"));
+ return 1;
+ }
+- /* request fw */
+- err = request_firmware(&firmware, rtlpriv->cfg->fw_name,
+- rtlpriv->io.dev);
+- if (err) {
+- RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
+- ("Failed to request firmware!\n"));
+- return 1;
+- }
+- if (firmware->size > 0x4000) {
+- RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
+- ("Firmware is too big!\n"));
+- release_firmware(firmware);
+- return 1;
+- }
+- memcpy(rtlpriv->rtlhal.pfirmware, firmware->data, firmware->size);
+- rtlpriv->rtlhal.fwsize = firmware->size;
+- release_firmware(firmware);
++
++ pr_info("Loading firmware %s\n", rtlpriv->cfg->fw_name);
++ rtlpriv->max_fw_size = 0x4000;
++ err = request_firmware_nowait(THIS_MODULE, 1,
++ rtlpriv->cfg->fw_name, rtlpriv->io.dev,
++ GFP_KERNEL, hw, rtl_fw_cb);
++
+
+ return 0;
+ }
+--- a/drivers/net/wireless/rtlwifi/rtl8192de/fw.c
++++ b/drivers/net/wireless/rtlwifi/rtl8192de/fw.c
+@@ -257,7 +257,7 @@ int rtl92d_download_fw(struct ieee80211_
+ bool fw_downloaded = false, fwdl_in_process = false;
+ unsigned long flags;
+
+- if (!rtlhal->pfirmware)
++ if (rtlpriv->max_fw_size == 0 || !rtlhal->pfirmware)
+ return 1;
+ fwsize = rtlhal->fwsize;
+ pfwheader = (u8 *) rtlhal->pfirmware;
+@@ -539,14 +539,8 @@ static void _rtl92d_fill_h2c_command(str
+ void rtl92d_fill_h2c_cmd(struct ieee80211_hw *hw,
+ u8 element_id, u32 cmd_len, u8 *cmdbuffer)
+ {
+- struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
+ u32 tmp_cmdbuf[2];
+
+- if (rtlhal->fw_ready == false) {
+- RT_ASSERT(false, ("return H2C cmd because of Fw "
+- "download fail!!!\n"));
+- return;
+- }
+ memset(tmp_cmdbuf, 0, 8);
+ memcpy(tmp_cmdbuf, cmdbuffer, cmd_len);
+ _rtl92d_fill_h2c_command(hw, element_id, cmd_len, (u8 *)&tmp_cmdbuf);
+--- a/drivers/net/wireless/rtlwifi/rtl8192de/hw.c
++++ b/drivers/net/wireless/rtlwifi/rtl8192de/hw.c
+@@ -932,10 +932,7 @@ int rtl92de_hw_init(struct ieee80211_hw
+ RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING,
+ ("Failed to download FW. Init HW "
+ "without FW..\n"));
+- rtlhal->fw_ready = false;
+ return 1;
+- } else {
+- rtlhal->fw_ready = true;
+ }
+ rtlhal->last_hmeboxnum = 0;
+ rtlpriv->psc.fw_current_inpsmode = false;
+--- a/drivers/net/wireless/rtlwifi/rtl8192de/sw.c
++++ b/drivers/net/wireless/rtlwifi/rtl8192de/sw.c
+@@ -94,7 +94,6 @@ static int rtl92d_init_sw_vars(struct ie
+ u8 tid;
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw));
+- const struct firmware *firmware;
+ static int header_print;
+
+ rtlpriv->dm.dm_initialgain_enable = true;
+@@ -170,6 +169,15 @@ static int rtl92d_init_sw_vars(struct ie
+ else if (rtlpriv->psc.reg_fwctrl_lps == 3)
+ rtlpriv->psc.fwctrl_psmode = FW_PS_DTIM_MODE;
+
++ /* for early mode */
++ rtlpriv->rtlhal.earlymode_enable = true;
++ for (tid = 0; tid < 8; tid++)
++ skb_queue_head_init(&rtlpriv->mac80211.skb_waitq[tid]);
++
++ /* Only load firmware for first MAC */
++ if (header_print)
++ return 0;
++
+ /* for firmware buf */
+ rtlpriv->rtlhal.pfirmware = vzalloc(0x8000);
+ if (!rtlpriv->rtlhal.pfirmware) {
+@@ -178,33 +186,21 @@ static int rtl92d_init_sw_vars(struct ie
+ return 1;
+ }
+
+- if (!header_print) {
+- pr_info("Driver for Realtek RTL8192DE WLAN interface\n");
+- pr_info("Loading firmware file %s\n", rtlpriv->cfg->fw_name);
+- header_print++;
+- }
++ rtlpriv->max_fw_size = 0x8000;
++ pr_info("Driver for Realtek RTL8192DE WLAN interface\n");
++ pr_info("Loading firmware file %s\n", rtlpriv->cfg->fw_name);
++ header_print++;
++
+ /* request fw */
+- err = request_firmware(&firmware, rtlpriv->cfg->fw_name,
+- rtlpriv->io.dev);
++ err = request_firmware_nowait(THIS_MODULE, 1, rtlpriv->cfg->fw_name,
++ rtlpriv->io.dev, GFP_KERNEL, hw,
++ rtl_fw_cb);
+ if (err) {
+ RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
+ ("Failed to request firmware!\n"));
+ return 1;
+ }
+- if (firmware->size > 0x8000) {
+- RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
+- ("Firmware is too big!\n"));
+- release_firmware(firmware);
+- return 1;
+- }
+- memcpy(rtlpriv->rtlhal.pfirmware, firmware->data, firmware->size);
+- rtlpriv->rtlhal.fwsize = firmware->size;
+- release_firmware(firmware);
+
+- /* for early mode */
+- rtlpriv->rtlhal.earlymode_enable = true;
+- for (tid = 0; tid < 8; tid++)
+- skb_queue_head_init(&rtlpriv->mac80211.skb_waitq[tid]);
+ return 0;
+ }
+
+--- a/drivers/net/wireless/rtlwifi/rtl8192se/fw.c
++++ b/drivers/net/wireless/rtlwifi/rtl8192se/fw.c
+@@ -364,7 +364,7 @@ int rtl92s_download_fw(struct ieee80211_
+ u8 fwstatus = FW_STATUS_INIT;
+ bool rtstatus = true;
+
+- if (!rtlhal->pfirmware)
++ if (rtlpriv->max_fw_size == 0 || !rtlhal->pfirmware)
+ return 1;
+
+ firmware = (struct rt_firmware *)rtlhal->pfirmware;
+--- a/drivers/net/wireless/rtlwifi/rtl8192se/hw.c
++++ b/drivers/net/wireless/rtlwifi/rtl8192se/hw.c
+@@ -952,11 +952,10 @@ int rtl92se_hw_init(struct ieee80211_hw
+ if (!rtstatus) {
+ RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING,
+ ("Failed to download FW. "
+- "Init HW without FW now.., Please copy FW into"
++ "Init HW without FW now.., "
++ "Please copy FW into"
+ "/lib/firmware/rtlwifi\n"));
+- rtlhal->fw_ready = false;
+- } else {
+- rtlhal->fw_ready = true;
++ return 1;
+ }
+
+ /* After FW download, we have to reset MAC register */
+@@ -1219,9 +1218,14 @@ void rtl92se_enable_interrupt(struct iee
+
+ void rtl92se_disable_interrupt(struct ieee80211_hw *hw)
+ {
+- struct rtl_priv *rtlpriv = rtl_priv(hw);
+- struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw));
++ struct rtl_priv *rtlpriv;
++ struct rtl_pci *rtlpci;
+
++ rtlpriv = rtl_priv(hw);
++ /* if firmware not available, no interrupts */
++ if (!rtlpriv || !rtlpriv->max_fw_size)
++ return;
++ rtlpci = rtl_pcidev(rtl_pcipriv(hw));
+ rtl_write_dword(rtlpriv, INTA_MASK, 0);
+ rtl_write_dword(rtlpriv, INTA_MASK + 4, 0);
+
+--- a/drivers/net/wireless/rtlwifi/rtl8192se/led.c
++++ b/drivers/net/wireless/rtlwifi/rtl8192se/led.c
+@@ -76,10 +76,13 @@ void rtl92se_sw_led_on(struct ieee80211_
+
+ void rtl92se_sw_led_off(struct ieee80211_hw *hw, struct rtl_led *pled)
+ {
+- struct rtl_priv *rtlpriv = rtl_priv(hw);
++ struct rtl_priv *rtlpriv;
+ struct rtl_pci_priv *pcipriv = rtl_pcipriv(hw);
+ u8 ledcfg;
+
++ rtlpriv = rtl_priv(hw);
++ if (!rtlpriv || rtlpriv->max_fw_size)
++ return;
+ RT_TRACE(rtlpriv, COMP_LED, DBG_LOUD,
+ ("LedAddr:%X ledpin=%d\n", LEDCFG, pled->ledpin));
+
+--- a/drivers/net/wireless/rtlwifi/rtl8192se/sw.c
++++ b/drivers/net/wireless/rtlwifi/rtl8192se/sw.c
+@@ -35,6 +35,8 @@
+ #include "../wifi.h"
+ #include "../core.h"
+ #include "../pci.h"
++#include "../base.h"
++#include "../pci.h"
+ #include "reg.h"
+ #include "def.h"
+ #include "phy.h"
+@@ -89,12 +91,53 @@ static void rtl92s_init_aspm_vars(struct
+ rtlpci->const_support_pciaspm = 2;
+ }
+
++static void rtl92se_fw_cb(const struct firmware *firmware, void *context)
++{
++ struct ieee80211_hw *hw = context;
++ struct rtl_pci_priv *pcipriv = rtl_pcipriv(hw);
++ struct rtl_priv *rtlpriv = rtl_priv(hw);
++ struct rtl_pci *rtlpci = rtl_pcidev(pcipriv);
++ struct rt_firmware *pfirmware = NULL;
++ int err;
++
++ RT_TRACE(rtlpriv, COMP_ERR, DBG_LOUD,
++ ("Firmware callback routine entered!\n"));
++ complete(&rtlpriv->firmware_loading_complete);
++ if (!firmware) {
++ pr_err("Firmware %s not available\n", rtlpriv->cfg->fw_name);
++ rtlpriv->max_fw_size = 0;
++ return;
++ }
++ if (firmware->size > rtlpriv->max_fw_size) {
++ RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
++ ("Firmware is too big!\n"));
++ release_firmware(firmware);
++ return;
++ }
++ pfirmware = (struct rt_firmware *)rtlpriv->rtlhal.pfirmware;
++ memcpy(pfirmware->sz_fw_tmpbuffer, firmware->data, firmware->size);
++ pfirmware->sz_fw_tmpbufferlen = firmware->size;
++ release_firmware(firmware);
++
++ err = ieee80211_register_hw(hw);
++ if (err) {
++ RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
++ ("Can't register mac80211 hw\n"));
++ return;
++ } else {
++ rtlpriv->mac80211.mac80211_registered = 1;
++ }
++ rtlpci->irq_alloc = 1;
++ set_bit(RTL_STATUS_INTERFACE_START, &rtlpriv->status);
++
++ /*init rfkill */
++ rtl_init_rfkill(hw);
++}
++
+ static int rtl92s_init_sw_vars(struct ieee80211_hw *hw)
+ {
+ struct rtl_priv *rtlpriv = rtl_priv(hw);
+ struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw));
+- const struct firmware *firmware;
+- struct rt_firmware *pfirmware = NULL;
+ int err = 0;
+ u16 earlyrxthreshold = 7;
+
+@@ -192,27 +235,19 @@ static int rtl92s_init_sw_vars(struct ie
+ return 1;
+ }
+
++ rtlpriv->max_fw_size = sizeof(struct rt_firmware);
++
+ pr_info("Driver for Realtek RTL8192SE/RTL8191SE\n"
+ "Loading firmware %s\n", rtlpriv->cfg->fw_name);
+ /* request fw */
+- err = request_firmware(&firmware, rtlpriv->cfg->fw_name,
+- rtlpriv->io.dev);
++ err = request_firmware_nowait(THIS_MODULE, 1, rtlpriv->cfg->fw_name,
++ rtlpriv->io.dev, GFP_KERNEL, hw,
++ rtl92se_fw_cb);
+ if (err) {
+ RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
+ ("Failed to request firmware!\n"));
+ return 1;
+ }
+- if (firmware->size > sizeof(struct rt_firmware)) {
+- RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
+- ("Firmware is too big!\n"));
+- release_firmware(firmware);
+- return 1;
+- }
+-
+- pfirmware = (struct rt_firmware *)rtlpriv->rtlhal.pfirmware;
+- memcpy(pfirmware->sz_fw_tmpbuffer, firmware->data, firmware->size);
+- pfirmware->sz_fw_tmpbufferlen = firmware->size;
+- release_firmware(firmware);
+
+ return err;
+ }
+--- a/drivers/net/wireless/rtlwifi/usb.c
++++ b/drivers/net/wireless/rtlwifi/usb.c
+@@ -29,8 +29,8 @@
+
+ #include <linux/usb.h>
+ #include <linux/export.h>
+-#include "core.h"
+ #include "wifi.h"
++#include "core.h"
+ #include "usb.h"
+ #include "base.h"
+ #include "ps.h"
+@@ -667,15 +667,17 @@ static int rtl_usb_start(struct ieee8021
+ struct rtl_usb *rtlusb = rtl_usbdev(rtl_usbpriv(hw));
+
+ err = rtlpriv->cfg->ops->hw_init(hw);
+- rtl_init_rx_config(hw);
++ if (!err) {
++ rtl_init_rx_config(hw);
+
+- /* Enable software */
+- SET_USB_START(rtlusb);
+- /* should after adapter start and interrupt enable. */
+- set_hal_start(rtlhal);
++ /* Enable software */
++ SET_USB_START(rtlusb);
++ /* should after adapter start and interrupt enable. */
++ set_hal_start(rtlhal);
+
+- /* Start bulk IN */
+- _rtl_usb_receive(hw);
++ /* Start bulk IN */
++ _rtl_usb_receive(hw);
++ }
+
+ return err;
+ }
+@@ -952,6 +954,7 @@ int __devinit rtl_usb_probe(struct usb_i
+ return -ENOMEM;
+ }
+ rtlpriv = hw->priv;
++ init_completion(&rtlpriv->firmware_loading_complete);
+ SET_IEEE80211_DEV(hw, &intf->dev);
+ udev = interface_to_usbdev(intf);
+ usb_get_dev(udev);
+@@ -986,24 +989,12 @@ int __devinit rtl_usb_probe(struct usb_i
+ goto error_out;
+ }
+
+- /*init rfkill */
+- /* rtl_init_rfkill(hw); */
+-
+- err = ieee80211_register_hw(hw);
+- if (err) {
+- RT_TRACE(rtlpriv, COMP_INIT, DBG_EMERG,
+- ("Can't register mac80211 hw.\n"));
+- goto error_out;
+- } else {
+- rtlpriv->mac80211.mac80211_registered = 1;
+- }
+- set_bit(RTL_STATUS_INTERFACE_START, &rtlpriv->status);
+ return 0;
+ error_out:
+ rtl_deinit_core(hw);
+ _rtl_usb_io_handler_release(hw);
+- ieee80211_free_hw(hw);
+ usb_put_dev(udev);
++ complete(&rtlpriv->firmware_loading_complete);
+ return -ENODEV;
+ }
+ EXPORT_SYMBOL(rtl_usb_probe);
+@@ -1017,6 +1008,9 @@ void rtl_usb_disconnect(struct usb_inter
+
+ if (unlikely(!rtlpriv))
+ return;
++
++ /* just in case driver is removed before firmware callback */
++ wait_for_completion(&rtlpriv->firmware_loading_complete);
+ /*ieee80211_unregister_hw will call ops_stop */
+ if (rtlmac->mac80211_registered == 1) {
+ ieee80211_unregister_hw(hw);
+--- a/drivers/net/wireless/rtlwifi/wifi.h
++++ b/drivers/net/wireless/rtlwifi/wifi.h
+@@ -36,6 +36,7 @@
+ #include <linux/vmalloc.h>
+ #include <linux/usb.h>
+ #include <net/mac80211.h>
++#include <linux/completion.h>
+ #include "debug.h"
+
+ #define RF_CHANGE_BY_INIT 0
+@@ -1045,7 +1046,6 @@ struct rtl_hal {
+ u16 fw_subversion;
+ bool h2c_setinprogress;
+ u8 last_hmeboxnum;
+- bool fw_ready;
+ /*Reserve page start offset except beacon in TxQ. */
+ u8 fw_rsvdpage_startoffset;
+ u8 h2c_txcmd_seq;
+@@ -1591,6 +1591,7 @@ struct rtl_debug {
+ };
+
+ struct rtl_priv {
++ struct completion firmware_loading_complete;
+ struct rtl_locks locks;
+ struct rtl_works works;
+ struct rtl_mac mac80211;
+@@ -1612,6 +1613,7 @@ struct rtl_priv {
+ struct rtl_rate_priv *rate_priv;
+
+ struct rtl_debug dbg;
++ int max_fw_size;
+
+ /*
+ *hal_cfg : for diff cards