Merge branch 'io_remap_pfn_range' of git://www.jni.nu/cris
[pandora-kernel.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_set_ps_mode(priv, PS_MODE_ACTION_EXIT_PS, false);
53         }
54         lbs_deb_leave(LBS_DEB_ASSOC);
55 }
56
57 int lbs_process_command_response(struct lbs_private *priv, u8 *data, u32 len)
58 {
59         uint16_t respcmd, curcmd;
60         struct cmd_header *resp;
61         int ret = 0;
62         unsigned long flags;
63         uint16_t result;
64
65         lbs_deb_enter(LBS_DEB_HOST);
66
67         mutex_lock(&priv->lock);
68         spin_lock_irqsave(&priv->driver_lock, flags);
69
70         if (!priv->cur_cmd) {
71                 lbs_deb_host("CMD_RESP: cur_cmd is NULL\n");
72                 ret = -1;
73                 spin_unlock_irqrestore(&priv->driver_lock, flags);
74                 goto done;
75         }
76
77         resp = (void *)data;
78         curcmd = le16_to_cpu(priv->cur_cmd->cmdbuf->command);
79         respcmd = le16_to_cpu(resp->command);
80         result = le16_to_cpu(resp->result);
81
82         lbs_deb_cmd("CMD_RESP: response 0x%04x, seq %d, size %d\n",
83                      respcmd, le16_to_cpu(resp->seqnum), len);
84         lbs_deb_hex(LBS_DEB_CMD, "CMD_RESP", (void *) resp, len);
85
86         if (resp->seqnum != priv->cur_cmd->cmdbuf->seqnum) {
87                 lbs_pr_info("Received CMD_RESP with invalid sequence %d (expected %d)\n",
88                             le16_to_cpu(resp->seqnum), le16_to_cpu(priv->cur_cmd->cmdbuf->seqnum));
89                 spin_unlock_irqrestore(&priv->driver_lock, flags);
90                 ret = -1;
91                 goto done;
92         }
93         if (respcmd != CMD_RET(curcmd) &&
94             respcmd != CMD_RET_802_11_ASSOCIATE && curcmd != CMD_802_11_ASSOCIATE) {
95                 lbs_pr_info("Invalid CMD_RESP %x to command %x!\n", respcmd, curcmd);
96                 spin_unlock_irqrestore(&priv->driver_lock, flags);
97                 ret = -1;
98                 goto done;
99         }
100
101         if (resp->result == cpu_to_le16(0x0004)) {
102                 /* 0x0004 means -EAGAIN. Drop the response, let it time out
103                    and be resubmitted */
104                 lbs_pr_info("Firmware returns DEFER to command %x. Will let it time out...\n",
105                             le16_to_cpu(resp->command));
106                 spin_unlock_irqrestore(&priv->driver_lock, flags);
107                 ret = -1;
108                 goto done;
109         }
110
111         /* Now we got response from FW, cancel the command timer */
112         del_timer(&priv->command_timer);
113         priv->cmd_timed_out = 0;
114
115         if (respcmd == CMD_RET(CMD_802_11_PS_MODE)) {
116                 struct cmd_ds_802_11_ps_mode *psmode = (void *) &resp[1];
117                 u16 action = le16_to_cpu(psmode->action);
118
119                 lbs_deb_host(
120                        "CMD_RESP: PS_MODE cmd reply result 0x%x, action 0x%x\n",
121                        result, action);
122
123                 if (result) {
124                         lbs_deb_host("CMD_RESP: PS command failed with 0x%x\n",
125                                     result);
126                         /*
127                          * We should not re-try enter-ps command in
128                          * ad-hoc mode. It takes place in
129                          * lbs_execute_next_command().
130                          */
131                         if (priv->wdev->iftype == NL80211_IFTYPE_MONITOR &&
132                             action == PS_MODE_ACTION_ENTER_PS)
133                                 priv->psmode = LBS802_11POWERMODECAM;
134                 } else if (action == PS_MODE_ACTION_ENTER_PS) {
135                         priv->needtowakeup = 0;
136                         priv->psstate = PS_STATE_AWAKE;
137
138                         lbs_deb_host("CMD_RESP: ENTER_PS command response\n");
139                         if (priv->connect_status != LBS_CONNECTED) {
140                                 /*
141                                  * When Deauth Event received before Enter_PS command
142                                  * response, We need to wake up the firmware.
143                                  */
144                                 lbs_deb_host(
145                                        "disconnected, invoking lbs_ps_wakeup\n");
146
147                                 spin_unlock_irqrestore(&priv->driver_lock, flags);
148                                 mutex_unlock(&priv->lock);
149                                 lbs_set_ps_mode(priv, PS_MODE_ACTION_EXIT_PS,
150                                                 false);
151                                 mutex_lock(&priv->lock);
152                                 spin_lock_irqsave(&priv->driver_lock, flags);
153                         }
154                 } else if (action == PS_MODE_ACTION_EXIT_PS) {
155                         priv->needtowakeup = 0;
156                         priv->psstate = PS_STATE_FULL_POWER;
157                         lbs_deb_host("CMD_RESP: EXIT_PS command response\n");
158                 } else {
159                         lbs_deb_host("CMD_RESP: PS action 0x%X\n", action);
160                 }
161
162                 lbs_complete_command(priv, priv->cur_cmd, result);
163                 spin_unlock_irqrestore(&priv->driver_lock, flags);
164
165                 ret = 0;
166                 goto done;
167         }
168
169         /* If the command is not successful, cleanup and return failure */
170         if ((result != 0 || !(respcmd & 0x8000))) {
171                 lbs_deb_host("CMD_RESP: error 0x%04x in command reply 0x%04x\n",
172                        result, respcmd);
173                 /*
174                  * Handling errors here
175                  */
176                 switch (respcmd) {
177                 case CMD_RET(CMD_GET_HW_SPEC):
178                 case CMD_RET(CMD_802_11_RESET):
179                         lbs_deb_host("CMD_RESP: reset failed\n");
180                         break;
181
182                 }
183                 lbs_complete_command(priv, priv->cur_cmd, result);
184                 spin_unlock_irqrestore(&priv->driver_lock, flags);
185
186                 ret = -1;
187                 goto done;
188         }
189
190         spin_unlock_irqrestore(&priv->driver_lock, flags);
191
192         if (priv->cur_cmd && priv->cur_cmd->callback) {
193                 ret = priv->cur_cmd->callback(priv, priv->cur_cmd->callback_arg,
194                                 resp);
195         }
196
197         spin_lock_irqsave(&priv->driver_lock, flags);
198
199         if (priv->cur_cmd) {
200                 /* Clean up and Put current command back to cmdfreeq */
201                 lbs_complete_command(priv, priv->cur_cmd, result);
202         }
203         spin_unlock_irqrestore(&priv->driver_lock, flags);
204
205 done:
206         mutex_unlock(&priv->lock);
207         lbs_deb_leave_args(LBS_DEB_HOST, "ret %d", ret);
208         return ret;
209 }
210
211 int lbs_process_event(struct lbs_private *priv, u32 event)
212 {
213         int ret = 0;
214         struct cmd_header cmd;
215
216         lbs_deb_enter(LBS_DEB_CMD);
217
218         switch (event) {
219         case MACREG_INT_CODE_LINK_SENSED:
220                 lbs_deb_cmd("EVENT: link sensed\n");
221                 break;
222
223         case MACREG_INT_CODE_DEAUTHENTICATED:
224                 lbs_deb_cmd("EVENT: deauthenticated\n");
225                 lbs_mac_event_disconnected(priv);
226                 break;
227
228         case MACREG_INT_CODE_DISASSOCIATED:
229                 lbs_deb_cmd("EVENT: disassociated\n");
230                 lbs_mac_event_disconnected(priv);
231                 break;
232
233         case MACREG_INT_CODE_LINK_LOST_NO_SCAN:
234                 lbs_deb_cmd("EVENT: link lost\n");
235                 lbs_mac_event_disconnected(priv);
236                 break;
237
238         case MACREG_INT_CODE_PS_SLEEP:
239                 lbs_deb_cmd("EVENT: ps sleep\n");
240
241                 /* handle unexpected PS SLEEP event */
242                 if (priv->psstate == PS_STATE_FULL_POWER) {
243                         lbs_deb_cmd(
244                                "EVENT: in FULL POWER mode, ignoreing PS_SLEEP\n");
245                         break;
246                 }
247                 priv->psstate = PS_STATE_PRE_SLEEP;
248
249                 lbs_ps_confirm_sleep(priv);
250
251                 break;
252
253         case MACREG_INT_CODE_HOST_AWAKE:
254                 lbs_deb_cmd("EVENT: host awake\n");
255                 if (priv->reset_deep_sleep_wakeup)
256                         priv->reset_deep_sleep_wakeup(priv);
257                 priv->is_deep_sleep = 0;
258                 lbs_cmd_async(priv, CMD_802_11_WAKEUP_CONFIRM, &cmd,
259                                 sizeof(cmd));
260                 priv->is_host_sleep_activated = 0;
261                 wake_up_interruptible(&priv->host_sleep_q);
262                 break;
263
264         case MACREG_INT_CODE_DEEP_SLEEP_AWAKE:
265                 if (priv->reset_deep_sleep_wakeup)
266                         priv->reset_deep_sleep_wakeup(priv);
267                 lbs_deb_cmd("EVENT: ds awake\n");
268                 priv->is_deep_sleep = 0;
269                 priv->wakeup_dev_required = 0;
270                 wake_up_interruptible(&priv->ds_awake_q);
271                 break;
272
273         case MACREG_INT_CODE_PS_AWAKE:
274                 lbs_deb_cmd("EVENT: ps awake\n");
275                 /* handle unexpected PS AWAKE event */
276                 if (priv->psstate == PS_STATE_FULL_POWER) {
277                         lbs_deb_cmd(
278                                "EVENT: In FULL POWER mode - ignore PS AWAKE\n");
279                         break;
280                 }
281
282                 priv->psstate = PS_STATE_AWAKE;
283
284                 if (priv->needtowakeup) {
285                         /*
286                          * wait for the command processing to finish
287                          * before resuming sending
288                          * priv->needtowakeup will be set to FALSE
289                          * in lbs_ps_wakeup()
290                          */
291                         lbs_deb_cmd("waking up ...\n");
292                         lbs_set_ps_mode(priv, PS_MODE_ACTION_EXIT_PS, false);
293                 }
294                 break;
295
296         case MACREG_INT_CODE_MIC_ERR_UNICAST:
297                 lbs_deb_cmd("EVENT: UNICAST MIC ERROR\n");
298                 lbs_send_mic_failureevent(priv, event);
299                 break;
300
301         case MACREG_INT_CODE_MIC_ERR_MULTICAST:
302                 lbs_deb_cmd("EVENT: MULTICAST MIC ERROR\n");
303                 lbs_send_mic_failureevent(priv, event);
304                 break;
305
306         case MACREG_INT_CODE_MIB_CHANGED:
307                 lbs_deb_cmd("EVENT: MIB CHANGED\n");
308                 break;
309         case MACREG_INT_CODE_INIT_DONE:
310                 lbs_deb_cmd("EVENT: INIT DONE\n");
311                 break;
312         case MACREG_INT_CODE_ADHOC_BCN_LOST:
313                 lbs_deb_cmd("EVENT: ADHOC beacon lost\n");
314                 break;
315         case MACREG_INT_CODE_RSSI_LOW:
316                 lbs_pr_alert("EVENT: rssi low\n");
317                 break;
318         case MACREG_INT_CODE_SNR_LOW:
319                 lbs_pr_alert("EVENT: snr low\n");
320                 break;
321         case MACREG_INT_CODE_MAX_FAIL:
322                 lbs_pr_alert("EVENT: max fail\n");
323                 break;
324         case MACREG_INT_CODE_RSSI_HIGH:
325                 lbs_pr_alert("EVENT: rssi high\n");
326                 break;
327         case MACREG_INT_CODE_SNR_HIGH:
328                 lbs_pr_alert("EVENT: snr high\n");
329                 break;
330
331         case MACREG_INT_CODE_MESH_AUTO_STARTED:
332                 /* Ignore spurious autostart events */
333                 lbs_pr_info("EVENT: MESH_AUTO_STARTED (ignoring)\n");
334                 break;
335
336         default:
337                 lbs_pr_alert("EVENT: unknown event id %d\n", event);
338                 break;
339         }
340
341         lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret);
342         return ret;
343 }