svn commit: r361768 - head/sys/dev/otus

Adrian Chadd adrian at FreeBSD.org
Wed Jun 3 20:25:03 UTC 2020


Author: adrian
Date: Wed Jun  3 20:25:02 2020
New Revision: 361768
URL: https://svnweb.freebsd.org/changeset/base/361768

Log:
  [otus] enable 802.11n for 2GHz and 5GHz.
  
  This flips on basic 11n for 2GHz/5GHz station operation.
  
  * It flips on HT20 and MCS rates;
  * It enables A-MPDU decap - the payload format is a bit different;
  * It does do some basic checks for HT40 but I haven't yet flipped on
    HT40 support;
  * It enables software A-MSDU transmit; I honestly don't want to make
    A-MPDU TX work and there are apparently issues with QoS and A-MPDU TX.
    So I totally am ignoring A-MPDU TX;
  * MCS rate transmit is fine.
  
  I haven't:
  
  * A-MPDU TX, as I said above;
  * made radiotap work fully;
  * HT40;
  * short-GI support;
  * lots of other stuff that honestly no-one is likely to use.
  
  But! Hey, this is another ye olde 11n USB NIC that now works pretty OK
  in 11n rates. A-MPDU receive seems fine enough given it's a draft-n
  device from before 2010.
  
  Tested:
  
  * Ye olde UB82 Test NIC (AR9170 + AR9104) - 2GHz/5GHz

Modified:
  head/sys/dev/otus/if_otus.c
  head/sys/dev/otus/if_otusreg.h

