diff options
Diffstat (limited to 'drivers/net/wireless/ath5k/pcu.c')
-rw-r--r-- | drivers/net/wireless/ath5k/pcu.c | 233 |
1 files changed, 187 insertions, 46 deletions
diff --git a/drivers/net/wireless/ath5k/pcu.c b/drivers/net/wireless/ath5k/pcu.c index a47df9a24aa1..0cac05c6a9ce 100644 --- a/drivers/net/wireless/ath5k/pcu.c +++ b/drivers/net/wireless/ath5k/pcu.c | |||
@@ -46,34 +46,45 @@ int ath5k_hw_set_opmode(struct ath5k_hw *ah) | |||
46 | { | 46 | { |
47 | u32 pcu_reg, beacon_reg, low_id, high_id; | 47 | u32 pcu_reg, beacon_reg, low_id, high_id; |
48 | 48 | ||
49 | pcu_reg = 0; | 49 | |
50 | /* Preserve rest settings */ | ||
51 | pcu_reg = ath5k_hw_reg_read(ah, AR5K_STA_ID1) & 0xffff0000; | ||
52 | pcu_reg &= ~(AR5K_STA_ID1_ADHOC | AR5K_STA_ID1_AP | ||
53 | | AR5K_STA_ID1_KEYSRCH_MODE | ||
54 | | (ah->ah_version == AR5K_AR5210 ? | ||
55 | (AR5K_STA_ID1_PWR_SV | AR5K_STA_ID1_NO_PSPOLL) : 0)); | ||
56 | |||
50 | beacon_reg = 0; | 57 | beacon_reg = 0; |
51 | 58 | ||
52 | ATH5K_TRACE(ah->ah_sc); | 59 | ATH5K_TRACE(ah->ah_sc); |
53 | 60 | ||
54 | switch (ah->ah_op_mode) { | 61 | switch (ah->ah_op_mode) { |
55 | case NL80211_IFTYPE_ADHOC: | 62 | case NL80211_IFTYPE_ADHOC: |
56 | pcu_reg |= AR5K_STA_ID1_ADHOC | AR5K_STA_ID1_DESC_ANTENNA | | 63 | pcu_reg |= AR5K_STA_ID1_ADHOC | AR5K_STA_ID1_KEYSRCH_MODE; |
57 | (ah->ah_version == AR5K_AR5210 ? | ||
58 | AR5K_STA_ID1_NO_PSPOLL : 0); | ||
59 | beacon_reg |= AR5K_BCR_ADHOC; | 64 | beacon_reg |= AR5K_BCR_ADHOC; |
65 | if (ah->ah_version == AR5K_AR5210) | ||
66 | pcu_reg |= AR5K_STA_ID1_NO_PSPOLL; | ||
67 | else | ||
68 | AR5K_REG_DISABLE_BITS(ah, AR5K_CFG, AR5K_CFG_ADHOC); | ||
60 | break; | 69 | break; |
61 | 70 | ||
62 | case NL80211_IFTYPE_AP: | 71 | case NL80211_IFTYPE_AP: |
63 | case NL80211_IFTYPE_MESH_POINT: | 72 | case NL80211_IFTYPE_MESH_POINT: |
64 | pcu_reg |= AR5K_STA_ID1_AP | AR5K_STA_ID1_RTS_DEF_ANTENNA | | 73 | pcu_reg |= AR5K_STA_ID1_AP | AR5K_STA_ID1_KEYSRCH_MODE; |
65 | (ah->ah_version == AR5K_AR5210 ? | ||
66 | AR5K_STA_ID1_NO_PSPOLL : 0); | ||
67 | beacon_reg |= AR5K_BCR_AP; | 74 | beacon_reg |= AR5K_BCR_AP; |
75 | if (ah->ah_version == AR5K_AR5210) | ||
76 | pcu_reg |= AR5K_STA_ID1_NO_PSPOLL; | ||
77 | else | ||
78 | AR5K_REG_ENABLE_BITS(ah, AR5K_CFG, AR5K_CFG_ADHOC); | ||
68 | break; | 79 | break; |
69 | 80 | ||
70 | case NL80211_IFTYPE_STATION: | 81 | case NL80211_IFTYPE_STATION: |
71 | pcu_reg |= AR5K_STA_ID1_DEFAULT_ANTENNA | | 82 | pcu_reg |= AR5K_STA_ID1_KEYSRCH_MODE |
72 | (ah->ah_version == AR5K_AR5210 ? | 83 | | (ah->ah_version == AR5K_AR5210 ? |
73 | AR5K_STA_ID1_PWR_SV : 0); | 84 | AR5K_STA_ID1_PWR_SV : 0); |
74 | case NL80211_IFTYPE_MONITOR: | 85 | case NL80211_IFTYPE_MONITOR: |
75 | pcu_reg |= AR5K_STA_ID1_DEFAULT_ANTENNA | | 86 | pcu_reg |= AR5K_STA_ID1_KEYSRCH_MODE |
76 | (ah->ah_version == AR5K_AR5210 ? | 87 | | (ah->ah_version == AR5K_AR5210 ? |
77 | AR5K_STA_ID1_NO_PSPOLL : 0); | 88 | AR5K_STA_ID1_NO_PSPOLL : 0); |
78 | break; | 89 | break; |
79 | 90 | ||
@@ -130,6 +141,8 @@ void ath5k_hw_update_mib_counters(struct ath5k_hw *ah, | |||
130 | ath5k_hw_reg_write(ah, 0, AR5K_PROFCNT_RXCLR); | 141 | ath5k_hw_reg_write(ah, 0, AR5K_PROFCNT_RXCLR); |
131 | ath5k_hw_reg_write(ah, 0, AR5K_PROFCNT_CYCLE); | 142 | ath5k_hw_reg_write(ah, 0, AR5K_PROFCNT_CYCLE); |
132 | } | 143 | } |
144 | |||
145 | /* TODO: Handle ANI stats */ | ||
133 | } | 146 | } |
134 | 147 | ||
135 | /** | 148 | /** |
@@ -258,16 +271,19 @@ void ath5k_hw_get_lladdr(struct ath5k_hw *ah, u8 *mac) | |||
258 | int ath5k_hw_set_lladdr(struct ath5k_hw *ah, const u8 *mac) | 271 | int ath5k_hw_set_lladdr(struct ath5k_hw *ah, const u8 *mac) |
259 | { | 272 | { |
260 | u32 low_id, high_id; | 273 | u32 low_id, high_id; |
274 | u32 pcu_reg; | ||
261 | 275 | ||
262 | ATH5K_TRACE(ah->ah_sc); | 276 | ATH5K_TRACE(ah->ah_sc); |
263 | /* Set new station ID */ | 277 | /* Set new station ID */ |
264 | memcpy(ah->ah_sta_id, mac, ETH_ALEN); | 278 | memcpy(ah->ah_sta_id, mac, ETH_ALEN); |
265 | 279 | ||
280 | pcu_reg = ath5k_hw_reg_read(ah, AR5K_STA_ID1) & 0xffff0000; | ||
281 | |||
266 | low_id = AR5K_LOW_ID(mac); | 282 | low_id = AR5K_LOW_ID(mac); |
267 | high_id = AR5K_HIGH_ID(mac); | 283 | high_id = AR5K_HIGH_ID(mac); |
268 | 284 | ||
269 | ath5k_hw_reg_write(ah, low_id, AR5K_STA_ID0); | 285 | ath5k_hw_reg_write(ah, low_id, AR5K_STA_ID0); |
270 | ath5k_hw_reg_write(ah, high_id, AR5K_STA_ID1); | 286 | ath5k_hw_reg_write(ah, pcu_reg | high_id, AR5K_STA_ID1); |
271 | 287 | ||
272 | return 0; | 288 | return 0; |
273 | } | 289 | } |
@@ -290,8 +306,10 @@ void ath5k_hw_set_associd(struct ath5k_hw *ah, const u8 *bssid, u16 assoc_id) | |||
290 | * Set simple BSSID mask on 5212 | 306 | * Set simple BSSID mask on 5212 |
291 | */ | 307 | */ |
292 | if (ah->ah_version == AR5K_AR5212) { | 308 | if (ah->ah_version == AR5K_AR5212) { |
293 | ath5k_hw_reg_write(ah, 0xffffffff, AR5K_BSS_IDM0); | 309 | ath5k_hw_reg_write(ah, AR5K_LOW_ID(ah->ah_bssid_mask), |
294 | ath5k_hw_reg_write(ah, 0xffffffff, AR5K_BSS_IDM1); | 310 | AR5K_BSS_IDM0); |
311 | ath5k_hw_reg_write(ah, AR5K_HIGH_ID(ah->ah_bssid_mask), | ||
312 | AR5K_BSS_IDM1); | ||
295 | } | 313 | } |
296 | 314 | ||
297 | /* | 315 | /* |
@@ -415,6 +433,9 @@ int ath5k_hw_set_bssid_mask(struct ath5k_hw *ah, const u8 *mask) | |||
415 | u32 low_id, high_id; | 433 | u32 low_id, high_id; |
416 | ATH5K_TRACE(ah->ah_sc); | 434 | ATH5K_TRACE(ah->ah_sc); |
417 | 435 | ||
436 | /* Cache bssid mask so that we can restore it | ||
437 | * on reset */ | ||
438 | memcpy(ah->ah_bssid_mask, mask, ETH_ALEN); | ||
418 | if (ah->ah_version == AR5K_AR5212) { | 439 | if (ah->ah_version == AR5K_AR5212) { |
419 | low_id = AR5K_LOW_ID(mask); | 440 | low_id = AR5K_LOW_ID(mask); |
420 | high_id = AR5K_HIGH_ID(mask); | 441 | high_id = AR5K_HIGH_ID(mask); |
@@ -576,7 +597,7 @@ void ath5k_hw_set_rx_filter(struct ath5k_hw *ah, u32 filter) | |||
576 | filter |= AR5K_RX_FILTER_PROM; | 597 | filter |= AR5K_RX_FILTER_PROM; |
577 | } | 598 | } |
578 | 599 | ||
579 | /*Zero length DMA*/ | 600 | /*Zero length DMA (phy error reporting) */ |
580 | if (data) | 601 | if (data) |
581 | AR5K_REG_ENABLE_BITS(ah, AR5K_RXCFG, AR5K_RXCFG_ZLFDMA); | 602 | AR5K_REG_ENABLE_BITS(ah, AR5K_RXCFG, AR5K_RXCFG_ZLFDMA); |
582 | else | 603 | else |
@@ -661,7 +682,12 @@ void ath5k_hw_init_beacon(struct ath5k_hw *ah, u32 next_beacon, u32 interval) | |||
661 | * Set the additional timers by mode | 682 | * Set the additional timers by mode |
662 | */ | 683 | */ |
663 | switch (ah->ah_op_mode) { | 684 | switch (ah->ah_op_mode) { |
685 | case NL80211_IFTYPE_MONITOR: | ||
664 | case NL80211_IFTYPE_STATION: | 686 | case NL80211_IFTYPE_STATION: |
687 | /* In STA mode timer1 is used as next wakeup | ||
688 | * timer and timer2 as next CFP duration start | ||
689 | * timer. Both in 1/8TUs. */ | ||
690 | /* TODO: PCF handling */ | ||
665 | if (ah->ah_version == AR5K_AR5210) { | 691 | if (ah->ah_version == AR5K_AR5210) { |
666 | timer1 = 0xffffffff; | 692 | timer1 = 0xffffffff; |
667 | timer2 = 0xffffffff; | 693 | timer2 = 0xffffffff; |
@@ -669,27 +695,60 @@ void ath5k_hw_init_beacon(struct ath5k_hw *ah, u32 next_beacon, u32 interval) | |||
669 | timer1 = 0x0000ffff; | 695 | timer1 = 0x0000ffff; |
670 | timer2 = 0x0007ffff; | 696 | timer2 = 0x0007ffff; |
671 | } | 697 | } |
698 | /* Mark associated AP as PCF incapable for now */ | ||
699 | AR5K_REG_DISABLE_BITS(ah, AR5K_STA_ID1, AR5K_STA_ID1_PCF); | ||
672 | break; | 700 | break; |
673 | 701 | case NL80211_IFTYPE_ADHOC: | |
702 | AR5K_REG_ENABLE_BITS(ah, AR5K_TXCFG, AR5K_TXCFG_ADHOC_BCN_ATIM); | ||
674 | default: | 703 | default: |
704 | /* On non-STA modes timer1 is used as next DMA | ||
705 | * beacon alert (DBA) timer and timer2 as next | ||
706 | * software beacon alert. Both in 1/8TUs. */ | ||
675 | timer1 = (next_beacon - AR5K_TUNE_DMA_BEACON_RESP) << 3; | 707 | timer1 = (next_beacon - AR5K_TUNE_DMA_BEACON_RESP) << 3; |
676 | timer2 = (next_beacon - AR5K_TUNE_SW_BEACON_RESP) << 3; | 708 | timer2 = (next_beacon - AR5K_TUNE_SW_BEACON_RESP) << 3; |
709 | break; | ||
677 | } | 710 | } |
678 | 711 | ||
712 | /* Timer3 marks the end of our ATIM window | ||
713 | * a zero length window is not allowed because | ||
714 | * we 'll get no beacons */ | ||
679 | timer3 = next_beacon + (ah->ah_atim_window ? ah->ah_atim_window : 1); | 715 | timer3 = next_beacon + (ah->ah_atim_window ? ah->ah_atim_window : 1); |
680 | 716 | ||
681 | /* | 717 | /* |
682 | * Set the beacon register and enable all timers. | 718 | * Set the beacon register and enable all timers. |
683 | * (next beacon, DMA beacon, software beacon, ATIM window time) | ||
684 | */ | 719 | */ |
685 | ath5k_hw_reg_write(ah, next_beacon, AR5K_TIMER0); | 720 | /* When in AP mode zero timer0 to start TSF */ |
721 | if (ah->ah_op_mode == NL80211_IFTYPE_AP) | ||
722 | ath5k_hw_reg_write(ah, 0, AR5K_TIMER0); | ||
723 | else | ||
724 | ath5k_hw_reg_write(ah, next_beacon, AR5K_TIMER0); | ||
686 | ath5k_hw_reg_write(ah, timer1, AR5K_TIMER1); | 725 | ath5k_hw_reg_write(ah, timer1, AR5K_TIMER1); |
687 | ath5k_hw_reg_write(ah, timer2, AR5K_TIMER2); | 726 | ath5k_hw_reg_write(ah, timer2, AR5K_TIMER2); |
688 | ath5k_hw_reg_write(ah, timer3, AR5K_TIMER3); | 727 | ath5k_hw_reg_write(ah, timer3, AR5K_TIMER3); |
689 | 728 | ||
729 | /* Force a TSF reset if requested and enable beacons */ | ||
730 | if (interval & AR5K_BEACON_RESET_TSF) | ||
731 | ath5k_hw_reset_tsf(ah); | ||
732 | |||
690 | ath5k_hw_reg_write(ah, interval & (AR5K_BEACON_PERIOD | | 733 | ath5k_hw_reg_write(ah, interval & (AR5K_BEACON_PERIOD | |
691 | AR5K_BEACON_RESET_TSF | AR5K_BEACON_ENABLE), | 734 | AR5K_BEACON_ENABLE), |
692 | AR5K_BEACON); | 735 | AR5K_BEACON); |
736 | |||
737 | /* Flush any pending BMISS interrupts on ISR by | ||
738 | * performing a clear-on-write operation on PISR | ||
739 | * register for the BMISS bit (writing a bit on | ||
740 | * ISR togles a reset for that bit and leaves | ||
741 | * the rest bits intact) */ | ||
742 | if (ah->ah_version == AR5K_AR5210) | ||
743 | ath5k_hw_reg_write(ah, AR5K_ISR_BMISS, AR5K_ISR); | ||
744 | else | ||
745 | ath5k_hw_reg_write(ah, AR5K_ISR_BMISS, AR5K_PISR); | ||
746 | |||
747 | /* TODO: Set enchanced sleep registers on AR5212 | ||
748 | * based on vif->bss_conf params, until then | ||
749 | * disable power save reporting.*/ | ||
750 | AR5K_REG_DISABLE_BITS(ah, AR5K_STA_ID1, AR5K_STA_ID1_PWR_SV); | ||
751 | |||
693 | } | 752 | } |
694 | 753 | ||
695 | #if 0 | 754 | #if 0 |
@@ -899,14 +958,26 @@ int ath5k_hw_beaconq_finish(struct ath5k_hw *ah, unsigned long phys_addr) | |||
899 | */ | 958 | */ |
900 | int ath5k_hw_reset_key(struct ath5k_hw *ah, u16 entry) | 959 | int ath5k_hw_reset_key(struct ath5k_hw *ah, u16 entry) |
901 | { | 960 | { |
902 | unsigned int i; | 961 | unsigned int i, type; |
962 | u16 micentry = entry + AR5K_KEYTABLE_MIC_OFFSET; | ||
903 | 963 | ||
904 | ATH5K_TRACE(ah->ah_sc); | 964 | ATH5K_TRACE(ah->ah_sc); |
905 | AR5K_ASSERT_ENTRY(entry, AR5K_KEYTABLE_SIZE); | 965 | AR5K_ASSERT_ENTRY(entry, AR5K_KEYTABLE_SIZE); |
906 | 966 | ||
967 | type = ath5k_hw_reg_read(ah, AR5K_KEYTABLE_TYPE(entry)); | ||
968 | |||
907 | for (i = 0; i < AR5K_KEYCACHE_SIZE; i++) | 969 | for (i = 0; i < AR5K_KEYCACHE_SIZE; i++) |
908 | ath5k_hw_reg_write(ah, 0, AR5K_KEYTABLE_OFF(entry, i)); | 970 | ath5k_hw_reg_write(ah, 0, AR5K_KEYTABLE_OFF(entry, i)); |
909 | 971 | ||
972 | /* Reset associated MIC entry if TKIP | ||
973 | * is enabled located at offset (entry + 64) */ | ||
974 | if (type == AR5K_KEYTABLE_TYPE_TKIP) { | ||
975 | AR5K_ASSERT_ENTRY(micentry, AR5K_KEYTABLE_SIZE); | ||
976 | for (i = 0; i < AR5K_KEYCACHE_SIZE / 2 ; i++) | ||
977 | ath5k_hw_reg_write(ah, 0, | ||
978 | AR5K_KEYTABLE_OFF(micentry, i)); | ||
979 | } | ||
980 | |||
910 | /* | 981 | /* |
911 | * Set NULL encryption on AR5212+ | 982 | * Set NULL encryption on AR5212+ |
912 | * | 983 | * |
@@ -916,10 +987,16 @@ int ath5k_hw_reset_key(struct ath5k_hw *ah, u16 entry) | |||
916 | * Note2: Windows driver (ndiswrapper) sets this to | 987 | * Note2: Windows driver (ndiswrapper) sets this to |
917 | * 0x00000714 instead of 0x00000007 | 988 | * 0x00000714 instead of 0x00000007 |
918 | */ | 989 | */ |
919 | if (ah->ah_version > AR5K_AR5211) | 990 | if (ah->ah_version > AR5K_AR5211) { |
920 | ath5k_hw_reg_write(ah, AR5K_KEYTABLE_TYPE_NULL, | 991 | ath5k_hw_reg_write(ah, AR5K_KEYTABLE_TYPE_NULL, |
921 | AR5K_KEYTABLE_TYPE(entry)); | 992 | AR5K_KEYTABLE_TYPE(entry)); |
922 | 993 | ||
994 | if (type == AR5K_KEYTABLE_TYPE_TKIP) { | ||
995 | ath5k_hw_reg_write(ah, AR5K_KEYTABLE_TYPE_NULL, | ||
996 | AR5K_KEYTABLE_TYPE(micentry)); | ||
997 | } | ||
998 | } | ||
999 | |||
923 | return 0; | 1000 | return 0; |
924 | } | 1001 | } |
925 | 1002 | ||
@@ -936,6 +1013,23 @@ int ath5k_hw_is_key_valid(struct ath5k_hw *ah, u16 entry) | |||
936 | AR5K_KEYTABLE_VALID; | 1013 | AR5K_KEYTABLE_VALID; |
937 | } | 1014 | } |
938 | 1015 | ||
1016 | static | ||
1017 | int ath5k_keycache_type(const struct ieee80211_key_conf *key) | ||
1018 | { | ||
1019 | switch (key->alg) { | ||
1020 | case ALG_TKIP: | ||
1021 | return AR5K_KEYTABLE_TYPE_TKIP; | ||
1022 | case ALG_CCMP: | ||
1023 | return AR5K_KEYTABLE_TYPE_CCM; | ||
1024 | case ALG_WEP: | ||
1025 | if (key->keylen == LEN_WEP40) | ||
1026 | return AR5K_KEYTABLE_TYPE_40; | ||
1027 | else if (key->keylen == LEN_WEP104) | ||
1028 | return AR5K_KEYTABLE_TYPE_104; | ||
1029 | } | ||
1030 | return -EINVAL; | ||
1031 | } | ||
1032 | |||
939 | /* | 1033 | /* |
940 | * Set a key entry on the table | 1034 | * Set a key entry on the table |
941 | */ | 1035 | */ |
@@ -943,40 +1037,53 @@ int ath5k_hw_set_key(struct ath5k_hw *ah, u16 entry, | |||
943 | const struct ieee80211_key_conf *key, const u8 *mac) | 1037 | const struct ieee80211_key_conf *key, const u8 *mac) |
944 | { | 1038 | { |
945 | unsigned int i; | 1039 | unsigned int i; |
1040 | int keylen; | ||
946 | __le32 key_v[5] = {}; | 1041 | __le32 key_v[5] = {}; |
1042 | __le32 key0 = 0, key1 = 0; | ||
1043 | __le32 *rxmic, *txmic; | ||
947 | u32 keytype; | 1044 | u32 keytype; |
1045 | u16 micentry = entry + AR5K_KEYTABLE_MIC_OFFSET; | ||
1046 | bool is_tkip; | ||
1047 | const u8 *key_ptr; | ||
948 | 1048 | ||
949 | ATH5K_TRACE(ah->ah_sc); | 1049 | ATH5K_TRACE(ah->ah_sc); |
950 | 1050 | ||
951 | /* key->keylen comes in from mac80211 in bytes */ | 1051 | is_tkip = (key->alg == ALG_TKIP); |
952 | 1052 | ||
953 | if (key->keylen > AR5K_KEYTABLE_SIZE / 8) | 1053 | /* |
1054 | * key->keylen comes in from mac80211 in bytes. | ||
1055 | * TKIP is 128 bit + 128 bit mic | ||
1056 | */ | ||
1057 | keylen = (is_tkip) ? (128 / 8) : key->keylen; | ||
1058 | |||
1059 | if (entry > AR5K_KEYTABLE_SIZE || | ||
1060 | (is_tkip && micentry > AR5K_KEYTABLE_SIZE)) | ||
954 | return -EOPNOTSUPP; | 1061 | return -EOPNOTSUPP; |
955 | 1062 | ||
956 | switch (key->keylen) { | 1063 | if (unlikely(keylen > 16)) |
957 | /* WEP 40-bit = 40-bit entered key + 24 bit IV = 64-bit */ | 1064 | return -EOPNOTSUPP; |
958 | case 40 / 8: | ||
959 | memcpy(&key_v[0], key->key, 5); | ||
960 | keytype = AR5K_KEYTABLE_TYPE_40; | ||
961 | break; | ||
962 | 1065 | ||
963 | /* WEP 104-bit = 104-bit entered key + 24-bit IV = 128-bit */ | 1066 | keytype = ath5k_keycache_type(key); |
964 | case 104 / 8: | 1067 | if (keytype < 0) |
965 | memcpy(&key_v[0], &key->key[0], 6); | 1068 | return keytype; |
966 | memcpy(&key_v[2], &key->key[6], 6); | ||
967 | memcpy(&key_v[4], &key->key[12], 1); | ||
968 | keytype = AR5K_KEYTABLE_TYPE_104; | ||
969 | break; | ||
970 | /* WEP 128-bit = 128-bit entered key + 24 bit IV = 152-bit */ | ||
971 | case 128 / 8: | ||
972 | memcpy(&key_v[0], &key->key[0], 6); | ||
973 | memcpy(&key_v[2], &key->key[6], 6); | ||
974 | memcpy(&key_v[4], &key->key[12], 4); | ||
975 | keytype = AR5K_KEYTABLE_TYPE_128; | ||
976 | break; | ||
977 | 1069 | ||
978 | default: | 1070 | /* |
979 | return -EINVAL; /* shouldn't happen */ | 1071 | * each key block is 6 bytes wide, written as pairs of |
1072 | * alternating 32 and 16 bit le values. | ||
1073 | */ | ||
1074 | key_ptr = key->key; | ||
1075 | for (i = 0; keylen >= 6; keylen -= 6) { | ||
1076 | memcpy(&key_v[i], key_ptr, 6); | ||
1077 | i += 2; | ||
1078 | key_ptr += 6; | ||
1079 | } | ||
1080 | if (keylen) | ||
1081 | memcpy(&key_v[i], key_ptr, keylen); | ||
1082 | |||
1083 | /* intentionally corrupt key until mic is installed */ | ||
1084 | if (is_tkip) { | ||
1085 | key0 = key_v[0] = ~key_v[0]; | ||
1086 | key1 = key_v[1] = ~key_v[1]; | ||
980 | } | 1087 | } |
981 | 1088 | ||
982 | for (i = 0; i < ARRAY_SIZE(key_v); i++) | 1089 | for (i = 0; i < ARRAY_SIZE(key_v); i++) |
@@ -985,6 +1092,40 @@ int ath5k_hw_set_key(struct ath5k_hw *ah, u16 entry, | |||
985 | 1092 | ||
986 | ath5k_hw_reg_write(ah, keytype, AR5K_KEYTABLE_TYPE(entry)); | 1093 | ath5k_hw_reg_write(ah, keytype, AR5K_KEYTABLE_TYPE(entry)); |
987 | 1094 | ||
1095 | if (is_tkip) { | ||
1096 | /* Install rx/tx MIC */ | ||
1097 | rxmic = (__le32 *) &key->key[16]; | ||
1098 | txmic = (__le32 *) &key->key[24]; | ||
1099 | |||
1100 | if (ah->ah_combined_mic) { | ||
1101 | key_v[0] = rxmic[0]; | ||
1102 | key_v[1] = cpu_to_le32(le32_to_cpu(txmic[0]) >> 16); | ||
1103 | key_v[2] = rxmic[1]; | ||
1104 | key_v[3] = cpu_to_le32(le32_to_cpu(txmic[0]) & 0xffff); | ||
1105 | key_v[4] = txmic[1]; | ||
1106 | } else { | ||
1107 | key_v[0] = rxmic[0]; | ||
1108 | key_v[1] = 0; | ||
1109 | key_v[2] = rxmic[1]; | ||
1110 | key_v[3] = 0; | ||
1111 | key_v[4] = 0; | ||
1112 | } | ||
1113 | for (i = 0; i < ARRAY_SIZE(key_v); i++) | ||
1114 | ath5k_hw_reg_write(ah, le32_to_cpu(key_v[i]), | ||
1115 | AR5K_KEYTABLE_OFF(micentry, i)); | ||
1116 | |||
1117 | ath5k_hw_reg_write(ah, AR5K_KEYTABLE_TYPE_NULL, | ||
1118 | AR5K_KEYTABLE_TYPE(micentry)); | ||
1119 | ath5k_hw_reg_write(ah, 0, AR5K_KEYTABLE_MAC0(micentry)); | ||
1120 | ath5k_hw_reg_write(ah, 0, AR5K_KEYTABLE_MAC1(micentry)); | ||
1121 | |||
1122 | /* restore first 2 words of key */ | ||
1123 | ath5k_hw_reg_write(ah, le32_to_cpu(~key0), | ||
1124 | AR5K_KEYTABLE_OFF(entry, 0)); | ||
1125 | ath5k_hw_reg_write(ah, le32_to_cpu(~key1), | ||
1126 | AR5K_KEYTABLE_OFF(entry, 1)); | ||
1127 | } | ||
1128 | |||
988 | return ath5k_hw_set_key_lladdr(ah, entry, mac); | 1129 | return ath5k_hw_set_key_lladdr(ah, entry, mac); |
989 | } | 1130 | } |
990 | 1131 | ||