Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at 4dfd459b738cf1f65b3eac4e0a9b19bc93cc91c6 511 lines 14 kB view raw
1/** 2 * This file contains the handling of command 3 * responses as well as events generated by firmware. 4 */ 5#include <linux/delay.h> 6#include <linux/sched.h> 7#include <linux/if_arp.h> 8#include <linux/netdevice.h> 9#include <asm/unaligned.h> 10#include <net/iw_handler.h> 11 12#include "host.h" 13#include "decl.h" 14#include "cmd.h" 15#include "defs.h" 16#include "dev.h" 17#include "assoc.h" 18#include "wext.h" 19 20/** 21 * @brief This function handles disconnect event. it 22 * reports disconnect to upper layer, clean tx/rx packets, 23 * reset link state etc. 24 * 25 * @param priv A pointer to struct lbs_private structure 26 * @return n/a 27 */ 28void lbs_mac_event_disconnected(struct lbs_private *priv) 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 lbs_send_disconnect_notification(priv); 41 42 /* report disconnect to upper layer */ 43 netif_stop_queue(priv->dev); 44 netif_carrier_off(priv->dev); 45 46 /* Free Tx and Rx packets */ 47 kfree_skb(priv->currenttxskb); 48 priv->currenttxskb = NULL; 49 priv->tx_pending_len = 0; 50 51 /* reset SNR/NF/RSSI values */ 52 memset(priv->SNR, 0x00, sizeof(priv->SNR)); 53 memset(priv->NF, 0x00, sizeof(priv->NF)); 54 memset(priv->RSSI, 0x00, sizeof(priv->RSSI)); 55 memset(priv->rawSNR, 0x00, sizeof(priv->rawSNR)); 56 memset(priv->rawNF, 0x00, sizeof(priv->rawNF)); 57 priv->nextSNRNF = 0; 58 priv->numSNRNF = 0; 59 priv->connect_status = LBS_DISCONNECTED; 60 61 /* Clear out associated SSID and BSSID since connection is 62 * no longer valid. 63 */ 64 memset(&priv->curbssparams.bssid, 0, ETH_ALEN); 65 memset(&priv->curbssparams.ssid, 0, IEEE80211_MAX_SSID_LEN); 66 priv->curbssparams.ssid_len = 0; 67 68 if (priv->psstate != PS_STATE_FULL_POWER) { 69 /* make firmware to exit PS mode */ 70 lbs_deb_cmd("disconnected, so exit PS mode\n"); 71 lbs_ps_wakeup(priv, 0); 72 } 73 lbs_deb_leave(LBS_DEB_ASSOC); 74} 75 76static int lbs_ret_reg_access(struct lbs_private *priv, 77 u16 type, struct cmd_ds_command *resp) 78{ 79 int ret = 0; 80 81 lbs_deb_enter(LBS_DEB_CMD); 82 83 switch (type) { 84 case CMD_RET(CMD_MAC_REG_ACCESS): 85 { 86 struct cmd_ds_mac_reg_access *reg = &resp->params.macreg; 87 88 priv->offsetvalue.offset = (u32)le16_to_cpu(reg->offset); 89 priv->offsetvalue.value = le32_to_cpu(reg->value); 90 break; 91 } 92 93 case CMD_RET(CMD_BBP_REG_ACCESS): 94 { 95 struct cmd_ds_bbp_reg_access *reg = &resp->params.bbpreg; 96 97 priv->offsetvalue.offset = (u32)le16_to_cpu(reg->offset); 98 priv->offsetvalue.value = reg->value; 99 break; 100 } 101 102 case CMD_RET(CMD_RF_REG_ACCESS): 103 { 104 struct cmd_ds_rf_reg_access *reg = &resp->params.rfreg; 105 106 priv->offsetvalue.offset = (u32)le16_to_cpu(reg->offset); 107 priv->offsetvalue.value = reg->value; 108 break; 109 } 110 111 default: 112 ret = -1; 113 } 114 115 lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret); 116 return ret; 117} 118 119static inline int handle_cmd_response(struct lbs_private *priv, 120 struct cmd_header *cmd_response) 121{ 122 struct cmd_ds_command *resp = (struct cmd_ds_command *) cmd_response; 123 int ret = 0; 124 unsigned long flags; 125 uint16_t respcmd = le16_to_cpu(resp->command); 126 127 lbs_deb_enter(LBS_DEB_HOST); 128 129 switch (respcmd) { 130 case CMD_RET(CMD_MAC_REG_ACCESS): 131 case CMD_RET(CMD_BBP_REG_ACCESS): 132 case CMD_RET(CMD_RF_REG_ACCESS): 133 ret = lbs_ret_reg_access(priv, respcmd, resp); 134 break; 135 136 case CMD_RET(CMD_802_11_SET_AFC): 137 case CMD_RET(CMD_802_11_GET_AFC): 138 spin_lock_irqsave(&priv->driver_lock, flags); 139 memmove((void *)priv->cur_cmd->callback_arg, &resp->params.afc, 140 sizeof(struct cmd_ds_802_11_afc)); 141 spin_unlock_irqrestore(&priv->driver_lock, flags); 142 143 break; 144 145 case CMD_RET(CMD_802_11_BEACON_STOP): 146 break; 147 148 case CMD_RET(CMD_802_11_RSSI): 149 ret = lbs_ret_802_11_rssi(priv, resp); 150 break; 151 152 case CMD_RET(CMD_802_11_TPC_CFG): 153 spin_lock_irqsave(&priv->driver_lock, flags); 154 memmove((void *)priv->cur_cmd->callback_arg, &resp->params.tpccfg, 155 sizeof(struct cmd_ds_802_11_tpc_cfg)); 156 spin_unlock_irqrestore(&priv->driver_lock, flags); 157 break; 158 159 case CMD_RET(CMD_BT_ACCESS): 160 spin_lock_irqsave(&priv->driver_lock, flags); 161 if (priv->cur_cmd->callback_arg) 162 memcpy((void *)priv->cur_cmd->callback_arg, 163 &resp->params.bt.addr1, 2 * ETH_ALEN); 164 spin_unlock_irqrestore(&priv->driver_lock, flags); 165 break; 166 case CMD_RET(CMD_FWT_ACCESS): 167 spin_lock_irqsave(&priv->driver_lock, flags); 168 if (priv->cur_cmd->callback_arg) 169 memcpy((void *)priv->cur_cmd->callback_arg, &resp->params.fwt, 170 sizeof(resp->params.fwt)); 171 spin_unlock_irqrestore(&priv->driver_lock, flags); 172 break; 173 case CMD_RET(CMD_802_11_BEACON_CTRL): 174 ret = lbs_ret_802_11_bcn_ctrl(priv, resp); 175 break; 176 177 default: 178 lbs_pr_err("CMD_RESP: unknown cmd response 0x%04x\n", 179 le16_to_cpu(resp->command)); 180 break; 181 } 182 lbs_deb_leave(LBS_DEB_HOST); 183 return ret; 184} 185 186int lbs_process_command_response(struct lbs_private *priv, u8 *data, u32 len) 187{ 188 uint16_t respcmd, curcmd; 189 struct cmd_header *resp; 190 int ret = 0; 191 unsigned long flags; 192 uint16_t result; 193 194 lbs_deb_enter(LBS_DEB_HOST); 195 196 mutex_lock(&priv->lock); 197 spin_lock_irqsave(&priv->driver_lock, flags); 198 199 if (!priv->cur_cmd) { 200 lbs_deb_host("CMD_RESP: cur_cmd is NULL\n"); 201 ret = -1; 202 spin_unlock_irqrestore(&priv->driver_lock, flags); 203 goto done; 204 } 205 206 resp = (void *)data; 207 curcmd = le16_to_cpu(priv->cur_cmd->cmdbuf->command); 208 respcmd = le16_to_cpu(resp->command); 209 result = le16_to_cpu(resp->result); 210 211 lbs_deb_cmd("CMD_RESP: response 0x%04x, seq %d, size %d\n", 212 respcmd, le16_to_cpu(resp->seqnum), len); 213 lbs_deb_hex(LBS_DEB_CMD, "CMD_RESP", (void *) resp, len); 214 215 if (resp->seqnum != priv->cur_cmd->cmdbuf->seqnum) { 216 lbs_pr_info("Received CMD_RESP with invalid sequence %d (expected %d)\n", 217 le16_to_cpu(resp->seqnum), le16_to_cpu(priv->cur_cmd->cmdbuf->seqnum)); 218 spin_unlock_irqrestore(&priv->driver_lock, flags); 219 ret = -1; 220 goto done; 221 } 222 if (respcmd != CMD_RET(curcmd) && 223 respcmd != CMD_RET_802_11_ASSOCIATE && curcmd != CMD_802_11_ASSOCIATE) { 224 lbs_pr_info("Invalid CMD_RESP %x to command %x!\n", respcmd, curcmd); 225 spin_unlock_irqrestore(&priv->driver_lock, flags); 226 ret = -1; 227 goto done; 228 } 229 230 if (resp->result == cpu_to_le16(0x0004)) { 231 /* 0x0004 means -EAGAIN. Drop the response, let it time out 232 and be resubmitted */ 233 lbs_pr_info("Firmware returns DEFER to command %x. Will let it time out...\n", 234 le16_to_cpu(resp->command)); 235 spin_unlock_irqrestore(&priv->driver_lock, flags); 236 ret = -1; 237 goto done; 238 } 239 240 /* Now we got response from FW, cancel the command timer */ 241 del_timer(&priv->command_timer); 242 priv->cmd_timed_out = 0; 243 if (priv->nr_retries) { 244 lbs_pr_info("Received result %x to command %x after %d retries\n", 245 result, curcmd, priv->nr_retries); 246 priv->nr_retries = 0; 247 } 248 249 /* Store the response code to cur_cmd_retcode. */ 250 priv->cur_cmd_retcode = result; 251 252 if (respcmd == CMD_RET(CMD_802_11_PS_MODE)) { 253 struct cmd_ds_802_11_ps_mode *psmode = (void *) &resp[1]; 254 u16 action = le16_to_cpu(psmode->action); 255 256 lbs_deb_host( 257 "CMD_RESP: PS_MODE cmd reply result 0x%x, action 0x%x\n", 258 result, action); 259 260 if (result) { 261 lbs_deb_host("CMD_RESP: PS command failed with 0x%x\n", 262 result); 263 /* 264 * We should not re-try enter-ps command in 265 * ad-hoc mode. It takes place in 266 * lbs_execute_next_command(). 267 */ 268 if (priv->mode == IW_MODE_ADHOC && 269 action == CMD_SUBCMD_ENTER_PS) 270 priv->psmode = LBS802_11POWERMODECAM; 271 } else if (action == CMD_SUBCMD_ENTER_PS) { 272 priv->needtowakeup = 0; 273 priv->psstate = PS_STATE_AWAKE; 274 275 lbs_deb_host("CMD_RESP: ENTER_PS command response\n"); 276 if (priv->connect_status != LBS_CONNECTED) { 277 /* 278 * When Deauth Event received before Enter_PS command 279 * response, We need to wake up the firmware. 280 */ 281 lbs_deb_host( 282 "disconnected, invoking lbs_ps_wakeup\n"); 283 284 spin_unlock_irqrestore(&priv->driver_lock, flags); 285 mutex_unlock(&priv->lock); 286 lbs_ps_wakeup(priv, 0); 287 mutex_lock(&priv->lock); 288 spin_lock_irqsave(&priv->driver_lock, flags); 289 } 290 } else if (action == CMD_SUBCMD_EXIT_PS) { 291 priv->needtowakeup = 0; 292 priv->psstate = PS_STATE_FULL_POWER; 293 lbs_deb_host("CMD_RESP: EXIT_PS command response\n"); 294 } else { 295 lbs_deb_host("CMD_RESP: PS action 0x%X\n", action); 296 } 297 298 lbs_complete_command(priv, priv->cur_cmd, result); 299 spin_unlock_irqrestore(&priv->driver_lock, flags); 300 301 ret = 0; 302 goto done; 303 } 304 305 /* If the command is not successful, cleanup and return failure */ 306 if ((result != 0 || !(respcmd & 0x8000))) { 307 lbs_deb_host("CMD_RESP: error 0x%04x in command reply 0x%04x\n", 308 result, respcmd); 309 /* 310 * Handling errors here 311 */ 312 switch (respcmd) { 313 case CMD_RET(CMD_GET_HW_SPEC): 314 case CMD_RET(CMD_802_11_RESET): 315 lbs_deb_host("CMD_RESP: reset failed\n"); 316 break; 317 318 } 319 lbs_complete_command(priv, priv->cur_cmd, result); 320 spin_unlock_irqrestore(&priv->driver_lock, flags); 321 322 ret = -1; 323 goto done; 324 } 325 326 spin_unlock_irqrestore(&priv->driver_lock, flags); 327 328 if (priv->cur_cmd && priv->cur_cmd->callback) { 329 ret = priv->cur_cmd->callback(priv, priv->cur_cmd->callback_arg, 330 resp); 331 } else 332 ret = handle_cmd_response(priv, resp); 333 334 spin_lock_irqsave(&priv->driver_lock, flags); 335 336 if (priv->cur_cmd) { 337 /* Clean up and Put current command back to cmdfreeq */ 338 lbs_complete_command(priv, priv->cur_cmd, result); 339 } 340 spin_unlock_irqrestore(&priv->driver_lock, flags); 341 342done: 343 mutex_unlock(&priv->lock); 344 lbs_deb_leave_args(LBS_DEB_HOST, "ret %d", ret); 345 return ret; 346} 347 348static int lbs_send_confirmwake(struct lbs_private *priv) 349{ 350 struct cmd_header cmd; 351 int ret = 0; 352 353 lbs_deb_enter(LBS_DEB_HOST); 354 355 cmd.command = cpu_to_le16(CMD_802_11_WAKEUP_CONFIRM); 356 cmd.size = cpu_to_le16(sizeof(cmd)); 357 cmd.seqnum = cpu_to_le16(++priv->seqnum); 358 cmd.result = 0; 359 360 lbs_deb_hex(LBS_DEB_HOST, "wake confirm", (u8 *) &cmd, 361 sizeof(cmd)); 362 363 ret = priv->hw_host_to_card(priv, MVMS_CMD, (u8 *) &cmd, sizeof(cmd)); 364 if (ret) 365 lbs_pr_alert("SEND_WAKEC_CMD: Host to Card failed for Confirm Wake\n"); 366 367 lbs_deb_leave_args(LBS_DEB_HOST, "ret %d", ret); 368 return ret; 369} 370 371int lbs_process_event(struct lbs_private *priv, u32 event) 372{ 373 int ret = 0; 374 375 lbs_deb_enter(LBS_DEB_CMD); 376 377 switch (event) { 378 case MACREG_INT_CODE_LINK_SENSED: 379 lbs_deb_cmd("EVENT: link sensed\n"); 380 break; 381 382 case MACREG_INT_CODE_DEAUTHENTICATED: 383 lbs_deb_cmd("EVENT: deauthenticated\n"); 384 lbs_mac_event_disconnected(priv); 385 break; 386 387 case MACREG_INT_CODE_DISASSOCIATED: 388 lbs_deb_cmd("EVENT: disassociated\n"); 389 lbs_mac_event_disconnected(priv); 390 break; 391 392 case MACREG_INT_CODE_LINK_LOST_NO_SCAN: 393 lbs_deb_cmd("EVENT: link lost\n"); 394 lbs_mac_event_disconnected(priv); 395 break; 396 397 case MACREG_INT_CODE_PS_SLEEP: 398 lbs_deb_cmd("EVENT: ps sleep\n"); 399 400 /* handle unexpected PS SLEEP event */ 401 if (priv->psstate == PS_STATE_FULL_POWER) { 402 lbs_deb_cmd( 403 "EVENT: in FULL POWER mode, ignoreing PS_SLEEP\n"); 404 break; 405 } 406 priv->psstate = PS_STATE_PRE_SLEEP; 407 408 lbs_ps_confirm_sleep(priv); 409 410 break; 411 412 case MACREG_INT_CODE_HOST_AWAKE: 413 lbs_deb_cmd("EVENT: host awake\n"); 414 if (priv->reset_deep_sleep_wakeup) 415 priv->reset_deep_sleep_wakeup(priv); 416 priv->is_deep_sleep = 0; 417 lbs_send_confirmwake(priv); 418 break; 419 420 case MACREG_INT_CODE_DEEP_SLEEP_AWAKE: 421 if (priv->reset_deep_sleep_wakeup) 422 priv->reset_deep_sleep_wakeup(priv); 423 lbs_deb_cmd("EVENT: ds awake\n"); 424 priv->is_deep_sleep = 0; 425 priv->wakeup_dev_required = 0; 426 wake_up_interruptible(&priv->ds_awake_q); 427 break; 428 429 case MACREG_INT_CODE_PS_AWAKE: 430 lbs_deb_cmd("EVENT: ps awake\n"); 431 /* handle unexpected PS AWAKE event */ 432 if (priv->psstate == PS_STATE_FULL_POWER) { 433 lbs_deb_cmd( 434 "EVENT: In FULL POWER mode - ignore PS AWAKE\n"); 435 break; 436 } 437 438 priv->psstate = PS_STATE_AWAKE; 439 440 if (priv->needtowakeup) { 441 /* 442 * wait for the command processing to finish 443 * before resuming sending 444 * priv->needtowakeup will be set to FALSE 445 * in lbs_ps_wakeup() 446 */ 447 lbs_deb_cmd("waking up ...\n"); 448 lbs_ps_wakeup(priv, 0); 449 } 450 break; 451 452 case MACREG_INT_CODE_MIC_ERR_UNICAST: 453 lbs_deb_cmd("EVENT: UNICAST MIC ERROR\n"); 454 lbs_send_mic_failureevent(priv, event); 455 break; 456 457 case MACREG_INT_CODE_MIC_ERR_MULTICAST: 458 lbs_deb_cmd("EVENT: MULTICAST MIC ERROR\n"); 459 lbs_send_mic_failureevent(priv, event); 460 break; 461 462 case MACREG_INT_CODE_MIB_CHANGED: 463 lbs_deb_cmd("EVENT: MIB CHANGED\n"); 464 break; 465 case MACREG_INT_CODE_INIT_DONE: 466 lbs_deb_cmd("EVENT: INIT DONE\n"); 467 break; 468 case MACREG_INT_CODE_ADHOC_BCN_LOST: 469 lbs_deb_cmd("EVENT: ADHOC beacon lost\n"); 470 break; 471 case MACREG_INT_CODE_RSSI_LOW: 472 lbs_pr_alert("EVENT: rssi low\n"); 473 break; 474 case MACREG_INT_CODE_SNR_LOW: 475 lbs_pr_alert("EVENT: snr low\n"); 476 break; 477 case MACREG_INT_CODE_MAX_FAIL: 478 lbs_pr_alert("EVENT: max fail\n"); 479 break; 480 case MACREG_INT_CODE_RSSI_HIGH: 481 lbs_pr_alert("EVENT: rssi high\n"); 482 break; 483 case MACREG_INT_CODE_SNR_HIGH: 484 lbs_pr_alert("EVENT: snr high\n"); 485 break; 486 487 case MACREG_INT_CODE_MESH_AUTO_STARTED: 488 /* Ignore spurious autostart events if autostart is disabled */ 489 if (!priv->mesh_autostart_enabled) { 490 lbs_pr_info("EVENT: MESH_AUTO_STARTED (ignoring)\n"); 491 break; 492 } 493 lbs_pr_info("EVENT: MESH_AUTO_STARTED\n"); 494 priv->mesh_connect_status = LBS_CONNECTED; 495 if (priv->mesh_open) { 496 netif_carrier_on(priv->mesh_dev); 497 if (!priv->tx_pending_len) 498 netif_wake_queue(priv->mesh_dev); 499 } 500 priv->mode = IW_MODE_ADHOC; 501 schedule_work(&priv->sync_channel); 502 break; 503 504 default: 505 lbs_pr_alert("EVENT: unknown event id %d\n", event); 506 break; 507 } 508 509 lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret); 510 return ret; 511}