]> git.karo-electronics.de Git - mv-sheeva.git/blob - drivers/net/wireless/libertas/cmdresp.c
libertas: remove unused indirect command response handler
[mv-sheeva.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 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         /* Store the response code to cur_cmd_retcode. */
116         priv->cur_cmd_retcode = result;
117
118         if (respcmd == CMD_RET(CMD_802_11_PS_MODE)) {
119                 struct cmd_ds_802_11_ps_mode *psmode = (void *) &resp[1];
120                 u16 action = le16_to_cpu(psmode->action);
121
122                 lbs_deb_host(
123                        "CMD_RESP: PS_MODE cmd reply result 0x%x, action 0x%x\n",
124                        result, action);
125
126                 if (result) {
127                         lbs_deb_host("CMD_RESP: PS command failed with 0x%x\n",
128                                     result);
129                         /*
130                          * We should not re-try enter-ps command in
131                          * ad-hoc mode. It takes place in
132                          * lbs_execute_next_command().
133                          */
134                         if (priv->wdev->iftype == NL80211_IFTYPE_MONITOR &&
135                             action == CMD_SUBCMD_ENTER_PS)
136                                 priv->psmode = LBS802_11POWERMODECAM;
137                 } else if (action == CMD_SUBCMD_ENTER_PS) {
138                         priv->needtowakeup = 0;
139                         priv->psstate = PS_STATE_AWAKE;
140
141                         lbs_deb_host("CMD_RESP: ENTER_PS command response\n");
142                         if (priv->connect_status != LBS_CONNECTED) {
143                                 /*
144                                  * When Deauth Event received before Enter_PS command
145                                  * response, We need to wake up the firmware.
146                                  */
147                                 lbs_deb_host(
148                                        "disconnected, invoking lbs_ps_wakeup\n");
149
150                                 spin_unlock_irqrestore(&priv->driver_lock, flags);
151                                 mutex_unlock(&priv->lock);
152                                 lbs_ps_wakeup(priv, 0);
153                                 mutex_lock(&priv->lock);
154                                 spin_lock_irqsave(&priv->driver_lock, flags);
155                         }
156                 } else if (action == CMD_SUBCMD_EXIT_PS) {
157                         priv->needtowakeup = 0;
158                         priv->psstate = PS_STATE_FULL_POWER;
159                         lbs_deb_host("CMD_RESP: EXIT_PS command response\n");
160                 } else {
161                         lbs_deb_host("CMD_RESP: PS action 0x%X\n", action);
162                 }
163
164                 lbs_complete_command(priv, priv->cur_cmd, result);
165                 spin_unlock_irqrestore(&priv->driver_lock, flags);
166
167                 ret = 0;
168                 goto done;
169         }
170
171         /* If the command is not successful, cleanup and return failure */
172         if ((result != 0 || !(respcmd & 0x8000))) {
173                 lbs_deb_host("CMD_RESP: error 0x%04x in command reply 0x%04x\n",
174                        result, respcmd);
175                 /*
176                  * Handling errors here
177                  */
178                 switch (respcmd) {
179                 case CMD_RET(CMD_GET_HW_SPEC):
180                 case CMD_RET(CMD_802_11_RESET):
181                         lbs_deb_host("CMD_RESP: reset failed\n");
182                         break;
183
184                 }
185                 lbs_complete_command(priv, priv->cur_cmd, result);
186                 spin_unlock_irqrestore(&priv->driver_lock, flags);
187
188                 ret = -1;
189                 goto done;
190         }
191
192         spin_unlock_irqrestore(&priv->driver_lock, flags);
193
194         if (priv->cur_cmd && priv->cur_cmd->callback) {
195                 ret = priv->cur_cmd->callback(priv, priv->cur_cmd->callback_arg,
196                                 resp);
197         }
198
199         spin_lock_irqsave(&priv->driver_lock, flags);
200
201         if (priv->cur_cmd) {
202                 /* Clean up and Put current command back to cmdfreeq */
203                 lbs_complete_command(priv, priv->cur_cmd, result);
204         }
205         spin_unlock_irqrestore(&priv->driver_lock, flags);
206
207 done:
208         mutex_unlock(&priv->lock);
209         lbs_deb_leave_args(LBS_DEB_HOST, "ret %d", ret);
210         return ret;
211 }
212
213 int lbs_process_event(struct lbs_private *priv, u32 event)
214 {
215         int ret = 0;
216         struct cmd_header cmd;
217
218         lbs_deb_enter(LBS_DEB_CMD);
219
220         switch (event) {
221         case MACREG_INT_CODE_LINK_SENSED:
222                 lbs_deb_cmd("EVENT: link sensed\n");
223                 break;
224
225         case MACREG_INT_CODE_DEAUTHENTICATED:
226                 lbs_deb_cmd("EVENT: deauthenticated\n");
227                 lbs_mac_event_disconnected(priv);
228                 break;
229
230         case MACREG_INT_CODE_DISASSOCIATED:
231                 lbs_deb_cmd("EVENT: disassociated\n");
232                 lbs_mac_event_disconnected(priv);
233                 break;
234
235         case MACREG_INT_CODE_LINK_LOST_NO_SCAN:
236                 lbs_deb_cmd("EVENT: link lost\n");
237                 lbs_mac_event_disconnected(priv);
238                 break;
239
240         case MACREG_INT_CODE_PS_SLEEP:
241                 lbs_deb_cmd("EVENT: ps sleep\n");
242
243                 /* handle unexpected PS SLEEP event */
244                 if (priv->psstate == PS_STATE_FULL_POWER) {
245                         lbs_deb_cmd(
246                                "EVENT: in FULL POWER mode, ignoreing PS_SLEEP\n");
247                         break;
248                 }
249                 priv->psstate = PS_STATE_PRE_SLEEP;
250
251                 lbs_ps_confirm_sleep(priv);
252
253                 break;
254
255         case MACREG_INT_CODE_HOST_AWAKE:
256                 lbs_deb_cmd("EVENT: host awake\n");
257                 if (priv->reset_deep_sleep_wakeup)
258                         priv->reset_deep_sleep_wakeup(priv);
259                 priv->is_deep_sleep = 0;
260                 lbs_cmd_async(priv, CMD_802_11_WAKEUP_CONFIRM, &cmd,
261                                 sizeof(cmd));
262                 priv->is_host_sleep_activated = 0;
263                 wake_up_interruptible(&priv->host_sleep_q);
264                 break;
265
266         case MACREG_INT_CODE_DEEP_SLEEP_AWAKE:
267                 if (priv->reset_deep_sleep_wakeup)
268                         priv->reset_deep_sleep_wakeup(priv);
269                 lbs_deb_cmd("EVENT: ds awake\n");
270                 priv->is_deep_sleep = 0;
271                 priv->wakeup_dev_required = 0;
272                 wake_up_interruptible(&priv->ds_awake_q);
273                 break;
274
275         case MACREG_INT_CODE_PS_AWAKE:
276                 lbs_deb_cmd("EVENT: ps awake\n");
277                 /* handle unexpected PS AWAKE event */
278                 if (priv->psstate == PS_STATE_FULL_POWER) {
279                         lbs_deb_cmd(
280                                "EVENT: In FULL POWER mode - ignore PS AWAKE\n");
281                         break;
282                 }
283
284                 priv->psstate = PS_STATE_AWAKE;
285
286                 if (priv->needtowakeup) {
287                         /*
288                          * wait for the command processing to finish
289                          * before resuming sending
290                          * priv->needtowakeup will be set to FALSE
291                          * in lbs_ps_wakeup()
292                          */
293                         lbs_deb_cmd("waking up ...\n");
294                         lbs_ps_wakeup(priv, 0);
295                 }
296                 break;
297
298         case MACREG_INT_CODE_MIC_ERR_UNICAST:
299                 lbs_deb_cmd("EVENT: UNICAST MIC ERROR\n");
300                 lbs_send_mic_failureevent(priv, event);
301                 break;
302
303         case MACREG_INT_CODE_MIC_ERR_MULTICAST:
304                 lbs_deb_cmd("EVENT: MULTICAST MIC ERROR\n");
305                 lbs_send_mic_failureevent(priv, event);
306                 break;
307
308         case MACREG_INT_CODE_MIB_CHANGED:
309                 lbs_deb_cmd("EVENT: MIB CHANGED\n");
310                 break;
311         case MACREG_INT_CODE_INIT_DONE:
312                 lbs_deb_cmd("EVENT: INIT DONE\n");
313                 break;
314         case MACREG_INT_CODE_ADHOC_BCN_LOST:
315                 lbs_deb_cmd("EVENT: ADHOC beacon lost\n");
316                 break;
317         case MACREG_INT_CODE_RSSI_LOW:
318                 lbs_pr_alert("EVENT: rssi low\n");
319                 break;
320         case MACREG_INT_CODE_SNR_LOW:
321                 lbs_pr_alert("EVENT: snr low\n");
322                 break;
323         case MACREG_INT_CODE_MAX_FAIL:
324                 lbs_pr_alert("EVENT: max fail\n");
325                 break;
326         case MACREG_INT_CODE_RSSI_HIGH:
327                 lbs_pr_alert("EVENT: rssi high\n");
328                 break;
329         case MACREG_INT_CODE_SNR_HIGH:
330                 lbs_pr_alert("EVENT: snr high\n");
331                 break;
332
333         case MACREG_INT_CODE_MESH_AUTO_STARTED:
334                 /* Ignore spurious autostart events */
335                 lbs_pr_info("EVENT: MESH_AUTO_STARTED (ignoring)\n");
336                 break;
337
338         default:
339                 lbs_pr_alert("EVENT: unknown event id %d\n", event);
340                 break;
341         }
342
343         lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret);
344         return ret;
345 }