0334a58820eed220d2000fc25fcf64ba7e0deeb8
[safe/jmp/linux-2.6] / drivers / net / wireless / libertas / cmdresp.c
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>
11
12 #include "host.h"
13 #include "decl.h"
14 #include "cmd.h"
15 #include "defs.h"
16 #include "dev.h"
17 #include "assoc.h"
18 #include "wext.h"
19
20 /**
21  *  @brief This function handles disconnect event. it
22  *  reports disconnect to upper layer, clean tx/rx packets,
23  *  reset link state etc.
24  *
25  *  @param priv    A pointer to struct lbs_private structure
26  *  @return        n/a
27  */
28 void lbs_mac_event_disconnected(struct lbs_private *priv)
29 {
30         if (priv->connect_status != LBS_CONNECTED)
31                 return;
32
33         lbs_deb_enter(LBS_DEB_ASSOC);
34
35         /*
36          * Cisco AP sends EAP failure and de-auth in less than 0.5 ms.
37          * It causes problem in the Supplicant
38          */
39         msleep_interruptible(1000);
40         lbs_send_disconnect_notification(priv);
41
42         /* report disconnect to upper layer */
43         netif_stop_queue(priv->dev);
44         netif_carrier_off(priv->dev);
45
46         /* Free Tx and Rx packets */
47         kfree_skb(priv->currenttxskb);
48         priv->currenttxskb = NULL;
49         priv->tx_pending_len = 0;
50
51         /* reset SNR/NF/RSSI values */
52         memset(priv->SNR, 0x00, sizeof(priv->SNR));
53         memset(priv->NF, 0x00, sizeof(priv->NF));
54         memset(priv->RSSI, 0x00, sizeof(priv->RSSI));
55         memset(priv->rawSNR, 0x00, sizeof(priv->rawSNR));
56         memset(priv->rawNF, 0x00, sizeof(priv->rawNF));
57         priv->nextSNRNF = 0;
58         priv->numSNRNF = 0;
59         priv->connect_status = LBS_DISCONNECTED;
60
61         /* Clear out associated SSID and BSSID since connection is
62          * no longer valid.
63          */
64         memset(&priv->curbssparams.bssid, 0, ETH_ALEN);
65         memset(&priv->curbssparams.ssid, 0, IEEE80211_MAX_SSID_LEN);
66         priv->curbssparams.ssid_len = 0;
67
68         if (priv->psstate != PS_STATE_FULL_POWER) {
69                 /* make firmware to exit PS mode */
70                 lbs_deb_cmd("disconnected, so exit PS mode\n");
71                 lbs_ps_wakeup(priv, 0);
72         }
73         lbs_deb_leave(LBS_DEB_ASSOC);
74 }
75
76 static int lbs_ret_reg_access(struct lbs_private *priv,
77                                u16 type, struct cmd_ds_command *resp)
78 {
79         int ret = 0;
80
81         lbs_deb_enter(LBS_DEB_CMD);
82
83         switch (type) {
84         case CMD_RET(CMD_MAC_REG_ACCESS):
85                 {
86                         struct cmd_ds_mac_reg_access *reg = &resp->params.macreg;
87
88                         priv->offsetvalue.offset = (u32)le16_to_cpu(reg->offset);
89                         priv->offsetvalue.value = le32_to_cpu(reg->value);
90                         break;
91                 }
92
93         case CMD_RET(CMD_BBP_REG_ACCESS):
94                 {
95                         struct cmd_ds_bbp_reg_access *reg = &resp->params.bbpreg;
96
97                         priv->offsetvalue.offset = (u32)le16_to_cpu(reg->offset);
98                         priv->offsetvalue.value = reg->value;
99                         break;
100                 }
101
102         case CMD_RET(CMD_RF_REG_ACCESS):
103                 {
104                         struct cmd_ds_rf_reg_access *reg = &resp->params.rfreg;
105
106                         priv->offsetvalue.offset = (u32)le16_to_cpu(reg->offset);
107                         priv->offsetvalue.value = reg->value;
108                         break;
109                 }
110
111         default:
112                 ret = -1;
113         }
114
115         lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret);
116         return ret;
117 }
118
119 static inline int handle_cmd_response(struct lbs_private *priv,
120                                       struct cmd_header *cmd_response)
121 {
122         struct cmd_ds_command *resp = (struct cmd_ds_command *) cmd_response;
123         int ret = 0;
124         unsigned long flags;
125         uint16_t respcmd = le16_to_cpu(resp->command);
126
127         lbs_deb_enter(LBS_DEB_HOST);
128
129         switch (respcmd) {
130         case CMD_RET(CMD_MAC_REG_ACCESS):
131         case CMD_RET(CMD_BBP_REG_ACCESS):
132         case CMD_RET(CMD_RF_REG_ACCESS):
133                 ret = lbs_ret_reg_access(priv, respcmd, resp);
134                 break;
135
136         case CMD_RET(CMD_802_11_SET_AFC):
137         case CMD_RET(CMD_802_11_GET_AFC):
138                 spin_lock_irqsave(&priv->driver_lock, flags);
139                 memmove((void *)priv->cur_cmd->callback_arg, &resp->params.afc,
140                         sizeof(struct cmd_ds_802_11_afc));
141                 spin_unlock_irqrestore(&priv->driver_lock, flags);
142
143                 break;
144
145         case CMD_RET(CMD_802_11_BEACON_STOP):
146                 break;
147
148         case CMD_RET(CMD_802_11_RSSI):
149                 ret = lbs_ret_802_11_rssi(priv, resp);
150                 break;
151
152         case CMD_RET(CMD_802_11_TPC_CFG):
153                 spin_lock_irqsave(&priv->driver_lock, flags);
154                 memmove((void *)priv->cur_cmd->callback_arg, &resp->params.tpccfg,
155                         sizeof(struct cmd_ds_802_11_tpc_cfg));
156                 spin_unlock_irqrestore(&priv->driver_lock, flags);
157                 break;
158
159         case CMD_RET(CMD_BT_ACCESS):
160                 spin_lock_irqsave(&priv->driver_lock, flags);
161                 if (priv->cur_cmd->callback_arg)
162                         memcpy((void *)priv->cur_cmd->callback_arg,
163                                &resp->params.bt.addr1, 2 * ETH_ALEN);
164                 spin_unlock_irqrestore(&priv->driver_lock, flags);
165                 break;
166         case CMD_RET(CMD_FWT_ACCESS):
167                 spin_lock_irqsave(&priv->driver_lock, flags);
168                 if (priv->cur_cmd->callback_arg)
169                         memcpy((void *)priv->cur_cmd->callback_arg, &resp->params.fwt,
170                                sizeof(resp->params.fwt));
171                 spin_unlock_irqrestore(&priv->driver_lock, flags);
172                 break;
173         case CMD_RET(CMD_802_11_BEACON_CTRL):
174                 ret = lbs_ret_802_11_bcn_ctrl(priv, resp);
175                 break;
176
177         default:
178                 lbs_pr_err("CMD_RESP: unknown cmd response 0x%04x\n",
179                            le16_to_cpu(resp->command));
180                 break;
181         }
182         lbs_deb_leave(LBS_DEB_HOST);
183         return ret;
184 }
185
186 int lbs_process_command_response(struct lbs_private *priv, u8 *data, u32 len)
187 {
188         uint16_t respcmd, curcmd;
189         struct cmd_header *resp;
190         int ret = 0;
191         unsigned long flags;
192         uint16_t result;
193
194         lbs_deb_enter(LBS_DEB_HOST);
195
196         mutex_lock(&priv->lock);
197         spin_lock_irqsave(&priv->driver_lock, flags);
198
199         if (!priv->cur_cmd) {
200                 lbs_deb_host("CMD_RESP: cur_cmd is NULL\n");
201                 ret = -1;
202                 spin_unlock_irqrestore(&priv->driver_lock, flags);
203                 goto done;
204         }
205
206         resp = (void *)data;
207         curcmd = le16_to_cpu(priv->cur_cmd->cmdbuf->command);
208         respcmd = le16_to_cpu(resp->command);
209         result = le16_to_cpu(resp->result);
210
211         lbs_deb_cmd("CMD_RESP: response 0x%04x, seq %d, size %d\n",
212                      respcmd, le16_to_cpu(resp->seqnum), len);
213         lbs_deb_hex(LBS_DEB_CMD, "CMD_RESP", (void *) resp, len);
214
215         if (resp->seqnum != priv->cur_cmd->cmdbuf->seqnum) {
216                 lbs_pr_info("Received CMD_RESP with invalid sequence %d (expected %d)\n",
217                             le16_to_cpu(resp->seqnum), le16_to_cpu(priv->cur_cmd->cmdbuf->seqnum));
218                 spin_unlock_irqrestore(&priv->driver_lock, flags);
219                 ret = -1;
220                 goto done;
221         }
222         if (respcmd != CMD_RET(curcmd) &&
223             respcmd != CMD_RET_802_11_ASSOCIATE && curcmd != CMD_802_11_ASSOCIATE) {
224                 lbs_pr_info("Invalid CMD_RESP %x to command %x!\n", respcmd, curcmd);
225                 spin_unlock_irqrestore(&priv->driver_lock, flags);
226                 ret = -1;
227                 goto done;
228         }
229
230         if (resp->result == cpu_to_le16(0x0004)) {
231                 /* 0x0004 means -EAGAIN. Drop the response, let it time out
232                    and be resubmitted */
233                 lbs_pr_info("Firmware returns DEFER to command %x. Will let it time out...\n",
234                             le16_to_cpu(resp->command));
235                 spin_unlock_irqrestore(&priv->driver_lock, flags);
236                 ret = -1;
237                 goto done;
238         }
239
240         /* Now we got response from FW, cancel the command timer */
241         del_timer(&priv->command_timer);
242         priv->cmd_timed_out = 0;
243         if (priv->nr_retries) {
244                 lbs_pr_info("Received result %x to command %x after %d retries\n",
245                             result, curcmd, priv->nr_retries);
246                 priv->nr_retries = 0;
247         }
248
249         /* Store the response code to cur_cmd_retcode. */
250         priv->cur_cmd_retcode = result;
251
252         if (respcmd == CMD_RET(CMD_802_11_PS_MODE)) {
253                 struct cmd_ds_802_11_ps_mode *psmode = (void *) &resp[1];
254                 u16 action = le16_to_cpu(psmode->action);
255
256                 lbs_deb_host(
257                        "CMD_RESP: PS_MODE cmd reply result 0x%x, action 0x%x\n",
258                        result, action);
259
260                 if (result) {
261                         lbs_deb_host("CMD_RESP: PS command failed with 0x%x\n",
262                                     result);
263                         /*
264                          * We should not re-try enter-ps command in
265                          * ad-hoc mode. It takes place in
266                          * lbs_execute_next_command().
267                          */
268                         if (priv->mode == IW_MODE_ADHOC &&
269                             action == CMD_SUBCMD_ENTER_PS)
270                                 priv->psmode = LBS802_11POWERMODECAM;
271                 } else if (action == CMD_SUBCMD_ENTER_PS) {
272                         priv->needtowakeup = 0;
273                         priv->psstate = PS_STATE_AWAKE;
274
275                         lbs_deb_host("CMD_RESP: ENTER_PS command response\n");
276                         if (priv->connect_status != LBS_CONNECTED) {
277                                 /*
278                                  * When Deauth Event received before Enter_PS command
279                                  * response, We need to wake up the firmware.
280                                  */
281                                 lbs_deb_host(
282                                        "disconnected, invoking lbs_ps_wakeup\n");
283
284                                 spin_unlock_irqrestore(&priv->driver_lock, flags);
285                                 mutex_unlock(&priv->lock);
286                                 lbs_ps_wakeup(priv, 0);
287                                 mutex_lock(&priv->lock);
288                                 spin_lock_irqsave(&priv->driver_lock, flags);
289                         }
290                 } else if (action == CMD_SUBCMD_EXIT_PS) {
291                         priv->needtowakeup = 0;
292                         priv->psstate = PS_STATE_FULL_POWER;
293                         lbs_deb_host("CMD_RESP: EXIT_PS command response\n");
294                 } else {
295                         lbs_deb_host("CMD_RESP: PS action 0x%X\n", action);
296                 }
297
298                 lbs_complete_command(priv, priv->cur_cmd, result);
299                 spin_unlock_irqrestore(&priv->driver_lock, flags);
300
301                 ret = 0;
302                 goto done;
303         }
304
305         /* If the command is not successful, cleanup and return failure */
306         if ((result != 0 || !(respcmd & 0x8000))) {
307                 lbs_deb_host("CMD_RESP: error 0x%04x in command reply 0x%04x\n",
308                        result, respcmd);
309                 /*
310                  * Handling errors here
311                  */
312                 switch (respcmd) {
313                 case CMD_RET(CMD_GET_HW_SPEC):
314                 case CMD_RET(CMD_802_11_RESET):
315                         lbs_deb_host("CMD_RESP: reset failed\n");
316                         break;
317
318                 }
319                 lbs_complete_command(priv, priv->cur_cmd, result);
320                 spin_unlock_irqrestore(&priv->driver_lock, flags);
321
322                 ret = -1;
323                 goto done;
324         }
325
326         spin_unlock_irqrestore(&priv->driver_lock, flags);
327
328         if (priv->cur_cmd && priv->cur_cmd->callback) {
329                 ret = priv->cur_cmd->callback(priv, priv->cur_cmd->callback_arg,
330                                 resp);
331         } else
332                 ret = handle_cmd_response(priv, resp);
333
334         spin_lock_irqsave(&priv->driver_lock, flags);
335
336         if (priv->cur_cmd) {
337                 /* Clean up and Put current command back to cmdfreeq */
338                 lbs_complete_command(priv, priv->cur_cmd, result);
339         }
340         spin_unlock_irqrestore(&priv->driver_lock, flags);
341
342 done:
343         mutex_unlock(&priv->lock);
344         lbs_deb_leave_args(LBS_DEB_HOST, "ret %d", ret);
345         return ret;
346 }
347
348 static int lbs_send_confirmwake(struct lbs_private *priv)
349 {
350         struct cmd_header cmd;
351         int ret = 0;
352
353         lbs_deb_enter(LBS_DEB_HOST);
354
355         cmd.command = cpu_to_le16(CMD_802_11_WAKEUP_CONFIRM);
356         cmd.size = cpu_to_le16(sizeof(cmd));
357         cmd.seqnum = cpu_to_le16(++priv->seqnum);
358         cmd.result = 0;
359
360         lbs_deb_hex(LBS_DEB_HOST, "wake confirm", (u8 *) &cmd,
361                 sizeof(cmd));
362
363         ret = priv->hw_host_to_card(priv, MVMS_CMD, (u8 *) &cmd, sizeof(cmd));
364         if (ret)
365                 lbs_pr_alert("SEND_WAKEC_CMD: Host to Card failed for Confirm Wake\n");
366
367         lbs_deb_leave_args(LBS_DEB_HOST, "ret %d", ret);
368         return ret;
369 }
370
371 int lbs_process_event(struct lbs_private *priv, u32 event)
372 {
373         int ret = 0;
374
375         lbs_deb_enter(LBS_DEB_CMD);
376
377         switch (event) {
378         case MACREG_INT_CODE_LINK_SENSED:
379                 lbs_deb_cmd("EVENT: link sensed\n");
380                 break;
381
382         case MACREG_INT_CODE_DEAUTHENTICATED:
383                 lbs_deb_cmd("EVENT: deauthenticated\n");
384                 lbs_mac_event_disconnected(priv);
385                 break;
386
387         case MACREG_INT_CODE_DISASSOCIATED:
388                 lbs_deb_cmd("EVENT: disassociated\n");
389                 lbs_mac_event_disconnected(priv);
390                 break;
391
392         case MACREG_INT_CODE_LINK_LOST_NO_SCAN:
393                 lbs_deb_cmd("EVENT: link lost\n");
394                 lbs_mac_event_disconnected(priv);
395                 break;
396
397         case MACREG_INT_CODE_PS_SLEEP:
398                 lbs_deb_cmd("EVENT: ps sleep\n");
399
400                 /* handle unexpected PS SLEEP event */
401                 if (priv->psstate == PS_STATE_FULL_POWER) {
402                         lbs_deb_cmd(
403                                "EVENT: in FULL POWER mode, ignoreing PS_SLEEP\n");
404                         break;
405                 }
406                 priv->psstate = PS_STATE_PRE_SLEEP;
407
408                 lbs_ps_confirm_sleep(priv);
409
410                 break;
411
412         case MACREG_INT_CODE_HOST_AWAKE:
413                 lbs_deb_cmd("EVENT: host awake\n");
414                 if (priv->reset_deep_sleep_wakeup)
415                         priv->reset_deep_sleep_wakeup(priv);
416                 priv->is_deep_sleep = 0;
417                 lbs_send_confirmwake(priv);
418                 break;
419
420         case MACREG_INT_CODE_DEEP_SLEEP_AWAKE:
421                 if (priv->reset_deep_sleep_wakeup)
422                         priv->reset_deep_sleep_wakeup(priv);
423                 lbs_deb_cmd("EVENT: ds awake\n");
424                 priv->is_deep_sleep = 0;
425                 priv->wakeup_dev_required = 0;
426                 wake_up_interruptible(&priv->ds_awake_q);
427                 break;
428
429         case MACREG_INT_CODE_PS_AWAKE:
430                 lbs_deb_cmd("EVENT: ps awake\n");
431                 /* handle unexpected PS AWAKE event */
432                 if (priv->psstate == PS_STATE_FULL_POWER) {
433                         lbs_deb_cmd(
434                                "EVENT: In FULL POWER mode - ignore PS AWAKE\n");
435                         break;
436                 }
437
438                 priv->psstate = PS_STATE_AWAKE;
439
440                 if (priv->needtowakeup) {
441                         /*
442                          * wait for the command processing to finish
443                          * before resuming sending
444                          * priv->needtowakeup will be set to FALSE
445                          * in lbs_ps_wakeup()
446                          */
447                         lbs_deb_cmd("waking up ...\n");
448                         lbs_ps_wakeup(priv, 0);
449                 }
450                 break;
451
452         case MACREG_INT_CODE_MIC_ERR_UNICAST:
453                 lbs_deb_cmd("EVENT: UNICAST MIC ERROR\n");
454                 lbs_send_mic_failureevent(priv, event);
455                 break;
456
457         case MACREG_INT_CODE_MIC_ERR_MULTICAST:
458                 lbs_deb_cmd("EVENT: MULTICAST MIC ERROR\n");
459                 lbs_send_mic_failureevent(priv, event);
460                 break;
461
462         case MACREG_INT_CODE_MIB_CHANGED:
463                 lbs_deb_cmd("EVENT: MIB CHANGED\n");
464                 break;
465         case MACREG_INT_CODE_INIT_DONE:
466                 lbs_deb_cmd("EVENT: INIT DONE\n");
467                 break;
468         case MACREG_INT_CODE_ADHOC_BCN_LOST:
469                 lbs_deb_cmd("EVENT: ADHOC beacon lost\n");
470                 break;
471         case MACREG_INT_CODE_RSSI_LOW:
472                 lbs_pr_alert("EVENT: rssi low\n");
473                 break;
474         case MACREG_INT_CODE_SNR_LOW:
475                 lbs_pr_alert("EVENT: snr low\n");
476                 break;
477         case MACREG_INT_CODE_MAX_FAIL:
478                 lbs_pr_alert("EVENT: max fail\n");
479                 break;
480         case MACREG_INT_CODE_RSSI_HIGH:
481                 lbs_pr_alert("EVENT: rssi high\n");
482                 break;
483         case MACREG_INT_CODE_SNR_HIGH:
484                 lbs_pr_alert("EVENT: snr high\n");
485                 break;
486
487         case MACREG_INT_CODE_MESH_AUTO_STARTED:
488                 /* Ignore spurious autostart events */
489                 lbs_pr_info("EVENT: MESH_AUTO_STARTED (ignoring)\n");
490                 break;
491
492         default:
493                 lbs_pr_alert("EVENT: unknown event id %d\n", event);
494                 break;
495         }
496
497         lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret);
498         return ret;
499 }