Index: ath.c
===================================================================
RCS file: /data/netbsd/cvs/src/sys/dev/ic/ath.c,v
retrieving revision 1.76
diff -u -r1.76 ath.c
--- ath.c       14 Jul 2006 13:37:25 -0000      1.76
+++ ath.c       9 Sep 2006 23:22:25 -0000
@@ -255,7 +255,7 @@
} while (0)
#define        KEYPRINTF(sc, ix, hk, mac) do {                         \
       if (sc->sc_debug & ATH_DEBUG_KEYCACHE)                  \
-               ath_keyprint(__func__, ix, hk, mac);            \
+               ath_keyprint(sc, __func__, ix, hk, mac);        \
} while (0)
static void ath_printrxbuf(struct ath_buf *bf, int);
static void ath_printtxbuf(struct ath_buf *bf, int);
@@ -375,18 +375,6 @@
        */
       for (i = 0; i < sc->sc_keymax; i++)
               ath_hal_keyreset(ah, i);
-       /*
-        * Mark key cache slots associated with global keys
-        * as in use.  If we knew TKIP was not to be used we
-        * could leave the +32, +64, and +32+64 slots free.
-        * XXX only for splitmic.
-        */
-       for (i = 0; i < IEEE80211_WEP_NKID; i++) {
-               setbit(sc->sc_keymap, i);
-               setbit(sc->sc_keymap, i+32);
-               setbit(sc->sc_keymap, i+64);
-               setbit(sc->sc_keymap, i+32+64);
-       }

       /*
        * Collect the channel list using the default country
@@ -567,12 +555,31 @@
                */
               if (ath_hal_ciphersupported(ah, HAL_CIPHER_MIC))
                       ic->ic_caps |= IEEE80211_C_TKIPMIC;
