blob: 0fa6b0e59ea58238e2a8cc91e257462d6d1fc587 [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>
Holger Schurige93156e2009-10-22 15:30:58 +02009
Marcelo Tosatti876c9d32007-02-10 12:25:27 -020010#include "host.h"
Marcelo Tosatti876c9d32007-02-10 12:25:27 -020011#include "decl.h"
12#include "defs.h"
13#include "dev.h"
Holger Schurig697900a2008-04-02 16:27:10 +020014#include "assoc.h"
Marcelo Tosatti876c9d32007-02-10 12:25:27 -020015#include "wext.h"
Holger Schurig2d465022009-10-22 15:30:48 +020016#include "scan.h"
Dan Williams6e66f032007-12-11 12:42:16 -050017#include "cmd.h"
Marcelo Tosatti876c9d32007-02-10 12:25:27 -020018
Holger Schurige93156e2009-10-22 15:30:58 +020019
David Woodhouse2fd6cfe2007-12-11 17:44:10 -050020static struct cmd_ctrl_node *lbs_get_cmd_ctrl_node(struct lbs_private *priv);
Holger Schurig0d61d042007-12-05 17:58:06 +010021
Marcelo Tosatti876c9d32007-02-10 12:25:27 -020022/**
Holger Schurig8db4a2b2008-03-19 10:11:00 +010023 * @brief Simple callback that copies response back into command
24 *
25 * @param priv A pointer to struct lbs_private structure
26 * @param extra A pointer to the original command structure for which
27 * 'resp' is a response
28 * @param resp A pointer to the command response
29 *
30 * @return 0 on success, error on failure
31 */
32int lbs_cmd_copyback(struct lbs_private *priv, unsigned long extra,
33 struct cmd_header *resp)
34{
35 struct cmd_header *buf = (void *)extra;
36 uint16_t copy_len;
37
38 copy_len = min(le16_to_cpu(buf->size), le16_to_cpu(resp->size));
39 memcpy(buf, resp, copy_len);
40 return 0;
41}
42EXPORT_SYMBOL_GPL(lbs_cmd_copyback);
43
44/**
45 * @brief Simple callback that ignores the result. Use this if
46 * you just want to send a command to the hardware, but don't
47 * care for the result.
48 *
49 * @param priv ignored
50 * @param extra ignored
51 * @param resp ignored
52 *
53 * @return 0 for success
54 */
55static int lbs_cmd_async_callback(struct lbs_private *priv, unsigned long extra,
56 struct cmd_header *resp)
57{
58 return 0;
59}
60
61
62/**
Dan Williams852e1f22007-12-10 15:24:47 -050063 * @brief Checks whether a command is allowed in Power Save mode
Marcelo Tosatti876c9d32007-02-10 12:25:27 -020064 *
65 * @param command the command ID
Dan Williams852e1f22007-12-10 15:24:47 -050066 * @return 1 if allowed, 0 if not allowed
Marcelo Tosatti876c9d32007-02-10 12:25:27 -020067 */
Dan Williams852e1f22007-12-10 15:24:47 -050068static u8 is_command_allowed_in_ps(u16 cmd)
Marcelo Tosatti876c9d32007-02-10 12:25:27 -020069{
Dan Williams852e1f22007-12-10 15:24:47 -050070 switch (cmd) {
71 case CMD_802_11_RSSI:
72 return 1;
Amitkumar Karwar66fceb62010-05-19 03:24:38 -070073 case CMD_802_11_HOST_SLEEP_CFG:
74 return 1;
Dan Williams852e1f22007-12-10 15:24:47 -050075 default:
76 break;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -020077 }
Marcelo Tosatti876c9d32007-02-10 12:25:27 -020078 return 0;
79}
80
Dan Williams6e66f032007-12-11 12:42:16 -050081/**
Amitkumar Karwar63f275d2009-10-06 19:20:28 -070082 * @brief This function checks if the command is allowed.
83 *
84 * @param priv A pointer to lbs_private structure
85 * @return allowed or not allowed.
86 */
87
88static int lbs_is_cmd_allowed(struct lbs_private *priv)
89{
90 int ret = 1;
91
92 lbs_deb_enter(LBS_DEB_CMD);
93
94 if (!priv->is_auto_deep_sleep_enabled) {
95 if (priv->is_deep_sleep) {
96 lbs_deb_cmd("command not allowed in deep sleep\n");
97 ret = 0;
98 }
99 }
100
101 lbs_deb_leave(LBS_DEB_CMD);
102 return ret;
103}
104
105/**
Dan Williams6e66f032007-12-11 12:42:16 -0500106 * @brief Updates the hardware details like MAC address and regulatory region
107 *
108 * @param priv A pointer to struct lbs_private structure
109 *
110 * @return 0 on success, error on failure
111 */
112int lbs_update_hw_spec(struct lbs_private *priv)
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200113{
Dan Williams6e66f032007-12-11 12:42:16 -0500114 struct cmd_ds_get_hw_spec cmd;
115 int ret = -1;
116 u32 i;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200117
Holger Schurig9012b282007-05-25 11:27:16 -0400118 lbs_deb_enter(LBS_DEB_CMD);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200119
Dan Williams6e66f032007-12-11 12:42:16 -0500120 memset(&cmd, 0, sizeof(cmd));
121 cmd.hdr.size = cpu_to_le16(sizeof(cmd));
122 memcpy(cmd.permanentaddr, priv->current_addr, ETH_ALEN);
David Woodhouse689442d2007-12-12 16:00:42 -0500123 ret = lbs_cmd_with_response(priv, CMD_GET_HW_SPEC, &cmd);
Dan Williams6e66f032007-12-11 12:42:16 -0500124 if (ret)
125 goto out;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200126
Dan Williams6e66f032007-12-11 12:42:16 -0500127 priv->fwcapinfo = le32_to_cpu(cmd.fwcapinfo);
Dan Williams6e66f032007-12-11 12:42:16 -0500128
Holger Schurigdac10a92008-01-16 15:55:22 +0100129 /* The firmware release is in an interesting format: the patch
130 * level is in the most significant nibble ... so fix that: */
131 priv->fwrelease = le32_to_cpu(cmd.fwrelease);
132 priv->fwrelease = (priv->fwrelease << 8) |
133 (priv->fwrelease >> 24 & 0xff);
134
135 /* Some firmware capabilities:
136 * CF card firmware 5.0.16p0: cap 0x00000303
137 * USB dongle firmware 5.110.17p2: cap 0x00000303
138 */
Johannes Berge1749612008-10-27 15:59:26 -0700139 lbs_pr_info("%pM, fw %u.%u.%up%u, cap 0x%08x\n",
140 cmd.permanentaddr,
Holger Schurigdac10a92008-01-16 15:55:22 +0100141 priv->fwrelease >> 24 & 0xff,
142 priv->fwrelease >> 16 & 0xff,
143 priv->fwrelease >> 8 & 0xff,
144 priv->fwrelease & 0xff,
145 priv->fwcapinfo);
Dan Williams6e66f032007-12-11 12:42:16 -0500146 lbs_deb_cmd("GET_HW_SPEC: hardware interface 0x%x, hardware spec 0x%04x\n",
147 cmd.hwifversion, cmd.version);
148
149 /* Clamp region code to 8-bit since FW spec indicates that it should
150 * only ever be 8-bit, even though the field size is 16-bit. Some firmware
151 * returns non-zero high 8 bits here.
Marek Vasut15483992009-07-16 19:19:53 +0200152 *
153 * Firmware version 4.0.102 used in CF8381 has region code shifted. We
154 * need to check for this problem and handle it properly.
Dan Williams6e66f032007-12-11 12:42:16 -0500155 */
Marek Vasut15483992009-07-16 19:19:53 +0200156 if (MRVL_FW_MAJOR_REV(priv->fwrelease) == MRVL_FW_V4)
157 priv->regioncode = (le16_to_cpu(cmd.regioncode) >> 8) & 0xFF;
158 else
159 priv->regioncode = le16_to_cpu(cmd.regioncode) & 0xFF;
Dan Williams6e66f032007-12-11 12:42:16 -0500160
161 for (i = 0; i < MRVDRV_MAX_REGION_CODE; i++) {
162 /* use the region code to search for the index */
163 if (priv->regioncode == lbs_region_code_to_index[i])
164 break;
165 }
166
167 /* if it's unidentified region code, use the default (USA) */
168 if (i >= MRVDRV_MAX_REGION_CODE) {
169 priv->regioncode = 0x10;
170 lbs_pr_info("unidentified region code; using the default (USA)\n");
171 }
172
173 if (priv->current_addr[0] == 0xff)
174 memmove(priv->current_addr, cmd.permanentaddr, ETH_ALEN);
175
176 memcpy(priv->dev->dev_addr, priv->current_addr, ETH_ALEN);
177 if (priv->mesh_dev)
178 memcpy(priv->mesh_dev->dev_addr, priv->current_addr, ETH_ALEN);
179
180 if (lbs_set_regiontable(priv, priv->regioncode, 0)) {
181 ret = -1;
182 goto out;
183 }
184
Dan Williams6e66f032007-12-11 12:42:16 -0500185out:
Holger Schurig9012b282007-05-25 11:27:16 -0400186 lbs_deb_leave(LBS_DEB_CMD);
Dan Williams6e66f032007-12-11 12:42:16 -0500187 return ret;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200188}
189
Amitkumar Karwar66fceb62010-05-19 03:24:38 -0700190static int lbs_ret_host_sleep_cfg(struct lbs_private *priv, unsigned long dummy,
191 struct cmd_header *resp)
192{
193 lbs_deb_enter(LBS_DEB_CMD);
194 if (priv->wol_criteria == EHS_REMOVE_WAKEUP) {
195 priv->is_host_sleep_configured = 0;
196 if (priv->psstate == PS_STATE_FULL_POWER) {
197 priv->is_host_sleep_activated = 0;
198 wake_up_interruptible(&priv->host_sleep_q);
199 }
200 } else {
201 priv->is_host_sleep_configured = 1;
202 }
203 lbs_deb_leave(LBS_DEB_CMD);
204 return 0;
205}
206
Anna Neal582c1b52008-10-20 16:46:56 -0700207int lbs_host_sleep_cfg(struct lbs_private *priv, uint32_t criteria,
208 struct wol_config *p_wol_config)
David Woodhouse6ce4fd22007-12-12 15:19:29 -0500209{
210 struct cmd_ds_host_sleep cmd_config;
211 int ret;
212
David Woodhouse9fae8992007-12-15 03:46:44 -0500213 cmd_config.hdr.size = cpu_to_le16(sizeof(cmd_config));
David Woodhouse6ce4fd22007-12-12 15:19:29 -0500214 cmd_config.criteria = cpu_to_le32(criteria);
David Woodhouse506e9022007-12-12 20:06:06 -0500215 cmd_config.gpio = priv->wol_gpio;
216 cmd_config.gap = priv->wol_gap;
David Woodhouse6ce4fd22007-12-12 15:19:29 -0500217
Anna Neal582c1b52008-10-20 16:46:56 -0700218 if (p_wol_config != NULL)
219 memcpy((uint8_t *)&cmd_config.wol_conf, (uint8_t *)p_wol_config,
220 sizeof(struct wol_config));
221 else
222 cmd_config.wol_conf.action = CMD_ACT_ACTION_NONE;
223
Amitkumar Karwar66fceb62010-05-19 03:24:38 -0700224 ret = __lbs_cmd(priv, CMD_802_11_HOST_SLEEP_CFG, &cmd_config.hdr,
225 le16_to_cpu(cmd_config.hdr.size),
226 lbs_ret_host_sleep_cfg, 0);
David Woodhouse506e9022007-12-12 20:06:06 -0500227 if (!ret) {
Amitkumar Karwar66fceb62010-05-19 03:24:38 -0700228 if (p_wol_config)
Anna Neal582c1b52008-10-20 16:46:56 -0700229 memcpy((uint8_t *) p_wol_config,
230 (uint8_t *)&cmd_config.wol_conf,
231 sizeof(struct wol_config));
David Woodhouse506e9022007-12-12 20:06:06 -0500232 } else {
David Woodhouse6ce4fd22007-12-12 15:19:29 -0500233 lbs_pr_info("HOST_SLEEP_CFG failed %d\n", ret);
David Woodhouse6ce4fd22007-12-12 15:19:29 -0500234 }
David Woodhouse506e9022007-12-12 20:06:06 -0500235
David Woodhouse6ce4fd22007-12-12 15:19:29 -0500236 return ret;
237}
238EXPORT_SYMBOL_GPL(lbs_host_sleep_cfg);
239
Holger Schurige98a88d2008-03-19 14:25:58 +0100240static int lbs_cmd_802_11_ps_mode(struct cmd_ds_command *cmd,
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200241 u16 cmd_action)
242{
243 struct cmd_ds_802_11_ps_mode *psm = &cmd->params.psmode;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200244
Holger Schurig9012b282007-05-25 11:27:16 -0400245 lbs_deb_enter(LBS_DEB_CMD);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200246
Dan Williams0aef64d2007-08-02 11:31:18 -0400247 cmd->command = cpu_to_le16(CMD_802_11_PS_MODE);
David Woodhouse981f1872007-05-25 23:36:54 -0400248 cmd->size = cpu_to_le16(sizeof(struct cmd_ds_802_11_ps_mode) +
Holger Schurig8ec97cc2009-10-22 15:30:55 +0200249 sizeof(struct cmd_header));
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200250 psm->action = cpu_to_le16(cmd_action);
251 psm->multipledtim = 0;
David Woodhouse981f1872007-05-25 23:36:54 -0400252 switch (cmd_action) {
Dan Williams0aef64d2007-08-02 11:31:18 -0400253 case CMD_SUBCMD_ENTER_PS:
Holger Schurig9012b282007-05-25 11:27:16 -0400254 lbs_deb_cmd("PS command:" "SubCode- Enter PS\n");
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200255
Holger Schurig252cf0d12007-08-02 13:09:34 -0400256 psm->locallisteninterval = 0;
Holger Schurig97605c32007-08-02 13:09:15 -0400257 psm->nullpktinterval = 0;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200258 psm->multipledtim =
Holger Schurig56c46562007-08-02 13:09:49 -0400259 cpu_to_le16(MRVDRV_DEFAULT_MULTIPLE_DTIM);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200260 break;
261
Dan Williams0aef64d2007-08-02 11:31:18 -0400262 case CMD_SUBCMD_EXIT_PS:
Holger Schurig9012b282007-05-25 11:27:16 -0400263 lbs_deb_cmd("PS command:" "SubCode- Exit PS\n");
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200264 break;
265
Dan Williams0aef64d2007-08-02 11:31:18 -0400266 case CMD_SUBCMD_SLEEP_CONFIRMED:
Holger Schurig9012b282007-05-25 11:27:16 -0400267 lbs_deb_cmd("PS command: SubCode- sleep confirm\n");
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200268 break;
269
270 default:
271 break;
272 }
273
Holger Schurig9012b282007-05-25 11:27:16 -0400274 lbs_deb_leave(LBS_DEB_CMD);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200275 return 0;
276}
277
David Woodhouse3fbe1042007-12-17 23:48:31 -0500278int lbs_cmd_802_11_sleep_params(struct lbs_private *priv, uint16_t cmd_action,
279 struct sleep_params *sp)
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200280{
David Woodhouse3fbe1042007-12-17 23:48:31 -0500281 struct cmd_ds_802_11_sleep_params cmd;
282 int ret;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200283
Holger Schurig9012b282007-05-25 11:27:16 -0400284 lbs_deb_enter(LBS_DEB_CMD);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200285
Dan Williams0aef64d2007-08-02 11:31:18 -0400286 if (cmd_action == CMD_ACT_GET) {
David Woodhouse3fbe1042007-12-17 23:48:31 -0500287 memset(&cmd, 0, sizeof(cmd));
288 } else {
289 cmd.error = cpu_to_le16(sp->sp_error);
290 cmd.offset = cpu_to_le16(sp->sp_offset);
291 cmd.stabletime = cpu_to_le16(sp->sp_stabletime);
292 cmd.calcontrol = sp->sp_calcontrol;
293 cmd.externalsleepclk = sp->sp_extsleepclk;
294 cmd.reserved = cpu_to_le16(sp->sp_reserved);
295 }
296 cmd.hdr.size = cpu_to_le16(sizeof(cmd));
297 cmd.action = cpu_to_le16(cmd_action);
298
299 ret = lbs_cmd_with_response(priv, CMD_802_11_SLEEP_PARAMS, &cmd);
300
301 if (!ret) {
302 lbs_deb_cmd("error 0x%x, offset 0x%x, stabletime 0x%x, "
303 "calcontrol 0x%x extsleepclk 0x%x\n",
304 le16_to_cpu(cmd.error), le16_to_cpu(cmd.offset),
305 le16_to_cpu(cmd.stabletime), cmd.calcontrol,
306 cmd.externalsleepclk);
307
308 sp->sp_error = le16_to_cpu(cmd.error);
309 sp->sp_offset = le16_to_cpu(cmd.offset);
310 sp->sp_stabletime = le16_to_cpu(cmd.stabletime);
311 sp->sp_calcontrol = cmd.calcontrol;
312 sp->sp_extsleepclk = cmd.externalsleepclk;
313 sp->sp_reserved = le16_to_cpu(cmd.reserved);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200314 }
315
David Woodhouse3fbe1042007-12-17 23:48:31 -0500316 lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200317 return 0;
318}
319
Amitkumar Karwar49125452009-09-30 20:04:38 -0700320static int lbs_wait_for_ds_awake(struct lbs_private *priv)
321{
322 int ret = 0;
323
324 lbs_deb_enter(LBS_DEB_CMD);
325
326 if (priv->is_deep_sleep) {
327 if (!wait_event_interruptible_timeout(priv->ds_awake_q,
328 !priv->is_deep_sleep, (10 * HZ))) {
329 lbs_pr_err("ds_awake_q: timer expired\n");
330 ret = -1;
331 }
332 }
333
334 lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret);
335 return ret;
336}
337
338int lbs_set_deep_sleep(struct lbs_private *priv, int deep_sleep)
339{
340 int ret = 0;
341
342 lbs_deb_enter(LBS_DEB_CMD);
343
344 if (deep_sleep) {
345 if (priv->is_deep_sleep != 1) {
346 lbs_deb_cmd("deep sleep: sleep\n");
347 BUG_ON(!priv->enter_deep_sleep);
348 ret = priv->enter_deep_sleep(priv);
349 if (!ret) {
350 netif_stop_queue(priv->dev);
351 netif_carrier_off(priv->dev);
352 }
353 } else {
354 lbs_pr_err("deep sleep: already enabled\n");
355 }
356 } else {
357 if (priv->is_deep_sleep) {
358 lbs_deb_cmd("deep sleep: wakeup\n");
359 BUG_ON(!priv->exit_deep_sleep);
360 ret = priv->exit_deep_sleep(priv);
361 if (!ret) {
362 ret = lbs_wait_for_ds_awake(priv);
363 if (ret)
364 lbs_pr_err("deep sleep: wakeup"
365 "failed\n");
366 }
367 }
368 }
369
370 lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret);
371 return ret;
372}
373
Dan Williams39fcf7a2008-09-10 12:49:00 -0400374/**
375 * @brief Set an SNMP MIB value
376 *
377 * @param priv A pointer to struct lbs_private structure
378 * @param oid The OID to set in the firmware
379 * @param val Value to set the OID to
380 *
381 * @return 0 on success, error on failure
382 */
383int lbs_set_snmp_mib(struct lbs_private *priv, u32 oid, u16 val)
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200384{
Dan Williams39fcf7a2008-09-10 12:49:00 -0400385 struct cmd_ds_802_11_snmp_mib cmd;
386 int ret;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200387
Holger Schurig9012b282007-05-25 11:27:16 -0400388 lbs_deb_enter(LBS_DEB_CMD);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200389
Dan Williams39fcf7a2008-09-10 12:49:00 -0400390 memset(&cmd, 0, sizeof (cmd));
391 cmd.hdr.size = cpu_to_le16(sizeof(cmd));
392 cmd.action = cpu_to_le16(CMD_ACT_SET);
393 cmd.oid = cpu_to_le16((u16) oid);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200394
Dan Williams39fcf7a2008-09-10 12:49:00 -0400395 switch (oid) {
396 case SNMP_MIB_OID_BSS_TYPE:
397 cmd.bufsize = cpu_to_le16(sizeof(u8));
Holger Schurigfef06402009-10-22 15:30:59 +0200398 cmd.value[0] = val;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200399 break;
Dan Williams39fcf7a2008-09-10 12:49:00 -0400400 case SNMP_MIB_OID_11D_ENABLE:
401 case SNMP_MIB_OID_FRAG_THRESHOLD:
402 case SNMP_MIB_OID_RTS_THRESHOLD:
403 case SNMP_MIB_OID_SHORT_RETRY_LIMIT:
404 case SNMP_MIB_OID_LONG_RETRY_LIMIT:
405 cmd.bufsize = cpu_to_le16(sizeof(u16));
406 *((__le16 *)(&cmd.value)) = cpu_to_le16(val);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200407 break;
408 default:
Dan Williams39fcf7a2008-09-10 12:49:00 -0400409 lbs_deb_cmd("SNMP_CMD: (set) unhandled OID 0x%x\n", oid);
410 ret = -EINVAL;
411 goto out;
412 }
413
414 lbs_deb_cmd("SNMP_CMD: (set) oid 0x%x, oid size 0x%x, value 0x%x\n",
415 le16_to_cpu(cmd.oid), le16_to_cpu(cmd.bufsize), val);
416
417 ret = lbs_cmd_with_response(priv, CMD_802_11_SNMP_MIB, &cmd);
418
419out:
420 lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret);
421 return ret;
422}
423
424/**
425 * @brief Get an SNMP MIB value
426 *
427 * @param priv A pointer to struct lbs_private structure
428 * @param oid The OID to retrieve from the firmware
429 * @param out_val Location for the returned value
430 *
431 * @return 0 on success, error on failure
432 */
433int lbs_get_snmp_mib(struct lbs_private *priv, u32 oid, u16 *out_val)
434{
435 struct cmd_ds_802_11_snmp_mib cmd;
436 int ret;
437
438 lbs_deb_enter(LBS_DEB_CMD);
439
440 memset(&cmd, 0, sizeof (cmd));
441 cmd.hdr.size = cpu_to_le16(sizeof(cmd));
442 cmd.action = cpu_to_le16(CMD_ACT_GET);
443 cmd.oid = cpu_to_le16(oid);
444
445 ret = lbs_cmd_with_response(priv, CMD_802_11_SNMP_MIB, &cmd);
446 if (ret)
447 goto out;
448
449 switch (le16_to_cpu(cmd.bufsize)) {
450 case sizeof(u8):
Holger Schurigfef06402009-10-22 15:30:59 +0200451 *out_val = cmd.value[0];
Dan Williams39fcf7a2008-09-10 12:49:00 -0400452 break;
453 case sizeof(u16):
454 *out_val = le16_to_cpu(*((__le16 *)(&cmd.value)));
455 break;
456 default:
457 lbs_deb_cmd("SNMP_CMD: (get) unhandled OID 0x%x size %d\n",
458 oid, le16_to_cpu(cmd.bufsize));
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200459 break;
460 }
461
Dan Williams39fcf7a2008-09-10 12:49:00 -0400462out:
463 lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret);
464 return ret;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200465}
466
Dan Williams87c8c722008-08-19 15:15:35 -0400467/**
468 * @brief Get the min, max, and current TX power
469 *
470 * @param priv A pointer to struct lbs_private structure
471 * @param curlevel Current power level in dBm
472 * @param minlevel Minimum supported power level in dBm (optional)
473 * @param maxlevel Maximum supported power level in dBm (optional)
474 *
475 * @return 0 on success, error on failure
476 */
477int lbs_get_tx_power(struct lbs_private *priv, s16 *curlevel, s16 *minlevel,
478 s16 *maxlevel)
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200479{
Dan Williams87c8c722008-08-19 15:15:35 -0400480 struct cmd_ds_802_11_rf_tx_power cmd;
481 int ret;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200482
Holger Schurig9012b282007-05-25 11:27:16 -0400483 lbs_deb_enter(LBS_DEB_CMD);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200484
Dan Williams87c8c722008-08-19 15:15:35 -0400485 memset(&cmd, 0, sizeof(cmd));
486 cmd.hdr.size = cpu_to_le16(sizeof(cmd));
487 cmd.action = cpu_to_le16(CMD_ACT_GET);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200488
Dan Williams87c8c722008-08-19 15:15:35 -0400489 ret = lbs_cmd_with_response(priv, CMD_802_11_RF_TX_POWER, &cmd);
490 if (ret == 0) {
491 *curlevel = le16_to_cpu(cmd.curlevel);
492 if (minlevel)
Holger Schurig87bf24f2008-10-29 10:35:02 +0100493 *minlevel = cmd.minlevel;
Dan Williams87c8c722008-08-19 15:15:35 -0400494 if (maxlevel)
Holger Schurig87bf24f2008-10-29 10:35:02 +0100495 *maxlevel = cmd.maxlevel;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200496 }
Holger Schurig9012b282007-05-25 11:27:16 -0400497
498 lbs_deb_leave(LBS_DEB_CMD);
Dan Williams87c8c722008-08-19 15:15:35 -0400499 return ret;
500}
501
502/**
503 * @brief Set the TX power
504 *
505 * @param priv A pointer to struct lbs_private structure
506 * @param dbm The desired power level in dBm
507 *
508 * @return 0 on success, error on failure
509 */
510int lbs_set_tx_power(struct lbs_private *priv, s16 dbm)
511{
512 struct cmd_ds_802_11_rf_tx_power cmd;
513 int ret;
514
515 lbs_deb_enter(LBS_DEB_CMD);
516
517 memset(&cmd, 0, sizeof(cmd));
518 cmd.hdr.size = cpu_to_le16(sizeof(cmd));
519 cmd.action = cpu_to_le16(CMD_ACT_SET);
520 cmd.curlevel = cpu_to_le16(dbm);
521
522 lbs_deb_cmd("SET_RF_TX_POWER: %d dBm\n", dbm);
523
524 ret = lbs_cmd_with_response(priv, CMD_802_11_RF_TX_POWER, &cmd);
525
526 lbs_deb_leave(LBS_DEB_CMD);
527 return ret;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200528}
529
Holger Schurige98a88d2008-03-19 14:25:58 +0100530static int lbs_cmd_802_11_monitor_mode(struct cmd_ds_command *cmd,
Luis Carlos Cobo965f8bb2007-08-02 13:16:55 -0400531 u16 cmd_action, void *pdata_buf)
532{
533 struct cmd_ds_802_11_monitor_mode *monitor = &cmd->params.monitor;
534
535 cmd->command = cpu_to_le16(CMD_802_11_MONITOR_MODE);
536 cmd->size =
537 cpu_to_le16(sizeof(struct cmd_ds_802_11_monitor_mode) +
Holger Schurig8ec97cc2009-10-22 15:30:55 +0200538 sizeof(struct cmd_header));
Luis Carlos Cobo965f8bb2007-08-02 13:16:55 -0400539
540 monitor->action = cpu_to_le16(cmd_action);
541 if (cmd_action == CMD_ACT_SET) {
542 monitor->mode =
543 cpu_to_le16((u16) (*(u32 *) pdata_buf));
544 }
545
546 return 0;
547}
548
Dan Williams2dd4b262007-12-11 16:54:15 -0500549/**
550 * @brief Get the radio channel
551 *
552 * @param priv A pointer to struct lbs_private structure
553 *
554 * @return The channel on success, error on failure
555 */
Holger Schuriga3cbfb02009-10-16 17:33:56 +0200556static int lbs_get_channel(struct lbs_private *priv)
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200557{
Dan Williams2dd4b262007-12-11 16:54:15 -0500558 struct cmd_ds_802_11_rf_channel cmd;
559 int ret = 0;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200560
Holger Schurig8ff12da2007-08-02 11:54:31 -0400561 lbs_deb_enter(LBS_DEB_CMD);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200562
Holger Schurig8d0c7fa2008-04-09 10:23:31 +0200563 memset(&cmd, 0, sizeof(cmd));
Dan Williams2dd4b262007-12-11 16:54:15 -0500564 cmd.hdr.size = cpu_to_le16(sizeof(cmd));
565 cmd.action = cpu_to_le16(CMD_OPT_802_11_RF_CHANNEL_GET);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200566
David Woodhouse689442d2007-12-12 16:00:42 -0500567 ret = lbs_cmd_with_response(priv, CMD_802_11_RF_CHANNEL, &cmd);
Dan Williams2dd4b262007-12-11 16:54:15 -0500568 if (ret)
569 goto out;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200570
Dan Williamscb182a62007-12-11 17:35:51 -0500571 ret = le16_to_cpu(cmd.channel);
572 lbs_deb_cmd("current radio channel is %d\n", ret);
Dan Williams2dd4b262007-12-11 16:54:15 -0500573
574out:
575 lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret);
576 return ret;
577}
578
Holger Schurig73ab1f22008-04-02 16:52:19 +0200579int lbs_update_channel(struct lbs_private *priv)
580{
581 int ret;
582
583 /* the channel in f/w could be out of sync; get the current channel */
584 lbs_deb_enter(LBS_DEB_ASSOC);
585
586 ret = lbs_get_channel(priv);
587 if (ret > 0) {
Holger Schurigc14951f2009-10-22 15:30:50 +0200588 priv->channel = ret;
Holger Schurig73ab1f22008-04-02 16:52:19 +0200589 ret = 0;
590 }
591 lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret);
592 return ret;
593}
594
Dan Williams2dd4b262007-12-11 16:54:15 -0500595/**
596 * @brief Set the radio channel
597 *
598 * @param priv A pointer to struct lbs_private structure
599 * @param channel The desired channel, or 0 to clear a locked channel
600 *
601 * @return 0 on success, error on failure
602 */
603int lbs_set_channel(struct lbs_private *priv, u8 channel)
604{
605 struct cmd_ds_802_11_rf_channel cmd;
Manish Katiyar96d46d52008-10-13 16:22:42 +0530606#ifdef DEBUG
Holger Schurigc14951f2009-10-22 15:30:50 +0200607 u8 old_channel = priv->channel;
Manish Katiyar96d46d52008-10-13 16:22:42 +0530608#endif
Dan Williams2dd4b262007-12-11 16:54:15 -0500609 int ret = 0;
610
611 lbs_deb_enter(LBS_DEB_CMD);
612
Holger Schurig8d0c7fa2008-04-09 10:23:31 +0200613 memset(&cmd, 0, sizeof(cmd));
Dan Williams2dd4b262007-12-11 16:54:15 -0500614 cmd.hdr.size = cpu_to_le16(sizeof(cmd));
615 cmd.action = cpu_to_le16(CMD_OPT_802_11_RF_CHANNEL_SET);
616 cmd.channel = cpu_to_le16(channel);
617
David Woodhouse689442d2007-12-12 16:00:42 -0500618 ret = lbs_cmd_with_response(priv, CMD_802_11_RF_CHANNEL, &cmd);
Dan Williams2dd4b262007-12-11 16:54:15 -0500619 if (ret)
620 goto out;
621
Holger Schurigc14951f2009-10-22 15:30:50 +0200622 priv->channel = (uint8_t) le16_to_cpu(cmd.channel);
Dan Williamscb182a62007-12-11 17:35:51 -0500623 lbs_deb_cmd("channel switch from %d to %d\n", old_channel,
Holger Schurigc14951f2009-10-22 15:30:50 +0200624 priv->channel);
Dan Williams2dd4b262007-12-11 16:54:15 -0500625
626out:
627 lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret);
628 return ret;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200629}
630
Holger Schurige98a88d2008-03-19 14:25:58 +0100631static int lbs_cmd_reg_access(struct cmd_ds_command *cmdptr,
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200632 u8 cmd_action, void *pdata_buf)
633{
Holger Schurig10078322007-11-15 18:05:47 -0500634 struct lbs_offset_value *offval;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200635
Holger Schurig9012b282007-05-25 11:27:16 -0400636 lbs_deb_enter(LBS_DEB_CMD);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200637
Holger Schurig10078322007-11-15 18:05:47 -0500638 offval = (struct lbs_offset_value *)pdata_buf;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200639
Holger Schurigc2df2ef2007-12-07 15:30:44 +0000640 switch (le16_to_cpu(cmdptr->command)) {
Dan Williams0aef64d2007-08-02 11:31:18 -0400641 case CMD_MAC_REG_ACCESS:
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200642 {
643 struct cmd_ds_mac_reg_access *macreg;
644
645 cmdptr->size =
David Woodhouse981f1872007-05-25 23:36:54 -0400646 cpu_to_le16(sizeof (struct cmd_ds_mac_reg_access)
Holger Schurig8ec97cc2009-10-22 15:30:55 +0200647 + sizeof(struct cmd_header));
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200648 macreg =
649 (struct cmd_ds_mac_reg_access *)&cmdptr->params.
650 macreg;
651
652 macreg->action = cpu_to_le16(cmd_action);
653 macreg->offset = cpu_to_le16((u16) offval->offset);
654 macreg->value = cpu_to_le32(offval->value);
655
656 break;
657 }
658
Dan Williams0aef64d2007-08-02 11:31:18 -0400659 case CMD_BBP_REG_ACCESS:
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200660 {
661 struct cmd_ds_bbp_reg_access *bbpreg;
662
663 cmdptr->size =
664 cpu_to_le16(sizeof
665 (struct cmd_ds_bbp_reg_access)
Holger Schurig8ec97cc2009-10-22 15:30:55 +0200666 + sizeof(struct cmd_header));
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200667 bbpreg =
668 (struct cmd_ds_bbp_reg_access *)&cmdptr->params.
669 bbpreg;
670
671 bbpreg->action = cpu_to_le16(cmd_action);
672 bbpreg->offset = cpu_to_le16((u16) offval->offset);
673 bbpreg->value = (u8) offval->value;
674
675 break;
676 }
677
Dan Williams0aef64d2007-08-02 11:31:18 -0400678 case CMD_RF_REG_ACCESS:
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200679 {
680 struct cmd_ds_rf_reg_access *rfreg;
681
682 cmdptr->size =
683 cpu_to_le16(sizeof
684 (struct cmd_ds_rf_reg_access) +
Holger Schurig8ec97cc2009-10-22 15:30:55 +0200685 sizeof(struct cmd_header));
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200686 rfreg =
687 (struct cmd_ds_rf_reg_access *)&cmdptr->params.
688 rfreg;
689
690 rfreg->action = cpu_to_le16(cmd_action);
691 rfreg->offset = cpu_to_le16((u16) offval->offset);
692 rfreg->value = (u8) offval->value;
693
694 break;
695 }
696
697 default:
698 break;
699 }
700
Holger Schurig9012b282007-05-25 11:27:16 -0400701 lbs_deb_leave(LBS_DEB_CMD);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200702 return 0;
703}
704
David Woodhouse681ffbb2007-12-15 20:04:54 -0500705static void lbs_queue_cmd(struct lbs_private *priv,
706 struct cmd_ctrl_node *cmdnode)
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200707{
708 unsigned long flags;
David Woodhouse681ffbb2007-12-15 20:04:54 -0500709 int addtail = 1;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200710
Holger Schurig8ff12da2007-08-02 11:54:31 -0400711 lbs_deb_enter(LBS_DEB_HOST);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200712
David Woodhousec4ab4122007-12-15 00:41:51 -0500713 if (!cmdnode) {
714 lbs_deb_host("QUEUE_CMD: cmdnode is NULL\n");
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200715 goto done;
716 }
David Woodhoused9896ee2007-12-15 00:09:25 -0500717 if (!cmdnode->cmdbuf->size) {
718 lbs_deb_host("DNLD_CMD: cmd size is zero\n");
719 goto done;
720 }
David Woodhouseae125bf2007-12-15 04:22:52 -0500721 cmdnode->result = 0;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200722
723 /* Exit_PS command needs to be queued in the header always. */
Dan Williamsddac4522007-12-11 13:49:39 -0500724 if (le16_to_cpu(cmdnode->cmdbuf->command) == CMD_802_11_PS_MODE) {
David Woodhouse38bfab12007-12-16 23:26:54 -0500725 struct cmd_ds_802_11_ps_mode *psm = (void *) &cmdnode->cmdbuf[1];
Dan Williamsddac4522007-12-11 13:49:39 -0500726
Dan Williams0aef64d2007-08-02 11:31:18 -0400727 if (psm->action == cpu_to_le16(CMD_SUBCMD_EXIT_PS)) {
David Woodhouseaa21c002007-12-08 20:04:36 +0000728 if (priv->psstate != PS_STATE_FULL_POWER)
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200729 addtail = 0;
730 }
731 }
732
Amitkumar Karwar66fceb62010-05-19 03:24:38 -0700733 if (le16_to_cpu(cmdnode->cmdbuf->command) ==
734 CMD_802_11_WAKEUP_CONFIRM)
735 addtail = 0;
736
David Woodhouseaa21c002007-12-08 20:04:36 +0000737 spin_lock_irqsave(&priv->driver_lock, flags);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200738
David Woodhouseac472462007-12-08 00:35:00 +0000739 if (addtail)
David Woodhouseaa21c002007-12-08 20:04:36 +0000740 list_add_tail(&cmdnode->list, &priv->cmdpendingq);
David Woodhouseac472462007-12-08 00:35:00 +0000741 else
David Woodhouseaa21c002007-12-08 20:04:36 +0000742 list_add(&cmdnode->list, &priv->cmdpendingq);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200743
David Woodhouseaa21c002007-12-08 20:04:36 +0000744 spin_unlock_irqrestore(&priv->driver_lock, flags);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200745
Holger Schurig8ff12da2007-08-02 11:54:31 -0400746 lbs_deb_host("QUEUE_CMD: inserted command 0x%04x into cmdpendingq\n",
David Woodhousec4ab4122007-12-15 00:41:51 -0500747 le16_to_cpu(cmdnode->cmdbuf->command));
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200748
749done:
Holger Schurig8ff12da2007-08-02 11:54:31 -0400750 lbs_deb_leave(LBS_DEB_HOST);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200751}
752
David Woodhouse18c52e72007-12-17 16:03:58 -0500753static void lbs_submit_command(struct lbs_private *priv,
754 struct cmd_ctrl_node *cmdnode)
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200755{
756 unsigned long flags;
Dan Williamsddac4522007-12-11 13:49:39 -0500757 struct cmd_header *cmd;
David Woodhouse18c52e72007-12-17 16:03:58 -0500758 uint16_t cmdsize;
759 uint16_t command;
Holger Schurig57962f02008-05-14 16:30:28 +0200760 int timeo = 3 * HZ;
David Woodhouse18c52e72007-12-17 16:03:58 -0500761 int ret;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200762
Holger Schurig8ff12da2007-08-02 11:54:31 -0400763 lbs_deb_enter(LBS_DEB_HOST);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200764
Dan Williamsddac4522007-12-11 13:49:39 -0500765 cmd = cmdnode->cmdbuf;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200766
David Woodhouseaa21c002007-12-08 20:04:36 +0000767 spin_lock_irqsave(&priv->driver_lock, flags);
David Woodhouseaa21c002007-12-08 20:04:36 +0000768 priv->cur_cmd = cmdnode;
769 priv->cur_cmd_retcode = 0;
770 spin_unlock_irqrestore(&priv->driver_lock, flags);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200771
Dan Williamsddac4522007-12-11 13:49:39 -0500772 cmdsize = le16_to_cpu(cmd->size);
773 command = le16_to_cpu(cmd->command);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200774
David Woodhouse18c52e72007-12-17 16:03:58 -0500775 /* These commands take longer */
Dan Williamsbe0d76e2009-05-22 20:05:25 -0400776 if (command == CMD_802_11_SCAN || command == CMD_802_11_ASSOCIATE)
Holger Schurig57962f02008-05-14 16:30:28 +0200777 timeo = 5 * HZ;
David Woodhouse18c52e72007-12-17 16:03:58 -0500778
Holger Schurige5225b32008-03-26 10:04:44 +0100779 lbs_deb_cmd("DNLD_CMD: command 0x%04x, seq %d, size %d\n",
780 command, le16_to_cpu(cmd->seqnum), cmdsize);
Holger Schurig1afc09ab2008-01-29 09:14:40 +0100781 lbs_deb_hex(LBS_DEB_CMD, "DNLD_CMD", (void *) cmdnode->cmdbuf, cmdsize);
Holger Schurig8ff12da2007-08-02 11:54:31 -0400782
Dan Williamsddac4522007-12-11 13:49:39 -0500783 ret = priv->hw_host_to_card(priv, MVMS_CMD, (u8 *) cmd, cmdsize);
David Woodhouse18c52e72007-12-17 16:03:58 -0500784
David Woodhoused9896ee2007-12-15 00:09:25 -0500785 if (ret) {
786 lbs_pr_info("DNLD_CMD: hw_host_to_card failed: %d\n", ret);
David Woodhouse18c52e72007-12-17 16:03:58 -0500787 /* Let the timer kick in and retry, and potentially reset
788 the whole thing if the condition persists */
Holger Schurig57962f02008-05-14 16:30:28 +0200789 timeo = HZ/4;
Holger Schurig1afc09ab2008-01-29 09:14:40 +0100790 }
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200791
Amitkumar Karwar49125452009-09-30 20:04:38 -0700792 if (command == CMD_802_11_DEEP_SLEEP) {
793 if (priv->is_auto_deep_sleep_enabled) {
794 priv->wakeup_dev_required = 1;
795 priv->dnld_sent = 0;
796 }
797 priv->is_deep_sleep = 1;
798 lbs_complete_command(priv, cmdnode, 0);
799 } else {
800 /* Setup the timer after transmit command */
801 mod_timer(&priv->command_timer, jiffies + timeo);
802 }
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200803
David Woodhouse18c52e72007-12-17 16:03:58 -0500804 lbs_deb_leave(LBS_DEB_HOST);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200805}
806
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200807/**
808 * This function inserts command node to cmdfreeq
David Woodhouseaa21c002007-12-08 20:04:36 +0000809 * after cleans it. Requires priv->driver_lock held.
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200810 */
David Woodhouse183aeac2007-12-15 01:52:54 -0500811static void __lbs_cleanup_and_insert_cmd(struct lbs_private *priv,
David Woodhouse5ba2f8a2007-12-15 02:02:56 -0500812 struct cmd_ctrl_node *cmdnode)
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200813{
David Woodhouse5ba2f8a2007-12-15 02:02:56 -0500814 lbs_deb_enter(LBS_DEB_HOST);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200815
David Woodhouse5ba2f8a2007-12-15 02:02:56 -0500816 if (!cmdnode)
817 goto out;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200818
David Woodhouse5ba2f8a2007-12-15 02:02:56 -0500819 cmdnode->callback = NULL;
820 cmdnode->callback_arg = 0;
821
822 memset(cmdnode->cmdbuf, 0, LBS_CMD_BUFFER_SIZE);
823
824 list_add_tail(&cmdnode->list, &priv->cmdfreeq);
825 out:
826 lbs_deb_leave(LBS_DEB_HOST);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200827}
828
Holger Schurig69f90322007-11-23 15:43:44 +0100829static void lbs_cleanup_and_insert_cmd(struct lbs_private *priv,
830 struct cmd_ctrl_node *ptempcmd)
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200831{
832 unsigned long flags;
833
David Woodhouseaa21c002007-12-08 20:04:36 +0000834 spin_lock_irqsave(&priv->driver_lock, flags);
Holger Schurig10078322007-11-15 18:05:47 -0500835 __lbs_cleanup_and_insert_cmd(priv, ptempcmd);
David Woodhouseaa21c002007-12-08 20:04:36 +0000836 spin_unlock_irqrestore(&priv->driver_lock, flags);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200837}
838
David Woodhouse183aeac2007-12-15 01:52:54 -0500839void lbs_complete_command(struct lbs_private *priv, struct cmd_ctrl_node *cmd,
840 int result)
841{
842 if (cmd == priv->cur_cmd)
843 priv->cur_cmd_retcode = result;
David Woodhouse5ba2f8a2007-12-15 02:02:56 -0500844
David Woodhouseae125bf2007-12-15 04:22:52 -0500845 cmd->result = result;
David Woodhouse5ba2f8a2007-12-15 02:02:56 -0500846 cmd->cmdwaitqwoken = 1;
847 wake_up_interruptible(&cmd->cmdwait_q);
848
Holger Schurig8db4a2b2008-03-19 10:11:00 +0100849 if (!cmd->callback || cmd->callback == lbs_cmd_async_callback)
David Woodhousead12d0f2007-12-15 02:06:16 -0500850 __lbs_cleanup_and_insert_cmd(priv, cmd);
David Woodhouse183aeac2007-12-15 01:52:54 -0500851 priv->cur_cmd = NULL;
852}
853
Dan Williamsd5db2df2008-08-21 17:51:07 -0400854int lbs_set_radio(struct lbs_private *priv, u8 preamble, u8 radio_on)
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200855{
David Woodhousea7c45892007-12-17 22:43:48 -0500856 struct cmd_ds_802_11_radio_control cmd;
Dan Williamsd5db2df2008-08-21 17:51:07 -0400857 int ret = -EINVAL;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200858
Holger Schurig9012b282007-05-25 11:27:16 -0400859 lbs_deb_enter(LBS_DEB_CMD);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200860
David Woodhousea7c45892007-12-17 22:43:48 -0500861 cmd.hdr.size = cpu_to_le16(sizeof(cmd));
862 cmd.action = cpu_to_le16(CMD_ACT_SET);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200863
Dan Williamsd5db2df2008-08-21 17:51:07 -0400864 /* Only v8 and below support setting the preamble */
865 if (priv->fwrelease < 0x09000000) {
866 switch (preamble) {
867 case RADIO_PREAMBLE_SHORT:
Dan Williamsd5db2df2008-08-21 17:51:07 -0400868 case RADIO_PREAMBLE_AUTO:
869 case RADIO_PREAMBLE_LONG:
870 cmd.control = cpu_to_le16(preamble);
871 break;
872 default:
873 goto out;
874 }
David Woodhousea7c45892007-12-17 22:43:48 -0500875 }
876
Dan Williamsd5db2df2008-08-21 17:51:07 -0400877 if (radio_on)
878 cmd.control |= cpu_to_le16(0x1);
879 else {
880 cmd.control &= cpu_to_le16(~0x1);
881 priv->txpower_cur = 0;
882 }
David Woodhousea7c45892007-12-17 22:43:48 -0500883
Dan Williamsd5db2df2008-08-21 17:51:07 -0400884 lbs_deb_cmd("RADIO_CONTROL: radio %s, preamble %d\n",
885 radio_on ? "ON" : "OFF", preamble);
886
887 priv->radio_on = radio_on;
David Woodhousea7c45892007-12-17 22:43:48 -0500888
889 ret = lbs_cmd_with_response(priv, CMD_802_11_RADIO_CONTROL, &cmd);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200890
Dan Williamsd5db2df2008-08-21 17:51:07 -0400891out:
Holger Schurig9012b282007-05-25 11:27:16 -0400892 lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200893 return ret;
894}
895
Holger Schurigc97329e2008-03-18 11:20:21 +0100896void lbs_set_mac_control(struct lbs_private *priv)
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200897{
Holger Schurig835d3ac2008-03-12 16:05:40 +0100898 struct cmd_ds_mac_control cmd;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200899
Holger Schurig9012b282007-05-25 11:27:16 -0400900 lbs_deb_enter(LBS_DEB_CMD);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200901
Holger Schurig835d3ac2008-03-12 16:05:40 +0100902 cmd.hdr.size = cpu_to_le16(sizeof(cmd));
Holger Schurigd9e97782008-03-12 16:06:43 +0100903 cmd.action = cpu_to_le16(priv->mac_control);
Holger Schurig835d3ac2008-03-12 16:05:40 +0100904 cmd.reserved = 0;
905
David Woodhouse75bf45a2008-05-20 13:32:45 +0100906 lbs_cmd_async(priv, CMD_MAC_CONTROL, &cmd.hdr, sizeof(cmd));
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200907
Holger Schurigc97329e2008-03-18 11:20:21 +0100908 lbs_deb_leave(LBS_DEB_CMD);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200909}
910
911/**
912 * @brief This function prepare the command before send to firmware.
913 *
Holger Schurig69f90322007-11-23 15:43:44 +0100914 * @param priv A pointer to struct lbs_private structure
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200915 * @param cmd_no command number
916 * @param cmd_action command action: GET or SET
917 * @param wait_option wait option: wait response or not
918 * @param cmd_oid cmd oid: treated as sub command
919 * @param pdata_buf A pointer to informaion buffer
920 * @return 0 or -1
921 */
Holger Schurig69f90322007-11-23 15:43:44 +0100922int lbs_prepare_and_send_command(struct lbs_private *priv,
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200923 u16 cmd_no,
924 u16 cmd_action,
925 u16 wait_option, u32 cmd_oid, void *pdata_buf)
926{
927 int ret = 0;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200928 struct cmd_ctrl_node *cmdnode;
929 struct cmd_ds_command *cmdptr;
930 unsigned long flags;
931
Holger Schurig8ff12da2007-08-02 11:54:31 -0400932 lbs_deb_enter(LBS_DEB_HOST);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200933
David Woodhouseaa21c002007-12-08 20:04:36 +0000934 if (!priv) {
935 lbs_deb_host("PREP_CMD: priv is NULL\n");
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200936 ret = -1;
937 goto done;
938 }
939
David Woodhouseaa21c002007-12-08 20:04:36 +0000940 if (priv->surpriseremoved) {
Holger Schurig8ff12da2007-08-02 11:54:31 -0400941 lbs_deb_host("PREP_CMD: card removed\n");
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200942 ret = -1;
943 goto done;
944 }
945
Amitkumar Karwar63f275d2009-10-06 19:20:28 -0700946 if (!lbs_is_cmd_allowed(priv)) {
947 ret = -EBUSY;
948 goto done;
949 }
950
Holger Schurig0d61d042007-12-05 17:58:06 +0100951 cmdnode = lbs_get_cmd_ctrl_node(priv);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200952
953 if (cmdnode == NULL) {
Holger Schurig8ff12da2007-08-02 11:54:31 -0400954 lbs_deb_host("PREP_CMD: cmdnode is NULL\n");
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200955
956 /* Wake up main thread to execute next command */
Dan Williamsfe336152007-08-02 11:32:25 -0400957 wake_up_interruptible(&priv->waitq);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200958 ret = -1;
959 goto done;
960 }
961
Holger Schurige98a88d2008-03-19 14:25:58 +0100962 cmdnode->callback = NULL;
963 cmdnode->callback_arg = (unsigned long)pdata_buf;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200964
Dan Williamsddac4522007-12-11 13:49:39 -0500965 cmdptr = (struct cmd_ds_command *)cmdnode->cmdbuf;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200966
Holger Schurig8ff12da2007-08-02 11:54:31 -0400967 lbs_deb_host("PREP_CMD: command 0x%04x\n", cmd_no);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200968
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200969 /* Set sequence number, command and INT option */
David Woodhouseaa21c002007-12-08 20:04:36 +0000970 priv->seqnum++;
971 cmdptr->seqnum = cpu_to_le16(priv->seqnum);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200972
David Woodhouse981f1872007-05-25 23:36:54 -0400973 cmdptr->command = cpu_to_le16(cmd_no);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200974 cmdptr->result = 0;
975
976 switch (cmd_no) {
Dan Williams0aef64d2007-08-02 11:31:18 -0400977 case CMD_802_11_PS_MODE:
Holger Schurige98a88d2008-03-19 14:25:58 +0100978 ret = lbs_cmd_802_11_ps_mode(cmdptr, cmd_action);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200979 break;
980
Dan Williams0aef64d2007-08-02 11:31:18 -0400981 case CMD_MAC_REG_ACCESS:
982 case CMD_BBP_REG_ACCESS:
983 case CMD_RF_REG_ACCESS:
Holger Schurige98a88d2008-03-19 14:25:58 +0100984 ret = lbs_cmd_reg_access(cmdptr, cmd_action, pdata_buf);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200985 break;
986
Luis Carlos Cobo965f8bb2007-08-02 13:16:55 -0400987 case CMD_802_11_MONITOR_MODE:
Holger Schurige98a88d2008-03-19 14:25:58 +0100988 ret = lbs_cmd_802_11_monitor_mode(cmdptr,
Luis Carlos Cobo965f8bb2007-08-02 13:16:55 -0400989 cmd_action, pdata_buf);
990 break;
991
Dan Williams0aef64d2007-08-02 11:31:18 -0400992 case CMD_802_11_RSSI:
Holger Schurig10078322007-11-15 18:05:47 -0500993 ret = lbs_cmd_802_11_rssi(priv, cmdptr);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200994 break;
995
Dan Williams0aef64d2007-08-02 11:31:18 -0400996 case CMD_802_11_SET_AFC:
997 case CMD_802_11_GET_AFC:
Marcelo Tosatti876c9d32007-02-10 12:25:27 -0200998
999 cmdptr->command = cpu_to_le16(cmd_no);
David Woodhouse981f1872007-05-25 23:36:54 -04001000 cmdptr->size = cpu_to_le16(sizeof(struct cmd_ds_802_11_afc) +
Holger Schurig8ec97cc2009-10-22 15:30:55 +02001001 sizeof(struct cmd_header));
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001002
1003 memmove(&cmdptr->params.afc,
1004 pdata_buf, sizeof(struct cmd_ds_802_11_afc));
1005
1006 ret = 0;
1007 goto done;
1008
Dan Williams0aef64d2007-08-02 11:31:18 -04001009 case CMD_802_11_TPC_CFG:
1010 cmdptr->command = cpu_to_le16(CMD_802_11_TPC_CFG);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001011 cmdptr->size =
1012 cpu_to_le16(sizeof(struct cmd_ds_802_11_tpc_cfg) +
Holger Schurig8ec97cc2009-10-22 15:30:55 +02001013 sizeof(struct cmd_header));
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001014
1015 memmove(&cmdptr->params.tpccfg,
1016 pdata_buf, sizeof(struct cmd_ds_802_11_tpc_cfg));
1017
1018 ret = 0;
1019 break;
David Woodhouse5844d122007-12-18 02:01:37 -05001020
Holger Schurig4143a232009-12-02 15:26:02 +01001021#ifdef CONFIG_LIBERTAS_MESH
1022
Dan Williams0aef64d2007-08-02 11:31:18 -04001023 case CMD_BT_ACCESS:
Holger Schurige98a88d2008-03-19 14:25:58 +01001024 ret = lbs_cmd_bt_access(cmdptr, cmd_action, pdata_buf);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001025 break;
1026
Dan Williams0aef64d2007-08-02 11:31:18 -04001027 case CMD_FWT_ACCESS:
Holger Schurige98a88d2008-03-19 14:25:58 +01001028 ret = lbs_cmd_fwt_access(cmdptr, cmd_action, pdata_buf);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001029 break;
1030
Holger Schurig4143a232009-12-02 15:26:02 +01001031#endif
1032
Brajesh Dave96287ac2007-11-20 17:44:28 -05001033 case CMD_802_11_BEACON_CTRL:
1034 ret = lbs_cmd_bcn_ctrl(priv, cmdptr, cmd_action);
1035 break;
Amitkumar Karwar49125452009-09-30 20:04:38 -07001036 case CMD_802_11_DEEP_SLEEP:
1037 cmdptr->command = cpu_to_le16(CMD_802_11_DEEP_SLEEP);
Holger Schurig8ec97cc2009-10-22 15:30:55 +02001038 cmdptr->size = cpu_to_le16(sizeof(struct cmd_header));
Amitkumar Karwar49125452009-09-30 20:04:38 -07001039 break;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001040 default:
David Woodhousee37fc6e2008-05-20 11:47:16 +01001041 lbs_pr_err("PREP_CMD: unknown command 0x%04x\n", cmd_no);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001042 ret = -1;
1043 break;
1044 }
1045
1046 /* return error, since the command preparation failed */
1047 if (ret != 0) {
Holger Schurig8ff12da2007-08-02 11:54:31 -04001048 lbs_deb_host("PREP_CMD: command preparation failed\n");
Holger Schurig10078322007-11-15 18:05:47 -05001049 lbs_cleanup_and_insert_cmd(priv, cmdnode);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001050 ret = -1;
1051 goto done;
1052 }
1053
1054 cmdnode->cmdwaitqwoken = 0;
1055
David Woodhouse681ffbb2007-12-15 20:04:54 -05001056 lbs_queue_cmd(priv, cmdnode);
Dan Williamsfe336152007-08-02 11:32:25 -04001057 wake_up_interruptible(&priv->waitq);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001058
Dan Williams0aef64d2007-08-02 11:31:18 -04001059 if (wait_option & CMD_OPTION_WAITFORRSP) {
Holger Schurig8ff12da2007-08-02 11:54:31 -04001060 lbs_deb_host("PREP_CMD: wait for response\n");
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001061 might_sleep();
1062 wait_event_interruptible(cmdnode->cmdwait_q,
1063 cmdnode->cmdwaitqwoken);
1064 }
1065
David Woodhouseaa21c002007-12-08 20:04:36 +00001066 spin_lock_irqsave(&priv->driver_lock, flags);
1067 if (priv->cur_cmd_retcode) {
Holger Schurig8ff12da2007-08-02 11:54:31 -04001068 lbs_deb_host("PREP_CMD: command failed with return code %d\n",
David Woodhouseaa21c002007-12-08 20:04:36 +00001069 priv->cur_cmd_retcode);
1070 priv->cur_cmd_retcode = 0;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001071 ret = -1;
1072 }
David Woodhouseaa21c002007-12-08 20:04:36 +00001073 spin_unlock_irqrestore(&priv->driver_lock, flags);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001074
1075done:
Holger Schurig8ff12da2007-08-02 11:54:31 -04001076 lbs_deb_leave_args(LBS_DEB_HOST, "ret %d", ret);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001077 return ret;
1078}
1079
1080/**
1081 * @brief This function allocates the command buffer and link
1082 * it to command free queue.
1083 *
Holger Schurig69f90322007-11-23 15:43:44 +01001084 * @param priv A pointer to struct lbs_private structure
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001085 * @return 0 or -1
1086 */
Holger Schurig69f90322007-11-23 15:43:44 +01001087int lbs_allocate_cmd_buffer(struct lbs_private *priv)
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001088{
1089 int ret = 0;
Dan Williamsddac4522007-12-11 13:49:39 -05001090 u32 bufsize;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001091 u32 i;
Dan Williamsddac4522007-12-11 13:49:39 -05001092 struct cmd_ctrl_node *cmdarray;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001093
Holger Schurig8ff12da2007-08-02 11:54:31 -04001094 lbs_deb_enter(LBS_DEB_HOST);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001095
Dan Williamsddac4522007-12-11 13:49:39 -05001096 /* Allocate and initialize the command array */
1097 bufsize = sizeof(struct cmd_ctrl_node) * LBS_NUM_CMD_BUFFERS;
1098 if (!(cmdarray = kzalloc(bufsize, GFP_KERNEL))) {
Holger Schurig8ff12da2007-08-02 11:54:31 -04001099 lbs_deb_host("ALLOC_CMD_BUF: tempcmd_array is NULL\n");
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001100 ret = -1;
1101 goto done;
1102 }
Dan Williamsddac4522007-12-11 13:49:39 -05001103 priv->cmd_array = cmdarray;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001104
Dan Williamsddac4522007-12-11 13:49:39 -05001105 /* Allocate and initialize each command buffer in the command array */
1106 for (i = 0; i < LBS_NUM_CMD_BUFFERS; i++) {
1107 cmdarray[i].cmdbuf = kzalloc(LBS_CMD_BUFFER_SIZE, GFP_KERNEL);
1108 if (!cmdarray[i].cmdbuf) {
Holger Schurig8ff12da2007-08-02 11:54:31 -04001109 lbs_deb_host("ALLOC_CMD_BUF: ptempvirtualaddr is NULL\n");
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001110 ret = -1;
1111 goto done;
1112 }
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001113 }
1114
Dan Williamsddac4522007-12-11 13:49:39 -05001115 for (i = 0; i < LBS_NUM_CMD_BUFFERS; i++) {
1116 init_waitqueue_head(&cmdarray[i].cmdwait_q);
1117 lbs_cleanup_and_insert_cmd(priv, &cmdarray[i]);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001118 }
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001119 ret = 0;
Holger Schurig9012b282007-05-25 11:27:16 -04001120
1121done:
Holger Schurig8ff12da2007-08-02 11:54:31 -04001122 lbs_deb_leave_args(LBS_DEB_HOST, "ret %d", ret);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001123 return ret;
1124}
1125
1126/**
1127 * @brief This function frees the command buffer.
1128 *
Holger Schurig69f90322007-11-23 15:43:44 +01001129 * @param priv A pointer to struct lbs_private structure
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001130 * @return 0 or -1
1131 */
Holger Schurig69f90322007-11-23 15:43:44 +01001132int lbs_free_cmd_buffer(struct lbs_private *priv)
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001133{
Dan Williamsddac4522007-12-11 13:49:39 -05001134 struct cmd_ctrl_node *cmdarray;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001135 unsigned int i;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001136
Holger Schurig8ff12da2007-08-02 11:54:31 -04001137 lbs_deb_enter(LBS_DEB_HOST);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001138
1139 /* need to check if cmd array is allocated or not */
David Woodhouseaa21c002007-12-08 20:04:36 +00001140 if (priv->cmd_array == NULL) {
Holger Schurig8ff12da2007-08-02 11:54:31 -04001141 lbs_deb_host("FREE_CMD_BUF: cmd_array is NULL\n");
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001142 goto done;
1143 }
1144
Dan Williamsddac4522007-12-11 13:49:39 -05001145 cmdarray = priv->cmd_array;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001146
1147 /* Release shared memory buffers */
Dan Williamsddac4522007-12-11 13:49:39 -05001148 for (i = 0; i < LBS_NUM_CMD_BUFFERS; i++) {
1149 if (cmdarray[i].cmdbuf) {
1150 kfree(cmdarray[i].cmdbuf);
1151 cmdarray[i].cmdbuf = NULL;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001152 }
1153 }
1154
1155 /* Release cmd_ctrl_node */
David Woodhouseaa21c002007-12-08 20:04:36 +00001156 if (priv->cmd_array) {
1157 kfree(priv->cmd_array);
1158 priv->cmd_array = NULL;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001159 }
1160
1161done:
Holger Schurig8ff12da2007-08-02 11:54:31 -04001162 lbs_deb_leave(LBS_DEB_HOST);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001163 return 0;
1164}
1165
1166/**
1167 * @brief This function gets a free command node if available in
1168 * command free queue.
1169 *
Holger Schurig69f90322007-11-23 15:43:44 +01001170 * @param priv A pointer to struct lbs_private structure
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001171 * @return cmd_ctrl_node A pointer to cmd_ctrl_node structure or NULL
1172 */
David Woodhouse2fd6cfe2007-12-11 17:44:10 -05001173static struct cmd_ctrl_node *lbs_get_cmd_ctrl_node(struct lbs_private *priv)
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001174{
1175 struct cmd_ctrl_node *tempnode;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001176 unsigned long flags;
1177
Holger Schurig8ff12da2007-08-02 11:54:31 -04001178 lbs_deb_enter(LBS_DEB_HOST);
1179
David Woodhouseaa21c002007-12-08 20:04:36 +00001180 if (!priv)
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001181 return NULL;
1182
David Woodhouseaa21c002007-12-08 20:04:36 +00001183 spin_lock_irqsave(&priv->driver_lock, flags);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001184
David Woodhouseaa21c002007-12-08 20:04:36 +00001185 if (!list_empty(&priv->cmdfreeq)) {
1186 tempnode = list_first_entry(&priv->cmdfreeq,
Li Zefanabe3ed12007-12-06 13:01:21 +01001187 struct cmd_ctrl_node, list);
1188 list_del(&tempnode->list);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001189 } else {
Holger Schurig8ff12da2007-08-02 11:54:31 -04001190 lbs_deb_host("GET_CMD_NODE: cmd_ctrl_node is not available\n");
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001191 tempnode = NULL;
1192 }
1193
David Woodhouseaa21c002007-12-08 20:04:36 +00001194 spin_unlock_irqrestore(&priv->driver_lock, flags);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001195
Holger Schurig8ff12da2007-08-02 11:54:31 -04001196 lbs_deb_leave(LBS_DEB_HOST);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001197 return tempnode;
1198}
1199
1200/**
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001201 * @brief This function executes next command in command
Nick Andrew877d0312009-01-26 11:06:57 +01001202 * pending queue. It will put firmware back to PS mode
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001203 * if applicable.
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 0 or -1
1207 */
Holger Schurig69f90322007-11-23 15:43:44 +01001208int lbs_execute_next_command(struct lbs_private *priv)
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001209{
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001210 struct cmd_ctrl_node *cmdnode = NULL;
Dan Williamsddac4522007-12-11 13:49:39 -05001211 struct cmd_header *cmd;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001212 unsigned long flags;
1213 int ret = 0;
1214
Holger Schurig1afc09ab2008-01-29 09:14:40 +01001215 /* Debug group is LBS_DEB_THREAD and not LBS_DEB_HOST, because the
1216 * only caller to us is lbs_thread() and we get even when a
1217 * data packet is received */
Holger Schurig8ff12da2007-08-02 11:54:31 -04001218 lbs_deb_enter(LBS_DEB_THREAD);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001219
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 (priv->cur_cmd) {
Holger Schurig8ff12da2007-08-02 11:54:31 -04001223 lbs_pr_alert( "EXEC_NEXT_CMD: already processing command!\n");
David Woodhouseaa21c002007-12-08 20:04:36 +00001224 spin_unlock_irqrestore(&priv->driver_lock, flags);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001225 ret = -1;
1226 goto done;
1227 }
1228
David Woodhouseaa21c002007-12-08 20:04:36 +00001229 if (!list_empty(&priv->cmdpendingq)) {
1230 cmdnode = list_first_entry(&priv->cmdpendingq,
Li Zefanabe3ed12007-12-06 13:01:21 +01001231 struct cmd_ctrl_node, list);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001232 }
1233
David Woodhouseaa21c002007-12-08 20:04:36 +00001234 spin_unlock_irqrestore(&priv->driver_lock, flags);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001235
1236 if (cmdnode) {
Dan Williamsddac4522007-12-11 13:49:39 -05001237 cmd = cmdnode->cmdbuf;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001238
Dan Williamsddac4522007-12-11 13:49:39 -05001239 if (is_command_allowed_in_ps(le16_to_cpu(cmd->command))) {
David Woodhouseaa21c002007-12-08 20:04:36 +00001240 if ((priv->psstate == PS_STATE_SLEEP) ||
1241 (priv->psstate == PS_STATE_PRE_SLEEP)) {
Holger Schurig8ff12da2007-08-02 11:54:31 -04001242 lbs_deb_host(
1243 "EXEC_NEXT_CMD: cannot send cmd 0x%04x in psstate %d\n",
Dan Williamsddac4522007-12-11 13:49:39 -05001244 le16_to_cpu(cmd->command),
David Woodhouseaa21c002007-12-08 20:04:36 +00001245 priv->psstate);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001246 ret = -1;
1247 goto done;
1248 }
Holger Schurig8ff12da2007-08-02 11:54:31 -04001249 lbs_deb_host("EXEC_NEXT_CMD: OK to send command "
Dan Williamsddac4522007-12-11 13:49:39 -05001250 "0x%04x in psstate %d\n",
1251 le16_to_cpu(cmd->command), priv->psstate);
David Woodhouseaa21c002007-12-08 20:04:36 +00001252 } else if (priv->psstate != PS_STATE_FULL_POWER) {
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001253 /*
1254 * 1. Non-PS command:
1255 * Queue it. set needtowakeup to TRUE if current state
Holger Schurig10078322007-11-15 18:05:47 -05001256 * is SLEEP, otherwise call lbs_ps_wakeup to send Exit_PS.
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001257 * 2. PS command but not Exit_PS:
1258 * Ignore it.
1259 * 3. PS command Exit_PS:
1260 * Set needtowakeup to TRUE if current state is SLEEP,
1261 * otherwise send this command down to firmware
1262 * immediately.
1263 */
Dan Williamsddac4522007-12-11 13:49:39 -05001264 if (cmd->command != cpu_to_le16(CMD_802_11_PS_MODE)) {
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001265 /* Prepare to send Exit PS,
1266 * this non PS command will be sent later */
David Woodhouseaa21c002007-12-08 20:04:36 +00001267 if ((priv->psstate == PS_STATE_SLEEP)
1268 || (priv->psstate == PS_STATE_PRE_SLEEP)
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001269 ) {
1270 /* w/ new scheme, it will not reach here.
1271 since it is blocked in main_thread. */
David Woodhouseaa21c002007-12-08 20:04:36 +00001272 priv->needtowakeup = 1;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001273 } else
Holger Schurig10078322007-11-15 18:05:47 -05001274 lbs_ps_wakeup(priv, 0);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001275
1276 ret = 0;
1277 goto done;
1278 } else {
1279 /*
1280 * PS command. Ignore it if it is not Exit_PS.
1281 * otherwise send it down immediately.
1282 */
David Woodhouse38bfab12007-12-16 23:26:54 -05001283 struct cmd_ds_802_11_ps_mode *psm = (void *)&cmd[1];
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001284
Holger Schurig8ff12da2007-08-02 11:54:31 -04001285 lbs_deb_host(
1286 "EXEC_NEXT_CMD: PS cmd, action 0x%02x\n",
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001287 psm->action);
1288 if (psm->action !=
Dan Williams0aef64d2007-08-02 11:31:18 -04001289 cpu_to_le16(CMD_SUBCMD_EXIT_PS)) {
Holger Schurig8ff12da2007-08-02 11:54:31 -04001290 lbs_deb_host(
1291 "EXEC_NEXT_CMD: ignore ENTER_PS cmd\n");
Li Zefanabe3ed12007-12-06 13:01:21 +01001292 list_del(&cmdnode->list);
David Woodhouse183aeac2007-12-15 01:52:54 -05001293 spin_lock_irqsave(&priv->driver_lock, flags);
1294 lbs_complete_command(priv, cmdnode, 0);
1295 spin_unlock_irqrestore(&priv->driver_lock, flags);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001296
1297 ret = 0;
1298 goto done;
1299 }
1300
David Woodhouseaa21c002007-12-08 20:04:36 +00001301 if ((priv->psstate == PS_STATE_SLEEP) ||
1302 (priv->psstate == PS_STATE_PRE_SLEEP)) {
Holger Schurig8ff12da2007-08-02 11:54:31 -04001303 lbs_deb_host(
1304 "EXEC_NEXT_CMD: ignore EXIT_PS cmd in sleep\n");
Li Zefanabe3ed12007-12-06 13:01:21 +01001305 list_del(&cmdnode->list);
David Woodhouse183aeac2007-12-15 01:52:54 -05001306 spin_lock_irqsave(&priv->driver_lock, flags);
1307 lbs_complete_command(priv, cmdnode, 0);
1308 spin_unlock_irqrestore(&priv->driver_lock, flags);
David Woodhouseaa21c002007-12-08 20:04:36 +00001309 priv->needtowakeup = 1;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001310
1311 ret = 0;
1312 goto done;
1313 }
1314
Holger Schurig8ff12da2007-08-02 11:54:31 -04001315 lbs_deb_host(
1316 "EXEC_NEXT_CMD: sending EXIT_PS\n");
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001317 }
1318 }
Li Zefanabe3ed12007-12-06 13:01:21 +01001319 list_del(&cmdnode->list);
Holger Schurig8ff12da2007-08-02 11:54:31 -04001320 lbs_deb_host("EXEC_NEXT_CMD: sending command 0x%04x\n",
Dan Williamsddac4522007-12-11 13:49:39 -05001321 le16_to_cpu(cmd->command));
David Woodhoused9896ee2007-12-15 00:09:25 -05001322 lbs_submit_command(priv, cmdnode);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001323 } else {
1324 /*
1325 * check if in power save mode, if yes, put the device back
1326 * to PS mode
1327 */
David Woodhouseaa21c002007-12-08 20:04:36 +00001328 if ((priv->psmode != LBS802_11POWERMODECAM) &&
1329 (priv->psstate == PS_STATE_FULL_POWER) &&
1330 ((priv->connect_status == LBS_CONNECTED) ||
Holger Schurig602114a2009-12-02 15:26:01 +01001331 lbs_mesh_connected(priv))) {
David Woodhouseaa21c002007-12-08 20:04:36 +00001332 if (priv->secinfo.WPAenabled ||
1333 priv->secinfo.WPA2enabled) {
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001334 /* check for valid WPA group keys */
David Woodhouseaa21c002007-12-08 20:04:36 +00001335 if (priv->wpa_mcast_key.len ||
1336 priv->wpa_unicast_key.len) {
Holger Schurig8ff12da2007-08-02 11:54:31 -04001337 lbs_deb_host(
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001338 "EXEC_NEXT_CMD: WPA enabled and GTK_SET"
1339 " go back to PS_SLEEP");
Holger Schurig10078322007-11-15 18:05:47 -05001340 lbs_ps_sleep(priv, 0);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001341 }
1342 } else {
Holger Schurig8ff12da2007-08-02 11:54:31 -04001343 lbs_deb_host(
1344 "EXEC_NEXT_CMD: cmdpendingq empty, "
1345 "go back to PS_SLEEP");
Holger Schurig10078322007-11-15 18:05:47 -05001346 lbs_ps_sleep(priv, 0);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001347 }
1348 }
1349 }
1350
1351 ret = 0;
1352done:
Holger Schurig8ff12da2007-08-02 11:54:31 -04001353 lbs_deb_leave(LBS_DEB_THREAD);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001354 return ret;
1355}
1356
Holger Schurigf539f2e2008-03-26 13:22:11 +01001357static void lbs_send_confirmsleep(struct lbs_private *priv)
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001358{
1359 unsigned long flags;
Holger Schurigf539f2e2008-03-26 13:22:11 +01001360 int ret;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001361
Holger Schurig8ff12da2007-08-02 11:54:31 -04001362 lbs_deb_enter(LBS_DEB_HOST);
Holger Schurigf539f2e2008-03-26 13:22:11 +01001363 lbs_deb_hex(LBS_DEB_HOST, "sleep confirm", (u8 *) &confirm_sleep,
1364 sizeof(confirm_sleep));
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001365
Holger Schurigf539f2e2008-03-26 13:22:11 +01001366 ret = priv->hw_host_to_card(priv, MVMS_CMD, (u8 *) &confirm_sleep,
1367 sizeof(confirm_sleep));
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001368 if (ret) {
Holger Schurigf539f2e2008-03-26 13:22:11 +01001369 lbs_pr_alert("confirm_sleep failed\n");
Holger Schurig7919b892008-04-01 14:50:43 +02001370 goto out;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001371 }
Holger Schurig7919b892008-04-01 14:50:43 +02001372
1373 spin_lock_irqsave(&priv->driver_lock, flags);
1374
Holger Schuriga01f5452008-06-04 11:10:40 +02001375 /* We don't get a response on the sleep-confirmation */
1376 priv->dnld_sent = DNLD_RES_RECEIVED;
1377
Amitkumar Karwar66fceb62010-05-19 03:24:38 -07001378 if (priv->is_host_sleep_configured) {
1379 priv->is_host_sleep_activated = 1;
1380 wake_up_interruptible(&priv->host_sleep_q);
1381 }
1382
Holger Schurig7919b892008-04-01 14:50:43 +02001383 /* If nothing to do, go back to sleep (?) */
Stefani Seibolde64c0262009-12-21 14:37:28 -08001384 if (!kfifo_len(&priv->event_fifo) && !priv->resp_len[priv->resp_idx])
Holger Schurig7919b892008-04-01 14:50:43 +02001385 priv->psstate = PS_STATE_SLEEP;
1386
1387 spin_unlock_irqrestore(&priv->driver_lock, flags);
1388
1389out:
Holger Schurigf539f2e2008-03-26 13:22:11 +01001390 lbs_deb_leave(LBS_DEB_HOST);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001391}
1392
Holger Schurig69f90322007-11-23 15:43:44 +01001393void lbs_ps_sleep(struct lbs_private *priv, int wait_option)
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001394{
Holger Schurig8ff12da2007-08-02 11:54:31 -04001395 lbs_deb_enter(LBS_DEB_HOST);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001396
1397 /*
1398 * PS is currently supported only in Infrastructure mode
1399 * Remove this check if it is to be supported in IBSS mode also
1400 */
1401
Holger Schurig10078322007-11-15 18:05:47 -05001402 lbs_prepare_and_send_command(priv, CMD_802_11_PS_MODE,
Dan Williams0aef64d2007-08-02 11:31:18 -04001403 CMD_SUBCMD_ENTER_PS, wait_option, 0, NULL);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001404
Holger Schurig8ff12da2007-08-02 11:54:31 -04001405 lbs_deb_leave(LBS_DEB_HOST);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001406}
1407
1408/**
Holger Schurig8ff12da2007-08-02 11:54:31 -04001409 * @brief This function sends Exit_PS command to firmware.
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001410 *
Holger Schurig69f90322007-11-23 15:43:44 +01001411 * @param priv A pointer to struct lbs_private structure
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001412 * @param wait_option wait response or not
1413 * @return n/a
1414 */
Holger Schurig69f90322007-11-23 15:43:44 +01001415void lbs_ps_wakeup(struct lbs_private *priv, int wait_option)
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001416{
David Woodhouse981f1872007-05-25 23:36:54 -04001417 __le32 Localpsmode;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001418
Holger Schurig8ff12da2007-08-02 11:54:31 -04001419 lbs_deb_enter(LBS_DEB_HOST);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001420
Holger Schurig10078322007-11-15 18:05:47 -05001421 Localpsmode = cpu_to_le32(LBS802_11POWERMODECAM);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001422
Holger Schurig10078322007-11-15 18:05:47 -05001423 lbs_prepare_and_send_command(priv, CMD_802_11_PS_MODE,
Dan Williams0aef64d2007-08-02 11:31:18 -04001424 CMD_SUBCMD_EXIT_PS,
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001425 wait_option, 0, &Localpsmode);
1426
Holger Schurig8ff12da2007-08-02 11:54:31 -04001427 lbs_deb_leave(LBS_DEB_HOST);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001428}
1429
1430/**
1431 * @brief This function checks condition and prepares to
1432 * send sleep confirm command to firmware if ok.
1433 *
Holger Schurig69f90322007-11-23 15:43:44 +01001434 * @param priv A pointer to struct lbs_private structure
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001435 * @param psmode Power Saving mode
1436 * @return n/a
1437 */
Holger Schurigd4ff0ef2008-03-19 14:25:18 +01001438void lbs_ps_confirm_sleep(struct lbs_private *priv)
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001439{
1440 unsigned long flags =0;
Holger Schurigd4ff0ef2008-03-19 14:25:18 +01001441 int allowed = 1;
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001442
Holger Schurig8ff12da2007-08-02 11:54:31 -04001443 lbs_deb_enter(LBS_DEB_HOST);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001444
Holger Schuriga01f5452008-06-04 11:10:40 +02001445 spin_lock_irqsave(&priv->driver_lock, flags);
Holger Schurig634b8f42007-05-25 13:05:16 -04001446 if (priv->dnld_sent) {
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001447 allowed = 0;
David Woodhouse23d36ee2007-12-12 00:14:21 -05001448 lbs_deb_host("dnld_sent was set\n");
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001449 }
1450
Holger Schurig7919b892008-04-01 14:50:43 +02001451 /* In-progress command? */
David Woodhouseaa21c002007-12-08 20:04:36 +00001452 if (priv->cur_cmd) {
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001453 allowed = 0;
David Woodhouse23d36ee2007-12-12 00:14:21 -05001454 lbs_deb_host("cur_cmd was set\n");
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001455 }
Holger Schurig7919b892008-04-01 14:50:43 +02001456
1457 /* Pending events or command responses? */
Stefani Seibolde64c0262009-12-21 14:37:28 -08001458 if (kfifo_len(&priv->event_fifo) || priv->resp_len[priv->resp_idx]) {
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001459 allowed = 0;
Holger Schurig7919b892008-04-01 14:50:43 +02001460 lbs_deb_host("pending events or command responses\n");
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001461 }
David Woodhouseaa21c002007-12-08 20:04:36 +00001462 spin_unlock_irqrestore(&priv->driver_lock, flags);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001463
1464 if (allowed) {
Holger Schurig10078322007-11-15 18:05:47 -05001465 lbs_deb_host("sending lbs_ps_confirm_sleep\n");
Holger Schurigf539f2e2008-03-26 13:22:11 +01001466 lbs_send_confirmsleep(priv);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001467 } else {
Holger Schurig8ff12da2007-08-02 11:54:31 -04001468 lbs_deb_host("sleep confirm has been delayed\n");
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001469 }
1470
Holger Schurig8ff12da2007-08-02 11:54:31 -04001471 lbs_deb_leave(LBS_DEB_HOST);
Marcelo Tosatti876c9d32007-02-10 12:25:27 -02001472}
Holger Schurig675787e2007-12-05 17:58:11 +01001473
1474
Anna Neal0112c9e2008-09-11 11:17:25 -07001475/**
1476 * @brief Configures the transmission power control functionality.
1477 *
1478 * @param priv A pointer to struct lbs_private structure
1479 * @param enable Transmission power control enable
1480 * @param p0 Power level when link quality is good (dBm).
1481 * @param p1 Power level when link quality is fair (dBm).
1482 * @param p2 Power level when link quality is poor (dBm).
1483 * @param usesnr Use Signal to Noise Ratio in TPC
1484 *
1485 * @return 0 on success
1486 */
1487int lbs_set_tpc_cfg(struct lbs_private *priv, int enable, int8_t p0, int8_t p1,
1488 int8_t p2, int usesnr)
1489{
1490 struct cmd_ds_802_11_tpc_cfg cmd;
1491 int ret;
1492
1493 memset(&cmd, 0, sizeof(cmd));
1494 cmd.hdr.size = cpu_to_le16(sizeof(cmd));
1495 cmd.action = cpu_to_le16(CMD_ACT_SET);
1496 cmd.enable = !!enable;
Anna Neal3ed6e082008-09-26 11:34:35 -04001497 cmd.usesnr = !!usesnr;
Anna Neal0112c9e2008-09-11 11:17:25 -07001498 cmd.P0 = p0;
1499 cmd.P1 = p1;
1500 cmd.P2 = p2;
1501
1502 ret = lbs_cmd_with_response(priv, CMD_802_11_TPC_CFG, &cmd);
1503
1504 return ret;
1505}
1506
1507/**
1508 * @brief Configures the power adaptation settings.
1509 *
1510 * @param priv A pointer to struct lbs_private structure
1511 * @param enable Power adaptation enable
1512 * @param p0 Power level for 1, 2, 5.5 and 11 Mbps (dBm).
1513 * @param p1 Power level for 6, 9, 12, 18, 22, 24 and 36 Mbps (dBm).
1514 * @param p2 Power level for 48 and 54 Mbps (dBm).
1515 *
1516 * @return 0 on Success
1517 */
1518
1519int lbs_set_power_adapt_cfg(struct lbs_private *priv, int enable, int8_t p0,
1520 int8_t p1, int8_t p2)
1521{
1522 struct cmd_ds_802_11_pa_cfg cmd;
1523 int ret;
1524
1525 memset(&cmd, 0, sizeof(cmd));
1526 cmd.hdr.size = cpu_to_le16(sizeof(cmd));
1527 cmd.action = cpu_to_le16(CMD_ACT_SET);
1528 cmd.enable = !!enable;
1529 cmd.P0 = p0;
1530 cmd.P1 = p1;
1531 cmd.P2 = p2;
1532
1533 ret = lbs_cmd_with_response(priv, CMD_802_11_PA_CFG , &cmd);
1534
1535 return ret;
1536}
1537
1538
Holger Schurig6d898b12009-10-14 16:49:53 +02001539struct cmd_ctrl_node *__lbs_cmd_async(struct lbs_private *priv,
Holger Schurig8db4a2b2008-03-19 10:11:00 +01001540 uint16_t command, struct cmd_header *in_cmd, int in_cmd_size,
1541 int (*callback)(struct lbs_private *, unsigned long, struct cmd_header *),
1542 unsigned long callback_arg)
Holger Schurig675787e2007-12-05 17:58:11 +01001543{
Holger Schurig675787e2007-12-05 17:58:11 +01001544 struct cmd_ctrl_node *cmdnode;
Holger Schurig675787e2007-12-05 17:58:11 +01001545
1546 lbs_deb_enter(LBS_DEB_HOST);
Holger Schurig675787e2007-12-05 17:58:11 +01001547
David Woodhouseaa21c002007-12-08 20:04:36 +00001548 if (priv->surpriseremoved) {
Holger Schurig675787e2007-12-05 17:58:11 +01001549 lbs_deb_host("PREP_CMD: card removed\n");
David Woodhouse3399ea52007-12-15 03:09:33 -05001550 cmdnode = ERR_PTR(-ENOENT);
Holger Schurig675787e2007-12-05 17:58:11 +01001551 goto done;
1552 }
1553
Amitkumar Karwar63f275d2009-10-06 19:20:28 -07001554 if (!lbs_is_cmd_allowed(priv)) {
1555 cmdnode = ERR_PTR(-EBUSY);
1556 goto done;
1557 }
1558
Holger Schurig675787e2007-12-05 17:58:11 +01001559 cmdnode = lbs_get_cmd_ctrl_node(priv);
Holger Schurig675787e2007-12-05 17:58:11 +01001560 if (cmdnode == NULL) {
1561 lbs_deb_host("PREP_CMD: cmdnode is NULL\n");
1562
1563 /* Wake up main thread to execute next command */
1564 wake_up_interruptible(&priv->waitq);
David Woodhouse3399ea52007-12-15 03:09:33 -05001565 cmdnode = ERR_PTR(-ENOBUFS);
Holger Schurig675787e2007-12-05 17:58:11 +01001566 goto done;
1567 }
1568
David Woodhouse448a51a2007-12-08 00:59:54 +00001569 cmdnode->callback = callback;
David Woodhouse1309b552007-12-10 13:36:10 -05001570 cmdnode->callback_arg = callback_arg;
Holger Schurig675787e2007-12-05 17:58:11 +01001571
Dan Williams7ad994d2007-12-11 12:33:30 -05001572 /* Copy the incoming command to the buffer */
Dan Williamsddac4522007-12-11 13:49:39 -05001573 memcpy(cmdnode->cmdbuf, in_cmd, in_cmd_size);
Dan Williams7ad994d2007-12-11 12:33:30 -05001574
Holger Schurig675787e2007-12-05 17:58:11 +01001575 /* Set sequence number, clean result, move to buffer */
David Woodhouseaa21c002007-12-08 20:04:36 +00001576 priv->seqnum++;
Dan Williamsddac4522007-12-11 13:49:39 -05001577 cmdnode->cmdbuf->command = cpu_to_le16(command);
1578 cmdnode->cmdbuf->size = cpu_to_le16(in_cmd_size);
1579 cmdnode->cmdbuf->seqnum = cpu_to_le16(priv->seqnum);
1580 cmdnode->cmdbuf->result = 0;
Holger Schurig675787e2007-12-05 17:58:11 +01001581
1582 lbs_deb_host("PREP_CMD: command 0x%04x\n", command);
1583
Holger Schurig675787e2007-12-05 17:58:11 +01001584 cmdnode->cmdwaitqwoken = 0;
David Woodhouse681ffbb2007-12-15 20:04:54 -05001585 lbs_queue_cmd(priv, cmdnode);
Holger Schurig675787e2007-12-05 17:58:11 +01001586 wake_up_interruptible(&priv->waitq);
1587
David Woodhouse3399ea52007-12-15 03:09:33 -05001588 done:
1589 lbs_deb_leave_args(LBS_DEB_HOST, "ret %p", cmdnode);
1590 return cmdnode;
1591}
1592
Holger Schurig8db4a2b2008-03-19 10:11:00 +01001593void lbs_cmd_async(struct lbs_private *priv, uint16_t command,
1594 struct cmd_header *in_cmd, int in_cmd_size)
1595{
1596 lbs_deb_enter(LBS_DEB_CMD);
1597 __lbs_cmd_async(priv, command, in_cmd, in_cmd_size,
1598 lbs_cmd_async_callback, 0);
1599 lbs_deb_leave(LBS_DEB_CMD);
1600}
1601
David Woodhouse3399ea52007-12-15 03:09:33 -05001602int __lbs_cmd(struct lbs_private *priv, uint16_t command,
1603 struct cmd_header *in_cmd, int in_cmd_size,
1604 int (*callback)(struct lbs_private *, unsigned long, struct cmd_header *),
1605 unsigned long callback_arg)
1606{
1607 struct cmd_ctrl_node *cmdnode;
1608 unsigned long flags;
1609 int ret = 0;
1610
1611 lbs_deb_enter(LBS_DEB_HOST);
1612
1613 cmdnode = __lbs_cmd_async(priv, command, in_cmd, in_cmd_size,
1614 callback, callback_arg);
1615 if (IS_ERR(cmdnode)) {
1616 ret = PTR_ERR(cmdnode);
1617 goto done;
1618 }
1619
Holger Schurig675787e2007-12-05 17:58:11 +01001620 might_sleep();
1621 wait_event_interruptible(cmdnode->cmdwait_q, cmdnode->cmdwaitqwoken);
1622
David Woodhouseaa21c002007-12-08 20:04:36 +00001623 spin_lock_irqsave(&priv->driver_lock, flags);
David Woodhouseae125bf2007-12-15 04:22:52 -05001624 ret = cmdnode->result;
1625 if (ret)
1626 lbs_pr_info("PREP_CMD: command 0x%04x failed: %d\n",
1627 command, ret);
David Woodhouse3399ea52007-12-15 03:09:33 -05001628
David Woodhousead12d0f2007-12-15 02:06:16 -05001629 __lbs_cleanup_and_insert_cmd(priv, cmdnode);
David Woodhouseaa21c002007-12-08 20:04:36 +00001630 spin_unlock_irqrestore(&priv->driver_lock, flags);
Holger Schurig675787e2007-12-05 17:58:11 +01001631
1632done:
1633 lbs_deb_leave_args(LBS_DEB_HOST, "ret %d", ret);
1634 return ret;
1635}
Dan Williams14e865b2007-12-10 15:11:23 -05001636EXPORT_SYMBOL_GPL(__lbs_cmd);