Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
cmdresp.c
Go to the documentation of this file.
1 /*
2  * This file contains the handling of command
3  * responses as well as events generated by firmware.
4  */
5 
6 #include <linux/hardirq.h>
7 #include <linux/slab.h>
8 #include <linux/delay.h>
9 #include <linux/sched.h>
10 #include <asm/unaligned.h>
11 #include <net/cfg80211.h>
12 
13 #include "cfg.h"
14 #include "cmd.h"
15 
26 {
27  if (priv->connect_status != LBS_CONNECTED)
28  return;
29 
31 
32  /*
33  * Cisco AP sends EAP failure and de-auth in less than 0.5 ms.
34  * It causes problem in the Supplicant
35  */
37 
38  if (priv->wdev->iftype == NL80211_IFTYPE_STATION)
40 
41  /* report disconnect to upper layer */
42  netif_stop_queue(priv->dev);
43  netif_carrier_off(priv->dev);
44 
45  /* Free Tx and Rx packets */
46  kfree_skb(priv->currenttxskb);
47  priv->currenttxskb = NULL;
48  priv->tx_pending_len = 0;
49 
51 
52  if (priv->psstate != PS_STATE_FULL_POWER) {
53  /* make firmware to exit PS mode */
54  lbs_deb_cmd("disconnected, so exit PS mode\n");
56  }
58 }
59 
61 {
62  uint16_t respcmd, curcmd;
63  struct cmd_header *resp;
64  int ret = 0;
65  unsigned long flags;
67 
69 
70  mutex_lock(&priv->lock);
71  spin_lock_irqsave(&priv->driver_lock, flags);
72 
73  if (!priv->cur_cmd) {
74  lbs_deb_host("CMD_RESP: cur_cmd is NULL\n");
75  ret = -1;
76  spin_unlock_irqrestore(&priv->driver_lock, flags);
77  goto done;
78  }
79 
80  resp = (void *)data;
81  curcmd = le16_to_cpu(priv->cur_cmd->cmdbuf->command);
82  respcmd = le16_to_cpu(resp->command);
83  result = le16_to_cpu(resp->result);
84 
85  lbs_deb_cmd("CMD_RESP: response 0x%04x, seq %d, size %d\n",
86  respcmd, le16_to_cpu(resp->seqnum), len);
87  lbs_deb_hex(LBS_DEB_CMD, "CMD_RESP", (void *) resp, len);
88 
89  if (resp->seqnum != priv->cur_cmd->cmdbuf->seqnum) {
90  netdev_info(priv->dev,
91  "Received CMD_RESP with invalid sequence %d (expected %d)\n",
92  le16_to_cpu(resp->seqnum),
93  le16_to_cpu(priv->cur_cmd->cmdbuf->seqnum));
94  spin_unlock_irqrestore(&priv->driver_lock, flags);
95  ret = -1;
96  goto done;
97  }
98  if (respcmd != CMD_RET(curcmd) &&
99  respcmd != CMD_RET_802_11_ASSOCIATE && curcmd != CMD_802_11_ASSOCIATE) {
100  netdev_info(priv->dev, "Invalid CMD_RESP %x to command %x!\n",
101  respcmd, curcmd);
102  spin_unlock_irqrestore(&priv->driver_lock, flags);
103  ret = -1;
104  goto done;
105  }
106 
107  if (resp->result == cpu_to_le16(0x0004)) {
108  /* 0x0004 means -EAGAIN. Drop the response, let it time out
109  and be resubmitted */
110  netdev_info(priv->dev,
111  "Firmware returns DEFER to command %x. Will let it time out...\n",
112  le16_to_cpu(resp->command));
113  spin_unlock_irqrestore(&priv->driver_lock, flags);
114  ret = -1;
115  goto done;
116  }
117 
118  /* Now we got response from FW, cancel the command timer */
119  del_timer(&priv->command_timer);
120  priv->cmd_timed_out = 0;
121 
122  if (respcmd == CMD_RET(CMD_802_11_PS_MODE)) {
123  struct cmd_ds_802_11_ps_mode *psmode = (void *) &resp[1];
124  u16 action = le16_to_cpu(psmode->action);
125 
126  lbs_deb_host(
127  "CMD_RESP: PS_MODE cmd reply result 0x%x, action 0x%x\n",
128  result, action);
129 
130  if (result) {
131  lbs_deb_host("CMD_RESP: PS command failed with 0x%x\n",
132  result);
133  /*
134  * We should not re-try enter-ps command in
135  * ad-hoc mode. It takes place in
136  * lbs_execute_next_command().
137  */
138  if (priv->wdev->iftype == NL80211_IFTYPE_MONITOR &&
139  action == PS_MODE_ACTION_ENTER_PS)
141  } else if (action == PS_MODE_ACTION_ENTER_PS) {
142  priv->needtowakeup = 0;
143  priv->psstate = PS_STATE_AWAKE;
144 
145  lbs_deb_host("CMD_RESP: ENTER_PS command response\n");
146  if (priv->connect_status != LBS_CONNECTED) {
147  /*
148  * When Deauth Event received before Enter_PS command
149  * response, We need to wake up the firmware.
150  */
151  lbs_deb_host(
152  "disconnected, invoking lbs_ps_wakeup\n");
153 
154  spin_unlock_irqrestore(&priv->driver_lock, flags);
155  mutex_unlock(&priv->lock);
157  false);
158  mutex_lock(&priv->lock);
159  spin_lock_irqsave(&priv->driver_lock, flags);
160  }
161  } else if (action == PS_MODE_ACTION_EXIT_PS) {
162  priv->needtowakeup = 0;
164  lbs_deb_host("CMD_RESP: EXIT_PS command response\n");
165  } else {
166  lbs_deb_host("CMD_RESP: PS action 0x%X\n", action);
167  }
168 
169  __lbs_complete_command(priv, priv->cur_cmd, result);
170  spin_unlock_irqrestore(&priv->driver_lock, flags);
171 
172  ret = 0;
173  goto done;
174  }
175 
176  /* If the command is not successful, cleanup and return failure */
177  if ((result != 0 || !(respcmd & 0x8000))) {
178  lbs_deb_host("CMD_RESP: error 0x%04x in command reply 0x%04x\n",
179  result, respcmd);
180  /*
181  * Handling errors here
182  */
183  switch (respcmd) {
184  case CMD_RET(CMD_GET_HW_SPEC):
186  lbs_deb_host("CMD_RESP: reset failed\n");
187  break;
188 
189  }
190  __lbs_complete_command(priv, priv->cur_cmd, result);
191  spin_unlock_irqrestore(&priv->driver_lock, flags);
192 
193  ret = -1;
194  goto done;
195  }
196 
197  spin_unlock_irqrestore(&priv->driver_lock, flags);
198 
199  if (priv->cur_cmd && priv->cur_cmd->callback) {
200  ret = priv->cur_cmd->callback(priv, priv->cur_cmd->callback_arg,
201  resp);
202  }
203 
204  spin_lock_irqsave(&priv->driver_lock, flags);
205 
206  if (priv->cur_cmd) {
207  /* Clean up and Put current command back to cmdfreeq */
208  __lbs_complete_command(priv, priv->cur_cmd, result);
209  }
210  spin_unlock_irqrestore(&priv->driver_lock, flags);
211 
212 done:
213  mutex_unlock(&priv->lock);
214  lbs_deb_leave_args(LBS_DEB_HOST, "ret %d", ret);
215  return ret;
216 }
217 
219 {
220  int ret = 0;
221  struct cmd_header cmd;
222 
224 
225  switch (event) {
227  lbs_deb_cmd("EVENT: link sensed\n");
228  break;
229 
231  lbs_deb_cmd("EVENT: deauthenticated\n");
233  break;
234 
236  lbs_deb_cmd("EVENT: disassociated\n");
238  break;
239 
241  lbs_deb_cmd("EVENT: link lost\n");
243  break;
244 
246  lbs_deb_cmd("EVENT: ps sleep\n");
247 
248  /* handle unexpected PS SLEEP event */
249  if (priv->psstate == PS_STATE_FULL_POWER) {
250  lbs_deb_cmd(
251  "EVENT: in FULL POWER mode, ignoreing PS_SLEEP\n");
252  break;
253  }
254  priv->psstate = PS_STATE_PRE_SLEEP;
255 
256  lbs_ps_confirm_sleep(priv);
257 
258  break;
259 
261  lbs_deb_cmd("EVENT: host awake\n");
262  if (priv->reset_deep_sleep_wakeup)
263  priv->reset_deep_sleep_wakeup(priv);
264  priv->is_deep_sleep = 0;
266  sizeof(cmd));
267  priv->is_host_sleep_activated = 0;
269  break;
270 
272  if (priv->reset_deep_sleep_wakeup)
273  priv->reset_deep_sleep_wakeup(priv);
274  lbs_deb_cmd("EVENT: ds awake\n");
275  priv->is_deep_sleep = 0;
276  priv->wakeup_dev_required = 0;
278  break;
279 
281  lbs_deb_cmd("EVENT: ps awake\n");
282  /* handle unexpected PS AWAKE event */
283  if (priv->psstate == PS_STATE_FULL_POWER) {
284  lbs_deb_cmd(
285  "EVENT: In FULL POWER mode - ignore PS AWAKE\n");
286  break;
287  }
288 
289  priv->psstate = PS_STATE_AWAKE;
290 
291  if (priv->needtowakeup) {
292  /*
293  * wait for the command processing to finish
294  * before resuming sending
295  * priv->needtowakeup will be set to FALSE
296  * in lbs_ps_wakeup()
297  */
298  lbs_deb_cmd("waking up ...\n");
300  }
301  break;
302 
304  lbs_deb_cmd("EVENT: UNICAST MIC ERROR\n");
305  lbs_send_mic_failureevent(priv, event);
306  break;
307 
309  lbs_deb_cmd("EVENT: MULTICAST MIC ERROR\n");
310  lbs_send_mic_failureevent(priv, event);
311  break;
312 
314  lbs_deb_cmd("EVENT: MIB CHANGED\n");
315  break;
317  lbs_deb_cmd("EVENT: INIT DONE\n");
318  break;
320  lbs_deb_cmd("EVENT: ADHOC beacon lost\n");
321  break;
323  netdev_alert(priv->dev, "EVENT: rssi low\n");
324  break;
326  netdev_alert(priv->dev, "EVENT: snr low\n");
327  break;
329  netdev_alert(priv->dev, "EVENT: max fail\n");
330  break;
332  netdev_alert(priv->dev, "EVENT: rssi high\n");
333  break;
335  netdev_alert(priv->dev, "EVENT: snr high\n");
336  break;
337 
339  /* Ignore spurious autostart events */
340  netdev_info(priv->dev, "EVENT: MESH_AUTO_STARTED (ignoring)\n");
341  break;
342 
343  default:
344  netdev_alert(priv->dev, "EVENT: unknown event id %d\n", event);
345  break;
346  }
347 
348  lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret);
349  return ret;
350 }