Using an arbitrary firmware command was actually very painful. One had to change big switch() statements in cmd.c, cmdresp.c, add structs to the big union in "struct cmd_ds_command" and add the define for the CMD_802_11_xxx to the proper place. With this function, this is now much easier. For now, it implements a blocking (a.k.a. CMD_OPTION_WAITFORRSP) way where one deals directly with command requests and response buffers. You can do everything in one place: Signed-off-by: Holger Schurig <hs4233@xxxxxxxxxxxxxxxxxxxx> --- Example of how to get background scan results: /* You need still to define something :-) */ #define CMD_802_11_BG_SCAN_QUERY 0x006c struct cmd_ds_802_11_bg_scan_query { u8 flush; }; struct cmd_ds_802_11_bg_scan_query_rsp { __le32 report_cond; __le16 bss_size; u8 num_sets; u8 bss[0]; }; /* set maximum size of response */ int rsp_size = sizeof(struct cmd_ds_802_11_bg_scan_query_rsp) + 1024; struct cmd_ds_802_11_bg_scan_query_rsp *rsp = kzalloc(rsp_size, GFP_KERNEL) struct cmd_ds_802_11_bg_scan_query cmd; cmd.flush = 0; /* After calling lbs_cmd(), rsp_size contains the size of the actual * firmware response. Use it when you have variable results */ res = lbs_cmd(priv, CMD_802_11_BG_SCAN_QUERY, &cmd, sizeof(cmd), rsp, &rsp_size); if (res == 0) printk("num_sets %d, resp_size %d\n", resp->num_sets, resp_size); Index: wireless-2.6/drivers/net/wireless/libertas/decl.h =================================================================== --- wireless-2.6.orig/drivers/net/wireless/libertas/decl.h 2007-12-05 18:10:38.000000000 +0100 +++ wireless-2.6/drivers/net/wireless/libertas/decl.h 2007-12-05 18:11:44.000000000 +0100 @@ -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_pri 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, Index: wireless-2.6/drivers/net/wireless/libertas/hostcmd.h =================================================================== --- wireless-2.6.orig/drivers/net/wireless/libertas/hostcmd.h 2007-12-05 18:10:38.000000000 +0100 +++ wireless-2.6/drivers/net/wireless/libertas/hostcmd.h 2007-12-05 18:11:44.000000000 +0100 @@ -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 Index: wireless-2.6/drivers/net/wireless/libertas/cmdresp.c =================================================================== --- wireless-2.6.orig/drivers/net/wireless/libertas/cmdresp.c 2007-12-05 18:10:38.000000000 +0100 +++ wireless-2.6/drivers/net/wireless/libertas/cmdresp.c 2007-12-05 18:11:44.000000000 +0100 @@ -799,7 +799,7 @@ int lbs_process_rx_command(struct lbs_pr } /* 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_pr 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); Index: wireless-2.6/drivers/net/wireless/libertas/cmd.c =================================================================== --- wireless-2.6.orig/drivers/net/wireless/libertas/cmd.c 2007-12-05 18:11:27.000000000 +0100 +++ wireless-2.6/drivers/net/wireless/libertas/cmd.c 2007-12-05 18:13:10.000000000 +0100 @@ -2007,3 +2007,99 @@ void lbs_ps_confirm_sleep(struct lbs_pri 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); + + -- M&N Solutions GmbH Ein Unternehmen der Datagroup AG Holger Schurig Raiffeisenstr. 10 ACHTUNG: die Straße hat sich geändert 61191 Rosbach Tel: 06003/9141-15 Fax 06003/9141-49 http://www.mn-solutions.de/ Handelsregister Friedberg, HRB 5903 Geschäftsführer: H.Herzig, P.Schrittenlocher - To unsubscribe from this list: send the line "unsubscribe linux-wireless" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html