svn commit: r309586 - head/sys/dev/usb/wlan

Andriy Voskoboinyk avos at FreeBSD.org
Tue Dec 6 00:13:50 UTC 2016


Author: avos
Date: Tue Dec  6 00:13:49 2016
New Revision: 309586
URL: https://svnweb.freebsd.org/changeset/base/309586

Log:
  rsu: add hardware crypto support (WEP, TKIP and CCMP).
  
  This change includes firmware commands for key setup +
  some additional checking via CAMREAD / CAMWRITE registers.
  Nothing (except rsu_delete_key() for pairwise keys) is deferred;
  to ensure that things are done in order rsu_set_key() will wait
  until key deletion task will be finished.
  
  Tested with Asus USB-N10 (all ciphers).
  
  Differences from initial (reviewed) patch:
  - Pause AC queues before disassociation - since CMD_DISCONNECT clears
  crypto state all pending frames must be processed / dropped before it.
  - Check sc_running flag before trying to set static keys.
  - Clear key index from bitmap even when firmware command fails
  (it will be invalidated via CAMWRITE anyway).
  
  Reviewed by:	adrian, kevlo
  Tested by:	kevlo
  Differential Revision:	https://reviews.freebsd.org/D8706

Modified:
  head/sys/dev/usb/wlan/if_rsu.c
  head/sys/dev/usb/wlan/if_rsureg.h

Modified: head/sys/dev/usb/wlan/if_rsu.c
==============================================================================
--- head/sys/dev/usb/wlan/if_rsu.c	Tue Dec  6 00:12:09 2016	(r309585)
+++ head/sys/dev/usb/wlan/if_rsu.c	Tue Dec  6 00:13:49 2016	(r309586)
@@ -22,8 +22,8 @@ __FBSDID("$FreeBSD$");
  * Driver for Realtek RTL8188SU/RTL8191SU/RTL8192SU.
  *
  * TODO:
- *   o h/w crypto
- *   o hostap / ibss / mesh
+ *   o tx a-mpdu
+ *   o monitor / hostap / ibss / mesh
  *   o power-save operation
  */
 