Modified: head/sys/dev/otus/if_otus.c
==============================================================================
--- head/sys/dev/otus/if_otus.c	Wed Jun  3 18:59:31 2020	(r361767)
+++ head/sys/dev/otus/if_otus.c	Wed Jun  3 20:25:02 2020	(r361768)
@@ -91,6 +91,7 @@ SYSCTL_INT(_hw_usb_otus, OID_AUTO, debug, CTLFLAG_RWTU
 #define	OTUS_DEBUG_REGIO	0x00000200
 #define	OTUS_DEBUG_IRQ		0x00000400
 #define	OTUS_DEBUG_TXCOMP	0x00000800
+#define	OTUS_DEBUG_RX_BUFFER	0x00001000
 #define	OTUS_DEBUG_ANY		0xffffffff
 
 #define	OTUS_DPRINTF(sc, dm, ...) \
@@ -131,7 +132,6 @@ static device_attach_t otus_attach;
 static device_detach_t otus_detach;
 
 static int	otus_attachhook(struct otus_softc *);
-void		otus_get_chanlist(struct otus_softc *);
 static void	otus_getradiocaps(struct ieee80211com *, int, int *,
 		    struct ieee80211_channel[]);
 int		otus_load_firmware(struct otus_softc *, const char *,
@@ -395,9 +395,8 @@ otus_vap_create(struct ieee80211com *ic, const char na
 	uvp->newstate = vap->iv_newstate;
 	vap->iv_newstate = otus_newstate;
 
-	/* XXX TODO: double-check */
-	vap->iv_ampdu_density = IEEE80211_HTCAP_MPDUDENSITY_16;
-	vap->iv_ampdu_rxmax = IEEE80211_HTCAP_MAXRXAMPDU_32K;
+	vap->iv_ampdu_density = IEEE80211_HTCAP_MPDUDENSITY_8;
+	vap->iv_ampdu_rxmax = IEEE80211_HTCAP_MAXRXAMPDU_64K;
 
 	ieee80211_ratectl_init(vap);
 
@@ -699,6 +698,16 @@ otus_attachhook(struct otus_softc *sc)
 	IEEE80211_ADDR_COPY(ic->ic_macaddr, sc->eeprom.baseEepHeader.macAddr);
 	sc->sc_led_newstate = otus_led_newstate_type3;	/* XXX */
 
+	if (sc->txmask == 0x5)
+		ic->ic_txstream = 2;
+	else
+		ic->ic_txstream = 1;
+
+	if (sc->rxmask == 0x5)
+		ic->ic_rxstream = 2;
+	else
+		ic->ic_rxstream = 1;
+
 	device_printf(sc->sc_dev,
 	    "MAC/BBP AR9170, RF AR%X, MIMO %dT%dR, address %s\n",
 	    (sc->capflags & AR5416_OPFLAGS_11A) ?
@@ -721,33 +730,21 @@ otus_attachhook(struct otus_softc *sc)
 	    IEEE80211_C_WME |		/* WME/QoS */
 	    IEEE80211_C_SHSLOT |	/* Short slot time supported. */
 	    IEEE80211_C_FF |		/* Atheros fast-frames supported. */
-	    IEEE80211_C_MONITOR |
+	    IEEE80211_C_MONITOR |	/* Enable monitor mode */
+	    IEEE80211_C_SWAMSDUTX |	/* Do software A-MSDU TX */
 	    IEEE80211_C_WPA;		/* WPA/RSN. */
 
-	/* XXX TODO: 11n */
-
+	ic->ic_htcaps =
+	    IEEE80211_HTC_HT |
 #if 0
-	if (sc->eeprom.baseEepHeader.opCapFlags & AR5416_OPFLAGS_11G) {
-		/* Set supported .11b and .11g rates. */
-		ic->ic_sup_rates[IEEE80211_MODE_11B] =
-		    ieee80211_std_rateset_11b;
-		ic->ic_sup_rates[IEEE80211_MODE_11G] =
-		    ieee80211_std_rateset_11g;
-	}
-	if (sc->eeprom.baseEepHeader.opCapFlags & AR5416_OPFLAGS_11A) {
-		/* Set supported .11a rates. */
-		ic->ic_sup_rates[IEEE80211_MODE_11A] =
-		    ieee80211_std_rateset_11a;
-	}
+	    IEEE80211_HTC_AMPDU |
 #endif
+	    IEEE80211_HTC_AMSDU |
+	    IEEE80211_HTCAP_MAXAMSDU_3839 |
+	    IEEE80211_HTCAP_SMPS_OFF;
 
-#if 0
-	/* Build the list of supported channels. */
-	otus_get_chanlist(sc);
-#else
 	otus_getradiocaps(ic, IEEE80211_CHAN_MAX, &ic->ic_nchans,
 	    ic->ic_channels);
-#endif
 
 	ieee80211_ifattach(ic);
 	ic->ic_raw_xmit = otus_raw_xmit;
@@ -780,38 +777,6 @@ otus_attachhook(struct otus_softc *sc)
 	return (0);
 }
 
-void
-otus_get_chanlist(struct otus_softc *sc)
-{
-	struct ieee80211com *ic = &sc->sc_ic;
-	uint16_t domain;
-	uint8_t chan;
-	int i;
-
-	/* XXX regulatory domain. */
-	domain = le16toh(sc->eeprom.baseEepHeader.regDmn[0]);
-	OTUS_DPRINTF(sc, OTUS_DEBUG_RESET, "regdomain=0x%04x\n", domain);
-
-	if (sc->eeprom.baseEepHeader.opCapFlags & AR5416_OPFLAGS_11G) {
-		for (i = 0; i < 14; i++) {
-			chan = ar_chans[i];
-			ic->ic_channels[chan].ic_freq =
-			    ieee80211_ieee2mhz(chan, IEEE80211_CHAN_2GHZ);
-			ic->ic_channels[chan].ic_flags =
-			    IEEE80211_CHAN_CCK | IEEE80211_CHAN_OFDM |
-			    IEEE80211_CHAN_DYN | IEEE80211_CHAN_2GHZ;
-		}
-	}
-	if (sc->eeprom.baseEepHeader.opCapFlags & AR5416_OPFLAGS_11A) {
-		for (i = 14; i < nitems(ar_chans); i++) {
-			chan = ar_chans[i];
-			ic->ic_channels[chan].ic_freq =
-			    ieee80211_ieee2mhz(chan, IEEE80211_CHAN_5GHZ);
-			ic->ic_channels[chan].ic_flags = IEEE80211_CHAN_A;
-		}
-	}
-}
-
 static void
 otus_getradiocaps(struct ieee80211com *ic,
     int maxchans, int *nchans, struct ieee80211_channel chans[])
@@ -824,15 +789,13 @@ otus_getradiocaps(struct ieee80211com *ic,
 	if (sc->eeprom.baseEepHeader.opCapFlags & AR5416_OPFLAGS_11G) {
 		setbit(bands, IEEE80211_MODE_11B);
 		setbit(bands, IEEE80211_MODE_11G);
-#if 0
-		if (sc->sc_ht)
-			setbit(bands, IEEE80211_MODE_11NG);
-#endif
+		setbit(bands, IEEE80211_MODE_11NG);
 		ieee80211_add_channel_list_2ghz(chans, maxchans, nchans,
 		    ar_chans, 14, bands, 0);
 	}
 	if (sc->eeprom.baseEepHeader.opCapFlags & AR5416_OPFLAGS_11A) {
 		setbit(bands, IEEE80211_MODE_11A);
+		setbit(bands, IEEE80211_MODE_11NA);
 		ieee80211_add_channel_list_5ghz(chans, maxchans, nchans,
                     &ar_chans[14], nitems(ar_chans) - 14, bands, 0);
 	}
@@ -1588,6 +1551,12 @@ otus_cmd_rxeof(struct otus_softc *sc, uint8_t *buf, in
 	}
 }
 
+/*
+ * Handle a single MPDU.
+ *
+ * This may be a single MPDU, or it may be a sub-frame from an A-MPDU.
+ * In the latter case some of the header details need to be adjusted.
+ */
 void
 otus_sub_rxeof(struct otus_softc *sc, uint8_t *buf, int len, struct mbufq *rxq)
 {
@@ -1596,41 +1565,126 @@ otus_sub_rxeof(struct otus_softc *sc, uint8_t *buf, in
 #if 0
 	struct ieee80211_node *ni;
 #endif
-	struct ar_rx_tail *tail;
+	struct ar_rx_macstatus *mac_status = NULL;
+	struct ar_rx_phystatus *phy_status = NULL;
 	struct ieee80211_frame *wh;
 	struct mbuf *m;
-	uint8_t *plcp;
 //	int s;
-	int mlen;
 
-	if (__predict_false(len < AR_PLCP_HDR_LEN)) {
-		OTUS_DPRINTF(sc, OTUS_DEBUG_RXDONE,
-		    "sub-xfer too short %d\n", len);
-		return;
+
+	if (otus_debug & OTUS_DEBUG_RX_BUFFER) {
+		device_printf(sc->sc_dev, "%s: %*D\n",
+		    __func__, len, buf, "-");
 	}
-	plcp = buf;
 
-	/* All bits in the PLCP header are set to 1 for non-MPDU. */
-	if (memcmp(plcp, AR_PLCP_HDR_INTR, AR_PLCP_HDR_LEN) == 0) {
-		otus_cmd_rxeof(sc, plcp + AR_PLCP_HDR_LEN,
+	/*
+	 * Before any data path stuff - check to see if this is a command
+	 * response.
+	 *
+	 * All bits in the PLCP header are set to 1 for non-MPDU.
+	 */
+	if ((len >= AR_PLCP_HDR_LEN) &&
+	    memcmp(buf, AR_PLCP_HDR_INTR, AR_PLCP_HDR_LEN) == 0) {
+		otus_cmd_rxeof(sc, buf + AR_PLCP_HDR_LEN,
 		    len - AR_PLCP_HDR_LEN);
 		return;
 	}
 
-	/* Received MPDU. */
-	if (__predict_false(len < AR_PLCP_HDR_LEN + sizeof (*tail))) {
-		OTUS_DPRINTF(sc, OTUS_DEBUG_RXDONE, "MPDU too short %d\n", len);
+	/*
+	 * First step - get the status for the given frame.
+	 * This will tell us whether it's a single MPDU or
+	 * an A-MPDU subframe.
+	 */
+	if (len < sizeof(*mac_status)) {
+		OTUS_DPRINTF(sc, OTUS_DEBUG_RXDONE,
+		    "%s: sub-xfer too short (no mac_status) (len %d)\n",
+		    __func__, len);
 		counter_u64_add(ic->ic_ierrors, 1);
 		return;
 	}
-	tail = (struct ar_rx_tail *)(plcp + len - sizeof (*tail));
+	/*
+	 * Remove the mac_status from the payload length.
+	 *
+	 * Note: cheating, don't reallocate the buffer!
+	 */
+	mac_status = (struct ar_rx_macstatus *)(buf + len - sizeof(*mac_status));
+	len -= sizeof(*mac_status);
 
-	/* Discard error frames; don't discard BAD_RA (eg monitor mode); let net80211 do that */
-	if (__predict_false((tail->error & ~AR_RX_ERROR_BAD_RA) != 0)) {
-		OTUS_DPRINTF(sc, OTUS_DEBUG_RXDONE, "error frame 0x%02x\n", tail->error);
-		if (tail->error & AR_RX_ERROR_FCS) {
+	OTUS_DPRINTF(sc, OTUS_DEBUG_RXDONE, "%s: mac status=0x%x\n",
+	    __func__, mac_status->status);
+
+	/*
+	 * Next - check the MAC status before doing anything else.
+	 * Extract out the PLCP header for single and first frames;
+	 * since there's a single RX path we can shove PLCP headers
+	 * from both into sc->ar_last_rx_plcp[] so it can be reused.
+	 */
+	if (((mac_status->status & AR_RX_STATUS_MPDU_MASK) == AR_RX_STATUS_MPDU_SINGLE) ||
+	    ((mac_status->status & AR_RX_STATUS_MPDU_MASK) == AR_RX_STATUS_MPDU_FIRST)) {
+		/*
+		 * Ok, we need to at least have a PLCP header at
+		 * this point.
+		 */
+		if (len < AR_PLCP_HDR_LEN) {
+			OTUS_DPRINTF(sc, OTUS_DEBUG_RXDONE,
+			    "%s sub-xfer too short (no mac+plcp) (len %d\n)",
+			    __func__, len);
+			counter_u64_add(ic->ic_ierrors, 1);
+			return;
+		}
+		memcpy(sc->ar_last_rx_plcp, buf, AR_PLCP_HDR_LEN);
+
+		/*
+		 * At this point we can just consume the PLCP header.
+		 * The beginning of the frame should thus be data.
+		 */
+		buf += AR_PLCP_HDR_LEN;
+		len -= AR_PLCP_HDR_LEN;
+	}
+
+	/*
+	 * Next - see if we have a PHY status.
+	 *
+	 * The PHY status is at the end of the final A-MPDU subframe
+	 * or a single MPDU frame.
+	 *
+	 * We'll use this to tag frames with noise floor / RSSI
+	 * if they have valid information.
+	 */
+	if (((mac_status->status & AR_RX_STATUS_MPDU_MASK) == AR_RX_STATUS_MPDU_SINGLE) ||
+	    ((mac_status->status & AR_RX_STATUS_MPDU_MASK) == AR_RX_STATUS_MPDU_LAST)) {
+		if (len < sizeof(*phy_status)) {
+			OTUS_DPRINTF(sc, OTUS_DEBUG_RXDONE,
+			    "%s sub-xfer too short (no phy status) (len %d\n)",
+			    __func__, len);
+			counter_u64_add(ic->ic_ierrors, 1);
+			return;
+		}
+		/*
+		 * Take a pointer to the phy status and remove the length
+		 * from the end of the buffer.
+		 *
+		 * Note: we're cheating here; don't reallocate the buffer!
+		 */
+		phy_status = (struct ar_rx_phystatus *)
+		    (buf + len - sizeof(*phy_status));
+		len -= sizeof(*phy_status);
+	}
+
+	/*
+	 * Middle frames just have a MAC status (stripped above.)
+	 * No PHY status, and PLCP is from ar_last_rx_plcp.
+	 */
+
+	/*
+	 * Discard error frames; don't discard BAD_RA (eg monitor mode);
+	 * let net80211 do that
+	 */
+	if (__predict_false((mac_status->error & ~AR_RX_ERROR_BAD_RA) != 0)) {
+		OTUS_DPRINTF(sc, OTUS_DEBUG_RXDONE, "error frame 0x%02x\n", mac_status->error);
+		if (mac_status->error & AR_RX_ERROR_FCS) {
 			OTUS_DPRINTF(sc, OTUS_DEBUG_RXDONE, "bad FCS\n");
-		} else if (tail->error & AR_RX_ERROR_MMIC) {
+		} else if (mac_status->error & AR_RX_ERROR_MMIC) {
 			/* Report Michael MIC failures to net80211. */
 #if 0
 			ieee80211_notify_michael_failure(ni->ni_vap, wh, keyidx);
@@ -1640,77 +1694,75 @@ otus_sub_rxeof(struct otus_softc *sc, uint8_t *buf, in
 		counter_u64_add(ic->ic_ierrors, 1);
 		return;
 	}
-	/* Compute MPDU's length. */
-	mlen = len - AR_PLCP_HDR_LEN - sizeof (*tail);
-	/* Make sure there's room for an 802.11 header + FCS. */
-	if (__predict_false(mlen < IEEE80211_MIN_LEN)) {
+
+	/*
+	 * Make sure there's room for an 802.11 header + FCS.
+	 *
+	 * Note: a CTS/ACK is 14 bytes (FC, DUR, RA, FCS).
+	 * Making it IEEE80211_MIN_LEN misses CTS/ACKs.
+	 *
+	 * This won't be tossed at this point; eventually once
+	 * rx radiotap is implemented this will allow for
+	 * CTS/ACK frames.  Passing them up to net80211 will
+	 * currently make it angry (too short packets.)
+	 */
+	if (len < 2 + 2 + IEEE80211_ADDR_LEN + IEEE80211_CRC_LEN) {
+		OTUS_DPRINTF(sc, OTUS_DEBUG_RXDONE,
+		    "%s: too short for 802.11 (len %d)\n",
+		    __func__, len);
 		counter_u64_add(ic->ic_ierrors, 1);
 		return;
 	}
-	mlen -= IEEE80211_CRC_LEN;	/* strip 802.11 FCS */
 
-	wh = (struct ieee80211_frame *)(plcp + AR_PLCP_HDR_LEN);
+	len -= IEEE80211_CRC_LEN;	/* strip 802.11 FCS */
+	wh = (struct ieee80211_frame *) buf;
 
 	/*
-	 * TODO: I see > 2KiB buffers in this path; is it A-MSDU or something?
+	 * The firmware does seem to spit out a bunch of frames
+	 * with invalid frame control values here.  Just toss them
+	 * rather than letting net80211 get angry and log.
 	 */
-	m = m_get2(mlen, M_NOWAIT, MT_DATA, M_PKTHDR);
+	if ((wh->i_fc[0] & IEEE80211_FC0_VERSION_MASK) !=
+	    IEEE80211_FC0_VERSION_0) {
+		OTUS_DPRINTF(sc, OTUS_DEBUG_RXDONE,
+		    "%s: invalid 802.11 fc version (firmware bug?)\n",
+		        __func__);
+		counter_u64_add(ic->ic_ierrors, 1);
+		return;
+	}
+
+	m = m_get2(len, M_NOWAIT, MT_DATA, M_PKTHDR);
 	if (m == NULL) {
-		device_printf(sc->sc_dev, "%s: failed m_get2() (mlen=%d)\n", __func__, mlen);
+		device_printf(sc->sc_dev, "%s: failed m_get2() (len=%d)\n",
+		    __func__, len);
 		counter_u64_add(ic->ic_ierrors, 1);
 		return;
 	}
 
 	/* Finalize mbuf. */
-	memcpy(mtod(m, uint8_t *), wh, mlen);
-	m->m_pkthdr.len = m->m_len = mlen;
+	memcpy(mtod(m, uint8_t *), wh, len);
+	m->m_pkthdr.len = m->m_len = len;
 
-#if 0
-	if (__predict_false(sc->sc_drvbpf != NULL)) {
-		struct otus_rx_radiotap_header *tap = &sc->sc_rxtap;
-		struct mbuf mb;
+	/* XXX TODO: add setting rx radiotap fields here */
 
-		tap->wr_flags = 0;
-		tap->wr_antsignal = tail->rssi;
-		tap->wr_rate = 2;	/* In case it can't be found below. */
-		switch (tail->status & AR_RX_STATUS_MT_MASK) {
-		case AR_RX_STATUS_MT_CCK:
-			switch (plcp[0]) {
-			case  10: tap->wr_rate =   2; break;
-			case  20: tap->wr_rate =   4; break;
-			case  55: tap->wr_rate =  11; break;
-			case 110: tap->wr_rate =  22; break;
-			}
-			if (tail->status & AR_RX_STATUS_SHPREAMBLE)
-				tap->wr_flags |= IEEE80211_RADIOTAP_F_SHORTPRE;
-			break;
-		case AR_RX_STATUS_MT_OFDM:
-			switch (plcp[0] & 0xf) {
-			case 0xb: tap->wr_rate =  12; break;
-			case 0xf: tap->wr_rate =  18; break;
-			case 0xa: tap->wr_rate =  24; break;
-			case 0xe: tap->wr_rate =  36; break;
-			case 0x9: tap->wr_rate =  48; break;
-			case 0xd: tap->wr_rate =  72; break;
-			case 0x8: tap->wr_rate =  96; break;
-			case 0xc: tap->wr_rate = 108; break;
-			}
-			break;
-		}
-		mb.m_data = (caddr_t)tap;
-		mb.m_next = m;
-		mb.m_nextpkt = NULL;
-		mb.m_type = 0;
-		mb.m_flags = 0;
-		bpf_mtap(sc->sc_drvbpf, &mb, BPF_DIRECTION_IN);
+	/*
+	 * Ok, check the frame length and toss if it's too short
+	 * for net80211.  This will toss ACK/CTS.
+	 */
+	if (m->m_len < IEEE80211_MIN_LEN) {
+		/* XXX TODO: add radiotap receive here */
+		m_free(m); m = NULL;
+		return;
 	}
-#endif
 
-	/* Add RSSI/NF to this mbuf */
+	/* Add RSSI to this mbuf if we have a PHY header */
 	bzero(&rxs, sizeof(rxs));
-	rxs.r_flags = IEEE80211_R_NF | IEEE80211_R_RSSI;
+	rxs.r_flags = IEEE80211_R_NF;
 	rxs.c_nf = sc->sc_nf[0];	/* XXX chain 0 != combined rssi/nf */
-	rxs.c_rssi = tail->rssi;
+	if (phy_status != NULL) {
+		rxs.r_flags |= IEEE80211_R_RSSI;
+		rxs.c_rssi = phy_status->rssi;
+	}
 	/* XXX TODO: add MIMO RSSI/NF as well */
 	if (ieee80211_add_rx_params(m, &rxs) == 0) {
 		counter_u64_add(ic->ic_ierrors, 1);
@@ -1741,10 +1793,18 @@ otus_rxeof(struct usb_xfer *xfer, struct otus_data *da
 	caddr_t buf = data->buf;
 	struct ar_rx_head *head;
 	uint16_t hlen;
-	int len;
+	int len, offset = 0;
 
 	usbd_xfer_status(xfer, &len, NULL, NULL, NULL);
 
+	OTUS_DPRINTF(sc, OTUS_DEBUG_RXDONE,
+	    "%s: transfer completed; len=%d\n",
+	    __func__, len);
+	if (otus_debug & OTUS_DEBUG_RX_BUFFER) {
+		device_printf(sc->sc_dev, "%s: %*D\n",
+		    __func__, len, buf, "-");
+	}
+
 	while (len >= sizeof (*head)) {
 		head = (struct ar_rx_head *)buf;
 		if (__predict_false(head->tag != htole16(AR_RX_HEAD_TAG))) {
@@ -1753,19 +1813,26 @@ otus_rxeof(struct usb_xfer *xfer, struct otus_data *da
 			break;
 		}
 		hlen = le16toh(head->len);
+		OTUS_DPRINTF(sc, OTUS_DEBUG_RXDONE, "%s: hlen=%d\n",
+		    __func__, hlen);
 		if (__predict_false(sizeof (*head) + hlen > len)) {
 			OTUS_DPRINTF(sc, OTUS_DEBUG_RXDONE,
 			    "xfer too short %d/%d\n", len, hlen);
 			break;
 		}
 		/* Process sub-xfer. */
-		otus_sub_rxeof(sc, (uint8_t *)&head[1], hlen, rxq);
+		otus_sub_rxeof(sc, (uint8_t *) (((uint8_t *) buf) + 4), hlen, rxq);
 
 		/* Next sub-xfer is aligned on a 32-bit boundary. */
 		hlen = (sizeof (*head) + hlen + 3) & ~3;
+		offset += hlen;
+		OTUS_DPRINTF(sc, OTUS_DEBUG_RXDONE,
+		    "%s: rounded size is %d, next packet starts at %d\n",
+		    __func__, hlen, offset);
 		buf += hlen;
 		len -= hlen;
 	}
+	OTUS_DPRINTF(sc, OTUS_DEBUG_RXDONE, "%s: done!\n", __func__);
 }
 
 static void
@@ -2094,6 +2161,11 @@ otus_rate_to_hw_rate(struct otus_softc *sc, uint8_t ra
 
 	is_2ghz = !! (IEEE80211_IS_CHAN_2GHZ(sc->sc_ic.ic_curchan));
 
+	/* MCS check */
+	if (rate & 0x80) {
+		return rate;
+	}
+
 	switch (rate) {
 	/* CCK */
 	case 2:
@@ -2129,12 +2201,17 @@ otus_rate_to_hw_rate(struct otus_softc *sc, uint8_t ra
 			return (0x0);	/* 1MB CCK */
 		else
 			return (0xb);	/* 6MB OFDM */
-
-	/* XXX TODO: HT */
 	}
 }
 
 static int
+otus_hw_rate_is_ht(struct otus_softc *sc, uint8_t hw_rate)
+{
+
+	return !! (hw_rate & 0x80);
+}
+
+static int
 otus_hw_rate_is_ofdm(struct otus_softc *sc, uint8_t hw_rate)
 {
 
@@ -2262,7 +2339,10 @@ otus_tx(struct otus_softc *sc, struct ieee80211_node *
 	if (!ismcast) {
 		if (m->m_pkthdr.len + IEEE80211_CRC_LEN >= vap->iv_rtsthreshold)
 			macctl |= AR_TX_MAC_RTS;
-		else if (ic->ic_flags & IEEE80211_F_USEPROT) {
+		else if (otus_hw_rate_is_ht(sc, rate)) {
+			if (ic->ic_htprotmode == IEEE80211_PROT_RTSCTS)
+				macctl |= AR_TX_MAC_RTS;
+		} else if (ic->ic_flags & IEEE80211_F_USEPROT) {
 			if (ic->ic_protmode == IEEE80211_PROT_CTSONLY)
 				macctl |= AR_TX_MAC_CTS;
 			else if (ic->ic_protmode == IEEE80211_PROT_RTSCTS)
@@ -2270,8 +2350,15 @@ otus_tx(struct otus_softc *sc, struct ieee80211_node *
 		}
 	}
 
-	phyctl |= AR_TX_PHY_MCS(rate);
-	if (otus_hw_rate_is_ofdm(sc, rate)) {
+	phyctl |= AR_TX_PHY_MCS(rate & 0x7f); /* Note: MCS rates are 0x80 and above */
+	if (otus_hw_rate_is_ht(sc, rate)) {
+		phyctl |= AR_TX_PHY_MT_HT;
+		/* Always use all tx antennas for now, just to be safe */
+		phyctl |= AR_TX_PHY_ANTMSK(sc->txmask);
+
+		/* Heavy clip */
+		phyctl |= (rate & 0x7) << AR_TX_PHY_TX_HEAVY_CLIP_SHIFT;
+	} else if (otus_hw_rate_is_ofdm(sc, rate)) {
 		phyctl |= AR_TX_PHY_MT_OFDM;
 		/* Always use all tx antennas for now, just to be safe */
 		phyctl |= AR_TX_PHY_ANTMSK(sc->txmask);
@@ -2287,7 +2374,6 @@ otus_tx(struct otus_softc *sc, struct ieee80211_node *
 	if (!(macctl & AR_TX_MAC_NOACK))
 		OTUS_NODE(ni)->tx_done++;
 
-
 	/* Fill Tx descriptor. */
 	head = (struct ar_tx_head *)data->buf;
 	head->len = htole16(m->m_pkthdr.len + IEEE80211_CRC_LEN);
@@ -2463,6 +2549,22 @@ otus_updateslot(struct otus_softc *sc)
 	(void)otus_write_barrier(sc);
 }
 
+/*
+ * Things to do based on 2GHz or 5GHz:
+ *
+ * + slottime
+ * + dyn_sifs_ack
+ * + rts_cts_rate
+ * + slot time
+ * + mac_rates
+ * + mac_tpc
+ *
+ * And in the transmit path
+ * + tpc: carl9170_tx_rate_tpc_chains
+ * + carl9170_tx_physet()
+ * + disable short premable tx
+ */
+
 int
 otus_init_mac(struct otus_softc *sc)
 {
@@ -2641,10 +2743,17 @@ otus_program_phy(struct otus_softc *sc, struct ieee802
 	int error, i;
 
 	/* Select PHY programming based on band and bandwidth. */
-	if (IEEE80211_IS_CHAN_2GHZ(c))
-		vals = ar5416_phy_vals_2ghz_20mhz;
-	else
-		vals = ar5416_phy_vals_5ghz_20mhz;
+	if (IEEE80211_IS_CHAN_2GHZ(c)) {
+		if (IEEE80211_IS_CHAN_HT40(c))
+			vals = ar5416_phy_vals_2ghz_40mhz;
+		else
+			vals = ar5416_phy_vals_2ghz_20mhz;
+	} else {
+		if (IEEE80211_IS_CHAN_HT40(c))
+			vals = ar5416_phy_vals_5ghz_40mhz;
+		else
+			vals = ar5416_phy_vals_5ghz_20mhz;
+	}
 	for (i = 0; i < nitems(ar5416_phy_regs); i++)
 		otus_write(sc, AR_PHY(ar5416_phy_regs[i]), vals[i]);
 	sc->phy_vals = vals;

Modified: head/sys/dev/otus/if_otusreg.h
==============================================================================
--- head/sys/dev/otus/if_otusreg.h	Wed Jun  3 18:59:31 2020	(r361767)
+++ head/sys/dev/otus/if_otusreg.h	Wed Jun  3 20:25:02 2020	(r361768)
@@ -201,12 +201,17 @@ struct ar_tx_head {
 
 	uint32_t	phyctl;
 /* Modulation type. */
+#define AR_TX_PHY_MT_SHIFT	0 /* 0:1 - PHY mode */
 #define AR_TX_PHY_MT_CCK	0
 #define AR_TX_PHY_MT_OFDM	1
 #define AR_TX_PHY_MT_HT		2
-#define AR_TX_PHY_GF		(1 << 2)
-#define AR_TX_PHY_BW_SHIFT	3
-#define AR_TX_PHY_TPC_SHIFT	9
+#define AR_TX_PHY_GF		(1 << 2) /* 2 - greenfield */
+#define AR_TX_PHY_BW_SHIFT	3 /* 4:3 - bandwidth */
+#define AR_TX_PHY_BW_20MHZ		0
+#define AR_TX_PHY_BW_40MHZ		2
+#define AR_TX_PHY_BW_40MHZ_DUP		3
+#define AR_TX_PHY_TX_HEAVY_CLIP_SHIFT	6	/* 9:6 - heavy clip */
+#define AR_TX_PHY_TPC_SHIFT	9 /* 14:9 - TX power */
 #define AR_TX_PHY_ANTMSK(msk)	((msk) << 15)
 #define AR_TX_PHY_MCS(mcs)	((mcs) << 18)
 #define AR_TX_PHY_SHGI		(1U << 31)
@@ -220,15 +225,11 @@ struct ar_rx_head {
 } __packed;
 
 /* Rx descriptor. */
-struct ar_rx_tail {
-	uint8_t	rssi_ant[3];
-	uint8_t	rssi_ant_ext[3];
-	uint8_t	rssi;		/* Combined RSSI. */
-	uint8_t	evm[2][6];	/* Error Vector Magnitude. */
-	uint8_t	phy_err;
-	uint8_t	sa_idx;
-	uint8_t	da_idx;
-	uint8_t	error;
+
+struct ar_rx_macstatus {
+	uint8_t		sa_idx;
+	uint8_t		da_idx;
+	uint8_t		error;
 #define AR_RX_ERROR_TIMEOUT	(1 << 0)
 #define AR_RX_ERROR_OVERRUN	(1 << 1)
 #define AR_RX_ERROR_DECRYPT	(1 << 2)
@@ -236,16 +237,28 @@ struct ar_rx_tail {
 #define AR_RX_ERROR_BAD_RA	(1 << 4)
 #define AR_RX_ERROR_PLCP	(1 << 5)
 #define AR_RX_ERROR_MMIC	(1 << 6)
-
-	uint8_t	status;
+	uint8_t		status;
 /* Modulation type (same as AR_TX_PHY_MT). */
 #define AR_RX_STATUS_MT_MASK	0x3
 #define AR_RX_STATUS_MT_CCK	0
 #define AR_RX_STATUS_MT_OFDM	1
 #define AR_RX_STATUS_MT_HT	2
 #define AR_RX_STATUS_SHPREAMBLE	(1 << 3)
+#define AR_RX_STATUS_MPDU_MASK		0x30
+#define AR_RX_STATUS_MPDU_SINGLE	0x00
+#define AR_RX_STATUS_MPDU_LAST		0x10
+#define AR_RX_STATUS_MPDU_FIRST		0x20
+#define AR_RX_STATUS_MPDU_MIDDLE	0x30
 } __packed;
 
+struct ar_rx_phystatus {
+	uint8_t		rssi_ant[3];
+	uint8_t		rssi_ant_ext[3];
+	uint8_t		rssi;		/* Combined RSSI. */
+	uint8_t		evm[2][6];	/* Error Vector Magnitude. */
+	uint8_t		phy_err;
+} __packed;
+
 #define AR_PLCP_HDR_LEN	12
 /* Magic PLCP header for firmware notifications through Rx bulk pipe. */
 static uint8_t AR_PLCP_HDR_INTR[] = {
@@ -468,7 +481,6 @@ static const uint32_t ar5416_phy_vals_5ghz_20mhz[] = {
 	0x5fd80682, 0x7fe00482, 0x7f3c7bba, 0xf3307ff0
 };
 
-#ifdef notyet
 static const uint32_t ar5416_phy_vals_5ghz_40mhz[] = {
 	0x00000007, 0x000003c4, 0x00000000, 0xad848e19, 0x7d14e000,
 	0x9c0a9f6b, 0x00000090, 0x00000000, 0x02020200, 0x00000e0e,
@@ -537,9 +549,7 @@ static const uint32_t ar5416_phy_vals_5ghz_40mhz[] = {
 	0x17601685, 0x1f801104, 0x37a00c03, 0x3fc40883, 0x57c00803,
 	0x5fd80682, 0x7fe00482, 0x7f3c7bba, 0xf3307ff0
 };
-#endif
 
-#ifdef notyet
 static const uint32_t ar5416_phy_vals_2ghz_40mhz[] = {
 	0x00000007, 0x000003c4, 0x00000000, 0xad848e19, 0x7d14e000,
 	0x9c0a9f6b, 0x00000090, 0x00000000, 0x02020200, 0x00000e0e,
@@ -608,7 +618,6 @@ static const uint32_t ar5416_phy_vals_2ghz_40mhz[] = {
 	0x17601685, 0x1f801104, 0x37a00c03, 0x3fc40883, 0x57c00803,
 	0x5fd80682, 0x7fe00482, 0x7f3c7bba, 0xf3307ff0
 };
-#endif
 
 static const uint32_t ar5416_phy_vals_2ghz_20mhz[] = {
 	0x00000007, 0x00000300, 0x00000000, 0xad848e19, 0x7d14e000,
@@ -1053,6 +1062,9 @@ struct otus_softc {
 	struct otus_tx_cmd		sc_cmd[OTUS_CMD_LIST_COUNT];
 
 	struct usb_xfer			*sc_xfer[OTUS_N_XFER];
+
+	/* Last seen PLCP header; for A-MPDU decap */
+	uint8_t ar_last_rx_plcp[AR_PLCP_HDR_LEN];
 
 	STAILQ_HEAD(, otus_data)	sc_rx_active;
 	STAILQ_HEAD(, otus_data)	sc_rx_inactive;


More information about the svn-src-all mailing list