diff options
author | David S. Miller <davem@davemloft.net> | 2008-11-10 16:24:44 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2008-11-10 16:24:44 -0500 |
commit | 23779897546c1effb546ff89b89803d9d955d517 (patch) | |
tree | d4b5d52b5d716a72755ba018382d4b87eae763a4 /drivers/net/wireless/ath9k/main.c | |
parent | f574179b63e48f5285468b5ee40f3c480221f708 (diff) | |
parent | c4832467a5c8c2ae96d6dad882be4d4ab9eefad7 (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/ath9k/main.c')
-rw-r--r-- | drivers/net/wireless/ath9k/main.c | 437 |
1 files changed, 135 insertions, 302 deletions
diff --git a/drivers/net/wireless/ath9k/main.c b/drivers/net/wireless/ath9k/main.c index f6dc4c826044..fb50aa0fc996 100644 --- a/drivers/net/wireless/ath9k/main.c +++ b/drivers/net/wireless/ath9k/main.c | |||
@@ -21,8 +21,6 @@ | |||
21 | 21 | ||
22 | #define ATH_PCI_VERSION "0.1" | 22 | #define ATH_PCI_VERSION "0.1" |
23 | 23 | ||
24 | #define IEEE80211_HTCAP_MAXRXAMPDU_FACTOR 13 | ||
25 | |||
26 | static char *dev_info = "ath9k"; | 24 | static char *dev_info = "ath9k"; |
27 | 25 | ||
28 | MODULE_AUTHOR("Atheros Communications"); | 26 | MODULE_AUTHOR("Atheros Communications"); |
@@ -164,7 +162,7 @@ static int ath_key_config(struct ath_softc *sc, | |||
164 | if (!sc->sc_vaps[0]) | 162 | if (!sc->sc_vaps[0]) |
165 | return -EIO; | 163 | return -EIO; |
166 | 164 | ||
167 | vif = sc->sc_vaps[0]->av_if_data; | 165 | vif = sc->sc_vaps[0]; |
168 | opmode = vif->type; | 166 | opmode = vif->type; |
169 | 167 | ||
170 | /* | 168 | /* |
@@ -297,41 +295,6 @@ static void ath9k_rx_prepare(struct ath_softc *sc, | |||
297 | rx_status->flag |= RX_FLAG_TSFT; | 295 | rx_status->flag |= RX_FLAG_TSFT; |
298 | } | 296 | } |
299 | 297 | ||
300 | static u8 parse_mpdudensity(u8 mpdudensity) | ||
301 | { | ||
302 | /* | ||
303 | * 802.11n D2.0 defined values for "Minimum MPDU Start Spacing": | ||
304 | * 0 for no restriction | ||
305 | * 1 for 1/4 us | ||
306 | * 2 for 1/2 us | ||
307 | * 3 for 1 us | ||
308 | * 4 for 2 us | ||
309 | * 5 for 4 us | ||
310 | * 6 for 8 us | ||
311 | * 7 for 16 us | ||
312 | */ | ||
313 | switch (mpdudensity) { | ||
314 | case 0: | ||
315 | return 0; | ||
316 | case 1: | ||
317 | case 2: | ||
318 | case 3: | ||
319 | /* Our lower layer calculations limit our precision to | ||
320 | 1 microsecond */ | ||
321 | return 1; | ||
322 | case 4: | ||
323 | return 2; | ||
324 | case 5: | ||
325 | return 4; | ||
326 | case 6: | ||
327 | return 8; | ||
328 | case 7: | ||
329 | return 16; | ||
330 | default: | ||
331 | return 0; | ||
332 | } | ||
333 | } | ||
334 | |||
335 | static void ath9k_ht_conf(struct ath_softc *sc, | 298 | static void ath9k_ht_conf(struct ath_softc *sc, |
336 | struct ieee80211_bss_conf *bss_conf) | 299 | struct ieee80211_bss_conf *bss_conf) |
337 | { | 300 | { |
@@ -350,11 +313,12 @@ static void ath9k_ht_conf(struct ath_softc *sc, | |||
350 | } | 313 | } |
351 | 314 | ||
352 | static void ath9k_bss_assoc_info(struct ath_softc *sc, | 315 | static void ath9k_bss_assoc_info(struct ath_softc *sc, |
316 | struct ieee80211_vif *vif, | ||
353 | struct ieee80211_bss_conf *bss_conf) | 317 | struct ieee80211_bss_conf *bss_conf) |
354 | { | 318 | { |
355 | struct ieee80211_hw *hw = sc->hw; | 319 | struct ieee80211_hw *hw = sc->hw; |
356 | struct ieee80211_channel *curchan = hw->conf.channel; | 320 | struct ieee80211_channel *curchan = hw->conf.channel; |
357 | struct ath_vap *avp; | 321 | struct ath_vap *avp = (void *)vif->drv_priv; |
358 | int pos; | 322 | int pos; |
359 | 323 | ||
360 | if (bss_conf->assoc) { | 324 | if (bss_conf->assoc) { |
@@ -362,13 +326,6 @@ static void ath9k_bss_assoc_info(struct ath_softc *sc, | |||
362 | __func__, | 326 | __func__, |
363 | bss_conf->aid); | 327 | bss_conf->aid); |
364 | 328 | ||
365 | avp = sc->sc_vaps[0]; | ||
366 | if (avp == NULL) { | ||
367 | DPRINTF(sc, ATH_DBG_FATAL, "%s: Invalid interface\n", | ||
368 | __func__); | ||
369 | return; | ||
370 | } | ||
371 | |||
372 | /* New association, store aid */ | 329 | /* New association, store aid */ |
373 | if (avp->av_opmode == ATH9K_M_STA) { | 330 | if (avp->av_opmode == ATH9K_M_STA) { |
374 | sc->sc_curaid = bss_conf->aid; | 331 | sc->sc_curaid = bss_conf->aid; |
@@ -449,7 +406,7 @@ void ath_get_beaconconfig(struct ath_softc *sc, | |||
449 | } | 406 | } |
450 | 407 | ||
451 | void ath_tx_complete(struct ath_softc *sc, struct sk_buff *skb, | 408 | void ath_tx_complete(struct ath_softc *sc, struct sk_buff *skb, |
452 | struct ath_xmit_status *tx_status, struct ath_node *an) | 409 | struct ath_xmit_status *tx_status) |
453 | { | 410 | { |
454 | struct ieee80211_hw *hw = sc->hw; | 411 | struct ieee80211_hw *hw = sc->hw; |
455 | struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); | 412 | struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); |
@@ -479,8 +436,6 @@ void ath_tx_complete(struct ath_softc *sc, struct sk_buff *skb, | |||
479 | tx_info->status.rates[0].count = tx_status->retries + 1; | 436 | tx_info->status.rates[0].count = tx_status->retries + 1; |
480 | 437 | ||
481 | ieee80211_tx_status(hw, skb); | 438 | ieee80211_tx_status(hw, skb); |
482 | if (an) | ||
483 | ath_node_put(sc, an, ATH9K_BH_STATUS_CHANGE); | ||
484 | } | 439 | } |
485 | 440 | ||
486 | int _ath_rx_indicate(struct ath_softc *sc, | 441 | int _ath_rx_indicate(struct ath_softc *sc, |
@@ -489,12 +444,10 @@ int _ath_rx_indicate(struct ath_softc *sc, | |||
489 | u16 keyix) | 444 | u16 keyix) |
490 | { | 445 | { |
491 | struct ieee80211_hw *hw = sc->hw; | 446 | struct ieee80211_hw *hw = sc->hw; |
492 | struct ath_node *an = NULL; | ||
493 | struct ieee80211_rx_status rx_status; | 447 | struct ieee80211_rx_status rx_status; |
494 | struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; | 448 | struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; |
495 | int hdrlen = ieee80211_get_hdrlen_from_skb(skb); | 449 | int hdrlen = ieee80211_get_hdrlen_from_skb(skb); |
496 | int padsize; | 450 | int padsize; |
497 | enum ATH_RX_TYPE st; | ||
498 | 451 | ||
499 | /* see if any padding is done by the hw and remove it */ | 452 | /* see if any padding is done by the hw and remove it */ |
500 | if (hdrlen & 3) { | 453 | if (hdrlen & 3) { |
@@ -518,33 +471,6 @@ int _ath_rx_indicate(struct ath_softc *sc, | |||
518 | rx_status.flag |= RX_FLAG_DECRYPTED; | 471 | rx_status.flag |= RX_FLAG_DECRYPTED; |
519 | } | 472 | } |
520 | 473 | ||
521 | spin_lock_bh(&sc->node_lock); | ||
522 | an = ath_node_find(sc, hdr->addr2); | ||
523 | spin_unlock_bh(&sc->node_lock); | ||
524 | |||
525 | if (an) { | ||
526 | ath_rx_input(sc, an, | ||
527 | skb, status, &st); | ||
528 | } | ||
529 | if (!an || (st != ATH_RX_CONSUMED)) | ||
530 | __ieee80211_rx(hw, skb, &rx_status); | ||
531 | |||
532 | return 0; | ||
533 | } | ||
534 | |||
535 | int ath_rx_subframe(struct ath_node *an, | ||
536 | struct sk_buff *skb, | ||
537 | struct ath_recv_status *status) | ||
538 | { | ||
539 | struct ath_softc *sc = an->an_sc; | ||
540 | struct ieee80211_hw *hw = sc->hw; | ||
541 | struct ieee80211_rx_status rx_status; | ||
542 | |||
543 | /* Prepare rx status */ | ||
544 | ath9k_rx_prepare(sc, skb, status, &rx_status); | ||
545 | if (!(status->flags & ATH_RX_DECRYPT_ERROR)) | ||
546 | rx_status.flag |= RX_FLAG_DECRYPTED; | ||
547 | |||
548 | __ieee80211_rx(hw, skb, &rx_status); | 474 | __ieee80211_rx(hw, skb, &rx_status); |
549 | 475 | ||
550 | return 0; | 476 | return 0; |
@@ -666,6 +592,7 @@ fail: | |||
666 | } | 592 | } |
667 | 593 | ||
668 | #ifdef CONFIG_RFKILL | 594 | #ifdef CONFIG_RFKILL |
595 | |||
669 | /*******************/ | 596 | /*******************/ |
670 | /* Rfkill */ | 597 | /* Rfkill */ |
671 | /*******************/ | 598 | /*******************/ |
@@ -866,43 +793,72 @@ static void ath_deinit_rfkill(struct ath_softc *sc) | |||
866 | sc->rf_kill.rfkill = NULL; | 793 | sc->rf_kill.rfkill = NULL; |
867 | } | 794 | } |
868 | } | 795 | } |
796 | |||
797 | static int ath_start_rfkill_poll(struct ath_softc *sc) | ||
798 | { | ||
799 | if (sc->sc_ah->ah_caps.hw_caps & ATH9K_HW_CAP_RFSILENT) | ||
800 | queue_delayed_work(sc->hw->workqueue, | ||
801 | &sc->rf_kill.rfkill_poll, 0); | ||
802 | |||
803 | if (!(sc->sc_flags & SC_OP_RFKILL_REGISTERED)) { | ||
804 | if (rfkill_register(sc->rf_kill.rfkill)) { | ||
805 | DPRINTF(sc, ATH_DBG_FATAL, | ||
806 | "Unable to register rfkill\n"); | ||
807 | rfkill_free(sc->rf_kill.rfkill); | ||
808 | |||
809 | /* Deinitialize the device */ | ||
810 | if (sc->pdev->irq) | ||
811 | free_irq(sc->pdev->irq, sc); | ||
812 | ath_detach(sc); | ||
813 | pci_iounmap(sc->pdev, sc->mem); | ||
814 | pci_release_region(sc->pdev, 0); | ||
815 | pci_disable_device(sc->pdev); | ||
816 | ieee80211_free_hw(hw); | ||
817 | return -EIO; | ||
818 | } else { | ||
819 | sc->sc_flags |= SC_OP_RFKILL_REGISTERED; | ||
820 | } | ||
821 | } | ||
822 | |||
823 | return 0; | ||
824 | } | ||
869 | #endif /* CONFIG_RFKILL */ | 825 | #endif /* CONFIG_RFKILL */ |
870 | 826 | ||
871 | static int ath_detach(struct ath_softc *sc) | 827 | static void ath_detach(struct ath_softc *sc) |
872 | { | 828 | { |
873 | struct ieee80211_hw *hw = sc->hw; | 829 | struct ieee80211_hw *hw = sc->hw; |
830 | int i = 0; | ||
874 | 831 | ||
875 | DPRINTF(sc, ATH_DBG_CONFIG, "%s: Detach ATH hw\n", __func__); | 832 | DPRINTF(sc, ATH_DBG_CONFIG, "%s: Detach ATH hw\n", __func__); |
876 | 833 | ||
877 | /* Deinit LED control */ | 834 | ieee80211_unregister_hw(hw); |
835 | |||
878 | ath_deinit_leds(sc); | 836 | ath_deinit_leds(sc); |
879 | 837 | ||
880 | #ifdef CONFIG_RFKILL | 838 | #ifdef CONFIG_RFKILL |
881 | /* deinit rfkill */ | ||
882 | ath_deinit_rfkill(sc); | 839 | ath_deinit_rfkill(sc); |
883 | #endif | 840 | #endif |
884 | |||
885 | /* Unregister hw */ | ||
886 | |||
887 | ieee80211_unregister_hw(hw); | ||
888 | |||
889 | /* unregister Rate control */ | ||
890 | ath_rate_control_unregister(); | 841 | ath_rate_control_unregister(); |
891 | 842 | ath_rate_detach(sc->sc_rc); | |
892 | /* tx/rx cleanup */ | ||
893 | 843 | ||
894 | ath_rx_cleanup(sc); | 844 | ath_rx_cleanup(sc); |
895 | ath_tx_cleanup(sc); | 845 | ath_tx_cleanup(sc); |
896 | 846 | ||
897 | /* Deinit */ | 847 | tasklet_kill(&sc->intr_tq); |
848 | tasklet_kill(&sc->bcon_tasklet); | ||
898 | 849 | ||
899 | ath_deinit(sc); | 850 | if (!(sc->sc_flags & SC_OP_INVALID)) |
851 | ath9k_hw_setpower(sc->sc_ah, ATH9K_PM_AWAKE); | ||
900 | 852 | ||
901 | return 0; | 853 | /* cleanup tx queues */ |
854 | for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) | ||
855 | if (ATH_TXQ_SETUP(sc, i)) | ||
856 | ath_tx_cleanupq(sc, &sc->sc_txq[i]); | ||
857 | |||
858 | ath9k_hw_detach(sc->sc_ah); | ||
902 | } | 859 | } |
903 | 860 | ||
904 | static int ath_attach(u16 devid, | 861 | static int ath_attach(u16 devid, struct ath_softc *sc) |
905 | struct ath_softc *sc) | ||
906 | { | 862 | { |
907 | struct ieee80211_hw *hw = sc->hw; | 863 | struct ieee80211_hw *hw = sc->hw; |
908 | int error = 0; | 864 | int error = 0; |
@@ -913,47 +869,23 @@ static int ath_attach(u16 devid, | |||
913 | if (error != 0) | 869 | if (error != 0) |
914 | return error; | 870 | return error; |
915 | 871 | ||
916 | /* Init nodes */ | ||
917 | |||
918 | INIT_LIST_HEAD(&sc->node_list); | ||
919 | spin_lock_init(&sc->node_lock); | ||
920 | |||
921 | /* get mac address from hardware and set in mac80211 */ | 872 | /* get mac address from hardware and set in mac80211 */ |
922 | 873 | ||
923 | SET_IEEE80211_PERM_ADDR(hw, sc->sc_myaddr); | 874 | SET_IEEE80211_PERM_ADDR(hw, sc->sc_myaddr); |
924 | 875 | ||
925 | /* setup channels and rates */ | 876 | hw->flags = IEEE80211_HW_RX_INCLUDES_FCS | |
926 | 877 | IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING | | |
927 | sc->sbands[IEEE80211_BAND_2GHZ].channels = | 878 | IEEE80211_HW_SIGNAL_DBM | |
928 | sc->channels[IEEE80211_BAND_2GHZ]; | 879 | IEEE80211_HW_AMPDU_AGGREGATION; |
929 | sc->sbands[IEEE80211_BAND_2GHZ].bitrates = | ||
930 | sc->rates[IEEE80211_BAND_2GHZ]; | ||
931 | sc->sbands[IEEE80211_BAND_2GHZ].band = IEEE80211_BAND_2GHZ; | ||
932 | |||
933 | if (sc->sc_ah->ah_caps.hw_caps & ATH9K_HW_CAP_HT) | ||
934 | /* Setup HT capabilities for 2.4Ghz*/ | ||
935 | setup_ht_cap(&sc->sbands[IEEE80211_BAND_2GHZ].ht_cap); | ||
936 | |||
937 | hw->wiphy->bands[IEEE80211_BAND_2GHZ] = | ||
938 | &sc->sbands[IEEE80211_BAND_2GHZ]; | ||
939 | |||
940 | if (test_bit(ATH9K_MODE_11A, sc->sc_ah->ah_caps.wireless_modes)) { | ||
941 | sc->sbands[IEEE80211_BAND_5GHZ].channels = | ||
942 | sc->channels[IEEE80211_BAND_5GHZ]; | ||
943 | sc->sbands[IEEE80211_BAND_5GHZ].bitrates = | ||
944 | sc->rates[IEEE80211_BAND_5GHZ]; | ||
945 | sc->sbands[IEEE80211_BAND_5GHZ].band = | ||
946 | IEEE80211_BAND_5GHZ; | ||
947 | |||
948 | if (sc->sc_ah->ah_caps.hw_caps & ATH9K_HW_CAP_HT) | ||
949 | /* Setup HT capabilities for 5Ghz*/ | ||
950 | setup_ht_cap(&sc->sbands[IEEE80211_BAND_5GHZ].ht_cap); | ||
951 | 880 | ||
952 | hw->wiphy->bands[IEEE80211_BAND_5GHZ] = | 881 | hw->wiphy->interface_modes = |
953 | &sc->sbands[IEEE80211_BAND_5GHZ]; | 882 | BIT(NL80211_IFTYPE_AP) | |
954 | } | 883 | BIT(NL80211_IFTYPE_STATION) | |
884 | BIT(NL80211_IFTYPE_ADHOC); | ||
955 | 885 | ||
956 | hw->queues = 4; | 886 | hw->queues = 4; |
887 | hw->sta_data_size = sizeof(struct ath_node); | ||
888 | hw->vif_data_size = sizeof(struct ath_vap); | ||
957 | 889 | ||
958 | /* Register rate control */ | 890 | /* Register rate control */ |
959 | hw->rate_control_algorithm = "ath9k_rate_control"; | 891 | hw->rate_control_algorithm = "ath9k_rate_control"; |
@@ -966,6 +898,17 @@ static int ath_attach(u16 devid, | |||
966 | goto bad; | 898 | goto bad; |
967 | } | 899 | } |
968 | 900 | ||
901 | if (sc->sc_ah->ah_caps.hw_caps & ATH9K_HW_CAP_HT) { | ||
902 | setup_ht_cap(&sc->sbands[IEEE80211_BAND_2GHZ].ht_cap); | ||
903 | if (test_bit(ATH9K_MODE_11A, sc->sc_ah->ah_caps.wireless_modes)) | ||
904 | setup_ht_cap(&sc->sbands[IEEE80211_BAND_5GHZ].ht_cap); | ||
905 | } | ||
906 | |||
907 | hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &sc->sbands[IEEE80211_BAND_2GHZ]; | ||
908 | if (test_bit(ATH9K_MODE_11A, sc->sc_ah->ah_caps.wireless_modes)) | ||
909 | hw->wiphy->bands[IEEE80211_BAND_5GHZ] = | ||
910 | &sc->sbands[IEEE80211_BAND_5GHZ]; | ||
911 | |||
969 | error = ieee80211_register_hw(hw); | 912 | error = ieee80211_register_hw(hw); |
970 | if (error != 0) { | 913 | if (error != 0) { |
971 | ath_rate_control_unregister(); | 914 | ath_rate_control_unregister(); |
@@ -1011,62 +954,44 @@ static int ath9k_start(struct ieee80211_hw *hw) | |||
1011 | DPRINTF(sc, ATH_DBG_CONFIG, "%s: Starting driver with " | 954 | DPRINTF(sc, ATH_DBG_CONFIG, "%s: Starting driver with " |
1012 | "initial channel: %d MHz\n", __func__, curchan->center_freq); | 955 | "initial channel: %d MHz\n", __func__, curchan->center_freq); |
1013 | 956 | ||
957 | memset(&sc->sc_ht_info, 0, sizeof(struct ath_ht_info)); | ||
958 | |||
1014 | /* setup initial channel */ | 959 | /* setup initial channel */ |
1015 | 960 | ||
1016 | pos = ath_get_channel(sc, curchan); | 961 | pos = ath_get_channel(sc, curchan); |
1017 | if (pos == -1) { | 962 | if (pos == -1) { |
1018 | DPRINTF(sc, ATH_DBG_FATAL, "%s: Invalid channel\n", __func__); | 963 | DPRINTF(sc, ATH_DBG_FATAL, "%s: Invalid channel\n", __func__); |
1019 | return -EINVAL; | 964 | error = -EINVAL; |
965 | goto exit; | ||
1020 | } | 966 | } |
1021 | 967 | ||
1022 | sc->sc_ah->ah_channels[pos].chanmode = | 968 | sc->sc_ah->ah_channels[pos].chanmode = |
1023 | (curchan->band == IEEE80211_BAND_2GHZ) ? CHANNEL_G : CHANNEL_A; | 969 | (curchan->band == IEEE80211_BAND_2GHZ) ? CHANNEL_G : CHANNEL_A; |
1024 | 970 | ||
1025 | /* open ath_dev */ | ||
1026 | error = ath_open(sc, &sc->sc_ah->ah_channels[pos]); | 971 | error = ath_open(sc, &sc->sc_ah->ah_channels[pos]); |
1027 | if (error) { | 972 | if (error) { |
1028 | DPRINTF(sc, ATH_DBG_FATAL, | 973 | DPRINTF(sc, ATH_DBG_FATAL, |
1029 | "%s: Unable to complete ath_open\n", __func__); | 974 | "%s: Unable to complete ath_open\n", __func__); |
1030 | return error; | 975 | goto exit; |
1031 | } | 976 | } |
1032 | 977 | ||
1033 | #ifdef CONFIG_RFKILL | 978 | #ifdef CONFIG_RFKILL |
1034 | /* Start rfkill polling */ | 979 | error = ath_start_rfkill_poll(sc); |
1035 | if (sc->sc_ah->ah_caps.hw_caps & ATH9K_HW_CAP_RFSILENT) | ||
1036 | queue_delayed_work(sc->hw->workqueue, | ||
1037 | &sc->rf_kill.rfkill_poll, 0); | ||
1038 | |||
1039 | if (!(sc->sc_flags & SC_OP_RFKILL_REGISTERED)) { | ||
1040 | if (rfkill_register(sc->rf_kill.rfkill)) { | ||
1041 | DPRINTF(sc, ATH_DBG_FATAL, | ||
1042 | "Unable to register rfkill\n"); | ||
1043 | rfkill_free(sc->rf_kill.rfkill); | ||
1044 | |||
1045 | /* Deinitialize the device */ | ||
1046 | if (sc->pdev->irq) | ||
1047 | free_irq(sc->pdev->irq, sc); | ||
1048 | ath_detach(sc); | ||
1049 | pci_iounmap(sc->pdev, sc->mem); | ||
1050 | pci_release_region(sc->pdev, 0); | ||
1051 | pci_disable_device(sc->pdev); | ||
1052 | ieee80211_free_hw(hw); | ||
1053 | return -EIO; | ||
1054 | } else { | ||
1055 | sc->sc_flags |= SC_OP_RFKILL_REGISTERED; | ||
1056 | } | ||
1057 | } | ||
1058 | #endif | 980 | #endif |
1059 | 981 | ||
1060 | ieee80211_wake_queues(hw); | 982 | exit: |
1061 | return 0; | 983 | return error; |
1062 | } | 984 | } |
1063 | 985 | ||
1064 | static int ath9k_tx(struct ieee80211_hw *hw, | 986 | static int ath9k_tx(struct ieee80211_hw *hw, |
1065 | struct sk_buff *skb) | 987 | struct sk_buff *skb) |
1066 | { | 988 | { |
989 | struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); | ||
1067 | struct ath_softc *sc = hw->priv; | 990 | struct ath_softc *sc = hw->priv; |
991 | struct ath_tx_control txctl; | ||
1068 | int hdrlen, padsize; | 992 | int hdrlen, padsize; |
1069 | struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); | 993 | |
994 | memset(&txctl, 0, sizeof(struct ath_tx_control)); | ||
1070 | 995 | ||
1071 | /* | 996 | /* |
1072 | * As a temporary workaround, assign seq# here; this will likely need | 997 | * As a temporary workaround, assign seq# here; this will likely need |
@@ -1091,45 +1016,47 @@ static int ath9k_tx(struct ieee80211_hw *hw, | |||
1091 | memmove(skb->data, skb->data + padsize, hdrlen); | 1016 | memmove(skb->data, skb->data + padsize, hdrlen); |
1092 | } | 1017 | } |
1093 | 1018 | ||
1019 | /* Check if a tx queue is available */ | ||
1020 | |||
1021 | txctl.txq = ath_test_get_txq(sc, skb); | ||
1022 | if (!txctl.txq) | ||
1023 | goto exit; | ||
1024 | |||
1094 | DPRINTF(sc, ATH_DBG_XMIT, "%s: transmitting packet, skb: %p\n", | 1025 | DPRINTF(sc, ATH_DBG_XMIT, "%s: transmitting packet, skb: %p\n", |
1095 | __func__, | 1026 | __func__, |
1096 | skb); | 1027 | skb); |
1097 | 1028 | ||
1098 | if (ath_tx_start(sc, skb) != 0) { | 1029 | if (ath_tx_start(sc, skb, &txctl) != 0) { |
1099 | DPRINTF(sc, ATH_DBG_XMIT, "%s: TX failed\n", __func__); | 1030 | DPRINTF(sc, ATH_DBG_XMIT, "%s: TX failed\n", __func__); |
1100 | dev_kfree_skb_any(skb); | 1031 | goto exit; |
1101 | /* FIXME: Check for proper return value from ATH_DEV */ | ||
1102 | return 0; | ||
1103 | } | 1032 | } |
1104 | 1033 | ||
1105 | return 0; | 1034 | return 0; |
1035 | exit: | ||
1036 | dev_kfree_skb_any(skb); | ||
1037 | return 0; | ||
1106 | } | 1038 | } |
1107 | 1039 | ||
1108 | static void ath9k_stop(struct ieee80211_hw *hw) | 1040 | static void ath9k_stop(struct ieee80211_hw *hw) |
1109 | { | 1041 | { |
1110 | struct ath_softc *sc = hw->priv; | 1042 | struct ath_softc *sc = hw->priv; |
1111 | int error; | ||
1112 | 1043 | ||
1113 | DPRINTF(sc, ATH_DBG_CONFIG, "%s: Driver halt\n", __func__); | 1044 | if (sc->sc_flags & SC_OP_INVALID) { |
1114 | 1045 | DPRINTF(sc, ATH_DBG_ANY, "%s: Device not present\n", __func__); | |
1115 | error = ath_suspend(sc); | 1046 | return; |
1116 | if (error) | 1047 | } |
1117 | DPRINTF(sc, ATH_DBG_CONFIG, | ||
1118 | "%s: Device is no longer present\n", __func__); | ||
1119 | 1048 | ||
1120 | ieee80211_stop_queues(hw); | 1049 | ath_stop(sc); |
1121 | 1050 | ||
1122 | #ifdef CONFIG_RFKILL | 1051 | DPRINTF(sc, ATH_DBG_CONFIG, "%s: Driver halt\n", __func__); |
1123 | if (sc->sc_ah->ah_caps.hw_caps & ATH9K_HW_CAP_RFSILENT) | ||
1124 | cancel_delayed_work_sync(&sc->rf_kill.rfkill_poll); | ||
1125 | #endif | ||
1126 | } | 1052 | } |
1127 | 1053 | ||
1128 | static int ath9k_add_interface(struct ieee80211_hw *hw, | 1054 | static int ath9k_add_interface(struct ieee80211_hw *hw, |
1129 | struct ieee80211_if_init_conf *conf) | 1055 | struct ieee80211_if_init_conf *conf) |
1130 | { | 1056 | { |
1131 | struct ath_softc *sc = hw->priv; | 1057 | struct ath_softc *sc = hw->priv; |
1132 | int error, ic_opmode = 0; | 1058 | struct ath_vap *avp = (void *)conf->vif->drv_priv; |
1059 | int ic_opmode = 0; | ||
1133 | 1060 | ||
1134 | /* Support only vap for now */ | 1061 | /* Support only vap for now */ |
1135 | 1062 | ||
@@ -1157,13 +1084,22 @@ static int ath9k_add_interface(struct ieee80211_hw *hw, | |||
1157 | __func__, | 1084 | __func__, |
1158 | ic_opmode); | 1085 | ic_opmode); |
1159 | 1086 | ||
1160 | error = ath_vap_attach(sc, 0, conf->vif, ic_opmode); | 1087 | /* Set the VAP opmode */ |
1161 | if (error) { | 1088 | avp->av_opmode = ic_opmode; |
1162 | DPRINTF(sc, ATH_DBG_FATAL, | 1089 | avp->av_bslot = -1; |
1163 | "%s: Unable to attach vap, error: %d\n", | 1090 | |
1164 | __func__, error); | 1091 | if (ic_opmode == ATH9K_M_HOSTAP) |
1165 | return error; | 1092 | ath9k_hw_set_tsfadjust(sc->sc_ah, 1); |
1166 | } | 1093 | |
1094 | sc->sc_vaps[0] = conf->vif; | ||
1095 | sc->sc_nvaps++; | ||
1096 | |||
1097 | /* Set the device opmode */ | ||
1098 | sc->sc_ah->ah_opmode = ic_opmode; | ||
1099 | |||
1100 | /* default VAP configuration */ | ||
1101 | avp->av_config.av_fixed_rateset = IEEE80211_FIXED_RATE_NONE; | ||
1102 | avp->av_config.av_fixed_retryset = 0x03030303; | ||
1167 | 1103 | ||
1168 | if (conf->type == NL80211_IFTYPE_AP) { | 1104 | if (conf->type == NL80211_IFTYPE_AP) { |
1169 | /* TODO: is this a suitable place to start ANI for AP mode? */ | 1105 | /* TODO: is this a suitable place to start ANI for AP mode? */ |
@@ -1179,27 +1115,16 @@ static void ath9k_remove_interface(struct ieee80211_hw *hw, | |||
1179 | struct ieee80211_if_init_conf *conf) | 1115 | struct ieee80211_if_init_conf *conf) |
1180 | { | 1116 | { |
1181 | struct ath_softc *sc = hw->priv; | 1117 | struct ath_softc *sc = hw->priv; |
1182 | struct ath_vap *avp; | 1118 | struct ath_vap *avp = (void *)conf->vif->drv_priv; |
1183 | int error; | ||
1184 | 1119 | ||
1185 | DPRINTF(sc, ATH_DBG_CONFIG, "%s: Detach VAP\n", __func__); | 1120 | DPRINTF(sc, ATH_DBG_CONFIG, "%s: Detach VAP\n", __func__); |
1186 | 1121 | ||
1187 | avp = sc->sc_vaps[0]; | ||
1188 | if (avp == NULL) { | ||
1189 | DPRINTF(sc, ATH_DBG_FATAL, "%s: Invalid interface\n", | ||
1190 | __func__); | ||
1191 | return; | ||
1192 | } | ||
1193 | |||
1194 | #ifdef CONFIG_SLOW_ANT_DIV | 1122 | #ifdef CONFIG_SLOW_ANT_DIV |
1195 | ath_slow_ant_div_stop(&sc->sc_antdiv); | 1123 | ath_slow_ant_div_stop(&sc->sc_antdiv); |
1196 | #endif | 1124 | #endif |
1197 | /* Stop ANI */ | 1125 | /* Stop ANI */ |
1198 | del_timer_sync(&sc->sc_ani.timer); | 1126 | del_timer_sync(&sc->sc_ani.timer); |
1199 | 1127 | ||
1200 | /* Update ratectrl */ | ||
1201 | ath_rate_newstate(sc, avp); | ||
1202 | |||
1203 | /* Reclaim beacon resources */ | 1128 | /* Reclaim beacon resources */ |
1204 | if (sc->sc_ah->ah_opmode == ATH9K_M_HOSTAP || | 1129 | if (sc->sc_ah->ah_opmode == ATH9K_M_HOSTAP || |
1205 | sc->sc_ah->ah_opmode == ATH9K_M_IBSS) { | 1130 | sc->sc_ah->ah_opmode == ATH9K_M_IBSS) { |
@@ -1207,16 +1132,10 @@ static void ath9k_remove_interface(struct ieee80211_hw *hw, | |||
1207 | ath_beacon_return(sc, avp); | 1132 | ath_beacon_return(sc, avp); |
1208 | } | 1133 | } |
1209 | 1134 | ||
1210 | /* Set interrupt mask */ | ||
1211 | sc->sc_imask &= ~(ATH9K_INT_SWBA | ATH9K_INT_BMISS); | ||
1212 | ath9k_hw_set_interrupts(sc->sc_ah, sc->sc_imask & ~ATH9K_INT_GLOBAL); | ||
1213 | sc->sc_flags &= ~SC_OP_BEACONS; | 1135 | sc->sc_flags &= ~SC_OP_BEACONS; |
1214 | 1136 | ||
1215 | error = ath_vap_detach(sc, 0); | 1137 | sc->sc_vaps[0] = NULL; |
1216 | if (error) | 1138 | sc->sc_nvaps--; |
1217 | DPRINTF(sc, ATH_DBG_FATAL, | ||
1218 | "%s: Unable to detach vap, error: %d\n", | ||
1219 | __func__, error); | ||
1220 | } | 1139 | } |
1221 | 1140 | ||
1222 | static int ath9k_config(struct ieee80211_hw *hw, u32 changed) | 1141 | static int ath9k_config(struct ieee80211_hw *hw, u32 changed) |
@@ -1264,17 +1183,10 @@ static int ath9k_config_interface(struct ieee80211_hw *hw, | |||
1264 | { | 1183 | { |
1265 | struct ath_softc *sc = hw->priv; | 1184 | struct ath_softc *sc = hw->priv; |
1266 | struct ath_hal *ah = sc->sc_ah; | 1185 | struct ath_hal *ah = sc->sc_ah; |
1267 | struct ath_vap *avp; | 1186 | struct ath_vap *avp = (void *)vif->drv_priv; |
1268 | u32 rfilt = 0; | 1187 | u32 rfilt = 0; |
1269 | int error, i; | 1188 | int error, i; |
1270 | 1189 | ||
1271 | avp = sc->sc_vaps[0]; | ||
1272 | if (avp == NULL) { | ||
1273 | DPRINTF(sc, ATH_DBG_FATAL, "%s: Invalid interface\n", | ||
1274 | __func__); | ||
1275 | return -EINVAL; | ||
1276 | } | ||
1277 | |||
1278 | /* TODO: Need to decide which hw opmode to use for multi-interface | 1190 | /* TODO: Need to decide which hw opmode to use for multi-interface |
1279 | * cases */ | 1191 | * cases */ |
1280 | if (vif->type == NL80211_IFTYPE_AP && | 1192 | if (vif->type == NL80211_IFTYPE_AP && |
@@ -1303,23 +1215,6 @@ static int ath9k_config_interface(struct ieee80211_hw *hw, | |||
1303 | /* Set aggregation protection mode parameters */ | 1215 | /* Set aggregation protection mode parameters */ |
1304 | sc->sc_config.ath_aggr_prot = 0; | 1216 | sc->sc_config.ath_aggr_prot = 0; |
1305 | 1217 | ||
1306 | /* | ||
1307 | * Reset our TSF so that its value is lower than the | ||
1308 | * beacon that we are trying to catch. | ||
1309 | * Only then hw will update its TSF register with the | ||
1310 | * new beacon. Reset the TSF before setting the BSSID | ||
1311 | * to avoid allowing in any frames that would update | ||
1312 | * our TSF only to have us clear it | ||
1313 | * immediately thereafter. | ||
1314 | */ | ||
1315 | ath9k_hw_reset_tsf(sc->sc_ah); | ||
1316 | |||
1317 | /* Disable BMISS interrupt when we're not associated */ | ||
1318 | ath9k_hw_set_interrupts(sc->sc_ah, | ||
1319 | sc->sc_imask & | ||
1320 | ~(ATH9K_INT_SWBA | ATH9K_INT_BMISS)); | ||
1321 | sc->sc_imask &= ~(ATH9K_INT_SWBA | ATH9K_INT_BMISS); | ||
1322 | |||
1323 | DPRINTF(sc, ATH_DBG_CONFIG, | 1218 | DPRINTF(sc, ATH_DBG_CONFIG, |
1324 | "%s: RX filter 0x%x bssid %pM aid 0x%x\n", | 1219 | "%s: RX filter 0x%x bssid %pM aid 0x%x\n", |
1325 | __func__, rfilt, | 1220 | __func__, rfilt, |
@@ -1355,7 +1250,7 @@ static int ath9k_config_interface(struct ieee80211_hw *hw, | |||
1355 | } | 1250 | } |
1356 | 1251 | ||
1357 | /* Check for WLAN_CAPABILITY_PRIVACY ? */ | 1252 | /* Check for WLAN_CAPABILITY_PRIVACY ? */ |
1358 | if ((avp->av_opmode != NL80211_IFTYPE_STATION)) { | 1253 | if ((avp->av_opmode != ATH9K_M_STA)) { |
1359 | for (i = 0; i < IEEE80211_WEP_NKID; i++) | 1254 | for (i = 0; i < IEEE80211_WEP_NKID; i++) |
1360 | if (ath9k_hw_keyisvalid(sc->sc_ah, (u16)i)) | 1255 | if (ath9k_hw_keyisvalid(sc->sc_ah, (u16)i)) |
1361 | ath9k_hw_keysetmac(sc->sc_ah, | 1256 | ath9k_hw_keysetmac(sc->sc_ah, |
@@ -1410,44 +1305,13 @@ static void ath9k_sta_notify(struct ieee80211_hw *hw, | |||
1410 | struct ieee80211_sta *sta) | 1305 | struct ieee80211_sta *sta) |
1411 | { | 1306 | { |
1412 | struct ath_softc *sc = hw->priv; | 1307 | struct ath_softc *sc = hw->priv; |
1413 | struct ath_node *an; | ||
1414 | unsigned long flags; | ||
1415 | |||
1416 | spin_lock_irqsave(&sc->node_lock, flags); | ||
1417 | an = ath_node_find(sc, sta->addr); | ||
1418 | spin_unlock_irqrestore(&sc->node_lock, flags); | ||
1419 | 1308 | ||
1420 | switch (cmd) { | 1309 | switch (cmd) { |
1421 | case STA_NOTIFY_ADD: | 1310 | case STA_NOTIFY_ADD: |
1422 | spin_lock_irqsave(&sc->node_lock, flags); | 1311 | ath_node_attach(sc, sta); |
1423 | if (!an) { | ||
1424 | ath_node_attach(sc, sta->addr, 0); | ||
1425 | DPRINTF(sc, ATH_DBG_CONFIG, "%s: Attach a node: %pM\n", | ||
1426 | __func__, sta->addr); | ||
1427 | } else { | ||
1428 | ath_node_get(sc, sta->addr); | ||
1429 | } | ||
1430 | |||
1431 | /* XXX: Is this right? Can the capabilities change? */ | ||
1432 | an = ath_node_find(sc, sta->addr); | ||
1433 | an->maxampdu = 1 << (IEEE80211_HTCAP_MAXRXAMPDU_FACTOR + | ||
1434 | sta->ht_cap.ampdu_factor); | ||
1435 | an->mpdudensity = | ||
1436 | parse_mpdudensity(sta->ht_cap.ampdu_density); | ||
1437 | |||
1438 | spin_unlock_irqrestore(&sc->node_lock, flags); | ||
1439 | break; | 1312 | break; |
1440 | case STA_NOTIFY_REMOVE: | 1313 | case STA_NOTIFY_REMOVE: |
1441 | if (!an) | 1314 | ath_node_detach(sc, sta); |
1442 | DPRINTF(sc, ATH_DBG_FATAL, | ||
1443 | "%s: Removal of a non-existent node\n", | ||
1444 | __func__); | ||
1445 | else { | ||
1446 | ath_node_put(sc, an, ATH9K_BH_STATUS_INTACT); | ||
1447 | DPRINTF(sc, ATH_DBG_CONFIG, "%s: Put a node: %pM\n", | ||
1448 | __func__, | ||
1449 | sta->addr); | ||
1450 | } | ||
1451 | break; | 1315 | break; |
1452 | default: | 1316 | default: |
1453 | break; | 1317 | break; |
@@ -1562,7 +1426,7 @@ static void ath9k_bss_info_changed(struct ieee80211_hw *hw, | |||
1562 | DPRINTF(sc, ATH_DBG_CONFIG, "%s: BSS Changed ASSOC %d\n", | 1426 | DPRINTF(sc, ATH_DBG_CONFIG, "%s: BSS Changed ASSOC %d\n", |
1563 | __func__, | 1427 | __func__, |
1564 | bss_conf->assoc); | 1428 | bss_conf->assoc); |
1565 | ath9k_bss_assoc_info(sc, bss_conf); | 1429 | ath9k_bss_assoc_info(sc, vif, bss_conf); |
1566 | } | 1430 | } |
1567 | } | 1431 | } |
1568 | 1432 | ||
@@ -1595,21 +1459,13 @@ static int ath9k_ampdu_action(struct ieee80211_hw *hw, | |||
1595 | 1459 | ||
1596 | switch (action) { | 1460 | switch (action) { |
1597 | case IEEE80211_AMPDU_RX_START: | 1461 | case IEEE80211_AMPDU_RX_START: |
1598 | ret = ath_rx_aggr_start(sc, sta->addr, tid, ssn); | 1462 | if (!(sc->sc_flags & SC_OP_RXAGGR)) |
1599 | if (ret < 0) | 1463 | ret = -ENOTSUPP; |
1600 | DPRINTF(sc, ATH_DBG_FATAL, | ||
1601 | "%s: Unable to start RX aggregation\n", | ||
1602 | __func__); | ||
1603 | break; | 1464 | break; |
1604 | case IEEE80211_AMPDU_RX_STOP: | 1465 | case IEEE80211_AMPDU_RX_STOP: |
1605 | ret = ath_rx_aggr_stop(sc, sta->addr, tid); | ||
1606 | if (ret < 0) | ||
1607 | DPRINTF(sc, ATH_DBG_FATAL, | ||
1608 | "%s: Unable to stop RX aggregation\n", | ||
1609 | __func__); | ||
1610 | break; | 1466 | break; |
1611 | case IEEE80211_AMPDU_TX_START: | 1467 | case IEEE80211_AMPDU_TX_START: |
1612 | ret = ath_tx_aggr_start(sc, sta->addr, tid, ssn); | 1468 | ret = ath_tx_aggr_start(sc, sta, tid, ssn); |
1613 | if (ret < 0) | 1469 | if (ret < 0) |
1614 | DPRINTF(sc, ATH_DBG_FATAL, | 1470 | DPRINTF(sc, ATH_DBG_FATAL, |
1615 | "%s: Unable to start TX aggregation\n", | 1471 | "%s: Unable to start TX aggregation\n", |
@@ -1618,7 +1474,7 @@ static int ath9k_ampdu_action(struct ieee80211_hw *hw, | |||
1618 | ieee80211_start_tx_ba_cb_irqsafe(hw, sta->addr, tid); | 1474 | ieee80211_start_tx_ba_cb_irqsafe(hw, sta->addr, tid); |
1619 | break; | 1475 | break; |
1620 | case IEEE80211_AMPDU_TX_STOP: | 1476 | case IEEE80211_AMPDU_TX_STOP: |
1621 | ret = ath_tx_aggr_stop(sc, sta->addr, tid); | 1477 | ret = ath_tx_aggr_stop(sc, sta, tid); |
1622 | if (ret < 0) | 1478 | if (ret < 0) |
1623 | DPRINTF(sc, ATH_DBG_FATAL, | 1479 | DPRINTF(sc, ATH_DBG_FATAL, |
1624 | "%s: Unable to stop TX aggregation\n", | 1480 | "%s: Unable to stop TX aggregation\n", |
@@ -1626,6 +1482,9 @@ static int ath9k_ampdu_action(struct ieee80211_hw *hw, | |||
1626 | 1482 | ||
1627 | ieee80211_stop_tx_ba_cb_irqsafe(hw, sta->addr, tid); | 1483 | ieee80211_stop_tx_ba_cb_irqsafe(hw, sta->addr, tid); |
1628 | break; | 1484 | break; |
1485 | case IEEE80211_AMPDU_TX_RESUME: | ||
1486 | ath_tx_aggr_resume(sc, sta, tid); | ||
1487 | break; | ||
1629 | default: | 1488 | default: |
1630 | DPRINTF(sc, ATH_DBG_FATAL, | 1489 | DPRINTF(sc, ATH_DBG_FATAL, |
1631 | "%s: Unknown AMPDU action\n", __func__); | 1490 | "%s: Unknown AMPDU action\n", __func__); |
@@ -1648,20 +1507,12 @@ static struct ieee80211_ops ath9k_ops = { | |||
1648 | .config = ath9k_config, | 1507 | .config = ath9k_config, |
1649 | .config_interface = ath9k_config_interface, | 1508 | .config_interface = ath9k_config_interface, |
1650 | .configure_filter = ath9k_configure_filter, | 1509 | .configure_filter = ath9k_configure_filter, |
1651 | .get_stats = NULL, | ||
1652 | .sta_notify = ath9k_sta_notify, | 1510 | .sta_notify = ath9k_sta_notify, |
1653 | .conf_tx = ath9k_conf_tx, | 1511 | .conf_tx = ath9k_conf_tx, |
1654 | .get_tx_stats = NULL, | ||
1655 | .bss_info_changed = ath9k_bss_info_changed, | 1512 | .bss_info_changed = ath9k_bss_info_changed, |
1656 | .set_tim = NULL, | ||
1657 | .set_key = ath9k_set_key, | 1513 | .set_key = ath9k_set_key, |
1658 | .hw_scan = NULL, | ||
1659 | .get_tkip_seq = NULL, | ||
1660 | .set_rts_threshold = NULL, | ||
1661 | .set_frag_threshold = NULL, | ||
1662 | .get_tsf = ath9k_get_tsf, | 1514 | .get_tsf = ath9k_get_tsf, |
1663 | .reset_tsf = ath9k_reset_tsf, | 1515 | .reset_tsf = ath9k_reset_tsf, |
1664 | .tx_last_beacon = NULL, | ||
1665 | .ampdu_action = ath9k_ampdu_action, | 1516 | .ampdu_action = ath9k_ampdu_action, |
1666 | .set_frag_threshold = ath9k_no_fragmentation, | 1517 | .set_frag_threshold = ath9k_no_fragmentation, |
1667 | }; | 1518 | }; |
@@ -1739,17 +1590,6 @@ static int ath_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) | |||
1739 | goto bad2; | 1590 | goto bad2; |
1740 | } | 1591 | } |
1741 | 1592 | ||
1742 | hw->flags = IEEE80211_HW_RX_INCLUDES_FCS | | ||
1743 | IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING | | ||
1744 | IEEE80211_HW_SIGNAL_DBM | | ||
1745 | IEEE80211_HW_NOISE_DBM | | ||
1746 | IEEE80211_HW_AMPDU_AGGREGATION; | ||
1747 | |||
1748 | hw->wiphy->interface_modes = | ||
1749 | BIT(NL80211_IFTYPE_AP) | | ||
1750 | BIT(NL80211_IFTYPE_STATION) | | ||
1751 | BIT(NL80211_IFTYPE_ADHOC); | ||
1752 | |||
1753 | SET_IEEE80211_DEV(hw, &pdev->dev); | 1593 | SET_IEEE80211_DEV(hw, &pdev->dev); |
1754 | pci_set_drvdata(pdev, hw); | 1594 | pci_set_drvdata(pdev, hw); |
1755 | 1595 | ||
@@ -1797,17 +1637,10 @@ static void ath_pci_remove(struct pci_dev *pdev) | |||
1797 | { | 1637 | { |
1798 | struct ieee80211_hw *hw = pci_get_drvdata(pdev); | 1638 | struct ieee80211_hw *hw = pci_get_drvdata(pdev); |
1799 | struct ath_softc *sc = hw->priv; | 1639 | struct ath_softc *sc = hw->priv; |
1800 | enum ath9k_int status; | ||
1801 | 1640 | ||
1802 | if (pdev->irq) { | ||
1803 | ath9k_hw_set_interrupts(sc->sc_ah, 0); | ||
1804 | /* clear the ISR */ | ||
1805 | ath9k_hw_getisr(sc->sc_ah, &status); | ||
1806 | sc->sc_flags |= SC_OP_INVALID; | ||
1807 | free_irq(pdev->irq, sc); | ||
1808 | } | ||
1809 | ath_detach(sc); | 1641 | ath_detach(sc); |
1810 | 1642 | if (pdev->irq) | |
1643 | free_irq(pdev->irq, sc); | ||
1811 | pci_iounmap(pdev, sc->mem); | 1644 | pci_iounmap(pdev, sc->mem); |
1812 | pci_release_region(pdev, 0); | 1645 | pci_release_region(pdev, 0); |
1813 | pci_disable_device(pdev); | 1646 | pci_disable_device(pdev); |