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