diff options
Diffstat (limited to 'drivers/net/wireless/libertas')
-rw-r--r-- | drivers/net/wireless/libertas/cfg.c | 1 | ||||
-rw-r--r-- | drivers/net/wireless/libertas/cmd.c | 47 | ||||
-rw-r--r-- | drivers/net/wireless/libertas/cmd.h | 2 | ||||
-rw-r--r-- | drivers/net/wireless/libertas/cmdresp.c | 7 | ||||
-rw-r--r-- | drivers/net/wireless/libertas/debugfs.c | 1 | ||||
-rw-r--r-- | drivers/net/wireless/libertas/ethtool.c | 1 | ||||
-rw-r--r-- | drivers/net/wireless/libertas/if_sdio.c | 34 | ||||
-rw-r--r-- | drivers/net/wireless/libertas/if_spi.c | 8 | ||||
-rw-r--r-- | drivers/net/wireless/libertas/main.c | 13 | ||||
-rw-r--r-- | drivers/net/wireless/libertas/mesh.c | 1 | ||||
-rw-r--r-- | drivers/net/wireless/libertas/rx.c | 1 | ||||
-rw-r--r-- | drivers/net/wireless/libertas/tx.c | 1 |
12 files changed, 96 insertions, 21 deletions
diff --git a/drivers/net/wireless/libertas/cfg.c b/drivers/net/wireless/libertas/cfg.c index 5d637af2d7c3..b456a53b64b1 100644 --- a/drivers/net/wireless/libertas/cfg.c +++ b/drivers/net/wireless/libertas/cfg.c @@ -8,6 +8,7 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include <linux/hardirq.h> #include <linux/sched.h> #include <linux/wait.h> #include <linux/slab.h> diff --git a/drivers/net/wireless/libertas/cmd.c b/drivers/net/wireless/libertas/cmd.c index 71c8f3fccfa1..dbd24a4607ec 100644 --- a/drivers/net/wireless/libertas/cmd.c +++ b/drivers/net/wireless/libertas/cmd.c @@ -3,6 +3,7 @@ * It prepares command and sends it to firmware when it is ready. */ +#include <linux/hardirq.h> #include <linux/kfifo.h> #include <linux/sched.h> #include <linux/slab.h> @@ -873,6 +874,7 @@ int lbs_get_reg(struct lbs_private *priv, u16 reg, u16 offset, u32 *value) memset(&cmd, 0, sizeof(cmd)); cmd.hdr.size = cpu_to_le16(sizeof(cmd)); cmd.action = cpu_to_le16(CMD_ACT_GET); + cmd.offset = cpu_to_le16(offset); if (reg != CMD_MAC_REG_ACCESS && reg != CMD_BBP_REG_ACCESS && @@ -882,7 +884,7 @@ int lbs_get_reg(struct lbs_private *priv, u16 reg, u16 offset, u32 *value) } ret = lbs_cmd_with_response(priv, reg, &cmd); - if (ret) { + if (!ret) { if (reg == CMD_BBP_REG_ACCESS || reg == CMD_RF_REG_ACCESS) *value = cmd.value.bbp_rf; else if (reg == CMD_MAC_REG_ACCESS) @@ -915,6 +917,7 @@ int lbs_set_reg(struct lbs_private *priv, u16 reg, u16 offset, u32 value) memset(&cmd, 0, sizeof(cmd)); cmd.hdr.size = cpu_to_le16(sizeof(cmd)); cmd.action = cpu_to_le16(CMD_ACT_SET); + cmd.offset = cpu_to_le16(offset); if (reg == CMD_BBP_REG_ACCESS || reg == CMD_RF_REG_ACCESS) cmd.value.bbp_rf = (u8) (value & 0xFF); @@ -1067,16 +1070,34 @@ static void lbs_cleanup_and_insert_cmd(struct lbs_private *priv, spin_unlock_irqrestore(&priv->driver_lock, flags); } -void lbs_complete_command(struct lbs_private *priv, struct cmd_ctrl_node *cmd, - int result) +void __lbs_complete_command(struct lbs_private *priv, struct cmd_ctrl_node *cmd, + int result) { + /* + * Normally, commands are removed from cmdpendingq before being + * submitted. However, we can arrive here on alternative codepaths + * where the command is still pending. Make sure the command really + * isn't part of a list at this point. + */ + list_del_init(&cmd->list); + cmd->result = result; cmd->cmdwaitqwoken = 1; - wake_up_interruptible(&cmd->cmdwait_q); + wake_up(&cmd->cmdwait_q); if (!cmd->callback || cmd->callback == lbs_cmd_async_callback) __lbs_cleanup_and_insert_cmd(priv, cmd); priv->cur_cmd = NULL; + wake_up_interruptible(&priv->waitq); +} + +void lbs_complete_command(struct lbs_private *priv, struct cmd_ctrl_node *cmd, + int result) +{ + unsigned long flags; + spin_lock_irqsave(&priv->driver_lock, flags); + __lbs_complete_command(priv, cmd, result); + spin_unlock_irqrestore(&priv->driver_lock, flags); } int lbs_set_radio(struct lbs_private *priv, u8 preamble, u8 radio_on) @@ -1248,7 +1269,7 @@ static struct cmd_ctrl_node *lbs_get_free_cmd_node(struct lbs_private *priv) if (!list_empty(&priv->cmdfreeq)) { tempnode = list_first_entry(&priv->cmdfreeq, struct cmd_ctrl_node, list); - list_del(&tempnode->list); + list_del_init(&tempnode->list); } else { lbs_deb_host("GET_CMD_NODE: cmd_ctrl_node is not available\n"); tempnode = NULL; @@ -1356,10 +1377,7 @@ int lbs_execute_next_command(struct lbs_private *priv) cpu_to_le16(PS_MODE_ACTION_EXIT_PS)) { lbs_deb_host( "EXEC_NEXT_CMD: ignore ENTER_PS cmd\n"); - spin_lock_irqsave(&priv->driver_lock, flags); - list_del(&cmdnode->list); lbs_complete_command(priv, cmdnode, 0); - spin_unlock_irqrestore(&priv->driver_lock, flags); ret = 0; goto done; @@ -1369,10 +1387,7 @@ int lbs_execute_next_command(struct lbs_private *priv) (priv->psstate == PS_STATE_PRE_SLEEP)) { lbs_deb_host( "EXEC_NEXT_CMD: ignore EXIT_PS cmd in sleep\n"); - spin_lock_irqsave(&priv->driver_lock, flags); - list_del(&cmdnode->list); lbs_complete_command(priv, cmdnode, 0); - spin_unlock_irqrestore(&priv->driver_lock, flags); priv->needtowakeup = 1; ret = 0; @@ -1384,7 +1399,7 @@ int lbs_execute_next_command(struct lbs_private *priv) } } spin_lock_irqsave(&priv->driver_lock, flags); - list_del(&cmdnode->list); + list_del_init(&cmdnode->list); spin_unlock_irqrestore(&priv->driver_lock, flags); lbs_deb_host("EXEC_NEXT_CMD: sending command 0x%04x\n", le16_to_cpu(cmd->command)); @@ -1667,7 +1682,13 @@ int __lbs_cmd(struct lbs_private *priv, uint16_t command, } might_sleep(); - wait_event_interruptible(cmdnode->cmdwait_q, cmdnode->cmdwaitqwoken); + + /* + * Be careful with signals here. A signal may be received as the system + * goes into suspend or resume. We do not want this to interrupt the + * command, so we perform an uninterruptible sleep. + */ + wait_event(cmdnode->cmdwait_q, cmdnode->cmdwaitqwoken); spin_lock_irqsave(&priv->driver_lock, flags); ret = cmdnode->result; diff --git a/drivers/net/wireless/libertas/cmd.h b/drivers/net/wireless/libertas/cmd.h index 7109d6b717ea..b280ef7a0aea 100644 --- a/drivers/net/wireless/libertas/cmd.h +++ b/drivers/net/wireless/libertas/cmd.h @@ -59,6 +59,8 @@ int lbs_allocate_cmd_buffer(struct lbs_private *priv); int lbs_free_cmd_buffer(struct lbs_private *priv); int lbs_execute_next_command(struct lbs_private *priv); +void __lbs_complete_command(struct lbs_private *priv, struct cmd_ctrl_node *cmd, + int result); void lbs_complete_command(struct lbs_private *priv, struct cmd_ctrl_node *cmd, int result); int lbs_process_command_response(struct lbs_private *priv, u8 *data, u32 len); diff --git a/drivers/net/wireless/libertas/cmdresp.c b/drivers/net/wireless/libertas/cmdresp.c index 207fc361db84..178b222b3ce1 100644 --- a/drivers/net/wireless/libertas/cmdresp.c +++ b/drivers/net/wireless/libertas/cmdresp.c @@ -3,6 +3,7 @@ * responses as well as events generated by firmware. */ +#include <linux/hardirq.h> #include <linux/slab.h> #include <linux/delay.h> #include <linux/sched.h> @@ -165,7 +166,7 @@ int lbs_process_command_response(struct lbs_private *priv, u8 *data, u32 len) lbs_deb_host("CMD_RESP: PS action 0x%X\n", action); } - lbs_complete_command(priv, priv->cur_cmd, result); + __lbs_complete_command(priv, priv->cur_cmd, result); spin_unlock_irqrestore(&priv->driver_lock, flags); ret = 0; @@ -186,7 +187,7 @@ int lbs_process_command_response(struct lbs_private *priv, u8 *data, u32 len) break; } - lbs_complete_command(priv, priv->cur_cmd, result); + __lbs_complete_command(priv, priv->cur_cmd, result); spin_unlock_irqrestore(&priv->driver_lock, flags); ret = -1; @@ -204,7 +205,7 @@ int lbs_process_command_response(struct lbs_private *priv, u8 *data, u32 len) if (priv->cur_cmd) { /* Clean up and Put current command back to cmdfreeq */ - lbs_complete_command(priv, priv->cur_cmd, result); + __lbs_complete_command(priv, priv->cur_cmd, result); } spin_unlock_irqrestore(&priv->driver_lock, flags); diff --git a/drivers/net/wireless/libertas/debugfs.c b/drivers/net/wireless/libertas/debugfs.c index 23250f621761..1af182778844 100644 --- a/drivers/net/wireless/libertas/debugfs.c +++ b/drivers/net/wireless/libertas/debugfs.c @@ -1,6 +1,7 @@ #include <linux/dcache.h> #include <linux/debugfs.h> #include <linux/delay.h> +#include <linux/hardirq.h> #include <linux/mm.h> #include <linux/string.h> #include <linux/slab.h> diff --git a/drivers/net/wireless/libertas/ethtool.c b/drivers/net/wireless/libertas/ethtool.c index 29dbce4a9f86..4dfb3bfd2cf3 100644 --- a/drivers/net/wireless/libertas/ethtool.c +++ b/drivers/net/wireless/libertas/ethtool.c @@ -1,3 +1,4 @@ +#include <linux/hardirq.h> #include <linux/netdevice.h> #include <linux/ethtool.h> #include <linux/delay.h> diff --git a/drivers/net/wireless/libertas/if_sdio.c b/drivers/net/wireless/libertas/if_sdio.c index 224e9853c480..387786e1b394 100644 --- a/drivers/net/wireless/libertas/if_sdio.c +++ b/drivers/net/wireless/libertas/if_sdio.c @@ -892,6 +892,37 @@ static int if_sdio_reset_deep_sleep_wakeup(struct lbs_private *priv) } +static struct mmc_host *reset_host; + +static void if_sdio_reset_card_worker(struct work_struct *work) +{ + /* + * The actual reset operation must be run outside of lbs_thread. This + * is because mmc_remove_host() will cause the device to be instantly + * destroyed, and the libertas driver then needs to end lbs_thread, + * leading to a deadlock. + * + * We run it in a workqueue totally independent from the if_sdio_card + * instance for that reason. + */ + + pr_info("Resetting card..."); + mmc_remove_host(reset_host); + mmc_add_host(reset_host); +} +static DECLARE_WORK(card_reset_work, if_sdio_reset_card_worker); + +static void if_sdio_reset_card(struct lbs_private *priv) +{ + struct if_sdio_card *card = priv->card; + + if (work_pending(&card_reset_work)) + return; + + reset_host = card->func->card->host; + schedule_work(&card_reset_work); +} + /*******************************************************************/ /* SDIO callbacks */ /*******************************************************************/ @@ -1065,6 +1096,7 @@ static int if_sdio_probe(struct sdio_func *func, priv->enter_deep_sleep = if_sdio_enter_deep_sleep; priv->exit_deep_sleep = if_sdio_exit_deep_sleep; priv->reset_deep_sleep_wakeup = if_sdio_reset_deep_sleep_wakeup; + priv->reset_card = if_sdio_reset_card; sdio_claim_host(func); @@ -1301,6 +1333,8 @@ static void __exit if_sdio_exit_module(void) /* Set the flag as user is removing this module. */ user_rmmod = 1; + cancel_work_sync(&card_reset_work); + sdio_unregister_driver(&if_sdio_driver); lbs_deb_leave(LBS_DEB_SDIO); diff --git a/drivers/net/wireless/libertas/if_spi.c b/drivers/net/wireless/libertas/if_spi.c index 463352c890d7..e0286cfbc91d 100644 --- a/drivers/net/wireless/libertas/if_spi.c +++ b/drivers/net/wireless/libertas/if_spi.c @@ -19,6 +19,8 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include <linux/hardirq.h> +#include <linux/interrupt.h> #include <linux/moduleparam.h> #include <linux/firmware.h> #include <linux/jiffies.h> @@ -1032,7 +1034,6 @@ static irqreturn_t if_spi_host_interrupt(int irq, void *dev_id) static int if_spi_init_card(struct if_spi_card *card) { struct lbs_private *priv = card->priv; - struct spi_device *spi = card->spi; int err, i; u32 scratch; const struct firmware *helper = NULL; @@ -1080,8 +1081,9 @@ static int if_spi_init_card(struct if_spi_card *card) "attached to SPI bus_num %d, chip_select %d. " "spi->max_speed_hz=%d\n", card->card_id, card->card_rev, - spi->master->bus_num, spi->chip_select, - spi->max_speed_hz); + card->spi->master->bus_num, + card->spi->chip_select, + card->spi->max_speed_hz); err = if_spi_prog_helper_firmware(card, helper); if (err) goto out; diff --git a/drivers/net/wireless/libertas/main.c b/drivers/net/wireless/libertas/main.c index 8c40949cb076..c79aac4b1dae 100644 --- a/drivers/net/wireless/libertas/main.c +++ b/drivers/net/wireless/libertas/main.c @@ -9,6 +9,7 @@ #include <linux/moduleparam.h> #include <linux/delay.h> #include <linux/etherdevice.h> +#include <linux/hardirq.h> #include <linux/netdevice.h> #include <linux/if_arp.h> #include <linux/kthread.h> @@ -638,6 +639,14 @@ static void lbs_cmd_timeout_handler(unsigned long data) le16_to_cpu(priv->cur_cmd->cmdbuf->command)); priv->cmd_timed_out = 1; + + /* + * If the device didn't even acknowledge the command, reset the state + * so that we don't block all future commands due to this one timeout. + */ + if (priv->dnld_sent == DNLD_CMD_SENT) + priv->dnld_sent = DNLD_RES_RECEIVED; + wake_up_interruptible(&priv->waitq); out: spin_unlock_irqrestore(&priv->driver_lock, flags); @@ -994,7 +1003,7 @@ void lbs_stop_card(struct lbs_private *priv) list_for_each_entry(cmdnode, &priv->cmdpendingq, list) { cmdnode->result = -ENOENT; cmdnode->cmdwaitqwoken = 1; - wake_up_interruptible(&cmdnode->cmdwait_q); + wake_up(&cmdnode->cmdwait_q); } /* Flush the command the card is currently processing */ @@ -1002,7 +1011,7 @@ void lbs_stop_card(struct lbs_private *priv) lbs_deb_main("clearing current command\n"); priv->cur_cmd->result = -ENOENT; priv->cur_cmd->cmdwaitqwoken = 1; - wake_up_interruptible(&priv->cur_cmd->cmdwait_q); + wake_up(&priv->cur_cmd->cmdwait_q); } lbs_deb_main("done clearing commands\n"); spin_unlock_irqrestore(&priv->driver_lock, flags); diff --git a/drivers/net/wireless/libertas/mesh.c b/drivers/net/wireless/libertas/mesh.c index 24cf06680c6b..7969d104189d 100644 --- a/drivers/net/wireless/libertas/mesh.c +++ b/drivers/net/wireless/libertas/mesh.c @@ -2,6 +2,7 @@ #include <linux/delay.h> #include <linux/etherdevice.h> +#include <linux/hardirq.h> #include <linux/netdevice.h> #include <linux/if_ether.h> #include <linux/if_arp.h> diff --git a/drivers/net/wireless/libertas/rx.c b/drivers/net/wireless/libertas/rx.c index fdb0448301a0..bfb8898ae518 100644 --- a/drivers/net/wireless/libertas/rx.c +++ b/drivers/net/wireless/libertas/rx.c @@ -5,6 +5,7 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include <linux/etherdevice.h> +#include <linux/hardirq.h> #include <linux/slab.h> #include <linux/types.h> #include <net/cfg80211.h> diff --git a/drivers/net/wireless/libertas/tx.c b/drivers/net/wireless/libertas/tx.c index bbb95f88dc01..f19495b178f6 100644 --- a/drivers/net/wireless/libertas/tx.c +++ b/drivers/net/wireless/libertas/tx.c @@ -1,6 +1,7 @@ /* * This file contains the handling of TX in wlan driver. */ +#include <linux/hardirq.h> #include <linux/netdevice.h> #include <linux/etherdevice.h> #include <linux/sched.h> |