blob: 4946bce232632d01d5328a51fea33b045c51b303 [file] [log] [blame]
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001/**
2 * This file contains the handling of command.
3 * It prepares command and sends it to firmware when it is ready.
4 */
5
Holger Schurig7919b892008-04-01 14:50:43 +02006#include <linux/kfifo.h>
Holger Schurige93156e2009-10-22 15:30:58 +02007#include <linux/sched.h>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +09008#include <linux/slab.h>
Dan Williamsa45b6f42010-07-27 12:54:34 -07009#include <linux/if_arp.h>
Holger Schurige93156e2009-10-22 15:30:58 +020010
Marcelo Tosatti876c9d32007-02-10 12:25:27 -020011#include "decl.h"
Kiran Divekare86dc1c2010-06-14 22:01:26 +053012#include "cfg.h"
Dan Williams6e66f032007-12-11 12:42:16 -050013#include "cmd.h"
Marcelo Tosatti876c9d32007-02-10 12:25:27 -020014
Dan Williams9fb76632010-07-27 12:55:21 -070015#define CAL_NF(nf) ((s32)(-(s32)(nf)))
16#define CAL_RSSI(snr, nf) ((s32)((s32)(snr) + CAL_NF(nf)))
Holger Schurige93156e2009-10-22 15:30:58 +020017
David Woodhouse2fd6cfe2007-12-11 17:44:10 -050018static struct cmd_ctrl_node *lbs_get_cmd_ctrl_node(struct lbs_private *priv);
Holger Schurig0d61d042007-12-05 17:58:06 +010019
Marcelo Tosatti876c9d32007-02-10 12:25:27 -020020/**
Holger Schurig8db4a2b2008-03-19 10:11:00 +010021 * @brief Simple callback that copies response back into command
22 *
23 * @param priv A pointer to struct lbs_private structure
24 * @param extra A pointer to the original command structure for which
25 * 'resp' is a response
26 * @param resp A pointer to the command response
27 *
28 * @return 0 on success, error on failure
29 */
30int lbs_cmd_copyback(struct lbs_private *priv, unsigned long extra,
31 struct cmd_header *resp)
32{
33 struct cmd_header *buf = (void *)extra;
34 uint16_t copy_len;
35
36 copy_len = min(le16_to_cpu(buf->size), le16_to_cpu(resp->size));
37 memcpy(buf, resp, copy_len);
38 return 0;
39}
40EXPORT_SYMBOL_GPL(lbs_cmd_copyback);
41
42/**
43 * @brief Simple callback that ignores the result. Use this if
44 * you just want to send a command to the hardware, but don't
45 * care for the result.
46 *
47 * @param priv ignored
48 * @param extra ignored
49 * @param resp ignored
50 *
51 * @return 0 for success
52 */
53static int lbs_cmd_async_callback(struct lbs_private *priv, unsigned long extra,
54 struct cmd_header *resp)
55{
56 return 0;
57}
58
59
60/**
Dan Williams852e1f22007-12-10 15:24:47 -050061 * @brief Checks whether a command is allowed in Power Save mode
Marcelo Tosatti876c9d32007-02-10 12:25:27 -020062 *
63 * @param command the command ID
Dan Williams852e1f22007-12-10 15:24:47 -050064 * @return 1 if allowed, 0 if not allowed
Marcelo Tosatti876c9d32007-02-10 12:25:27 -020065 */
Dan Williams852e1f22007-12-10 15:24:47 -050066static u8 is_command_allowed_in_ps(u16 cmd)
Marcelo Tosatti876c9d32007-02-10 12:25:27 -020067{
Dan Williams852e1f22007-12-10 15:24:47 -050068 switch (cmd) {
69 case CMD_802_11_RSSI:
70 return 1;
Amitkumar Karwar66fceb62010-05-19 03:24:38 -070071 case CMD_802_11_HOST_SLEEP_CFG:
72 return 1;
Dan Williams852e1f22007-12-10 15:24:47 -050073 default:
74 break;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -020075 }
Marcelo Tosatti876c9d32007-02-10 12:25:27 -020076 return 0;
77}
78
Dan Williams6e66f032007-12-11 12:42:16 -050079/**
80 * @brief Updates the hardware details like MAC address and regulatory region
81 *
82 * @param priv A pointer to struct lbs_private structure
83 *
84 * @return 0 on success, error on failure
85 */
86int lbs_update_hw_spec(struct lbs_private *priv)
Marcelo Tosatti876c9d32007-02-10 12:25:27 -020087{
Dan Williams6e66f032007-12-11 12:42:16 -050088 struct cmd_ds_get_hw_spec cmd;
89 int ret = -1;
90 u32 i;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -020091
Holger Schurig9012b282007-05-25 11:27:16 -040092 lbs_deb_enter(LBS_DEB_CMD);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -020093
Dan Williams6e66f032007-12-11 12:42:16 -050094 memset(&cmd, 0, sizeof(cmd));
95 cmd.hdr.size = cpu_to_le16(sizeof(cmd));
96 memcpy(cmd.permanentaddr, priv->current_addr, ETH_ALEN);
David Woodhouse689442d2007-12-12 16:00:42 -050097 ret = lbs_cmd_with_response(priv, CMD_GET_HW_SPEC, &cmd);
Dan Williams6e66f032007-12-11 12:42:16 -050098 if (ret)
99 goto out;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200100
Dan Williams6e66f032007-12-11 12:42:16 -0500101 priv->fwcapinfo = le32_to_cpu(cmd.fwcapinfo);
Dan Williams6e66f032007-12-11 12:42:16 -0500102
Holger Schurigdac10a92008-01-16 15:55:22 +0100103 /* The firmware release is in an interesting format: the patch
104 * level is in the most significant nibble ... so fix that: */
105 priv->fwrelease = le32_to_cpu(cmd.fwrelease);
106 priv->fwrelease = (priv->fwrelease << 8) |
107 (priv->fwrelease >> 24 & 0xff);
108
109 /* Some firmware capabilities:
110 * CF card firmware 5.0.16p0: cap 0x00000303
111 * USB dongle firmware 5.110.17p2: cap 0x00000303
112 */
Johannes Berge1749612008-10-27 15:59:26 -0700113 lbs_pr_info("%pM, fw %u.%u.%up%u, cap 0x%08x\n",
114 cmd.permanentaddr,
Holger Schurigdac10a92008-01-16 15:55:22 +0100115 priv->fwrelease >> 24 & 0xff,
116 priv->fwrelease >> 16 & 0xff,
117 priv->fwrelease >> 8 & 0xff,
118 priv->fwrelease & 0xff,
119 priv->fwcapinfo);
Dan Williams6e66f032007-12-11 12:42:16 -0500120 lbs_deb_cmd("GET_HW_SPEC: hardware interface 0x%x, hardware spec 0x%04x\n",
121 cmd.hwifversion, cmd.version);
122
123 /* Clamp region code to 8-bit since FW spec indicates that it should
124 * only ever be 8-bit, even though the field size is 16-bit. Some firmware
125 * returns non-zero high 8 bits here.
Marek Vasut15483992009-07-16 19:19:53 +0200126 *
127 * Firmware version 4.0.102 used in CF8381 has region code shifted. We
128 * need to check for this problem and handle it properly.
Dan Williams6e66f032007-12-11 12:42:16 -0500129 */
Marek Vasut15483992009-07-16 19:19:53 +0200130 if (MRVL_FW_MAJOR_REV(priv->fwrelease) == MRVL_FW_V4)
131 priv->regioncode = (le16_to_cpu(cmd.regioncode) >> 8) & 0xFF;
132 else
133 priv->regioncode = le16_to_cpu(cmd.regioncode) & 0xFF;
Dan Williams6e66f032007-12-11 12:42:16 -0500134
135 for (i = 0; i < MRVDRV_MAX_REGION_CODE; i++) {
136 /* use the region code to search for the index */
137 if (priv->regioncode == lbs_region_code_to_index[i])
138 break;
139 }
140
141 /* if it's unidentified region code, use the default (USA) */
142 if (i >= MRVDRV_MAX_REGION_CODE) {
143 priv->regioncode = 0x10;
144 lbs_pr_info("unidentified region code; using the default (USA)\n");
145 }
146
147 if (priv->current_addr[0] == 0xff)
148 memmove(priv->current_addr, cmd.permanentaddr, ETH_ALEN);
149
150 memcpy(priv->dev->dev_addr, priv->current_addr, ETH_ALEN);
151 if (priv->mesh_dev)
152 memcpy(priv->mesh_dev->dev_addr, priv->current_addr, ETH_ALEN);
153
Dan Williams6e66f032007-12-11 12:42:16 -0500154out:
Holger Schurig9012b282007-05-25 11:27:16 -0400155 lbs_deb_leave(LBS_DEB_CMD);
Dan Williams6e66f032007-12-11 12:42:16 -0500156 return ret;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200157}
158
Amitkumar Karwar66fceb62010-05-19 03:24:38 -0700159static int lbs_ret_host_sleep_cfg(struct lbs_private *priv, unsigned long dummy,
160 struct cmd_header *resp)
161{
162 lbs_deb_enter(LBS_DEB_CMD);
Amitkumar Karwar13118432010-07-08 06:43:48 +0530163 if (priv->is_host_sleep_activated) {
Amitkumar Karwar66fceb62010-05-19 03:24:38 -0700164 priv->is_host_sleep_configured = 0;
165 if (priv->psstate == PS_STATE_FULL_POWER) {
166 priv->is_host_sleep_activated = 0;
167 wake_up_interruptible(&priv->host_sleep_q);
168 }
169 } else {
170 priv->is_host_sleep_configured = 1;
171 }
172 lbs_deb_leave(LBS_DEB_CMD);
173 return 0;
174}
175
Anna Neal582c1b52008-10-20 16:46:56 -0700176int lbs_host_sleep_cfg(struct lbs_private *priv, uint32_t criteria,
177 struct wol_config *p_wol_config)
David Woodhouse6ce4fd22007-12-12 15:19:29 -0500178{
179 struct cmd_ds_host_sleep cmd_config;
180 int ret;
181
David Woodhouse9fae8992007-12-15 03:46:44 -0500182 cmd_config.hdr.size = cpu_to_le16(sizeof(cmd_config));
David Woodhouse6ce4fd22007-12-12 15:19:29 -0500183 cmd_config.criteria = cpu_to_le32(criteria);
David Woodhouse506e9022007-12-12 20:06:06 -0500184 cmd_config.gpio = priv->wol_gpio;
185 cmd_config.gap = priv->wol_gap;
David Woodhouse6ce4fd22007-12-12 15:19:29 -0500186
Anna Neal582c1b52008-10-20 16:46:56 -0700187 if (p_wol_config != NULL)
188 memcpy((uint8_t *)&cmd_config.wol_conf, (uint8_t *)p_wol_config,
189 sizeof(struct wol_config));
190 else
191 cmd_config.wol_conf.action = CMD_ACT_ACTION_NONE;
192
Amitkumar Karwar66fceb62010-05-19 03:24:38 -0700193 ret = __lbs_cmd(priv, CMD_802_11_HOST_SLEEP_CFG, &cmd_config.hdr,
194 le16_to_cpu(cmd_config.hdr.size),
195 lbs_ret_host_sleep_cfg, 0);
David Woodhouse506e9022007-12-12 20:06:06 -0500196 if (!ret) {
Amitkumar Karwar66fceb62010-05-19 03:24:38 -0700197 if (p_wol_config)
Anna Neal582c1b52008-10-20 16:46:56 -0700198 memcpy((uint8_t *) p_wol_config,
199 (uint8_t *)&cmd_config.wol_conf,
200 sizeof(struct wol_config));
David Woodhouse506e9022007-12-12 20:06:06 -0500201 } else {
David Woodhouse6ce4fd22007-12-12 15:19:29 -0500202 lbs_pr_info("HOST_SLEEP_CFG failed %d\n", ret);
David Woodhouse6ce4fd22007-12-12 15:19:29 -0500203 }
David Woodhouse506e9022007-12-12 20:06:06 -0500204
David Woodhouse6ce4fd22007-12-12 15:19:29 -0500205 return ret;
206}
207EXPORT_SYMBOL_GPL(lbs_host_sleep_cfg);
208
Dan Williams0bb64082010-07-27 13:08:08 -0700209/**
210 * @brief Sets the Power Save mode
211 *
212 * @param priv A pointer to struct lbs_private structure
213 * @param cmd_action The Power Save operation (PS_MODE_ACTION_ENTER_PS or
214 * PS_MODE_ACTION_EXIT_PS)
215 * @param block Whether to block on a response or not
216 *
217 * @return 0 on success, error on failure
218 */
219int lbs_set_ps_mode(struct lbs_private *priv, u16 cmd_action, bool block)
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200220{
Dan Williams0bb64082010-07-27 13:08:08 -0700221 struct cmd_ds_802_11_ps_mode cmd;
222 int ret = 0;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200223
Holger Schurig9012b282007-05-25 11:27:16 -0400224 lbs_deb_enter(LBS_DEB_CMD);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200225
Dan Williams0bb64082010-07-27 13:08:08 -0700226 memset(&cmd, 0, sizeof(cmd));
227 cmd.hdr.size = cpu_to_le16(sizeof(cmd));
228 cmd.action = cpu_to_le16(cmd_action);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200229
Dan Williams0bb64082010-07-27 13:08:08 -0700230 if (cmd_action == PS_MODE_ACTION_ENTER_PS) {
231 lbs_deb_cmd("PS_MODE: action ENTER_PS\n");
232 cmd.multipledtim = cpu_to_le16(1); /* Default DTIM multiple */
233 } else if (cmd_action == PS_MODE_ACTION_EXIT_PS) {
234 lbs_deb_cmd("PS_MODE: action EXIT_PS\n");
235 } else {
236 /* We don't handle CONFIRM_SLEEP here because it needs to
237 * be fastpathed to the firmware.
238 */
239 lbs_deb_cmd("PS_MODE: unknown action 0x%X\n", cmd_action);
240 ret = -EOPNOTSUPP;
241 goto out;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200242 }
243
Dan Williams0bb64082010-07-27 13:08:08 -0700244 if (block)
245 ret = lbs_cmd_with_response(priv, CMD_802_11_PS_MODE, &cmd);
246 else
247 lbs_cmd_async(priv, CMD_802_11_PS_MODE, &cmd.hdr, sizeof (cmd));
248
249out:
250 lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret);
251 return ret;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200252}
253
David Woodhouse3fbe1042007-12-17 23:48:31 -0500254int lbs_cmd_802_11_sleep_params(struct lbs_private *priv, uint16_t cmd_action,
255 struct sleep_params *sp)
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200256{
David Woodhouse3fbe1042007-12-17 23:48:31 -0500257 struct cmd_ds_802_11_sleep_params cmd;
258 int ret;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200259
Holger Schurig9012b282007-05-25 11:27:16 -0400260 lbs_deb_enter(LBS_DEB_CMD);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200261
Dan Williams0aef64d2007-08-02 11:31:18 -0400262 if (cmd_action == CMD_ACT_GET) {
David Woodhouse3fbe1042007-12-17 23:48:31 -0500263 memset(&cmd, 0, sizeof(cmd));
264 } else {
265 cmd.error = cpu_to_le16(sp->sp_error);
266 cmd.offset = cpu_to_le16(sp->sp_offset);
267 cmd.stabletime = cpu_to_le16(sp->sp_stabletime);
268 cmd.calcontrol = sp->sp_calcontrol;
269 cmd.externalsleepclk = sp->sp_extsleepclk;
270 cmd.reserved = cpu_to_le16(sp->sp_reserved);
271 }
272 cmd.hdr.size = cpu_to_le16(sizeof(cmd));
273 cmd.action = cpu_to_le16(cmd_action);
274
275 ret = lbs_cmd_with_response(priv, CMD_802_11_SLEEP_PARAMS, &cmd);
276
277 if (!ret) {
278 lbs_deb_cmd("error 0x%x, offset 0x%x, stabletime 0x%x, "
279 "calcontrol 0x%x extsleepclk 0x%x\n",
280 le16_to_cpu(cmd.error), le16_to_cpu(cmd.offset),
281 le16_to_cpu(cmd.stabletime), cmd.calcontrol,
282 cmd.externalsleepclk);
283
284 sp->sp_error = le16_to_cpu(cmd.error);
285 sp->sp_offset = le16_to_cpu(cmd.offset);
286 sp->sp_stabletime = le16_to_cpu(cmd.stabletime);
287 sp->sp_calcontrol = cmd.calcontrol;
288 sp->sp_extsleepclk = cmd.externalsleepclk;
289 sp->sp_reserved = le16_to_cpu(cmd.reserved);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200290 }
291
David Woodhouse3fbe1042007-12-17 23:48:31 -0500292 lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200293 return 0;
294}
295
Amitkumar Karwar49125452009-09-30 20:04:38 -0700296static int lbs_wait_for_ds_awake(struct lbs_private *priv)
297{
298 int ret = 0;
299
300 lbs_deb_enter(LBS_DEB_CMD);
301
302 if (priv->is_deep_sleep) {
303 if (!wait_event_interruptible_timeout(priv->ds_awake_q,
304 !priv->is_deep_sleep, (10 * HZ))) {
305 lbs_pr_err("ds_awake_q: timer expired\n");
306 ret = -1;
307 }
308 }
309
310 lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret);
311 return ret;
312}
313
314int lbs_set_deep_sleep(struct lbs_private *priv, int deep_sleep)
315{
316 int ret = 0;
317
318 lbs_deb_enter(LBS_DEB_CMD);
319
320 if (deep_sleep) {
321 if (priv->is_deep_sleep != 1) {
322 lbs_deb_cmd("deep sleep: sleep\n");
323 BUG_ON(!priv->enter_deep_sleep);
324 ret = priv->enter_deep_sleep(priv);
325 if (!ret) {
326 netif_stop_queue(priv->dev);
327 netif_carrier_off(priv->dev);
328 }
329 } else {
330 lbs_pr_err("deep sleep: already enabled\n");
331 }
332 } else {
333 if (priv->is_deep_sleep) {
334 lbs_deb_cmd("deep sleep: wakeup\n");
335 BUG_ON(!priv->exit_deep_sleep);
336 ret = priv->exit_deep_sleep(priv);
337 if (!ret) {
338 ret = lbs_wait_for_ds_awake(priv);
339 if (ret)
340 lbs_pr_err("deep sleep: wakeup"
341 "failed\n");
342 }
343 }
344 }
345
346 lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret);
347 return ret;
348}
349
Amitkumar Karwar13118432010-07-08 06:43:48 +0530350static int lbs_ret_host_sleep_activate(struct lbs_private *priv,
351 unsigned long dummy,
352 struct cmd_header *cmd)
353{
354 lbs_deb_enter(LBS_DEB_FW);
355 priv->is_host_sleep_activated = 1;
356 wake_up_interruptible(&priv->host_sleep_q);
357 lbs_deb_leave(LBS_DEB_FW);
358 return 0;
359}
360
361int lbs_set_host_sleep(struct lbs_private *priv, int host_sleep)
362{
363 struct cmd_header cmd;
364 int ret = 0;
365 uint32_t criteria = EHS_REMOVE_WAKEUP;
366
367 lbs_deb_enter(LBS_DEB_CMD);
368
369 if (host_sleep) {
370 if (priv->is_host_sleep_activated != 1) {
371 memset(&cmd, 0, sizeof(cmd));
372 ret = lbs_host_sleep_cfg(priv, priv->wol_criteria,
373 (struct wol_config *)NULL);
374 if (ret) {
375 lbs_pr_info("Host sleep configuration failed: "
376 "%d\n", ret);
377 return ret;
378 }
379 if (priv->psstate == PS_STATE_FULL_POWER) {
380 ret = __lbs_cmd(priv,
381 CMD_802_11_HOST_SLEEP_ACTIVATE,
382 &cmd,
383 sizeof(cmd),
384 lbs_ret_host_sleep_activate, 0);
385 if (ret)
386 lbs_pr_info("HOST_SLEEP_ACTIVATE "
387 "failed: %d\n", ret);
388 }
389
390 if (!wait_event_interruptible_timeout(
391 priv->host_sleep_q,
392 priv->is_host_sleep_activated,
393 (10 * HZ))) {
394 lbs_pr_err("host_sleep_q: timer expired\n");
395 ret = -1;
396 }
397 } else {
398 lbs_pr_err("host sleep: already enabled\n");
399 }
400 } else {
401 if (priv->is_host_sleep_activated)
402 ret = lbs_host_sleep_cfg(priv, criteria,
403 (struct wol_config *)NULL);
404 }
405
406 return ret;
407}
408
Dan Williams39fcf7a2008-09-10 12:49:00 -0400409/**
410 * @brief Set an SNMP MIB value
411 *
412 * @param priv A pointer to struct lbs_private structure
413 * @param oid The OID to set in the firmware
414 * @param val Value to set the OID to
415 *
416 * @return 0 on success, error on failure
417 */
418int lbs_set_snmp_mib(struct lbs_private *priv, u32 oid, u16 val)
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200419{
Dan Williams39fcf7a2008-09-10 12:49:00 -0400420 struct cmd_ds_802_11_snmp_mib cmd;
421 int ret;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200422
Holger Schurig9012b282007-05-25 11:27:16 -0400423 lbs_deb_enter(LBS_DEB_CMD);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200424
Dan Williams39fcf7a2008-09-10 12:49:00 -0400425 memset(&cmd, 0, sizeof (cmd));
426 cmd.hdr.size = cpu_to_le16(sizeof(cmd));
427 cmd.action = cpu_to_le16(CMD_ACT_SET);
428 cmd.oid = cpu_to_le16((u16) oid);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200429
Dan Williams39fcf7a2008-09-10 12:49:00 -0400430 switch (oid) {
431 case SNMP_MIB_OID_BSS_TYPE:
432 cmd.bufsize = cpu_to_le16(sizeof(u8));
Holger Schurigfef06402009-10-22 15:30:59 +0200433 cmd.value[0] = val;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200434 break;
Dan Williams39fcf7a2008-09-10 12:49:00 -0400435 case SNMP_MIB_OID_11D_ENABLE:
436 case SNMP_MIB_OID_FRAG_THRESHOLD:
437 case SNMP_MIB_OID_RTS_THRESHOLD:
438 case SNMP_MIB_OID_SHORT_RETRY_LIMIT:
439 case SNMP_MIB_OID_LONG_RETRY_LIMIT:
440 cmd.bufsize = cpu_to_le16(sizeof(u16));
441 *((__le16 *)(&cmd.value)) = cpu_to_le16(val);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200442 break;
443 default:
Dan Williams39fcf7a2008-09-10 12:49:00 -0400444 lbs_deb_cmd("SNMP_CMD: (set) unhandled OID 0x%x\n", oid);
445 ret = -EINVAL;
446 goto out;
447 }
448
449 lbs_deb_cmd("SNMP_CMD: (set) oid 0x%x, oid size 0x%x, value 0x%x\n",
450 le16_to_cpu(cmd.oid), le16_to_cpu(cmd.bufsize), val);
451
452 ret = lbs_cmd_with_response(priv, CMD_802_11_SNMP_MIB, &cmd);
453
454out:
455 lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret);
456 return ret;
457}
458
459/**
460 * @brief Get an SNMP MIB value
461 *
462 * @param priv A pointer to struct lbs_private structure
463 * @param oid The OID to retrieve from the firmware
464 * @param out_val Location for the returned value
465 *
466 * @return 0 on success, error on failure
467 */
468int lbs_get_snmp_mib(struct lbs_private *priv, u32 oid, u16 *out_val)
469{
470 struct cmd_ds_802_11_snmp_mib cmd;
471 int ret;
472
473 lbs_deb_enter(LBS_DEB_CMD);
474
475 memset(&cmd, 0, sizeof (cmd));
476 cmd.hdr.size = cpu_to_le16(sizeof(cmd));
477 cmd.action = cpu_to_le16(CMD_ACT_GET);
478 cmd.oid = cpu_to_le16(oid);
479
480 ret = lbs_cmd_with_response(priv, CMD_802_11_SNMP_MIB, &cmd);
481 if (ret)
482 goto out;
483
484 switch (le16_to_cpu(cmd.bufsize)) {
485 case sizeof(u8):
Holger Schurigfef06402009-10-22 15:30:59 +0200486 *out_val = cmd.value[0];
Dan Williams39fcf7a2008-09-10 12:49:00 -0400487 break;
488 case sizeof(u16):
489 *out_val = le16_to_cpu(*((__le16 *)(&cmd.value)));
490 break;
491 default:
492 lbs_deb_cmd("SNMP_CMD: (get) unhandled OID 0x%x size %d\n",
493 oid, le16_to_cpu(cmd.bufsize));
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200494 break;
495 }
496
Dan Williams39fcf7a2008-09-10 12:49:00 -0400497out:
498 lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret);
499 return ret;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200500}
501
Dan Williams87c8c722008-08-19 15:15:35 -0400502/**
503 * @brief Get the min, max, and current TX power
504 *
505 * @param priv A pointer to struct lbs_private structure
506 * @param curlevel Current power level in dBm
507 * @param minlevel Minimum supported power level in dBm (optional)
508 * @param maxlevel Maximum supported power level in dBm (optional)
509 *
510 * @return 0 on success, error on failure
511 */
512int lbs_get_tx_power(struct lbs_private *priv, s16 *curlevel, s16 *minlevel,
513 s16 *maxlevel)
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200514{
Dan Williams87c8c722008-08-19 15:15:35 -0400515 struct cmd_ds_802_11_rf_tx_power cmd;
516 int ret;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200517
Holger Schurig9012b282007-05-25 11:27:16 -0400518 lbs_deb_enter(LBS_DEB_CMD);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200519
Dan Williams87c8c722008-08-19 15:15:35 -0400520 memset(&cmd, 0, sizeof(cmd));
521 cmd.hdr.size = cpu_to_le16(sizeof(cmd));
522 cmd.action = cpu_to_le16(CMD_ACT_GET);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200523
Dan Williams87c8c722008-08-19 15:15:35 -0400524 ret = lbs_cmd_with_response(priv, CMD_802_11_RF_TX_POWER, &cmd);
525 if (ret == 0) {
526 *curlevel = le16_to_cpu(cmd.curlevel);
527 if (minlevel)
Holger Schurig87bf24f2008-10-29 10:35:02 +0100528 *minlevel = cmd.minlevel;
Dan Williams87c8c722008-08-19 15:15:35 -0400529 if (maxlevel)
Holger Schurig87bf24f2008-10-29 10:35:02 +0100530 *maxlevel = cmd.maxlevel;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200531 }
Holger Schurig9012b282007-05-25 11:27:16 -0400532
533 lbs_deb_leave(LBS_DEB_CMD);
Dan Williams87c8c722008-08-19 15:15:35 -0400534 return ret;
535}
536
537/**
538 * @brief Set the TX power
539 *
540 * @param priv A pointer to struct lbs_private structure
541 * @param dbm The desired power level in dBm
542 *
543 * @return 0 on success, error on failure
544 */
545int lbs_set_tx_power(struct lbs_private *priv, s16 dbm)
546{
547 struct cmd_ds_802_11_rf_tx_power cmd;
548 int ret;
549
550 lbs_deb_enter(LBS_DEB_CMD);
551
552 memset(&cmd, 0, sizeof(cmd));
553 cmd.hdr.size = cpu_to_le16(sizeof(cmd));
554 cmd.action = cpu_to_le16(CMD_ACT_SET);
555 cmd.curlevel = cpu_to_le16(dbm);
556
557 lbs_deb_cmd("SET_RF_TX_POWER: %d dBm\n", dbm);
558
559 ret = lbs_cmd_with_response(priv, CMD_802_11_RF_TX_POWER, &cmd);
560
561 lbs_deb_leave(LBS_DEB_CMD);
562 return ret;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200563}
564
Dan Williamsa45b6f42010-07-27 12:54:34 -0700565/**
566 * @brief Enable or disable monitor mode (only implemented on OLPC usb8388 FW)
567 *
568 * @param priv A pointer to struct lbs_private structure
569 * @param enable 1 to enable monitor mode, 0 to disable
570 *
571 * @return 0 on success, error on failure
572 */
573int lbs_set_monitor_mode(struct lbs_private *priv, int enable)
Luis Carlos Cobo965f8bb2007-08-02 13:16:55 -0400574{
Dan Williamsa45b6f42010-07-27 12:54:34 -0700575 struct cmd_ds_802_11_monitor_mode cmd;
576 int ret;
Luis Carlos Cobo965f8bb2007-08-02 13:16:55 -0400577
Dan Williamsa45b6f42010-07-27 12:54:34 -0700578 memset(&cmd, 0, sizeof(cmd));
579 cmd.hdr.size = cpu_to_le16(sizeof(cmd));
580 cmd.action = cpu_to_le16(CMD_ACT_SET);
581 if (enable)
582 cmd.mode = cpu_to_le16(0x1);
Luis Carlos Cobo965f8bb2007-08-02 13:16:55 -0400583
Dan Williamsa45b6f42010-07-27 12:54:34 -0700584 lbs_deb_cmd("SET_MONITOR_MODE: %d\n", enable);
585
586 ret = lbs_cmd_with_response(priv, CMD_802_11_MONITOR_MODE, &cmd);
587 if (ret == 0) {
588 priv->dev->type = enable ? ARPHRD_IEEE80211_RADIOTAP :
589 ARPHRD_ETHER;
Luis Carlos Cobo965f8bb2007-08-02 13:16:55 -0400590 }
591
Dan Williamsa45b6f42010-07-27 12:54:34 -0700592 lbs_deb_leave(LBS_DEB_CMD);
593 return ret;
Luis Carlos Cobo965f8bb2007-08-02 13:16:55 -0400594}
595
Dan Williams2dd4b262007-12-11 16:54:15 -0500596/**
597 * @brief Get the radio channel
598 *
599 * @param priv A pointer to struct lbs_private structure
600 *
601 * @return The channel on success, error on failure
602 */
Holger Schuriga3cbfb02009-10-16 17:33:56 +0200603static int lbs_get_channel(struct lbs_private *priv)
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200604{
Dan Williams2dd4b262007-12-11 16:54:15 -0500605 struct cmd_ds_802_11_rf_channel cmd;
606 int ret = 0;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200607
Holger Schurig8ff12da2007-08-02 11:54:31 -0400608 lbs_deb_enter(LBS_DEB_CMD);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200609
Holger Schurig8d0c7fa2008-04-09 10:23:31 +0200610 memset(&cmd, 0, sizeof(cmd));
Dan Williams2dd4b262007-12-11 16:54:15 -0500611 cmd.hdr.size = cpu_to_le16(sizeof(cmd));
612 cmd.action = cpu_to_le16(CMD_OPT_802_11_RF_CHANNEL_GET);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200613
David Woodhouse689442d2007-12-12 16:00:42 -0500614 ret = lbs_cmd_with_response(priv, CMD_802_11_RF_CHANNEL, &cmd);
Dan Williams2dd4b262007-12-11 16:54:15 -0500615 if (ret)
616 goto out;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200617
Dan Williamscb182a62007-12-11 17:35:51 -0500618 ret = le16_to_cpu(cmd.channel);
619 lbs_deb_cmd("current radio channel is %d\n", ret);
Dan Williams2dd4b262007-12-11 16:54:15 -0500620
621out:
622 lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret);
623 return ret;
624}
625
Holger Schurig73ab1f22008-04-02 16:52:19 +0200626int lbs_update_channel(struct lbs_private *priv)
627{
628 int ret;
629
630 /* the channel in f/w could be out of sync; get the current channel */
631 lbs_deb_enter(LBS_DEB_ASSOC);
632
633 ret = lbs_get_channel(priv);
634 if (ret > 0) {
Holger Schurigc14951f2009-10-22 15:30:50 +0200635 priv->channel = ret;
Holger Schurig73ab1f22008-04-02 16:52:19 +0200636 ret = 0;
637 }
638 lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret);
639 return ret;
640}
641
Dan Williams2dd4b262007-12-11 16:54:15 -0500642/**
643 * @brief Set the radio channel
644 *
645 * @param priv A pointer to struct lbs_private structure
646 * @param channel The desired channel, or 0 to clear a locked channel
647 *
648 * @return 0 on success, error on failure
649 */
650int lbs_set_channel(struct lbs_private *priv, u8 channel)
651{
652 struct cmd_ds_802_11_rf_channel cmd;
Manish Katiyar96d46d52008-10-13 16:22:42 +0530653#ifdef DEBUG
Holger Schurigc14951f2009-10-22 15:30:50 +0200654 u8 old_channel = priv->channel;
Manish Katiyar96d46d52008-10-13 16:22:42 +0530655#endif
Dan Williams2dd4b262007-12-11 16:54:15 -0500656 int ret = 0;
657
658 lbs_deb_enter(LBS_DEB_CMD);
659
Holger Schurig8d0c7fa2008-04-09 10:23:31 +0200660 memset(&cmd, 0, sizeof(cmd));
Dan Williams2dd4b262007-12-11 16:54:15 -0500661 cmd.hdr.size = cpu_to_le16(sizeof(cmd));
662 cmd.action = cpu_to_le16(CMD_OPT_802_11_RF_CHANNEL_SET);
663 cmd.channel = cpu_to_le16(channel);
664
David Woodhouse689442d2007-12-12 16:00:42 -0500665 ret = lbs_cmd_with_response(priv, CMD_802_11_RF_CHANNEL, &cmd);
Dan Williams2dd4b262007-12-11 16:54:15 -0500666 if (ret)
667 goto out;
668
Holger Schurigc14951f2009-10-22 15:30:50 +0200669 priv->channel = (uint8_t) le16_to_cpu(cmd.channel);
Dan Williamscb182a62007-12-11 17:35:51 -0500670 lbs_deb_cmd("channel switch from %d to %d\n", old_channel,
Holger Schurigc14951f2009-10-22 15:30:50 +0200671 priv->channel);
Dan Williams2dd4b262007-12-11 16:54:15 -0500672
673out:
674 lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret);
675 return ret;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200676}
677
Dan Williams9fb76632010-07-27 12:55:21 -0700678/**
679 * @brief Get current RSSI and noise floor
680 *
681 * @param priv A pointer to struct lbs_private structure
682 * @param rssi On successful return, signal level in mBm
683 *
684 * @return The channel on success, error on failure
685 */
686int lbs_get_rssi(struct lbs_private *priv, s8 *rssi, s8 *nf)
687{
688 struct cmd_ds_802_11_rssi cmd;
689 int ret = 0;
690
691 lbs_deb_enter(LBS_DEB_CMD);
692
693 BUG_ON(rssi == NULL);
694 BUG_ON(nf == NULL);
695
696 memset(&cmd, 0, sizeof(cmd));
697 cmd.hdr.size = cpu_to_le16(sizeof(cmd));
698 /* Average SNR over last 8 beacons */
699 cmd.n_or_snr = cpu_to_le16(8);
700
701 ret = lbs_cmd_with_response(priv, CMD_802_11_RSSI, &cmd);
702 if (ret == 0) {
703 *nf = CAL_NF(le16_to_cpu(cmd.nf));
704 *rssi = CAL_RSSI(le16_to_cpu(cmd.n_or_snr), le16_to_cpu(cmd.nf));
705 }
706
707 lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret);
708 return ret;
709}
710
Dan Williamscc4b9d32010-07-27 12:56:05 -0700711/**
712 * @brief Send regulatory and 802.11d domain information to the firmware
713 *
714 * @param priv pointer to struct lbs_private
715 * @param request cfg80211 regulatory request structure
716 * @param bands the device's supported bands and channels
717 *
718 * @return 0 on success, error code on failure
719*/
720int lbs_set_11d_domain_info(struct lbs_private *priv,
721 struct regulatory_request *request,
722 struct ieee80211_supported_band **bands)
723{
724 struct cmd_ds_802_11d_domain_info cmd;
725 struct mrvl_ie_domain_param_set *domain = &cmd.domain;
726 struct ieee80211_country_ie_triplet *t;
727 enum ieee80211_band band;
728 struct ieee80211_channel *ch;
729 u8 num_triplet = 0;
730 u8 num_parsed_chan = 0;
731 u8 first_channel = 0, next_chan = 0, max_pwr = 0;
732 u8 i, flag = 0;
733 size_t triplet_size;
734 int ret;
735
736 lbs_deb_enter(LBS_DEB_11D);
737
738 memset(&cmd, 0, sizeof(cmd));
739 cmd.action = cpu_to_le16(CMD_ACT_SET);
740
741 lbs_deb_11d("Setting country code '%c%c'\n",
742 request->alpha2[0], request->alpha2[1]);
743
744 domain->header.type = cpu_to_le16(TLV_TYPE_DOMAIN);
745
746 /* Set country code */
747 domain->country_code[0] = request->alpha2[0];
748 domain->country_code[1] = request->alpha2[1];
749 domain->country_code[2] = ' ';
750
751 /* Now set up the channel triplets; firmware is somewhat picky here
752 * and doesn't validate channel numbers and spans; hence it would
753 * interpret a triplet of (36, 4, 20) as channels 36, 37, 38, 39. Since
754 * the last 3 aren't valid channels, the driver is responsible for
755 * splitting that up into 4 triplet pairs of (36, 1, 20) + (40, 1, 20)
756 * etc.
757 */
758 for (band = 0;
759 (band < IEEE80211_NUM_BANDS) && (num_triplet < MAX_11D_TRIPLETS);
760 band++) {
761
762 if (!bands[band])
763 continue;
764
765 for (i = 0;
766 (i < bands[band]->n_channels) && (num_triplet < MAX_11D_TRIPLETS);
767 i++) {
768 ch = &bands[band]->channels[i];
769 if (ch->flags & IEEE80211_CHAN_DISABLED)
770 continue;
771
772 if (!flag) {
773 flag = 1;
774 next_chan = first_channel = (u32) ch->hw_value;
775 max_pwr = ch->max_power;
776 num_parsed_chan = 1;
777 continue;
778 }
779
780 if ((ch->hw_value == next_chan + 1) &&
781 (ch->max_power == max_pwr)) {
782 /* Consolidate adjacent channels */
783 next_chan++;
784 num_parsed_chan++;
785 } else {
786 /* Add this triplet */
787 lbs_deb_11d("11D triplet (%d, %d, %d)\n",
788 first_channel, num_parsed_chan,
789 max_pwr);
790 t = &domain->triplet[num_triplet];
791 t->chans.first_channel = first_channel;
792 t->chans.num_channels = num_parsed_chan;
793 t->chans.max_power = max_pwr;
794 num_triplet++;
795 flag = 0;
796 }
797 }
798
799 if (flag) {
800 /* Add last triplet */
801 lbs_deb_11d("11D triplet (%d, %d, %d)\n", first_channel,
802 num_parsed_chan, max_pwr);
803 t = &domain->triplet[num_triplet];
804 t->chans.first_channel = first_channel;
805 t->chans.num_channels = num_parsed_chan;
806 t->chans.max_power = max_pwr;
807 num_triplet++;
808 }
809 }
810
811 lbs_deb_11d("# triplets %d\n", num_triplet);
812
813 /* Set command header sizes */
814 triplet_size = num_triplet * sizeof(struct ieee80211_country_ie_triplet);
815 domain->header.len = cpu_to_le16(sizeof(domain->country_code) +
816 triplet_size);
817
818 lbs_deb_hex(LBS_DEB_11D, "802.11D domain param set",
819 (u8 *) &cmd.domain.country_code,
820 le16_to_cpu(domain->header.len));
821
822 cmd.hdr.size = cpu_to_le16(sizeof(cmd.hdr) +
823 sizeof(cmd.action) +
824 sizeof(cmd.domain.header) +
825 sizeof(cmd.domain.country_code) +
826 triplet_size);
827
828 ret = lbs_cmd_with_response(priv, CMD_802_11D_DOMAIN_INFO, &cmd);
829
830 lbs_deb_leave_args(LBS_DEB_11D, "ret %d", ret);
831 return ret;
832}
833
Dan Williams4c7c6e002010-07-27 13:01:07 -0700834/**
835 * @brief Read a MAC, Baseband, or RF register
836 *
837 * @param priv pointer to struct lbs_private
838 * @param cmd register command, one of CMD_MAC_REG_ACCESS,
839 * CMD_BBP_REG_ACCESS, or CMD_RF_REG_ACCESS
840 * @param offset byte offset of the register to get
841 * @param value on success, the value of the register at 'offset'
842 *
843 * @return 0 on success, error code on failure
844*/
845int lbs_get_reg(struct lbs_private *priv, u16 reg, u16 offset, u32 *value)
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200846{
Dan Williams4c7c6e002010-07-27 13:01:07 -0700847 struct cmd_ds_reg_access cmd;
848 int ret = 0;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200849
Holger Schurig9012b282007-05-25 11:27:16 -0400850 lbs_deb_enter(LBS_DEB_CMD);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200851
Dan Williams4c7c6e002010-07-27 13:01:07 -0700852 BUG_ON(value == NULL);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200853
Dan Williams4c7c6e002010-07-27 13:01:07 -0700854 memset(&cmd, 0, sizeof(cmd));
855 cmd.hdr.size = cpu_to_le16(sizeof(cmd));
856 cmd.action = cpu_to_le16(CMD_ACT_GET);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200857
Dan Williams4c7c6e002010-07-27 13:01:07 -0700858 if (reg != CMD_MAC_REG_ACCESS &&
859 reg != CMD_BBP_REG_ACCESS &&
860 reg != CMD_RF_REG_ACCESS) {
861 ret = -EINVAL;
862 goto out;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200863 }
864
Dan Williams4c7c6e002010-07-27 13:01:07 -0700865 ret = lbs_cmd_with_response(priv, reg, &cmd);
866 if (ret) {
867 if (reg == CMD_BBP_REG_ACCESS || reg == CMD_RF_REG_ACCESS)
868 *value = cmd.value.bbp_rf;
869 else if (reg == CMD_MAC_REG_ACCESS)
870 *value = le32_to_cpu(cmd.value.mac);
871 }
872
873out:
874 lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret);
875 return ret;
876}
877
878/**
879 * @brief Write a MAC, Baseband, or RF register
880 *
881 * @param priv pointer to struct lbs_private
882 * @param cmd register command, one of CMD_MAC_REG_ACCESS,
883 * CMD_BBP_REG_ACCESS, or CMD_RF_REG_ACCESS
884 * @param offset byte offset of the register to set
885 * @param value the value to write to the register at 'offset'
886 *
887 * @return 0 on success, error code on failure
888*/
889int lbs_set_reg(struct lbs_private *priv, u16 reg, u16 offset, u32 value)
890{
891 struct cmd_ds_reg_access cmd;
892 int ret = 0;
893
894 lbs_deb_enter(LBS_DEB_CMD);
895
896 memset(&cmd, 0, sizeof(cmd));
897 cmd.hdr.size = cpu_to_le16(sizeof(cmd));
898 cmd.action = cpu_to_le16(CMD_ACT_SET);
899
900 if (reg == CMD_BBP_REG_ACCESS || reg == CMD_RF_REG_ACCESS)
901 cmd.value.bbp_rf = (u8) (value & 0xFF);
902 else if (reg == CMD_MAC_REG_ACCESS)
903 cmd.value.mac = cpu_to_le32(value);
904 else {
905 ret = -EINVAL;
906 goto out;
907 }
908
909 ret = lbs_cmd_with_response(priv, reg, &cmd);
910
911out:
912 lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret);
913 return ret;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200914}
915
David Woodhouse681ffbb2007-12-15 20:04:54 -0500916static void lbs_queue_cmd(struct lbs_private *priv,
917 struct cmd_ctrl_node *cmdnode)
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200918{
919 unsigned long flags;
David Woodhouse681ffbb2007-12-15 20:04:54 -0500920 int addtail = 1;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200921
Holger Schurig8ff12da2007-08-02 11:54:31 -0400922 lbs_deb_enter(LBS_DEB_HOST);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200923
David Woodhousec4ab4122007-12-15 00:41:51 -0500924 if (!cmdnode) {
925 lbs_deb_host("QUEUE_CMD: cmdnode is NULL\n");
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200926 goto done;
927 }
David Woodhoused9896ee2007-12-15 00:09:25 -0500928 if (!cmdnode->cmdbuf->size) {
929 lbs_deb_host("DNLD_CMD: cmd size is zero\n");
930 goto done;
931 }
David Woodhouseae125bf2007-12-15 04:22:52 -0500932 cmdnode->result = 0;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200933
934 /* Exit_PS command needs to be queued in the header always. */
Dan Williamsddac4522007-12-11 13:49:39 -0500935 if (le16_to_cpu(cmdnode->cmdbuf->command) == CMD_802_11_PS_MODE) {
Dan Williams0bb64082010-07-27 13:08:08 -0700936 struct cmd_ds_802_11_ps_mode *psm = (void *) &cmdnode->cmdbuf;
Dan Williamsddac4522007-12-11 13:49:39 -0500937
Dan Williams0bb64082010-07-27 13:08:08 -0700938 if (psm->action == cpu_to_le16(PS_MODE_ACTION_EXIT_PS)) {
David Woodhouseaa21c002007-12-08 20:04:36 +0000939 if (priv->psstate != PS_STATE_FULL_POWER)
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200940 addtail = 0;
941 }
942 }
943
Dan Williams0bb64082010-07-27 13:08:08 -0700944 if (le16_to_cpu(cmdnode->cmdbuf->command) == CMD_802_11_WAKEUP_CONFIRM)
Amitkumar Karwar66fceb62010-05-19 03:24:38 -0700945 addtail = 0;
946
David Woodhouseaa21c002007-12-08 20:04:36 +0000947 spin_lock_irqsave(&priv->driver_lock, flags);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200948
David Woodhouseac472462007-12-08 00:35:00 +0000949 if (addtail)
David Woodhouseaa21c002007-12-08 20:04:36 +0000950 list_add_tail(&cmdnode->list, &priv->cmdpendingq);
David Woodhouseac472462007-12-08 00:35:00 +0000951 else
David Woodhouseaa21c002007-12-08 20:04:36 +0000952 list_add(&cmdnode->list, &priv->cmdpendingq);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200953
David Woodhouseaa21c002007-12-08 20:04:36 +0000954 spin_unlock_irqrestore(&priv->driver_lock, flags);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200955
Holger Schurig8ff12da2007-08-02 11:54:31 -0400956 lbs_deb_host("QUEUE_CMD: inserted command 0x%04x into cmdpendingq\n",
David Woodhousec4ab4122007-12-15 00:41:51 -0500957 le16_to_cpu(cmdnode->cmdbuf->command));
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200958
959done:
Holger Schurig8ff12da2007-08-02 11:54:31 -0400960 lbs_deb_leave(LBS_DEB_HOST);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200961}
962
David Woodhouse18c52e72007-12-17 16:03:58 -0500963static void lbs_submit_command(struct lbs_private *priv,
964 struct cmd_ctrl_node *cmdnode)
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200965{
966 unsigned long flags;
Dan Williamsddac4522007-12-11 13:49:39 -0500967 struct cmd_header *cmd;
David Woodhouse18c52e72007-12-17 16:03:58 -0500968 uint16_t cmdsize;
969 uint16_t command;
Holger Schurig57962f02008-05-14 16:30:28 +0200970 int timeo = 3 * HZ;
David Woodhouse18c52e72007-12-17 16:03:58 -0500971 int ret;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200972
Holger Schurig8ff12da2007-08-02 11:54:31 -0400973 lbs_deb_enter(LBS_DEB_HOST);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200974
Dan Williamsddac4522007-12-11 13:49:39 -0500975 cmd = cmdnode->cmdbuf;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200976
David Woodhouseaa21c002007-12-08 20:04:36 +0000977 spin_lock_irqsave(&priv->driver_lock, flags);
David Woodhouseaa21c002007-12-08 20:04:36 +0000978 priv->cur_cmd = cmdnode;
David Woodhouseaa21c002007-12-08 20:04:36 +0000979 spin_unlock_irqrestore(&priv->driver_lock, flags);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200980
Dan Williamsddac4522007-12-11 13:49:39 -0500981 cmdsize = le16_to_cpu(cmd->size);
982 command = le16_to_cpu(cmd->command);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200983
David Woodhouse18c52e72007-12-17 16:03:58 -0500984 /* These commands take longer */
Dan Williamsbe0d76e2009-05-22 20:05:25 -0400985 if (command == CMD_802_11_SCAN || command == CMD_802_11_ASSOCIATE)
Holger Schurig57962f02008-05-14 16:30:28 +0200986 timeo = 5 * HZ;
David Woodhouse18c52e72007-12-17 16:03:58 -0500987
Holger Schurige5225b32008-03-26 10:04:44 +0100988 lbs_deb_cmd("DNLD_CMD: command 0x%04x, seq %d, size %d\n",
989 command, le16_to_cpu(cmd->seqnum), cmdsize);
Holger Schurig1afc09ab2008-01-29 09:14:40 +0100990 lbs_deb_hex(LBS_DEB_CMD, "DNLD_CMD", (void *) cmdnode->cmdbuf, cmdsize);
Holger Schurig8ff12da2007-08-02 11:54:31 -0400991
Dan Williamsddac4522007-12-11 13:49:39 -0500992 ret = priv->hw_host_to_card(priv, MVMS_CMD, (u8 *) cmd, cmdsize);
David Woodhouse18c52e72007-12-17 16:03:58 -0500993
David Woodhoused9896ee2007-12-15 00:09:25 -0500994 if (ret) {
995 lbs_pr_info("DNLD_CMD: hw_host_to_card failed: %d\n", ret);
David Woodhouse18c52e72007-12-17 16:03:58 -0500996 /* Let the timer kick in and retry, and potentially reset
997 the whole thing if the condition persists */
Holger Schurig57962f02008-05-14 16:30:28 +0200998 timeo = HZ/4;
Holger Schurig1afc09ab2008-01-29 09:14:40 +0100999 }
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001000
Amitkumar Karwar49125452009-09-30 20:04:38 -07001001 if (command == CMD_802_11_DEEP_SLEEP) {
1002 if (priv->is_auto_deep_sleep_enabled) {
1003 priv->wakeup_dev_required = 1;
1004 priv->dnld_sent = 0;
1005 }
1006 priv->is_deep_sleep = 1;
1007 lbs_complete_command(priv, cmdnode, 0);
1008 } else {
1009 /* Setup the timer after transmit command */
1010 mod_timer(&priv->command_timer, jiffies + timeo);
1011 }
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001012
David Woodhouse18c52e72007-12-17 16:03:58 -05001013 lbs_deb_leave(LBS_DEB_HOST);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001014}
1015
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001016/**
1017 * This function inserts command node to cmdfreeq
David Woodhouseaa21c002007-12-08 20:04:36 +00001018 * after cleans it. Requires priv->driver_lock held.
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001019 */
David Woodhouse183aeac2007-12-15 01:52:54 -05001020static void __lbs_cleanup_and_insert_cmd(struct lbs_private *priv,
David Woodhouse5ba2f8a2007-12-15 02:02:56 -05001021 struct cmd_ctrl_node *cmdnode)
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001022{
David Woodhouse5ba2f8a2007-12-15 02:02:56 -05001023 lbs_deb_enter(LBS_DEB_HOST);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001024
David Woodhouse5ba2f8a2007-12-15 02:02:56 -05001025 if (!cmdnode)
1026 goto out;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001027
David Woodhouse5ba2f8a2007-12-15 02:02:56 -05001028 cmdnode->callback = NULL;
1029 cmdnode->callback_arg = 0;
1030
1031 memset(cmdnode->cmdbuf, 0, LBS_CMD_BUFFER_SIZE);
1032
1033 list_add_tail(&cmdnode->list, &priv->cmdfreeq);
1034 out:
1035 lbs_deb_leave(LBS_DEB_HOST);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001036}
1037
Holger Schurig69f90322007-11-23 15:43:44 +01001038static void lbs_cleanup_and_insert_cmd(struct lbs_private *priv,
1039 struct cmd_ctrl_node *ptempcmd)
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001040{
1041 unsigned long flags;
1042
David Woodhouseaa21c002007-12-08 20:04:36 +00001043 spin_lock_irqsave(&priv->driver_lock, flags);
Holger Schurig10078322007-11-15 18:05:47 -05001044 __lbs_cleanup_and_insert_cmd(priv, ptempcmd);
David Woodhouseaa21c002007-12-08 20:04:36 +00001045 spin_unlock_irqrestore(&priv->driver_lock, flags);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001046}
1047
David Woodhouse183aeac2007-12-15 01:52:54 -05001048void lbs_complete_command(struct lbs_private *priv, struct cmd_ctrl_node *cmd,
1049 int result)
1050{
David Woodhouseae125bf2007-12-15 04:22:52 -05001051 cmd->result = result;
David Woodhouse5ba2f8a2007-12-15 02:02:56 -05001052 cmd->cmdwaitqwoken = 1;
1053 wake_up_interruptible(&cmd->cmdwait_q);
1054
Holger Schurig8db4a2b2008-03-19 10:11:00 +01001055 if (!cmd->callback || cmd->callback == lbs_cmd_async_callback)
David Woodhousead12d0f2007-12-15 02:06:16 -05001056 __lbs_cleanup_and_insert_cmd(priv, cmd);
David Woodhouse183aeac2007-12-15 01:52:54 -05001057 priv->cur_cmd = NULL;
1058}
1059
Dan Williamsd5db2df2008-08-21 17:51:07 -04001060int lbs_set_radio(struct lbs_private *priv, u8 preamble, u8 radio_on)
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001061{
David Woodhousea7c45892007-12-17 22:43:48 -05001062 struct cmd_ds_802_11_radio_control cmd;
Dan Williamsd5db2df2008-08-21 17:51:07 -04001063 int ret = -EINVAL;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001064
Holger Schurig9012b282007-05-25 11:27:16 -04001065 lbs_deb_enter(LBS_DEB_CMD);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001066
David Woodhousea7c45892007-12-17 22:43:48 -05001067 cmd.hdr.size = cpu_to_le16(sizeof(cmd));
1068 cmd.action = cpu_to_le16(CMD_ACT_SET);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001069
Dan Williamsd5db2df2008-08-21 17:51:07 -04001070 /* Only v8 and below support setting the preamble */
1071 if (priv->fwrelease < 0x09000000) {
1072 switch (preamble) {
1073 case RADIO_PREAMBLE_SHORT:
Dan Williamsd5db2df2008-08-21 17:51:07 -04001074 case RADIO_PREAMBLE_AUTO:
1075 case RADIO_PREAMBLE_LONG:
1076 cmd.control = cpu_to_le16(preamble);
1077 break;
1078 default:
1079 goto out;
1080 }
David Woodhousea7c45892007-12-17 22:43:48 -05001081 }
1082
Dan Williamsd5db2df2008-08-21 17:51:07 -04001083 if (radio_on)
1084 cmd.control |= cpu_to_le16(0x1);
1085 else {
1086 cmd.control &= cpu_to_le16(~0x1);
1087 priv->txpower_cur = 0;
1088 }
David Woodhousea7c45892007-12-17 22:43:48 -05001089
Dan Williamsd5db2df2008-08-21 17:51:07 -04001090 lbs_deb_cmd("RADIO_CONTROL: radio %s, preamble %d\n",
1091 radio_on ? "ON" : "OFF", preamble);
1092
1093 priv->radio_on = radio_on;
David Woodhousea7c45892007-12-17 22:43:48 -05001094
1095 ret = lbs_cmd_with_response(priv, CMD_802_11_RADIO_CONTROL, &cmd);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001096
Dan Williamsd5db2df2008-08-21 17:51:07 -04001097out:
Holger Schurig9012b282007-05-25 11:27:16 -04001098 lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001099 return ret;
1100}
1101
Holger Schurigc97329e2008-03-18 11:20:21 +01001102void lbs_set_mac_control(struct lbs_private *priv)
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001103{
Holger Schurig835d3ac2008-03-12 16:05:40 +01001104 struct cmd_ds_mac_control cmd;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001105
Holger Schurig9012b282007-05-25 11:27:16 -04001106 lbs_deb_enter(LBS_DEB_CMD);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001107
Holger Schurig835d3ac2008-03-12 16:05:40 +01001108 cmd.hdr.size = cpu_to_le16(sizeof(cmd));
Holger Schurigd9e97782008-03-12 16:06:43 +01001109 cmd.action = cpu_to_le16(priv->mac_control);
Holger Schurig835d3ac2008-03-12 16:05:40 +01001110 cmd.reserved = 0;
1111
David Woodhouse75bf45a2008-05-20 13:32:45 +01001112 lbs_cmd_async(priv, CMD_MAC_CONTROL, &cmd.hdr, sizeof(cmd));
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001113
Holger Schurigc97329e2008-03-18 11:20:21 +01001114 lbs_deb_leave(LBS_DEB_CMD);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001115}
1116
1117/**
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001118 * @brief This function allocates the command buffer and link
1119 * it to command free queue.
1120 *
Holger Schurig69f90322007-11-23 15:43:44 +01001121 * @param priv A pointer to struct lbs_private structure
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001122 * @return 0 or -1
1123 */
Holger Schurig69f90322007-11-23 15:43:44 +01001124int lbs_allocate_cmd_buffer(struct lbs_private *priv)
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001125{
1126 int ret = 0;
Dan Williamsddac4522007-12-11 13:49:39 -05001127 u32 bufsize;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001128 u32 i;
Dan Williamsddac4522007-12-11 13:49:39 -05001129 struct cmd_ctrl_node *cmdarray;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001130
Holger Schurig8ff12da2007-08-02 11:54:31 -04001131 lbs_deb_enter(LBS_DEB_HOST);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001132
Dan Williamsddac4522007-12-11 13:49:39 -05001133 /* Allocate and initialize the command array */
1134 bufsize = sizeof(struct cmd_ctrl_node) * LBS_NUM_CMD_BUFFERS;
1135 if (!(cmdarray = kzalloc(bufsize, GFP_KERNEL))) {
Holger Schurig8ff12da2007-08-02 11:54:31 -04001136 lbs_deb_host("ALLOC_CMD_BUF: tempcmd_array is NULL\n");
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001137 ret = -1;
1138 goto done;
1139 }
Dan Williamsddac4522007-12-11 13:49:39 -05001140 priv->cmd_array = cmdarray;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001141
Dan Williamsddac4522007-12-11 13:49:39 -05001142 /* Allocate and initialize each command buffer in the command array */
1143 for (i = 0; i < LBS_NUM_CMD_BUFFERS; i++) {
1144 cmdarray[i].cmdbuf = kzalloc(LBS_CMD_BUFFER_SIZE, GFP_KERNEL);
1145 if (!cmdarray[i].cmdbuf) {
Holger Schurig8ff12da2007-08-02 11:54:31 -04001146 lbs_deb_host("ALLOC_CMD_BUF: ptempvirtualaddr is NULL\n");
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001147 ret = -1;
1148 goto done;
1149 }
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001150 }
1151
Dan Williamsddac4522007-12-11 13:49:39 -05001152 for (i = 0; i < LBS_NUM_CMD_BUFFERS; i++) {
1153 init_waitqueue_head(&cmdarray[i].cmdwait_q);
1154 lbs_cleanup_and_insert_cmd(priv, &cmdarray[i]);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001155 }
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001156 ret = 0;
Holger Schurig9012b282007-05-25 11:27:16 -04001157
1158done:
Holger Schurig8ff12da2007-08-02 11:54:31 -04001159 lbs_deb_leave_args(LBS_DEB_HOST, "ret %d", ret);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001160 return ret;
1161}
1162
1163/**
1164 * @brief This function frees the command buffer.
1165 *
Holger Schurig69f90322007-11-23 15:43:44 +01001166 * @param priv A pointer to struct lbs_private structure
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001167 * @return 0 or -1
1168 */
Holger Schurig69f90322007-11-23 15:43:44 +01001169int lbs_free_cmd_buffer(struct lbs_private *priv)
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001170{
Dan Williamsddac4522007-12-11 13:49:39 -05001171 struct cmd_ctrl_node *cmdarray;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001172 unsigned int i;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001173
Holger Schurig8ff12da2007-08-02 11:54:31 -04001174 lbs_deb_enter(LBS_DEB_HOST);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001175
1176 /* need to check if cmd array is allocated or not */
David Woodhouseaa21c002007-12-08 20:04:36 +00001177 if (priv->cmd_array == NULL) {
Holger Schurig8ff12da2007-08-02 11:54:31 -04001178 lbs_deb_host("FREE_CMD_BUF: cmd_array is NULL\n");
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001179 goto done;
1180 }
1181
Dan Williamsddac4522007-12-11 13:49:39 -05001182 cmdarray = priv->cmd_array;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001183
1184 /* Release shared memory buffers */
Dan Williamsddac4522007-12-11 13:49:39 -05001185 for (i = 0; i < LBS_NUM_CMD_BUFFERS; i++) {
1186 if (cmdarray[i].cmdbuf) {
1187 kfree(cmdarray[i].cmdbuf);
1188 cmdarray[i].cmdbuf = NULL;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001189 }
1190 }
1191
1192 /* Release cmd_ctrl_node */
David Woodhouseaa21c002007-12-08 20:04:36 +00001193 if (priv->cmd_array) {
1194 kfree(priv->cmd_array);
1195 priv->cmd_array = NULL;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001196 }
1197
1198done:
Holger Schurig8ff12da2007-08-02 11:54:31 -04001199 lbs_deb_leave(LBS_DEB_HOST);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001200 return 0;
1201}
1202
1203/**
1204 * @brief This function gets a free command node if available in
1205 * command free queue.
1206 *
Holger Schurig69f90322007-11-23 15:43:44 +01001207 * @param priv A pointer to struct lbs_private structure
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001208 * @return cmd_ctrl_node A pointer to cmd_ctrl_node structure or NULL
1209 */
David Woodhouse2fd6cfe2007-12-11 17:44:10 -05001210static struct cmd_ctrl_node *lbs_get_cmd_ctrl_node(struct lbs_private *priv)
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001211{
1212 struct cmd_ctrl_node *tempnode;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001213 unsigned long flags;
1214
Holger Schurig8ff12da2007-08-02 11:54:31 -04001215 lbs_deb_enter(LBS_DEB_HOST);
1216
David Woodhouseaa21c002007-12-08 20:04:36 +00001217 if (!priv)
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001218 return NULL;
1219
David Woodhouseaa21c002007-12-08 20:04:36 +00001220 spin_lock_irqsave(&priv->driver_lock, flags);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001221
David Woodhouseaa21c002007-12-08 20:04:36 +00001222 if (!list_empty(&priv->cmdfreeq)) {
1223 tempnode = list_first_entry(&priv->cmdfreeq,
Li Zefanabe3ed12007-12-06 13:01:21 +01001224 struct cmd_ctrl_node, list);
1225 list_del(&tempnode->list);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001226 } else {
Holger Schurig8ff12da2007-08-02 11:54:31 -04001227 lbs_deb_host("GET_CMD_NODE: cmd_ctrl_node is not available\n");
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001228 tempnode = NULL;
1229 }
1230
David Woodhouseaa21c002007-12-08 20:04:36 +00001231 spin_unlock_irqrestore(&priv->driver_lock, flags);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001232
Holger Schurig8ff12da2007-08-02 11:54:31 -04001233 lbs_deb_leave(LBS_DEB_HOST);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001234 return tempnode;
1235}
1236
1237/**
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001238 * @brief This function executes next command in command
Nick Andrew877d0312009-01-26 11:06:57 +01001239 * pending queue. It will put firmware back to PS mode
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001240 * if applicable.
1241 *
Holger Schurig69f90322007-11-23 15:43:44 +01001242 * @param priv A pointer to struct lbs_private structure
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001243 * @return 0 or -1
1244 */
Holger Schurig69f90322007-11-23 15:43:44 +01001245int lbs_execute_next_command(struct lbs_private *priv)
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001246{
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001247 struct cmd_ctrl_node *cmdnode = NULL;
Dan Williamsddac4522007-12-11 13:49:39 -05001248 struct cmd_header *cmd;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001249 unsigned long flags;
1250 int ret = 0;
1251
Holger Schurig1afc09ab2008-01-29 09:14:40 +01001252 /* Debug group is LBS_DEB_THREAD and not LBS_DEB_HOST, because the
1253 * only caller to us is lbs_thread() and we get even when a
1254 * data packet is received */
Holger Schurig8ff12da2007-08-02 11:54:31 -04001255 lbs_deb_enter(LBS_DEB_THREAD);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001256
David Woodhouseaa21c002007-12-08 20:04:36 +00001257 spin_lock_irqsave(&priv->driver_lock, flags);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001258
David Woodhouseaa21c002007-12-08 20:04:36 +00001259 if (priv->cur_cmd) {
Holger Schurig8ff12da2007-08-02 11:54:31 -04001260 lbs_pr_alert( "EXEC_NEXT_CMD: already processing command!\n");
David Woodhouseaa21c002007-12-08 20:04:36 +00001261 spin_unlock_irqrestore(&priv->driver_lock, flags);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001262 ret = -1;
1263 goto done;
1264 }
1265
David Woodhouseaa21c002007-12-08 20:04:36 +00001266 if (!list_empty(&priv->cmdpendingq)) {
1267 cmdnode = list_first_entry(&priv->cmdpendingq,
Li Zefanabe3ed12007-12-06 13:01:21 +01001268 struct cmd_ctrl_node, list);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001269 }
1270
David Woodhouseaa21c002007-12-08 20:04:36 +00001271 spin_unlock_irqrestore(&priv->driver_lock, flags);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001272
1273 if (cmdnode) {
Dan Williamsddac4522007-12-11 13:49:39 -05001274 cmd = cmdnode->cmdbuf;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001275
Dan Williamsddac4522007-12-11 13:49:39 -05001276 if (is_command_allowed_in_ps(le16_to_cpu(cmd->command))) {
David Woodhouseaa21c002007-12-08 20:04:36 +00001277 if ((priv->psstate == PS_STATE_SLEEP) ||
1278 (priv->psstate == PS_STATE_PRE_SLEEP)) {
Holger Schurig8ff12da2007-08-02 11:54:31 -04001279 lbs_deb_host(
1280 "EXEC_NEXT_CMD: cannot send cmd 0x%04x in psstate %d\n",
Dan Williamsddac4522007-12-11 13:49:39 -05001281 le16_to_cpu(cmd->command),
David Woodhouseaa21c002007-12-08 20:04:36 +00001282 priv->psstate);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001283 ret = -1;
1284 goto done;
1285 }
Holger Schurig8ff12da2007-08-02 11:54:31 -04001286 lbs_deb_host("EXEC_NEXT_CMD: OK to send command "
Dan Williamsddac4522007-12-11 13:49:39 -05001287 "0x%04x in psstate %d\n",
1288 le16_to_cpu(cmd->command), priv->psstate);
David Woodhouseaa21c002007-12-08 20:04:36 +00001289 } else if (priv->psstate != PS_STATE_FULL_POWER) {
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001290 /*
1291 * 1. Non-PS command:
1292 * Queue it. set needtowakeup to TRUE if current state
Dan Williams0bb64082010-07-27 13:08:08 -07001293 * is SLEEP, otherwise call send EXIT_PS.
1294 * 2. PS command but not EXIT_PS:
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001295 * Ignore it.
Dan Williams0bb64082010-07-27 13:08:08 -07001296 * 3. PS command EXIT_PS:
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001297 * Set needtowakeup to TRUE if current state is SLEEP,
1298 * otherwise send this command down to firmware
1299 * immediately.
1300 */
Dan Williamsddac4522007-12-11 13:49:39 -05001301 if (cmd->command != cpu_to_le16(CMD_802_11_PS_MODE)) {
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001302 /* Prepare to send Exit PS,
1303 * this non PS command will be sent later */
David Woodhouseaa21c002007-12-08 20:04:36 +00001304 if ((priv->psstate == PS_STATE_SLEEP)
1305 || (priv->psstate == PS_STATE_PRE_SLEEP)
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001306 ) {
1307 /* w/ new scheme, it will not reach here.
1308 since it is blocked in main_thread. */
David Woodhouseaa21c002007-12-08 20:04:36 +00001309 priv->needtowakeup = 1;
Dan Williams0bb64082010-07-27 13:08:08 -07001310 } else {
1311 lbs_set_ps_mode(priv,
1312 PS_MODE_ACTION_EXIT_PS,
1313 false);
1314 }
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001315
1316 ret = 0;
1317 goto done;
1318 } else {
1319 /*
1320 * PS command. Ignore it if it is not Exit_PS.
1321 * otherwise send it down immediately.
1322 */
David Woodhouse38bfab12007-12-16 23:26:54 -05001323 struct cmd_ds_802_11_ps_mode *psm = (void *)&cmd[1];
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001324
Holger Schurig8ff12da2007-08-02 11:54:31 -04001325 lbs_deb_host(
1326 "EXEC_NEXT_CMD: PS cmd, action 0x%02x\n",
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001327 psm->action);
1328 if (psm->action !=
Dan Williams0bb64082010-07-27 13:08:08 -07001329 cpu_to_le16(PS_MODE_ACTION_EXIT_PS)) {
Holger Schurig8ff12da2007-08-02 11:54:31 -04001330 lbs_deb_host(
1331 "EXEC_NEXT_CMD: ignore ENTER_PS cmd\n");
Li Zefanabe3ed12007-12-06 13:01:21 +01001332 list_del(&cmdnode->list);
David Woodhouse183aeac2007-12-15 01:52:54 -05001333 spin_lock_irqsave(&priv->driver_lock, flags);
1334 lbs_complete_command(priv, cmdnode, 0);
1335 spin_unlock_irqrestore(&priv->driver_lock, flags);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001336
1337 ret = 0;
1338 goto done;
1339 }
1340
David Woodhouseaa21c002007-12-08 20:04:36 +00001341 if ((priv->psstate == PS_STATE_SLEEP) ||
1342 (priv->psstate == PS_STATE_PRE_SLEEP)) {
Holger Schurig8ff12da2007-08-02 11:54:31 -04001343 lbs_deb_host(
1344 "EXEC_NEXT_CMD: ignore EXIT_PS cmd in sleep\n");
Li Zefanabe3ed12007-12-06 13:01:21 +01001345 list_del(&cmdnode->list);
David Woodhouse183aeac2007-12-15 01:52:54 -05001346 spin_lock_irqsave(&priv->driver_lock, flags);
1347 lbs_complete_command(priv, cmdnode, 0);
1348 spin_unlock_irqrestore(&priv->driver_lock, flags);
David Woodhouseaa21c002007-12-08 20:04:36 +00001349 priv->needtowakeup = 1;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001350
1351 ret = 0;
1352 goto done;
1353 }
1354
Holger Schurig8ff12da2007-08-02 11:54:31 -04001355 lbs_deb_host(
1356 "EXEC_NEXT_CMD: sending EXIT_PS\n");
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001357 }
1358 }
Li Zefanabe3ed12007-12-06 13:01:21 +01001359 list_del(&cmdnode->list);
Holger Schurig8ff12da2007-08-02 11:54:31 -04001360 lbs_deb_host("EXEC_NEXT_CMD: sending command 0x%04x\n",
Dan Williamsddac4522007-12-11 13:49:39 -05001361 le16_to_cpu(cmd->command));
David Woodhoused9896ee2007-12-15 00:09:25 -05001362 lbs_submit_command(priv, cmdnode);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001363 } else {
1364 /*
1365 * check if in power save mode, if yes, put the device back
1366 * to PS mode
1367 */
Kiran Divekare86dc1c2010-06-14 22:01:26 +05301368#ifdef TODO
1369 /*
1370 * This was the old code for libertas+wext. Someone that
1371 * understands this beast should re-code it in a sane way.
1372 *
1373 * I actually don't understand why this is related to WPA
1374 * and to connection status, shouldn't powering should be
1375 * independ of such things?
1376 */
David Woodhouseaa21c002007-12-08 20:04:36 +00001377 if ((priv->psmode != LBS802_11POWERMODECAM) &&
1378 (priv->psstate == PS_STATE_FULL_POWER) &&
1379 ((priv->connect_status == LBS_CONNECTED) ||
Holger Schurig602114a2009-12-02 15:26:01 +01001380 lbs_mesh_connected(priv))) {
David Woodhouseaa21c002007-12-08 20:04:36 +00001381 if (priv->secinfo.WPAenabled ||
1382 priv->secinfo.WPA2enabled) {
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001383 /* check for valid WPA group keys */
David Woodhouseaa21c002007-12-08 20:04:36 +00001384 if (priv->wpa_mcast_key.len ||
1385 priv->wpa_unicast_key.len) {
Holger Schurig8ff12da2007-08-02 11:54:31 -04001386 lbs_deb_host(
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001387 "EXEC_NEXT_CMD: WPA enabled and GTK_SET"
1388 " go back to PS_SLEEP");
Dan Williams0bb64082010-07-27 13:08:08 -07001389 lbs_set_ps_mode(priv,
1390 PS_MODE_ACTION_ENTER_PS,
1391 false);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001392 }
1393 } else {
Holger Schurig8ff12da2007-08-02 11:54:31 -04001394 lbs_deb_host(
1395 "EXEC_NEXT_CMD: cmdpendingq empty, "
1396 "go back to PS_SLEEP");
Dan Williams0bb64082010-07-27 13:08:08 -07001397 lbs_set_ps_mode(priv, PS_MODE_ACTION_ENTER_PS,
1398 false);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001399 }
1400 }
Kiran Divekare86dc1c2010-06-14 22:01:26 +05301401#endif
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001402 }
1403
1404 ret = 0;
1405done:
Holger Schurig8ff12da2007-08-02 11:54:31 -04001406 lbs_deb_leave(LBS_DEB_THREAD);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001407 return ret;
1408}
1409
Holger Schurigf539f2e2008-03-26 13:22:11 +01001410static void lbs_send_confirmsleep(struct lbs_private *priv)
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001411{
1412 unsigned long flags;
Holger Schurigf539f2e2008-03-26 13:22:11 +01001413 int ret;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001414
Holger Schurig8ff12da2007-08-02 11:54:31 -04001415 lbs_deb_enter(LBS_DEB_HOST);
Holger Schurigf539f2e2008-03-26 13:22:11 +01001416 lbs_deb_hex(LBS_DEB_HOST, "sleep confirm", (u8 *) &confirm_sleep,
1417 sizeof(confirm_sleep));
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001418
Holger Schurigf539f2e2008-03-26 13:22:11 +01001419 ret = priv->hw_host_to_card(priv, MVMS_CMD, (u8 *) &confirm_sleep,
1420 sizeof(confirm_sleep));
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001421 if (ret) {
Holger Schurigf539f2e2008-03-26 13:22:11 +01001422 lbs_pr_alert("confirm_sleep failed\n");
Holger Schurig7919b892008-04-01 14:50:43 +02001423 goto out;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001424 }
Holger Schurig7919b892008-04-01 14:50:43 +02001425
1426 spin_lock_irqsave(&priv->driver_lock, flags);
1427
Holger Schuriga01f5452008-06-04 11:10:40 +02001428 /* We don't get a response on the sleep-confirmation */
1429 priv->dnld_sent = DNLD_RES_RECEIVED;
1430
Amitkumar Karwar66fceb62010-05-19 03:24:38 -07001431 if (priv->is_host_sleep_configured) {
1432 priv->is_host_sleep_activated = 1;
1433 wake_up_interruptible(&priv->host_sleep_q);
1434 }
1435
Holger Schurig7919b892008-04-01 14:50:43 +02001436 /* If nothing to do, go back to sleep (?) */
Stefani Seibolde64c0262009-12-21 14:37:28 -08001437 if (!kfifo_len(&priv->event_fifo) && !priv->resp_len[priv->resp_idx])
Holger Schurig7919b892008-04-01 14:50:43 +02001438 priv->psstate = PS_STATE_SLEEP;
1439
1440 spin_unlock_irqrestore(&priv->driver_lock, flags);
1441
1442out:
Holger Schurigf539f2e2008-03-26 13:22:11 +01001443 lbs_deb_leave(LBS_DEB_HOST);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001444}
1445
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001446/**
1447 * @brief This function checks condition and prepares to
1448 * send sleep confirm command to firmware if ok.
1449 *
Holger Schurig69f90322007-11-23 15:43:44 +01001450 * @param priv A pointer to struct lbs_private structure
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001451 * @param psmode Power Saving mode
1452 * @return n/a
1453 */
Holger Schurigd4ff0ef2008-03-19 14:25:18 +01001454void lbs_ps_confirm_sleep(struct lbs_private *priv)
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001455{
1456 unsigned long flags =0;
Holger Schurigd4ff0ef2008-03-19 14:25:18 +01001457 int allowed = 1;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001458
Holger Schurig8ff12da2007-08-02 11:54:31 -04001459 lbs_deb_enter(LBS_DEB_HOST);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001460
Holger Schuriga01f5452008-06-04 11:10:40 +02001461 spin_lock_irqsave(&priv->driver_lock, flags);
Holger Schurig634b8f42007-05-25 13:05:16 -04001462 if (priv->dnld_sent) {
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001463 allowed = 0;
David Woodhouse23d36ee2007-12-12 00:14:21 -05001464 lbs_deb_host("dnld_sent was set\n");
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001465 }
1466
Holger Schurig7919b892008-04-01 14:50:43 +02001467 /* In-progress command? */
David Woodhouseaa21c002007-12-08 20:04:36 +00001468 if (priv->cur_cmd) {
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001469 allowed = 0;
David Woodhouse23d36ee2007-12-12 00:14:21 -05001470 lbs_deb_host("cur_cmd was set\n");
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001471 }
Holger Schurig7919b892008-04-01 14:50:43 +02001472
1473 /* Pending events or command responses? */
Stefani Seibolde64c0262009-12-21 14:37:28 -08001474 if (kfifo_len(&priv->event_fifo) || priv->resp_len[priv->resp_idx]) {
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001475 allowed = 0;
Holger Schurig7919b892008-04-01 14:50:43 +02001476 lbs_deb_host("pending events or command responses\n");
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001477 }
David Woodhouseaa21c002007-12-08 20:04:36 +00001478 spin_unlock_irqrestore(&priv->driver_lock, flags);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001479
1480 if (allowed) {
Holger Schurig10078322007-11-15 18:05:47 -05001481 lbs_deb_host("sending lbs_ps_confirm_sleep\n");
Holger Schurigf539f2e2008-03-26 13:22:11 +01001482 lbs_send_confirmsleep(priv);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001483 } else {
Holger Schurig8ff12da2007-08-02 11:54:31 -04001484 lbs_deb_host("sleep confirm has been delayed\n");
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001485 }
1486
Holger Schurig8ff12da2007-08-02 11:54:31 -04001487 lbs_deb_leave(LBS_DEB_HOST);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001488}
Holger Schurig675787e2007-12-05 17:58:11 +01001489
1490
Anna Neal0112c9e2008-09-11 11:17:25 -07001491/**
1492 * @brief Configures the transmission power control functionality.
1493 *
1494 * @param priv A pointer to struct lbs_private structure
1495 * @param enable Transmission power control enable
1496 * @param p0 Power level when link quality is good (dBm).
1497 * @param p1 Power level when link quality is fair (dBm).
1498 * @param p2 Power level when link quality is poor (dBm).
1499 * @param usesnr Use Signal to Noise Ratio in TPC
1500 *
1501 * @return 0 on success
1502 */
1503int lbs_set_tpc_cfg(struct lbs_private *priv, int enable, int8_t p0, int8_t p1,
1504 int8_t p2, int usesnr)
1505{
1506 struct cmd_ds_802_11_tpc_cfg cmd;
1507 int ret;
1508
1509 memset(&cmd, 0, sizeof(cmd));
1510 cmd.hdr.size = cpu_to_le16(sizeof(cmd));
1511 cmd.action = cpu_to_le16(CMD_ACT_SET);
1512 cmd.enable = !!enable;
Anna Neal3ed6e082008-09-26 11:34:35 -04001513 cmd.usesnr = !!usesnr;
Anna Neal0112c9e2008-09-11 11:17:25 -07001514 cmd.P0 = p0;
1515 cmd.P1 = p1;
1516 cmd.P2 = p2;
1517
1518 ret = lbs_cmd_with_response(priv, CMD_802_11_TPC_CFG, &cmd);
1519
1520 return ret;
1521}
1522
1523/**
1524 * @brief Configures the power adaptation settings.
1525 *
1526 * @param priv A pointer to struct lbs_private structure
1527 * @param enable Power adaptation enable
1528 * @param p0 Power level for 1, 2, 5.5 and 11 Mbps (dBm).
1529 * @param p1 Power level for 6, 9, 12, 18, 22, 24 and 36 Mbps (dBm).
1530 * @param p2 Power level for 48 and 54 Mbps (dBm).
1531 *
1532 * @return 0 on Success
1533 */
1534
1535int lbs_set_power_adapt_cfg(struct lbs_private *priv, int enable, int8_t p0,
1536 int8_t p1, int8_t p2)
1537{
1538 struct cmd_ds_802_11_pa_cfg cmd;
1539 int ret;
1540
1541 memset(&cmd, 0, sizeof(cmd));
1542 cmd.hdr.size = cpu_to_le16(sizeof(cmd));
1543 cmd.action = cpu_to_le16(CMD_ACT_SET);
1544 cmd.enable = !!enable;
1545 cmd.P0 = p0;
1546 cmd.P1 = p1;
1547 cmd.P2 = p2;
1548
1549 ret = lbs_cmd_with_response(priv, CMD_802_11_PA_CFG , &cmd);
1550
1551 return ret;
1552}
1553
1554
Holger Schurig6d898b12009-10-14 16:49:53 +02001555struct cmd_ctrl_node *__lbs_cmd_async(struct lbs_private *priv,
Holger Schurig8db4a2b2008-03-19 10:11:00 +01001556 uint16_t command, struct cmd_header *in_cmd, int in_cmd_size,
1557 int (*callback)(struct lbs_private *, unsigned long, struct cmd_header *),
1558 unsigned long callback_arg)
Holger Schurig675787e2007-12-05 17:58:11 +01001559{
Holger Schurig675787e2007-12-05 17:58:11 +01001560 struct cmd_ctrl_node *cmdnode;
Holger Schurig675787e2007-12-05 17:58:11 +01001561
1562 lbs_deb_enter(LBS_DEB_HOST);
Holger Schurig675787e2007-12-05 17:58:11 +01001563
David Woodhouseaa21c002007-12-08 20:04:36 +00001564 if (priv->surpriseremoved) {
Holger Schurig675787e2007-12-05 17:58:11 +01001565 lbs_deb_host("PREP_CMD: card removed\n");
David Woodhouse3399ea52007-12-15 03:09:33 -05001566 cmdnode = ERR_PTR(-ENOENT);
Holger Schurig675787e2007-12-05 17:58:11 +01001567 goto done;
1568 }
1569
Dan Williams77ccdcf2010-07-27 13:15:45 -07001570 /* No commands are allowed in Deep Sleep until we toggle the GPIO
1571 * to wake up the card and it has signaled that it's ready.
1572 */
1573 if (!priv->is_auto_deep_sleep_enabled) {
1574 if (priv->is_deep_sleep) {
1575 lbs_deb_cmd("command not allowed in deep sleep\n");
1576 cmdnode = ERR_PTR(-EBUSY);
1577 goto done;
1578 }
Amitkumar Karwar63f275d2009-10-06 19:20:28 -07001579 }
1580
Holger Schurig675787e2007-12-05 17:58:11 +01001581 cmdnode = lbs_get_cmd_ctrl_node(priv);
Holger Schurig675787e2007-12-05 17:58:11 +01001582 if (cmdnode == NULL) {
1583 lbs_deb_host("PREP_CMD: cmdnode is NULL\n");
1584
1585 /* Wake up main thread to execute next command */
1586 wake_up_interruptible(&priv->waitq);
David Woodhouse3399ea52007-12-15 03:09:33 -05001587 cmdnode = ERR_PTR(-ENOBUFS);
Holger Schurig675787e2007-12-05 17:58:11 +01001588 goto done;
1589 }
1590
David Woodhouse448a51a2007-12-08 00:59:54 +00001591 cmdnode->callback = callback;
David Woodhouse1309b552007-12-10 13:36:10 -05001592 cmdnode->callback_arg = callback_arg;
Holger Schurig675787e2007-12-05 17:58:11 +01001593
Dan Williams7ad994d2007-12-11 12:33:30 -05001594 /* Copy the incoming command to the buffer */
Dan Williamsddac4522007-12-11 13:49:39 -05001595 memcpy(cmdnode->cmdbuf, in_cmd, in_cmd_size);
Dan Williams7ad994d2007-12-11 12:33:30 -05001596
Holger Schurig675787e2007-12-05 17:58:11 +01001597 /* Set sequence number, clean result, move to buffer */
David Woodhouseaa21c002007-12-08 20:04:36 +00001598 priv->seqnum++;
Dan Williamsddac4522007-12-11 13:49:39 -05001599 cmdnode->cmdbuf->command = cpu_to_le16(command);
1600 cmdnode->cmdbuf->size = cpu_to_le16(in_cmd_size);
1601 cmdnode->cmdbuf->seqnum = cpu_to_le16(priv->seqnum);
1602 cmdnode->cmdbuf->result = 0;
Holger Schurig675787e2007-12-05 17:58:11 +01001603
1604 lbs_deb_host("PREP_CMD: command 0x%04x\n", command);
1605
Holger Schurig675787e2007-12-05 17:58:11 +01001606 cmdnode->cmdwaitqwoken = 0;
David Woodhouse681ffbb2007-12-15 20:04:54 -05001607 lbs_queue_cmd(priv, cmdnode);
Holger Schurig675787e2007-12-05 17:58:11 +01001608 wake_up_interruptible(&priv->waitq);
1609
David Woodhouse3399ea52007-12-15 03:09:33 -05001610 done:
1611 lbs_deb_leave_args(LBS_DEB_HOST, "ret %p", cmdnode);
1612 return cmdnode;
1613}
1614
Holger Schurig8db4a2b2008-03-19 10:11:00 +01001615void lbs_cmd_async(struct lbs_private *priv, uint16_t command,
1616 struct cmd_header *in_cmd, int in_cmd_size)
1617{
1618 lbs_deb_enter(LBS_DEB_CMD);
1619 __lbs_cmd_async(priv, command, in_cmd, in_cmd_size,
1620 lbs_cmd_async_callback, 0);
1621 lbs_deb_leave(LBS_DEB_CMD);
1622}
1623
David Woodhouse3399ea52007-12-15 03:09:33 -05001624int __lbs_cmd(struct lbs_private *priv, uint16_t command,
1625 struct cmd_header *in_cmd, int in_cmd_size,
1626 int (*callback)(struct lbs_private *, unsigned long, struct cmd_header *),
1627 unsigned long callback_arg)
1628{
1629 struct cmd_ctrl_node *cmdnode;
1630 unsigned long flags;
1631 int ret = 0;
1632
1633 lbs_deb_enter(LBS_DEB_HOST);
1634
1635 cmdnode = __lbs_cmd_async(priv, command, in_cmd, in_cmd_size,
1636 callback, callback_arg);
1637 if (IS_ERR(cmdnode)) {
1638 ret = PTR_ERR(cmdnode);
1639 goto done;
1640 }
1641
Holger Schurig675787e2007-12-05 17:58:11 +01001642 might_sleep();
1643 wait_event_interruptible(cmdnode->cmdwait_q, cmdnode->cmdwaitqwoken);
1644
David Woodhouseaa21c002007-12-08 20:04:36 +00001645 spin_lock_irqsave(&priv->driver_lock, flags);
David Woodhouseae125bf2007-12-15 04:22:52 -05001646 ret = cmdnode->result;
1647 if (ret)
1648 lbs_pr_info("PREP_CMD: command 0x%04x failed: %d\n",
1649 command, ret);
David Woodhouse3399ea52007-12-15 03:09:33 -05001650
David Woodhousead12d0f2007-12-15 02:06:16 -05001651 __lbs_cleanup_and_insert_cmd(priv, cmdnode);
David Woodhouseaa21c002007-12-08 20:04:36 +00001652 spin_unlock_irqrestore(&priv->driver_lock, flags);
Holger Schurig675787e2007-12-05 17:58:11 +01001653
1654done:
1655 lbs_deb_leave_args(LBS_DEB_HOST, "ret %d", ret);
1656 return ret;
1657}
Dan Williams14e865b2007-12-10 15:11:23 -05001658EXPORT_SYMBOL_GPL(__lbs_cmd);