04ca5f9
A recent LKML thread (http://lkml.indiana.edu/hypermail/linux/kernel/1112.3/00965.html)
04ca5f9
discusses warnings that occur during a suspend/resume cycle. The driver
04ca5f9
attempts to read the firmware file before userspace is ready, leading to the
04ca5f9
following warning:
04ca5f9
04ca5f9
WARNING: at drivers/base/firmware_class.c:537 _request_firmware+0x3f6/0x420()
04ca5f9
04ca5f9
For rtl8192cu, the problem is fixed by storing the firmware in a global buffer
04ca5f9
rather than one allocated per device. The usage count is increased when
04ca5f9
suspending and decreased when resuming. This way, the firmware is retained
04ca5f9
through a suspend/resume cycle, and does not have to be reread.
04ca5f9
04ca5f9
This patch should fix the bug reported in
04ca5f9
https://bugzilla.redhat.com/show_bug.cgi?id=771002.
04ca5f9
04ca5f9
Note: This patch also touches rtl8192ce as the "firmware" loaded message
04ca5f9
is now printed in the wrong place.
04ca5f9
Note: This patch also touches rtl8192ce as the "firmware" loaded message
04ca5f9
is now printed in the wrong place.
04ca5f9
04ca5f9
Reported-by: Mohammed Arafa <bugzilla@xxxxxxxxxxxx>
04ca5f9
Reported-by: Dave Jones <davej@xxxxxxxxxx>
04ca5f9
Signed-off-by: Larry Finger <Larry.Finger@xxxxxxxxxxxx>
04ca5f9
Cc: Linus Torvalds <torvalds@xxxxxxxxxxxxxxxxxxxx>
04ca5f9
Cc: Stable <stable@xxxxxxxxxxxxxxx>
04ca5f9
04ca5f9
---
04ca5f9
 drivers/net/wireless/rtlwifi/rtl8192c/fw_common.c |    1 -
04ca5f9
 drivers/net/wireless/rtlwifi/rtl8192ce/sw.c       |    1 +
04ca5f9
 drivers/net/wireless/rtlwifi/rtl8192cu/sw.c       |   58 +++++++++++++++++----
04ca5f9
 3 files changed, 49 insertions(+), 11 deletions(-)
04ca5f9
04ca5f9
--- linux-2.6/drivers/net/wireless/rtlwifi/rtl8192c/fw_common.c	2012-01-13 13:07:58.830625006 -0500
04ca5f9
+++ linux-2.6/drivers/net/wireless/rtlwifi/rtl8192c/fw_common.c	2012-01-13 13:08:06.825439927 -0500
04ca5f9
@@ -227,7 +227,6 @@ int rtl92c_download_fw(struct ieee80211_
04ca5f9
 	u32 fwsize;
04ca5f9
 	enum version_8192c version = rtlhal->version;
04ca5f9
 
04ca5f9
-	pr_info("Loading firmware file %s\n", rtlpriv->cfg->fw_name);
04ca5f9
 	if (!rtlhal->pfirmware)
04ca5f9
 		return 1;
04ca5f9
04ca5f9
--- linux-2.6/drivers/net/wireless/rtlwifi/rtl8192ce/sw.c	
04ca5f9
+++ linux-2.6/drivers/net/wireless/rtlwifi/rtl8192ce/sw.c	
04ca5f9
@@ -186,6 +186,7 @@ int rtl92c_init_sw_vars(struct ieee80211
04ca5f9
 	memcpy(rtlpriv->rtlhal.pfirmware, firmware->data, firmware->size);
04ca5f9
 	rtlpriv->rtlhal.fwsize = firmware->size;
04ca5f9
 	release_firmware(firmware);
04ca5f9
+	pr_info("rtl8192ce: Loaded firmware file %s\n", rtlpriv->cfg->fw_name);
04ca5f9
 
04ca5f9
 	return 0;
04ca5f9
 }
04ca5f9
--- linux-2.6/drivers/net/wireless/rtlwifi/rtl8192cu/sw.c	
04ca5f9
+++ linux-2.6/drivers/net/wireless/rtlwifi/rtl8192cu/sw.c	
04ca5f9
@@ -43,6 +43,8 @@ 
04ca5f9
 #include "hw.h"
04ca5f9
 #include <linux/vmalloc.h>
04ca5f9
 #include <linux/module.h>
04ca5f9
+#include <linux/atomic.h>
04ca5f9
+#include <linux/types.h>
04ca5f9
 
04ca5f9
 MODULE_AUTHOR("Georgia		<georgia@realtek.com>");
04ca5f9
 MODULE_AUTHOR("Ziv Huang	<ziv_huang@realtek.com>");
04ca5f9
@@ -51,6 +53,10 @@ MODULE_LICENSE("GPL");
04ca5f9
 MODULE_DESCRIPTION("Realtek 8192C/8188C 802.11n USB wireless");
04ca5f9
 MODULE_FIRMWARE("rtlwifi/rtl8192cufw.bin");
04ca5f9
 
04ca5f9
+static char *rtl8192cu_firmware;		/* pointer to firmware */
04ca5f9
+static int firmware_size;
04ca5f9
+static atomic_t usage_count;
04ca5f9
+
04ca5f9
 static int rtl92cu_init_sw_vars(struct ieee80211_hw *hw)
04ca5f9
 {
04ca5f9
 	struct rtl_priv *rtlpriv = rtl_priv(hw);
04ca5f9
@@ -62,12 +68,21 @@ static int rtl92cu_init_sw_vars(struct i
04ca5f9
 	rtlpriv->dm.disable_framebursting = false;
04ca5f9
 	rtlpriv->dm.thermalvalue = 0;
04ca5f9
 	rtlpriv->dbg.global_debuglevel = rtlpriv->cfg->mod_params->debug;
04ca5f9
-	rtlpriv->rtlhal.pfirmware = vmalloc(0x4000);
04ca5f9
-	if (!rtlpriv->rtlhal.pfirmware) {
04ca5f9
+
04ca5f9
+	if (rtl8192cu_firmware) {
04ca5f9
+		/* firmware already loaded - true for suspend/resume
04ca5f9
+		 * and multiple instances of the device */
04ca5f9
+		rtlpriv->rtlhal.pfirmware = rtl8192cu_firmware;
04ca5f9
+		rtlpriv->rtlhal.fwsize = firmware_size;
04ca5f9
+		return 0;
04ca5f9
+	}
04ca5f9
+	rtl8192cu_firmware = vzalloc(0x4000);
04ca5f9
+	if (!rtl8192cu_firmware) {
04ca5f9
 		RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
04ca5f9
 			 ("Can't alloc buffer for fw.\n"));
04ca5f9
 		return 1;
04ca5f9
 	}
04ca5f9
+
04ca5f9
 	/* request fw */
04ca5f9
 	err = request_firmware(&firmware, rtlpriv->cfg->fw_name,
04ca5f9
 			rtlpriv->io.dev);
04ca5f9
@@ -82,9 +97,14 @@ static int rtl92cu_init_sw_vars(struct i
04ca5f9
 		release_firmware(firmware);
04ca5f9
 		return 1;
04ca5f9
 	}
04ca5f9
-	memcpy(rtlpriv->rtlhal.pfirmware, firmware->data, firmware->size);
04ca5f9
+	pr_info("rtl8192cu: Loaded firmware from file %s\n",
04ca5f9
+		rtlpriv->cfg->fw_name);
04ca5f9
+	memcpy(rtl8192cu_firmware, firmware->data, firmware->size);
04ca5f9
+	firmware_size = firmware->size;
04ca5f9
 	rtlpriv->rtlhal.fwsize = firmware->size;
04ca5f9
+	rtlpriv->rtlhal.pfirmware = rtl8192cu_firmware;
04ca5f9
 	release_firmware(firmware);
04ca5f9
+	atomic_inc(&usage_count);
04ca5f9
 
04ca5f9
 	return 0;
04ca5f9
 }
04ca5f9
@@ -93,12 +113,30 @@ static void rtl92cu_deinit_sw_vars(struc
04ca5f9
 {
04ca5f9
 	struct rtl_priv *rtlpriv = rtl_priv(hw);
04ca5f9
 
04ca5f9
-	if (rtlpriv->rtlhal.pfirmware) {
04ca5f9
-		vfree(rtlpriv->rtlhal.pfirmware);
04ca5f9
+	atomic_dec(&usage_count);
04ca5f9
+	if (!atomic_read(&usage_count) && rtlpriv->rtlhal.pfirmware) {
04ca5f9
+		vfree(rtl8192cu_firmware);
04ca5f9
+		rtl8192cu_firmware = NULL;
04ca5f9
 		rtlpriv->rtlhal.pfirmware = NULL;
04ca5f9
 	}
04ca5f9
 }
04ca5f9
 
04ca5f9
+#ifdef CONFIG_PM_SLEEP
04ca5f9
+static int rtl8192cu_usb_suspend(struct usb_interface *pusb_intf,
04ca5f9
+				 pm_message_t message)
04ca5f9
+{
04ca5f9
+	/* Increase usage_count to Save loaded fw across suspend/resume */
04ca5f9
+	atomic_inc(&usage_count);
04ca5f9
+	return 0;
04ca5f9
+}
04ca5f9
+
04ca5f9
+static int rtl8192cu_usb_resume(struct usb_interface *pusb_intf)
04ca5f9
+{
04ca5f9
+	atomic_dec(&usage_count);	/* after resume, decrease usage count */
04ca5f9
+	return 0;
04ca5f9
+}
04ca5f9
+#endif
04ca5f9
+
04ca5f9
 static struct rtl_hal_ops rtl8192cu_hal_ops = {
04ca5f9
 	.init_sw_vars = rtl92cu_init_sw_vars,
04ca5f9
 	.deinit_sw_vars = rtl92cu_deinit_sw_vars,
04ca5f9
@@ -374,11 +412,10 @@ static struct usb_driver rtl8192cu_drive
04ca5f9
 	.disconnect = rtl_usb_disconnect,
04ca5f9
 	.id_table = rtl8192c_usb_ids,
04ca5f9
 
04ca5f9
-#ifdef CONFIG_PM
04ca5f9
-	/* .suspend = rtl_usb_suspend, */
04ca5f9
-	/* .resume = rtl_usb_resume, */
04ca5f9
-	/* .reset_resume = rtl8192c_resume, */
04ca5f9
-#endif /* CONFIG_PM */
04ca5f9
+#ifdef CONFIG_PM_SLEEP
04ca5f9
+	.suspend = rtl8192cu_usb_suspend,
04ca5f9
+	.resume = rtl8192cu_usb_resume,
04ca5f9
+#endif /* CONFIG_PM_SLEEP */
04ca5f9
 #ifdef CONFIG_AUTOSUSPEND
04ca5f9
 	.supports_autosuspend = 1,
04ca5f9
 #endif