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 * 23 * returns: n/a 24 */ 25void lbs_mac_event_disconnected(struct lbs_private *priv) 26{ 27 if (priv->connect_status != LBS_CONNECTED) 28 return; 29 30 lbs_deb_enter(LBS_DEB_ASSOC); 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 */ 36 msleep_interruptible(1000); 37 38 if (priv->wdev->iftype == NL80211_IFTYPE_STATION) 39 lbs_send_disconnect_notification(priv); 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 50 priv->connect_status = LBS_DISCONNECTED; 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"); 55 lbs_set_ps_mode(priv, PS_MODE_ACTION_EXIT_PS, false); 56 } 57 lbs_deb_leave(LBS_DEB_ASSOC); 58} 59 60int lbs_process_command_response(struct lbs_private *priv, u8 *data, u32 len) 61{ 62 uint16_t respcmd, curcmd; 63 struct cmd_header *resp; 64 int ret = 0; 65 unsigned long flags; 66 uint16_t result; 67 68 lbs_deb_enter(LBS_DEB_HOST); 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) 140 priv->psmode = LBS802_11POWERMODECAM; 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); 156 lbs_set_ps_mode(priv, PS_MODE_ACTION_EXIT_PS, 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; 163 priv->psstate = PS_STATE_FULL_POWER; 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): 185 case CMD_RET(CMD_802_11_RESET): 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 212done: 213 mutex_unlock(&priv->lock); 214 lbs_deb_leave_args(LBS_DEB_HOST, "ret %d", ret); 215 return ret; 216} 217 218int lbs_process_event(struct lbs_private *priv, u32 event) 219{ 220 int ret = 0; 221 struct cmd_header cmd; 222 223 lbs_deb_enter(LBS_DEB_CMD); 224 225 switch (event) { 226 case MACREG_INT_CODE_LINK_SENSED: 227 lbs_deb_cmd("EVENT: link sensed\n"); 228 break; 229 230 case MACREG_INT_CODE_DEAUTHENTICATED: 231 lbs_deb_cmd("EVENT: deauthenticated\n"); 232 lbs_mac_event_disconnected(priv); 233 break; 234 235 case MACREG_INT_CODE_DISASSOCIATED: 236 lbs_deb_cmd("EVENT: disassociated\n"); 237 lbs_mac_event_disconnected(priv); 238 break; 239 240 case MACREG_INT_CODE_LINK_LOST_NO_SCAN: 241 lbs_deb_cmd("EVENT: link lost\n"); 242 lbs_mac_event_disconnected(priv); 243 break; 244 245 case MACREG_INT_CODE_PS_SLEEP: 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, ignoring PS_SLEEP\n"); 252 break; 253 } 254 priv->psstate = PS_STATE_PRE_SLEEP; 255 256 lbs_ps_confirm_sleep(priv); 257 258 break; 259 260 case MACREG_INT_CODE_HOST_AWAKE: 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; 265 lbs_cmd_async(priv, CMD_802_11_WAKEUP_CONFIRM, &cmd, 266 sizeof(cmd)); 267 priv->is_host_sleep_activated = 0; 268 wake_up_interruptible(&priv->host_sleep_q); 269 break; 270 271 case MACREG_INT_CODE_DEEP_SLEEP_AWAKE: 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; 277 wake_up_interruptible(&priv->ds_awake_q); 278 break; 279 280 case MACREG_INT_CODE_PS_AWAKE: 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"); 299 lbs_set_ps_mode(priv, PS_MODE_ACTION_EXIT_PS, false); 300 } 301 break; 302 303 case MACREG_INT_CODE_MIC_ERR_UNICAST: 304 lbs_deb_cmd("EVENT: UNICAST MIC ERROR\n"); 305 lbs_send_mic_failureevent(priv, event); 306 break; 307 308 case MACREG_INT_CODE_MIC_ERR_MULTICAST: 309 lbs_deb_cmd("EVENT: MULTICAST MIC ERROR\n"); 310 lbs_send_mic_failureevent(priv, event); 311 break; 312 313 case MACREG_INT_CODE_MIB_CHANGED: 314 lbs_deb_cmd("EVENT: MIB CHANGED\n"); 315 break; 316 case MACREG_INT_CODE_INIT_DONE: 317 lbs_deb_cmd("EVENT: INIT DONE\n"); 318 break; 319 case MACREG_INT_CODE_ADHOC_BCN_LOST: 320 lbs_deb_cmd("EVENT: ADHOC beacon lost\n"); 321 break; 322 case MACREG_INT_CODE_RSSI_LOW: 323 netdev_alert(priv->dev, "EVENT: rssi low\n"); 324 break; 325 case MACREG_INT_CODE_SNR_LOW: 326 netdev_alert(priv->dev, "EVENT: snr low\n"); 327 break; 328 case MACREG_INT_CODE_MAX_FAIL: 329 netdev_alert(priv->dev, "EVENT: max fail\n"); 330 break; 331 case MACREG_INT_CODE_RSSI_HIGH: 332 netdev_alert(priv->dev, "EVENT: rssi high\n"); 333 break; 334 case MACREG_INT_CODE_SNR_HIGH: 335 netdev_alert(priv->dev, "EVENT: snr high\n"); 336 break; 337 338 case MACREG_INT_CODE_MESH_AUTO_STARTED: 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} 351