-               if (ath_hal_tkipsplit(ah))
+               /*
+                * If the h/w supports storing tx+rx MIC keys
+                * in one cache slot automatically enable use.
+                */
+               if (ath_hal_hastkipsplit(ah) ||
+                   !ath_hal_settkipsplit(ah, AH_FALSE))
                       sc->sc_splitmic = 1;
       }
       sc->sc_hasclrkey = ath_hal_ciphersupported(ah, HAL_CIPHER_CLR);
       sc->sc_mcastkey = ath_hal_getmcastkeysearch(ah);
       /*
+        * Mark key cache slots associated with global keys
+        * as in use.  If we knew TKIP was not to be used we
+        * could leave the +32, +64, and +32+64 slots free.
+        * XXX only for splitmic.
+        */
+       for (i = 0; i < IEEE80211_WEP_NKID; i++) {
+               setbit(sc->sc_keymap, i);
+               setbit(sc->sc_keymap, i+64);
+               if (sc->sc_splitmic) {
+                       setbit(sc->sc_keymap, i+32);
+                       setbit(sc->sc_keymap, i+32+64);
+               }
+       }
+       /*
        * TPC support can be done either with a global cap or
        * per-packet support.  The latter is not available on
        * all parts.  We're a bit pedantic here as all parts
@@ -1410,7 +1417,7 @@

#ifdef AR_DEBUG
static void
-ath_keyprint(const char *tag, u_int ix,
+ath_keyprint(struct ath_softc *sc, const char *tag, u_int ix,
       const HAL_KEYVAL *hk, const u_int8_t mac[IEEE80211_ADDR_LEN])
{
       static const char *ciphers[] = {
@@ -1428,9 +1435,16 @@
               printf("%02x", hk->kv_val[i]);
       printf(" mac %s", ether_sprintf(mac));
       if (hk->kv_type == HAL_CIPHER_TKIP) {
-               printf(" mic ");
+               printf(" %s ", sc->sc_splitmic ? "mic" : "rxmic");
               for (i = 0; i < sizeof(hk->kv_mic); i++)
                       printf("%02x", hk->kv_mic[i]);
+#if HAL_ABI_VERSION > 0x06052200
+               if (!sc->sc_splitmic) {
+                       printf(" txmic ");
+                       for (i = 0; i < sizeof(hk->kv_txmic); i++)
+                               printf("%02x", hk->kv_txmic[i]);
+               }
+#endif
       }
       printf("\n");
}
@@ -1451,21 +1465,34 @@

       KASSERT(k->wk_cipher->ic_cipher == IEEE80211_CIPHER_TKIP,
               ("got a non-TKIP key, cipher %u", k->wk_cipher->ic_cipher));
-       KASSERT(sc->sc_splitmic, ("key cache !split"));
       if ((k->wk_flags & IEEE80211_KEY_XR) == IEEE80211_KEY_XR) {
-               /*
-                * TX key goes at first index, RX key at the rx index.
-                * The hal handles the MIC keys at index+64.
-                */
-               memcpy(hk->kv_mic, k->wk_txmic, sizeof(hk->kv_mic));
-               KEYPRINTF(sc, k->wk_keyix, hk, zerobssid);
-               if (!ath_hal_keyset(ah, k->wk_keyix, hk, zerobssid))
-                       return 0;
-
-               memcpy(hk->kv_mic, k->wk_rxmic, sizeof(hk->kv_mic));
-               KEYPRINTF(sc, k->wk_keyix+32, hk, mac);
-               /* XXX delete tx key on failure? */
-               return ath_hal_keyset(ah, k->wk_keyix+32, hk, mac);
+               if (sc->sc_splitmic) {
+                       /*
+                        * TX key goes at first index, RX key at the rx index.
+                        * The hal handles the MIC keys at index+64.
+                        */
+                       memcpy(hk->kv_mic, k->wk_txmic, sizeof(hk->kv_mic));
+                       KEYPRINTF(sc, k->wk_keyix, hk, zerobssid);
+                       if (!ath_hal_keyset(ah, k->wk_keyix, hk, zerobssid))
+                               return 0;
+
+                       memcpy(hk->kv_mic, k->wk_rxmic, sizeof(hk->kv_mic));
+                       KEYPRINTF(sc, k->wk_keyix+32, hk, mac);
+                       /* XXX delete tx key on failure? */
+                       return ath_hal_keyset(ah, k->wk_keyix+32, hk, mac);
+               } else {
+                       /*
+                        * Room for both TX+RX MIC keys in one key cache
+                        * slot, just set key at the first index; the hal
+                        * will handle the reset.
+                        */
+                       memcpy(hk->kv_mic, k->wk_rxmic, sizeof(hk->kv_mic));
+#if HAL_ABI_VERSION > 0x06052200
+                       memcpy(hk->kv_txmic, k->wk_txmic, sizeof(hk->kv_txmic));
+#endif
+                       KEYPRINTF(sc, k->wk_keyix, hk, mac);
+                       return ath_hal_keyset(ah, k->wk_keyix, hk, mac);
+               }
       } else if (k->wk_flags & IEEE80211_KEY_XR) {
               /*
                * TX/RX key goes at first index.
@@ -1534,8 +1561,7 @@
               mac = mac0;

       if (hk.kv_type == HAL_CIPHER_TKIP &&
-           (k->wk_flags & IEEE80211_KEY_SWMIC) == 0 &&
-           sc->sc_splitmic) {
+           (k->wk_flags & IEEE80211_KEY_SWMIC) == 0) {
               return ath_keyset_tkip(sc, k, &hk, mac);
       } else {
               KEYPRINTF(sc, k->wk_keyix, &hk, mac);
@@ -1600,6 +1626,54 @@
}

/*
+ * Allocate tx/rx key slots for TKIP.  We allocate two slots for
+ * each key, one for decrypt/encrypt and the other for the MIC.
+ */
+static u_int16_t
+key_alloc_pair(struct ath_softc *sc,
+       ieee80211_keyix *txkeyix, ieee80211_keyix *rxkeyix)
+{
+#define        N(a)    (sizeof(a)/sizeof(a[0]))
+       u_int i, keyix;
+
+       KASSERT(!sc->sc_splitmic, ("key cache split"));
+       /* XXX could optimize */
+       for (i = 0; i < N(sc->sc_keymap)/4; i++) {
+               u_int8_t b = sc->sc_keymap[i];
+               if (b != 0xff) {
+                       /*
+                        * One or more slots in this byte are free.
+                        */
+                       keyix = i*NBBY;
+                       while (b & 1) {
+               again:
+                               keyix++;
+                               b >>= 1;
+                       }
+                       if (isset(sc->sc_keymap, keyix+64)) {
+                               /* full pair unavailable */
+                               /* XXX statistic */
+                               if (keyix == (i+1)*NBBY) {
+                                       /* no slots were appropriate, advance */
+                                       continue;
+                               }
+                               goto again;
+                       }
+                       setbit(sc->sc_keymap, keyix);
+                       setbit(sc->sc_keymap, keyix+64);
+                       DPRINTF(sc, ATH_DEBUG_KEYCACHE,
+                               "%s: key pair %u,%u\n",
+                               __func__, keyix, keyix+64);
+                       *txkeyix = *rxkeyix = keyix;
+                       return 1;
+               }
+       }
+       DPRINTF(sc, ATH_DEBUG_KEYCACHE, "%s: out of pair space\n", __func__);
+       return 0;
+#undef N
+}
+
+/*
 * Allocate a single key cache slot.
 */
static int
@@ -1684,8 +1758,11 @@
       if (k->wk_flags & IEEE80211_KEY_SWCRYPT) {
               return key_alloc_single(sc, keyix, rxkeyix);
       } else if (k->wk_cipher->ic_cipher == IEEE80211_CIPHER_TKIP &&
-           (k->wk_flags & IEEE80211_KEY_SWMIC) == 0 && sc->sc_splitmic) {
-               return key_alloc_2pair(sc, keyix, rxkeyix);
+           (k->wk_flags & IEEE80211_KEY_SWMIC) == 0) {
+               if (sc->sc_splitmic)
+                       return key_alloc_2pair(sc, keyix, rxkeyix);
+               else
+                       return key_alloc_pair(sc, keyix, rxkeyix);
       } else {
               return key_alloc_single(sc, keyix, rxkeyix);
       }
@@ -1718,11 +1795,13 @@
                */
               clrbit(sc->sc_keymap, keyix);
               if (cip->ic_cipher == IEEE80211_CIPHER_TKIP &&
-                   (k->wk_flags & IEEE80211_KEY_SWMIC) == 0 &&
-                   sc->sc_splitmic) {
+                   (k->wk_flags & IEEE80211_KEY_SWMIC) == 0) {
                       clrbit(sc->sc_keymap, keyix+64);        /* TX key MIC */
-                       clrbit(sc->sc_keymap, keyix+32);        /* RX key */
-                       clrbit(sc->sc_keymap, keyix+32+64);     /* RX key MIC */
+                       if (sc->sc_splitmic) {
+                               /* +32 for RX key, +32+64 for RX key MIC */
+                               clrbit(sc->sc_keymap, keyix+32);
+                               clrbit(sc->sc_keymap, keyix+32+64);
+                       }
               }
       }
       return 1;
