1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * This file contains the handling of command 4 * responses as well as events generated by firmware. 5 */ 6 7 #include <linux/hardirq.h> 8 #include <linux/slab.h> 9 #include <linux/delay.h> 10 #include <linux/sched.h> 11 #include <linux/unaligned.h> 12 #include <net/cfg80211.h> 13 14 #include "cfg.h" 15 #include "cmd.h" 16 17 /** 18 * lbs_mac_event_disconnected - handles disconnect event. It 19 * reports disconnect to upper layer, clean tx/rx packets, 20 * reset link state etc. 21 * 22 * @priv: A pointer to struct lbs_private structure 23 * @locally_generated: indicates disconnect was requested locally 24 * (usually by userspace) 25 * 26 * returns: n/a 27 */ 28 void lbs_mac_event_disconnected(struct lbs_private *priv, 29 bool locally_generated) 30 { 31 unsigned long flags; 32 33 if (priv->connect_status != LBS_CONNECTED) 34 return; 35 36 /* 37 * Cisco AP sends EAP failure and de-auth in less than 0.5 ms. 38 * It causes problem in the Supplicant 39 */ 40 msleep_interruptible(1000); 41 42 if (priv->wdev->iftype == NL80211_IFTYPE_STATION) 43 lbs_send_disconnect_notification(priv, locally_generated); 44 45 /* report disconnect to upper layer */ 46 netif_stop_queue(priv->dev); 47 netif_carrier_off(priv->dev); 48 49 /* Free Tx and Rx packets */ 50 spin_lock_irqsave(&priv->driver_lock, flags); 51 dev_kfree_skb_irq(priv->currenttxskb); 52 priv->currenttxskb = NULL; 53 priv->tx_pending_len = 0; 54 spin_unlock_irqrestore(&priv->driver_lock, flags); 55 56 priv->connect_status = LBS_DISCONNECTED; 57 58 if (priv->psstate != PS_STATE_FULL_POWER) { 59 /* make firmware to exit PS mode */ 60 lbs_deb_cmd("disconnected, so exit PS mode\n"); 61 lbs_set_ps_mode(priv, PS_MODE_ACTION_EXIT_PS, false); 62 } 63 } 64 65 int lbs_process_command_response(struct lbs_private *priv, u8 *data, u32 len) 66 { 67 uint16_t respcmd, curcmd; 68 struct cmd_header *resp; 69 int ret = 0; 70 unsigned long flags; 71 uint16_t result; 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 timer_delete(&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 also contains 127 * the header 128 */ 129 struct cmd_ds_802_11_ps_mode *psmode = (void *)resp; 130 u16 action = le16_to_cpu(psmode->action); 131 132 lbs_deb_host( 133 "CMD_RESP: PS_MODE cmd reply result 0x%x, action 0x%x\n", 134 result, action); 135 136 if (result) { 137 lbs_deb_host("CMD_RESP: PS command failed with 0x%x\n", 138 result); 139 /* 140 * We should not re-try enter-ps command in 141 * ad-hoc mode. It takes place in 142 * lbs_execute_next_command(). 143 */ 144 if (priv->wdev->iftype == NL80211_IFTYPE_MONITOR && 145 action == PS_MODE_ACTION_ENTER_PS) 146 priv->psmode = LBS802_11POWERMODECAM; 147 } else if (action == PS_MODE_ACTION_ENTER_PS) { 148 priv->needtowakeup = 0; 149 priv->psstate = PS_STATE_AWAKE; 150 151 lbs_deb_host("CMD_RESP: ENTER_PS command response\n"); 152 if (priv->connect_status != LBS_CONNECTED) { 153 /* 154 * When Deauth Event received before Enter_PS command 155 * response, We need to wake up the firmware. 156 */ 157 lbs_deb_host( 158 "disconnected, invoking lbs_ps_wakeup\n"); 159 160 spin_unlock_irqrestore(&priv->driver_lock, flags); 161 mutex_unlock(&priv->lock); 162 lbs_set_ps_mode(priv, PS_MODE_ACTION_EXIT_PS, 163 false); 164 mutex_lock(&priv->lock); 165 spin_lock_irqsave(&priv->driver_lock, flags); 166 } 167 } else if (action == PS_MODE_ACTION_EXIT_PS) { 168 priv->needtowakeup = 0; 169 priv->psstate = PS_STATE_FULL_POWER; 170 lbs_deb_host("CMD_RESP: EXIT_PS command response\n"); 171 } else { 172 lbs_deb_host("CMD_RESP: PS action 0x%X\n", action); 173 } 174 175 __lbs_complete_command(priv, priv->cur_cmd, result); 176 spin_unlock_irqrestore(&priv->driver_lock, flags); 177 178 ret = 0; 179 goto done; 180 } 181 182 /* If the command is not successful, cleanup and return failure */ 183 if ((result != 0 || !(respcmd & 0x8000))) { 184 lbs_deb_host("CMD_RESP: error 0x%04x in command reply 0x%04x\n", 185 result, respcmd); 186 /* 187 * Handling errors here 188 */ 189 switch (respcmd) { 190 case CMD_RET(CMD_GET_HW_SPEC): 191 case CMD_RET(CMD_802_11_RESET): 192 lbs_deb_host("CMD_RESP: reset failed\n"); 193 break; 194 195 } 196 __lbs_complete_command(priv, priv->cur_cmd, result); 197 spin_unlock_irqrestore(&priv->driver_lock, flags); 198 199 ret = -1; 200 goto done; 201 } 202 203 spin_unlock_irqrestore(&priv->driver_lock, flags); 204 205 if (priv->cur_cmd && priv->cur_cmd->callback) { 206 ret = priv->cur_cmd->callback(priv, priv->cur_cmd->callback_arg, 207 resp); 208 } 209 210 spin_lock_irqsave(&priv->driver_lock, flags); 211 212 if (priv->cur_cmd) { 213 /* Clean up and Put current command back to cmdfreeq */ 214 __lbs_complete_command(priv, priv->cur_cmd, result); 215 } 216 spin_unlock_irqrestore(&priv->driver_lock, flags); 217 218 done: 219 mutex_unlock(&priv->lock); 220 return ret; 221 } 222 223 void lbs_process_event(struct lbs_private *priv, u32 event) 224 { 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 wake_up_interruptible(&priv->ds_awake_q); 283 break; 284 285 case MACREG_INT_CODE_PS_AWAKE: 286 lbs_deb_cmd("EVENT: ps awake\n"); 287 /* handle unexpected PS AWAKE event */ 288 if (priv->psstate == PS_STATE_FULL_POWER) { 289 lbs_deb_cmd( 290 "EVENT: In FULL POWER mode - ignore PS AWAKE\n"); 291 break; 292 } 293 294 priv->psstate = PS_STATE_AWAKE; 295 296 if (priv->needtowakeup) { 297 /* 298 * wait for the command processing to finish 299 * before resuming sending 300 * priv->needtowakeup will be set to FALSE 301 * in lbs_ps_wakeup() 302 */ 303 lbs_deb_cmd("waking up ...\n"); 304 lbs_set_ps_mode(priv, PS_MODE_ACTION_EXIT_PS, false); 305 } 306 break; 307 308 case MACREG_INT_CODE_MIC_ERR_UNICAST: 309 lbs_deb_cmd("EVENT: UNICAST MIC ERROR\n"); 310 lbs_send_mic_failureevent(priv, event); 311 break; 312 313 case MACREG_INT_CODE_MIC_ERR_MULTICAST: 314 lbs_deb_cmd("EVENT: MULTICAST MIC ERROR\n"); 315 lbs_send_mic_failureevent(priv, event); 316 break; 317 318 case MACREG_INT_CODE_MIB_CHANGED: 319 lbs_deb_cmd("EVENT: MIB CHANGED\n"); 320 break; 321 case MACREG_INT_CODE_INIT_DONE: 322 lbs_deb_cmd("EVENT: INIT DONE\n"); 323 break; 324 case MACREG_INT_CODE_ADHOC_BCN_LOST: 325 lbs_deb_cmd("EVENT: ADHOC beacon lost\n"); 326 break; 327 case MACREG_INT_CODE_RSSI_LOW: 328 netdev_alert(priv->dev, "EVENT: rssi low\n"); 329 break; 330 case MACREG_INT_CODE_SNR_LOW: 331 netdev_alert(priv->dev, "EVENT: snr low\n"); 332 break; 333 case MACREG_INT_CODE_MAX_FAIL: 334 netdev_alert(priv->dev, "EVENT: max fail\n"); 335 break; 336 case MACREG_INT_CODE_RSSI_HIGH: 337 netdev_alert(priv->dev, "EVENT: rssi high\n"); 338 break; 339 case MACREG_INT_CODE_SNR_HIGH: 340 netdev_alert(priv->dev, "EVENT: snr high\n"); 341 break; 342 343 case MACREG_INT_CODE_MESH_AUTO_STARTED: 344 /* Ignore spurious autostart events */ 345 netdev_info(priv->dev, "EVENT: MESH_AUTO_STARTED (ignoring)\n"); 346 break; 347 348 default: 349 netdev_alert(priv->dev, "EVENT: unknown event id %d\n", event); 350 break; 351 } 352 } 353