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