diff options
Diffstat (limited to 'drivers/net')
-rw-r--r-- | drivers/net/wireless/mwl8k.c | 207 |
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 */ | ||
413 | static void mwl8k_fw_state_machine(const struct firmware *fw, void *context); | ||
414 | enum { | ||
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 */ |
407 | static int mwl8k_request_fw(struct mwl8k_priv *priv, | 422 | static 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 | ||
418 | static int mwl8k_request_firmware(struct mwl8k_priv *priv, char *fw_image) | 439 | static 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 | }; |
3999 | MODULE_DEVICE_TABLE(pci, mwl8k_pci_id_table); | 4037 | MODULE_DEVICE_TABLE(pci, mwl8k_pci_id_table); |
4000 | 4038 | ||
4001 | static int mwl8k_init_firmware(struct ieee80211_hw *hw, char *fw_image) | 4039 | static 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 | |||
4054 | static int mwl8k_firmware_load_success(struct mwl8k_priv *priv); | ||
4055 | static 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 | |||
4123 | fail: | ||
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 | |||
4130 | static 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 | ||
4351 | err_stop_firmware: | 4492 | err_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 | ||
4558 | unmap: | ||
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); |