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