diff options
author | David S. Miller <davem@davemloft.net> | 2010-03-29 16:50:10 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2010-03-29 16:50:10 -0400 |
commit | 7905e357ebe67a26d9dc8caa1a0b8346431b5f0d (patch) | |
tree | 134442df2f062caa6cebda1b352948b8209efcec /drivers/net/wireless/iwlwifi/iwl-rx.c | |
parent | 083ba279d52bcad20f1dfa3cefd4255cbe82d521 (diff) | |
parent | 76232ebf898c4d5e657f2b663fbf7108bca80ded (diff) |
Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-next-2.6
Diffstat (limited to 'drivers/net/wireless/iwlwifi/iwl-rx.c')
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-rx.c | 155 |
1 files changed, 127 insertions, 28 deletions
diff --git a/drivers/net/wireless/iwlwifi/iwl-rx.c b/drivers/net/wireless/iwlwifi/iwl-rx.c index 8116aa0d767..2fa30dfb7c5 100644 --- a/drivers/net/wireless/iwlwifi/iwl-rx.c +++ b/drivers/net/wireless/iwlwifi/iwl-rx.c | |||
@@ -616,29 +616,77 @@ static void iwl_accumulative_statistics(struct iwl_priv *priv, | |||
616 | 616 | ||
617 | #define REG_RECALIB_PERIOD (60) | 617 | #define REG_RECALIB_PERIOD (60) |
618 | 618 | ||
619 | #define PLCP_MSG "plcp_err exceeded %u, %u, %u, %u, %u, %d, %u mSecs\n" | 619 | /* the threshold ratio of actual_ack_cnt to expected_ack_cnt in percent */ |
620 | void iwl_rx_statistics(struct iwl_priv *priv, | 620 | #define ACK_CNT_RATIO (50) |
621 | struct iwl_rx_mem_buffer *rxb) | 621 | #define BA_TIMEOUT_CNT (5) |
622 | #define BA_TIMEOUT_MAX (16) | ||
623 | |||
624 | #if defined(CONFIG_IWLAGN) || defined(CONFIG_IWLAGN_MODULE) | ||
625 | /** | ||
626 | * iwl_good_ack_health - checks for ACK count ratios, BA timeout retries. | ||
627 | * | ||
628 | * When the ACK count ratio is 0 and aggregated BA timeout retries exceeding | ||
629 | * the BA_TIMEOUT_MAX, reload firmware and bring system back to normal | ||
630 | * operation state. | ||
631 | */ | ||
632 | bool iwl_good_ack_health(struct iwl_priv *priv, | ||
633 | struct iwl_rx_packet *pkt) | ||
622 | { | 634 | { |
623 | int change; | 635 | bool rc = true; |
624 | struct iwl_rx_packet *pkt = rxb_addr(rxb); | 636 | int actual_ack_cnt_delta, expected_ack_cnt_delta; |
637 | int ba_timeout_delta; | ||
638 | |||
639 | actual_ack_cnt_delta = | ||
640 | le32_to_cpu(pkt->u.stats.tx.actual_ack_cnt) - | ||
641 | le32_to_cpu(priv->statistics.tx.actual_ack_cnt); | ||
642 | expected_ack_cnt_delta = | ||
643 | le32_to_cpu(pkt->u.stats.tx.expected_ack_cnt) - | ||
644 | le32_to_cpu(priv->statistics.tx.expected_ack_cnt); | ||
645 | ba_timeout_delta = | ||
646 | le32_to_cpu(pkt->u.stats.tx.agg.ba_timeout) - | ||
647 | le32_to_cpu(priv->statistics.tx.agg.ba_timeout); | ||
648 | if ((priv->_agn.agg_tids_count > 0) && | ||
649 | (expected_ack_cnt_delta > 0) && | ||
650 | (((actual_ack_cnt_delta * 100) / expected_ack_cnt_delta) | ||
651 | < ACK_CNT_RATIO) && | ||
652 | (ba_timeout_delta > BA_TIMEOUT_CNT)) { | ||
653 | IWL_DEBUG_RADIO(priv, "actual_ack_cnt delta = %d," | ||
654 | " expected_ack_cnt = %d\n", | ||
655 | actual_ack_cnt_delta, expected_ack_cnt_delta); | ||
656 | |||
657 | #ifdef CONFIG_IWLWIFI_DEBUG | ||
658 | IWL_DEBUG_RADIO(priv, "rx_detected_cnt delta = %d\n", | ||
659 | priv->delta_statistics.tx.rx_detected_cnt); | ||
660 | IWL_DEBUG_RADIO(priv, | ||
661 | "ack_or_ba_timeout_collision delta = %d\n", | ||
662 | priv->delta_statistics.tx. | ||
663 | ack_or_ba_timeout_collision); | ||
664 | #endif | ||
665 | IWL_DEBUG_RADIO(priv, "agg ba_timeout delta = %d\n", | ||
666 | ba_timeout_delta); | ||
667 | if (!actual_ack_cnt_delta && | ||
668 | (ba_timeout_delta >= BA_TIMEOUT_MAX)) | ||
669 | rc = false; | ||
670 | } | ||
671 | return rc; | ||
672 | } | ||
673 | EXPORT_SYMBOL(iwl_good_ack_health); | ||
674 | #endif | ||
675 | |||
676 | /** | ||
677 | * iwl_good_plcp_health - checks for plcp error. | ||
678 | * | ||
679 | * When the plcp error is exceeding the thresholds, reset the radio | ||
680 | * to improve the throughput. | ||
681 | */ | ||
682 | bool iwl_good_plcp_health(struct iwl_priv *priv, | ||
683 | struct iwl_rx_packet *pkt) | ||
684 | { | ||
685 | bool rc = true; | ||
625 | int combined_plcp_delta; | 686 | int combined_plcp_delta; |
626 | unsigned int plcp_msec; | 687 | unsigned int plcp_msec; |
627 | unsigned long plcp_received_jiffies; | 688 | unsigned long plcp_received_jiffies; |
628 | 689 | ||
629 | IWL_DEBUG_RX(priv, "Statistics notification received (%d vs %d).\n", | ||
630 | (int)sizeof(priv->statistics), | ||
631 | le32_to_cpu(pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK); | ||
632 | |||
633 | change = ((priv->statistics.general.temperature != | ||
634 | pkt->u.stats.general.temperature) || | ||
635 | ((priv->statistics.flag & | ||
636 | STATISTICS_REPLY_FLG_HT40_MODE_MSK) != | ||
637 | (pkt->u.stats.flag & STATISTICS_REPLY_FLG_HT40_MODE_MSK))); | ||
638 | |||
639 | #ifdef CONFIG_IWLWIFI_DEBUG | ||
640 | iwl_accumulative_statistics(priv, (__le32 *)&pkt->u.stats); | ||
641 | #endif | ||
642 | /* | 690 | /* |
643 | * check for plcp_err and trigger radio reset if it exceeds | 691 | * check for plcp_err and trigger radio reset if it exceeds |
644 | * the plcp error threshold plcp_delta. | 692 | * the plcp error threshold plcp_delta. |
@@ -659,11 +707,11 @@ void iwl_rx_statistics(struct iwl_priv *priv, | |||
659 | le32_to_cpu(priv->statistics.rx.ofdm_ht.plcp_err)); | 707 | le32_to_cpu(priv->statistics.rx.ofdm_ht.plcp_err)); |
660 | 708 | ||
661 | if ((combined_plcp_delta > 0) && | 709 | if ((combined_plcp_delta > 0) && |
662 | ((combined_plcp_delta * 100) / plcp_msec) > | 710 | ((combined_plcp_delta * 100) / plcp_msec) > |
663 | priv->cfg->plcp_delta_threshold) { | 711 | priv->cfg->plcp_delta_threshold) { |
664 | /* | 712 | /* |
665 | * if plcp_err exceed the threshold, the following | 713 | * if plcp_err exceed the threshold, |
666 | * data is printed in csv format: | 714 | * the following data is printed in csv format: |
667 | * Text: plcp_err exceeded %d, | 715 | * Text: plcp_err exceeded %d, |
668 | * Received ofdm.plcp_err, | 716 | * Received ofdm.plcp_err, |
669 | * Current ofdm.plcp_err, | 717 | * Current ofdm.plcp_err, |
@@ -672,22 +720,73 @@ void iwl_rx_statistics(struct iwl_priv *priv, | |||
672 | * combined_plcp_delta, | 720 | * combined_plcp_delta, |
673 | * plcp_msec | 721 | * plcp_msec |
674 | */ | 722 | */ |
675 | IWL_DEBUG_RADIO(priv, PLCP_MSG, | 723 | IWL_DEBUG_RADIO(priv, "plcp_err exceeded %u, " |
724 | "%u, %u, %u, %u, %d, %u mSecs\n", | ||
676 | priv->cfg->plcp_delta_threshold, | 725 | priv->cfg->plcp_delta_threshold, |
677 | le32_to_cpu(pkt->u.stats.rx.ofdm.plcp_err), | 726 | le32_to_cpu(pkt->u.stats.rx.ofdm.plcp_err), |
678 | le32_to_cpu(priv->statistics.rx.ofdm.plcp_err), | 727 | le32_to_cpu(priv->statistics.rx.ofdm.plcp_err), |
679 | le32_to_cpu(pkt->u.stats.rx.ofdm_ht.plcp_err), | 728 | le32_to_cpu(pkt->u.stats.rx.ofdm_ht.plcp_err), |
680 | le32_to_cpu( | 729 | le32_to_cpu( |
681 | priv->statistics.rx.ofdm_ht.plcp_err), | 730 | priv->statistics.rx.ofdm_ht.plcp_err), |
682 | combined_plcp_delta, plcp_msec); | 731 | combined_plcp_delta, plcp_msec); |
732 | rc = false; | ||
733 | } | ||
734 | } | ||
735 | return rc; | ||
736 | } | ||
737 | EXPORT_SYMBOL(iwl_good_plcp_health); | ||
683 | 738 | ||
684 | /* | 739 | static void iwl_recover_from_statistics(struct iwl_priv *priv, |
685 | * Reset the RF radio due to the high plcp | 740 | struct iwl_rx_packet *pkt) |
686 | * error rate | 741 | { |
687 | */ | 742 | if (test_bit(STATUS_EXIT_PENDING, &priv->status)) |
688 | iwl_force_reset(priv, IWL_RF_RESET); | 743 | return; |
744 | if (iwl_is_associated(priv)) { | ||
745 | if (priv->cfg->ops->lib->check_ack_health) { | ||
746 | if (!priv->cfg->ops->lib->check_ack_health( | ||
747 | priv, pkt)) { | ||
748 | /* | ||
749 | * low ack count detected | ||
750 | * restart Firmware | ||
751 | */ | ||
752 | IWL_ERR(priv, "low ack count detected, " | ||
753 | "restart firmware\n"); | ||
754 | iwl_force_reset(priv, IWL_FW_RESET); | ||
755 | } | ||
756 | } else if (priv->cfg->ops->lib->check_plcp_health) { | ||
757 | if (!priv->cfg->ops->lib->check_plcp_health( | ||
758 | priv, pkt)) { | ||
759 | /* | ||
760 | * high plcp error detected | ||
761 | * reset Radio | ||
762 | */ | ||
763 | iwl_force_reset(priv, IWL_RF_RESET); | ||
764 | } | ||
689 | } | 765 | } |
690 | } | 766 | } |
767 | } | ||
768 | |||
769 | void iwl_rx_statistics(struct iwl_priv *priv, | ||
770 | struct iwl_rx_mem_buffer *rxb) | ||
771 | { | ||
772 | int change; | ||
773 | struct iwl_rx_packet *pkt = rxb_addr(rxb); | ||
774 | |||
775 | |||
776 | IWL_DEBUG_RX(priv, "Statistics notification received (%d vs %d).\n", | ||
777 | (int)sizeof(priv->statistics), | ||
778 | le32_to_cpu(pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK); | ||
779 | |||
780 | change = ((priv->statistics.general.temperature != | ||
781 | pkt->u.stats.general.temperature) || | ||
782 | ((priv->statistics.flag & | ||
783 | STATISTICS_REPLY_FLG_HT40_MODE_MSK) != | ||
784 | (pkt->u.stats.flag & STATISTICS_REPLY_FLG_HT40_MODE_MSK))); | ||
785 | |||
786 | #ifdef CONFIG_IWLWIFI_DEBUG | ||
787 | iwl_accumulative_statistics(priv, (__le32 *)&pkt->u.stats); | ||
788 | #endif | ||
789 | iwl_recover_from_statistics(priv, pkt); | ||
691 | 790 | ||
692 | memcpy(&priv->statistics, &pkt->u.stats, sizeof(priv->statistics)); | 791 | memcpy(&priv->statistics, &pkt->u.stats, sizeof(priv->statistics)); |
693 | 792 | ||