Search Linux Wireless

[PATCH 5/5] libertas: handy function to call firmware commands

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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

[Index of Archives]     [Linux Host AP]     [ATH6KL]     [Linux Bluetooth]     [Linux Netdev]     [Kernel Newbies]     [Linux Kernel]     [IDE]     [Security]     [Git]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux ATA RAID]     [Samba]     [Device Mapper]
  Powered by Linux