Index: athvar.h
===================================================================
RCS file: /data/netbsd/cvs/src/sys/dev/ic/athvar.h,v
retrieving revision 1.19
diff -u -r1.19 athvar.h
--- athvar.h    14 Jul 2006 13:37:25 -0000      1.19
+++ athvar.h    9 Sep 2006 23:23:01 -0000
@@ -459,8 +459,12 @@
       ((*(_ah)->ah_setRegulatoryDomain)((_ah), (_rd), NULL))
#define        ath_hal_getcountrycode(_ah, _pcc) \
       (*(_pcc) = (_ah)->ah_countryCode)
-#define        ath_hal_tkipsplit(_ah) \
+#define        ath_hal_hastkipsplit(_ah) \
       (ath_hal_getcapability(_ah, HAL_CAP_TKIP_SPLIT, 0, NULL) == HAL_OK)
+#define        ath_hal_gettkipsplit(_ah) \
+       (ath_hal_getcapability(_ah, HAL_CAP_TKIP_SPLIT, 1, NULL) == HAL_OK)
+#define        ath_hal_settkipsplit(_ah, _v) \
+       ath_hal_setcapability(_ah, HAL_CAP_TKIP_SPLIT, 1, _v, NULL)
#define        ath_hal_hwphycounters(_ah) \
       (ath_hal_getcapability(_ah, HAL_CAP_PHYCOUNTERS, 0, NULL) == HAL_OK)
#define        ath_hal_hasdiversity(_ah) \