|
@@ -3942,73 +3942,10 @@ static DEFINE_PCI_DEVICE_TABLE(mwl8k_pci_id_table) = {
|
|
|
};
|
|
|
MODULE_DEVICE_TABLE(pci, mwl8k_pci_id_table);
|
|
|
|
|
|
-static int __devinit mwl8k_probe(struct pci_dev *pdev,
|
|
|
- const struct pci_device_id *id)
|
|
|
+static int mwl8k_init_firmware(struct ieee80211_hw *hw)
|
|
|
{
|
|
|
- static int printed_version = 0;
|
|
|
- struct ieee80211_hw *hw;
|
|
|
- struct mwl8k_priv *priv;
|
|
|
+ struct mwl8k_priv *priv = hw->priv;
|
|
|
int rc;
|
|
|
- int i;
|
|
|
-
|
|
|
- if (!printed_version) {
|
|
|
- printk(KERN_INFO "%s version %s\n", MWL8K_DESC, MWL8K_VERSION);
|
|
|
- printed_version = 1;
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- rc = pci_enable_device(pdev);
|
|
|
- if (rc) {
|
|
|
- printk(KERN_ERR "%s: Cannot enable new PCI device\n",
|
|
|
- MWL8K_NAME);
|
|
|
- return rc;
|
|
|
- }
|
|
|
-
|
|
|
- rc = pci_request_regions(pdev, MWL8K_NAME);
|
|
|
- if (rc) {
|
|
|
- printk(KERN_ERR "%s: Cannot obtain PCI resources\n",
|
|
|
- MWL8K_NAME);
|
|
|
- goto err_disable_device;
|
|
|
- }
|
|
|
-
|
|
|
- pci_set_master(pdev);
|
|
|
-
|
|
|
-
|
|
|
- hw = ieee80211_alloc_hw(sizeof(*priv), &mwl8k_ops);
|
|
|
- if (hw == NULL) {
|
|
|
- printk(KERN_ERR "%s: ieee80211 alloc failed\n", MWL8K_NAME);
|
|
|
- rc = -ENOMEM;
|
|
|
- goto err_free_reg;
|
|
|
- }
|
|
|
-
|
|
|
- SET_IEEE80211_DEV(hw, &pdev->dev);
|
|
|
- pci_set_drvdata(pdev, hw);
|
|
|
-
|
|
|
- priv = hw->priv;
|
|
|
- priv->hw = hw;
|
|
|
- priv->pdev = pdev;
|
|
|
- priv->device_info = &mwl8k_info_tbl[id->driver_data];
|
|
|
-
|
|
|
-
|
|
|
- priv->sram = pci_iomap(pdev, 0, 0x10000);
|
|
|
- if (priv->sram == NULL) {
|
|
|
- wiphy_err(hw->wiphy, "Cannot map device SRAM\n");
|
|
|
- goto err_iounmap;
|
|
|
- }
|
|
|
-
|
|
|
- /*
|
|
|
- * If BAR0 is a 32 bit BAR, the register BAR will be BAR1.
|
|
|
- * If BAR0 is a 64 bit BAR, the register BAR will be BAR2.
|
|
|
- */
|
|
|
- priv->regs = pci_iomap(pdev, 1, 0x10000);
|
|
|
- if (priv->regs == NULL) {
|
|
|
- priv->regs = pci_iomap(pdev, 2, 0x10000);
|
|
|
- if (priv->regs == NULL) {
|
|
|
- wiphy_err(hw->wiphy, "Cannot map device registers\n");
|
|
|
- goto err_iounmap;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
|
|
|
/* Reset firmware and hardware */
|
|
|
mwl8k_hw_reset(priv);
|
|
@@ -4017,19 +3954,26 @@ static int __devinit mwl8k_probe(struct pci_dev *pdev,
|
|
|
rc = mwl8k_request_firmware(priv);
|
|
|
if (rc) {
|
|
|
wiphy_err(hw->wiphy, "Firmware files not found\n");
|
|
|
- goto err_stop_firmware;
|
|
|
+ return rc;
|
|
|
}
|
|
|
|
|
|
/* Load firmware into hardware */
|
|
|
rc = mwl8k_load_firmware(hw);
|
|
|
- if (rc) {
|
|
|
+ if (rc)
|
|
|
wiphy_err(hw->wiphy, "Cannot start firmware\n");
|
|
|
- goto err_stop_firmware;
|
|
|
- }
|
|
|
|
|
|
/* Reclaim memory once firmware is successfully loaded */
|
|
|
mwl8k_release_firmware(priv);
|
|
|
|
|
|
+ return rc;
|
|
|
+}
|
|
|
+
|
|
|
+/* initialize hw after successfully loading a firmware image */
|
|
|
+static int mwl8k_probe_hw(struct ieee80211_hw *hw)
|
|
|
+{
|
|
|
+ struct mwl8k_priv *priv = hw->priv;
|
|
|
+ int rc = 0;
|
|
|
+ int i;
|
|
|
|
|
|
if (priv->ap_fw) {
|
|
|
priv->rxd_ops = priv->device_info->ap_rxd_ops;
|
|
@@ -4046,58 +3990,11 @@ static int __devinit mwl8k_probe(struct pci_dev *pdev,
|
|
|
priv->wmm_enabled = false;
|
|
|
priv->pending_tx_pkts = 0;
|
|
|
|
|
|
-
|
|
|
- /*
|
|
|
- * Extra headroom is the size of the required DMA header
|
|
|
- * minus the size of the smallest 802.11 frame (CTS frame).
|
|
|
- */
|
|
|
- hw->extra_tx_headroom =
|
|
|
- sizeof(struct mwl8k_dma_data) - sizeof(struct ieee80211_cts);
|
|
|
-
|
|
|
- hw->channel_change_time = 10;
|
|
|
-
|
|
|
- hw->queues = MWL8K_TX_QUEUES;
|
|
|
-
|
|
|
- /* Set rssi values to dBm */
|
|
|
- hw->flags |= IEEE80211_HW_SIGNAL_DBM;
|
|
|
- hw->vif_data_size = sizeof(struct mwl8k_vif);
|
|
|
- hw->sta_data_size = sizeof(struct mwl8k_sta);
|
|
|
-
|
|
|
- priv->macids_used = 0;
|
|
|
- INIT_LIST_HEAD(&priv->vif_list);
|
|
|
-
|
|
|
- /* Set default radio state and preamble */
|
|
|
- priv->radio_on = 0;
|
|
|
- priv->radio_short_preamble = 0;
|
|
|
-
|
|
|
- /* Finalize join worker */
|
|
|
- INIT_WORK(&priv->finalize_join_worker, mwl8k_finalize_join_worker);
|
|
|
-
|
|
|
- /* TX reclaim and RX tasklets. */
|
|
|
- tasklet_init(&priv->poll_tx_task, mwl8k_tx_poll, (unsigned long)hw);
|
|
|
- tasklet_disable(&priv->poll_tx_task);
|
|
|
- tasklet_init(&priv->poll_rx_task, mwl8k_rx_poll, (unsigned long)hw);
|
|
|
- tasklet_disable(&priv->poll_rx_task);
|
|
|
-
|
|
|
- /* Power management cookie */
|
|
|
- priv->cookie = pci_alloc_consistent(priv->pdev, 4, &priv->cookie_dma);
|
|
|
- if (priv->cookie == NULL)
|
|
|
- goto err_stop_firmware;
|
|
|
-
|
|
|
rc = mwl8k_rxq_init(hw, 0);
|
|
|
if (rc)
|
|
|
- goto err_free_cookie;
|
|
|
+ goto err_stop_firmware;
|
|
|
rxq_refill(hw, 0, INT_MAX);
|
|
|
|
|
|
- mutex_init(&priv->fw_mutex);
|
|
|
- priv->fw_mutex_owner = NULL;
|
|
|
- priv->fw_mutex_depth = 0;
|
|
|
- priv->hostcmd_wait = NULL;
|
|
|
-
|
|
|
- spin_lock_init(&priv->tx_lock);
|
|
|
-
|
|
|
- priv->tx_wait = NULL;
|
|
|
-
|
|
|
for (i = 0; i < MWL8K_TX_QUEUES; i++) {
|
|
|
rc = mwl8k_txq_init(hw, i);
|
|
|
if (rc)
|
|
@@ -4137,13 +4034,6 @@ static int __devinit mwl8k_probe(struct pci_dev *pdev,
|
|
|
goto err_free_irq;
|
|
|
}
|
|
|
|
|
|
- hw->wiphy->interface_modes = 0;
|
|
|
- if (priv->ap_macids_supported)
|
|
|
- hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_AP);
|
|
|
- if (priv->sta_macids_supported)
|
|
|
- hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_STATION);
|
|
|
-
|
|
|
-
|
|
|
/* Turn radio off */
|
|
|
rc = mwl8k_cmd_radio_disable(hw);
|
|
|
if (rc) {
|
|
@@ -4162,12 +4052,6 @@ static int __devinit mwl8k_probe(struct pci_dev *pdev,
|
|
|
iowrite32(0, priv->regs + MWL8K_HIU_A2H_INTERRUPT_MASK);
|
|
|
free_irq(priv->pdev->irq, hw);
|
|
|
|
|
|
- rc = ieee80211_register_hw(hw);
|
|
|
- if (rc) {
|
|
|
- wiphy_err(hw->wiphy, "Cannot register device\n");
|
|
|
- goto err_free_queues;
|
|
|
- }
|
|
|
-
|
|
|
wiphy_info(hw->wiphy, "%s v%d, %pm, %s firmware %u.%u.%u.%u\n",
|
|
|
priv->device_info->part_name,
|
|
|
priv->hw_rev, hw->wiphy->perm_addr,
|
|
@@ -4186,14 +4070,213 @@ err_free_queues:
|
|
|
mwl8k_txq_deinit(hw, i);
|
|
|
mwl8k_rxq_deinit(hw, 0);
|
|
|
|
|
|
+err_stop_firmware:
|
|
|
+ mwl8k_hw_reset(priv);
|
|
|
+
|
|
|
+ return rc;
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * invoke mwl8k_reload_firmware to change the firmware image after the device
|
|
|
+ * has already been registered
|
|
|
+ */
|
|
|
+static int mwl8k_reload_firmware(struct ieee80211_hw *hw, char *fw_image)
|
|
|
+{
|
|
|
+ int i, rc = 0;
|
|
|
+ struct mwl8k_priv *priv = hw->priv;
|
|
|
+
|
|
|
+ mwl8k_stop(hw);
|
|
|
+ mwl8k_rxq_deinit(hw, 0);
|
|
|
+
|
|
|
+ for (i = 0; i < MWL8K_TX_QUEUES; i++)
|
|
|
+ mwl8k_txq_deinit(hw, i);
|
|
|
+
|
|
|
+ rc = mwl8k_init_firmware(hw, fw_image);
|
|
|
+ if (rc)
|
|
|
+ goto fail;
|
|
|
+
|
|
|
+ rc = mwl8k_probe_hw(hw);
|
|
|
+ if (rc)
|
|
|
+ goto fail;
|
|
|
+
|
|
|
+ rc = mwl8k_start(hw);
|
|
|
+ if (rc)
|
|
|
+ goto fail;
|
|
|
+
|
|
|
+ rc = mwl8k_config(hw, ~0);
|
|
|
+ if (rc)
|
|
|
+ goto fail;
|
|
|
+
|
|
|
+ for (i = 0; i < MWL8K_TX_QUEUES; i++) {
|
|
|
+ rc = mwl8k_conf_tx(hw, i, &priv->wmm_params[i]);
|
|
|
+ if (rc)
|
|
|
+ goto fail;
|
|
|
+ }
|
|
|
+
|
|
|
+ return rc;
|
|
|
+
|
|
|
+fail:
|
|
|
+ printk(KERN_WARNING "mwl8k: Failed to reload firmware image.\n");
|
|
|
+ return rc;
|
|
|
+}
|
|
|
+
|
|
|
+static int mwl8k_firmware_load_success(struct mwl8k_priv *priv)
|
|
|
+{
|
|
|
+ struct ieee80211_hw *hw = priv->hw;
|
|
|
+ int i, rc;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Extra headroom is the size of the required DMA header
|
|
|
+ * minus the size of the smallest 802.11 frame (CTS frame).
|
|
|
+ */
|
|
|
+ hw->extra_tx_headroom =
|
|
|
+ sizeof(struct mwl8k_dma_data) - sizeof(struct ieee80211_cts);
|
|
|
+
|
|
|
+ hw->channel_change_time = 10;
|
|
|
+
|
|
|
+ hw->queues = MWL8K_TX_QUEUES;
|
|
|
+
|
|
|
+ /* Set rssi values to dBm */
|
|
|
+ hw->flags |= IEEE80211_HW_SIGNAL_DBM;
|
|
|
+ hw->vif_data_size = sizeof(struct mwl8k_vif);
|
|
|
+ hw->sta_data_size = sizeof(struct mwl8k_sta);
|
|
|
+
|
|
|
+ priv->macids_used = 0;
|
|
|
+ INIT_LIST_HEAD(&priv->vif_list);
|
|
|
+
|
|
|
+ /* Set default radio state and preamble */
|
|
|
+ priv->radio_on = 0;
|
|
|
+ priv->radio_short_preamble = 0;
|
|
|
+
|
|
|
+ /* Finalize join worker */
|
|
|
+ INIT_WORK(&priv->finalize_join_worker, mwl8k_finalize_join_worker);
|
|
|
+
|
|
|
+ /* TX reclaim and RX tasklets. */
|
|
|
+ tasklet_init(&priv->poll_tx_task, mwl8k_tx_poll, (unsigned long)hw);
|
|
|
+ tasklet_disable(&priv->poll_tx_task);
|
|
|
+ tasklet_init(&priv->poll_rx_task, mwl8k_rx_poll, (unsigned long)hw);
|
|
|
+ tasklet_disable(&priv->poll_rx_task);
|
|
|
+
|
|
|
+ /* Power management cookie */
|
|
|
+ priv->cookie = pci_alloc_consistent(priv->pdev, 4, &priv->cookie_dma);
|
|
|
+ if (priv->cookie == NULL)
|
|
|
+ return -ENOMEM;
|
|
|
+
|
|
|
+ mutex_init(&priv->fw_mutex);
|
|
|
+ priv->fw_mutex_owner = NULL;
|
|
|
+ priv->fw_mutex_depth = 0;
|
|
|
+ priv->hostcmd_wait = NULL;
|
|
|
+
|
|
|
+ spin_lock_init(&priv->tx_lock);
|
|
|
+
|
|
|
+ priv->tx_wait = NULL;
|
|
|
+
|
|
|
+ rc = mwl8k_probe_hw(hw);
|
|
|
+ if (rc)
|
|
|
+ goto err_free_cookie;
|
|
|
+
|
|
|
+ hw->wiphy->interface_modes = 0;
|
|
|
+ if (priv->ap_macids_supported || priv->device_info->fw_image_ap)
|
|
|
+ hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_AP);
|
|
|
+ if (priv->sta_macids_supported || priv->device_info->fw_image_sta)
|
|
|
+ hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_STATION);
|
|
|
+
|
|
|
+ rc = ieee80211_register_hw(hw);
|
|
|
+ if (rc) {
|
|
|
+ wiphy_err(hw->wiphy, "Cannot register device\n");
|
|
|
+ goto err_unprobe_hw;
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+
|
|
|
+err_unprobe_hw:
|
|
|
+ for (i = 0; i < MWL8K_TX_QUEUES; i++)
|
|
|
+ mwl8k_txq_deinit(hw, i);
|
|
|
+ mwl8k_rxq_deinit(hw, 0);
|
|
|
+
|
|
|
err_free_cookie:
|
|
|
if (priv->cookie != NULL)
|
|
|
pci_free_consistent(priv->pdev, 4,
|
|
|
priv->cookie, priv->cookie_dma);
|
|
|
|
|
|
+ return rc;
|
|
|
+}
|
|
|
+static int __devinit mwl8k_probe(struct pci_dev *pdev,
|
|
|
+ const struct pci_device_id *id)
|
|
|
+{
|
|
|
+ static int printed_version;
|
|
|
+ struct ieee80211_hw *hw;
|
|
|
+ struct mwl8k_priv *priv;
|
|
|
+ int rc;
|
|
|
+
|
|
|
+ if (!printed_version) {
|
|
|
+ printk(KERN_INFO "%s version %s\n", MWL8K_DESC, MWL8K_VERSION);
|
|
|
+ printed_version = 1;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ rc = pci_enable_device(pdev);
|
|
|
+ if (rc) {
|
|
|
+ printk(KERN_ERR "%s: Cannot enable new PCI device\n",
|
|
|
+ MWL8K_NAME);
|
|
|
+ return rc;
|
|
|
+ }
|
|
|
+
|
|
|
+ rc = pci_request_regions(pdev, MWL8K_NAME);
|
|
|
+ if (rc) {
|
|
|
+ printk(KERN_ERR "%s: Cannot obtain PCI resources\n",
|
|
|
+ MWL8K_NAME);
|
|
|
+ goto err_disable_device;
|
|
|
+ }
|
|
|
+
|
|
|
+ pci_set_master(pdev);
|
|
|
+
|
|
|
+
|
|
|
+ hw = ieee80211_alloc_hw(sizeof(*priv), &mwl8k_ops);
|
|
|
+ if (hw == NULL) {
|
|
|
+ printk(KERN_ERR "%s: ieee80211 alloc failed\n", MWL8K_NAME);
|
|
|
+ rc = -ENOMEM;
|
|
|
+ goto err_free_reg;
|
|
|
+ }
|
|
|
+
|
|
|
+ SET_IEEE80211_DEV(hw, &pdev->dev);
|
|
|
+ pci_set_drvdata(pdev, hw);
|
|
|
+
|
|
|
+ priv = hw->priv;
|
|
|
+ priv->hw = hw;
|
|
|
+ priv->pdev = pdev;
|
|
|
+ priv->device_info = &mwl8k_info_tbl[id->driver_data];
|
|
|
+
|
|
|
+
|
|
|
+ priv->sram = pci_iomap(pdev, 0, 0x10000);
|
|
|
+ if (priv->sram == NULL) {
|
|
|
+ wiphy_err(hw->wiphy, "Cannot map device SRAM\n");
|
|
|
+ goto err_iounmap;
|
|
|
+ }
|
|
|
+
|
|
|
+ /*
|
|
|
+ * If BAR0 is a 32 bit BAR, the register BAR will be BAR1.
|
|
|
+ * If BAR0 is a 64 bit BAR, the register BAR will be BAR2.
|
|
|
+ */
|
|
|
+ priv->regs = pci_iomap(pdev, 1, 0x10000);
|
|
|
+ if (priv->regs == NULL) {
|
|
|
+ priv->regs = pci_iomap(pdev, 2, 0x10000);
|
|
|
+ if (priv->regs == NULL) {
|
|
|
+ wiphy_err(hw->wiphy, "Cannot map device registers\n");
|
|
|
+ goto err_iounmap;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ rc = mwl8k_init_firmware(hw);
|
|
|
+ if (rc)
|
|
|
+ goto err_stop_firmware;
|
|
|
+
|
|
|
+ rc = mwl8k_firmware_load_success(priv);
|
|
|
+ if (!rc)
|
|
|
+ return rc;
|
|
|
+
|
|
|
err_stop_firmware:
|
|
|
mwl8k_hw_reset(priv);
|
|
|
- mwl8k_release_firmware(priv);
|
|
|
|
|
|
err_iounmap:
|
|
|
if (priv->regs != NULL)
|