Skip to content

Commit

Permalink
rtlwifi: Convert to asynchronous firmware load
Browse files Browse the repository at this point in the history
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>
Cc: Stable <stable@vger.kernel.org>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
  • Loading branch information
Larry Finger authored and John W. Linville committed Jan 30, 2012
1 parent feced20 commit b0302ab
Show file tree
Hide file tree
Showing 20 changed files with 194 additions and 164 deletions.
1 change: 1 addition & 0 deletions drivers/net/wireless/rtlwifi/base.c
Original file line number Diff line number Diff line change
Expand Up @@ -410,6 +410,7 @@ void rtl_init_rfkill(struct ieee80211_hw *hw)

wiphy_rfkill_start_polling(hw->wiphy);
}
EXPORT_SYMBOL(rtl_init_rfkill);

void rtl_deinit_rfkill(struct ieee80211_hw *hw)
{
Expand Down
46 changes: 45 additions & 1 deletion drivers/net/wireless/rtlwifi/core.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down Expand Up @@ -254,10 +296,12 @@ static int rtl_op_config(struct ieee80211_hw *hw, u32 changed)
* because that will cause nullfunc send by mac80211
* fail, and cause pkt loss, we have tested that 5mA
* is worked very well */
if (!rtlpriv->psc.multi_buffered)
if (!rtlpriv->psc.multi_buffered) {
queue_delayed_work(rtlpriv->works.rtl_wq,
&rtlpriv->works.ps_work,
MSECS(5));
pr_info("In section\n");
}
} else {
rtl_swlps_rf_awake(hw);
rtlpriv->psc.sw_ps_enabled = false;
Expand Down
2 changes: 2 additions & 0 deletions drivers/net/wireless/rtlwifi/core.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,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
26 changes: 9 additions & 17 deletions drivers/net/wireless/rtlwifi/pci.c
Original file line number Diff line number Diff line change
Expand Up @@ -1565,6 +1565,9 @@ static void rtl_pci_stop(struct ieee80211_hw *hw)

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);
Expand Down Expand Up @@ -1779,6 +1782,7 @@ int __devinit rtl_pci_probe(struct pci_dev *pdev,
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;
Expand All @@ -1799,7 +1803,7 @@ int __devinit rtl_pci_probe(struct pci_dev *pdev,
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);
Expand Down Expand Up @@ -1862,25 +1866,13 @@ int __devinit rtl_pci_probe(struct pci_dev *pdev,
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,
"failed to create sysfs device attributes\n");
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);
Expand All @@ -1889,24 +1881,22 @@ int __devinit rtl_pci_probe(struct pci_dev *pdev,
"%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:

Expand All @@ -1925,6 +1915,8 @@ void rtl_pci_disconnect(struct pci_dev *pdev)
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);
Expand Down
1 change: 0 additions & 1 deletion drivers/net/wireless/rtlwifi/pci.h
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,6 @@ int __devinit rtl_pci_probe(struct pci_dev *pdev,
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);
Expand Down
3 changes: 2 additions & 1 deletion drivers/net/wireless/rtlwifi/ps.c
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,8 @@ bool rtl_ps_enable_nic(struct ieee80211_hw *hw)
"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 */
Expand Down
10 changes: 1 addition & 9 deletions drivers/net/wireless/rtlwifi/rtl8192c/fw_common.c
Original file line number Diff line number Diff line change
Expand Up @@ -257,10 +257,9 @@ int rtl92c_download_fw(struct ieee80211_hw *hw)
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;
Expand Down Expand Up @@ -512,15 +511,8 @@ static void _rtl92c_fill_h2c_command(struct ieee80211_hw *hw,
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);
Expand Down
6 changes: 1 addition & 5 deletions drivers/net/wireless/rtlwifi/rtl8192ce/hw.c
Original file line number Diff line number Diff line change
Expand Up @@ -917,10 +917,7 @@ int rtl92ce_hw_init(struct ieee80211_hw *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;
Expand Down Expand Up @@ -1193,7 +1190,6 @@ static void _rtl92ce_poweroff_adapter(struct ieee80211_hw *hw)
{
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;

Expand All @@ -1204,7 +1200,7 @@ static void _rtl92ce_poweroff_adapter(struct ieee80211_hw *hw)
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);
Expand Down
24 changes: 8 additions & 16 deletions drivers/net/wireless/rtlwifi/rtl8192ce/sw.c
Original file line number Diff line number Diff line change
Expand Up @@ -91,9 +91,7 @@ int rtl92c_init_sw_vars(struct ieee80211_hw *hw)
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);

Expand Down Expand Up @@ -165,26 +163,20 @@ int rtl92c_init_sw_vars(struct ieee80211_hw *hw)
/* 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;
}
Expand Down
35 changes: 15 additions & 20 deletions drivers/net/wireless/rtlwifi/rtl8192cu/hw.c
Original file line number Diff line number Diff line change
Expand Up @@ -997,10 +997,7 @@ int rtl92cu_hw_init(struct ieee80211_hw *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);
Expand Down Expand Up @@ -1094,23 +1091,21 @@ static void _ResetDigitalProcedure1(struct ieee80211_hw *hw, bool bWithoutHWSM)
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 failed!.........................\n");
/* if 8051 reset fail, reset MAC. */
rtl_write_byte(rtlpriv,
REG_SYS_FUNC_EN + 1,
0x50);
udelay(100);
}
/* 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);
}
}
/* Reset MAC and Enable 8051 */
Expand Down
29 changes: 10 additions & 19 deletions drivers/net/wireless/rtlwifi/rtl8192cu/sw.c
Original file line number Diff line number Diff line change
Expand Up @@ -53,37 +53,28 @@ 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;
rtlpriv->dm.dm_flag = 0;
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;
}
Expand Down
Loading

0 comments on commit b0302ab

Please sign in to comment.