@@ -102,6 +102,7 @@ TUNABLE_INT("hw.usb.rsu.enable_11n", &rs
 #define	RSU_DEBUG_FW		0x00000100
 #define	RSU_DEBUG_FWDBG		0x00000200
 #define	RSU_DEBUG_AMPDU		0x00000400
+#define	RSU_DEBUG_KEY		0x00000800
 
 static const STRUCT_USB_HOST_ID rsu_devs[] = {
 #define	RSU_HT_NOT_SUPPORTED 0
@@ -202,10 +203,25 @@ static int	rsu_fw_cmd(struct rsu_softc *
 static void	rsu_calib_task(void *, int);
 static void	rsu_tx_task(void *, int);
 static int	rsu_newstate(struct ieee80211vap *, enum ieee80211_state, int);
-#ifdef notyet
-static void	rsu_set_key(struct rsu_softc *, const struct ieee80211_key *);
-static void	rsu_delete_key(struct rsu_softc *, const struct ieee80211_key *);
-#endif
+static int	rsu_key_alloc(struct ieee80211vap *, struct ieee80211_key *,
+		    ieee80211_keyix *, ieee80211_keyix *);
+static int	rsu_process_key(struct ieee80211vap *,
+		    const struct ieee80211_key *, int);
+static int	rsu_key_set(struct ieee80211vap *,
+		    const struct ieee80211_key *);
+static int	rsu_key_delete(struct ieee80211vap *,
+		    const struct ieee80211_key *);
+static int	rsu_cam_read(struct rsu_softc *, uint8_t, uint32_t *);
+static void	rsu_cam_write(struct rsu_softc *, uint8_t, uint32_t);
+static int	rsu_key_check(struct rsu_softc *, ieee80211_keyix, int);
+static uint8_t	rsu_crypto_mode(struct rsu_softc *, u_int, int);
+static int	rsu_set_key_group(struct rsu_softc *,
+		    const struct ieee80211_key *);
+static int	rsu_set_key_pair(struct rsu_softc *,
+		    const struct ieee80211_key *);
+static int	rsu_reinit_static_keys(struct rsu_softc *);
+static int	rsu_delete_key(struct rsu_softc *sc, ieee80211_keyix);
+static void	rsu_delete_key_pair_cb(void *, int);
 static int	rsu_site_survey(struct rsu_softc *,
 		    struct ieee80211_scan_ssid *);
 static int	rsu_join_bss(struct rsu_softc *, struct ieee80211_node *);
@@ -437,8 +453,10 @@ rsu_attach(device_t self)
 
 	mtx_init(&sc->sc_mtx, device_get_nameunit(self), MTX_NETWORK_LOCK,
 	    MTX_DEF);
+	RSU_DELKEY_BMAP_LOCK_INIT(sc);
 	TIMEOUT_TASK_INIT(taskqueue_thread, &sc->calib_task, 0, 
 	    rsu_calib_task, sc);
+	TASK_INIT(&sc->del_key_task, 0, rsu_delete_key_pair_cb, sc);
 	TASK_INIT(&sc->tx_task, 0, rsu_tx_task, sc);
 	mbufq_init(&sc->sc_snd, ifqmaxlen);
 
@@ -524,6 +542,11 @@ rsu_attach(device_t self)
 	    IEEE80211_C_SHSLOT |	/* Short slot time supported. */
 	    IEEE80211_C_WPA;		/* WPA/RSN. */
 
+	ic->ic_cryptocaps =
+	    IEEE80211_CRYPTO_WEP |
+	    IEEE80211_CRYPTO_TKIP |
+	    IEEE80211_CRYPTO_AES_CCM;
+
 	/* Check if HT support is present. */
 	if (sc->sc_ht) {
 		device_printf(sc->sc_dev, "%s: enabling 11n\n", __func__);
@@ -608,8 +631,10 @@ rsu_detach(device_t self)
 	ieee80211_ifdetach(ic);
 
 	taskqueue_drain_timeout(taskqueue_thread, &sc->calib_task);
+	taskqueue_drain(taskqueue_thread, &sc->del_key_task);
 	taskqueue_drain(taskqueue_thread, &sc->tx_task);
 
+	RSU_DELKEY_BMAP_LOCK_DESTROY(sc);
 	mtx_destroy(&sc->sc_mtx);
 
 	return (0);
@@ -662,6 +687,9 @@ rsu_vap_create(struct ieee80211com *ic, 
 	/* override state transition machine */
 	uvp->newstate = vap->iv_newstate;
 	vap->iv_newstate = rsu_newstate;
+	vap->iv_key_alloc = rsu_key_alloc;
+	vap->iv_key_set = rsu_key_set;
+	vap->iv_key_delete = rsu_key_delete;
 
 	/* Limits from the r92su driver */
 	vap->iv_ampdu_density = IEEE80211_HTCAP_MPDUDENSITY_16;
@@ -1335,12 +1363,20 @@ rsu_newstate(struct ieee80211vap *vap, e
 		RSU_LOCK(sc);
 		/* Stop calibration. */
 		sc->sc_calibrating = 0;
+
+		/* Pause Tx for AC queues. */
+		rsu_write_1(sc, R92S_TXPAUSE, R92S_TXPAUSE_AC);
+		usb_pause_mtx(&sc->sc_mtx, USB_MS_TO_TICKS(10));
+
 		RSU_UNLOCK(sc);
 		taskqueue_drain_timeout(taskqueue_thread, &sc->calib_task);
 		taskqueue_drain(taskqueue_thread, &sc->tx_task);
-		/* Disassociate from our current BSS. */
 		RSU_LOCK(sc);
+		/* Disassociate from our current BSS. */
 		rsu_disconnect(sc);
+		/* Reinstall static keys. */
+		if (sc->sc_running)
+			rsu_reinit_static_keys(sc);
 	} else
 		RSU_LOCK(sc);
 	switch (nstate) {
@@ -1358,6 +1394,9 @@ rsu_newstate(struct ieee80211vap *vap, e
 		}
 		break;
 	case IEEE80211_S_RUN:
+		/* Flush all AC queues. */
+		rsu_write_1(sc, R92S_TXPAUSE, 0);
+
 		ni = ieee80211_ref_node(vap->iv_bss);
 		rs = &ni->ni_rates;
 		/* Indicate highest supported rate. */
@@ -1380,46 +1419,365 @@ rsu_newstate(struct ieee80211vap *vap, e
 	return (uvp->newstate(vap, nstate, arg));
 }
 
-#ifdef notyet
+static int
+rsu_key_alloc(struct ieee80211vap *vap, struct ieee80211_key *k,
+    ieee80211_keyix *keyix, ieee80211_keyix *rxkeyix)
+{
+	struct rsu_softc *sc = vap->iv_ic->ic_softc;
+	int is_checked = 0;
+
+	if (&vap->iv_nw_keys[0] <= k &&
+	    k < &vap->iv_nw_keys[IEEE80211_WEP_NKID]) {
+		*keyix = k - vap->iv_nw_keys;
+	} else {
+		if (vap->iv_opmode != IEEE80211_M_STA) {
+			*keyix = 0;
+			/* TODO: obtain keyix from node id */
+			is_checked = 1;
+			k->wk_flags |= IEEE80211_KEY_SWCRYPT;
+		} else
+			*keyix = R92S_MACID_BSS;
+	}
+
+	if (!is_checked) {
+		RSU_LOCK(sc);
+		if (isset(sc->keys_bmap, *keyix)) {
+			device_printf(sc->sc_dev,
+			    "%s: key slot %d is already used!\n",
+			    __func__, *keyix);
+			RSU_UNLOCK(sc);
+			return (0);
+		}
+		setbit(sc->keys_bmap, *keyix);
+		RSU_UNLOCK(sc);
+	}
+
+	*rxkeyix = *keyix;
+
+	return (1);
+}
+
+static int
+rsu_process_key(struct ieee80211vap *vap, const struct ieee80211_key *k,
+    int set)
+{
+	struct rsu_softc *sc = vap->iv_ic->ic_softc;
+	int ret;
+
+	if (k->wk_flags & IEEE80211_KEY_SWCRYPT) {
+		/* Not for us. */
+		return (1);
+	}
+
+	/* Handle group keys. */
+	if (&vap->iv_nw_keys[0] <= k &&
+	    k < &vap->iv_nw_keys[IEEE80211_WEP_NKID]) {
+		KASSERT(k->wk_keyix < nitems(sc->group_keys),
+		    ("keyix %d > %d\n", k->wk_keyix, nitems(sc->group_keys)));
+
+		RSU_LOCK(sc);
+		sc->group_keys[k->wk_keyix] = (set ? k : NULL);
+		if (!sc->sc_running) {
+			/* Static keys will be set during device startup. */
+			RSU_UNLOCK(sc);
+			return (1);
+		}
+
+		if (set)
+			ret = rsu_set_key_group(sc, k);
+		else
+			ret = rsu_delete_key(sc, k->wk_keyix);
+		RSU_UNLOCK(sc);
+
+		return (!ret);
+	}
+
+	if (set) {
+		/* wait for pending key removal */
+		taskqueue_drain(taskqueue_thread, &sc->del_key_task);
+
+		RSU_LOCK(sc);
+		ret = rsu_set_key_pair(sc, k);
+		RSU_UNLOCK(sc);
+	} else {
+		RSU_DELKEY_BMAP_LOCK(sc);
+		setbit(sc->free_keys_bmap, k->wk_keyix);
+		RSU_DELKEY_BMAP_UNLOCK(sc);
+
+		/* workaround ieee80211_node_delucastkey() locking */
+		taskqueue_enqueue(taskqueue_thread, &sc->del_key_task);
+		ret = 0;	/* fake success */
+	}
+
+	return (!ret);
+}
+
+static int
+rsu_key_set(struct ieee80211vap *vap, const struct ieee80211_key *k)
+{
+	return (rsu_process_key(vap, k, 1));
+}
+
+static int
+rsu_key_delete(struct ieee80211vap *vap, const struct ieee80211_key *k)
+{
+	return (rsu_process_key(vap, k, 0));
+}
+
+static int
+rsu_cam_read(struct rsu_softc *sc, uint8_t addr, uint32_t *val)
+{
+	int ntries;
+
+	rsu_write_4(sc, R92S_CAMCMD,
+	    R92S_CAMCMD_POLLING | SM(R92S_CAMCMD_ADDR, addr));
+	for (ntries = 0; ntries < 10; ntries++) {
+		if (!(rsu_read_4(sc, R92S_CAMCMD) & R92S_CAMCMD_POLLING))
+			break;
+
+		usb_pause_mtx(&sc->sc_mtx, USB_MS_TO_TICKS(1));
+	}
+	if (ntries == 10) {
+		device_printf(sc->sc_dev,
+		    "%s: cannot read CAM entry at address %02X\n",
+		    __func__, addr);
+		return (ETIMEDOUT);
+	}
+
+	*val = rsu_read_4(sc, R92S_CAMREAD);
+
+	return (0);
+}
+
 static void
-rsu_set_key(struct rsu_softc *sc, const struct ieee80211_key *k)
+rsu_cam_write(struct rsu_softc *sc, uint8_t addr, uint32_t data)
 {
-	struct r92s_fw_cmd_set_key key;
 
-	memset(&key, 0, sizeof(key));
-	/* Map net80211 cipher to HW crypto algorithm. */
-	switch (k->wk_cipher->ic_cipher) {
+	rsu_write_4(sc, R92S_CAMWRITE, data);
+	rsu_write_4(sc, R92S_CAMCMD,
+	    R92S_CAMCMD_POLLING | R92S_CAMCMD_WRITE |
+	    SM(R92S_CAMCMD_ADDR, addr));
+}
+
+static int
+rsu_key_check(struct rsu_softc *sc, ieee80211_keyix keyix, int is_valid)
+{
+	uint32_t val;
+	int error, ntries;
+
+	for (ntries = 0; ntries < 20; ntries++) {
+		usb_pause_mtx(&sc->sc_mtx, USB_MS_TO_TICKS(1));
+
+		error = rsu_cam_read(sc, R92S_CAM_CTL0(keyix), &val);
+		if (error != 0) {
+			device_printf(sc->sc_dev,
+			    "%s: cannot check key status!\n", __func__);
+			return (error);
+		}
+		if (((val & R92S_CAM_VALID) == 0) ^ is_valid)
+			break;
+	}
+	if (ntries == 20) {
+		device_printf(sc->sc_dev,
+		    "%s: key %d is %s marked as valid, rejecting request\n",
+		    __func__, keyix, is_valid ? "not" : "still");
+		return (EIO);
+	}
+
+	return (0);
+}
+
+/*
+ * Map net80211 cipher to RTL8712 security mode.
+ */
+static uint8_t
+rsu_crypto_mode(struct rsu_softc *sc, u_int cipher, int keylen)
+{
+	switch (cipher) {
 	case IEEE80211_CIPHER_WEP:
-		if (k->wk_keylen < 8)
-			key.algo = R92S_KEY_ALGO_WEP40;
-		else
-			key.algo = R92S_KEY_ALGO_WEP104;
-		break;
+		return keylen < 8 ? R92S_KEY_ALGO_WEP40 : R92S_KEY_ALGO_WEP104;
 	case IEEE80211_CIPHER_TKIP:
-		key.algo = R92S_KEY_ALGO_TKIP;
-		break;
+		return R92S_KEY_ALGO_TKIP;
 	case IEEE80211_CIPHER_AES_CCM:
-		key.algo = R92S_KEY_ALGO_AES;
-		break;
+		return R92S_KEY_ALGO_AES;
 	default:
-		return;
+		device_printf(sc->sc_dev, "unknown cipher %d\n", cipher);
+		return R92S_KEY_ALGO_INVALID;
 	}
-	key.id = k->wk_keyix;
+}
+
+static int
+rsu_set_key_group(struct rsu_softc *sc, const struct ieee80211_key *k)
+{
+	struct r92s_fw_cmd_set_key key;
+	uint8_t algo;
+	int error;
+
+	RSU_ASSERT_LOCKED(sc);
+
+	/* Map net80211 cipher to HW crypto algorithm. */
+	algo = rsu_crypto_mode(sc, k->wk_cipher->ic_cipher, k->wk_keylen);
+	if (algo == R92S_KEY_ALGO_INVALID)
+		return (EINVAL);
+
+	memset(&key, 0, sizeof(key));
+	key.algo = algo;
+	key.cam_id = k->wk_keyix;
 	key.grpkey = (k->wk_flags & IEEE80211_KEY_GROUP) != 0;
 	memcpy(key.key, k->wk_key, MIN(k->wk_keylen, sizeof(key.key)));
-	(void)rsu_fw_cmd(sc, R92S_CMD_SET_KEY, &key, sizeof(key));
+
+	RSU_DPRINTF(sc, RSU_DEBUG_KEY | RSU_DEBUG_FWCMD,
+	    "%s: keyix %u, group %u, algo %u/%u, flags %04X, len %u, "
+	    "macaddr %s\n", __func__, key.cam_id, key.grpkey,
+	    k->wk_cipher->ic_cipher, key.algo, k->wk_flags, k->wk_keylen,
+	    ether_sprintf(k->wk_macaddr));
+
+	error = rsu_fw_cmd(sc, R92S_CMD_SET_KEY, &key, sizeof(key));
+	if (error != 0) {
+		device_printf(sc->sc_dev,
+		    "%s: cannot send firmware command, error %d\n",
+		    __func__, error);
+		return (error);
+	}
+
+	return (rsu_key_check(sc, k->wk_keyix, 1));
 }
 
-static void
-rsu_delete_key(struct rsu_softc *sc, const struct ieee80211_key *k)
+static int
+rsu_set_key_pair(struct rsu_softc *sc, const struct ieee80211_key *k)
+{
+	struct r92s_fw_cmd_set_key_mac key;
+	uint8_t algo;
+	int error;
+
+	RSU_ASSERT_LOCKED(sc);
+
+	if (!sc->sc_running)
+		return (ESHUTDOWN);
+
+	/* Map net80211 cipher to HW crypto algorithm. */
+	algo = rsu_crypto_mode(sc, k->wk_cipher->ic_cipher, k->wk_keylen);
+	if (algo == R92S_KEY_ALGO_INVALID)
+		return (EINVAL);
+
+	memset(&key, 0, sizeof(key));
+	key.algo = algo;
+	memcpy(key.macaddr, k->wk_macaddr, sizeof(key.macaddr));
+	memcpy(key.key, k->wk_key, MIN(k->wk_keylen, sizeof(key.key)));
+
+	RSU_DPRINTF(sc, RSU_DEBUG_KEY | RSU_DEBUG_FWCMD,
+	    "%s: keyix %u, algo %u/%u, flags %04X, len %u, macaddr %s\n",
+	    __func__, k->wk_keyix, k->wk_cipher->ic_cipher, key.algo,
+	    k->wk_flags, k->wk_keylen, ether_sprintf(key.macaddr));
+
+	error = rsu_fw_cmd(sc, R92S_CMD_SET_STA_KEY, &key, sizeof(key));
+	if (error != 0) {
+		device_printf(sc->sc_dev,
+		    "%s: cannot send firmware command, error %d\n",
+		    __func__, error);
+		return (error);
+	}
+
+	return (rsu_key_check(sc, k->wk_keyix, 1));
+}
+
+static int
+rsu_reinit_static_keys(struct rsu_softc *sc)
+{
+	int i, error;
+
+	for (i = 0; i < nitems(sc->group_keys); i++) {
+		if (sc->group_keys[i] != NULL) {
+			error = rsu_set_key_group(sc, sc->group_keys[i]);
+			if (error != 0) {
+				device_printf(sc->sc_dev,
+				    "%s: failed to set static key %d, "
+				    "error %d\n", __func__, i, error);
+				return (error);
+			}
+		}
+	}
+
+	return (0);
+}
+
+static int
+rsu_delete_key(struct rsu_softc *sc, ieee80211_keyix keyix)
 {
 	struct r92s_fw_cmd_set_key key;
+	uint32_t val;
+	int error;
+
+	RSU_ASSERT_LOCKED(sc);
+
+	if (!sc->sc_running)
+		return (0);
+
+	/* check if it was automatically removed by firmware */
+	error = rsu_cam_read(sc, R92S_CAM_CTL0(keyix), &val);
+	if (error == 0 && (val & R92S_CAM_VALID) == 0) {
+		RSU_DPRINTF(sc, RSU_DEBUG_KEY,
+		    "%s: key %u does not exist\n", __func__, keyix);
+		clrbit(sc->keys_bmap, keyix);
+		return (0);
+	}
 
 	memset(&key, 0, sizeof(key));
-	key.id = k->wk_keyix;
-	(void)rsu_fw_cmd(sc, R92S_CMD_SET_KEY, &key, sizeof(key));
+	key.cam_id = keyix;
+
+	RSU_DPRINTF(sc, RSU_DEBUG_KEY | RSU_DEBUG_FWCMD,
+	    "%s: removing key %u\n", __func__, key.cam_id);
+
+	error = rsu_fw_cmd(sc, R92S_CMD_SET_KEY, &key, sizeof(key));
+	if (error != 0) {
+		device_printf(sc->sc_dev,
+		    "%s: cannot send firmware command, error %d\n",
+		    __func__, error);
+		goto finish;
+	}
+
+	usb_pause_mtx(&sc->sc_mtx, USB_MS_TO_TICKS(5));
+
+	/*
+	 * Clear 'valid' bit manually (cannot be done via firmware command).
+	 * Used for key check + when firmware command cannot be sent.
+	 */
+finish:
+	rsu_cam_write(sc, R92S_CAM_CTL0(keyix), 0);
+
+	clrbit(sc->keys_bmap, keyix);
+
+	return (rsu_key_check(sc, keyix, 0));
+}
+
+static void
+rsu_delete_key_pair_cb(void *arg, int pending __unused)
+{
+	struct rsu_softc *sc = arg;
+	int i;
+
+	RSU_DELKEY_BMAP_LOCK(sc);
+	for (i = IEEE80211_WEP_NKID; i < R92S_CAM_ENTRY_LIMIT; i++) {
+		if (isset(sc->free_keys_bmap, i)) {
+			RSU_DELKEY_BMAP_UNLOCK(sc);
+
+			RSU_LOCK(sc);
+			RSU_DPRINTF(sc, RSU_DEBUG_KEY,
+			    "%s: calling rsu_delete_key() with keyix = %d\n",
+			    __func__, i);
+			(void) rsu_delete_key(sc, i);
+			RSU_UNLOCK(sc);
+
+			RSU_DELKEY_BMAP_LOCK(sc);
+			clrbit(sc->free_keys_bmap, i);
+
+			/* bmap can be changed */
+			i = IEEE80211_WEP_NKID - 1;
+			continue;
+		}
+	}
+	RSU_DELKEY_BMAP_UNLOCK(sc);
 }
-#endif
 
 static int
 rsu_site_survey(struct rsu_softc *sc, struct ieee80211_scan_ssid *ssid)
@@ -1834,9 +2192,10 @@ rsu_rx_copy_to_mbuf(struct rsu_softc *sc
 	int pktlen;
 
 	rxdw0 = le32toh(stat->rxdw0);
-	if (__predict_false(rxdw0 & R92S_RXDW0_CRCERR)) {
+	if (__predict_false(rxdw0 & (R92S_RXDW0_CRCERR | R92S_RXDW0_ICVERR))) {
 		RSU_DPRINTF(sc, RSU_DEBUG_RX,
-		    "%s: RX flags error (CRC)\n", __func__);
+		    "%s: RX flags error (%s)\n", __func__,
+		    rxdw0 & R92S_RXDW0_CRCERR ? "CRC" : "ICV");
 		goto fail;
 	}
 
@@ -1871,7 +2230,7 @@ rsu_rx_frame(struct rsu_softc *sc, struc
 	struct ieee80211_frame_min *wh;
 	struct r92s_rx_stat *stat;
 	uint32_t rxdw0, rxdw3;
-	uint8_t rate;
+	uint8_t cipher, rate;
 	int infosz;
 
 	stat = mtod(m, struct r92s_rx_stat *);
@@ -1879,6 +2238,7 @@ rsu_rx_frame(struct rsu_softc *sc, struc
 	rxdw3 = le32toh(stat->rxdw3);
 
 	rate = MS(rxdw3, R92S_RXDW3_RATE);
+	cipher = MS(rxdw0, R92S_RXDW0_CIPHER);
 	infosz = MS(rxdw0, R92S_RXDW0_INFOSZ) * 8;
 
 	/* Get RSSI from PHY status descriptor if present. */
@@ -1930,6 +2290,10 @@ rsu_rx_frame(struct rsu_softc *sc, struc
 	/* Drop descriptor. */
 	m_adj(m, sizeof(*stat) + infosz);
 	wh = mtod(m, struct ieee80211_frame_min *);
+	if ((wh->i_fc[1] & IEEE80211_FC1_PROTECTED) &&
+	    cipher != R92S_KEY_ALGO_NONE) {
+		m->m_flags |= M_WEP;
+	}
 
 	RSU_DPRINTF(sc, RSU_DEBUG_RX,
 	    "%s: Rx frame len %d, rate %d, infosz %d\n",
@@ -2225,7 +2589,7 @@ rsu_tx_start(struct rsu_softc *sc, struc
 	struct ieee80211_frame *wh;
 	struct ieee80211_key *k = NULL;
 	struct r92s_tx_desc *txd;
-	uint8_t type;
+	uint8_t type, cipher;
 	int prio = 0;
 	uint8_t which;
 	int hasqos;
@@ -2298,8 +2662,7 @@ rsu_tx_start(struct rsu_softc *sc, struc
 	    SM(R92S_TXDW1_MACID, R92S_MACID_BSS) | SM(R92S_TXDW1_QSEL, qid));
 	if (!hasqos)
 		txd->txdw1 |= htole32(R92S_TXDW1_NONQOS);
-#ifdef notyet
-	if (k != NULL) {
+	if (k != NULL && !(k->wk_flags & IEEE80211_KEY_SWENCRYPT)) {
 		switch (k->wk_cipher->ic_cipher) {
 		case IEEE80211_CIPHER_WEP:
 			cipher = R92S_TXDW1_CIPHER_WEP;
@@ -2315,9 +2678,8 @@ rsu_tx_start(struct rsu_softc *sc, struc
 		}
 		txd->txdw1 |= htole32(
 		    SM(R92S_TXDW1_CIPHER, cipher) |
-		    SM(R92S_TXDW1_KEYIDX, k->k_id));
+		    SM(R92S_TXDW1_KEYIDX, k->wk_keyix));
 	}
-#endif
 	/* XXX todo: set AGGEN bit if appropriate? */
 	txd->txdw2 |= htole32(R92S_TXDW2_BK);
 	if (IEEE80211_IS_MULTICAST(wh->i_addr1))
@@ -3021,12 +3383,16 @@ rsu_init(struct rsu_softc *sc)
 
 	/* Set PS mode fully active */
 	error = rsu_set_fw_power_state(sc, RSU_PWR_ACTIVE);
-
 	if (error != 0) {
 		device_printf(sc->sc_dev, "could not set PS mode\n");
 		goto fail;
 	}
 
+	/* Install static keys (if any). */
+	error = rsu_reinit_static_keys(sc);
+	if (error != 0)
+		goto fail;
+
 	sc->sc_extra_scan = 0;
 	usbd_transfer_start(sc->sc_xfer[RSU_BULK_RX]);
 
@@ -3054,6 +3420,13 @@ rsu_stop(struct rsu_softc *sc)
 	/* Power off adapter. */
 	rsu_power_off(sc);
 
+	/*
+	 * CAM is not accessible after shutdown;
+	 * all entries are marked (by firmware?) as invalid.
+	 */
+	memset(sc->free_keys_bmap, 0, sizeof(sc->free_keys_bmap));
+	memset(sc->keys_bmap, 0, sizeof(sc->keys_bmap));
+
 	for (i = 0; i < RSU_N_TRANSFER; i++)
 		usbd_transfer_stop(sc->sc_xfer[i]);
 

Modified: head/sys/dev/usb/wlan/if_rsureg.h
==============================================================================
--- head/sys/dev/usb/wlan/if_rsureg.h	Tue Dec  6 00:12:09 2016	(r309585)
+++ head/sys/dev/usb/wlan/if_rsureg.h	Tue Dec  6 00:13:49 2016	(r309586)
@@ -43,6 +43,7 @@
 
 #define R92S_CMDCTRL		0x0040
 #define R92S_CR			(R92S_CMDCTRL + 0x000)
+#define R92S_TXPAUSE		(R92S_CMDCTRL + 0x002)
 #define R92S_TCR		(R92S_CMDCTRL + 0x004)
 #define R92S_RCR		(R92S_CMDCTRL + 0x008)
 
@@ -55,6 +56,11 @@
 #define R92S_GPIO_IO_SEL	(R92S_GP + 0x00e)
 #define R92S_MAC_PINMUX_CTRL	(R92S_GP + 0x011)
 
+#define R92S_SECURITY		0x0240
+#define R92S_CAMCMD		(R92S_SECURITY + 0x000)
+#define R92S_CAMWRITE		(R92S_SECURITY + 0x004)
+#define R92S_CAMREAD		(R92S_SECURITY + 0x008)
+
 #define R92S_IOCMD_CTRL		0x0370
 #define R92S_IOCMD_DATA		0x0374
 
@@ -105,6 +111,24 @@
 /* Bits for R92S_CR. */
 #define R92S_CR_TXDMA_EN	0x10
 
+/* Bits for R92S_TXPAUSE. */
+#define R92S_TXPAUSE_VO		0x01
+#define R92S_TXPAUSE_VI		0x02
+#define R92S_TXPAUSE_BE		0x04
+#define R92S_TXPAUSE_BK		0x08
+#define R92S_TXPAUSE_MGT	0x10
+#define R92S_TXPAUSE_HIGH	0x20
+#define R92S_TXPAUSE_HCCA	0x40
+
+/* Shortcuts. */
+#define R92S_TXPAUSE_AC				\
+	(R92S_TXPAUSE_VO | R92S_TXPAUSE_VI |	\
+	 R92S_TXPAUSE_BE | R92S_TXPAUSE_BK)
+
+#define R92S_TXPAUSE_ALL			\
+	(R92S_TXPAUSE_AC | R92S_TXPAUSE_MGT |	\
+	 R92S_TXPAUSE_HIGH | R92S_TXPAUSE_HCCA | 0x80)
+
 /* Bits for R92S_TCR. */
 #define R92S_TCR_IMEM_CODE_DONE	0x01
 #define R92S_TCR_IMEM_CHK_RPT	0x02
@@ -126,6 +150,32 @@
 #define R92S_GPIOSEL_GPIO_WLANDBG	3
 #define R92S_GPIOMUX_EN			0x08
 
+/* Bits for R92S_CAMCMD. */
+#define R92S_CAMCMD_ADDR_M		0x000000ff
+#define R92S_CAMCMD_ADDR_S		0
+#define R92S_CAMCMD_READ		0x00000000
+#define R92S_CAMCMD_WRITE		0x00010000
+#define R92S_CAMCMD_POLLING		0x80000000
+
+/*
+ * CAM entries.
+ */
+#define R92S_CAM_ENTRY_LIMIT	32
+#define R92S_CAM_ENTRY_BYTES	howmany(R92S_CAM_ENTRY_LIMIT, NBBY)
+
+#define R92S_CAM_CTL0(entry)	((entry) * 8 + 0)
+#define R92S_CAM_CTL1(entry)	((entry) * 8 + 1)
+#define R92S_CAM_KEY(entry, i)	((entry) * 8 + 2 + (i))
+
+/* Bits for R92S_CAM_CTL0(i). */
+#define R92S_CAM_KEYID_M	0x00000003
+#define R92S_CAM_KEYID_S	0
+#define R92S_CAM_ALGO_M		0x0000001c
+#define R92S_CAM_ALGO_S		2
+#define R92S_CAM_VALID		0x00008000
+#define R92S_CAM_MACLO_M	0xffff0000
+#define R92S_CAM_MACLO_S	16
+
 /* Bits for R92S_IOCMD_CTRL. */
 #define R92S_IOCMD_CLASS_M		0xff000000
 #define R92S_IOCMD_CLASS_S		24
@@ -391,10 +441,18 @@ struct r92s_fw_cmd_set_key {
 #define R92S_KEY_ALGO_TKIP_MMIC	3
 #define R92S_KEY_ALGO_AES	4
 #define R92S_KEY_ALGO_WEP104	5
+#define R92S_KEY_ALGO_INVALID	0xff	/* for rsu_crypto_mode() only */
 
-	uint8_t	id;
+	uint8_t	cam_id;
 	uint8_t	grpkey;
-	uint8_t	key[16];
+	uint8_t	key[IEEE80211_KEYBUF_SIZE];
+} __packed;
+
+/* Structure for R92S_CMD_SET_STA_KEY. */
+struct r92s_fw_cmd_set_key_mac {
+	uint8_t	macaddr[IEEE80211_ADDR_LEN];
+	uint8_t	algo;
+	uint8_t	key[IEEE80211_KEYBUF_SIZE];
 } __packed;
 
 /* Structures for R92S_EVENT_SURVEY/R92S_CMD_JOIN_BSS. */
@@ -497,7 +555,7 @@ struct r92s_event_join_bss {
 	struct		ndis_wlan_bssid_ex bss;
 } __packed;
 
-#define R92S_MACID_BSS	5
+#define R92S_MACID_BSS	5	/* XXX hardcoded somewhere */
 
 /* Rx MAC descriptor. */
 struct r92s_rx_stat {
@@ -505,8 +563,11 @@ struct r92s_rx_stat {
 #define R92S_RXDW0_PKTLEN_M	0x00003fff
 #define R92S_RXDW0_PKTLEN_S	0
 #define R92S_RXDW0_CRCERR	0x00004000
+#define R92S_RXDW0_ICVERR	0x00008000
 #define R92S_RXDW0_INFOSZ_M	0x000f0000
 #define R92S_RXDW0_INFOSZ_S	16
+#define R92S_RXDW0_CIPHER_M	0x00700000
+#define R92S_RXDW0_CIPHER_S	20
 #define R92S_RXDW0_QOS		0x00800000
 #define R92S_RXDW0_SHIFT_M	0x03000000
 #define R92S_RXDW0_SHIFT_S	24
@@ -581,6 +642,7 @@ struct r92s_tx_desc {
 #define R92S_TXDW1_KEYIDX_S	17
 #define R92S_TXDW1_CIPHER_M	0x00c00000
 #define R92S_TXDW1_CIPHER_S	22
+#define R92S_TXDW1_CIPHER_NONE	0
 #define R92S_TXDW1_CIPHER_WEP	1
 #define R92S_TXDW1_CIPHER_TKIP	2
 #define R92S_TXDW1_CIPHER_AES	3
@@ -728,6 +790,13 @@ struct rsu_vap {
 #define	RSU_UNLOCK(sc)			mtx_unlock(&(sc)->sc_mtx)
 #define	RSU_ASSERT_LOCKED(sc)		mtx_assert(&(sc)->sc_mtx, MA_OWNED)
 
+#define RSU_DELKEY_BMAP_LOCK_INIT(_sc)	\
+	mtx_init(&(_sc)->free_keys_bmap_mtx, "bmap lock", NULL, MTX_DEF)
+#define RSU_DELKEY_BMAP_LOCK(_sc)	mtx_lock(&(_sc)->free_keys_bmap_mtx)
+#define RSU_DELKEY_BMAP_UNLOCK(_sc)	mtx_unlock(&(_sc)->free_keys_bmap_mtx)
+#define RSU_DELKEY_BMAP_LOCK_DESTROY(_sc)	\
+	mtx_destroy(&(_sc)->free_keys_bmap_mtx)
+
 struct rsu_softc {
 	struct ieee80211com		sc_ic;
 	struct mbufq			sc_snd;
@@ -762,6 +831,13 @@ struct rsu_softc {
 	STAILQ_HEAD(, rsu_data)		sc_tx_inactive;
 	STAILQ_HEAD(, rsu_data)		sc_tx_pending[RSU_N_TRANSFER];
 
+	struct task			del_key_task;
+	uint8_t				keys_bmap[R92S_CAM_ENTRY_BYTES];
+	const struct ieee80211_key	*group_keys[IEEE80211_WEP_NKID];
+
+	struct mtx			free_keys_bmap_mtx;
+	uint8_t				free_keys_bmap[R92S_CAM_ENTRY_BYTES];
+
 	union {
 		struct rsu_rx_radiotap_header th;
 		uint8_t	pad[64];


More information about the svn-src-all mailing list