[ARM] pxa: Gumstix Verdex PCMCIA support
[linux-2.6/verdex.git] / drivers / net / wireless / libertas / cmdresp.c
blob23f684337fdd0f9db16b9a89a8c88a1dd1b6930d
1 /**
2 * This file contains the handling of command
3 * responses as well as events generated by firmware.
4 */
5 #include <linux/delay.h>
6 #include <linux/sched.h>
7 #include <linux/if_arp.h>
8 #include <linux/netdevice.h>
9 #include <asm/unaligned.h>
10 #include <net/iw_handler.h>
12 #include "host.h"
13 #include "decl.h"
14 #include "defs.h"
15 #include "dev.h"
16 #include "assoc.h"
17 #include "wext.h"
19 /**
20 * @brief This function handles disconnect event. it
21 * reports disconnect to upper layer, clean tx/rx packets,
22 * reset link state etc.
24 * @param priv A pointer to struct lbs_private structure
25 * @return n/a
27 void lbs_mac_event_disconnected(struct lbs_private *priv)
29 union iwreq_data wrqu;
31 if (priv->connect_status != LBS_CONNECTED)
32 return;
34 lbs_deb_enter(LBS_DEB_ASSOC);
36 memset(wrqu.ap_addr.sa_data, 0x00, ETH_ALEN);
37 wrqu.ap_addr.sa_family = ARPHRD_ETHER;
40 * Cisco AP sends EAP failure and de-auth in less than 0.5 ms.
41 * It causes problem in the Supplicant
44 msleep_interruptible(1000);
45 wireless_send_event(priv->dev, SIOCGIWAP, &wrqu, NULL);
47 /* report disconnect to upper layer */
48 netif_stop_queue(priv->dev);
49 netif_carrier_off(priv->dev);
51 /* Free Tx and Rx packets */
52 kfree_skb(priv->currenttxskb);
53 priv->currenttxskb = NULL;
54 priv->tx_pending_len = 0;
56 /* reset SNR/NF/RSSI values */
57 memset(priv->SNR, 0x00, sizeof(priv->SNR));
58 memset(priv->NF, 0x00, sizeof(priv->NF));
59 memset(priv->RSSI, 0x00, sizeof(priv->RSSI));
60 memset(priv->rawSNR, 0x00, sizeof(priv->rawSNR));
61 memset(priv->rawNF, 0x00, sizeof(priv->rawNF));
62 priv->nextSNRNF = 0;
63 priv->numSNRNF = 0;
64 priv->connect_status = LBS_DISCONNECTED;
66 /* Clear out associated SSID and BSSID since connection is
67 * no longer valid.
69 memset(&priv->curbssparams.bssid, 0, ETH_ALEN);
70 memset(&priv->curbssparams.ssid, 0, IW_ESSID_MAX_SIZE);
71 priv->curbssparams.ssid_len = 0;
73 if (priv->psstate != PS_STATE_FULL_POWER) {
74 /* make firmware to exit PS mode */
75 lbs_deb_cmd("disconnected, so exit PS mode\n");
76 lbs_ps_wakeup(priv, 0);
78 lbs_deb_leave(LBS_DEB_ASSOC);
81 /**
82 * @brief This function handles MIC failure event.
84 * @param priv A pointer to struct lbs_private structure
85 * @para event the event id
86 * @return n/a
88 static void handle_mic_failureevent(struct lbs_private *priv, u32 event)
90 char buf[50];
92 lbs_deb_enter(LBS_DEB_CMD);
93 memset(buf, 0, sizeof(buf));
95 sprintf(buf, "%s", "MLME-MICHAELMICFAILURE.indication ");
97 if (event == MACREG_INT_CODE_MIC_ERR_UNICAST) {
98 strcat(buf, "unicast ");
99 } else {
100 strcat(buf, "multicast ");
103 lbs_send_iwevcustom_event(priv, buf);
104 lbs_deb_leave(LBS_DEB_CMD);
107 static int lbs_ret_reg_access(struct lbs_private *priv,
108 u16 type, struct cmd_ds_command *resp)
110 int ret = 0;
112 lbs_deb_enter(LBS_DEB_CMD);
114 switch (type) {
115 case CMD_RET(CMD_MAC_REG_ACCESS):
117 struct cmd_ds_mac_reg_access *reg = &resp->params.macreg;
119 priv->offsetvalue.offset = (u32)le16_to_cpu(reg->offset);
120 priv->offsetvalue.value = le32_to_cpu(reg->value);
121 break;
124 case CMD_RET(CMD_BBP_REG_ACCESS):
126 struct cmd_ds_bbp_reg_access *reg = &resp->params.bbpreg;
128 priv->offsetvalue.offset = (u32)le16_to_cpu(reg->offset);
129 priv->offsetvalue.value = reg->value;
130 break;
133 case CMD_RET(CMD_RF_REG_ACCESS):
135 struct cmd_ds_rf_reg_access *reg = &resp->params.rfreg;
137 priv->offsetvalue.offset = (u32)le16_to_cpu(reg->offset);
138 priv->offsetvalue.value = reg->value;
139 break;
142 default:
143 ret = -1;
146 lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret);
147 return ret;
150 static int lbs_ret_802_11_rssi(struct lbs_private *priv,
151 struct cmd_ds_command *resp)
153 struct cmd_ds_802_11_rssi_rsp *rssirsp = &resp->params.rssirsp;
155 lbs_deb_enter(LBS_DEB_CMD);
157 /* store the non average value */
158 priv->SNR[TYPE_BEACON][TYPE_NOAVG] = get_unaligned_le16(&rssirsp->SNR);
159 priv->NF[TYPE_BEACON][TYPE_NOAVG] = get_unaligned_le16(&rssirsp->noisefloor);
161 priv->SNR[TYPE_BEACON][TYPE_AVG] = get_unaligned_le16(&rssirsp->avgSNR);
162 priv->NF[TYPE_BEACON][TYPE_AVG] = get_unaligned_le16(&rssirsp->avgnoisefloor);
164 priv->RSSI[TYPE_BEACON][TYPE_NOAVG] =
165 CAL_RSSI(priv->SNR[TYPE_BEACON][TYPE_NOAVG],
166 priv->NF[TYPE_BEACON][TYPE_NOAVG]);
168 priv->RSSI[TYPE_BEACON][TYPE_AVG] =
169 CAL_RSSI(priv->SNR[TYPE_BEACON][TYPE_AVG] / AVG_SCALE,
170 priv->NF[TYPE_BEACON][TYPE_AVG] / AVG_SCALE);
172 lbs_deb_cmd("RSSI: beacon %d, avg %d\n",
173 priv->RSSI[TYPE_BEACON][TYPE_NOAVG],
174 priv->RSSI[TYPE_BEACON][TYPE_AVG]);
176 lbs_deb_leave(LBS_DEB_CMD);
177 return 0;
180 static int lbs_ret_802_11_bcn_ctrl(struct lbs_private * priv,
181 struct cmd_ds_command *resp)
183 struct cmd_ds_802_11_beacon_control *bcn_ctrl =
184 &resp->params.bcn_ctrl;
186 lbs_deb_enter(LBS_DEB_CMD);
188 if (bcn_ctrl->action == CMD_ACT_GET) {
189 priv->beacon_enable = (u8) le16_to_cpu(bcn_ctrl->beacon_enable);
190 priv->beacon_period = le16_to_cpu(bcn_ctrl->beacon_period);
193 lbs_deb_enter(LBS_DEB_CMD);
194 return 0;
197 static inline int handle_cmd_response(struct lbs_private *priv,
198 struct cmd_header *cmd_response)
200 struct cmd_ds_command *resp = (struct cmd_ds_command *) cmd_response;
201 int ret = 0;
202 unsigned long flags;
203 uint16_t respcmd = le16_to_cpu(resp->command);
205 lbs_deb_enter(LBS_DEB_HOST);
207 switch (respcmd) {
208 case CMD_RET(CMD_MAC_REG_ACCESS):
209 case CMD_RET(CMD_BBP_REG_ACCESS):
210 case CMD_RET(CMD_RF_REG_ACCESS):
211 ret = lbs_ret_reg_access(priv, respcmd, resp);
212 break;
214 case CMD_RET(CMD_802_11_SET_AFC):
215 case CMD_RET(CMD_802_11_GET_AFC):
216 spin_lock_irqsave(&priv->driver_lock, flags);
217 memmove((void *)priv->cur_cmd->callback_arg, &resp->params.afc,
218 sizeof(struct cmd_ds_802_11_afc));
219 spin_unlock_irqrestore(&priv->driver_lock, flags);
221 break;
223 case CMD_RET(CMD_802_11_BEACON_STOP):
224 break;
226 case CMD_RET(CMD_802_11_RSSI):
227 ret = lbs_ret_802_11_rssi(priv, resp);
228 break;
230 case CMD_RET(CMD_802_11D_DOMAIN_INFO):
231 ret = lbs_ret_802_11d_domain_info(resp);
232 break;
234 case CMD_RET(CMD_802_11_TPC_CFG):
235 spin_lock_irqsave(&priv->driver_lock, flags);
236 memmove((void *)priv->cur_cmd->callback_arg, &resp->params.tpccfg,
237 sizeof(struct cmd_ds_802_11_tpc_cfg));
238 spin_unlock_irqrestore(&priv->driver_lock, flags);
239 break;
240 case CMD_RET(CMD_802_11_LED_GPIO_CTRL):
241 spin_lock_irqsave(&priv->driver_lock, flags);
242 memmove((void *)priv->cur_cmd->callback_arg, &resp->params.ledgpio,
243 sizeof(struct cmd_ds_802_11_led_ctrl));
244 spin_unlock_irqrestore(&priv->driver_lock, flags);
245 break;
247 case CMD_RET(CMD_GET_TSF):
248 spin_lock_irqsave(&priv->driver_lock, flags);
249 memcpy((void *)priv->cur_cmd->callback_arg,
250 &resp->params.gettsf.tsfvalue, sizeof(u64));
251 spin_unlock_irqrestore(&priv->driver_lock, flags);
252 break;
253 case CMD_RET(CMD_BT_ACCESS):
254 spin_lock_irqsave(&priv->driver_lock, flags);
255 if (priv->cur_cmd->callback_arg)
256 memcpy((void *)priv->cur_cmd->callback_arg,
257 &resp->params.bt.addr1, 2 * ETH_ALEN);
258 spin_unlock_irqrestore(&priv->driver_lock, flags);
259 break;
260 case CMD_RET(CMD_FWT_ACCESS):
261 spin_lock_irqsave(&priv->driver_lock, flags);
262 if (priv->cur_cmd->callback_arg)
263 memcpy((void *)priv->cur_cmd->callback_arg, &resp->params.fwt,
264 sizeof(resp->params.fwt));
265 spin_unlock_irqrestore(&priv->driver_lock, flags);
266 break;
267 case CMD_RET(CMD_802_11_BEACON_CTRL):
268 ret = lbs_ret_802_11_bcn_ctrl(priv, resp);
269 break;
271 default:
272 lbs_pr_err("CMD_RESP: unknown cmd response 0x%04x\n",
273 le16_to_cpu(resp->command));
274 break;
276 lbs_deb_leave(LBS_DEB_HOST);
277 return ret;
280 int lbs_process_command_response(struct lbs_private *priv, u8 *data, u32 len)
282 uint16_t respcmd, curcmd;
283 struct cmd_header *resp;
284 int ret = 0;
285 unsigned long flags;
286 uint16_t result;
288 lbs_deb_enter(LBS_DEB_HOST);
290 mutex_lock(&priv->lock);
291 spin_lock_irqsave(&priv->driver_lock, flags);
293 if (!priv->cur_cmd) {
294 lbs_deb_host("CMD_RESP: cur_cmd is NULL\n");
295 ret = -1;
296 spin_unlock_irqrestore(&priv->driver_lock, flags);
297 goto done;
300 resp = (void *)data;
301 curcmd = le16_to_cpu(priv->cur_cmd->cmdbuf->command);
302 respcmd = le16_to_cpu(resp->command);
303 result = le16_to_cpu(resp->result);
305 lbs_deb_cmd("CMD_RESP: response 0x%04x, seq %d, size %d\n",
306 respcmd, le16_to_cpu(resp->seqnum), len);
307 lbs_deb_hex(LBS_DEB_CMD, "CMD_RESP", (void *) resp, len);
309 if (resp->seqnum != priv->cur_cmd->cmdbuf->seqnum) {
310 lbs_pr_info("Received CMD_RESP with invalid sequence %d (expected %d)\n",
311 le16_to_cpu(resp->seqnum), le16_to_cpu(priv->cur_cmd->cmdbuf->seqnum));
312 spin_unlock_irqrestore(&priv->driver_lock, flags);
313 ret = -1;
314 goto done;
316 if (respcmd != CMD_RET(curcmd) &&
317 respcmd != CMD_RET_802_11_ASSOCIATE && curcmd != CMD_802_11_ASSOCIATE) {
318 lbs_pr_info("Invalid CMD_RESP %x to command %x!\n", respcmd, curcmd);
319 spin_unlock_irqrestore(&priv->driver_lock, flags);
320 ret = -1;
321 goto done;
324 if (resp->result == cpu_to_le16(0x0004)) {
325 /* 0x0004 means -EAGAIN. Drop the response, let it time out
326 and be resubmitted */
327 lbs_pr_info("Firmware returns DEFER to command %x. Will let it time out...\n",
328 le16_to_cpu(resp->command));
329 spin_unlock_irqrestore(&priv->driver_lock, flags);
330 ret = -1;
331 goto done;
334 /* Now we got response from FW, cancel the command timer */
335 del_timer(&priv->command_timer);
336 priv->cmd_timed_out = 0;
337 if (priv->nr_retries) {
338 lbs_pr_info("Received result %x to command %x after %d retries\n",
339 result, curcmd, priv->nr_retries);
340 priv->nr_retries = 0;
343 /* Store the response code to cur_cmd_retcode. */
344 priv->cur_cmd_retcode = result;
346 if (respcmd == CMD_RET(CMD_802_11_PS_MODE)) {
347 struct cmd_ds_802_11_ps_mode *psmode = (void *) &resp[1];
348 u16 action = le16_to_cpu(psmode->action);
350 lbs_deb_host(
351 "CMD_RESP: PS_MODE cmd reply result 0x%x, action 0x%x\n",
352 result, action);
354 if (result) {
355 lbs_deb_host("CMD_RESP: PS command failed with 0x%x\n",
356 result);
358 * We should not re-try enter-ps command in
359 * ad-hoc mode. It takes place in
360 * lbs_execute_next_command().
362 if (priv->mode == IW_MODE_ADHOC &&
363 action == CMD_SUBCMD_ENTER_PS)
364 priv->psmode = LBS802_11POWERMODECAM;
365 } else if (action == CMD_SUBCMD_ENTER_PS) {
366 priv->needtowakeup = 0;
367 priv->psstate = PS_STATE_AWAKE;
369 lbs_deb_host("CMD_RESP: ENTER_PS command response\n");
370 if (priv->connect_status != LBS_CONNECTED) {
372 * When Deauth Event received before Enter_PS command
373 * response, We need to wake up the firmware.
375 lbs_deb_host(
376 "disconnected, invoking lbs_ps_wakeup\n");
378 spin_unlock_irqrestore(&priv->driver_lock, flags);
379 mutex_unlock(&priv->lock);
380 lbs_ps_wakeup(priv, 0);
381 mutex_lock(&priv->lock);
382 spin_lock_irqsave(&priv->driver_lock, flags);
384 } else if (action == CMD_SUBCMD_EXIT_PS) {
385 priv->needtowakeup = 0;
386 priv->psstate = PS_STATE_FULL_POWER;
387 lbs_deb_host("CMD_RESP: EXIT_PS command response\n");
388 } else {
389 lbs_deb_host("CMD_RESP: PS action 0x%X\n", action);
392 lbs_complete_command(priv, priv->cur_cmd, result);
393 spin_unlock_irqrestore(&priv->driver_lock, flags);
395 ret = 0;
396 goto done;
399 /* If the command is not successful, cleanup and return failure */
400 if ((result != 0 || !(respcmd & 0x8000))) {
401 lbs_deb_host("CMD_RESP: error 0x%04x in command reply 0x%04x\n",
402 result, respcmd);
404 * Handling errors here
406 switch (respcmd) {
407 case CMD_RET(CMD_GET_HW_SPEC):
408 case CMD_RET(CMD_802_11_RESET):
409 lbs_deb_host("CMD_RESP: reset failed\n");
410 break;
413 lbs_complete_command(priv, priv->cur_cmd, result);
414 spin_unlock_irqrestore(&priv->driver_lock, flags);
416 ret = -1;
417 goto done;
420 spin_unlock_irqrestore(&priv->driver_lock, flags);
422 if (priv->cur_cmd && priv->cur_cmd->callback) {
423 ret = priv->cur_cmd->callback(priv, priv->cur_cmd->callback_arg,
424 resp);
425 } else
426 ret = handle_cmd_response(priv, resp);
428 spin_lock_irqsave(&priv->driver_lock, flags);
430 if (priv->cur_cmd) {
431 /* Clean up and Put current command back to cmdfreeq */
432 lbs_complete_command(priv, priv->cur_cmd, result);
434 spin_unlock_irqrestore(&priv->driver_lock, flags);
436 done:
437 mutex_unlock(&priv->lock);
438 lbs_deb_leave_args(LBS_DEB_HOST, "ret %d", ret);
439 return ret;
442 static int lbs_send_confirmwake(struct lbs_private *priv)
444 struct cmd_header cmd;
445 int ret = 0;
447 lbs_deb_enter(LBS_DEB_HOST);
449 cmd.command = cpu_to_le16(CMD_802_11_WAKEUP_CONFIRM);
450 cmd.size = cpu_to_le16(sizeof(cmd));
451 cmd.seqnum = cpu_to_le16(++priv->seqnum);
452 cmd.result = 0;
454 lbs_deb_hex(LBS_DEB_HOST, "wake confirm", (u8 *) &cmd,
455 sizeof(cmd));
457 ret = priv->hw_host_to_card(priv, MVMS_CMD, (u8 *) &cmd, sizeof(cmd));
458 if (ret)
459 lbs_pr_alert("SEND_WAKEC_CMD: Host to Card failed for Confirm Wake\n");
461 lbs_deb_leave_args(LBS_DEB_HOST, "ret %d", ret);
462 return ret;
465 int lbs_process_event(struct lbs_private *priv, u32 event)
467 int ret = 0;
469 lbs_deb_enter(LBS_DEB_CMD);
471 switch (event) {
472 case MACREG_INT_CODE_LINK_SENSED:
473 lbs_deb_cmd("EVENT: link sensed\n");
474 break;
476 case MACREG_INT_CODE_DEAUTHENTICATED:
477 lbs_deb_cmd("EVENT: deauthenticated\n");
478 lbs_mac_event_disconnected(priv);
479 break;
481 case MACREG_INT_CODE_DISASSOCIATED:
482 lbs_deb_cmd("EVENT: disassociated\n");
483 lbs_mac_event_disconnected(priv);
484 break;
486 case MACREG_INT_CODE_LINK_LOST_NO_SCAN:
487 lbs_deb_cmd("EVENT: link lost\n");
488 lbs_mac_event_disconnected(priv);
489 break;
491 case MACREG_INT_CODE_PS_SLEEP:
492 lbs_deb_cmd("EVENT: ps sleep\n");
494 /* handle unexpected PS SLEEP event */
495 if (priv->psstate == PS_STATE_FULL_POWER) {
496 lbs_deb_cmd(
497 "EVENT: in FULL POWER mode, ignoreing PS_SLEEP\n");
498 break;
500 priv->psstate = PS_STATE_PRE_SLEEP;
502 lbs_ps_confirm_sleep(priv);
504 break;
506 case MACREG_INT_CODE_HOST_AWAKE:
507 lbs_deb_cmd("EVENT: host awake\n");
508 lbs_send_confirmwake(priv);
509 break;
511 case MACREG_INT_CODE_PS_AWAKE:
512 lbs_deb_cmd("EVENT: ps awake\n");
513 /* handle unexpected PS AWAKE event */
514 if (priv->psstate == PS_STATE_FULL_POWER) {
515 lbs_deb_cmd(
516 "EVENT: In FULL POWER mode - ignore PS AWAKE\n");
517 break;
520 priv->psstate = PS_STATE_AWAKE;
522 if (priv->needtowakeup) {
524 * wait for the command processing to finish
525 * before resuming sending
526 * priv->needtowakeup will be set to FALSE
527 * in lbs_ps_wakeup()
529 lbs_deb_cmd("waking up ...\n");
530 lbs_ps_wakeup(priv, 0);
532 break;
534 case MACREG_INT_CODE_MIC_ERR_UNICAST:
535 lbs_deb_cmd("EVENT: UNICAST MIC ERROR\n");
536 handle_mic_failureevent(priv, MACREG_INT_CODE_MIC_ERR_UNICAST);
537 break;
539 case MACREG_INT_CODE_MIC_ERR_MULTICAST:
540 lbs_deb_cmd("EVENT: MULTICAST MIC ERROR\n");
541 handle_mic_failureevent(priv, MACREG_INT_CODE_MIC_ERR_MULTICAST);
542 break;
544 case MACREG_INT_CODE_MIB_CHANGED:
545 lbs_deb_cmd("EVENT: MIB CHANGED\n");
546 break;
547 case MACREG_INT_CODE_INIT_DONE:
548 lbs_deb_cmd("EVENT: INIT DONE\n");
549 break;
550 case MACREG_INT_CODE_ADHOC_BCN_LOST:
551 lbs_deb_cmd("EVENT: ADHOC beacon lost\n");
552 break;
553 case MACREG_INT_CODE_RSSI_LOW:
554 lbs_pr_alert("EVENT: rssi low\n");
555 break;
556 case MACREG_INT_CODE_SNR_LOW:
557 lbs_pr_alert("EVENT: snr low\n");
558 break;
559 case MACREG_INT_CODE_MAX_FAIL:
560 lbs_pr_alert("EVENT: max fail\n");
561 break;
562 case MACREG_INT_CODE_RSSI_HIGH:
563 lbs_pr_alert("EVENT: rssi high\n");
564 break;
565 case MACREG_INT_CODE_SNR_HIGH:
566 lbs_pr_alert("EVENT: snr high\n");
567 break;
569 case MACREG_INT_CODE_MESH_AUTO_STARTED:
570 /* Ignore spurious autostart events if autostart is disabled */
571 if (!priv->mesh_autostart_enabled) {
572 lbs_pr_info("EVENT: MESH_AUTO_STARTED (ignoring)\n");
573 break;
575 lbs_pr_info("EVENT: MESH_AUTO_STARTED\n");
576 priv->mesh_connect_status = LBS_CONNECTED;
577 if (priv->mesh_open) {
578 netif_carrier_on(priv->mesh_dev);
579 if (!priv->tx_pending_len)
580 netif_wake_queue(priv->mesh_dev);
582 priv->mode = IW_MODE_ADHOC;
583 schedule_work(&priv->sync_channel);
584 break;
586 default:
587 lbs_pr_alert("EVENT: unknown event id %d\n", event);
588 break;
591 lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret);
592 return ret;