blob: 70745928f3f86ed5f26eea3975133177d0b36e52 [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
Marcelo Tosatti876c9d32007-02-10 12:25:27 -020018/**
Holger Schurig8db4a2b2008-03-19 10:11:00 +010019 * @brief Simple callback that copies response back into command
20 *
21 * @param priv A pointer to struct lbs_private structure
22 * @param extra A pointer to the original command structure for which
23 * 'resp' is a response
24 * @param resp A pointer to the command response
25 *
26 * @return 0 on success, error on failure
27 */
28int lbs_cmd_copyback(struct lbs_private *priv, unsigned long extra,
29 struct cmd_header *resp)
30{
31 struct cmd_header *buf = (void *)extra;
32 uint16_t copy_len;
33
34 copy_len = min(le16_to_cpu(buf->size), le16_to_cpu(resp->size));
35 memcpy(buf, resp, copy_len);
36 return 0;
37}
38EXPORT_SYMBOL_GPL(lbs_cmd_copyback);
39
40/**
41 * @brief Simple callback that ignores the result. Use this if
42 * you just want to send a command to the hardware, but don't
43 * care for the result.
44 *
45 * @param priv ignored
46 * @param extra ignored
47 * @param resp ignored
48 *
49 * @return 0 for success
50 */
51static int lbs_cmd_async_callback(struct lbs_private *priv, unsigned long extra,
52 struct cmd_header *resp)
53{
54 return 0;
55}
56
57
58/**
Dan Williams852e1f22007-12-10 15:24:47 -050059 * @brief Checks whether a command is allowed in Power Save mode
Marcelo Tosatti876c9d32007-02-10 12:25:27 -020060 *
61 * @param command the command ID
Dan Williams852e1f22007-12-10 15:24:47 -050062 * @return 1 if allowed, 0 if not allowed
Marcelo Tosatti876c9d32007-02-10 12:25:27 -020063 */
Dan Williams852e1f22007-12-10 15:24:47 -050064static u8 is_command_allowed_in_ps(u16 cmd)
Marcelo Tosatti876c9d32007-02-10 12:25:27 -020065{
Dan Williams852e1f22007-12-10 15:24:47 -050066 switch (cmd) {
67 case CMD_802_11_RSSI:
68 return 1;
Amitkumar Karwar66fceb62010-05-19 03:24:38 -070069 case CMD_802_11_HOST_SLEEP_CFG:
70 return 1;
Dan Williams852e1f22007-12-10 15:24:47 -050071 default:
72 break;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -020073 }
Marcelo Tosatti876c9d32007-02-10 12:25:27 -020074 return 0;
75}
76
Dan Williams6e66f032007-12-11 12:42:16 -050077/**
78 * @brief Updates the hardware details like MAC address and regulatory region
79 *
80 * @param priv A pointer to struct lbs_private structure
81 *
82 * @return 0 on success, error on failure
83 */
84int lbs_update_hw_spec(struct lbs_private *priv)
Marcelo Tosatti876c9d32007-02-10 12:25:27 -020085{
Dan Williams6e66f032007-12-11 12:42:16 -050086 struct cmd_ds_get_hw_spec cmd;
87 int ret = -1;
88 u32 i;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -020089
Holger Schurig9012b282007-05-25 11:27:16 -040090 lbs_deb_enter(LBS_DEB_CMD);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -020091
Dan Williams6e66f032007-12-11 12:42:16 -050092 memset(&cmd, 0, sizeof(cmd));
93 cmd.hdr.size = cpu_to_le16(sizeof(cmd));
94 memcpy(cmd.permanentaddr, priv->current_addr, ETH_ALEN);
David Woodhouse689442d2007-12-12 16:00:42 -050095 ret = lbs_cmd_with_response(priv, CMD_GET_HW_SPEC, &cmd);
Dan Williams6e66f032007-12-11 12:42:16 -050096 if (ret)
97 goto out;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -020098
Dan Williams6e66f032007-12-11 12:42:16 -050099 priv->fwcapinfo = le32_to_cpu(cmd.fwcapinfo);
Dan Williams6e66f032007-12-11 12:42:16 -0500100
Holger Schurigdac10a92008-01-16 15:55:22 +0100101 /* The firmware release is in an interesting format: the patch
102 * level is in the most significant nibble ... so fix that: */
103 priv->fwrelease = le32_to_cpu(cmd.fwrelease);
104 priv->fwrelease = (priv->fwrelease << 8) |
105 (priv->fwrelease >> 24 & 0xff);
106
107 /* Some firmware capabilities:
108 * CF card firmware 5.0.16p0: cap 0x00000303
109 * USB dongle firmware 5.110.17p2: cap 0x00000303
110 */
Johannes Berge1749612008-10-27 15:59:26 -0700111 lbs_pr_info("%pM, fw %u.%u.%up%u, cap 0x%08x\n",
112 cmd.permanentaddr,
Holger Schurigdac10a92008-01-16 15:55:22 +0100113 priv->fwrelease >> 24 & 0xff,
114 priv->fwrelease >> 16 & 0xff,
115 priv->fwrelease >> 8 & 0xff,
116 priv->fwrelease & 0xff,
117 priv->fwcapinfo);
Dan Williams6e66f032007-12-11 12:42:16 -0500118 lbs_deb_cmd("GET_HW_SPEC: hardware interface 0x%x, hardware spec 0x%04x\n",
119 cmd.hwifversion, cmd.version);
120
121 /* Clamp region code to 8-bit since FW spec indicates that it should
122 * only ever be 8-bit, even though the field size is 16-bit. Some firmware
123 * returns non-zero high 8 bits here.
Marek Vasut15483992009-07-16 19:19:53 +0200124 *
125 * Firmware version 4.0.102 used in CF8381 has region code shifted. We
126 * need to check for this problem and handle it properly.
Dan Williams6e66f032007-12-11 12:42:16 -0500127 */
Marek Vasut15483992009-07-16 19:19:53 +0200128 if (MRVL_FW_MAJOR_REV(priv->fwrelease) == MRVL_FW_V4)
129 priv->regioncode = (le16_to_cpu(cmd.regioncode) >> 8) & 0xFF;
130 else
131 priv->regioncode = le16_to_cpu(cmd.regioncode) & 0xFF;
Dan Williams6e66f032007-12-11 12:42:16 -0500132
133 for (i = 0; i < MRVDRV_MAX_REGION_CODE; i++) {
134 /* use the region code to search for the index */
135 if (priv->regioncode == lbs_region_code_to_index[i])
136 break;
137 }
138
139 /* if it's unidentified region code, use the default (USA) */
140 if (i >= MRVDRV_MAX_REGION_CODE) {
141 priv->regioncode = 0x10;
142 lbs_pr_info("unidentified region code; using the default (USA)\n");
143 }
144
145 if (priv->current_addr[0] == 0xff)
146 memmove(priv->current_addr, cmd.permanentaddr, ETH_ALEN);
147
148 memcpy(priv->dev->dev_addr, priv->current_addr, ETH_ALEN);
149 if (priv->mesh_dev)
150 memcpy(priv->mesh_dev->dev_addr, priv->current_addr, ETH_ALEN);
151
Dan Williams6e66f032007-12-11 12:42:16 -0500152out:
Holger Schurig9012b282007-05-25 11:27:16 -0400153 lbs_deb_leave(LBS_DEB_CMD);
Dan Williams6e66f032007-12-11 12:42:16 -0500154 return ret;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200155}
156
Amitkumar Karwar66fceb62010-05-19 03:24:38 -0700157static int lbs_ret_host_sleep_cfg(struct lbs_private *priv, unsigned long dummy,
158 struct cmd_header *resp)
159{
160 lbs_deb_enter(LBS_DEB_CMD);
Amitkumar Karwar13118432010-07-08 06:43:48 +0530161 if (priv->is_host_sleep_activated) {
Amitkumar Karwar66fceb62010-05-19 03:24:38 -0700162 priv->is_host_sleep_configured = 0;
163 if (priv->psstate == PS_STATE_FULL_POWER) {
164 priv->is_host_sleep_activated = 0;
165 wake_up_interruptible(&priv->host_sleep_q);
166 }
167 } else {
168 priv->is_host_sleep_configured = 1;
169 }
170 lbs_deb_leave(LBS_DEB_CMD);
171 return 0;
172}
173
Anna Neal582c1b52008-10-20 16:46:56 -0700174int lbs_host_sleep_cfg(struct lbs_private *priv, uint32_t criteria,
175 struct wol_config *p_wol_config)
David Woodhouse6ce4fd22007-12-12 15:19:29 -0500176{
177 struct cmd_ds_host_sleep cmd_config;
178 int ret;
179
David Woodhouse9fae8992007-12-15 03:46:44 -0500180 cmd_config.hdr.size = cpu_to_le16(sizeof(cmd_config));
David Woodhouse6ce4fd22007-12-12 15:19:29 -0500181 cmd_config.criteria = cpu_to_le32(criteria);
David Woodhouse506e9022007-12-12 20:06:06 -0500182 cmd_config.gpio = priv->wol_gpio;
183 cmd_config.gap = priv->wol_gap;
David Woodhouse6ce4fd22007-12-12 15:19:29 -0500184
Anna Neal582c1b52008-10-20 16:46:56 -0700185 if (p_wol_config != NULL)
186 memcpy((uint8_t *)&cmd_config.wol_conf, (uint8_t *)p_wol_config,
187 sizeof(struct wol_config));
188 else
189 cmd_config.wol_conf.action = CMD_ACT_ACTION_NONE;
190
Amitkumar Karwar66fceb62010-05-19 03:24:38 -0700191 ret = __lbs_cmd(priv, CMD_802_11_HOST_SLEEP_CFG, &cmd_config.hdr,
192 le16_to_cpu(cmd_config.hdr.size),
193 lbs_ret_host_sleep_cfg, 0);
David Woodhouse506e9022007-12-12 20:06:06 -0500194 if (!ret) {
Amitkumar Karwar66fceb62010-05-19 03:24:38 -0700195 if (p_wol_config)
Anna Neal582c1b52008-10-20 16:46:56 -0700196 memcpy((uint8_t *) p_wol_config,
197 (uint8_t *)&cmd_config.wol_conf,
198 sizeof(struct wol_config));
David Woodhouse506e9022007-12-12 20:06:06 -0500199 } else {
David Woodhouse6ce4fd22007-12-12 15:19:29 -0500200 lbs_pr_info("HOST_SLEEP_CFG failed %d\n", ret);
David Woodhouse6ce4fd22007-12-12 15:19:29 -0500201 }
David Woodhouse506e9022007-12-12 20:06:06 -0500202
David Woodhouse6ce4fd22007-12-12 15:19:29 -0500203 return ret;
204}
205EXPORT_SYMBOL_GPL(lbs_host_sleep_cfg);
206
Dan Williams0bb64082010-07-27 13:08:08 -0700207/**
208 * @brief Sets the Power Save mode
209 *
210 * @param priv A pointer to struct lbs_private structure
211 * @param cmd_action The Power Save operation (PS_MODE_ACTION_ENTER_PS or
212 * PS_MODE_ACTION_EXIT_PS)
213 * @param block Whether to block on a response or not
214 *
215 * @return 0 on success, error on failure
216 */
217int lbs_set_ps_mode(struct lbs_private *priv, u16 cmd_action, bool block)
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200218{
Dan Williams0bb64082010-07-27 13:08:08 -0700219 struct cmd_ds_802_11_ps_mode cmd;
220 int ret = 0;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200221
Holger Schurig9012b282007-05-25 11:27:16 -0400222 lbs_deb_enter(LBS_DEB_CMD);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200223
Dan Williams0bb64082010-07-27 13:08:08 -0700224 memset(&cmd, 0, sizeof(cmd));
225 cmd.hdr.size = cpu_to_le16(sizeof(cmd));
226 cmd.action = cpu_to_le16(cmd_action);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200227
Dan Williams0bb64082010-07-27 13:08:08 -0700228 if (cmd_action == PS_MODE_ACTION_ENTER_PS) {
229 lbs_deb_cmd("PS_MODE: action ENTER_PS\n");
230 cmd.multipledtim = cpu_to_le16(1); /* Default DTIM multiple */
231 } else if (cmd_action == PS_MODE_ACTION_EXIT_PS) {
232 lbs_deb_cmd("PS_MODE: action EXIT_PS\n");
233 } else {
234 /* We don't handle CONFIRM_SLEEP here because it needs to
235 * be fastpathed to the firmware.
236 */
237 lbs_deb_cmd("PS_MODE: unknown action 0x%X\n", cmd_action);
238 ret = -EOPNOTSUPP;
239 goto out;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200240 }
241
Dan Williams0bb64082010-07-27 13:08:08 -0700242 if (block)
243 ret = lbs_cmd_with_response(priv, CMD_802_11_PS_MODE, &cmd);
244 else
245 lbs_cmd_async(priv, CMD_802_11_PS_MODE, &cmd.hdr, sizeof (cmd));
246
247out:
248 lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret);
249 return ret;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200250}
251
David Woodhouse3fbe1042007-12-17 23:48:31 -0500252int lbs_cmd_802_11_sleep_params(struct lbs_private *priv, uint16_t cmd_action,
253 struct sleep_params *sp)
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200254{
David Woodhouse3fbe1042007-12-17 23:48:31 -0500255 struct cmd_ds_802_11_sleep_params cmd;
256 int ret;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200257
Holger Schurig9012b282007-05-25 11:27:16 -0400258 lbs_deb_enter(LBS_DEB_CMD);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200259
Dan Williams0aef64d2007-08-02 11:31:18 -0400260 if (cmd_action == CMD_ACT_GET) {
David Woodhouse3fbe1042007-12-17 23:48:31 -0500261 memset(&cmd, 0, sizeof(cmd));
262 } else {
263 cmd.error = cpu_to_le16(sp->sp_error);
264 cmd.offset = cpu_to_le16(sp->sp_offset);
265 cmd.stabletime = cpu_to_le16(sp->sp_stabletime);
266 cmd.calcontrol = sp->sp_calcontrol;
267 cmd.externalsleepclk = sp->sp_extsleepclk;
268 cmd.reserved = cpu_to_le16(sp->sp_reserved);
269 }
270 cmd.hdr.size = cpu_to_le16(sizeof(cmd));
271 cmd.action = cpu_to_le16(cmd_action);
272
273 ret = lbs_cmd_with_response(priv, CMD_802_11_SLEEP_PARAMS, &cmd);
274
275 if (!ret) {
276 lbs_deb_cmd("error 0x%x, offset 0x%x, stabletime 0x%x, "
277 "calcontrol 0x%x extsleepclk 0x%x\n",
278 le16_to_cpu(cmd.error), le16_to_cpu(cmd.offset),
279 le16_to_cpu(cmd.stabletime), cmd.calcontrol,
280 cmd.externalsleepclk);
281
282 sp->sp_error = le16_to_cpu(cmd.error);
283 sp->sp_offset = le16_to_cpu(cmd.offset);
284 sp->sp_stabletime = le16_to_cpu(cmd.stabletime);
285 sp->sp_calcontrol = cmd.calcontrol;
286 sp->sp_extsleepclk = cmd.externalsleepclk;
287 sp->sp_reserved = le16_to_cpu(cmd.reserved);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200288 }
289
David Woodhouse3fbe1042007-12-17 23:48:31 -0500290 lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200291 return 0;
292}
293
Amitkumar Karwar49125452009-09-30 20:04:38 -0700294static int lbs_wait_for_ds_awake(struct lbs_private *priv)
295{
296 int ret = 0;
297
298 lbs_deb_enter(LBS_DEB_CMD);
299
300 if (priv->is_deep_sleep) {
301 if (!wait_event_interruptible_timeout(priv->ds_awake_q,
302 !priv->is_deep_sleep, (10 * HZ))) {
303 lbs_pr_err("ds_awake_q: timer expired\n");
304 ret = -1;
305 }
306 }
307
308 lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret);
309 return ret;
310}
311
312int lbs_set_deep_sleep(struct lbs_private *priv, int deep_sleep)
313{
314 int ret = 0;
315
316 lbs_deb_enter(LBS_DEB_CMD);
317
318 if (deep_sleep) {
319 if (priv->is_deep_sleep != 1) {
320 lbs_deb_cmd("deep sleep: sleep\n");
321 BUG_ON(!priv->enter_deep_sleep);
322 ret = priv->enter_deep_sleep(priv);
323 if (!ret) {
324 netif_stop_queue(priv->dev);
325 netif_carrier_off(priv->dev);
326 }
327 } else {
328 lbs_pr_err("deep sleep: already enabled\n");
329 }
330 } else {
331 if (priv->is_deep_sleep) {
332 lbs_deb_cmd("deep sleep: wakeup\n");
333 BUG_ON(!priv->exit_deep_sleep);
334 ret = priv->exit_deep_sleep(priv);
335 if (!ret) {
336 ret = lbs_wait_for_ds_awake(priv);
337 if (ret)
338 lbs_pr_err("deep sleep: wakeup"
339 "failed\n");
340 }
341 }
342 }
343
344 lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret);
345 return ret;
346}
347
Amitkumar Karwar13118432010-07-08 06:43:48 +0530348static int lbs_ret_host_sleep_activate(struct lbs_private *priv,
349 unsigned long dummy,
350 struct cmd_header *cmd)
351{
352 lbs_deb_enter(LBS_DEB_FW);
353 priv->is_host_sleep_activated = 1;
354 wake_up_interruptible(&priv->host_sleep_q);
355 lbs_deb_leave(LBS_DEB_FW);
356 return 0;
357}
358
359int lbs_set_host_sleep(struct lbs_private *priv, int host_sleep)
360{
361 struct cmd_header cmd;
362 int ret = 0;
363 uint32_t criteria = EHS_REMOVE_WAKEUP;
364
365 lbs_deb_enter(LBS_DEB_CMD);
366
367 if (host_sleep) {
368 if (priv->is_host_sleep_activated != 1) {
369 memset(&cmd, 0, sizeof(cmd));
370 ret = lbs_host_sleep_cfg(priv, priv->wol_criteria,
371 (struct wol_config *)NULL);
372 if (ret) {
373 lbs_pr_info("Host sleep configuration failed: "
374 "%d\n", ret);
375 return ret;
376 }
377 if (priv->psstate == PS_STATE_FULL_POWER) {
378 ret = __lbs_cmd(priv,
379 CMD_802_11_HOST_SLEEP_ACTIVATE,
380 &cmd,
381 sizeof(cmd),
382 lbs_ret_host_sleep_activate, 0);
383 if (ret)
384 lbs_pr_info("HOST_SLEEP_ACTIVATE "
385 "failed: %d\n", ret);
386 }
387
388 if (!wait_event_interruptible_timeout(
389 priv->host_sleep_q,
390 priv->is_host_sleep_activated,
391 (10 * HZ))) {
392 lbs_pr_err("host_sleep_q: timer expired\n");
393 ret = -1;
394 }
395 } else {
396 lbs_pr_err("host sleep: already enabled\n");
397 }
398 } else {
399 if (priv->is_host_sleep_activated)
400 ret = lbs_host_sleep_cfg(priv, criteria,
401 (struct wol_config *)NULL);
402 }
403
404 return ret;
405}
406
Dan Williams39fcf7a2008-09-10 12:49:00 -0400407/**
408 * @brief Set an SNMP MIB value
409 *
410 * @param priv A pointer to struct lbs_private structure
411 * @param oid The OID to set in the firmware
412 * @param val Value to set the OID to
413 *
414 * @return 0 on success, error on failure
415 */
416int lbs_set_snmp_mib(struct lbs_private *priv, u32 oid, u16 val)
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200417{
Dan Williams39fcf7a2008-09-10 12:49:00 -0400418 struct cmd_ds_802_11_snmp_mib cmd;
419 int ret;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200420
Holger Schurig9012b282007-05-25 11:27:16 -0400421 lbs_deb_enter(LBS_DEB_CMD);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200422
Dan Williams39fcf7a2008-09-10 12:49:00 -0400423 memset(&cmd, 0, sizeof (cmd));
424 cmd.hdr.size = cpu_to_le16(sizeof(cmd));
425 cmd.action = cpu_to_le16(CMD_ACT_SET);
426 cmd.oid = cpu_to_le16((u16) oid);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200427
Dan Williams39fcf7a2008-09-10 12:49:00 -0400428 switch (oid) {
429 case SNMP_MIB_OID_BSS_TYPE:
430 cmd.bufsize = cpu_to_le16(sizeof(u8));
Holger Schurigfef06402009-10-22 15:30:59 +0200431 cmd.value[0] = val;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200432 break;
Dan Williams39fcf7a2008-09-10 12:49:00 -0400433 case SNMP_MIB_OID_11D_ENABLE:
434 case SNMP_MIB_OID_FRAG_THRESHOLD:
435 case SNMP_MIB_OID_RTS_THRESHOLD:
436 case SNMP_MIB_OID_SHORT_RETRY_LIMIT:
437 case SNMP_MIB_OID_LONG_RETRY_LIMIT:
438 cmd.bufsize = cpu_to_le16(sizeof(u16));
439 *((__le16 *)(&cmd.value)) = cpu_to_le16(val);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200440 break;
441 default:
Dan Williams39fcf7a2008-09-10 12:49:00 -0400442 lbs_deb_cmd("SNMP_CMD: (set) unhandled OID 0x%x\n", oid);
443 ret = -EINVAL;
444 goto out;
445 }
446
447 lbs_deb_cmd("SNMP_CMD: (set) oid 0x%x, oid size 0x%x, value 0x%x\n",
448 le16_to_cpu(cmd.oid), le16_to_cpu(cmd.bufsize), val);
449
450 ret = lbs_cmd_with_response(priv, CMD_802_11_SNMP_MIB, &cmd);
451
452out:
453 lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret);
454 return ret;
455}
456
457/**
458 * @brief Get an SNMP MIB value
459 *
460 * @param priv A pointer to struct lbs_private structure
461 * @param oid The OID to retrieve from the firmware
462 * @param out_val Location for the returned value
463 *
464 * @return 0 on success, error on failure
465 */
466int lbs_get_snmp_mib(struct lbs_private *priv, u32 oid, u16 *out_val)
467{
468 struct cmd_ds_802_11_snmp_mib cmd;
469 int ret;
470
471 lbs_deb_enter(LBS_DEB_CMD);
472
473 memset(&cmd, 0, sizeof (cmd));
474 cmd.hdr.size = cpu_to_le16(sizeof(cmd));
475 cmd.action = cpu_to_le16(CMD_ACT_GET);
476 cmd.oid = cpu_to_le16(oid);
477
478 ret = lbs_cmd_with_response(priv, CMD_802_11_SNMP_MIB, &cmd);
479 if (ret)
480 goto out;
481
482 switch (le16_to_cpu(cmd.bufsize)) {
483 case sizeof(u8):
Holger Schurigfef06402009-10-22 15:30:59 +0200484 *out_val = cmd.value[0];
Dan Williams39fcf7a2008-09-10 12:49:00 -0400485 break;
486 case sizeof(u16):
487 *out_val = le16_to_cpu(*((__le16 *)(&cmd.value)));
488 break;
489 default:
490 lbs_deb_cmd("SNMP_CMD: (get) unhandled OID 0x%x size %d\n",
491 oid, le16_to_cpu(cmd.bufsize));
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200492 break;
493 }
494
Dan Williams39fcf7a2008-09-10 12:49:00 -0400495out:
496 lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret);
497 return ret;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200498}
499
Dan Williams87c8c722008-08-19 15:15:35 -0400500/**
501 * @brief Get the min, max, and current TX power
502 *
503 * @param priv A pointer to struct lbs_private structure
504 * @param curlevel Current power level in dBm
505 * @param minlevel Minimum supported power level in dBm (optional)
506 * @param maxlevel Maximum supported power level in dBm (optional)
507 *
508 * @return 0 on success, error on failure
509 */
510int lbs_get_tx_power(struct lbs_private *priv, s16 *curlevel, s16 *minlevel,
511 s16 *maxlevel)
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200512{
Dan Williams87c8c722008-08-19 15:15:35 -0400513 struct cmd_ds_802_11_rf_tx_power cmd;
514 int ret;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200515
Holger Schurig9012b282007-05-25 11:27:16 -0400516 lbs_deb_enter(LBS_DEB_CMD);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200517
Dan Williams87c8c722008-08-19 15:15:35 -0400518 memset(&cmd, 0, sizeof(cmd));
519 cmd.hdr.size = cpu_to_le16(sizeof(cmd));
520 cmd.action = cpu_to_le16(CMD_ACT_GET);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200521
Dan Williams87c8c722008-08-19 15:15:35 -0400522 ret = lbs_cmd_with_response(priv, CMD_802_11_RF_TX_POWER, &cmd);
523 if (ret == 0) {
524 *curlevel = le16_to_cpu(cmd.curlevel);
525 if (minlevel)
Holger Schurig87bf24f2008-10-29 10:35:02 +0100526 *minlevel = cmd.minlevel;
Dan Williams87c8c722008-08-19 15:15:35 -0400527 if (maxlevel)
Holger Schurig87bf24f2008-10-29 10:35:02 +0100528 *maxlevel = cmd.maxlevel;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200529 }
Holger Schurig9012b282007-05-25 11:27:16 -0400530
531 lbs_deb_leave(LBS_DEB_CMD);
Dan Williams87c8c722008-08-19 15:15:35 -0400532 return ret;
533}
534
535/**
536 * @brief Set the TX power
537 *
538 * @param priv A pointer to struct lbs_private structure
539 * @param dbm The desired power level in dBm
540 *
541 * @return 0 on success, error on failure
542 */
543int lbs_set_tx_power(struct lbs_private *priv, s16 dbm)
544{
545 struct cmd_ds_802_11_rf_tx_power cmd;
546 int ret;
547
548 lbs_deb_enter(LBS_DEB_CMD);
549
550 memset(&cmd, 0, sizeof(cmd));
551 cmd.hdr.size = cpu_to_le16(sizeof(cmd));
552 cmd.action = cpu_to_le16(CMD_ACT_SET);
553 cmd.curlevel = cpu_to_le16(dbm);
554
555 lbs_deb_cmd("SET_RF_TX_POWER: %d dBm\n", dbm);
556
557 ret = lbs_cmd_with_response(priv, CMD_802_11_RF_TX_POWER, &cmd);
558
559 lbs_deb_leave(LBS_DEB_CMD);
560 return ret;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200561}
562
Dan Williamsa45b6f42010-07-27 12:54:34 -0700563/**
564 * @brief Enable or disable monitor mode (only implemented on OLPC usb8388 FW)
565 *
566 * @param priv A pointer to struct lbs_private structure
567 * @param enable 1 to enable monitor mode, 0 to disable
568 *
569 * @return 0 on success, error on failure
570 */
571int lbs_set_monitor_mode(struct lbs_private *priv, int enable)
Luis Carlos Cobo965f8bb2007-08-02 13:16:55 -0400572{
Dan Williamsa45b6f42010-07-27 12:54:34 -0700573 struct cmd_ds_802_11_monitor_mode cmd;
574 int ret;
Luis Carlos Cobo965f8bb2007-08-02 13:16:55 -0400575
Dan Williamsa45b6f42010-07-27 12:54:34 -0700576 memset(&cmd, 0, sizeof(cmd));
577 cmd.hdr.size = cpu_to_le16(sizeof(cmd));
578 cmd.action = cpu_to_le16(CMD_ACT_SET);
579 if (enable)
580 cmd.mode = cpu_to_le16(0x1);
Luis Carlos Cobo965f8bb2007-08-02 13:16:55 -0400581
Dan Williamsa45b6f42010-07-27 12:54:34 -0700582 lbs_deb_cmd("SET_MONITOR_MODE: %d\n", enable);
583
584 ret = lbs_cmd_with_response(priv, CMD_802_11_MONITOR_MODE, &cmd);
585 if (ret == 0) {
586 priv->dev->type = enable ? ARPHRD_IEEE80211_RADIOTAP :
587 ARPHRD_ETHER;
Luis Carlos Cobo965f8bb2007-08-02 13:16:55 -0400588 }
589
Dan Williamsa45b6f42010-07-27 12:54:34 -0700590 lbs_deb_leave(LBS_DEB_CMD);
591 return ret;
Luis Carlos Cobo965f8bb2007-08-02 13:16:55 -0400592}
593
Dan Williams2dd4b262007-12-11 16:54:15 -0500594/**
595 * @brief Get the radio channel
596 *
597 * @param priv A pointer to struct lbs_private structure
598 *
599 * @return The channel on success, error on failure
600 */
Holger Schuriga3cbfb02009-10-16 17:33:56 +0200601static int lbs_get_channel(struct lbs_private *priv)
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200602{
Dan Williams2dd4b262007-12-11 16:54:15 -0500603 struct cmd_ds_802_11_rf_channel cmd;
604 int ret = 0;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200605
Holger Schurig8ff12da2007-08-02 11:54:31 -0400606 lbs_deb_enter(LBS_DEB_CMD);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200607
Holger Schurig8d0c7fa2008-04-09 10:23:31 +0200608 memset(&cmd, 0, sizeof(cmd));
Dan Williams2dd4b262007-12-11 16:54:15 -0500609 cmd.hdr.size = cpu_to_le16(sizeof(cmd));
610 cmd.action = cpu_to_le16(CMD_OPT_802_11_RF_CHANNEL_GET);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200611
David Woodhouse689442d2007-12-12 16:00:42 -0500612 ret = lbs_cmd_with_response(priv, CMD_802_11_RF_CHANNEL, &cmd);
Dan Williams2dd4b262007-12-11 16:54:15 -0500613 if (ret)
614 goto out;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200615
Dan Williamscb182a62007-12-11 17:35:51 -0500616 ret = le16_to_cpu(cmd.channel);
617 lbs_deb_cmd("current radio channel is %d\n", ret);
Dan Williams2dd4b262007-12-11 16:54:15 -0500618
619out:
620 lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret);
621 return ret;
622}
623
Holger Schurig73ab1f22008-04-02 16:52:19 +0200624int lbs_update_channel(struct lbs_private *priv)
625{
626 int ret;
627
628 /* the channel in f/w could be out of sync; get the current channel */
629 lbs_deb_enter(LBS_DEB_ASSOC);
630
631 ret = lbs_get_channel(priv);
632 if (ret > 0) {
Holger Schurigc14951f2009-10-22 15:30:50 +0200633 priv->channel = ret;
Holger Schurig73ab1f22008-04-02 16:52:19 +0200634 ret = 0;
635 }
636 lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret);
637 return ret;
638}
639
Dan Williams2dd4b262007-12-11 16:54:15 -0500640/**
641 * @brief Set the radio channel
642 *
643 * @param priv A pointer to struct lbs_private structure
644 * @param channel The desired channel, or 0 to clear a locked channel
645 *
646 * @return 0 on success, error on failure
647 */
648int lbs_set_channel(struct lbs_private *priv, u8 channel)
649{
650 struct cmd_ds_802_11_rf_channel cmd;
Manish Katiyar96d46d52008-10-13 16:22:42 +0530651#ifdef DEBUG
Holger Schurigc14951f2009-10-22 15:30:50 +0200652 u8 old_channel = priv->channel;
Manish Katiyar96d46d52008-10-13 16:22:42 +0530653#endif
Dan Williams2dd4b262007-12-11 16:54:15 -0500654 int ret = 0;
655
656 lbs_deb_enter(LBS_DEB_CMD);
657
Holger Schurig8d0c7fa2008-04-09 10:23:31 +0200658 memset(&cmd, 0, sizeof(cmd));
Dan Williams2dd4b262007-12-11 16:54:15 -0500659 cmd.hdr.size = cpu_to_le16(sizeof(cmd));
660 cmd.action = cpu_to_le16(CMD_OPT_802_11_RF_CHANNEL_SET);
661 cmd.channel = cpu_to_le16(channel);
662
David Woodhouse689442d2007-12-12 16:00:42 -0500663 ret = lbs_cmd_with_response(priv, CMD_802_11_RF_CHANNEL, &cmd);
Dan Williams2dd4b262007-12-11 16:54:15 -0500664 if (ret)
665 goto out;
666
Holger Schurigc14951f2009-10-22 15:30:50 +0200667 priv->channel = (uint8_t) le16_to_cpu(cmd.channel);
Dan Williamscb182a62007-12-11 17:35:51 -0500668 lbs_deb_cmd("channel switch from %d to %d\n", old_channel,
Holger Schurigc14951f2009-10-22 15:30:50 +0200669 priv->channel);
Dan Williams2dd4b262007-12-11 16:54:15 -0500670
671out:
672 lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret);
673 return ret;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200674}
675
Dan Williams9fb76632010-07-27 12:55:21 -0700676/**
677 * @brief Get current RSSI and noise floor
678 *
679 * @param priv A pointer to struct lbs_private structure
680 * @param rssi On successful return, signal level in mBm
681 *
682 * @return The channel on success, error on failure
683 */
684int lbs_get_rssi(struct lbs_private *priv, s8 *rssi, s8 *nf)
685{
686 struct cmd_ds_802_11_rssi cmd;
687 int ret = 0;
688
689 lbs_deb_enter(LBS_DEB_CMD);
690
691 BUG_ON(rssi == NULL);
692 BUG_ON(nf == NULL);
693
694 memset(&cmd, 0, sizeof(cmd));
695 cmd.hdr.size = cpu_to_le16(sizeof(cmd));
696 /* Average SNR over last 8 beacons */
697 cmd.n_or_snr = cpu_to_le16(8);
698
699 ret = lbs_cmd_with_response(priv, CMD_802_11_RSSI, &cmd);
700 if (ret == 0) {
701 *nf = CAL_NF(le16_to_cpu(cmd.nf));
702 *rssi = CAL_RSSI(le16_to_cpu(cmd.n_or_snr), le16_to_cpu(cmd.nf));
703 }
704
705 lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret);
706 return ret;
707}
708
Dan Williamscc4b9d32010-07-27 12:56:05 -0700709/**
710 * @brief Send regulatory and 802.11d domain information to the firmware
711 *
712 * @param priv pointer to struct lbs_private
713 * @param request cfg80211 regulatory request structure
714 * @param bands the device's supported bands and channels
715 *
716 * @return 0 on success, error code on failure
717*/
718int lbs_set_11d_domain_info(struct lbs_private *priv,
719 struct regulatory_request *request,
720 struct ieee80211_supported_band **bands)
721{
722 struct cmd_ds_802_11d_domain_info cmd;
723 struct mrvl_ie_domain_param_set *domain = &cmd.domain;
724 struct ieee80211_country_ie_triplet *t;
725 enum ieee80211_band band;
726 struct ieee80211_channel *ch;
727 u8 num_triplet = 0;
728 u8 num_parsed_chan = 0;
729 u8 first_channel = 0, next_chan = 0, max_pwr = 0;
730 u8 i, flag = 0;
731 size_t triplet_size;
732 int ret;
733
734 lbs_deb_enter(LBS_DEB_11D);
735
736 memset(&cmd, 0, sizeof(cmd));
737 cmd.action = cpu_to_le16(CMD_ACT_SET);
738
739 lbs_deb_11d("Setting country code '%c%c'\n",
740 request->alpha2[0], request->alpha2[1]);
741
742 domain->header.type = cpu_to_le16(TLV_TYPE_DOMAIN);
743
744 /* Set country code */
745 domain->country_code[0] = request->alpha2[0];
746 domain->country_code[1] = request->alpha2[1];
747 domain->country_code[2] = ' ';
748
749 /* Now set up the channel triplets; firmware is somewhat picky here
750 * and doesn't validate channel numbers and spans; hence it would
751 * interpret a triplet of (36, 4, 20) as channels 36, 37, 38, 39. Since
752 * the last 3 aren't valid channels, the driver is responsible for
753 * splitting that up into 4 triplet pairs of (36, 1, 20) + (40, 1, 20)
754 * etc.
755 */
756 for (band = 0;
757 (band < IEEE80211_NUM_BANDS) && (num_triplet < MAX_11D_TRIPLETS);
758 band++) {
759
760 if (!bands[band])
761 continue;
762
763 for (i = 0;
764 (i < bands[band]->n_channels) && (num_triplet < MAX_11D_TRIPLETS);
765 i++) {
766 ch = &bands[band]->channels[i];
767 if (ch->flags & IEEE80211_CHAN_DISABLED)
768 continue;
769
770 if (!flag) {
771 flag = 1;
772 next_chan = first_channel = (u32) ch->hw_value;
773 max_pwr = ch->max_power;
774 num_parsed_chan = 1;
775 continue;
776 }
777
778 if ((ch->hw_value == next_chan + 1) &&
779 (ch->max_power == max_pwr)) {
780 /* Consolidate adjacent channels */
781 next_chan++;
782 num_parsed_chan++;
783 } else {
784 /* Add this triplet */
785 lbs_deb_11d("11D triplet (%d, %d, %d)\n",
786 first_channel, num_parsed_chan,
787 max_pwr);
788 t = &domain->triplet[num_triplet];
789 t->chans.first_channel = first_channel;
790 t->chans.num_channels = num_parsed_chan;
791 t->chans.max_power = max_pwr;
792 num_triplet++;
793 flag = 0;
794 }
795 }
796
797 if (flag) {
798 /* Add last triplet */
799 lbs_deb_11d("11D triplet (%d, %d, %d)\n", first_channel,
800 num_parsed_chan, max_pwr);
801 t = &domain->triplet[num_triplet];
802 t->chans.first_channel = first_channel;
803 t->chans.num_channels = num_parsed_chan;
804 t->chans.max_power = max_pwr;
805 num_triplet++;
806 }
807 }
808
809 lbs_deb_11d("# triplets %d\n", num_triplet);
810
811 /* Set command header sizes */
812 triplet_size = num_triplet * sizeof(struct ieee80211_country_ie_triplet);
813 domain->header.len = cpu_to_le16(sizeof(domain->country_code) +
814 triplet_size);
815
816 lbs_deb_hex(LBS_DEB_11D, "802.11D domain param set",
817 (u8 *) &cmd.domain.country_code,
818 le16_to_cpu(domain->header.len));
819
820 cmd.hdr.size = cpu_to_le16(sizeof(cmd.hdr) +
821 sizeof(cmd.action) +
822 sizeof(cmd.domain.header) +
823 sizeof(cmd.domain.country_code) +
824 triplet_size);
825
826 ret = lbs_cmd_with_response(priv, CMD_802_11D_DOMAIN_INFO, &cmd);
827
828 lbs_deb_leave_args(LBS_DEB_11D, "ret %d", ret);
829 return ret;
830}
831
Dan Williams4c7c6e002010-07-27 13:01:07 -0700832/**
833 * @brief Read a MAC, Baseband, or RF register
834 *
835 * @param priv pointer to struct lbs_private
836 * @param cmd register command, one of CMD_MAC_REG_ACCESS,
837 * CMD_BBP_REG_ACCESS, or CMD_RF_REG_ACCESS
838 * @param offset byte offset of the register to get
839 * @param value on success, the value of the register at 'offset'
840 *
841 * @return 0 on success, error code on failure
842*/
843int lbs_get_reg(struct lbs_private *priv, u16 reg, u16 offset, u32 *value)
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200844{
Dan Williams4c7c6e002010-07-27 13:01:07 -0700845 struct cmd_ds_reg_access cmd;
846 int ret = 0;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200847
Holger Schurig9012b282007-05-25 11:27:16 -0400848 lbs_deb_enter(LBS_DEB_CMD);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200849
Dan Williams4c7c6e002010-07-27 13:01:07 -0700850 BUG_ON(value == NULL);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200851
Dan Williams4c7c6e002010-07-27 13:01:07 -0700852 memset(&cmd, 0, sizeof(cmd));
853 cmd.hdr.size = cpu_to_le16(sizeof(cmd));
854 cmd.action = cpu_to_le16(CMD_ACT_GET);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200855
Dan Williams4c7c6e002010-07-27 13:01:07 -0700856 if (reg != CMD_MAC_REG_ACCESS &&
857 reg != CMD_BBP_REG_ACCESS &&
858 reg != CMD_RF_REG_ACCESS) {
859 ret = -EINVAL;
860 goto out;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200861 }
862
Dan Williams4c7c6e002010-07-27 13:01:07 -0700863 ret = lbs_cmd_with_response(priv, reg, &cmd);
864 if (ret) {
865 if (reg == CMD_BBP_REG_ACCESS || reg == CMD_RF_REG_ACCESS)
866 *value = cmd.value.bbp_rf;
867 else if (reg == CMD_MAC_REG_ACCESS)
868 *value = le32_to_cpu(cmd.value.mac);
869 }
870
871out:
872 lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret);
873 return ret;
874}
875
876/**
877 * @brief Write a MAC, Baseband, or RF register
878 *
879 * @param priv pointer to struct lbs_private
880 * @param cmd register command, one of CMD_MAC_REG_ACCESS,
881 * CMD_BBP_REG_ACCESS, or CMD_RF_REG_ACCESS
882 * @param offset byte offset of the register to set
883 * @param value the value to write to the register at 'offset'
884 *
885 * @return 0 on success, error code on failure
886*/
887int lbs_set_reg(struct lbs_private *priv, u16 reg, u16 offset, u32 value)
888{
889 struct cmd_ds_reg_access cmd;
890 int ret = 0;
891
892 lbs_deb_enter(LBS_DEB_CMD);
893
894 memset(&cmd, 0, sizeof(cmd));
895 cmd.hdr.size = cpu_to_le16(sizeof(cmd));
896 cmd.action = cpu_to_le16(CMD_ACT_SET);
897
898 if (reg == CMD_BBP_REG_ACCESS || reg == CMD_RF_REG_ACCESS)
899 cmd.value.bbp_rf = (u8) (value & 0xFF);
900 else if (reg == CMD_MAC_REG_ACCESS)
901 cmd.value.mac = cpu_to_le32(value);
902 else {
903 ret = -EINVAL;
904 goto out;
905 }
906
907 ret = lbs_cmd_with_response(priv, reg, &cmd);
908
909out:
910 lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret);
911 return ret;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200912}
913
David Woodhouse681ffbb2007-12-15 20:04:54 -0500914static void lbs_queue_cmd(struct lbs_private *priv,
915 struct cmd_ctrl_node *cmdnode)
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200916{
917 unsigned long flags;
David Woodhouse681ffbb2007-12-15 20:04:54 -0500918 int addtail = 1;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200919
Holger Schurig8ff12da2007-08-02 11:54:31 -0400920 lbs_deb_enter(LBS_DEB_HOST);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200921
David Woodhousec4ab4122007-12-15 00:41:51 -0500922 if (!cmdnode) {
923 lbs_deb_host("QUEUE_CMD: cmdnode is NULL\n");
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200924 goto done;
925 }
David Woodhoused9896ee2007-12-15 00:09:25 -0500926 if (!cmdnode->cmdbuf->size) {
927 lbs_deb_host("DNLD_CMD: cmd size is zero\n");
928 goto done;
929 }
David Woodhouseae125bf2007-12-15 04:22:52 -0500930 cmdnode->result = 0;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200931
932 /* Exit_PS command needs to be queued in the header always. */
Dan Williamsddac4522007-12-11 13:49:39 -0500933 if (le16_to_cpu(cmdnode->cmdbuf->command) == CMD_802_11_PS_MODE) {
Dan Williams0bb64082010-07-27 13:08:08 -0700934 struct cmd_ds_802_11_ps_mode *psm = (void *) &cmdnode->cmdbuf;
Dan Williamsddac4522007-12-11 13:49:39 -0500935
Dan Williams0bb64082010-07-27 13:08:08 -0700936 if (psm->action == cpu_to_le16(PS_MODE_ACTION_EXIT_PS)) {
David Woodhouseaa21c002007-12-08 20:04:36 +0000937 if (priv->psstate != PS_STATE_FULL_POWER)
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200938 addtail = 0;
939 }
940 }
941
Dan Williams0bb64082010-07-27 13:08:08 -0700942 if (le16_to_cpu(cmdnode->cmdbuf->command) == CMD_802_11_WAKEUP_CONFIRM)
Amitkumar Karwar66fceb62010-05-19 03:24:38 -0700943 addtail = 0;
944
David Woodhouseaa21c002007-12-08 20:04:36 +0000945 spin_lock_irqsave(&priv->driver_lock, flags);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200946
David Woodhouseac472462007-12-08 00:35:00 +0000947 if (addtail)
David Woodhouseaa21c002007-12-08 20:04:36 +0000948 list_add_tail(&cmdnode->list, &priv->cmdpendingq);
David Woodhouseac472462007-12-08 00:35:00 +0000949 else
David Woodhouseaa21c002007-12-08 20:04:36 +0000950 list_add(&cmdnode->list, &priv->cmdpendingq);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200951
David Woodhouseaa21c002007-12-08 20:04:36 +0000952 spin_unlock_irqrestore(&priv->driver_lock, flags);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200953
Holger Schurig8ff12da2007-08-02 11:54:31 -0400954 lbs_deb_host("QUEUE_CMD: inserted command 0x%04x into cmdpendingq\n",
David Woodhousec4ab4122007-12-15 00:41:51 -0500955 le16_to_cpu(cmdnode->cmdbuf->command));
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200956
957done:
Holger Schurig8ff12da2007-08-02 11:54:31 -0400958 lbs_deb_leave(LBS_DEB_HOST);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200959}
960
David Woodhouse18c52e72007-12-17 16:03:58 -0500961static void lbs_submit_command(struct lbs_private *priv,
962 struct cmd_ctrl_node *cmdnode)
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200963{
964 unsigned long flags;
Dan Williamsddac4522007-12-11 13:49:39 -0500965 struct cmd_header *cmd;
David Woodhouse18c52e72007-12-17 16:03:58 -0500966 uint16_t cmdsize;
967 uint16_t command;
Holger Schurig57962f02008-05-14 16:30:28 +0200968 int timeo = 3 * HZ;
David Woodhouse18c52e72007-12-17 16:03:58 -0500969 int ret;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200970
Holger Schurig8ff12da2007-08-02 11:54:31 -0400971 lbs_deb_enter(LBS_DEB_HOST);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200972
Dan Williamsddac4522007-12-11 13:49:39 -0500973 cmd = cmdnode->cmdbuf;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200974
David Woodhouseaa21c002007-12-08 20:04:36 +0000975 spin_lock_irqsave(&priv->driver_lock, flags);
David Woodhouseaa21c002007-12-08 20:04:36 +0000976 priv->cur_cmd = cmdnode;
David Woodhouseaa21c002007-12-08 20:04:36 +0000977 spin_unlock_irqrestore(&priv->driver_lock, flags);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200978
Dan Williamsddac4522007-12-11 13:49:39 -0500979 cmdsize = le16_to_cpu(cmd->size);
980 command = le16_to_cpu(cmd->command);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200981
David Woodhouse18c52e72007-12-17 16:03:58 -0500982 /* These commands take longer */
Dan Williamsbe0d76e2009-05-22 20:05:25 -0400983 if (command == CMD_802_11_SCAN || command == CMD_802_11_ASSOCIATE)
Holger Schurig57962f02008-05-14 16:30:28 +0200984 timeo = 5 * HZ;
David Woodhouse18c52e72007-12-17 16:03:58 -0500985
Holger Schurige5225b32008-03-26 10:04:44 +0100986 lbs_deb_cmd("DNLD_CMD: command 0x%04x, seq %d, size %d\n",
987 command, le16_to_cpu(cmd->seqnum), cmdsize);
Holger Schurig1afc09ab2008-01-29 09:14:40 +0100988 lbs_deb_hex(LBS_DEB_CMD, "DNLD_CMD", (void *) cmdnode->cmdbuf, cmdsize);
Holger Schurig8ff12da2007-08-02 11:54:31 -0400989
Dan Williamsddac4522007-12-11 13:49:39 -0500990 ret = priv->hw_host_to_card(priv, MVMS_CMD, (u8 *) cmd, cmdsize);
David Woodhouse18c52e72007-12-17 16:03:58 -0500991
David Woodhoused9896ee2007-12-15 00:09:25 -0500992 if (ret) {
993 lbs_pr_info("DNLD_CMD: hw_host_to_card failed: %d\n", ret);
David Woodhouse18c52e72007-12-17 16:03:58 -0500994 /* Let the timer kick in and retry, and potentially reset
995 the whole thing if the condition persists */
Holger Schurig57962f02008-05-14 16:30:28 +0200996 timeo = HZ/4;
Holger Schurig1afc09ab2008-01-29 09:14:40 +0100997 }
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200998
Amitkumar Karwar49125452009-09-30 20:04:38 -0700999 if (command == CMD_802_11_DEEP_SLEEP) {
1000 if (priv->is_auto_deep_sleep_enabled) {
1001 priv->wakeup_dev_required = 1;
1002 priv->dnld_sent = 0;
1003 }
1004 priv->is_deep_sleep = 1;
1005 lbs_complete_command(priv, cmdnode, 0);
1006 } else {
1007 /* Setup the timer after transmit command */
1008 mod_timer(&priv->command_timer, jiffies + timeo);
1009 }
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001010
David Woodhouse18c52e72007-12-17 16:03:58 -05001011 lbs_deb_leave(LBS_DEB_HOST);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001012}
1013
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001014/**
1015 * This function inserts command node to cmdfreeq
David Woodhouseaa21c002007-12-08 20:04:36 +00001016 * after cleans it. Requires priv->driver_lock held.
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001017 */
David Woodhouse183aeac2007-12-15 01:52:54 -05001018static void __lbs_cleanup_and_insert_cmd(struct lbs_private *priv,
David Woodhouse5ba2f8a2007-12-15 02:02:56 -05001019 struct cmd_ctrl_node *cmdnode)
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001020{
David Woodhouse5ba2f8a2007-12-15 02:02:56 -05001021 lbs_deb_enter(LBS_DEB_HOST);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001022
David Woodhouse5ba2f8a2007-12-15 02:02:56 -05001023 if (!cmdnode)
1024 goto out;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001025
David Woodhouse5ba2f8a2007-12-15 02:02:56 -05001026 cmdnode->callback = NULL;
1027 cmdnode->callback_arg = 0;
1028
1029 memset(cmdnode->cmdbuf, 0, LBS_CMD_BUFFER_SIZE);
1030
1031 list_add_tail(&cmdnode->list, &priv->cmdfreeq);
1032 out:
1033 lbs_deb_leave(LBS_DEB_HOST);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001034}
1035
Holger Schurig69f90322007-11-23 15:43:44 +01001036static void lbs_cleanup_and_insert_cmd(struct lbs_private *priv,
1037 struct cmd_ctrl_node *ptempcmd)
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001038{
1039 unsigned long flags;
1040
David Woodhouseaa21c002007-12-08 20:04:36 +00001041 spin_lock_irqsave(&priv->driver_lock, flags);
Holger Schurig10078322007-11-15 18:05:47 -05001042 __lbs_cleanup_and_insert_cmd(priv, ptempcmd);
David Woodhouseaa21c002007-12-08 20:04:36 +00001043 spin_unlock_irqrestore(&priv->driver_lock, flags);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001044}
1045
David Woodhouse183aeac2007-12-15 01:52:54 -05001046void lbs_complete_command(struct lbs_private *priv, struct cmd_ctrl_node *cmd,
1047 int result)
1048{
David Woodhouseae125bf2007-12-15 04:22:52 -05001049 cmd->result = result;
David Woodhouse5ba2f8a2007-12-15 02:02:56 -05001050 cmd->cmdwaitqwoken = 1;
1051 wake_up_interruptible(&cmd->cmdwait_q);
1052
Holger Schurig8db4a2b2008-03-19 10:11:00 +01001053 if (!cmd->callback || cmd->callback == lbs_cmd_async_callback)
David Woodhousead12d0f2007-12-15 02:06:16 -05001054 __lbs_cleanup_and_insert_cmd(priv, cmd);
David Woodhouse183aeac2007-12-15 01:52:54 -05001055 priv->cur_cmd = NULL;
1056}
1057
Dan Williamsd5db2df2008-08-21 17:51:07 -04001058int lbs_set_radio(struct lbs_private *priv, u8 preamble, u8 radio_on)
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001059{
David Woodhousea7c45892007-12-17 22:43:48 -05001060 struct cmd_ds_802_11_radio_control cmd;
Dan Williamsd5db2df2008-08-21 17:51:07 -04001061 int ret = -EINVAL;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001062
Holger Schurig9012b282007-05-25 11:27:16 -04001063 lbs_deb_enter(LBS_DEB_CMD);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001064
David Woodhousea7c45892007-12-17 22:43:48 -05001065 cmd.hdr.size = cpu_to_le16(sizeof(cmd));
1066 cmd.action = cpu_to_le16(CMD_ACT_SET);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001067
Dan Williamsd5db2df2008-08-21 17:51:07 -04001068 /* Only v8 and below support setting the preamble */
1069 if (priv->fwrelease < 0x09000000) {
1070 switch (preamble) {
1071 case RADIO_PREAMBLE_SHORT:
Dan Williamsd5db2df2008-08-21 17:51:07 -04001072 case RADIO_PREAMBLE_AUTO:
1073 case RADIO_PREAMBLE_LONG:
1074 cmd.control = cpu_to_le16(preamble);
1075 break;
1076 default:
1077 goto out;
1078 }
David Woodhousea7c45892007-12-17 22:43:48 -05001079 }
1080
Dan Williamsd5db2df2008-08-21 17:51:07 -04001081 if (radio_on)
1082 cmd.control |= cpu_to_le16(0x1);
1083 else {
1084 cmd.control &= cpu_to_le16(~0x1);
1085 priv->txpower_cur = 0;
1086 }
David Woodhousea7c45892007-12-17 22:43:48 -05001087
Dan Williamsd5db2df2008-08-21 17:51:07 -04001088 lbs_deb_cmd("RADIO_CONTROL: radio %s, preamble %d\n",
1089 radio_on ? "ON" : "OFF", preamble);
1090
1091 priv->radio_on = radio_on;
David Woodhousea7c45892007-12-17 22:43:48 -05001092
1093 ret = lbs_cmd_with_response(priv, CMD_802_11_RADIO_CONTROL, &cmd);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001094
Dan Williamsd5db2df2008-08-21 17:51:07 -04001095out:
Holger Schurig9012b282007-05-25 11:27:16 -04001096 lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001097 return ret;
1098}
1099
Holger Schurigc97329e2008-03-18 11:20:21 +01001100void lbs_set_mac_control(struct lbs_private *priv)
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001101{
Holger Schurig835d3ac2008-03-12 16:05:40 +01001102 struct cmd_ds_mac_control cmd;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001103
Holger Schurig9012b282007-05-25 11:27:16 -04001104 lbs_deb_enter(LBS_DEB_CMD);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001105
Holger Schurig835d3ac2008-03-12 16:05:40 +01001106 cmd.hdr.size = cpu_to_le16(sizeof(cmd));
Holger Schurigd9e97782008-03-12 16:06:43 +01001107 cmd.action = cpu_to_le16(priv->mac_control);
Holger Schurig835d3ac2008-03-12 16:05:40 +01001108 cmd.reserved = 0;
1109
David Woodhouse75bf45a2008-05-20 13:32:45 +01001110 lbs_cmd_async(priv, CMD_MAC_CONTROL, &cmd.hdr, sizeof(cmd));
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001111
Holger Schurigc97329e2008-03-18 11:20:21 +01001112 lbs_deb_leave(LBS_DEB_CMD);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001113}
1114
1115/**
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001116 * @brief This function allocates the command buffer and link
1117 * it to command free queue.
1118 *
Holger Schurig69f90322007-11-23 15:43:44 +01001119 * @param priv A pointer to struct lbs_private structure
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001120 * @return 0 or -1
1121 */
Holger Schurig69f90322007-11-23 15:43:44 +01001122int lbs_allocate_cmd_buffer(struct lbs_private *priv)
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001123{
1124 int ret = 0;
Dan Williamsddac4522007-12-11 13:49:39 -05001125 u32 bufsize;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001126 u32 i;
Dan Williamsddac4522007-12-11 13:49:39 -05001127 struct cmd_ctrl_node *cmdarray;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001128
Holger Schurig8ff12da2007-08-02 11:54:31 -04001129 lbs_deb_enter(LBS_DEB_HOST);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001130
Dan Williamsddac4522007-12-11 13:49:39 -05001131 /* Allocate and initialize the command array */
1132 bufsize = sizeof(struct cmd_ctrl_node) * LBS_NUM_CMD_BUFFERS;
1133 if (!(cmdarray = kzalloc(bufsize, GFP_KERNEL))) {
Holger Schurig8ff12da2007-08-02 11:54:31 -04001134 lbs_deb_host("ALLOC_CMD_BUF: tempcmd_array is NULL\n");
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001135 ret = -1;
1136 goto done;
1137 }
Dan Williamsddac4522007-12-11 13:49:39 -05001138 priv->cmd_array = cmdarray;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001139
Dan Williamsddac4522007-12-11 13:49:39 -05001140 /* Allocate and initialize each command buffer in the command array */
1141 for (i = 0; i < LBS_NUM_CMD_BUFFERS; i++) {
1142 cmdarray[i].cmdbuf = kzalloc(LBS_CMD_BUFFER_SIZE, GFP_KERNEL);
1143 if (!cmdarray[i].cmdbuf) {
Holger Schurig8ff12da2007-08-02 11:54:31 -04001144 lbs_deb_host("ALLOC_CMD_BUF: ptempvirtualaddr is NULL\n");
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001145 ret = -1;
1146 goto done;
1147 }
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001148 }
1149
Dan Williamsddac4522007-12-11 13:49:39 -05001150 for (i = 0; i < LBS_NUM_CMD_BUFFERS; i++) {
1151 init_waitqueue_head(&cmdarray[i].cmdwait_q);
1152 lbs_cleanup_and_insert_cmd(priv, &cmdarray[i]);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001153 }
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001154 ret = 0;
Holger Schurig9012b282007-05-25 11:27:16 -04001155
1156done:
Holger Schurig8ff12da2007-08-02 11:54:31 -04001157 lbs_deb_leave_args(LBS_DEB_HOST, "ret %d", ret);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001158 return ret;
1159}
1160
1161/**
1162 * @brief This function frees the command buffer.
1163 *
Holger Schurig69f90322007-11-23 15:43:44 +01001164 * @param priv A pointer to struct lbs_private structure
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001165 * @return 0 or -1
1166 */
Holger Schurig69f90322007-11-23 15:43:44 +01001167int lbs_free_cmd_buffer(struct lbs_private *priv)
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001168{
Dan Williamsddac4522007-12-11 13:49:39 -05001169 struct cmd_ctrl_node *cmdarray;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001170 unsigned int i;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001171
Holger Schurig8ff12da2007-08-02 11:54:31 -04001172 lbs_deb_enter(LBS_DEB_HOST);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001173
1174 /* need to check if cmd array is allocated or not */
David Woodhouseaa21c002007-12-08 20:04:36 +00001175 if (priv->cmd_array == NULL) {
Holger Schurig8ff12da2007-08-02 11:54:31 -04001176 lbs_deb_host("FREE_CMD_BUF: cmd_array is NULL\n");
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001177 goto done;
1178 }
1179
Dan Williamsddac4522007-12-11 13:49:39 -05001180 cmdarray = priv->cmd_array;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001181
1182 /* Release shared memory buffers */
Dan Williamsddac4522007-12-11 13:49:39 -05001183 for (i = 0; i < LBS_NUM_CMD_BUFFERS; i++) {
1184 if (cmdarray[i].cmdbuf) {
1185 kfree(cmdarray[i].cmdbuf);
1186 cmdarray[i].cmdbuf = NULL;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001187 }
1188 }
1189
1190 /* Release cmd_ctrl_node */
David Woodhouseaa21c002007-12-08 20:04:36 +00001191 if (priv->cmd_array) {
1192 kfree(priv->cmd_array);
1193 priv->cmd_array = NULL;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001194 }
1195
1196done:
Holger Schurig8ff12da2007-08-02 11:54:31 -04001197 lbs_deb_leave(LBS_DEB_HOST);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001198 return 0;
1199}
1200
1201/**
1202 * @brief This function gets a free command node if available in
1203 * command free queue.
1204 *
Holger Schurig69f90322007-11-23 15:43:44 +01001205 * @param priv A pointer to struct lbs_private structure
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001206 * @return cmd_ctrl_node A pointer to cmd_ctrl_node structure or NULL
1207 */
Dan Williamsd06956b2010-07-27 13:16:24 -07001208static struct cmd_ctrl_node *lbs_get_free_cmd_node(struct lbs_private *priv)
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001209{
1210 struct cmd_ctrl_node *tempnode;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001211 unsigned long flags;
1212
Holger Schurig8ff12da2007-08-02 11:54:31 -04001213 lbs_deb_enter(LBS_DEB_HOST);
1214
David Woodhouseaa21c002007-12-08 20:04:36 +00001215 if (!priv)
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001216 return NULL;
1217
David Woodhouseaa21c002007-12-08 20:04:36 +00001218 spin_lock_irqsave(&priv->driver_lock, flags);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001219
David Woodhouseaa21c002007-12-08 20:04:36 +00001220 if (!list_empty(&priv->cmdfreeq)) {
1221 tempnode = list_first_entry(&priv->cmdfreeq,
Li Zefanabe3ed12007-12-06 13:01:21 +01001222 struct cmd_ctrl_node, list);
1223 list_del(&tempnode->list);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001224 } else {
Holger Schurig8ff12da2007-08-02 11:54:31 -04001225 lbs_deb_host("GET_CMD_NODE: cmd_ctrl_node is not available\n");
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001226 tempnode = NULL;
1227 }
1228
David Woodhouseaa21c002007-12-08 20:04:36 +00001229 spin_unlock_irqrestore(&priv->driver_lock, flags);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001230
Holger Schurig8ff12da2007-08-02 11:54:31 -04001231 lbs_deb_leave(LBS_DEB_HOST);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001232 return tempnode;
1233}
1234
1235/**
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001236 * @brief This function executes next command in command
Nick Andrew877d0312009-01-26 11:06:57 +01001237 * pending queue. It will put firmware back to PS mode
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001238 * if applicable.
1239 *
Holger Schurig69f90322007-11-23 15:43:44 +01001240 * @param priv A pointer to struct lbs_private structure
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001241 * @return 0 or -1
1242 */
Holger Schurig69f90322007-11-23 15:43:44 +01001243int lbs_execute_next_command(struct lbs_private *priv)
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001244{
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001245 struct cmd_ctrl_node *cmdnode = NULL;
Dan Williamsddac4522007-12-11 13:49:39 -05001246 struct cmd_header *cmd;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001247 unsigned long flags;
1248 int ret = 0;
1249
Holger Schurig1afc09ab2008-01-29 09:14:40 +01001250 /* Debug group is LBS_DEB_THREAD and not LBS_DEB_HOST, because the
1251 * only caller to us is lbs_thread() and we get even when a
1252 * data packet is received */
Holger Schurig8ff12da2007-08-02 11:54:31 -04001253 lbs_deb_enter(LBS_DEB_THREAD);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001254
David Woodhouseaa21c002007-12-08 20:04:36 +00001255 spin_lock_irqsave(&priv->driver_lock, flags);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001256
David Woodhouseaa21c002007-12-08 20:04:36 +00001257 if (priv->cur_cmd) {
Holger Schurig8ff12da2007-08-02 11:54:31 -04001258 lbs_pr_alert( "EXEC_NEXT_CMD: already processing command!\n");
David Woodhouseaa21c002007-12-08 20:04:36 +00001259 spin_unlock_irqrestore(&priv->driver_lock, flags);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001260 ret = -1;
1261 goto done;
1262 }
1263
David Woodhouseaa21c002007-12-08 20:04:36 +00001264 if (!list_empty(&priv->cmdpendingq)) {
1265 cmdnode = list_first_entry(&priv->cmdpendingq,
Li Zefanabe3ed12007-12-06 13:01:21 +01001266 struct cmd_ctrl_node, list);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001267 }
1268
David Woodhouseaa21c002007-12-08 20:04:36 +00001269 spin_unlock_irqrestore(&priv->driver_lock, flags);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001270
1271 if (cmdnode) {
Dan Williamsddac4522007-12-11 13:49:39 -05001272 cmd = cmdnode->cmdbuf;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001273
Dan Williamsddac4522007-12-11 13:49:39 -05001274 if (is_command_allowed_in_ps(le16_to_cpu(cmd->command))) {
David Woodhouseaa21c002007-12-08 20:04:36 +00001275 if ((priv->psstate == PS_STATE_SLEEP) ||
1276 (priv->psstate == PS_STATE_PRE_SLEEP)) {
Holger Schurig8ff12da2007-08-02 11:54:31 -04001277 lbs_deb_host(
1278 "EXEC_NEXT_CMD: cannot send cmd 0x%04x in psstate %d\n",
Dan Williamsddac4522007-12-11 13:49:39 -05001279 le16_to_cpu(cmd->command),
David Woodhouseaa21c002007-12-08 20:04:36 +00001280 priv->psstate);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001281 ret = -1;
1282 goto done;
1283 }
Holger Schurig8ff12da2007-08-02 11:54:31 -04001284 lbs_deb_host("EXEC_NEXT_CMD: OK to send command "
Dan Williamsddac4522007-12-11 13:49:39 -05001285 "0x%04x in psstate %d\n",
1286 le16_to_cpu(cmd->command), priv->psstate);
David Woodhouseaa21c002007-12-08 20:04:36 +00001287 } else if (priv->psstate != PS_STATE_FULL_POWER) {
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001288 /*
1289 * 1. Non-PS command:
1290 * Queue it. set needtowakeup to TRUE if current state
Dan Williams0bb64082010-07-27 13:08:08 -07001291 * is SLEEP, otherwise call send EXIT_PS.
1292 * 2. PS command but not EXIT_PS:
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001293 * Ignore it.
Dan Williams0bb64082010-07-27 13:08:08 -07001294 * 3. PS command EXIT_PS:
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001295 * Set needtowakeup to TRUE if current state is SLEEP,
1296 * otherwise send this command down to firmware
1297 * immediately.
1298 */
Dan Williamsddac4522007-12-11 13:49:39 -05001299 if (cmd->command != cpu_to_le16(CMD_802_11_PS_MODE)) {
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001300 /* Prepare to send Exit PS,
1301 * this non PS command will be sent later */
David Woodhouseaa21c002007-12-08 20:04:36 +00001302 if ((priv->psstate == PS_STATE_SLEEP)
1303 || (priv->psstate == PS_STATE_PRE_SLEEP)
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001304 ) {
1305 /* w/ new scheme, it will not reach here.
1306 since it is blocked in main_thread. */
David Woodhouseaa21c002007-12-08 20:04:36 +00001307 priv->needtowakeup = 1;
Dan Williams0bb64082010-07-27 13:08:08 -07001308 } else {
1309 lbs_set_ps_mode(priv,
1310 PS_MODE_ACTION_EXIT_PS,
1311 false);
1312 }
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001313
1314 ret = 0;
1315 goto done;
1316 } else {
1317 /*
1318 * PS command. Ignore it if it is not Exit_PS.
1319 * otherwise send it down immediately.
1320 */
David Woodhouse38bfab12007-12-16 23:26:54 -05001321 struct cmd_ds_802_11_ps_mode *psm = (void *)&cmd[1];
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001322
Holger Schurig8ff12da2007-08-02 11:54:31 -04001323 lbs_deb_host(
1324 "EXEC_NEXT_CMD: PS cmd, action 0x%02x\n",
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001325 psm->action);
1326 if (psm->action !=
Dan Williams0bb64082010-07-27 13:08:08 -07001327 cpu_to_le16(PS_MODE_ACTION_EXIT_PS)) {
Holger Schurig8ff12da2007-08-02 11:54:31 -04001328 lbs_deb_host(
1329 "EXEC_NEXT_CMD: ignore ENTER_PS cmd\n");
Li Zefanabe3ed12007-12-06 13:01:21 +01001330 list_del(&cmdnode->list);
David Woodhouse183aeac2007-12-15 01:52:54 -05001331 spin_lock_irqsave(&priv->driver_lock, flags);
1332 lbs_complete_command(priv, cmdnode, 0);
1333 spin_unlock_irqrestore(&priv->driver_lock, flags);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001334
1335 ret = 0;
1336 goto done;
1337 }
1338
David Woodhouseaa21c002007-12-08 20:04:36 +00001339 if ((priv->psstate == PS_STATE_SLEEP) ||
1340 (priv->psstate == PS_STATE_PRE_SLEEP)) {
Holger Schurig8ff12da2007-08-02 11:54:31 -04001341 lbs_deb_host(
1342 "EXEC_NEXT_CMD: ignore EXIT_PS cmd in sleep\n");
Li Zefanabe3ed12007-12-06 13:01:21 +01001343 list_del(&cmdnode->list);
David Woodhouse183aeac2007-12-15 01:52:54 -05001344 spin_lock_irqsave(&priv->driver_lock, flags);
1345 lbs_complete_command(priv, cmdnode, 0);
1346 spin_unlock_irqrestore(&priv->driver_lock, flags);
David Woodhouseaa21c002007-12-08 20:04:36 +00001347 priv->needtowakeup = 1;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001348
1349 ret = 0;
1350 goto done;
1351 }
1352
Holger Schurig8ff12da2007-08-02 11:54:31 -04001353 lbs_deb_host(
1354 "EXEC_NEXT_CMD: sending EXIT_PS\n");
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001355 }
1356 }
Li Zefanabe3ed12007-12-06 13:01:21 +01001357 list_del(&cmdnode->list);
Holger Schurig8ff12da2007-08-02 11:54:31 -04001358 lbs_deb_host("EXEC_NEXT_CMD: sending command 0x%04x\n",
Dan Williamsddac4522007-12-11 13:49:39 -05001359 le16_to_cpu(cmd->command));
David Woodhoused9896ee2007-12-15 00:09:25 -05001360 lbs_submit_command(priv, cmdnode);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001361 } else {
1362 /*
1363 * check if in power save mode, if yes, put the device back
1364 * to PS mode
1365 */
Kiran Divekare86dc1c2010-06-14 22:01:26 +05301366#ifdef TODO
1367 /*
1368 * This was the old code for libertas+wext. Someone that
1369 * understands this beast should re-code it in a sane way.
1370 *
1371 * I actually don't understand why this is related to WPA
1372 * and to connection status, shouldn't powering should be
1373 * independ of such things?
1374 */
David Woodhouseaa21c002007-12-08 20:04:36 +00001375 if ((priv->psmode != LBS802_11POWERMODECAM) &&
1376 (priv->psstate == PS_STATE_FULL_POWER) &&
1377 ((priv->connect_status == LBS_CONNECTED) ||
Holger Schurig602114a2009-12-02 15:26:01 +01001378 lbs_mesh_connected(priv))) {
David Woodhouseaa21c002007-12-08 20:04:36 +00001379 if (priv->secinfo.WPAenabled ||
1380 priv->secinfo.WPA2enabled) {
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001381 /* check for valid WPA group keys */
David Woodhouseaa21c002007-12-08 20:04:36 +00001382 if (priv->wpa_mcast_key.len ||
1383 priv->wpa_unicast_key.len) {
Holger Schurig8ff12da2007-08-02 11:54:31 -04001384 lbs_deb_host(
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001385 "EXEC_NEXT_CMD: WPA enabled and GTK_SET"
1386 " go back to PS_SLEEP");
Dan Williams0bb64082010-07-27 13:08:08 -07001387 lbs_set_ps_mode(priv,
1388 PS_MODE_ACTION_ENTER_PS,
1389 false);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001390 }
1391 } else {
Holger Schurig8ff12da2007-08-02 11:54:31 -04001392 lbs_deb_host(
1393 "EXEC_NEXT_CMD: cmdpendingq empty, "
1394 "go back to PS_SLEEP");
Dan Williams0bb64082010-07-27 13:08:08 -07001395 lbs_set_ps_mode(priv, PS_MODE_ACTION_ENTER_PS,
1396 false);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001397 }
1398 }
Kiran Divekare86dc1c2010-06-14 22:01:26 +05301399#endif
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001400 }
1401
1402 ret = 0;
1403done:
Holger Schurig8ff12da2007-08-02 11:54:31 -04001404 lbs_deb_leave(LBS_DEB_THREAD);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001405 return ret;
1406}
1407
Holger Schurigf539f2e2008-03-26 13:22:11 +01001408static void lbs_send_confirmsleep(struct lbs_private *priv)
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001409{
1410 unsigned long flags;
Holger Schurigf539f2e2008-03-26 13:22:11 +01001411 int ret;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001412
Holger Schurig8ff12da2007-08-02 11:54:31 -04001413 lbs_deb_enter(LBS_DEB_HOST);
Holger Schurigf539f2e2008-03-26 13:22:11 +01001414 lbs_deb_hex(LBS_DEB_HOST, "sleep confirm", (u8 *) &confirm_sleep,
1415 sizeof(confirm_sleep));
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001416
Holger Schurigf539f2e2008-03-26 13:22:11 +01001417 ret = priv->hw_host_to_card(priv, MVMS_CMD, (u8 *) &confirm_sleep,
1418 sizeof(confirm_sleep));
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001419 if (ret) {
Holger Schurigf539f2e2008-03-26 13:22:11 +01001420 lbs_pr_alert("confirm_sleep failed\n");
Holger Schurig7919b892008-04-01 14:50:43 +02001421 goto out;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001422 }
Holger Schurig7919b892008-04-01 14:50:43 +02001423
1424 spin_lock_irqsave(&priv->driver_lock, flags);
1425
Holger Schuriga01f5452008-06-04 11:10:40 +02001426 /* We don't get a response on the sleep-confirmation */
1427 priv->dnld_sent = DNLD_RES_RECEIVED;
1428
Amitkumar Karwar66fceb62010-05-19 03:24:38 -07001429 if (priv->is_host_sleep_configured) {
1430 priv->is_host_sleep_activated = 1;
1431 wake_up_interruptible(&priv->host_sleep_q);
1432 }
1433
Holger Schurig7919b892008-04-01 14:50:43 +02001434 /* If nothing to do, go back to sleep (?) */
Stefani Seibolde64c0262009-12-21 14:37:28 -08001435 if (!kfifo_len(&priv->event_fifo) && !priv->resp_len[priv->resp_idx])
Holger Schurig7919b892008-04-01 14:50:43 +02001436 priv->psstate = PS_STATE_SLEEP;
1437
1438 spin_unlock_irqrestore(&priv->driver_lock, flags);
1439
1440out:
Holger Schurigf539f2e2008-03-26 13:22:11 +01001441 lbs_deb_leave(LBS_DEB_HOST);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001442}
1443
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001444/**
1445 * @brief This function checks condition and prepares to
1446 * send sleep confirm command to firmware if ok.
1447 *
Holger Schurig69f90322007-11-23 15:43:44 +01001448 * @param priv A pointer to struct lbs_private structure
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001449 * @param psmode Power Saving mode
1450 * @return n/a
1451 */
Holger Schurigd4ff0ef2008-03-19 14:25:18 +01001452void lbs_ps_confirm_sleep(struct lbs_private *priv)
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001453{
1454 unsigned long flags =0;
Holger Schurigd4ff0ef2008-03-19 14:25:18 +01001455 int allowed = 1;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001456
Holger Schurig8ff12da2007-08-02 11:54:31 -04001457 lbs_deb_enter(LBS_DEB_HOST);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001458
Holger Schuriga01f5452008-06-04 11:10:40 +02001459 spin_lock_irqsave(&priv->driver_lock, flags);
Holger Schurig634b8f42007-05-25 13:05:16 -04001460 if (priv->dnld_sent) {
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001461 allowed = 0;
David Woodhouse23d36ee2007-12-12 00:14:21 -05001462 lbs_deb_host("dnld_sent was set\n");
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001463 }
1464
Holger Schurig7919b892008-04-01 14:50:43 +02001465 /* In-progress command? */
David Woodhouseaa21c002007-12-08 20:04:36 +00001466 if (priv->cur_cmd) {
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001467 allowed = 0;
David Woodhouse23d36ee2007-12-12 00:14:21 -05001468 lbs_deb_host("cur_cmd was set\n");
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001469 }
Holger Schurig7919b892008-04-01 14:50:43 +02001470
1471 /* Pending events or command responses? */
Stefani Seibolde64c0262009-12-21 14:37:28 -08001472 if (kfifo_len(&priv->event_fifo) || priv->resp_len[priv->resp_idx]) {
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001473 allowed = 0;
Holger Schurig7919b892008-04-01 14:50:43 +02001474 lbs_deb_host("pending events or command responses\n");
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001475 }
David Woodhouseaa21c002007-12-08 20:04:36 +00001476 spin_unlock_irqrestore(&priv->driver_lock, flags);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001477
1478 if (allowed) {
Holger Schurig10078322007-11-15 18:05:47 -05001479 lbs_deb_host("sending lbs_ps_confirm_sleep\n");
Holger Schurigf539f2e2008-03-26 13:22:11 +01001480 lbs_send_confirmsleep(priv);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001481 } else {
Holger Schurig8ff12da2007-08-02 11:54:31 -04001482 lbs_deb_host("sleep confirm has been delayed\n");
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001483 }
1484
Holger Schurig8ff12da2007-08-02 11:54:31 -04001485 lbs_deb_leave(LBS_DEB_HOST);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001486}
Holger Schurig675787e2007-12-05 17:58:11 +01001487
1488
Anna Neal0112c9e2008-09-11 11:17:25 -07001489/**
1490 * @brief Configures the transmission power control functionality.
1491 *
1492 * @param priv A pointer to struct lbs_private structure
1493 * @param enable Transmission power control enable
1494 * @param p0 Power level when link quality is good (dBm).
1495 * @param p1 Power level when link quality is fair (dBm).
1496 * @param p2 Power level when link quality is poor (dBm).
1497 * @param usesnr Use Signal to Noise Ratio in TPC
1498 *
1499 * @return 0 on success
1500 */
1501int lbs_set_tpc_cfg(struct lbs_private *priv, int enable, int8_t p0, int8_t p1,
1502 int8_t p2, int usesnr)
1503{
1504 struct cmd_ds_802_11_tpc_cfg cmd;
1505 int ret;
1506
1507 memset(&cmd, 0, sizeof(cmd));
1508 cmd.hdr.size = cpu_to_le16(sizeof(cmd));
1509 cmd.action = cpu_to_le16(CMD_ACT_SET);
1510 cmd.enable = !!enable;
Anna Neal3ed6e082008-09-26 11:34:35 -04001511 cmd.usesnr = !!usesnr;
Anna Neal0112c9e2008-09-11 11:17:25 -07001512 cmd.P0 = p0;
1513 cmd.P1 = p1;
1514 cmd.P2 = p2;
1515
1516 ret = lbs_cmd_with_response(priv, CMD_802_11_TPC_CFG, &cmd);
1517
1518 return ret;
1519}
1520
1521/**
1522 * @brief Configures the power adaptation settings.
1523 *
1524 * @param priv A pointer to struct lbs_private structure
1525 * @param enable Power adaptation enable
1526 * @param p0 Power level for 1, 2, 5.5 and 11 Mbps (dBm).
1527 * @param p1 Power level for 6, 9, 12, 18, 22, 24 and 36 Mbps (dBm).
1528 * @param p2 Power level for 48 and 54 Mbps (dBm).
1529 *
1530 * @return 0 on Success
1531 */
1532
1533int lbs_set_power_adapt_cfg(struct lbs_private *priv, int enable, int8_t p0,
1534 int8_t p1, int8_t p2)
1535{
1536 struct cmd_ds_802_11_pa_cfg cmd;
1537 int ret;
1538
1539 memset(&cmd, 0, sizeof(cmd));
1540 cmd.hdr.size = cpu_to_le16(sizeof(cmd));
1541 cmd.action = cpu_to_le16(CMD_ACT_SET);
1542 cmd.enable = !!enable;
1543 cmd.P0 = p0;
1544 cmd.P1 = p1;
1545 cmd.P2 = p2;
1546
1547 ret = lbs_cmd_with_response(priv, CMD_802_11_PA_CFG , &cmd);
1548
1549 return ret;
1550}
1551
1552
Holger Schurig6d898b12009-10-14 16:49:53 +02001553struct cmd_ctrl_node *__lbs_cmd_async(struct lbs_private *priv,
Holger Schurig8db4a2b2008-03-19 10:11:00 +01001554 uint16_t command, struct cmd_header *in_cmd, int in_cmd_size,
1555 int (*callback)(struct lbs_private *, unsigned long, struct cmd_header *),
1556 unsigned long callback_arg)
Holger Schurig675787e2007-12-05 17:58:11 +01001557{
Holger Schurig675787e2007-12-05 17:58:11 +01001558 struct cmd_ctrl_node *cmdnode;
Holger Schurig675787e2007-12-05 17:58:11 +01001559
1560 lbs_deb_enter(LBS_DEB_HOST);
Holger Schurig675787e2007-12-05 17:58:11 +01001561
David Woodhouseaa21c002007-12-08 20:04:36 +00001562 if (priv->surpriseremoved) {
Holger Schurig675787e2007-12-05 17:58:11 +01001563 lbs_deb_host("PREP_CMD: card removed\n");
David Woodhouse3399ea52007-12-15 03:09:33 -05001564 cmdnode = ERR_PTR(-ENOENT);
Holger Schurig675787e2007-12-05 17:58:11 +01001565 goto done;
1566 }
1567
Dan Williams77ccdcf2010-07-27 13:15:45 -07001568 /* No commands are allowed in Deep Sleep until we toggle the GPIO
1569 * to wake up the card and it has signaled that it's ready.
1570 */
1571 if (!priv->is_auto_deep_sleep_enabled) {
1572 if (priv->is_deep_sleep) {
1573 lbs_deb_cmd("command not allowed in deep sleep\n");
1574 cmdnode = ERR_PTR(-EBUSY);
1575 goto done;
1576 }
Amitkumar Karwar63f275d2009-10-06 19:20:28 -07001577 }
1578
Dan Williamsd06956b2010-07-27 13:16:24 -07001579 cmdnode = lbs_get_free_cmd_node(priv);
Holger Schurig675787e2007-12-05 17:58:11 +01001580 if (cmdnode == NULL) {
1581 lbs_deb_host("PREP_CMD: cmdnode is NULL\n");
1582
1583 /* Wake up main thread to execute next command */
1584 wake_up_interruptible(&priv->waitq);
David Woodhouse3399ea52007-12-15 03:09:33 -05001585 cmdnode = ERR_PTR(-ENOBUFS);
Holger Schurig675787e2007-12-05 17:58:11 +01001586 goto done;
1587 }
1588
David Woodhouse448a51a2007-12-08 00:59:54 +00001589 cmdnode->callback = callback;
David Woodhouse1309b552007-12-10 13:36:10 -05001590 cmdnode->callback_arg = callback_arg;
Holger Schurig675787e2007-12-05 17:58:11 +01001591
Dan Williams7ad994d2007-12-11 12:33:30 -05001592 /* Copy the incoming command to the buffer */
Dan Williamsddac4522007-12-11 13:49:39 -05001593 memcpy(cmdnode->cmdbuf, in_cmd, in_cmd_size);
Dan Williams7ad994d2007-12-11 12:33:30 -05001594
Holger Schurig675787e2007-12-05 17:58:11 +01001595 /* Set sequence number, clean result, move to buffer */
David Woodhouseaa21c002007-12-08 20:04:36 +00001596 priv->seqnum++;
Dan Williamsddac4522007-12-11 13:49:39 -05001597 cmdnode->cmdbuf->command = cpu_to_le16(command);
1598 cmdnode->cmdbuf->size = cpu_to_le16(in_cmd_size);
1599 cmdnode->cmdbuf->seqnum = cpu_to_le16(priv->seqnum);
1600 cmdnode->cmdbuf->result = 0;
Holger Schurig675787e2007-12-05 17:58:11 +01001601
1602 lbs_deb_host("PREP_CMD: command 0x%04x\n", command);
1603
Holger Schurig675787e2007-12-05 17:58:11 +01001604 cmdnode->cmdwaitqwoken = 0;
David Woodhouse681ffbb2007-12-15 20:04:54 -05001605 lbs_queue_cmd(priv, cmdnode);
Holger Schurig675787e2007-12-05 17:58:11 +01001606 wake_up_interruptible(&priv->waitq);
1607
David Woodhouse3399ea52007-12-15 03:09:33 -05001608 done:
1609 lbs_deb_leave_args(LBS_DEB_HOST, "ret %p", cmdnode);
1610 return cmdnode;
1611}
1612
Holger Schurig8db4a2b2008-03-19 10:11:00 +01001613void lbs_cmd_async(struct lbs_private *priv, uint16_t command,
1614 struct cmd_header *in_cmd, int in_cmd_size)
1615{
1616 lbs_deb_enter(LBS_DEB_CMD);
1617 __lbs_cmd_async(priv, command, in_cmd, in_cmd_size,
1618 lbs_cmd_async_callback, 0);
1619 lbs_deb_leave(LBS_DEB_CMD);
1620}
1621
David Woodhouse3399ea52007-12-15 03:09:33 -05001622int __lbs_cmd(struct lbs_private *priv, uint16_t command,
1623 struct cmd_header *in_cmd, int in_cmd_size,
1624 int (*callback)(struct lbs_private *, unsigned long, struct cmd_header *),
1625 unsigned long callback_arg)
1626{
1627 struct cmd_ctrl_node *cmdnode;
1628 unsigned long flags;
1629 int ret = 0;
1630
1631 lbs_deb_enter(LBS_DEB_HOST);
1632
1633 cmdnode = __lbs_cmd_async(priv, command, in_cmd, in_cmd_size,
1634 callback, callback_arg);
1635 if (IS_ERR(cmdnode)) {
1636 ret = PTR_ERR(cmdnode);
1637 goto done;
1638 }
1639
Holger Schurig675787e2007-12-05 17:58:11 +01001640 might_sleep();
1641 wait_event_interruptible(cmdnode->cmdwait_q, cmdnode->cmdwaitqwoken);
1642
David Woodhouseaa21c002007-12-08 20:04:36 +00001643 spin_lock_irqsave(&priv->driver_lock, flags);
David Woodhouseae125bf2007-12-15 04:22:52 -05001644 ret = cmdnode->result;
1645 if (ret)
1646 lbs_pr_info("PREP_CMD: command 0x%04x failed: %d\n",
1647 command, ret);
David Woodhouse3399ea52007-12-15 03:09:33 -05001648
David Woodhousead12d0f2007-12-15 02:06:16 -05001649 __lbs_cleanup_and_insert_cmd(priv, cmdnode);
David Woodhouseaa21c002007-12-08 20:04:36 +00001650 spin_unlock_irqrestore(&priv->driver_lock, flags);
Holger Schurig675787e2007-12-05 17:58:11 +01001651
1652done:
1653 lbs_deb_leave_args(LBS_DEB_HOST, "ret %d", ret);
1654 return ret;
1655}
Dan Williams14e865b2007-12-10 15:11:23 -05001656EXPORT_SYMBOL_GPL(__lbs_cmd);