diff options
author | Vasanthakumar Thiagarajan <vasanth@atheros.com> | 2008-09-10 09:20:17 -0400 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2008-09-15 16:48:19 -0400 |
commit | 500c064d3a5f9c8aa604ef63a1346ab70eed443a (patch) | |
tree | d7345fa9f894bcbc21e80e886cc3f32589469bad /drivers | |
parent | 8feceb67929bd23bfca58d5f49df93d7fc315bb1 (diff) |
ath9k: Add RF kill support
RF kill support is enabled when CONFIG_RFKILL
is set.
Signed-off-by: Vasanthakumar Thiagarajan <vasanth@atheros.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/net/wireless/ath9k/ath9k.h | 7 | ||||
-rw-r--r-- | drivers/net/wireless/ath9k/core.h | 16 | ||||
-rw-r--r-- | drivers/net/wireless/ath9k/hw.c | 70 | ||||
-rw-r--r-- | drivers/net/wireless/ath9k/main.c | 266 |
4 files changed, 327 insertions, 32 deletions
diff --git a/drivers/net/wireless/ath9k/ath9k.h b/drivers/net/wireless/ath9k/ath9k.h index 28b8d84f49b4..0e897c276858 100644 --- a/drivers/net/wireless/ath9k/ath9k.h +++ b/drivers/net/wireless/ath9k/ath9k.h | |||
@@ -798,10 +798,11 @@ struct ath_hal { | |||
798 | struct ath9k_channel *ah_curchan; | 798 | struct ath9k_channel *ah_curchan; |
799 | u32 ah_nchan; | 799 | u32 ah_nchan; |
800 | 800 | ||
801 | u16 ah_rfsilent; | ||
802 | bool ah_rfkillEnabled; | ||
803 | bool ah_isPciExpress; | 801 | bool ah_isPciExpress; |
804 | u16 ah_txTrigLevel; | 802 | u16 ah_txTrigLevel; |
803 | u16 ah_rfsilent; | ||
804 | u32 ah_rfkill_gpio; | ||
805 | u32 ah_rfkill_polarity; | ||
805 | 806 | ||
806 | #ifndef ATH_NF_PER_CHAN | 807 | #ifndef ATH_NF_PER_CHAN |
807 | struct ath9k_nfcal_hist nfCalHist[NUM_NF_READINGS]; | 808 | struct ath9k_nfcal_hist nfCalHist[NUM_NF_READINGS]; |
@@ -1003,4 +1004,6 @@ bool ath9k_get_channel_edges(struct ath_hal *ah, | |||
1003 | void ath9k_hw_cfg_output(struct ath_hal *ah, u32 gpio, | 1004 | void ath9k_hw_cfg_output(struct ath_hal *ah, u32 gpio, |
1004 | u32 ah_signal_type); | 1005 | u32 ah_signal_type); |
1005 | void ath9k_hw_set_gpio(struct ath_hal *ah, u32 gpio, u32 value); | 1006 | void ath9k_hw_set_gpio(struct ath_hal *ah, u32 gpio, u32 value); |
1007 | u32 ath9k_hw_gpio_get(struct ath_hal *ah, u32 gpio); | ||
1008 | void ath9k_hw_cfg_gpio_input(struct ath_hal *ah, u32 gpio); | ||
1006 | #endif | 1009 | #endif |
diff --git a/drivers/net/wireless/ath9k/core.h b/drivers/net/wireless/ath9k/core.h index 1faa1effa02c..b66de29cf662 100644 --- a/drivers/net/wireless/ath9k/core.h +++ b/drivers/net/wireless/ath9k/core.h | |||
@@ -40,6 +40,7 @@ | |||
40 | #include <asm/page.h> | 40 | #include <asm/page.h> |
41 | #include <net/mac80211.h> | 41 | #include <net/mac80211.h> |
42 | #include <linux/leds.h> | 42 | #include <linux/leds.h> |
43 | #include <linux/rfkill.h> | ||
43 | 44 | ||
44 | #include "ath9k.h" | 45 | #include "ath9k.h" |
45 | #include "rc.h" | 46 | #include "rc.h" |
@@ -823,6 +824,15 @@ struct ath_led { | |||
823 | bool registered; | 824 | bool registered; |
824 | }; | 825 | }; |
825 | 826 | ||
827 | /* Rfkill */ | ||
828 | #define ATH_RFKILL_POLL_INTERVAL 2000 /* msecs */ | ||
829 | |||
830 | struct ath_rfkill { | ||
831 | struct rfkill *rfkill; | ||
832 | struct delayed_work rfkill_poll; | ||
833 | char rfkill_name[32]; | ||
834 | }; | ||
835 | |||
826 | /********************/ | 836 | /********************/ |
827 | /* Main driver core */ | 837 | /* Main driver core */ |
828 | /********************/ | 838 | /********************/ |
@@ -906,6 +916,9 @@ struct ath_ht_info { | |||
906 | #define SC_OP_PROTECT_ENABLE BIT(8) | 916 | #define SC_OP_PROTECT_ENABLE BIT(8) |
907 | #define SC_OP_RXFLUSH BIT(9) | 917 | #define SC_OP_RXFLUSH BIT(9) |
908 | #define SC_OP_LED_ASSOCIATED BIT(10) | 918 | #define SC_OP_LED_ASSOCIATED BIT(10) |
919 | #define SC_OP_RFKILL_REGISTERED BIT(11) | ||
920 | #define SC_OP_RFKILL_SW_BLOCKED BIT(12) | ||
921 | #define SC_OP_RFKILL_HW_BLOCKED BIT(13) | ||
909 | 922 | ||
910 | struct ath_softc { | 923 | struct ath_softc { |
911 | struct ieee80211_hw *hw; | 924 | struct ieee80211_hw *hw; |
@@ -1015,6 +1028,9 @@ struct ath_softc { | |||
1015 | struct ath_led assoc_led; | 1028 | struct ath_led assoc_led; |
1016 | struct ath_led tx_led; | 1029 | struct ath_led tx_led; |
1017 | struct ath_led rx_led; | 1030 | struct ath_led rx_led; |
1031 | |||
1032 | /* Rfkill */ | ||
1033 | struct ath_rfkill rf_kill; | ||
1018 | }; | 1034 | }; |
1019 | 1035 | ||
1020 | int ath_init(u16 devid, struct ath_softc *sc); | 1036 | int ath_init(u16 devid, struct ath_softc *sc); |
diff --git a/drivers/net/wireless/ath9k/hw.c b/drivers/net/wireless/ath9k/hw.c index 4ccbbc07cf1e..0251e59f2f84 100644 --- a/drivers/net/wireless/ath9k/hw.c +++ b/drivers/net/wireless/ath9k/hw.c | |||
@@ -2821,7 +2821,38 @@ void ath9k_hw_set_gpio(struct ath_hal *ah, u32 gpio, u32 val) | |||
2821 | AR_GPIO_BIT(gpio)); | 2821 | AR_GPIO_BIT(gpio)); |
2822 | } | 2822 | } |
2823 | 2823 | ||
2824 | static u32 ath9k_hw_gpio_get(struct ath_hal *ah, u32 gpio) | 2824 | /* |
2825 | * Configure GPIO Input lines | ||
2826 | */ | ||
2827 | void ath9k_hw_cfg_gpio_input(struct ath_hal *ah, u32 gpio) | ||
2828 | { | ||
2829 | u32 gpio_shift; | ||
2830 | |||
2831 | ASSERT(gpio < ah->ah_caps.num_gpio_pins); | ||
2832 | |||
2833 | gpio_shift = gpio << 1; | ||
2834 | |||
2835 | REG_RMW(ah, | ||
2836 | AR_GPIO_OE_OUT, | ||
2837 | (AR_GPIO_OE_OUT_DRV_NO << gpio_shift), | ||
2838 | (AR_GPIO_OE_OUT_DRV << gpio_shift)); | ||
2839 | } | ||
2840 | |||
2841 | #ifdef CONFIG_RFKILL | ||
2842 | static void ath9k_enable_rfkill(struct ath_hal *ah) | ||
2843 | { | ||
2844 | REG_SET_BIT(ah, AR_GPIO_INPUT_EN_VAL, | ||
2845 | AR_GPIO_INPUT_EN_VAL_RFSILENT_BB); | ||
2846 | |||
2847 | REG_CLR_BIT(ah, AR_GPIO_INPUT_MUX2, | ||
2848 | AR_GPIO_INPUT_MUX2_RFSILENT); | ||
2849 | |||
2850 | ath9k_hw_cfg_gpio_input(ah, ah->ah_rfkill_gpio); | ||
2851 | REG_SET_BIT(ah, AR_PHY_TEST, RFSILENT_BB); | ||
2852 | } | ||
2853 | #endif | ||
2854 | |||
2855 | u32 ath9k_hw_gpio_get(struct ath_hal *ah, u32 gpio) | ||
2825 | { | 2856 | { |
2826 | if (gpio >= ah->ah_caps.num_gpio_pins) | 2857 | if (gpio >= ah->ah_caps.num_gpio_pins) |
2827 | return 0xffffffff; | 2858 | return 0xffffffff; |
@@ -3034,17 +3065,17 @@ static bool ath9k_hw_fill_cap_info(struct ath_hal *ah) | |||
3034 | 3065 | ||
3035 | pCap->hw_caps |= ATH9K_HW_CAP_ENHANCEDPM; | 3066 | pCap->hw_caps |= ATH9K_HW_CAP_ENHANCEDPM; |
3036 | 3067 | ||
3068 | #ifdef CONFIG_RFKILL | ||
3037 | ah->ah_rfsilent = ath9k_hw_get_eeprom(ahp, EEP_RF_SILENT); | 3069 | ah->ah_rfsilent = ath9k_hw_get_eeprom(ahp, EEP_RF_SILENT); |
3038 | if (ah->ah_rfsilent & EEP_RFSILENT_ENABLED) { | 3070 | if (ah->ah_rfsilent & EEP_RFSILENT_ENABLED) { |
3039 | ahp->ah_gpioSelect = | 3071 | ah->ah_rfkill_gpio = |
3040 | MS(ah->ah_rfsilent, EEP_RFSILENT_GPIO_SEL); | 3072 | MS(ah->ah_rfsilent, EEP_RFSILENT_GPIO_SEL); |
3041 | ahp->ah_polarity = | 3073 | ah->ah_rfkill_polarity = |
3042 | MS(ah->ah_rfsilent, EEP_RFSILENT_POLARITY); | 3074 | MS(ah->ah_rfsilent, EEP_RFSILENT_POLARITY); |
3043 | 3075 | ||
3044 | ath9k_hw_setcapability(ah, ATH9K_CAP_RFSILENT, 1, true, | ||
3045 | NULL); | ||
3046 | pCap->hw_caps |= ATH9K_HW_CAP_RFSILENT; | 3076 | pCap->hw_caps |= ATH9K_HW_CAP_RFSILENT; |
3047 | } | 3077 | } |
3078 | #endif | ||
3048 | 3079 | ||
3049 | if ((ah->ah_macVersion == AR_SREV_VERSION_5416_PCI) || | 3080 | if ((ah->ah_macVersion == AR_SREV_VERSION_5416_PCI) || |
3050 | (ah->ah_macVersion == AR_SREV_VERSION_5416_PCIE) || | 3081 | (ah->ah_macVersion == AR_SREV_VERSION_5416_PCIE) || |
@@ -5961,6 +5992,10 @@ bool ath9k_hw_reset(struct ath_hal *ah, | |||
5961 | ath9k_hw_init_interrupt_masks(ah, ah->ah_opmode); | 5992 | ath9k_hw_init_interrupt_masks(ah, ah->ah_opmode); |
5962 | ath9k_hw_init_qos(ah); | 5993 | ath9k_hw_init_qos(ah); |
5963 | 5994 | ||
5995 | #ifdef CONFIG_RFKILL | ||
5996 | if (ah->ah_caps.hw_caps & ATH9K_HW_CAP_RFSILENT) | ||
5997 | ath9k_enable_rfkill(ah); | ||
5998 | #endif | ||
5964 | ath9k_hw_init_user_settings(ah); | 5999 | ath9k_hw_init_user_settings(ah); |
5965 | 6000 | ||
5966 | REG_WRITE(ah, AR_STA_ID1, | 6001 | REG_WRITE(ah, AR_STA_ID1, |
@@ -6490,31 +6525,6 @@ ath9k_hw_setbssidmask(struct ath_hal *ah, const u8 *mask) | |||
6490 | return true; | 6525 | return true; |
6491 | } | 6526 | } |
6492 | 6527 | ||
6493 | #ifdef CONFIG_ATH9K_RFKILL | ||
6494 | static void ath9k_enable_rfkill(struct ath_hal *ah) | ||
6495 | { | ||
6496 | struct ath_hal_5416 *ahp = AH5416(ah); | ||
6497 | |||
6498 | REG_SET_BIT(ah, AR_GPIO_INPUT_EN_VAL, | ||
6499 | AR_GPIO_INPUT_EN_VAL_RFSILENT_BB); | ||
6500 | |||
6501 | REG_CLR_BIT(ah, AR_GPIO_INPUT_MUX2, | ||
6502 | AR_GPIO_INPUT_MUX2_RFSILENT); | ||
6503 | |||
6504 | ath9k_hw_cfg_gpio_input(ah, ahp->ah_gpioSelect); | ||
6505 | REG_SET_BIT(ah, AR_PHY_TEST, RFSILENT_BB); | ||
6506 | |||
6507 | if (ahp->ah_gpioBit == ath9k_hw_gpio_get(ah, ahp->ah_gpioSelect)) { | ||
6508 | |||
6509 | ath9k_hw_set_gpio_intr(ah, ahp->ah_gpioSelect, | ||
6510 | !ahp->ah_gpioBit); | ||
6511 | } else { | ||
6512 | ath9k_hw_set_gpio_intr(ah, ahp->ah_gpioSelect, | ||
6513 | ahp->ah_gpioBit); | ||
6514 | } | ||
6515 | } | ||
6516 | #endif | ||
6517 | |||
6518 | void | 6528 | void |
6519 | ath9k_hw_write_associd(struct ath_hal *ah, const u8 *bssid, | 6529 | ath9k_hw_write_associd(struct ath_hal *ah, const u8 *bssid, |
6520 | u16 assocId) | 6530 | u16 assocId) |
diff --git a/drivers/net/wireless/ath9k/main.c b/drivers/net/wireless/ath9k/main.c index 07e5b5d877b6..b493dff5643e 100644 --- a/drivers/net/wireless/ath9k/main.c +++ b/drivers/net/wireless/ath9k/main.c | |||
@@ -672,6 +672,209 @@ fail: | |||
672 | ath_deinit_leds(sc); | 672 | ath_deinit_leds(sc); |
673 | } | 673 | } |
674 | 674 | ||
675 | #ifdef CONFIG_RFKILL | ||
676 | /*******************/ | ||
677 | /* Rfkill */ | ||
678 | /*******************/ | ||
679 | |||
680 | static void ath_radio_enable(struct ath_softc *sc) | ||
681 | { | ||
682 | struct ath_hal *ah = sc->sc_ah; | ||
683 | int status; | ||
684 | |||
685 | spin_lock_bh(&sc->sc_resetlock); | ||
686 | if (!ath9k_hw_reset(ah, ah->ah_curchan, | ||
687 | sc->sc_ht_info.tx_chan_width, | ||
688 | sc->sc_tx_chainmask, | ||
689 | sc->sc_rx_chainmask, | ||
690 | sc->sc_ht_extprotspacing, | ||
691 | false, &status)) { | ||
692 | DPRINTF(sc, ATH_DBG_FATAL, | ||
693 | "%s: unable to reset channel %u (%uMhz) " | ||
694 | "flags 0x%x hal status %u\n", __func__, | ||
695 | ath9k_hw_mhz2ieee(ah, | ||
696 | ah->ah_curchan->channel, | ||
697 | ah->ah_curchan->channelFlags), | ||
698 | ah->ah_curchan->channel, | ||
699 | ah->ah_curchan->channelFlags, status); | ||
700 | } | ||
701 | spin_unlock_bh(&sc->sc_resetlock); | ||
702 | |||
703 | ath_update_txpow(sc); | ||
704 | if (ath_startrecv(sc) != 0) { | ||
705 | DPRINTF(sc, ATH_DBG_FATAL, | ||
706 | "%s: unable to restart recv logic\n", __func__); | ||
707 | return; | ||
708 | } | ||
709 | |||
710 | if (sc->sc_flags & SC_OP_BEACONS) | ||
711 | ath_beacon_config(sc, ATH_IF_ID_ANY); /* restart beacons */ | ||
712 | |||
713 | /* Re-Enable interrupts */ | ||
714 | ath9k_hw_set_interrupts(ah, sc->sc_imask); | ||
715 | |||
716 | /* Enable LED */ | ||
717 | ath9k_hw_cfg_output(ah, ATH_LED_PIN, | ||
718 | AR_GPIO_OUTPUT_MUX_AS_OUTPUT); | ||
719 | ath9k_hw_set_gpio(ah, ATH_LED_PIN, 0); | ||
720 | |||
721 | ieee80211_wake_queues(sc->hw); | ||
722 | } | ||
723 | |||
724 | static void ath_radio_disable(struct ath_softc *sc) | ||
725 | { | ||
726 | struct ath_hal *ah = sc->sc_ah; | ||
727 | int status; | ||
728 | |||
729 | |||
730 | ieee80211_stop_queues(sc->hw); | ||
731 | |||
732 | /* Disable LED */ | ||
733 | ath9k_hw_set_gpio(ah, ATH_LED_PIN, 1); | ||
734 | ath9k_hw_cfg_gpio_input(ah, ATH_LED_PIN); | ||
735 | |||
736 | /* Disable interrupts */ | ||
737 | ath9k_hw_set_interrupts(ah, 0); | ||
738 | |||
739 | ath_draintxq(sc, false); /* clear pending tx frames */ | ||
740 | ath_stoprecv(sc); /* turn off frame recv */ | ||
741 | ath_flushrecv(sc); /* flush recv queue */ | ||
742 | |||
743 | spin_lock_bh(&sc->sc_resetlock); | ||
744 | if (!ath9k_hw_reset(ah, ah->ah_curchan, | ||
745 | sc->sc_ht_info.tx_chan_width, | ||
746 | sc->sc_tx_chainmask, | ||
747 | sc->sc_rx_chainmask, | ||
748 | sc->sc_ht_extprotspacing, | ||
749 | false, &status)) { | ||
750 | DPRINTF(sc, ATH_DBG_FATAL, | ||
751 | "%s: unable to reset channel %u (%uMhz) " | ||
752 | "flags 0x%x hal status %u\n", __func__, | ||
753 | ath9k_hw_mhz2ieee(ah, | ||
754 | ah->ah_curchan->channel, | ||
755 | ah->ah_curchan->channelFlags), | ||
756 | ah->ah_curchan->channel, | ||
757 | ah->ah_curchan->channelFlags, status); | ||
758 | } | ||
759 | spin_unlock_bh(&sc->sc_resetlock); | ||
760 | |||
761 | ath9k_hw_phy_disable(ah); | ||
762 | ath9k_hw_setpower(ah, ATH9K_PM_FULL_SLEEP); | ||
763 | } | ||
764 | |||
765 | static bool ath_is_rfkill_set(struct ath_softc *sc) | ||
766 | { | ||
767 | struct ath_hal *ah = sc->sc_ah; | ||
768 | |||
769 | return ath9k_hw_gpio_get(ah, ah->ah_rfkill_gpio) == | ||
770 | ah->ah_rfkill_polarity; | ||
771 | } | ||
772 | |||
773 | /* h/w rfkill poll function */ | ||
774 | static void ath_rfkill_poll(struct work_struct *work) | ||
775 | { | ||
776 | struct ath_softc *sc = container_of(work, struct ath_softc, | ||
777 | rf_kill.rfkill_poll.work); | ||
778 | bool radio_on; | ||
779 | |||
780 | if (sc->sc_flags & SC_OP_INVALID) | ||
781 | return; | ||
782 | |||
783 | radio_on = !ath_is_rfkill_set(sc); | ||
784 | |||
785 | /* | ||
786 | * enable/disable radio only when there is a | ||
787 | * state change in RF switch | ||
788 | */ | ||
789 | if (radio_on == !!(sc->sc_flags & SC_OP_RFKILL_HW_BLOCKED)) { | ||
790 | enum rfkill_state state; | ||
791 | |||
792 | if (sc->sc_flags & SC_OP_RFKILL_SW_BLOCKED) { | ||
793 | state = radio_on ? RFKILL_STATE_SOFT_BLOCKED | ||
794 | : RFKILL_STATE_HARD_BLOCKED; | ||
795 | } else if (radio_on) { | ||
796 | ath_radio_enable(sc); | ||
797 | state = RFKILL_STATE_UNBLOCKED; | ||
798 | } else { | ||
799 | ath_radio_disable(sc); | ||
800 | state = RFKILL_STATE_HARD_BLOCKED; | ||
801 | } | ||
802 | |||
803 | if (state == RFKILL_STATE_HARD_BLOCKED) | ||
804 | sc->sc_flags |= SC_OP_RFKILL_HW_BLOCKED; | ||
805 | else | ||
806 | sc->sc_flags &= ~SC_OP_RFKILL_HW_BLOCKED; | ||
807 | |||
808 | rfkill_force_state(sc->rf_kill.rfkill, state); | ||
809 | } | ||
810 | |||
811 | queue_delayed_work(sc->hw->workqueue, &sc->rf_kill.rfkill_poll, | ||
812 | msecs_to_jiffies(ATH_RFKILL_POLL_INTERVAL)); | ||
813 | } | ||
814 | |||
815 | /* s/w rfkill handler */ | ||
816 | static int ath_sw_toggle_radio(void *data, enum rfkill_state state) | ||
817 | { | ||
818 | struct ath_softc *sc = data; | ||
819 | |||
820 | switch (state) { | ||
821 | case RFKILL_STATE_SOFT_BLOCKED: | ||
822 | if (!(sc->sc_flags & (SC_OP_RFKILL_HW_BLOCKED | | ||
823 | SC_OP_RFKILL_SW_BLOCKED))) | ||
824 | ath_radio_disable(sc); | ||
825 | sc->sc_flags |= SC_OP_RFKILL_SW_BLOCKED; | ||
826 | return 0; | ||
827 | case RFKILL_STATE_UNBLOCKED: | ||
828 | if ((sc->sc_flags & SC_OP_RFKILL_SW_BLOCKED)) { | ||
829 | sc->sc_flags &= ~SC_OP_RFKILL_SW_BLOCKED; | ||
830 | if (sc->sc_flags & SC_OP_RFKILL_HW_BLOCKED) { | ||
831 | DPRINTF(sc, ATH_DBG_FATAL, "Can't turn on the" | ||
832 | "radio as it is disabled by h/w \n"); | ||
833 | return -EPERM; | ||
834 | } | ||
835 | ath_radio_enable(sc); | ||
836 | } | ||
837 | return 0; | ||
838 | default: | ||
839 | return -EINVAL; | ||
840 | } | ||
841 | } | ||
842 | |||
843 | /* Init s/w rfkill */ | ||
844 | static int ath_init_sw_rfkill(struct ath_softc *sc) | ||
845 | { | ||
846 | sc->rf_kill.rfkill = rfkill_allocate(wiphy_dev(sc->hw->wiphy), | ||
847 | RFKILL_TYPE_WLAN); | ||
848 | if (!sc->rf_kill.rfkill) { | ||
849 | DPRINTF(sc, ATH_DBG_FATAL, "Failed to allocate rfkill\n"); | ||
850 | return -ENOMEM; | ||
851 | } | ||
852 | |||
853 | snprintf(sc->rf_kill.rfkill_name, sizeof(sc->rf_kill.rfkill_name), | ||
854 | "ath9k-%s:rfkill", wiphy_name(sc->hw->wiphy)); | ||
855 | sc->rf_kill.rfkill->name = sc->rf_kill.rfkill_name; | ||
856 | sc->rf_kill.rfkill->data = sc; | ||
857 | sc->rf_kill.rfkill->toggle_radio = ath_sw_toggle_radio; | ||
858 | sc->rf_kill.rfkill->state = RFKILL_STATE_UNBLOCKED; | ||
859 | sc->rf_kill.rfkill->user_claim_unsupported = 1; | ||
860 | |||
861 | return 0; | ||
862 | } | ||
863 | |||
864 | /* Deinitialize rfkill */ | ||
865 | static void ath_deinit_rfkill(struct ath_softc *sc) | ||
866 | { | ||
867 | if (sc->sc_ah->ah_caps.hw_caps & ATH9K_HW_CAP_RFSILENT) | ||
868 | cancel_delayed_work_sync(&sc->rf_kill.rfkill_poll); | ||
869 | |||
870 | if (sc->sc_flags & SC_OP_RFKILL_REGISTERED) { | ||
871 | rfkill_unregister(sc->rf_kill.rfkill); | ||
872 | sc->sc_flags &= ~SC_OP_RFKILL_REGISTERED; | ||
873 | sc->rf_kill.rfkill = NULL; | ||
874 | } | ||
875 | } | ||
876 | #endif /* CONFIG_RFKILL */ | ||
877 | |||
675 | static int ath_detach(struct ath_softc *sc) | 878 | static int ath_detach(struct ath_softc *sc) |
676 | { | 879 | { |
677 | struct ieee80211_hw *hw = sc->hw; | 880 | struct ieee80211_hw *hw = sc->hw; |
@@ -681,6 +884,11 @@ static int ath_detach(struct ath_softc *sc) | |||
681 | /* Deinit LED control */ | 884 | /* Deinit LED control */ |
682 | ath_deinit_leds(sc); | 885 | ath_deinit_leds(sc); |
683 | 886 | ||
887 | #ifdef CONFIG_RFKILL | ||
888 | /* deinit rfkill */ | ||
889 | ath_deinit_rfkill(sc); | ||
890 | #endif | ||
891 | |||
684 | /* Unregister hw */ | 892 | /* Unregister hw */ |
685 | 893 | ||
686 | ieee80211_unregister_hw(hw); | 894 | ieee80211_unregister_hw(hw); |
@@ -777,6 +985,16 @@ static int ath_attach(u16 devid, | |||
777 | /* Initialize LED control */ | 985 | /* Initialize LED control */ |
778 | ath_init_leds(sc); | 986 | ath_init_leds(sc); |
779 | 987 | ||
988 | #ifdef CONFIG_RFKILL | ||
989 | /* Initialze h/w Rfkill */ | ||
990 | if (sc->sc_ah->ah_caps.hw_caps & ATH9K_HW_CAP_RFSILENT) | ||
991 | INIT_DELAYED_WORK(&sc->rf_kill.rfkill_poll, ath_rfkill_poll); | ||
992 | |||
993 | /* Initialize s/w rfkill */ | ||
994 | if (ath_init_sw_rfkill(sc)) | ||
995 | goto detach; | ||
996 | #endif | ||
997 | |||
780 | /* initialize tx/rx engine */ | 998 | /* initialize tx/rx engine */ |
781 | 999 | ||
782 | error = ath_tx_init(sc, ATH_TXBUF); | 1000 | error = ath_tx_init(sc, ATH_TXBUF); |
@@ -822,6 +1040,33 @@ static int ath9k_start(struct ieee80211_hw *hw) | |||
822 | return error; | 1040 | return error; |
823 | } | 1041 | } |
824 | 1042 | ||
1043 | #ifdef CONFIG_RFKILL | ||
1044 | /* Start rfkill polling */ | ||
1045 | if (sc->sc_ah->ah_caps.hw_caps & ATH9K_HW_CAP_RFSILENT) | ||
1046 | queue_delayed_work(sc->hw->workqueue, | ||
1047 | &sc->rf_kill.rfkill_poll, 0); | ||
1048 | |||
1049 | if (!(sc->sc_flags & SC_OP_RFKILL_REGISTERED)) { | ||
1050 | if (rfkill_register(sc->rf_kill.rfkill)) { | ||
1051 | DPRINTF(sc, ATH_DBG_FATAL, | ||
1052 | "Unable to register rfkill\n"); | ||
1053 | rfkill_free(sc->rf_kill.rfkill); | ||
1054 | |||
1055 | /* Deinitialize the device */ | ||
1056 | if (sc->pdev->irq) | ||
1057 | free_irq(sc->pdev->irq, sc); | ||
1058 | ath_detach(sc); | ||
1059 | pci_iounmap(sc->pdev, sc->mem); | ||
1060 | pci_release_region(sc->pdev, 0); | ||
1061 | pci_disable_device(sc->pdev); | ||
1062 | ieee80211_free_hw(hw); | ||
1063 | return -EIO; | ||
1064 | } else { | ||
1065 | sc->sc_flags |= SC_OP_RFKILL_REGISTERED; | ||
1066 | } | ||
1067 | } | ||
1068 | #endif | ||
1069 | |||
825 | ieee80211_wake_queues(hw); | 1070 | ieee80211_wake_queues(hw); |
826 | return 0; | 1071 | return 0; |
827 | } | 1072 | } |
@@ -883,6 +1128,11 @@ static void ath9k_stop(struct ieee80211_hw *hw) | |||
883 | "%s: Device is no longer present\n", __func__); | 1128 | "%s: Device is no longer present\n", __func__); |
884 | 1129 | ||
885 | ieee80211_stop_queues(hw); | 1130 | ieee80211_stop_queues(hw); |
1131 | |||
1132 | #ifdef CONFIG_RFKILL | ||
1133 | if (sc->sc_ah->ah_caps.hw_caps & ATH9K_HW_CAP_RFSILENT) | ||
1134 | cancel_delayed_work_sync(&sc->rf_kill.rfkill_poll); | ||
1135 | #endif | ||
886 | } | 1136 | } |
887 | 1137 | ||
888 | static int ath9k_add_interface(struct ieee80211_hw *hw, | 1138 | static int ath9k_add_interface(struct ieee80211_hw *hw, |
@@ -1554,6 +1804,12 @@ static int ath_pci_suspend(struct pci_dev *pdev, pm_message_t state) | |||
1554 | struct ath_softc *sc = hw->priv; | 1804 | struct ath_softc *sc = hw->priv; |
1555 | 1805 | ||
1556 | ath9k_hw_set_gpio(sc->sc_ah, ATH_LED_PIN, 1); | 1806 | ath9k_hw_set_gpio(sc->sc_ah, ATH_LED_PIN, 1); |
1807 | |||
1808 | #ifdef CONFIG_RFKILL | ||
1809 | if (sc->sc_ah->ah_caps.hw_caps & ATH9K_HW_CAP_RFSILENT) | ||
1810 | cancel_delayed_work_sync(&sc->rf_kill.rfkill_poll); | ||
1811 | #endif | ||
1812 | |||
1557 | pci_save_state(pdev); | 1813 | pci_save_state(pdev); |
1558 | pci_disable_device(pdev); | 1814 | pci_disable_device(pdev); |
1559 | pci_set_power_state(pdev, 3); | 1815 | pci_set_power_state(pdev, 3); |
@@ -1586,6 +1842,16 @@ static int ath_pci_resume(struct pci_dev *pdev) | |||
1586 | AR_GPIO_OUTPUT_MUX_AS_OUTPUT); | 1842 | AR_GPIO_OUTPUT_MUX_AS_OUTPUT); |
1587 | ath9k_hw_set_gpio(sc->sc_ah, ATH_LED_PIN, 1); | 1843 | ath9k_hw_set_gpio(sc->sc_ah, ATH_LED_PIN, 1); |
1588 | 1844 | ||
1845 | #ifdef CONFIG_RFKILL | ||
1846 | /* | ||
1847 | * check the h/w rfkill state on resume | ||
1848 | * and start the rfkill poll timer | ||
1849 | */ | ||
1850 | if (sc->sc_ah->ah_caps.hw_caps & ATH9K_HW_CAP_RFSILENT) | ||
1851 | queue_delayed_work(sc->hw->workqueue, | ||
1852 | &sc->rf_kill.rfkill_poll, 0); | ||
1853 | #endif | ||
1854 | |||
1589 | return 0; | 1855 | return 0; |
1590 | } | 1856 | } |
1591 | 1857 | ||