xref: /freebsd/sys/dev/rtwn/if_rtwn_cam.c (revision 2589197adb199ec37f132dd7e279eb0795713f1e)
17453645fSAndriy Voskoboinyk /*	$OpenBSD: if_urtwn.c,v 1.16 2011/02/10 17:26:40 jakemsr Exp $	*/
27453645fSAndriy Voskoboinyk 
37453645fSAndriy Voskoboinyk /*-
47453645fSAndriy Voskoboinyk  * Copyright (c) 2010 Damien Bergamini <damien.bergamini@free.fr>
57453645fSAndriy Voskoboinyk  * Copyright (c) 2014 Kevin Lo <kevlo@FreeBSD.org>
67453645fSAndriy Voskoboinyk  * Copyright (c) 2015-2016 Andriy Voskoboinyk <avos@FreeBSD.org>
77453645fSAndriy Voskoboinyk  *
87453645fSAndriy Voskoboinyk  * Permission to use, copy, modify, and distribute this software for any
97453645fSAndriy Voskoboinyk  * purpose with or without fee is hereby granted, provided that the above
107453645fSAndriy Voskoboinyk  * copyright notice and this permission notice appear in all copies.
117453645fSAndriy Voskoboinyk  *
127453645fSAndriy Voskoboinyk  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
137453645fSAndriy Voskoboinyk  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
147453645fSAndriy Voskoboinyk  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
157453645fSAndriy Voskoboinyk  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
167453645fSAndriy Voskoboinyk  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
177453645fSAndriy Voskoboinyk  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
187453645fSAndriy Voskoboinyk  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
197453645fSAndriy Voskoboinyk  */
207453645fSAndriy Voskoboinyk 
217453645fSAndriy Voskoboinyk #include <sys/cdefs.h>
227453645fSAndriy Voskoboinyk #include "opt_wlan.h"
237453645fSAndriy Voskoboinyk 
247453645fSAndriy Voskoboinyk #include <sys/param.h>
257453645fSAndriy Voskoboinyk #include <sys/lock.h>
267453645fSAndriy Voskoboinyk #include <sys/mutex.h>
277453645fSAndriy Voskoboinyk #include <sys/mbuf.h>
287453645fSAndriy Voskoboinyk #include <sys/kernel.h>
297453645fSAndriy Voskoboinyk #include <sys/socket.h>
307453645fSAndriy Voskoboinyk #include <sys/systm.h>
317453645fSAndriy Voskoboinyk #include <sys/malloc.h>
327453645fSAndriy Voskoboinyk #include <sys/queue.h>
337453645fSAndriy Voskoboinyk #include <sys/taskqueue.h>
347453645fSAndriy Voskoboinyk #include <sys/bus.h>
357453645fSAndriy Voskoboinyk #include <sys/endian.h>
367453645fSAndriy Voskoboinyk 
377453645fSAndriy Voskoboinyk #include <net/if.h>
387453645fSAndriy Voskoboinyk #include <net/ethernet.h>
397453645fSAndriy Voskoboinyk #include <net/if_media.h>
407453645fSAndriy Voskoboinyk 
417453645fSAndriy Voskoboinyk #include <net80211/ieee80211_var.h>
427453645fSAndriy Voskoboinyk #include <net80211/ieee80211_radiotap.h>
437453645fSAndriy Voskoboinyk 
447453645fSAndriy Voskoboinyk #include <dev/rtwn/if_rtwnreg.h>
457453645fSAndriy Voskoboinyk #include <dev/rtwn/if_rtwnvar.h>
467453645fSAndriy Voskoboinyk 
477453645fSAndriy Voskoboinyk #include <dev/rtwn/if_rtwn_cam.h>
487453645fSAndriy Voskoboinyk #include <dev/rtwn/if_rtwn_debug.h>
497453645fSAndriy Voskoboinyk #include <dev/rtwn/if_rtwn_task.h>
507453645fSAndriy Voskoboinyk 
517453645fSAndriy Voskoboinyk #include <dev/rtwn/rtl8192c/r92c_reg.h>
527453645fSAndriy Voskoboinyk 
537453645fSAndriy Voskoboinyk void
rtwn_init_cam(struct rtwn_softc * sc)547453645fSAndriy Voskoboinyk rtwn_init_cam(struct rtwn_softc *sc)
557453645fSAndriy Voskoboinyk {
567453645fSAndriy Voskoboinyk 	/* Invalidate all CAM entries. */
577453645fSAndriy Voskoboinyk 	rtwn_write_4(sc, R92C_CAMCMD,
587453645fSAndriy Voskoboinyk 	    R92C_CAMCMD_POLLING | R92C_CAMCMD_CLR);
597453645fSAndriy Voskoboinyk }
607453645fSAndriy Voskoboinyk 
617453645fSAndriy Voskoboinyk static int
rtwn_cam_write(struct rtwn_softc * sc,uint32_t addr,uint32_t data)627453645fSAndriy Voskoboinyk rtwn_cam_write(struct rtwn_softc *sc, uint32_t addr, uint32_t data)
637453645fSAndriy Voskoboinyk {
647453645fSAndriy Voskoboinyk 	int error;
657453645fSAndriy Voskoboinyk 
667453645fSAndriy Voskoboinyk 	error = rtwn_write_4(sc, R92C_CAMWRITE, data);
677453645fSAndriy Voskoboinyk 	if (error != 0)
687453645fSAndriy Voskoboinyk 		return (error);
697453645fSAndriy Voskoboinyk 	error = rtwn_write_4(sc, R92C_CAMCMD,
707453645fSAndriy Voskoboinyk 	    R92C_CAMCMD_POLLING | R92C_CAMCMD_WRITE |
717453645fSAndriy Voskoboinyk 	    SM(R92C_CAMCMD_ADDR, addr));
727453645fSAndriy Voskoboinyk 
737453645fSAndriy Voskoboinyk 	return (error);
747453645fSAndriy Voskoboinyk }
757453645fSAndriy Voskoboinyk 
767453645fSAndriy Voskoboinyk void
rtwn_init_seccfg(struct rtwn_softc * sc)777453645fSAndriy Voskoboinyk rtwn_init_seccfg(struct rtwn_softc *sc)
787453645fSAndriy Voskoboinyk {
797453645fSAndriy Voskoboinyk 	uint16_t seccfg;
807453645fSAndriy Voskoboinyk 
817453645fSAndriy Voskoboinyk 	/* Select decryption / encryption flags. */
827453645fSAndriy Voskoboinyk 	seccfg = 0;
837453645fSAndriy Voskoboinyk 	switch (sc->sc_hwcrypto) {
847453645fSAndriy Voskoboinyk 	case RTWN_CRYPTO_SW:
857453645fSAndriy Voskoboinyk 		break;	/* nothing to do */
867453645fSAndriy Voskoboinyk 	case RTWN_CRYPTO_PAIR:
877453645fSAndriy Voskoboinyk 		/* NB: TXUCKEY_DEF / RXUCKEY_DEF are required for RTL8192C */
887453645fSAndriy Voskoboinyk 		seccfg = R92C_SECCFG_TXUCKEY_DEF | R92C_SECCFG_RXUCKEY_DEF |
897453645fSAndriy Voskoboinyk 		    R92C_SECCFG_TXENC_ENA | R92C_SECCFG_RXDEC_ENA |
907453645fSAndriy Voskoboinyk 		    R92C_SECCFG_MC_SRCH_DIS;
917453645fSAndriy Voskoboinyk 		break;
927453645fSAndriy Voskoboinyk 	case RTWN_CRYPTO_FULL:
937453645fSAndriy Voskoboinyk 		seccfg = R92C_SECCFG_TXUCKEY_DEF | R92C_SECCFG_RXUCKEY_DEF |
947453645fSAndriy Voskoboinyk 		    R92C_SECCFG_TXENC_ENA | R92C_SECCFG_RXDEC_ENA |
957453645fSAndriy Voskoboinyk 		    R92C_SECCFG_TXBCKEY_DEF | R92C_SECCFG_RXBCKEY_DEF;
967453645fSAndriy Voskoboinyk 		break;
977453645fSAndriy Voskoboinyk 	default:
987453645fSAndriy Voskoboinyk 		KASSERT(0, ("%s: case %d was not handled\n", __func__,
997453645fSAndriy Voskoboinyk 		    sc->sc_hwcrypto));
1007453645fSAndriy Voskoboinyk 		break;
1017453645fSAndriy Voskoboinyk 	}
1027453645fSAndriy Voskoboinyk 
1037453645fSAndriy Voskoboinyk 	RTWN_DPRINTF(sc, RTWN_DEBUG_KEY, "%s: seccfg %04X, hwcrypto %d\n",
1047453645fSAndriy Voskoboinyk 	    __func__, seccfg, sc->sc_hwcrypto);
1057453645fSAndriy Voskoboinyk 
1067453645fSAndriy Voskoboinyk 	rtwn_write_2(sc, R92C_SECCFG, seccfg);
1077453645fSAndriy Voskoboinyk }
1087453645fSAndriy Voskoboinyk 
1097453645fSAndriy Voskoboinyk int
rtwn_key_alloc(struct ieee80211vap * vap,struct ieee80211_key * k,ieee80211_keyix * keyix,ieee80211_keyix * rxkeyix)1107453645fSAndriy Voskoboinyk rtwn_key_alloc(struct ieee80211vap *vap, struct ieee80211_key *k,
1117453645fSAndriy Voskoboinyk     ieee80211_keyix *keyix, ieee80211_keyix *rxkeyix)
1127453645fSAndriy Voskoboinyk {
1137453645fSAndriy Voskoboinyk 	struct rtwn_softc *sc = vap->iv_ic->ic_softc;
1147453645fSAndriy Voskoboinyk 	int i, start;
1157453645fSAndriy Voskoboinyk 
116*2589197aSAdrian Chadd 	if (ieee80211_is_key_global(vap, k)) {
1174a19d712SAndriy Voskoboinyk 		*keyix = ieee80211_crypto_get_key_wepidx(vap, k);
1187453645fSAndriy Voskoboinyk 		if (sc->sc_hwcrypto != RTWN_CRYPTO_FULL)
1197453645fSAndriy Voskoboinyk 			k->wk_flags |= IEEE80211_KEY_SWCRYPT;
1207453645fSAndriy Voskoboinyk 		else {
1217453645fSAndriy Voskoboinyk 			RTWN_LOCK(sc);
1227453645fSAndriy Voskoboinyk 			if (isset(sc->keys_bmap, *keyix)) {
1237453645fSAndriy Voskoboinyk 				device_printf(sc->sc_dev,
1247453645fSAndriy Voskoboinyk 				    "%s: group key slot %d is already used!\n",
1257453645fSAndriy Voskoboinyk 				    __func__, *keyix);
1267453645fSAndriy Voskoboinyk 				/* XXX recover? */
1277453645fSAndriy Voskoboinyk 				RTWN_UNLOCK(sc);
1287453645fSAndriy Voskoboinyk 				return (0);
1297453645fSAndriy Voskoboinyk 			}
1307453645fSAndriy Voskoboinyk 
1317453645fSAndriy Voskoboinyk 			setbit(sc->keys_bmap, *keyix);
1327453645fSAndriy Voskoboinyk 			RTWN_UNLOCK(sc);
1337453645fSAndriy Voskoboinyk 		}
1347453645fSAndriy Voskoboinyk 
1357453645fSAndriy Voskoboinyk 		goto end;
1367453645fSAndriy Voskoboinyk 	}
1377453645fSAndriy Voskoboinyk 
1387453645fSAndriy Voskoboinyk 	start = sc->cam_entry_limit;
1397453645fSAndriy Voskoboinyk 	switch (sc->sc_hwcrypto) {
1407453645fSAndriy Voskoboinyk 	case RTWN_CRYPTO_SW:
1417453645fSAndriy Voskoboinyk 		k->wk_flags |= IEEE80211_KEY_SWCRYPT;
1427453645fSAndriy Voskoboinyk 		*keyix = 0;
1437453645fSAndriy Voskoboinyk 		goto end;
1447453645fSAndriy Voskoboinyk 	case RTWN_CRYPTO_PAIR:
1457453645fSAndriy Voskoboinyk 		/* all slots for pairwise keys. */
1467453645fSAndriy Voskoboinyk 		start = 0;
1477453645fSAndriy Voskoboinyk 		RTWN_LOCK(sc);
1487453645fSAndriy Voskoboinyk 		if (sc->sc_flags & RTWN_FLAG_CAM_FIXED)
1497453645fSAndriy Voskoboinyk 			start = 4;
1507453645fSAndriy Voskoboinyk 		RTWN_UNLOCK(sc);
1517453645fSAndriy Voskoboinyk 		break;
1527453645fSAndriy Voskoboinyk 	case RTWN_CRYPTO_FULL:
1537453645fSAndriy Voskoboinyk 		/* first 4 - for group keys, others for pairwise. */
1547453645fSAndriy Voskoboinyk 		start = 4;
1557453645fSAndriy Voskoboinyk 		break;
1567453645fSAndriy Voskoboinyk 	default:
1577453645fSAndriy Voskoboinyk 		KASSERT(0, ("%s: case %d was not handled!\n",
1587453645fSAndriy Voskoboinyk 		    __func__, sc->sc_hwcrypto));
1597453645fSAndriy Voskoboinyk 		break;
1607453645fSAndriy Voskoboinyk 	}
1617453645fSAndriy Voskoboinyk 
1627453645fSAndriy Voskoboinyk 	RTWN_LOCK(sc);
1637453645fSAndriy Voskoboinyk 	for (i = start; i < sc->cam_entry_limit; i++) {
1647453645fSAndriy Voskoboinyk 		if (isclr(sc->keys_bmap, i)) {
1657453645fSAndriy Voskoboinyk 			setbit(sc->keys_bmap, i);
1667453645fSAndriy Voskoboinyk 			*keyix = i;
1677453645fSAndriy Voskoboinyk 			break;
1687453645fSAndriy Voskoboinyk 		}
1697453645fSAndriy Voskoboinyk 	}
1707453645fSAndriy Voskoboinyk 	RTWN_UNLOCK(sc);
1717453645fSAndriy Voskoboinyk 	if (i == sc->cam_entry_limit) {
1727453645fSAndriy Voskoboinyk 		/* XXX check and remove keys with the same MAC address */
1737453645fSAndriy Voskoboinyk 		k->wk_flags |= IEEE80211_KEY_SWCRYPT;
1747453645fSAndriy Voskoboinyk 		*keyix = 0;
1757453645fSAndriy Voskoboinyk 	}
1767453645fSAndriy Voskoboinyk 
1777453645fSAndriy Voskoboinyk end:
1787453645fSAndriy Voskoboinyk 	*rxkeyix = *keyix;
1797453645fSAndriy Voskoboinyk 	return (1);
1807453645fSAndriy Voskoboinyk }
1817453645fSAndriy Voskoboinyk 
1827453645fSAndriy Voskoboinyk static int
rtwn_key_set_cb0(struct rtwn_softc * sc,const struct ieee80211_key * k)1837453645fSAndriy Voskoboinyk rtwn_key_set_cb0(struct rtwn_softc *sc, const struct ieee80211_key *k)
1847453645fSAndriy Voskoboinyk {
1857453645fSAndriy Voskoboinyk 	uint8_t algo, keyid;
1867453645fSAndriy Voskoboinyk 	int i, error;
1877453645fSAndriy Voskoboinyk 
1887453645fSAndriy Voskoboinyk 	if (sc->sc_hwcrypto == RTWN_CRYPTO_FULL &&
1897453645fSAndriy Voskoboinyk 	    k->wk_keyix < IEEE80211_WEP_NKID)
1907453645fSAndriy Voskoboinyk 		keyid = k->wk_keyix;
1917453645fSAndriy Voskoboinyk 	else
1927453645fSAndriy Voskoboinyk 		keyid = 0;
1937453645fSAndriy Voskoboinyk 
1947453645fSAndriy Voskoboinyk 	/* Map net80211 cipher to HW crypto algorithm. */
1957453645fSAndriy Voskoboinyk 	switch (k->wk_cipher->ic_cipher) {
1967453645fSAndriy Voskoboinyk 	case IEEE80211_CIPHER_WEP:
1977453645fSAndriy Voskoboinyk 		if (k->wk_keylen < 8)
1987453645fSAndriy Voskoboinyk 			algo = R92C_CAM_ALGO_WEP40;
1997453645fSAndriy Voskoboinyk 		else
2007453645fSAndriy Voskoboinyk 			algo = R92C_CAM_ALGO_WEP104;
2017453645fSAndriy Voskoboinyk 		break;
2027453645fSAndriy Voskoboinyk 	case IEEE80211_CIPHER_TKIP:
2037453645fSAndriy Voskoboinyk 		algo = R92C_CAM_ALGO_TKIP;
2047453645fSAndriy Voskoboinyk 		break;
2057453645fSAndriy Voskoboinyk 	case IEEE80211_CIPHER_AES_CCM:
2067453645fSAndriy Voskoboinyk 		algo = R92C_CAM_ALGO_AES;
2077453645fSAndriy Voskoboinyk 		break;
2087453645fSAndriy Voskoboinyk 	default:
2097453645fSAndriy Voskoboinyk 		device_printf(sc->sc_dev, "%s: unknown cipher %u\n",
2107453645fSAndriy Voskoboinyk 		    __func__, k->wk_cipher->ic_cipher);
2117453645fSAndriy Voskoboinyk 		return (EINVAL);
2127453645fSAndriy Voskoboinyk 	}
2137453645fSAndriy Voskoboinyk 
2147453645fSAndriy Voskoboinyk 	RTWN_DPRINTF(sc, RTWN_DEBUG_KEY,
2157453645fSAndriy Voskoboinyk 	    "%s: keyix %u, keyid %u, algo %u/%u, flags %04X, len %u, "
2167453645fSAndriy Voskoboinyk 	    "macaddr %s\n", __func__, k->wk_keyix, keyid,
2177453645fSAndriy Voskoboinyk 	    k->wk_cipher->ic_cipher, algo, k->wk_flags, k->wk_keylen,
2187453645fSAndriy Voskoboinyk 	    ether_sprintf(k->wk_macaddr));
2197453645fSAndriy Voskoboinyk 
2207453645fSAndriy Voskoboinyk 	/* Clear high bits. */
2217453645fSAndriy Voskoboinyk 	rtwn_cam_write(sc, R92C_CAM_CTL6(k->wk_keyix), 0);
2227453645fSAndriy Voskoboinyk 	rtwn_cam_write(sc, R92C_CAM_CTL7(k->wk_keyix), 0);
2237453645fSAndriy Voskoboinyk 
2247453645fSAndriy Voskoboinyk 	/* Write key. */
2257453645fSAndriy Voskoboinyk 	for (i = 0; i < 4; i++) {
2267453645fSAndriy Voskoboinyk 		error = rtwn_cam_write(sc, R92C_CAM_KEY(k->wk_keyix, i),
2277453645fSAndriy Voskoboinyk 		    le32dec(&k->wk_key[i * 4]));
2287453645fSAndriy Voskoboinyk 		if (error != 0)
2297453645fSAndriy Voskoboinyk 			goto fail;
2307453645fSAndriy Voskoboinyk 	}
2317453645fSAndriy Voskoboinyk 
2327453645fSAndriy Voskoboinyk 	/* Write CTL0 last since that will validate the CAM entry. */
2337453645fSAndriy Voskoboinyk 	error = rtwn_cam_write(sc, R92C_CAM_CTL1(k->wk_keyix),
2347453645fSAndriy Voskoboinyk 	    le32dec(&k->wk_macaddr[2]));
2357453645fSAndriy Voskoboinyk 	if (error != 0)
2367453645fSAndriy Voskoboinyk 		goto fail;
2377453645fSAndriy Voskoboinyk 	error = rtwn_cam_write(sc, R92C_CAM_CTL0(k->wk_keyix),
2387453645fSAndriy Voskoboinyk 	    SM(R92C_CAM_ALGO, algo) |
2397453645fSAndriy Voskoboinyk 	    SM(R92C_CAM_KEYID, keyid) |
2407453645fSAndriy Voskoboinyk 	    SM(R92C_CAM_MACLO, le16dec(&k->wk_macaddr[0])) |
2417453645fSAndriy Voskoboinyk 	    R92C_CAM_VALID);
2427453645fSAndriy Voskoboinyk 	if (error != 0)
2437453645fSAndriy Voskoboinyk 		goto fail;
2447453645fSAndriy Voskoboinyk 
2457453645fSAndriy Voskoboinyk 	return (0);
2467453645fSAndriy Voskoboinyk 
2477453645fSAndriy Voskoboinyk fail:
2487453645fSAndriy Voskoboinyk 	device_printf(sc->sc_dev, "%s fails, error %d\n", __func__, error);
2497453645fSAndriy Voskoboinyk 	return (error);
2507453645fSAndriy Voskoboinyk }
2517453645fSAndriy Voskoboinyk 
2527453645fSAndriy Voskoboinyk static void
rtwn_key_set_cb(struct rtwn_softc * sc,union sec_param * data)2537453645fSAndriy Voskoboinyk rtwn_key_set_cb(struct rtwn_softc *sc, union sec_param *data)
2547453645fSAndriy Voskoboinyk {
2557453645fSAndriy Voskoboinyk 	const struct ieee80211_key *k = &data->key;
2567453645fSAndriy Voskoboinyk 
2577453645fSAndriy Voskoboinyk 	(void) rtwn_key_set_cb0(sc, k);
2587453645fSAndriy Voskoboinyk }
2597453645fSAndriy Voskoboinyk 
2607453645fSAndriy Voskoboinyk int
rtwn_init_static_keys(struct rtwn_softc * sc,struct rtwn_vap * rvp)2617453645fSAndriy Voskoboinyk rtwn_init_static_keys(struct rtwn_softc *sc, struct rtwn_vap *rvp)
2627453645fSAndriy Voskoboinyk {
2637453645fSAndriy Voskoboinyk 	int i, error;
2647453645fSAndriy Voskoboinyk 
2657453645fSAndriy Voskoboinyk 	if (sc->sc_hwcrypto != RTWN_CRYPTO_FULL)
2667453645fSAndriy Voskoboinyk 		return (0);		/* nothing to do */
2677453645fSAndriy Voskoboinyk 
2687453645fSAndriy Voskoboinyk 	for (i = 0; i < IEEE80211_WEP_NKID; i++) {
2697453645fSAndriy Voskoboinyk 		const struct ieee80211_key *k = rvp->keys[i];
2707453645fSAndriy Voskoboinyk 		if (k != NULL) {
2717453645fSAndriy Voskoboinyk 			error = rtwn_key_set_cb0(sc, k);
2727453645fSAndriy Voskoboinyk 			if (error != 0)
2737453645fSAndriy Voskoboinyk 				return (error);
2747453645fSAndriy Voskoboinyk 		}
2757453645fSAndriy Voskoboinyk 	}
2767453645fSAndriy Voskoboinyk 
2777453645fSAndriy Voskoboinyk 	return (0);
2787453645fSAndriy Voskoboinyk }
2797453645fSAndriy Voskoboinyk 
2807453645fSAndriy Voskoboinyk static void
rtwn_key_del_cb(struct rtwn_softc * sc,union sec_param * data)2817453645fSAndriy Voskoboinyk rtwn_key_del_cb(struct rtwn_softc *sc, union sec_param *data)
2827453645fSAndriy Voskoboinyk {
2837453645fSAndriy Voskoboinyk 	struct ieee80211_key *k = &data->key;
2847453645fSAndriy Voskoboinyk 	int i;
2857453645fSAndriy Voskoboinyk 
2867453645fSAndriy Voskoboinyk 	RTWN_DPRINTF(sc, RTWN_DEBUG_KEY,
2877453645fSAndriy Voskoboinyk 	    "%s: keyix %u, flags %04X, macaddr %s\n", __func__,
2887453645fSAndriy Voskoboinyk 	    k->wk_keyix, k->wk_flags, ether_sprintf(k->wk_macaddr));
2897453645fSAndriy Voskoboinyk 
2907453645fSAndriy Voskoboinyk 	rtwn_cam_write(sc, R92C_CAM_CTL0(k->wk_keyix), 0);
2917453645fSAndriy Voskoboinyk 	rtwn_cam_write(sc, R92C_CAM_CTL1(k->wk_keyix), 0);
2927453645fSAndriy Voskoboinyk 
2937453645fSAndriy Voskoboinyk 	/* Clear key. */
2947453645fSAndriy Voskoboinyk 	for (i = 0; i < 4; i++)
2957453645fSAndriy Voskoboinyk 		rtwn_cam_write(sc, R92C_CAM_KEY(k->wk_keyix, i), 0);
2967453645fSAndriy Voskoboinyk 	clrbit(sc->keys_bmap, k->wk_keyix);
2977453645fSAndriy Voskoboinyk }
2987453645fSAndriy Voskoboinyk 
2997453645fSAndriy Voskoboinyk static int
rtwn_process_key(struct ieee80211vap * vap,const struct ieee80211_key * k,int set)3007453645fSAndriy Voskoboinyk rtwn_process_key(struct ieee80211vap *vap, const struct ieee80211_key *k,
3017453645fSAndriy Voskoboinyk     int set)
3027453645fSAndriy Voskoboinyk {
3037453645fSAndriy Voskoboinyk 	struct rtwn_softc *sc = vap->iv_ic->ic_softc;
3047453645fSAndriy Voskoboinyk 
3057453645fSAndriy Voskoboinyk 	if (k->wk_flags & IEEE80211_KEY_SWCRYPT) {
3067453645fSAndriy Voskoboinyk 		/* Not for us. */
3077453645fSAndriy Voskoboinyk 		return (1);
3087453645fSAndriy Voskoboinyk 	}
3097453645fSAndriy Voskoboinyk 
310*2589197aSAdrian Chadd 	if (ieee80211_is_key_global(vap, k)) {
3117453645fSAndriy Voskoboinyk 		if (sc->sc_hwcrypto == RTWN_CRYPTO_FULL) {
3127453645fSAndriy Voskoboinyk 			struct rtwn_vap *rvp = RTWN_VAP(vap);
3137453645fSAndriy Voskoboinyk 
3147453645fSAndriy Voskoboinyk 			RTWN_LOCK(sc);
3157453645fSAndriy Voskoboinyk 			rvp->keys[k->wk_keyix] = (set ? k : NULL);
3167453645fSAndriy Voskoboinyk 			if ((sc->sc_flags & RTWN_RUNNING) == 0) {
3177453645fSAndriy Voskoboinyk 				if (!set)
3187453645fSAndriy Voskoboinyk 					clrbit(sc->keys_bmap, k->wk_keyix);
3197453645fSAndriy Voskoboinyk 				RTWN_UNLOCK(sc);
3207453645fSAndriy Voskoboinyk 				return (1);
3217453645fSAndriy Voskoboinyk 			}
3227453645fSAndriy Voskoboinyk 			RTWN_UNLOCK(sc);
3237453645fSAndriy Voskoboinyk 		}
3247453645fSAndriy Voskoboinyk 	}
3257453645fSAndriy Voskoboinyk 
3267453645fSAndriy Voskoboinyk 	return (!rtwn_cmd_sleepable(sc, k, sizeof(*k),
3277453645fSAndriy Voskoboinyk 	    set ? rtwn_key_set_cb : rtwn_key_del_cb));
3287453645fSAndriy Voskoboinyk }
3297453645fSAndriy Voskoboinyk 
3307453645fSAndriy Voskoboinyk int
rtwn_key_set(struct ieee80211vap * vap,const struct ieee80211_key * k)3317453645fSAndriy Voskoboinyk rtwn_key_set(struct ieee80211vap *vap, const struct ieee80211_key *k)
3327453645fSAndriy Voskoboinyk {
3337453645fSAndriy Voskoboinyk 	return (rtwn_process_key(vap, k, 1));
3347453645fSAndriy Voskoboinyk }
3357453645fSAndriy Voskoboinyk 
3367453645fSAndriy Voskoboinyk int
rtwn_key_delete(struct ieee80211vap * vap,const struct ieee80211_key * k)3377453645fSAndriy Voskoboinyk rtwn_key_delete(struct ieee80211vap *vap, const struct ieee80211_key *k)
3387453645fSAndriy Voskoboinyk {
3397453645fSAndriy Voskoboinyk 	return (rtwn_process_key(vap, k, 0));
3407453645fSAndriy Voskoboinyk }
341