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/net/wireless/ath9k/main.c | |
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/net/wireless/ath9k/main.c')
-rw-r--r-- | drivers/net/wireless/ath9k/main.c | 266 |
1 files changed, 266 insertions, 0 deletions
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 | ||