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