Kyle Swenson | 8d8f654 | 2021-03-15 11:02:55 -0600 | [diff] [blame^] | 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 | } |