aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJohannes Berg <johannes.berg@intel.com>2013-01-08 05:29:12 -0500
committerJohannes Berg <johannes.berg@intel.com>2013-02-12 10:52:25 -0500
commit5718d27fc9a076ca397ea8c3d70e29969be24b9b (patch)
treecee1ba3d70f3c3ea2f0c82baac38b82ba4a98eb8
parent94d2f0ba052150a6d7217aeb836f5a709d1cadda (diff)
iwlwifi: dvm: query and report WoWLAN wakeup reason
Implement proper WoWLAN wakeup and query the wakeup reasons, then report them to userspace. Note that this is tricky: a firmware bug (that has been fixed in later versions) means that the status command response isn't properly closed in hardware and thus won't arrive at the host. Sending another command after it closes the status response but the next command gets stuck, etc. We reset the device after querying though, so this is not a big issue, just makes for strange code. Reviewed-by: Emmanuel Grumbach <emmanuel.grumbach@intel.com> Signed-off-by: Johannes Berg <johannes.berg@intel.com>
-rw-r--r--drivers/net/wireless/iwlwifi/dvm/commands.h18
-rw-r--r--drivers/net/wireless/iwlwifi/dvm/mac80211.c161
2 files changed, 150 insertions, 29 deletions
diff --git a/drivers/net/wireless/iwlwifi/dvm/commands.h b/drivers/net/wireless/iwlwifi/dvm/commands.h
index 8bce4b0148e0..02c9ebb3b340 100644
--- a/drivers/net/wireless/iwlwifi/dvm/commands.h
+++ b/drivers/net/wireless/iwlwifi/dvm/commands.h
@@ -3897,6 +3897,24 @@ struct iwlagn_wowlan_kek_kck_material_cmd {
3897 __le64 replay_ctr; 3897 __le64 replay_ctr;
3898} __packed; 3898} __packed;
3899 3899
3900#define RF_KILL_INDICATOR_FOR_WOWLAN 0x87
3901
3902/*
3903 * REPLY_WOWLAN_GET_STATUS = 0xe5
3904 */
3905struct iwlagn_wowlan_status {
3906 __le64 replay_ctr;
3907 __le32 rekey_status;
3908 __le32 wakeup_reason;
3909 u8 pattern_number;
3910 u8 reserved1;
3911 __le16 qos_seq_ctr[8];
3912 __le16 non_qos_seq_ctr;
3913 __le16 reserved2;
3914 union iwlagn_all_tsc_rsc tsc_rsc;
3915 __le16 reserved3;
3916} __packed;
3917
3900/* 3918/*
3901 * REPLY_WIPAN_PARAMS = 0xb2 (Commands and Notification) 3919 * REPLY_WIPAN_PARAMS = 0xb2 (Commands and Notification)
3902 */ 3920 */
diff --git a/drivers/net/wireless/iwlwifi/dvm/mac80211.c b/drivers/net/wireless/iwlwifi/dvm/mac80211.c
index 75f6f6cfdd47..ebbfcf47c732 100644
--- a/drivers/net/wireless/iwlwifi/dvm/mac80211.c
+++ b/drivers/net/wireless/iwlwifi/dvm/mac80211.c
@@ -442,52 +442,154 @@ static int iwlagn_mac_suspend(struct ieee80211_hw *hw,
442 return ret; 442 return ret;
443} 443}
444 444
445struct iwl_resume_data {
446 struct iwl_priv *priv;
447 struct iwlagn_wowlan_status *cmd;
448 bool valid;
449};
450
451static bool iwl_resume_status_fn(struct iwl_notif_wait_data *notif_wait,
452 struct iwl_rx_packet *pkt, void *data)
453{
454 struct iwl_resume_data *resume_data = data;
455 struct iwl_priv *priv = resume_data->priv;
456 u32 len = le32_to_cpu(pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK;
457
458 if (len - 4 != sizeof(*resume_data->cmd)) {
459 IWL_ERR(priv, "rx wrong size data\n");
460 return true;
461 }
462 memcpy(resume_data->cmd, pkt->data, sizeof(*resume_data->cmd));
463 resume_data->valid = true;
464
465 return true;
466}
467
445static int iwlagn_mac_resume(struct ieee80211_hw *hw) 468static int iwlagn_mac_resume(struct ieee80211_hw *hw)
446{ 469{
447 struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); 470 struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw);
448 struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS]; 471 struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS];
449 struct ieee80211_vif *vif; 472 struct ieee80211_vif *vif;
450 unsigned long flags; 473 u32 base;
451 u32 base, status = 0xffffffff; 474 int ret;
452 int ret = -EIO; 475 enum iwl_d3_status d3_status;
476 struct error_table_start {
477 /* cf. struct iwl_error_event_table */
478 u32 valid;
479 u32 error_id;
480 } err_info;
481 struct iwl_notification_wait status_wait;
482 static const u8 status_cmd[] = {
483 REPLY_WOWLAN_GET_STATUS,
484 };
485 struct iwlagn_wowlan_status status_data = {};
486 struct iwl_resume_data resume_data = {
487 .priv = priv,
488 .cmd = &status_data,
489 .valid = false,
490 };
491 struct cfg80211_wowlan_wakeup wakeup = {
492 .pattern_idx = -1,
493 };
494#ifdef CONFIG_IWLWIFI_DEBUGFS
495 const struct fw_img *img;
496#endif
453 497
454 IWL_DEBUG_MAC80211(priv, "enter\n"); 498 IWL_DEBUG_MAC80211(priv, "enter\n");
455 mutex_lock(&priv->mutex); 499 mutex_lock(&priv->mutex);
456 500
457 iwl_write32(priv->trans, CSR_UCODE_DRV_GP1_CLR, 501 /* we'll clear ctx->vif during iwlagn_prepare_restart() */
458 CSR_UCODE_DRV_GP1_BIT_D3_CFG_COMPLETE); 502 vif = ctx->vif;
503
504 ret = iwl_trans_d3_resume(priv->trans, &d3_status);
505 if (ret)
506 goto out_unlock;
507
508 if (d3_status != IWL_D3_STATUS_ALIVE) {
509 IWL_INFO(priv, "Device was reset during suspend\n");
510 goto out_unlock;
511 }
459 512
460 base = priv->device_pointers.error_event_table; 513 base = priv->device_pointers.error_event_table;
461 if (iwlagn_hw_valid_rtc_data_addr(base)) { 514 if (!iwlagn_hw_valid_rtc_data_addr(base)) {
462 if (iwl_trans_grab_nic_access(priv->trans, true, &flags)) { 515 IWL_WARN(priv, "Invalid error table during resume!\n");
463 iwl_write32(priv->trans, HBUS_TARG_MEM_RADDR, base); 516 goto out_unlock;
464 status = iwl_read32(priv->trans, HBUS_TARG_MEM_RDAT); 517 }
465 iwl_trans_release_nic_access(priv->trans, &flags); 518
466 ret = 0; 519 iwl_trans_read_mem_bytes(priv->trans, base,
520 &err_info, sizeof(err_info));
521
522 if (err_info.valid) {
523 IWL_INFO(priv, "error table is valid (%d, 0x%x)\n",
524 err_info.valid, err_info.error_id);
525 if (err_info.error_id == RF_KILL_INDICATOR_FOR_WOWLAN) {
526 wakeup.rfkill_release = true;
527 ieee80211_report_wowlan_wakeup(vif, &wakeup,
528 GFP_KERNEL);
467 } 529 }
530 goto out_unlock;
531 }
468 532
469#ifdef CONFIG_IWLWIFI_DEBUGFS 533#ifdef CONFIG_IWLWIFI_DEBUGFS
470 if (ret == 0) { 534 img = &priv->fw->img[IWL_UCODE_WOWLAN];
471 const struct fw_img *img; 535 if (!priv->wowlan_sram)
472 536 priv->wowlan_sram =
473 img = &(priv->fw->img[IWL_UCODE_WOWLAN]); 537 kzalloc(img->sec[IWL_UCODE_SECTION_DATA].len,
474 if (!priv->wowlan_sram) { 538 GFP_KERNEL);
475 priv->wowlan_sram = 539
476 kzalloc(img->sec[IWL_UCODE_SECTION_DATA].len, 540 if (priv->wowlan_sram)
477 GFP_KERNEL); 541 iwl_trans_read_mem(priv->trans, 0x800000,
478 } 542 priv->wowlan_sram,
543 img->sec[IWL_UCODE_SECTION_DATA].len / 4);
544#endif
479 545
480 if (priv->wowlan_sram) 546 /*
481 iwl_trans_read_mem( 547 * This is very strange. The GET_STATUS command is sent but the device
482 priv->trans, 0x800000, 548 * doesn't reply properly, it seems it doesn't close the RBD so one is
483 priv->wowlan_sram, 549 * always left open ... As a result, we need to send another command
484 img->sec[IWL_UCODE_SECTION_DATA].len / 4); 550 * and have to reset the driver afterwards. As we need to switch to
551 * runtime firmware again that'll happen.
552 */
553
554 iwl_init_notification_wait(&priv->notif_wait, &status_wait, status_cmd,
555 ARRAY_SIZE(status_cmd), iwl_resume_status_fn,
556 &resume_data);
557
558 iwl_dvm_send_cmd_pdu(priv, REPLY_WOWLAN_GET_STATUS, CMD_ASYNC, 0, NULL);
559 iwl_dvm_send_cmd_pdu(priv, REPLY_ECHO, CMD_ASYNC, 0, NULL);
560 /* an RBD is left open in the firmware now! */
561
562 ret = iwl_wait_notification(&priv->notif_wait, &status_wait, HZ/5);
563 if (ret)
564 goto out_unlock;
565
566 if (resume_data.valid && priv->contexts[IWL_RXON_CTX_BSS].vif) {
567 u32 reasons = le32_to_cpu(status_data.wakeup_reason);
568 struct cfg80211_wowlan_wakeup *wakeup_report;
569
570 IWL_INFO(priv, "WoWLAN wakeup reason(s): 0x%.8x\n", reasons);
571
572 if (reasons) {
573 if (reasons & IWLAGN_WOWLAN_WAKEUP_MAGIC_PACKET)
574 wakeup.magic_pkt = true;
575 if (reasons & IWLAGN_WOWLAN_WAKEUP_PATTERN_MATCH)
576 wakeup.pattern_idx = status_data.pattern_number;
577 if (reasons & (IWLAGN_WOWLAN_WAKEUP_BEACON_MISS |
578 IWLAGN_WOWLAN_WAKEUP_LINK_CHANGE))
579 wakeup.disconnect = true;
580 if (reasons & IWLAGN_WOWLAN_WAKEUP_GTK_REKEY_FAIL)
581 wakeup.gtk_rekey_failure = true;
582 if (reasons & IWLAGN_WOWLAN_WAKEUP_EAP_IDENT_REQ)
583 wakeup.eap_identity_req = true;
584 if (reasons & IWLAGN_WOWLAN_WAKEUP_4WAY_HANDSHAKE)
585 wakeup.four_way_handshake = true;
586 wakeup_report = &wakeup;
587 } else {
588 wakeup_report = NULL;
485 } 589 }
486#endif
487 }
488 590
489 /* we'll clear ctx->vif during iwlagn_prepare_restart() */ 591 ieee80211_report_wowlan_wakeup(vif, wakeup_report, GFP_KERNEL);
490 vif = ctx->vif; 592 }
491 593
492 priv->wowlan = false; 594 priv->wowlan = false;
493 595
@@ -497,6 +599,7 @@ static int iwlagn_mac_resume(struct ieee80211_hw *hw)
497 iwl_connection_init_rx_config(priv, ctx); 599 iwl_connection_init_rx_config(priv, ctx);
498 iwlagn_set_rxon_chain(priv, ctx); 600 iwlagn_set_rxon_chain(priv, ctx);
499 601
602 out_unlock:
500 mutex_unlock(&priv->mutex); 603 mutex_unlock(&priv->mutex);
501 IWL_DEBUG_MAC80211(priv, "leave\n"); 604 IWL_DEBUG_MAC80211(priv, "leave\n");
502 605