diff --git a/drivers/net/wireless/libertas/cmd.c b/drivers/net/wireless/libertas/cmd.c index 54ef990a46cc11e44a672bc70d24e640fd6d237e..9064513aea0d22a1bc94e84e41bf6b95a11ee412 100644 --- a/drivers/net/wireless/libertas/cmd.c +++ b/drivers/net/wireless/libertas/cmd.c @@ -2007,3 +2007,99 @@ void lbs_ps_confirm_sleep(struct lbs_private *priv, u16 psmode) lbs_deb_leave(LBS_DEB_HOST); } + + +/** + * @brief Simple way to call firmware functions + * + * @param priv A pointer to struct lbs_private structure + * @param psmode one of the many CMD_802_11_xxxx + * @param cmd pointer to the parameters structure for above command + * (this should not include the command, size, sequence + * and result fields from struct cmd_ds_gen) + * @param cmd_size size structure pointed to by cmd + * @param rsp pointer to an area where the result should be placed + * @param rsp_size pointer to the size of the rsp area. If the firmware + * returns fewer bytes, then this *rsp_size will be + * changed to the actual size. + * @return -1 in case of a higher level error, otherwise + * the result code from the firmware + */ +int lbs_cmd(struct lbs_private *priv, + u16 command, + void *cmd, int cmd_size, + void *rsp, int *rsp_size) +{ + struct lbs_adapter *adapter = priv->adapter; + struct cmd_ctrl_node *cmdnode; + struct cmd_ds_gen *cmdptr; + unsigned long flags; + int ret = 0; + + lbs_deb_enter(LBS_DEB_HOST); + lbs_deb_host("rsp at %p, rsp_size at %p\n", rsp, rsp_size); + + if (!adapter || !rsp_size) { + lbs_deb_host("PREP_CMD: adapter is NULL\n"); + ret = -1; + goto done; + } + + if (adapter->surpriseremoved) { + lbs_deb_host("PREP_CMD: card removed\n"); + ret = -1; + goto done; + } + + cmdnode = lbs_get_cmd_ctrl_node(priv); + + if (cmdnode == NULL) { + lbs_deb_host("PREP_CMD: cmdnode is NULL\n"); + + /* Wake up main thread to execute next command */ + wake_up_interruptible(&priv->waitq); + ret = -1; + goto done; + } + + cmdptr = (struct cmd_ds_gen *)cmdnode->bufvirtualaddr; + cmdnode->wait_option = CMD_OPTION_WAITFORRSP; + cmdnode->pdata_buf = rsp; + cmdnode->pdata_size = rsp_size; + + /* Set sequence number, clean result, move to buffer */ + adapter->seqnum++; + cmdptr->command = cpu_to_le16(command); + cmdptr->size = cmd_size + S_DS_GEN; + cmdptr->seqnum = cpu_to_le16(adapter->seqnum); + cmdptr->result = 0; + memcpy(cmdptr->cmdresp, cmd, cmd_size); + + lbs_deb_host("PREP_CMD: command 0x%04x\n", command); + + /* here was the big old switch() statement, which is now obsolete, + * because the caller of lbs_cmd() sets up all of *cmd for us. */ + + cmdnode->cmdwaitqwoken = 0; + lbs_queue_cmd(adapter, cmdnode, 1); + wake_up_interruptible(&priv->waitq); + + might_sleep(); + wait_event_interruptible(cmdnode->cmdwait_q, cmdnode->cmdwaitqwoken); + + spin_lock_irqsave(&adapter->driver_lock, flags); + if (adapter->cur_cmd_retcode) { + lbs_deb_host("PREP_CMD: command failed with return code %d\n", + adapter->cur_cmd_retcode); + adapter->cur_cmd_retcode = 0; + ret = -1; + } + spin_unlock_irqrestore(&adapter->driver_lock, flags); + +done: + lbs_deb_leave_args(LBS_DEB_HOST, "ret %d", ret); + return ret; +} +EXPORT_SYMBOL_GPL(lbs_cmd); + + diff --git a/drivers/net/wireless/libertas/cmdresp.c b/drivers/net/wireless/libertas/cmdresp.c index cbd28ee7c5b8ef26babd098154950a9270ad10a0..6a43de772aa043ea766cc335608a898a50a69f11 100644 --- a/drivers/net/wireless/libertas/cmdresp.c +++ b/drivers/net/wireless/libertas/cmdresp.c @@ -799,7 +799,7 @@ int lbs_process_rx_command(struct lbs_private *priv) } /* Store the response code to cur_cmd_retcode. */ - adapter->cur_cmd_retcode = result;; + adapter->cur_cmd_retcode = result; if (respcmd == CMD_RET(CMD_802_11_PS_MODE)) { struct cmd_ds_802_11_ps_mode *psmode = &resp->params.psmode; @@ -880,11 +880,22 @@ int lbs_process_rx_command(struct lbs_private *priv) goto done; } - spin_unlock_irqrestore(&adapter->driver_lock, flags); - - ret = handle_cmd_response(respcmd, resp, priv); - - spin_lock_irqsave(&adapter->driver_lock, flags); + if (adapter->cur_cmd->pdata_size) { + struct cmd_ds_gen *r = (struct cmd_ds_gen *)resp; + u16 sz = cpu_to_le16(resp->size); + if (sz > *adapter->cur_cmd->pdata_size) { + lbs_pr_err("response 0x%04x doesn't fit into " + "buffer (%d > %d)\n", respcmd, + sz, *adapter->cur_cmd->pdata_size); + sz = *adapter->cur_cmd->pdata_size; + } + memcpy(adapter->cur_cmd->pdata_buf, r->cmdresp, sz); + *adapter->cur_cmd->pdata_size = sz; + } else { + spin_unlock_irqrestore(&adapter->driver_lock, flags); + ret = handle_cmd_response(respcmd, resp, priv); + spin_lock_irqsave(&adapter->driver_lock, flags); + } if (adapter->cur_cmd) { /* Clean up and Put current command back to cmdfreeq */ __lbs_cleanup_and_insert_cmd(priv, adapter->cur_cmd); diff --git a/drivers/net/wireless/libertas/decl.h b/drivers/net/wireless/libertas/decl.h index 0856cc96f513fc4f5228708c6f42149dcb3ff30b..6f47ff089622bac4baa57f17c731369c13987e31 100644 --- a/drivers/net/wireless/libertas/decl.h +++ b/drivers/net/wireless/libertas/decl.h @@ -16,6 +16,7 @@ struct lbs_adapter; struct sk_buff; struct net_device; struct cmd_ctrl_node; +struct cmd_ds_command; int lbs_set_mac_packet_filter(struct lbs_private *priv); @@ -23,6 +24,11 @@ void lbs_send_tx_feedback(struct lbs_private *priv); int lbs_free_cmd_buffer(struct lbs_private *priv); +int lbs_cmd(struct lbs_private *priv, + u16 command, + void *cmd, int cmd_size, + void *resp, int *resp_size); + int lbs_prepare_and_send_command(struct lbs_private *priv, u16 cmd_no, u16 cmd_action, diff --git a/drivers/net/wireless/libertas/hostcmd.h b/drivers/net/wireless/libertas/hostcmd.h index f096d995194a00628554bca169eddd5a44efd7f5..be69ae6529232309634dc79f6a4a6a38206ff518 100644 --- a/drivers/net/wireless/libertas/hostcmd.h +++ b/drivers/net/wireless/libertas/hostcmd.h @@ -66,13 +66,13 @@ struct rxpd { }; struct cmd_ctrl_node { - /* CMD link list */ struct list_head list; - /*CMD wait option: wait for finish or no wait */ + /* wait for finish or not */ u16 wait_option; - /* command parameter */ + /* command response */ void *pdata_buf; - /*command data */ + int *pdata_size; + /* command data */ u8 *bufvirtualaddr; /* wait queue */ u16 cmdwaitqwoken; @@ -100,9 +100,12 @@ struct cmd_ds_gen { __le16 size; __le16 seqnum; __le16 result; + void *cmdresp[0]; }; #define S_DS_GEN sizeof(struct cmd_ds_gen) + + /* * Define data structure for CMD_GET_HW_SPEC * This structure defines the response for the GET_HW_SPEC command