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