summaryrefslogtreecommitdiffstats
path: root/drivers/net/wireless/iwmc3200wifi
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/wireless/iwmc3200wifi')
-rw-r--r--drivers/net/wireless/iwmc3200wifi/iwm.h4
-rw-r--r--drivers/net/wireless/iwmc3200wifi/main.c64
-rw-r--r--drivers/net/wireless/iwmc3200wifi/netdev.c49
-rw-r--r--drivers/net/wireless/iwmc3200wifi/sdio.c11
4 files changed, 103 insertions, 25 deletions
diff --git a/drivers/net/wireless/iwmc3200wifi/iwm.h b/drivers/net/wireless/iwmc3200wifi/iwm.h
index 635c16ee618..77c339f8516 100644
--- a/drivers/net/wireless/iwmc3200wifi/iwm.h
+++ b/drivers/net/wireless/iwmc3200wifi/iwm.h
@@ -288,6 +288,7 @@ struct iwm_priv {
u8 *eeprom;
struct timer_list watchdog;
struct work_struct reset_worker;
+ struct mutex mutex;
struct rfkill *rfkill;
char private[0] __attribute__((__aligned__(NETDEV_ALIGN)));
@@ -315,8 +316,11 @@ extern const struct iw_handler_def iwm_iw_handler_def;
void *iwm_if_alloc(int sizeof_bus, struct device *dev,
struct iwm_if_ops *if_ops);
void iwm_if_free(struct iwm_priv *iwm);
+int iwm_if_add(struct iwm_priv *iwm);
+void iwm_if_remove(struct iwm_priv *iwm);
int iwm_mode_to_nl80211_iftype(int mode);
int iwm_priv_init(struct iwm_priv *iwm);
+void iwm_priv_deinit(struct iwm_priv *iwm);
void iwm_reset(struct iwm_priv *iwm);
void iwm_tx_credit_init_pools(struct iwm_priv *iwm,
struct iwm_umac_notif_alive *alive);
diff --git a/drivers/net/wireless/iwmc3200wifi/main.c b/drivers/net/wireless/iwmc3200wifi/main.c
index 6a2640f16b6..8be206d5822 100644
--- a/drivers/net/wireless/iwmc3200wifi/main.c
+++ b/drivers/net/wireless/iwmc3200wifi/main.c
@@ -112,6 +112,9 @@ static void iwm_statistics_request(struct work_struct *work)
iwm_send_umac_stats_req(iwm, 0);
}
+int __iwm_up(struct iwm_priv *iwm);
+int __iwm_down(struct iwm_priv *iwm);
+
static void iwm_reset_worker(struct work_struct *work)
{
struct iwm_priv *iwm;
@@ -120,6 +123,19 @@ static void iwm_reset_worker(struct work_struct *work)
iwm = container_of(work, struct iwm_priv, reset_worker);
+ /*
+ * XXX: The iwm->mutex is introduced purely for this reset work,
+ * because the other users for iwm_up and iwm_down are only netdev
+ * ndo_open and ndo_stop which are already protected by rtnl.
+ * Please remove iwm->mutex together if iwm_reset_worker() is not
+ * required in the future.
+ */
+ if (!mutex_trylock(&iwm->mutex)) {
+ IWM_WARN(iwm, "We are in the middle of interface bringing "
+ "UP/DOWN. Skip driver resetting.\n");
+ return;
+ }
+
if (iwm->umac_profile_active) {
profile = kmalloc(sizeof(struct iwm_umac_profile), GFP_KERNEL);
if (profile)
@@ -128,10 +144,10 @@ static void iwm_reset_worker(struct work_struct *work)
IWM_ERR(iwm, "Couldn't alloc memory for profile\n");
}
- iwm_down(iwm);
+ __iwm_down(iwm);
while (retry++ < 3) {
- ret = iwm_up(iwm);
+ ret = __iwm_up(iwm);
if (!ret)
break;
@@ -142,7 +158,7 @@ static void iwm_reset_worker(struct work_struct *work)
IWM_WARN(iwm, "iwm_up() failed: %d\n", ret);
kfree(profile);
- return;
+ goto out;
}
if (profile) {
@@ -151,6 +167,9 @@ static void iwm_reset_worker(struct work_struct *work)
iwm_send_mlme_profile(iwm);
kfree(profile);
}
+
+ out:
+ mutex_unlock(&iwm->mutex);
}
static void iwm_watchdog(unsigned long data)
@@ -215,10 +234,21 @@ int iwm_priv_init(struct iwm_priv *iwm)
init_timer(&iwm->watchdog);
iwm->watchdog.function = iwm_watchdog;
iwm->watchdog.data = (unsigned long)iwm;
+ mutex_init(&iwm->mutex);
return 0;
}
+void iwm_priv_deinit(struct iwm_priv *iwm)
+{
+ int i;
+
+ for (i = 0; i < IWM_TX_QUEUES; i++)
+ destroy_workqueue(iwm->txq[i].wq);
+
+ destroy_workqueue(iwm->rx_wq);
+}
+
/*
* We reset all the structures, and we reset the UMAC.
* After calling this routine, you're expected to reload
@@ -466,7 +496,7 @@ void iwm_link_off(struct iwm_priv *iwm)
iwm_rx_free(iwm);
- cancel_delayed_work(&iwm->stats_request);
+ cancel_delayed_work_sync(&iwm->stats_request);
memset(wstats, 0, sizeof(struct iw_statistics));
wstats->qual.updated = IW_QUAL_ALL_INVALID;
@@ -511,7 +541,7 @@ static int iwm_channels_init(struct iwm_priv *iwm)
return 0;
}
-int iwm_up(struct iwm_priv *iwm)
+int __iwm_up(struct iwm_priv *iwm)
{
int ret;
struct iwm_notif *notif_reboot, *notif_ack = NULL;
@@ -647,7 +677,18 @@ int iwm_up(struct iwm_priv *iwm)
return -EIO;
}
-int iwm_down(struct iwm_priv *iwm)
+int iwm_up(struct iwm_priv *iwm)
+{
+ int ret;
+
+ mutex_lock(&iwm->mutex);
+ ret = __iwm_up(iwm);
+ mutex_unlock(&iwm->mutex);
+
+ return ret;
+}
+
+int __iwm_down(struct iwm_priv *iwm)
{
int ret;
@@ -678,3 +719,14 @@ int iwm_down(struct iwm_priv *iwm)
return 0;
}
+
+int iwm_down(struct iwm_priv *iwm)
+{
+ int ret;
+
+ mutex_lock(&iwm->mutex);
+ ret = __iwm_down(iwm);
+ mutex_unlock(&iwm->mutex);
+
+ return ret;
+}
diff --git a/drivers/net/wireless/iwmc3200wifi/netdev.c b/drivers/net/wireless/iwmc3200wifi/netdev.c
index 68e2c3b6c7a..aaa20c6885c 100644
--- a/drivers/net/wireless/iwmc3200wifi/netdev.c
+++ b/drivers/net/wireless/iwmc3200wifi/netdev.c
@@ -114,32 +114,31 @@ void *iwm_if_alloc(int sizeof_bus, struct device *dev,
iwm = wdev_to_iwm(wdev);
iwm->bus_ops = if_ops;
iwm->wdev = wdev;
- iwm_priv_init(iwm);
+
+ ret = iwm_priv_init(iwm);
+ if (ret) {
+ dev_err(dev, "failed to init iwm_priv\n");
+ goto out_wdev;
+ }
+
wdev->iftype = iwm_mode_to_nl80211_iftype(iwm->conf.mode);
- ndev = alloc_netdev_mq(0, "wlan%d", ether_setup,
- IWM_TX_QUEUES);
+ ndev = alloc_netdev_mq(0, "wlan%d", ether_setup, IWM_TX_QUEUES);
if (!ndev) {
dev_err(dev, "no memory for network device instance\n");
- goto out_wdev;
+ goto out_priv;
}
ndev->netdev_ops = &iwm_netdev_ops;
ndev->wireless_handlers = &iwm_iw_handler_def;
ndev->ieee80211_ptr = wdev;
SET_NETDEV_DEV(ndev, wiphy_dev(wdev->wiphy));
- ret = register_netdev(ndev);
- if (ret < 0) {
- dev_err(dev, "Failed to register netdev: %d\n", ret);
- goto out_ndev;
- }
-
wdev->netdev = ndev;
return iwm;
- out_ndev:
- free_netdev(ndev);
+ out_priv:
+ iwm_priv_deinit(iwm);
out_wdev:
iwm_wdev_free(iwm);
@@ -148,15 +147,29 @@ void *iwm_if_alloc(int sizeof_bus, struct device *dev,
void iwm_if_free(struct iwm_priv *iwm)
{
- int i;
-
if (!iwm_to_ndev(iwm))
return;
- unregister_netdev(iwm_to_ndev(iwm));
free_netdev(iwm_to_ndev(iwm));
iwm_wdev_free(iwm);
- destroy_workqueue(iwm->rx_wq);
- for (i = 0; i < IWM_TX_QUEUES; i++)
- destroy_workqueue(iwm->txq[i].wq);
+ iwm_priv_deinit(iwm);
+}
+
+int iwm_if_add(struct iwm_priv *iwm)
+{
+ struct net_device *ndev = iwm_to_ndev(iwm);
+ int ret;
+
+ ret = register_netdev(ndev);
+ if (ret < 0) {
+ dev_err(&ndev->dev, "Failed to register netdev: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+void iwm_if_remove(struct iwm_priv *iwm)
+{
+ unregister_netdev(iwm_to_ndev(iwm));
}
diff --git a/drivers/net/wireless/iwmc3200wifi/sdio.c b/drivers/net/wireless/iwmc3200wifi/sdio.c
index b54da677b37..916681837fd 100644
--- a/drivers/net/wireless/iwmc3200wifi/sdio.c
+++ b/drivers/net/wireless/iwmc3200wifi/sdio.c
@@ -454,10 +454,18 @@ static int iwm_sdio_probe(struct sdio_func *func,
INIT_WORK(&hw->isr_worker, iwm_sdio_isr_worker);
+ ret = iwm_if_add(iwm);
+ if (ret) {
+ dev_err(dev, "add SDIO interface failed\n");
+ goto destroy_wq;
+ }
+
dev_info(dev, "IWM SDIO probe\n");
return 0;
+ destroy_wq:
+ destroy_workqueue(hw->isr_wq);
debugfs_exit:
iwm_debugfs_exit(iwm);
if_free:
@@ -471,9 +479,10 @@ static void iwm_sdio_remove(struct sdio_func *func)
struct iwm_priv *iwm = hw_to_iwm(hw);
struct device *dev = &func->dev;
+ iwm_if_remove(iwm);
+ destroy_workqueue(hw->isr_wq);
iwm_debugfs_exit(iwm);
iwm_if_free(iwm);
- destroy_workqueue(hw->isr_wq);
sdio_set_drvdata(func, NULL);