diff options
author | Sujith Manoharan <c_manoha@qca.qualcomm.com> | 2012-01-30 03:47:18 -0500 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2012-01-30 15:48:42 -0500 |
commit | 0ed7b93e307fd02188a33b80897069d3acc76485 (patch) | |
tree | 889bd982d2cd0b00f24723c70ebedd5754e3ef69 /drivers/net | |
parent | ed072f9e80f02e5ae4150ad5a605fc3906e9fbc7 (diff) |
ath9k_htc: Load firmware asynchronously
This patch modifies ath9k_htc to load the needed
firmware in an asynchronous manner, fixing timeouts
that were introduced with the new udev changes.
Signed-off-by: Sujith Manoharan <c_manoha@qca.qualcomm.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers/net')
-rw-r--r-- | drivers/net/wireless/ath/ath9k/hif_usb.c | 154 | ||||
-rw-r--r-- | drivers/net/wireless/ath/ath9k/hif_usb.h | 3 |
2 files changed, 101 insertions, 56 deletions
diff --git a/drivers/net/wireless/ath/ath9k/hif_usb.c b/drivers/net/wireless/ath/ath9k/hif_usb.c index 77c8ded8de57..f317515d8bf3 100644 --- a/drivers/net/wireless/ath/ath9k/hif_usb.c +++ b/drivers/net/wireless/ath/ath9k/hif_usb.c | |||
@@ -968,8 +968,7 @@ static void ath9k_hif_usb_dealloc_urbs(struct hif_device_usb *hif_dev) | |||
968 | ath9k_hif_usb_dealloc_rx_urbs(hif_dev); | 968 | ath9k_hif_usb_dealloc_rx_urbs(hif_dev); |
969 | } | 969 | } |
970 | 970 | ||
971 | static int ath9k_hif_usb_download_fw(struct hif_device_usb *hif_dev, | 971 | static int ath9k_hif_usb_download_fw(struct hif_device_usb *hif_dev) |
972 | u32 drv_info) | ||
973 | { | 972 | { |
974 | int transfer, err; | 973 | int transfer, err; |
975 | const void *data = hif_dev->firmware->data; | 974 | const void *data = hif_dev->firmware->data; |
@@ -1000,7 +999,7 @@ static int ath9k_hif_usb_download_fw(struct hif_device_usb *hif_dev, | |||
1000 | } | 999 | } |
1001 | kfree(buf); | 1000 | kfree(buf); |
1002 | 1001 | ||
1003 | if (IS_AR7010_DEVICE(drv_info)) | 1002 | if (IS_AR7010_DEVICE(hif_dev->usb_device_id->driver_info)) |
1004 | firm_offset = AR7010_FIRMWARE_TEXT; | 1003 | firm_offset = AR7010_FIRMWARE_TEXT; |
1005 | else | 1004 | else |
1006 | firm_offset = AR9271_FIRMWARE_TEXT; | 1005 | firm_offset = AR9271_FIRMWARE_TEXT; |
@@ -1021,28 +1020,18 @@ static int ath9k_hif_usb_download_fw(struct hif_device_usb *hif_dev, | |||
1021 | return 0; | 1020 | return 0; |
1022 | } | 1021 | } |
1023 | 1022 | ||
1024 | static int ath9k_hif_usb_dev_init(struct hif_device_usb *hif_dev, u32 drv_info) | 1023 | static int ath9k_hif_usb_dev_init(struct hif_device_usb *hif_dev) |
1025 | { | 1024 | { |
1026 | int ret, idx; | ||
1027 | struct usb_host_interface *alt = &hif_dev->interface->altsetting[0]; | 1025 | struct usb_host_interface *alt = &hif_dev->interface->altsetting[0]; |
1028 | struct usb_endpoint_descriptor *endp; | 1026 | struct usb_endpoint_descriptor *endp; |
1027 | int ret, idx; | ||
1029 | 1028 | ||
1030 | /* Request firmware */ | 1029 | ret = ath9k_hif_usb_download_fw(hif_dev); |
1031 | ret = request_firmware(&hif_dev->firmware, hif_dev->fw_name, | ||
1032 | &hif_dev->udev->dev); | ||
1033 | if (ret) { | ||
1034 | dev_err(&hif_dev->udev->dev, | ||
1035 | "ath9k_htc: Firmware - %s not found\n", hif_dev->fw_name); | ||
1036 | goto err_fw_req; | ||
1037 | } | ||
1038 | |||
1039 | /* Download firmware */ | ||
1040 | ret = ath9k_hif_usb_download_fw(hif_dev, drv_info); | ||
1041 | if (ret) { | 1030 | if (ret) { |
1042 | dev_err(&hif_dev->udev->dev, | 1031 | dev_err(&hif_dev->udev->dev, |
1043 | "ath9k_htc: Firmware - %s download failed\n", | 1032 | "ath9k_htc: Firmware - %s download failed\n", |
1044 | hif_dev->fw_name); | 1033 | hif_dev->fw_name); |
1045 | goto err_fw_download; | 1034 | return ret; |
1046 | } | 1035 | } |
1047 | 1036 | ||
1048 | /* On downloading the firmware to the target, the USB descriptor of EP4 | 1037 | /* On downloading the firmware to the target, the USB descriptor of EP4 |
@@ -1064,23 +1053,84 @@ static int ath9k_hif_usb_dev_init(struct hif_device_usb *hif_dev, u32 drv_info) | |||
1064 | if (ret) { | 1053 | if (ret) { |
1065 | dev_err(&hif_dev->udev->dev, | 1054 | dev_err(&hif_dev->udev->dev, |
1066 | "ath9k_htc: Unable to allocate URBs\n"); | 1055 | "ath9k_htc: Unable to allocate URBs\n"); |
1067 | goto err_fw_download; | 1056 | return ret; |
1068 | } | 1057 | } |
1069 | 1058 | ||
1070 | return 0; | 1059 | return 0; |
1071 | |||
1072 | err_fw_download: | ||
1073 | release_firmware(hif_dev->firmware); | ||
1074 | err_fw_req: | ||
1075 | hif_dev->firmware = NULL; | ||
1076 | return ret; | ||
1077 | } | 1060 | } |
1078 | 1061 | ||
1079 | static void ath9k_hif_usb_dev_deinit(struct hif_device_usb *hif_dev) | 1062 | static void ath9k_hif_usb_dev_deinit(struct hif_device_usb *hif_dev) |
1080 | { | 1063 | { |
1081 | ath9k_hif_usb_dealloc_urbs(hif_dev); | 1064 | ath9k_hif_usb_dealloc_urbs(hif_dev); |
1082 | if (hif_dev->firmware) | 1065 | } |
1083 | release_firmware(hif_dev->firmware); | 1066 | |
1067 | /* | ||
1068 | * If initialization fails or the FW cannot be retrieved, | ||
1069 | * detach the device. | ||
1070 | */ | ||
1071 | static void ath9k_hif_usb_firmware_fail(struct hif_device_usb *hif_dev) | ||
1072 | { | ||
1073 | struct device *parent = hif_dev->udev->dev.parent; | ||
1074 | |||
1075 | complete(&hif_dev->fw_done); | ||
1076 | |||
1077 | if (parent) | ||
1078 | device_lock(parent); | ||
1079 | |||
1080 | device_release_driver(&hif_dev->udev->dev); | ||
1081 | |||
1082 | if (parent) | ||
1083 | device_unlock(parent); | ||
1084 | } | ||
1085 | |||
1086 | static void ath9k_hif_usb_firmware_cb(const struct firmware *fw, void *context) | ||
1087 | { | ||
1088 | struct hif_device_usb *hif_dev = context; | ||
1089 | int ret; | ||
1090 | |||
1091 | if (!fw) { | ||
1092 | dev_err(&hif_dev->udev->dev, | ||
1093 | "ath9k_htc: Failed to get firmware %s\n", | ||
1094 | hif_dev->fw_name); | ||
1095 | goto err_fw; | ||
1096 | } | ||
1097 | |||
1098 | hif_dev->htc_handle = ath9k_htc_hw_alloc(hif_dev, &hif_usb, | ||
1099 | &hif_dev->udev->dev); | ||
1100 | if (hif_dev->htc_handle == NULL) { | ||
1101 | goto err_fw; | ||
1102 | } | ||
1103 | |||
1104 | hif_dev->firmware = fw; | ||
1105 | |||
1106 | /* Proceed with initialization */ | ||
1107 | |||
1108 | ret = ath9k_hif_usb_dev_init(hif_dev); | ||
1109 | if (ret) | ||
1110 | goto err_dev_init; | ||
1111 | |||
1112 | ret = ath9k_htc_hw_init(hif_dev->htc_handle, | ||
1113 | &hif_dev->interface->dev, | ||
1114 | hif_dev->usb_device_id->idProduct, | ||
1115 | hif_dev->udev->product, | ||
1116 | hif_dev->usb_device_id->driver_info); | ||
1117 | if (ret) { | ||
1118 | ret = -EINVAL; | ||
1119 | goto err_htc_hw_init; | ||
1120 | } | ||
1121 | |||
1122 | complete(&hif_dev->fw_done); | ||
1123 | |||
1124 | return; | ||
1125 | |||
1126 | err_htc_hw_init: | ||
1127 | ath9k_hif_usb_dev_deinit(hif_dev); | ||
1128 | err_dev_init: | ||
1129 | ath9k_htc_hw_free(hif_dev->htc_handle); | ||
1130 | release_firmware(fw); | ||
1131 | hif_dev->firmware = NULL; | ||
1132 | err_fw: | ||
1133 | ath9k_hif_usb_firmware_fail(hif_dev); | ||
1084 | } | 1134 | } |
1085 | 1135 | ||
1086 | /* | 1136 | /* |
@@ -1155,20 +1205,16 @@ static int ath9k_hif_usb_probe(struct usb_interface *interface, | |||
1155 | } | 1205 | } |
1156 | 1206 | ||
1157 | usb_get_dev(udev); | 1207 | usb_get_dev(udev); |
1208 | |||
1158 | hif_dev->udev = udev; | 1209 | hif_dev->udev = udev; |
1159 | hif_dev->interface = interface; | 1210 | hif_dev->interface = interface; |
1160 | hif_dev->device_id = id->idProduct; | 1211 | hif_dev->usb_device_id = id; |
1161 | #ifdef CONFIG_PM | 1212 | #ifdef CONFIG_PM |
1162 | udev->reset_resume = 1; | 1213 | udev->reset_resume = 1; |
1163 | #endif | 1214 | #endif |
1164 | usb_set_intfdata(interface, hif_dev); | 1215 | usb_set_intfdata(interface, hif_dev); |
1165 | 1216 | ||
1166 | hif_dev->htc_handle = ath9k_htc_hw_alloc(hif_dev, &hif_usb, | 1217 | init_completion(&hif_dev->fw_done); |
1167 | &hif_dev->udev->dev); | ||
1168 | if (hif_dev->htc_handle == NULL) { | ||
1169 | ret = -ENOMEM; | ||
1170 | goto err_htc_hw_alloc; | ||
1171 | } | ||
1172 | 1218 | ||
1173 | /* Find out which firmware to load */ | 1219 | /* Find out which firmware to load */ |
1174 | 1220 | ||
@@ -1177,29 +1223,22 @@ static int ath9k_hif_usb_probe(struct usb_interface *interface, | |||
1177 | else | 1223 | else |
1178 | hif_dev->fw_name = FIRMWARE_AR9271; | 1224 | hif_dev->fw_name = FIRMWARE_AR9271; |
1179 | 1225 | ||
1180 | ret = ath9k_hif_usb_dev_init(hif_dev, id->driver_info); | 1226 | ret = request_firmware_nowait(THIS_MODULE, true, hif_dev->fw_name, |
1181 | if (ret) { | 1227 | &hif_dev->udev->dev, GFP_KERNEL, |
1182 | ret = -EINVAL; | 1228 | hif_dev, ath9k_hif_usb_firmware_cb); |
1183 | goto err_hif_init_usb; | ||
1184 | } | ||
1185 | |||
1186 | ret = ath9k_htc_hw_init(hif_dev->htc_handle, | ||
1187 | &interface->dev, hif_dev->device_id, | ||
1188 | hif_dev->udev->product, id->driver_info); | ||
1189 | if (ret) { | 1229 | if (ret) { |
1190 | ret = -EINVAL; | 1230 | dev_err(&hif_dev->udev->dev, |
1191 | goto err_htc_hw_init; | 1231 | "ath9k_htc: Async request for firmware %s failed\n", |
1232 | hif_dev->fw_name); | ||
1233 | goto err_fw_req; | ||
1192 | } | 1234 | } |
1193 | 1235 | ||
1194 | dev_info(&hif_dev->udev->dev, "ath9k_htc: USB layer initialized\n"); | 1236 | dev_info(&hif_dev->udev->dev, "ath9k_htc: Firmware %s requested\n", |
1237 | hif_dev->fw_name); | ||
1195 | 1238 | ||
1196 | return 0; | 1239 | return 0; |
1197 | 1240 | ||
1198 | err_htc_hw_init: | 1241 | err_fw_req: |
1199 | ath9k_hif_usb_dev_deinit(hif_dev); | ||
1200 | err_hif_init_usb: | ||
1201 | ath9k_htc_hw_free(hif_dev->htc_handle); | ||
1202 | err_htc_hw_alloc: | ||
1203 | usb_set_intfdata(interface, NULL); | 1242 | usb_set_intfdata(interface, NULL); |
1204 | kfree(hif_dev); | 1243 | kfree(hif_dev); |
1205 | usb_put_dev(udev); | 1244 | usb_put_dev(udev); |
@@ -1234,9 +1273,15 @@ static void ath9k_hif_usb_disconnect(struct usb_interface *interface) | |||
1234 | if (!hif_dev) | 1273 | if (!hif_dev) |
1235 | return; | 1274 | return; |
1236 | 1275 | ||
1237 | ath9k_htc_hw_deinit(hif_dev->htc_handle, unplugged); | 1276 | wait_for_completion(&hif_dev->fw_done); |
1238 | ath9k_htc_hw_free(hif_dev->htc_handle); | 1277 | |
1239 | ath9k_hif_usb_dev_deinit(hif_dev); | 1278 | if (hif_dev->firmware) { |
1279 | ath9k_htc_hw_deinit(hif_dev->htc_handle, unplugged); | ||
1280 | ath9k_htc_hw_free(hif_dev->htc_handle); | ||
1281 | ath9k_hif_usb_dev_deinit(hif_dev); | ||
1282 | release_firmware(hif_dev->firmware); | ||
1283 | } | ||
1284 | |||
1240 | usb_set_intfdata(interface, NULL); | 1285 | usb_set_intfdata(interface, NULL); |
1241 | 1286 | ||
1242 | if (!unplugged && (hif_dev->flags & HIF_USB_START)) | 1287 | if (!unplugged && (hif_dev->flags & HIF_USB_START)) |
@@ -1276,8 +1321,7 @@ static int ath9k_hif_usb_resume(struct usb_interface *interface) | |||
1276 | return ret; | 1321 | return ret; |
1277 | 1322 | ||
1278 | if (hif_dev->firmware) { | 1323 | if (hif_dev->firmware) { |
1279 | ret = ath9k_hif_usb_download_fw(hif_dev, | 1324 | ret = ath9k_hif_usb_download_fw(hif_dev); |
1280 | htc_handle->drv_priv->ah->hw_version.usbdev); | ||
1281 | if (ret) | 1325 | if (ret) |
1282 | goto fail_resume; | 1326 | goto fail_resume; |
1283 | } else { | 1327 | } else { |
diff --git a/drivers/net/wireless/ath/ath9k/hif_usb.h b/drivers/net/wireless/ath/ath9k/hif_usb.h index 794f63094e5d..487ff658b4c1 100644 --- a/drivers/net/wireless/ath/ath9k/hif_usb.h +++ b/drivers/net/wireless/ath/ath9k/hif_usb.h | |||
@@ -87,10 +87,11 @@ struct cmd_buf { | |||
87 | #define HIF_USB_START BIT(0) | 87 | #define HIF_USB_START BIT(0) |
88 | 88 | ||
89 | struct hif_device_usb { | 89 | struct hif_device_usb { |
90 | u16 device_id; | ||
91 | struct usb_device *udev; | 90 | struct usb_device *udev; |
92 | struct usb_interface *interface; | 91 | struct usb_interface *interface; |
92 | const struct usb_device_id *usb_device_id; | ||
93 | const struct firmware *firmware; | 93 | const struct firmware *firmware; |
94 | struct completion fw_done; | ||
94 | struct htc_target *htc_handle; | 95 | struct htc_target *htc_handle; |
95 | struct hif_usb_tx tx; | 96 | struct hif_usb_tx tx; |
96 | struct usb_anchor regout_submitted; | 97 | struct usb_anchor regout_submitted; |