2007-12-05 16:57:54

by Holger Schurig

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

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 <[email protected]>

---

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 =3D sizeof(struct cmd_ds_802_11_bg_scan_query_rsp) + 1024;
struct cmd_ds_802_11_bg_scan_query_rsp *rsp =3D kzalloc(rsp_size, GFP_K=
ERNEL)

struct cmd_ds_802_11_bg_scan_query cmd;
cmd.flush =3D 0;

/* After calling lbs_cmd(), rsp_size contains the size of the actual
* firmware response. Use it when you have variable results */
res =3D lbs_cmd(priv, CMD_802_11_BG_SCAN_QUERY, &cmd, sizeof(cmd), rsp,=
&rsp_size);
if (res =3D=3D 0)
printk("num_sets %d, resp_size %d\n", resp->num_sets, resp_size);


Index: wireless-2.6/drivers/net/wireless/libertas/decl.h
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
--- wireless-2.6.orig/drivers/net/wireless/libertas/decl.h 2007-12-05 1=
8: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;
=20
int lbs_set_mac_packet_filter(struct lbs_private *priv);
=20
@@ -23,6 +24,11 @@ void lbs_send_tx_feedback(struct lbs_pri
=20
int lbs_free_cmd_buffer(struct lbs_private *priv);
=20
+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
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
--- wireless-2.6.orig/drivers/net/wireless/libertas/hostcmd.h 2007-12-0=
5 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 {
};
=20
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];
};
=20
#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
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
--- wireless-2.6.orig/drivers/net/wireless/libertas/cmdresp.c 2007-12-0=
5 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
}
=20
/* Store the response code to cur_cmd_retcode. */
- adapter->cur_cmd_retcode =3D result;;
+ adapter->cur_cmd_retcode =3D result;
=20
if (respcmd =3D=3D CMD_RET(CMD_802_11_PS_MODE)) {
struct cmd_ds_802_11_ps_mode *psmode =3D &resp->params.psmode;
@@ -880,11 +880,22 @@ int lbs_process_rx_command(struct lbs_pr
goto done;
}
=20
- spin_unlock_irqrestore(&adapter->driver_lock, flags);
-
- ret =3D handle_cmd_response(respcmd, resp, priv);
-
- spin_lock_irqsave(&adapter->driver_lock, flags);
+ if (adapter->cur_cmd->pdata_size) {
+ struct cmd_ds_gen *r =3D (struct cmd_ds_gen *)resp;
+ u16 sz =3D 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 =3D *adapter->cur_cmd->pdata_size;
+ }
+ memcpy(adapter->cur_cmd->pdata_buf, r->cmdresp, sz);
+ *adapter->cur_cmd->pdata_size =3D sz;
+ } else {
+ spin_unlock_irqrestore(&adapter->driver_lock, flags);
+ ret =3D 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
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
--- 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:1=
0.000000000 +0100
@@ -2007,3 +2007,99 @@ void lbs_ps_confirm_sleep(struct lbs_pri
=20
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, se=
quence
+ * 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 fi=
rmware
+ * 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 =3D priv->adapter;
+ struct cmd_ctrl_node *cmdnode;
+ struct cmd_ds_gen *cmdptr;
+ unsigned long flags;
+ int ret =3D 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 =3D -1;
+ goto done;
+ }
+
+ if (adapter->surpriseremoved) {
+ lbs_deb_host("PREP_CMD: card removed\n");
+ ret =3D -1;
+ goto done;
+ }
+
+ cmdnode =3D lbs_get_cmd_ctrl_node(priv);
+
+ if (cmdnode =3D=3D NULL) {
+ lbs_deb_host("PREP_CMD: cmdnode is NULL\n");
+
+ /* Wake up main thread to execute next command */
+ wake_up_interruptible(&priv->waitq);
+ ret =3D -1;
+ goto done;
+ }
+
+ cmdptr =3D (struct cmd_ds_gen *)cmdnode->bufvirtualaddr;
+ cmdnode->wait_option =3D CMD_OPTION_WAITFORRSP;
+ cmdnode->pdata_buf =3D rsp;
+ cmdnode->pdata_size =3D rsp_size;
+
+ /* Set sequence number, clean result, move to buffer */
+ adapter->seqnum++;
+ cmdptr->command =3D cpu_to_le16(command);
+ cmdptr->size =3D cmd_size + S_DS_GEN;
+ cmdptr->seqnum =3D cpu_to_le16(adapter->seqnum);
+ cmdptr->result =3D 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 =3D 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 =3D 0;
+ ret =3D -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);
+
+

--=20
M&N Solutions GmbH Ein Unternehmen der Datagroup AG
Holger Schurig
Raiffeisenstr. 10 ACHTUNG: die Stra=DFe hat sich ge=E4ndert
61191 Rosbach
Tel: 06003/9141-15 Fax 06003/9141-49
http://www.mn-solutions.de/

Handelsregister Friedberg, HRB 5903
Gesch=E4ftsf=FChrer: H.Herzig, P.Schrittenlocher