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