aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorBrian Cavagnolo <brian@cozybit.com>2010-11-12 20:23:52 -0500
committerJohn W. Linville <linville@tuxdriver.com>2010-11-16 16:37:03 -0500
commit99020471001dbbd6edf61f105368cb6667cc683d (patch)
treeba76f0cff8090293901573ea7664536bdae8ecee /drivers
parent952a0e963fb02e50f4afbf502f7d468a8fe2b0fa (diff)
mwl8k: make initial firmware load asynchronous
Introduce a firmware loading state machine to manage the process of loading firmware asynchronously and completing initialization upon success. The state machine attempts to load the preferred firmware image. If that fails, and if an alternative firmware image is available, it will attempt to load that one. Signed-off-by: Brian Cavagnolo <brian@cozybit.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/net/wireless/mwl8k.c207
1 files changed, 178 insertions, 29 deletions
diff --git a/drivers/net/wireless/mwl8k.c b/drivers/net/wireless/mwl8k.c
index e5b062c3bd56..081bb6c848d9 100644
--- a/drivers/net/wireless/mwl8k.c
+++ b/drivers/net/wireless/mwl8k.c
@@ -224,6 +224,12 @@ struct mwl8k_priv {
224 * the firmware image is swapped. 224 * the firmware image is swapped.
225 */ 225 */
226 struct ieee80211_tx_queue_params wmm_params[MWL8K_TX_QUEUES]; 226 struct ieee80211_tx_queue_params wmm_params[MWL8K_TX_QUEUES];
227
228 /* async firmware loading state */
229 unsigned fw_state;
230 char *fw_pref;
231 char *fw_alt;
232 struct completion firmware_loading_complete;
227}; 233};
228 234
229/* Per interface specific private data */ 235/* Per interface specific private data */
@@ -403,34 +409,66 @@ static void mwl8k_release_firmware(struct mwl8k_priv *priv)
403 mwl8k_release_fw(&priv->fw_helper); 409 mwl8k_release_fw(&priv->fw_helper);
404} 410}
405 411
412/* states for asynchronous f/w loading */
413static void mwl8k_fw_state_machine(const struct firmware *fw, void *context);
414enum {
415 FW_STATE_INIT = 0,
416 FW_STATE_LOADING_PREF,
417 FW_STATE_LOADING_ALT,
418 FW_STATE_ERROR,
419};
420
406/* Request fw image */ 421/* Request fw image */
407static int mwl8k_request_fw(struct mwl8k_priv *priv, 422static int mwl8k_request_fw(struct mwl8k_priv *priv,
408 const char *fname, struct firmware **fw) 423 const char *fname, struct firmware **fw,
424 bool nowait)
409{ 425{
410 /* release current image */ 426 /* release current image */
411 if (*fw != NULL) 427 if (*fw != NULL)
412 mwl8k_release_fw(fw); 428 mwl8k_release_fw(fw);
413 429
414 return request_firmware((const struct firmware **)fw, 430 if (nowait)
415 fname, &priv->pdev->dev); 431 return request_firmware_nowait(THIS_MODULE, 1, fname,
432 &priv->pdev->dev, GFP_KERNEL,
433 priv, mwl8k_fw_state_machine);
434 else
435 return request_firmware((const struct firmware **)fw,
436 fname, &priv->pdev->dev);
416} 437}
417 438
418static int mwl8k_request_firmware(struct mwl8k_priv *priv, char *fw_image) 439static int mwl8k_request_firmware(struct mwl8k_priv *priv, char *fw_image,
440 bool nowait)
419{ 441{
420 struct mwl8k_device_info *di = priv->device_info; 442 struct mwl8k_device_info *di = priv->device_info;
421 int rc; 443 int rc;
422 444
423 if (di->helper_image != NULL) { 445 if (di->helper_image != NULL) {
424 rc = mwl8k_request_fw(priv, di->helper_image, &priv->fw_helper); 446 if (nowait)
425 if (rc) { 447 rc = mwl8k_request_fw(priv, di->helper_image,
426 printk(KERN_ERR "%s: Error requesting helper " 448 &priv->fw_helper, true);
427 "firmware file %s\n", pci_name(priv->pdev), 449 else
428 di->helper_image); 450 rc = mwl8k_request_fw(priv, di->helper_image,
451 &priv->fw_helper, false);
452 if (rc)
453 printk(KERN_ERR "%s: Error requesting helper fw %s\n",
454 pci_name(priv->pdev), di->helper_image);
455
456 if (rc || nowait)
429 return rc; 457 return rc;
430 }
431 } 458 }
432 459
433 rc = mwl8k_request_fw(priv, fw_image, &priv->fw_ucode); 460 if (nowait) {
461 /*
462 * if we get here, no helper image is needed. Skip the
463 * FW_STATE_INIT state.
464 */
465 priv->fw_state = FW_STATE_LOADING_PREF;
466 rc = mwl8k_request_fw(priv, fw_image,
467 &priv->fw_ucode,
468 true);
469 } else
470 rc = mwl8k_request_fw(priv, fw_image,
471 &priv->fw_ucode, false);
434 if (rc) { 472 if (rc) {
435 printk(KERN_ERR "%s: Error requesting firmware file %s\n", 473 printk(KERN_ERR "%s: Error requesting firmware file %s\n",
436 pci_name(priv->pdev), fw_image); 474 pci_name(priv->pdev), fw_image);
@@ -3998,7 +4036,99 @@ static DEFINE_PCI_DEVICE_TABLE(mwl8k_pci_id_table) = {
3998}; 4036};
3999MODULE_DEVICE_TABLE(pci, mwl8k_pci_id_table); 4037MODULE_DEVICE_TABLE(pci, mwl8k_pci_id_table);
4000 4038
4001static int mwl8k_init_firmware(struct ieee80211_hw *hw, char *fw_image) 4039static int mwl8k_request_alt_fw(struct mwl8k_priv *priv)
4040{
4041 int rc;
4042 printk(KERN_ERR "%s: Error requesting preferred fw %s.\n"
4043 "Trying alternative firmware %s\n", pci_name(priv->pdev),
4044 priv->fw_pref, priv->fw_alt);
4045 rc = mwl8k_request_fw(priv, priv->fw_alt, &priv->fw_ucode, true);
4046 if (rc) {
4047 printk(KERN_ERR "%s: Error requesting alt fw %s\n",
4048 pci_name(priv->pdev), priv->fw_alt);
4049 return rc;
4050 }
4051 return 0;
4052}
4053
4054static int mwl8k_firmware_load_success(struct mwl8k_priv *priv);
4055static void mwl8k_fw_state_machine(const struct firmware *fw, void *context)
4056{
4057 struct mwl8k_priv *priv = context;
4058 struct mwl8k_device_info *di = priv->device_info;
4059 int rc;
4060
4061 switch (priv->fw_state) {
4062 case FW_STATE_INIT:
4063 if (!fw) {
4064 printk(KERN_ERR "%s: Error requesting helper fw %s\n",
4065 pci_name(priv->pdev), di->helper_image);
4066 goto fail;
4067 }
4068 priv->fw_helper = fw;
4069 rc = mwl8k_request_fw(priv, priv->fw_pref, &priv->fw_ucode,
4070 true);
4071 if (rc && priv->fw_alt) {
4072 rc = mwl8k_request_alt_fw(priv);
4073 if (rc)
4074 goto fail;
4075 priv->fw_state = FW_STATE_LOADING_ALT;
4076 } else if (rc)
4077 goto fail;
4078 else
4079 priv->fw_state = FW_STATE_LOADING_PREF;
4080 break;
4081
4082 case FW_STATE_LOADING_PREF:
4083 if (!fw) {
4084 if (priv->fw_alt) {
4085 rc = mwl8k_request_alt_fw(priv);
4086 if (rc)
4087 goto fail;
4088 priv->fw_state = FW_STATE_LOADING_ALT;
4089 } else
4090 goto fail;
4091 } else {
4092 priv->fw_ucode = fw;
4093 rc = mwl8k_firmware_load_success(priv);
4094 if (rc)
4095 goto fail;
4096 else
4097 complete(&priv->firmware_loading_complete);
4098 }
4099 break;
4100
4101 case FW_STATE_LOADING_ALT:
4102 if (!fw) {
4103 printk(KERN_ERR "%s: Error requesting alt fw %s\n",
4104 pci_name(priv->pdev), di->helper_image);
4105 goto fail;
4106 }
4107 priv->fw_ucode = fw;
4108 rc = mwl8k_firmware_load_success(priv);
4109 if (rc)
4110 goto fail;
4111 else
4112 complete(&priv->firmware_loading_complete);
4113 break;
4114
4115 default:
4116 printk(KERN_ERR "%s: Unexpected firmware loading state: %d\n",
4117 MWL8K_NAME, priv->fw_state);
4118 BUG_ON(1);
4119 }
4120
4121 return;
4122
4123fail:
4124 priv->fw_state = FW_STATE_ERROR;
4125 complete(&priv->firmware_loading_complete);
4126 device_release_driver(&priv->pdev->dev);
4127 mwl8k_release_firmware(priv);
4128}
4129
4130static int mwl8k_init_firmware(struct ieee80211_hw *hw, char *fw_image,
4131 bool nowait)
4002{ 4132{
4003 struct mwl8k_priv *priv = hw->priv; 4133 struct mwl8k_priv *priv = hw->priv;
4004 int rc; 4134 int rc;
@@ -4007,12 +4137,15 @@ static int mwl8k_init_firmware(struct ieee80211_hw *hw, char *fw_image)
4007 mwl8k_hw_reset(priv); 4137 mwl8k_hw_reset(priv);
4008 4138
4009 /* Ask userland hotplug daemon for the device firmware */ 4139 /* Ask userland hotplug daemon for the device firmware */
4010 rc = mwl8k_request_firmware(priv, fw_image); 4140 rc = mwl8k_request_firmware(priv, fw_image, nowait);
4011 if (rc) { 4141 if (rc) {
4012 wiphy_err(hw->wiphy, "Firmware files not found\n"); 4142 wiphy_err(hw->wiphy, "Firmware files not found\n");
4013 return rc; 4143 return rc;
4014 } 4144 }
4015 4145
4146 if (nowait)
4147 return rc;
4148
4016 /* Load firmware into hardware */ 4149 /* Load firmware into hardware */
4017 rc = mwl8k_load_firmware(hw); 4150 rc = mwl8k_load_firmware(hw);
4018 if (rc) 4151 if (rc)
@@ -4147,7 +4280,7 @@ static int mwl8k_reload_firmware(struct ieee80211_hw *hw, char *fw_image)
4147 for (i = 0; i < MWL8K_TX_QUEUES; i++) 4280 for (i = 0; i < MWL8K_TX_QUEUES; i++)
4148 mwl8k_txq_deinit(hw, i); 4281 mwl8k_txq_deinit(hw, i);
4149 4282
4150 rc = mwl8k_init_firmware(hw, fw_image); 4283 rc = mwl8k_init_firmware(hw, fw_image, false);
4151 if (rc) 4284 if (rc)
4152 goto fail; 4285 goto fail;
4153 4286
@@ -4181,6 +4314,13 @@ static int mwl8k_firmware_load_success(struct mwl8k_priv *priv)
4181 struct ieee80211_hw *hw = priv->hw; 4314 struct ieee80211_hw *hw = priv->hw;
4182 int i, rc; 4315 int i, rc;
4183 4316
4317 rc = mwl8k_load_firmware(hw);
4318 mwl8k_release_firmware(priv);
4319 if (rc) {
4320 wiphy_err(hw->wiphy, "Cannot start firmware\n");
4321 return rc;
4322 }
4323
4184 /* 4324 /*
4185 * Extra headroom is the size of the required DMA header 4325 * Extra headroom is the size of the required DMA header
4186 * minus the size of the smallest 802.11 frame (CTS frame). 4326 * minus the size of the smallest 802.11 frame (CTS frame).
@@ -4325,28 +4465,29 @@ static int __devinit mwl8k_probe(struct pci_dev *pdev,
4325 } 4465 }
4326 4466
4327 /* 4467 /*
4328 * Choose the initial fw image depending on user input and availability 4468 * Choose the initial fw image depending on user input. If a second
4329 * of images. 4469 * image is available, make it the alternative image that will be
4470 * loaded if the first one fails.
4330 */ 4471 */
4472 init_completion(&priv->firmware_loading_complete);
4331 di = priv->device_info; 4473 di = priv->device_info;
4332 if (ap_mode_default && di->fw_image_ap) 4474 if (ap_mode_default && di->fw_image_ap) {
4333 rc = mwl8k_init_firmware(hw, di->fw_image_ap); 4475 priv->fw_pref = di->fw_image_ap;
4334 else if (!ap_mode_default && di->fw_image_sta) 4476 priv->fw_alt = di->fw_image_sta;
4335 rc = mwl8k_init_firmware(hw, di->fw_image_sta); 4477 } else if (!ap_mode_default && di->fw_image_sta) {
4336 else if (ap_mode_default && !di->fw_image_ap && di->fw_image_sta) { 4478 priv->fw_pref = di->fw_image_sta;
4479 priv->fw_alt = di->fw_image_ap;
4480 } else if (ap_mode_default && !di->fw_image_ap && di->fw_image_sta) {
4337 printk(KERN_WARNING "AP fw is unavailable. Using STA fw."); 4481 printk(KERN_WARNING "AP fw is unavailable. Using STA fw.");
4338 rc = mwl8k_init_firmware(hw, di->fw_image_sta); 4482 priv->fw_pref = di->fw_image_sta;
4339 } else if (!ap_mode_default && !di->fw_image_sta && di->fw_image_ap) { 4483 } else if (!ap_mode_default && !di->fw_image_sta && di->fw_image_ap) {
4340 printk(KERN_WARNING "STA fw is unavailable. Using AP fw."); 4484 printk(KERN_WARNING "STA fw is unavailable. Using AP fw.");
4341 rc = mwl8k_init_firmware(hw, di->fw_image_ap); 4485 priv->fw_pref = di->fw_image_ap;
4342 } else 4486 }
4343 rc = mwl8k_init_firmware(hw, di->fw_image_sta); 4487 rc = mwl8k_init_firmware(hw, priv->fw_pref, true);
4344 if (rc) 4488 if (rc)
4345 goto err_stop_firmware; 4489 goto err_stop_firmware;
4346 4490 return rc;
4347 rc = mwl8k_firmware_load_success(priv);
4348 if (!rc)
4349 return rc;
4350 4491
4351err_stop_firmware: 4492err_stop_firmware:
4352 mwl8k_hw_reset(priv); 4493 mwl8k_hw_reset(priv);
@@ -4385,6 +4526,13 @@ static void __devexit mwl8k_remove(struct pci_dev *pdev)
4385 return; 4526 return;
4386 priv = hw->priv; 4527 priv = hw->priv;
4387 4528
4529 wait_for_completion(&priv->firmware_loading_complete);
4530
4531 if (priv->fw_state == FW_STATE_ERROR) {
4532 mwl8k_hw_reset(priv);
4533 goto unmap;
4534 }
4535
4388 ieee80211_stop_queues(hw); 4536 ieee80211_stop_queues(hw);
4389 4537
4390 ieee80211_unregister_hw(hw); 4538 ieee80211_unregister_hw(hw);
@@ -4407,6 +4555,7 @@ static void __devexit mwl8k_remove(struct pci_dev *pdev)
4407 4555
4408 pci_free_consistent(priv->pdev, 4, priv->cookie, priv->cookie_dma); 4556 pci_free_consistent(priv->pdev, 4, priv->cookie, priv->cookie_dma);
4409 4557
4558unmap:
4410 pci_iounmap(pdev, priv->regs); 4559 pci_iounmap(pdev, priv->regs);
4411 pci_iounmap(pdev, priv->sram); 4560 pci_iounmap(pdev, priv->sram);
4412 pci_set_drvdata(pdev, NULL); 4561 pci_set_drvdata(pdev, NULL);