diff options
author | Ming Lei <ming.lei@canonical.com> | 2012-08-21 04:04:27 -0400 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2012-09-07 15:03:36 -0400 |
commit | 32e31de5c37856dba7f174970806e38199e53d81 (patch) | |
tree | d8d2446ec1b38f0aac30b6b8944a97319fb67ebc /drivers | |
parent | ee91592711ed90a1abfbb1b2ceadded11d685164 (diff) |
wireless: ath9k-htc: only load firmware in need
It is not necessary to hold the firmware memory during the whole
driver lifetime, and obviously it does waste memory. Suppose there
are 4 ath9k-htc usb dongles working, kernel has to consume about
4*50KBytes RAM to cache firmware for all dongles. After applying the
patch, kernel only caches one single firmware image in RAM for
all ath9k-htc devices just during system suspend/resume cycle.
When system is ready for loading firmware, ath9k-htc can request
the loading from usersapce. During system resume, ath9k-htc still
can load the firmware which was cached in kernel memory before
system suspend.
Cc: ath9k-devel@lists.ath9k.org
Cc: "Luis R. Rodriguez" <mcgrof@qca.qualcomm.com>
Cc: Jouni Malinen <jouni@qca.qualcomm.com>
Cc: Vasanthakumar Thiagarajan <vthiagar@qca.qualcomm.com>
Cc: Senthil Balasubramanian <senthilb@qca.qualcomm.com>
Cc: "John W. Linville" <linville@tuxdriver.com>
Signed-off-by: Ming Lei <ming.lei@canonical.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/net/wireless/ath/ath9k/hif_usb.c | 33 | ||||
-rw-r--r-- | drivers/net/wireless/ath/ath9k/hif_usb.h | 4 |
2 files changed, 25 insertions, 12 deletions
diff --git a/drivers/net/wireless/ath/ath9k/hif_usb.c b/drivers/net/wireless/ath/ath9k/hif_usb.c index aa327adcc3d8..ee6e50aebf8d 100644 --- a/drivers/net/wireless/ath/ath9k/hif_usb.c +++ b/drivers/net/wireless/ath/ath9k/hif_usb.c | |||
@@ -973,8 +973,8 @@ static void ath9k_hif_usb_dealloc_urbs(struct hif_device_usb *hif_dev) | |||
973 | static int ath9k_hif_usb_download_fw(struct hif_device_usb *hif_dev) | 973 | static int ath9k_hif_usb_download_fw(struct hif_device_usb *hif_dev) |
974 | { | 974 | { |
975 | int transfer, err; | 975 | int transfer, err; |
976 | const void *data = hif_dev->firmware->data; | 976 | const void *data = hif_dev->fw_data; |
977 | size_t len = hif_dev->firmware->size; | 977 | size_t len = hif_dev->fw_size; |
978 | u32 addr = AR9271_FIRMWARE; | 978 | u32 addr = AR9271_FIRMWARE; |
979 | u8 *buf = kzalloc(4096, GFP_KERNEL); | 979 | u8 *buf = kzalloc(4096, GFP_KERNEL); |
980 | u32 firm_offset; | 980 | u32 firm_offset; |
@@ -1017,7 +1017,7 @@ static int ath9k_hif_usb_download_fw(struct hif_device_usb *hif_dev) | |||
1017 | return -EIO; | 1017 | return -EIO; |
1018 | 1018 | ||
1019 | dev_info(&hif_dev->udev->dev, "ath9k_htc: Transferred FW: %s, size: %ld\n", | 1019 | dev_info(&hif_dev->udev->dev, "ath9k_htc: Transferred FW: %s, size: %ld\n", |
1020 | hif_dev->fw_name, (unsigned long) hif_dev->firmware->size); | 1020 | hif_dev->fw_name, (unsigned long) hif_dev->fw_size); |
1021 | 1021 | ||
1022 | return 0; | 1022 | return 0; |
1023 | } | 1023 | } |
@@ -1099,11 +1099,11 @@ static void ath9k_hif_usb_firmware_cb(const struct firmware *fw, void *context) | |||
1099 | 1099 | ||
1100 | hif_dev->htc_handle = ath9k_htc_hw_alloc(hif_dev, &hif_usb, | 1100 | hif_dev->htc_handle = ath9k_htc_hw_alloc(hif_dev, &hif_usb, |
1101 | &hif_dev->udev->dev); | 1101 | &hif_dev->udev->dev); |
1102 | if (hif_dev->htc_handle == NULL) { | 1102 | if (hif_dev->htc_handle == NULL) |
1103 | goto err_fw; | 1103 | goto err_dev_alloc; |
1104 | } | ||
1105 | 1104 | ||
1106 | hif_dev->firmware = fw; | 1105 | hif_dev->fw_data = fw->data; |
1106 | hif_dev->fw_size = fw->size; | ||
1107 | 1107 | ||
1108 | /* Proceed with initialization */ | 1108 | /* Proceed with initialization */ |
1109 | 1109 | ||
@@ -1121,6 +1121,8 @@ static void ath9k_hif_usb_firmware_cb(const struct firmware *fw, void *context) | |||
1121 | goto err_htc_hw_init; | 1121 | goto err_htc_hw_init; |
1122 | } | 1122 | } |
1123 | 1123 | ||
1124 | release_firmware(fw); | ||
1125 | hif_dev->flags |= HIF_USB_READY; | ||
1124 | complete(&hif_dev->fw_done); | 1126 | complete(&hif_dev->fw_done); |
1125 | 1127 | ||
1126 | return; | 1128 | return; |
@@ -1129,8 +1131,8 @@ err_htc_hw_init: | |||
1129 | ath9k_hif_usb_dev_deinit(hif_dev); | 1131 | ath9k_hif_usb_dev_deinit(hif_dev); |
1130 | err_dev_init: | 1132 | err_dev_init: |
1131 | ath9k_htc_hw_free(hif_dev->htc_handle); | 1133 | ath9k_htc_hw_free(hif_dev->htc_handle); |
1134 | err_dev_alloc: | ||
1132 | release_firmware(fw); | 1135 | release_firmware(fw); |
1133 | hif_dev->firmware = NULL; | ||
1134 | err_fw: | 1136 | err_fw: |
1135 | ath9k_hif_usb_firmware_fail(hif_dev); | 1137 | ath9k_hif_usb_firmware_fail(hif_dev); |
1136 | } | 1138 | } |
@@ -1277,11 +1279,10 @@ static void ath9k_hif_usb_disconnect(struct usb_interface *interface) | |||
1277 | 1279 | ||
1278 | wait_for_completion(&hif_dev->fw_done); | 1280 | wait_for_completion(&hif_dev->fw_done); |
1279 | 1281 | ||
1280 | if (hif_dev->firmware) { | 1282 | if (hif_dev->flags & HIF_USB_READY) { |
1281 | ath9k_htc_hw_deinit(hif_dev->htc_handle, unplugged); | 1283 | ath9k_htc_hw_deinit(hif_dev->htc_handle, unplugged); |
1282 | ath9k_htc_hw_free(hif_dev->htc_handle); | 1284 | ath9k_htc_hw_free(hif_dev->htc_handle); |
1283 | ath9k_hif_usb_dev_deinit(hif_dev); | 1285 | ath9k_hif_usb_dev_deinit(hif_dev); |
1284 | release_firmware(hif_dev->firmware); | ||
1285 | } | 1286 | } |
1286 | 1287 | ||
1287 | usb_set_intfdata(interface, NULL); | 1288 | usb_set_intfdata(interface, NULL); |
@@ -1317,13 +1318,23 @@ static int ath9k_hif_usb_resume(struct usb_interface *interface) | |||
1317 | struct hif_device_usb *hif_dev = usb_get_intfdata(interface); | 1318 | struct hif_device_usb *hif_dev = usb_get_intfdata(interface); |
1318 | struct htc_target *htc_handle = hif_dev->htc_handle; | 1319 | struct htc_target *htc_handle = hif_dev->htc_handle; |
1319 | int ret; | 1320 | int ret; |
1321 | const struct firmware *fw; | ||
1320 | 1322 | ||
1321 | ret = ath9k_hif_usb_alloc_urbs(hif_dev); | 1323 | ret = ath9k_hif_usb_alloc_urbs(hif_dev); |
1322 | if (ret) | 1324 | if (ret) |
1323 | return ret; | 1325 | return ret; |
1324 | 1326 | ||
1325 | if (hif_dev->firmware) { | 1327 | if (hif_dev->flags & HIF_USB_READY) { |
1328 | /* request cached firmware during suspend/resume cycle */ | ||
1329 | ret = request_firmware(&fw, hif_dev->fw_name, | ||
1330 | &hif_dev->udev->dev); | ||
1331 | if (ret) | ||
1332 | goto fail_resume; | ||
1333 | |||
1334 | hif_dev->fw_data = fw->data; | ||
1335 | hif_dev->fw_size = fw->size; | ||
1326 | ret = ath9k_hif_usb_download_fw(hif_dev); | 1336 | ret = ath9k_hif_usb_download_fw(hif_dev); |
1337 | release_firmware(fw); | ||
1327 | if (ret) | 1338 | if (ret) |
1328 | goto fail_resume; | 1339 | goto fail_resume; |
1329 | } else { | 1340 | } else { |
diff --git a/drivers/net/wireless/ath/ath9k/hif_usb.h b/drivers/net/wireless/ath/ath9k/hif_usb.h index 487ff658b4c1..51496e74b83e 100644 --- a/drivers/net/wireless/ath/ath9k/hif_usb.h +++ b/drivers/net/wireless/ath/ath9k/hif_usb.h | |||
@@ -85,12 +85,14 @@ struct cmd_buf { | |||
85 | }; | 85 | }; |
86 | 86 | ||
87 | #define HIF_USB_START BIT(0) | 87 | #define HIF_USB_START BIT(0) |
88 | #define HIF_USB_READY BIT(1) | ||
88 | 89 | ||
89 | struct hif_device_usb { | 90 | struct hif_device_usb { |
90 | struct usb_device *udev; | 91 | struct usb_device *udev; |
91 | struct usb_interface *interface; | 92 | struct usb_interface *interface; |
92 | const struct usb_device_id *usb_device_id; | 93 | const struct usb_device_id *usb_device_id; |
93 | const struct firmware *firmware; | 94 | const void *fw_data; |
95 | size_t fw_size; | ||
94 | struct completion fw_done; | 96 | struct completion fw_done; |
95 | struct htc_target *htc_handle; | 97 | struct htc_target *htc_handle; |
96 | struct hif_usb_tx tx; | 98 | struct hif_usb_tx tx; |