diff options
Diffstat (limited to 'drivers/net/wireless/libertas_tf/cmd.c')
-rw-r--r-- | drivers/net/wireless/libertas_tf/cmd.c | 807 |
1 files changed, 0 insertions, 807 deletions
diff --git a/drivers/net/wireless/libertas_tf/cmd.c b/drivers/net/wireless/libertas_tf/cmd.c deleted file mode 100644 index 909ac3685010..000000000000 --- a/drivers/net/wireless/libertas_tf/cmd.c +++ /dev/null @@ -1,807 +0,0 @@ -/* - * Copyright (C) 2008, cozybit Inc. - * Copyright (C) 2003-2006, Marvell International Ltd. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or (at - * your option) any later version. - */ -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include <linux/hardirq.h> -#include <linux/slab.h> -#include <linux/export.h> - -#include "libertas_tf.h" - -static const struct channel_range channel_ranges[] = { - { LBTF_REGDOMAIN_US, 1, 12 }, - { LBTF_REGDOMAIN_CA, 1, 12 }, - { LBTF_REGDOMAIN_EU, 1, 14 }, - { LBTF_REGDOMAIN_JP, 1, 14 }, - { LBTF_REGDOMAIN_SP, 1, 14 }, - { LBTF_REGDOMAIN_FR, 1, 14 }, -}; - -static u16 lbtf_region_code_to_index[MRVDRV_MAX_REGION_CODE] = -{ - LBTF_REGDOMAIN_US, LBTF_REGDOMAIN_CA, LBTF_REGDOMAIN_EU, - LBTF_REGDOMAIN_SP, LBTF_REGDOMAIN_FR, LBTF_REGDOMAIN_JP, -}; - -static struct cmd_ctrl_node *lbtf_get_cmd_ctrl_node(struct lbtf_private *priv); - - -/** - * lbtf_cmd_copyback - Simple callback that copies response back into command - * - * @priv A pointer to struct lbtf_private structure - * @extra A pointer to the original command structure for which - * 'resp' is a response - * @resp A pointer to the command response - * - * Returns: 0 on success, error on failure - */ -int lbtf_cmd_copyback(struct lbtf_private *priv, unsigned long extra, - struct cmd_header *resp) -{ - struct cmd_header *buf = (void *)extra; - uint16_t copy_len; - - copy_len = min(le16_to_cpu(buf->size), le16_to_cpu(resp->size)); - memcpy(buf, resp, copy_len); - return 0; -} -EXPORT_SYMBOL_GPL(lbtf_cmd_copyback); - -#define CHAN_TO_IDX(chan) ((chan) - 1) - -static void lbtf_geo_init(struct lbtf_private *priv) -{ - const struct channel_range *range = channel_ranges; - u8 ch; - int i; - - for (i = 0; i < ARRAY_SIZE(channel_ranges); i++) - if (channel_ranges[i].regdomain == priv->regioncode) { - range = &channel_ranges[i]; - break; - } - - for (ch = priv->range.start; ch < priv->range.end; ch++) - priv->channels[CHAN_TO_IDX(ch)].flags = 0; -} - -/** - * lbtf_update_hw_spec: Updates the hardware details. - * - * @priv A pointer to struct lbtf_private structure - * - * Returns: 0 on success, error on failure - */ -int lbtf_update_hw_spec(struct lbtf_private *priv) -{ - struct cmd_ds_get_hw_spec cmd; - int ret = -1; - u32 i; - - lbtf_deb_enter(LBTF_DEB_CMD); - - memset(&cmd, 0, sizeof(cmd)); - cmd.hdr.size = cpu_to_le16(sizeof(cmd)); - memcpy(cmd.permanentaddr, priv->current_addr, ETH_ALEN); - ret = lbtf_cmd_with_response(priv, CMD_GET_HW_SPEC, &cmd); - if (ret) - goto out; - - priv->fwcapinfo = le32_to_cpu(cmd.fwcapinfo); - - /* The firmware release is in an interesting format: the patch - * level is in the most significant nibble ... so fix that: */ - priv->fwrelease = le32_to_cpu(cmd.fwrelease); - priv->fwrelease = (priv->fwrelease << 8) | - (priv->fwrelease >> 24 & 0xff); - - printk(KERN_INFO "libertastf: %pM, fw %u.%u.%up%u, cap 0x%08x\n", - cmd.permanentaddr, - priv->fwrelease >> 24 & 0xff, - priv->fwrelease >> 16 & 0xff, - priv->fwrelease >> 8 & 0xff, - priv->fwrelease & 0xff, - priv->fwcapinfo); - lbtf_deb_cmd("GET_HW_SPEC: hardware interface 0x%x, hardware spec 0x%04x\n", - cmd.hwifversion, cmd.version); - - /* Clamp region code to 8-bit since FW spec indicates that it should - * only ever be 8-bit, even though the field size is 16-bit. Some - * firmware returns non-zero high 8 bits here. - */ - priv->regioncode = le16_to_cpu(cmd.regioncode) & 0xFF; - - for (i = 0; i < MRVDRV_MAX_REGION_CODE; i++) { - /* use the region code to search for the index */ - if (priv->regioncode == lbtf_region_code_to_index[i]) - break; - } - - /* if it's unidentified region code, use the default (USA) */ - if (i >= MRVDRV_MAX_REGION_CODE) { - priv->regioncode = 0x10; - pr_info("unidentified region code; using the default (USA)\n"); - } - - if (priv->current_addr[0] == 0xff) - memmove(priv->current_addr, cmd.permanentaddr, ETH_ALEN); - - SET_IEEE80211_PERM_ADDR(priv->hw, priv->current_addr); - - lbtf_geo_init(priv); -out: - lbtf_deb_leave(LBTF_DEB_CMD); - return ret; -} - -/** - * lbtf_set_channel: Set the radio channel - * - * @priv A pointer to struct lbtf_private structure - * @channel The desired channel, or 0 to clear a locked channel - * - * Returns: 0 on success, error on failure - */ -int lbtf_set_channel(struct lbtf_private *priv, u8 channel) -{ - int ret = 0; - struct cmd_ds_802_11_rf_channel cmd; - - lbtf_deb_enter(LBTF_DEB_CMD); - - cmd.hdr.size = cpu_to_le16(sizeof(cmd)); - cmd.action = cpu_to_le16(CMD_OPT_802_11_RF_CHANNEL_SET); - cmd.channel = cpu_to_le16(channel); - - ret = lbtf_cmd_with_response(priv, CMD_802_11_RF_CHANNEL, &cmd); - lbtf_deb_leave_args(LBTF_DEB_CMD, "ret %d", ret); - return ret; -} - -int lbtf_beacon_set(struct lbtf_private *priv, struct sk_buff *beacon) -{ - struct cmd_ds_802_11_beacon_set cmd; - int size; - - lbtf_deb_enter(LBTF_DEB_CMD); - - if (beacon->len > MRVL_MAX_BCN_SIZE) { - lbtf_deb_leave_args(LBTF_DEB_CMD, "ret %d", -1); - return -1; - } - size = sizeof(cmd) - sizeof(cmd.beacon) + beacon->len; - cmd.hdr.size = cpu_to_le16(size); - cmd.len = cpu_to_le16(beacon->len); - memcpy(cmd.beacon, (u8 *) beacon->data, beacon->len); - - lbtf_cmd_async(priv, CMD_802_11_BEACON_SET, &cmd.hdr, size); - - lbtf_deb_leave_args(LBTF_DEB_CMD, "ret %d", 0); - return 0; -} - -int lbtf_beacon_ctrl(struct lbtf_private *priv, bool beacon_enable, - int beacon_int) -{ - struct cmd_ds_802_11_beacon_control cmd; - lbtf_deb_enter(LBTF_DEB_CMD); - - cmd.hdr.size = cpu_to_le16(sizeof(cmd)); - cmd.action = cpu_to_le16(CMD_ACT_SET); - cmd.beacon_enable = cpu_to_le16(beacon_enable); - cmd.beacon_period = cpu_to_le16(beacon_int); - - lbtf_cmd_async(priv, CMD_802_11_BEACON_CTRL, &cmd.hdr, sizeof(cmd)); - - lbtf_deb_leave(LBTF_DEB_CMD); - return 0; -} - -static void lbtf_queue_cmd(struct lbtf_private *priv, - struct cmd_ctrl_node *cmdnode) -{ - unsigned long flags; - lbtf_deb_enter(LBTF_DEB_HOST); - - if (!cmdnode) { - lbtf_deb_host("QUEUE_CMD: cmdnode is NULL\n"); - goto qcmd_done; - } - - if (!cmdnode->cmdbuf->size) { - lbtf_deb_host("DNLD_CMD: cmd size is zero\n"); - goto qcmd_done; - } - - cmdnode->result = 0; - spin_lock_irqsave(&priv->driver_lock, flags); - list_add_tail(&cmdnode->list, &priv->cmdpendingq); - spin_unlock_irqrestore(&priv->driver_lock, flags); - - lbtf_deb_host("QUEUE_CMD: inserted command 0x%04x into cmdpendingq\n", - le16_to_cpu(cmdnode->cmdbuf->command)); - -qcmd_done: - lbtf_deb_leave(LBTF_DEB_HOST); -} - -static void lbtf_submit_command(struct lbtf_private *priv, - struct cmd_ctrl_node *cmdnode) -{ - unsigned long flags; - struct cmd_header *cmd; - uint16_t cmdsize; - uint16_t command; - int timeo = 5 * HZ; - int ret; - - lbtf_deb_enter(LBTF_DEB_HOST); - - cmd = cmdnode->cmdbuf; - - spin_lock_irqsave(&priv->driver_lock, flags); - priv->cur_cmd = cmdnode; - cmdsize = le16_to_cpu(cmd->size); - command = le16_to_cpu(cmd->command); - - lbtf_deb_cmd("DNLD_CMD: command 0x%04x, seq %d, size %d\n", - command, le16_to_cpu(cmd->seqnum), cmdsize); - lbtf_deb_hex(LBTF_DEB_CMD, "DNLD_CMD", (void *) cmdnode->cmdbuf, cmdsize); - - ret = priv->hw_host_to_card(priv, MVMS_CMD, (u8 *) cmd, cmdsize); - spin_unlock_irqrestore(&priv->driver_lock, flags); - - if (ret) { - pr_info("DNLD_CMD: hw_host_to_card failed: %d\n", ret); - /* Let the timer kick in and retry, and potentially reset - the whole thing if the condition persists */ - timeo = HZ; - } - - /* Setup the timer after transmit command */ - mod_timer(&priv->command_timer, jiffies + timeo); - - lbtf_deb_leave(LBTF_DEB_HOST); -} - -/** - * This function inserts command node to cmdfreeq - * after cleans it. Requires priv->driver_lock held. - */ -static void __lbtf_cleanup_and_insert_cmd(struct lbtf_private *priv, - struct cmd_ctrl_node *cmdnode) -{ - lbtf_deb_enter(LBTF_DEB_HOST); - - if (!cmdnode) - goto cl_ins_out; - - cmdnode->callback = NULL; - cmdnode->callback_arg = 0; - - memset(cmdnode->cmdbuf, 0, LBS_CMD_BUFFER_SIZE); - - list_add_tail(&cmdnode->list, &priv->cmdfreeq); - -cl_ins_out: - lbtf_deb_leave(LBTF_DEB_HOST); -} - -static void lbtf_cleanup_and_insert_cmd(struct lbtf_private *priv, - struct cmd_ctrl_node *ptempcmd) -{ - unsigned long flags; - - spin_lock_irqsave(&priv->driver_lock, flags); - __lbtf_cleanup_and_insert_cmd(priv, ptempcmd); - spin_unlock_irqrestore(&priv->driver_lock, flags); -} - -void lbtf_complete_command(struct lbtf_private *priv, struct cmd_ctrl_node *cmd, - int result) -{ - cmd->result = result; - cmd->cmdwaitqwoken = 1; - wake_up_interruptible(&cmd->cmdwait_q); - - if (!cmd->callback) - __lbtf_cleanup_and_insert_cmd(priv, cmd); - priv->cur_cmd = NULL; -} - -int lbtf_cmd_set_mac_multicast_addr(struct lbtf_private *priv) -{ - struct cmd_ds_mac_multicast_addr cmd; - - lbtf_deb_enter(LBTF_DEB_CMD); - - cmd.hdr.size = cpu_to_le16(sizeof(cmd)); - cmd.action = cpu_to_le16(CMD_ACT_SET); - - cmd.nr_of_adrs = cpu_to_le16((u16) priv->nr_of_multicastmacaddr); - - lbtf_deb_cmd("MULTICAST_ADR: setting %d addresses\n", cmd.nr_of_adrs); - - memcpy(cmd.maclist, priv->multicastlist, - priv->nr_of_multicastmacaddr * ETH_ALEN); - - lbtf_cmd_async(priv, CMD_MAC_MULTICAST_ADR, &cmd.hdr, sizeof(cmd)); - - lbtf_deb_leave(LBTF_DEB_CMD); - return 0; -} - -void lbtf_set_mode(struct lbtf_private *priv, enum lbtf_mode mode) -{ - struct cmd_ds_set_mode cmd; - lbtf_deb_enter(LBTF_DEB_WEXT); - - cmd.hdr.size = cpu_to_le16(sizeof(cmd)); - cmd.mode = cpu_to_le16(mode); - lbtf_deb_wext("Switching to mode: 0x%x\n", mode); - lbtf_cmd_async(priv, CMD_802_11_SET_MODE, &cmd.hdr, sizeof(cmd)); - - lbtf_deb_leave(LBTF_DEB_WEXT); -} - -void lbtf_set_bssid(struct lbtf_private *priv, bool activate, const u8 *bssid) -{ - struct cmd_ds_set_bssid cmd; - lbtf_deb_enter(LBTF_DEB_CMD); - - cmd.hdr.size = cpu_to_le16(sizeof(cmd)); - cmd.activate = activate ? 1 : 0; - if (activate) - memcpy(cmd.bssid, bssid, ETH_ALEN); - - lbtf_cmd_async(priv, CMD_802_11_SET_BSSID, &cmd.hdr, sizeof(cmd)); - lbtf_deb_leave(LBTF_DEB_CMD); -} - -int lbtf_set_mac_address(struct lbtf_private *priv, uint8_t *mac_addr) -{ - struct cmd_ds_802_11_mac_address cmd; - lbtf_deb_enter(LBTF_DEB_CMD); - - cmd.hdr.size = cpu_to_le16(sizeof(cmd)); - cmd.action = cpu_to_le16(CMD_ACT_SET); - - memcpy(cmd.macadd, mac_addr, ETH_ALEN); - - lbtf_cmd_async(priv, CMD_802_11_MAC_ADDRESS, &cmd.hdr, sizeof(cmd)); - lbtf_deb_leave(LBTF_DEB_CMD); - return 0; -} - -int lbtf_set_radio_control(struct lbtf_private *priv) -{ - int ret = 0; - struct cmd_ds_802_11_radio_control cmd; - - lbtf_deb_enter(LBTF_DEB_CMD); - - cmd.hdr.size = cpu_to_le16(sizeof(cmd)); - cmd.action = cpu_to_le16(CMD_ACT_SET); - - switch (priv->preamble) { - case CMD_TYPE_SHORT_PREAMBLE: - cmd.control = cpu_to_le16(SET_SHORT_PREAMBLE); - break; - - case CMD_TYPE_LONG_PREAMBLE: - cmd.control = cpu_to_le16(SET_LONG_PREAMBLE); - break; - - case CMD_TYPE_AUTO_PREAMBLE: - default: - cmd.control = cpu_to_le16(SET_AUTO_PREAMBLE); - break; - } - - if (priv->radioon) - cmd.control |= cpu_to_le16(TURN_ON_RF); - else - cmd.control &= cpu_to_le16(~TURN_ON_RF); - - lbtf_deb_cmd("RADIO_SET: radio %d, preamble %d\n", priv->radioon, - priv->preamble); - - ret = lbtf_cmd_with_response(priv, CMD_802_11_RADIO_CONTROL, &cmd); - - lbtf_deb_leave_args(LBTF_DEB_CMD, "ret %d", ret); - return ret; -} - -void lbtf_set_mac_control(struct lbtf_private *priv) -{ - struct cmd_ds_mac_control cmd; - lbtf_deb_enter(LBTF_DEB_CMD); - - cmd.hdr.size = cpu_to_le16(sizeof(cmd)); - cmd.action = cpu_to_le16(priv->mac_control); - cmd.reserved = 0; - - lbtf_cmd_async(priv, CMD_MAC_CONTROL, - &cmd.hdr, sizeof(cmd)); - - lbtf_deb_leave(LBTF_DEB_CMD); -} - -/** - * lbtf_allocate_cmd_buffer - Allocates cmd buffer, links it to free cmd queue - * - * @priv A pointer to struct lbtf_private structure - * - * Returns: 0 on success. - */ -int lbtf_allocate_cmd_buffer(struct lbtf_private *priv) -{ - int ret = 0; - u32 bufsize; - u32 i; - struct cmd_ctrl_node *cmdarray; - - lbtf_deb_enter(LBTF_DEB_HOST); - - /* Allocate and initialize the command array */ - bufsize = sizeof(struct cmd_ctrl_node) * LBS_NUM_CMD_BUFFERS; - cmdarray = kzalloc(bufsize, GFP_KERNEL); - if (!cmdarray) { - lbtf_deb_host("ALLOC_CMD_BUF: tempcmd_array is NULL\n"); - ret = -1; - goto done; - } - priv->cmd_array = cmdarray; - - /* Allocate and initialize each command buffer in the command array */ - for (i = 0; i < LBS_NUM_CMD_BUFFERS; i++) { - cmdarray[i].cmdbuf = kzalloc(LBS_CMD_BUFFER_SIZE, GFP_KERNEL); - if (!cmdarray[i].cmdbuf) { - lbtf_deb_host("ALLOC_CMD_BUF: ptempvirtualaddr is NULL\n"); - ret = -1; - goto done; - } - } - - for (i = 0; i < LBS_NUM_CMD_BUFFERS; i++) { - init_waitqueue_head(&cmdarray[i].cmdwait_q); - lbtf_cleanup_and_insert_cmd(priv, &cmdarray[i]); - } - - ret = 0; - -done: - lbtf_deb_leave_args(LBTF_DEB_HOST, "ret %d", ret); - return ret; -} - -/** - * lbtf_free_cmd_buffer - Frees the cmd buffer. - * - * @priv A pointer to struct lbtf_private structure - * - * Returns: 0 - */ -int lbtf_free_cmd_buffer(struct lbtf_private *priv) -{ - struct cmd_ctrl_node *cmdarray; - unsigned int i; - - lbtf_deb_enter(LBTF_DEB_HOST); - - /* need to check if cmd array is allocated or not */ - if (priv->cmd_array == NULL) { - lbtf_deb_host("FREE_CMD_BUF: cmd_array is NULL\n"); - goto done; - } - - cmdarray = priv->cmd_array; - - /* Release shared memory buffers */ - for (i = 0; i < LBS_NUM_CMD_BUFFERS; i++) { - kfree(cmdarray[i].cmdbuf); - cmdarray[i].cmdbuf = NULL; - } - - /* Release cmd_ctrl_node */ - kfree(priv->cmd_array); - priv->cmd_array = NULL; - -done: - lbtf_deb_leave(LBTF_DEB_HOST); - return 0; -} - -/** - * lbtf_get_cmd_ctrl_node - Gets free cmd node from free cmd queue. - * - * @priv A pointer to struct lbtf_private structure - * - * Returns: pointer to a struct cmd_ctrl_node or NULL if none available. - */ -static struct cmd_ctrl_node *lbtf_get_cmd_ctrl_node(struct lbtf_private *priv) -{ - struct cmd_ctrl_node *tempnode; - unsigned long flags; - - lbtf_deb_enter(LBTF_DEB_HOST); - - if (!priv) - return NULL; - - spin_lock_irqsave(&priv->driver_lock, flags); - - if (!list_empty(&priv->cmdfreeq)) { - tempnode = list_first_entry(&priv->cmdfreeq, - struct cmd_ctrl_node, list); - list_del(&tempnode->list); - } else { - lbtf_deb_host("GET_CMD_NODE: cmd_ctrl_node is not available\n"); - tempnode = NULL; - } - - spin_unlock_irqrestore(&priv->driver_lock, flags); - - lbtf_deb_leave(LBTF_DEB_HOST); - return tempnode; -} - -/** - * lbtf_execute_next_command: execute next command in cmd pending queue. - * - * @priv A pointer to struct lbtf_private structure - * - * Returns: 0 on success. - */ -int lbtf_execute_next_command(struct lbtf_private *priv) -{ - struct cmd_ctrl_node *cmdnode = NULL; - struct cmd_header *cmd; - unsigned long flags; - int ret = 0; - - /* Debug group is lbtf_deb_THREAD and not lbtf_deb_HOST, because the - * only caller to us is lbtf_thread() and we get even when a - * data packet is received */ - lbtf_deb_enter(LBTF_DEB_THREAD); - - spin_lock_irqsave(&priv->driver_lock, flags); - - if (priv->cur_cmd) { - pr_alert("EXEC_NEXT_CMD: already processing command!\n"); - spin_unlock_irqrestore(&priv->driver_lock, flags); - ret = -1; - goto done; - } - - if (!list_empty(&priv->cmdpendingq)) { - cmdnode = list_first_entry(&priv->cmdpendingq, - struct cmd_ctrl_node, list); - } - - if (cmdnode) { - cmd = cmdnode->cmdbuf; - - list_del(&cmdnode->list); - lbtf_deb_host("EXEC_NEXT_CMD: sending command 0x%04x\n", - le16_to_cpu(cmd->command)); - spin_unlock_irqrestore(&priv->driver_lock, flags); - lbtf_submit_command(priv, cmdnode); - } else - spin_unlock_irqrestore(&priv->driver_lock, flags); - - ret = 0; -done: - lbtf_deb_leave(LBTF_DEB_THREAD); - return ret; -} - -static struct cmd_ctrl_node *__lbtf_cmd_async(struct lbtf_private *priv, - uint16_t command, struct cmd_header *in_cmd, int in_cmd_size, - int (*callback)(struct lbtf_private *, unsigned long, - struct cmd_header *), - unsigned long callback_arg) -{ - struct cmd_ctrl_node *cmdnode; - - lbtf_deb_enter(LBTF_DEB_HOST); - - if (priv->surpriseremoved) { - lbtf_deb_host("PREP_CMD: card removed\n"); - cmdnode = ERR_PTR(-ENOENT); - goto done; - } - - cmdnode = lbtf_get_cmd_ctrl_node(priv); - if (cmdnode == NULL) { - lbtf_deb_host("PREP_CMD: cmdnode is NULL\n"); - - /* Wake up main thread to execute next command */ - queue_work(lbtf_wq, &priv->cmd_work); - cmdnode = ERR_PTR(-ENOBUFS); - goto done; - } - - cmdnode->callback = callback; - cmdnode->callback_arg = callback_arg; - - /* Copy the incoming command to the buffer */ - memcpy(cmdnode->cmdbuf, in_cmd, in_cmd_size); - - /* Set sequence number, clean result, move to buffer */ - priv->seqnum++; - cmdnode->cmdbuf->command = cpu_to_le16(command); - cmdnode->cmdbuf->size = cpu_to_le16(in_cmd_size); - cmdnode->cmdbuf->seqnum = cpu_to_le16(priv->seqnum); - cmdnode->cmdbuf->result = 0; - - lbtf_deb_host("PREP_CMD: command 0x%04x\n", command); - - cmdnode->cmdwaitqwoken = 0; - lbtf_queue_cmd(priv, cmdnode); - queue_work(lbtf_wq, &priv->cmd_work); - - done: - lbtf_deb_leave_args(LBTF_DEB_HOST, "ret %p", cmdnode); - return cmdnode; -} - -void lbtf_cmd_async(struct lbtf_private *priv, uint16_t command, - struct cmd_header *in_cmd, int in_cmd_size) -{ - lbtf_deb_enter(LBTF_DEB_CMD); - __lbtf_cmd_async(priv, command, in_cmd, in_cmd_size, NULL, 0); - lbtf_deb_leave(LBTF_DEB_CMD); -} - -int __lbtf_cmd(struct lbtf_private *priv, uint16_t command, - struct cmd_header *in_cmd, int in_cmd_size, - int (*callback)(struct lbtf_private *, - unsigned long, struct cmd_header *), - unsigned long callback_arg) -{ - struct cmd_ctrl_node *cmdnode; - unsigned long flags; - int ret = 0; - - lbtf_deb_enter(LBTF_DEB_HOST); - - cmdnode = __lbtf_cmd_async(priv, command, in_cmd, in_cmd_size, - callback, callback_arg); - if (IS_ERR(cmdnode)) { - ret = PTR_ERR(cmdnode); - goto done; - } - - might_sleep(); - ret = wait_event_interruptible(cmdnode->cmdwait_q, - cmdnode->cmdwaitqwoken); - if (ret) { - pr_info("PREP_CMD: command 0x%04x interrupted by signal: %d\n", - command, ret); - goto done; - } - - spin_lock_irqsave(&priv->driver_lock, flags); - ret = cmdnode->result; - if (ret) - pr_info("PREP_CMD: command 0x%04x failed: %d\n", - command, ret); - - __lbtf_cleanup_and_insert_cmd(priv, cmdnode); - spin_unlock_irqrestore(&priv->driver_lock, flags); - -done: - lbtf_deb_leave_args(LBTF_DEB_HOST, "ret %d", ret); - return ret; -} -EXPORT_SYMBOL_GPL(__lbtf_cmd); - -/* Call holding driver_lock */ -void lbtf_cmd_response_rx(struct lbtf_private *priv) -{ - priv->cmd_response_rxed = 1; - queue_work(lbtf_wq, &priv->cmd_work); -} -EXPORT_SYMBOL_GPL(lbtf_cmd_response_rx); - -int lbtf_process_rx_command(struct lbtf_private *priv) -{ - uint16_t respcmd, curcmd; - struct cmd_header *resp; - int ret = 0; - unsigned long flags; - uint16_t result; - - lbtf_deb_enter(LBTF_DEB_CMD); - - mutex_lock(&priv->lock); - spin_lock_irqsave(&priv->driver_lock, flags); - - if (!priv->cur_cmd) { - ret = -1; - spin_unlock_irqrestore(&priv->driver_lock, flags); - goto done; - } - - resp = (void *)priv->cmd_resp_buff; - curcmd = le16_to_cpu(priv->cur_cmd->cmdbuf->command); - respcmd = le16_to_cpu(resp->command); - result = le16_to_cpu(resp->result); - - if (net_ratelimit()) - pr_info("libertastf: cmd response 0x%04x, seq %d, size %d\n", - respcmd, le16_to_cpu(resp->seqnum), - le16_to_cpu(resp->size)); - - if (resp->seqnum != priv->cur_cmd->cmdbuf->seqnum) { - spin_unlock_irqrestore(&priv->driver_lock, flags); - ret = -1; - goto done; - } - if (respcmd != CMD_RET(curcmd)) { - spin_unlock_irqrestore(&priv->driver_lock, flags); - ret = -1; - goto done; - } - - if (resp->result == cpu_to_le16(0x0004)) { - /* 0x0004 means -EAGAIN. Drop the response, let it time out - and be resubmitted */ - spin_unlock_irqrestore(&priv->driver_lock, flags); - ret = -1; - goto done; - } - - /* Now we got response from FW, cancel the command timer */ - del_timer(&priv->command_timer); - priv->cmd_timed_out = 0; - if (priv->nr_retries) - priv->nr_retries = 0; - - /* If the command is not successful, cleanup and return failure */ - if ((result != 0 || !(respcmd & 0x8000))) { - /* - * Handling errors here - */ - switch (respcmd) { - case CMD_RET(CMD_GET_HW_SPEC): - case CMD_RET(CMD_802_11_RESET): - pr_info("libertastf: reset failed\n"); - break; - - } - lbtf_complete_command(priv, priv->cur_cmd, result); - spin_unlock_irqrestore(&priv->driver_lock, flags); - - ret = -1; - goto done; - } - - spin_unlock_irqrestore(&priv->driver_lock, flags); - - if (priv->cur_cmd && priv->cur_cmd->callback) { - ret = priv->cur_cmd->callback(priv, priv->cur_cmd->callback_arg, - resp); - } - spin_lock_irqsave(&priv->driver_lock, flags); - - if (priv->cur_cmd) { - /* Clean up and Put current command back to cmdfreeq */ - lbtf_complete_command(priv, priv->cur_cmd, result); - } - spin_unlock_irqrestore(&priv->driver_lock, flags); - -done: - mutex_unlock(&priv->lock); - lbtf_deb_leave_args(LBTF_DEB_CMD, "ret %d", ret); - return ret; -} |