Skip site navigation (1)Skip section navigation (2)
Date:      Tue, 10 Nov 2015 00:12:01 +0000 (UTC)
From:      Andriy Voskoboinyk <avos@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r290631 - head/sys/dev/usb/wlan
Message-ID:  <201511100012.tAA0C1mI096810@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: avos
Date: Tue Nov 10 00:12:00 2015
New Revision: 290631
URL: https://svnweb.freebsd.org/changeset/base/290631

Log:
  urtwn(4): add HOSTAP mode support.
  
  Tested with RTL8188EU, HOSTAP and STA modes
  
  Reviewed by:	kevlo
  Approved by:	adrian (mentor)
  Differential Revision:	https://reviews.freebsd.org/D4034

Modified:
  head/sys/dev/usb/wlan/if_urtwn.c
  head/sys/dev/usb/wlan/if_urtwnreg.h
  head/sys/dev/usb/wlan/if_urtwnvar.h

Modified: head/sys/dev/usb/wlan/if_urtwn.c
==============================================================================
--- head/sys/dev/usb/wlan/if_urtwn.c	Mon Nov  9 23:46:13 2015	(r290630)
+++ head/sys/dev/usb/wlan/if_urtwn.c	Tue Nov 10 00:12:00 2015	(r290631)
@@ -221,7 +221,15 @@ static int		urtwn_read_chipid(struct urt
 static void		urtwn_read_rom(struct urtwn_softc *);
 static void		urtwn_r88e_read_rom(struct urtwn_softc *);
 static int		urtwn_ra_init(struct urtwn_softc *);
-static void		urtwn_tsf_sync_enable(struct urtwn_softc *);
+static void		urtwn_init_beacon(struct urtwn_softc *,
+			    struct urtwn_vap *);
+static int		urtwn_setup_beacon(struct urtwn_softc *,
+			    struct ieee80211_node *);
+static void		urtwn_update_beacon(struct ieee80211vap *, int);
+static int		urtwn_tx_beacon(struct urtwn_softc *sc,
+			    struct urtwn_vap *);
+static void		urtwn_tsf_sync_enable(struct urtwn_softc *,
+			    struct ieee80211vap *);
 static void		urtwn_set_led(struct urtwn_softc *, int, int);
 static void		urtwn_set_mode(struct urtwn_softc *, uint8_t);
 static int		urtwn_newstate(struct ieee80211vap *,
@@ -441,6 +449,7 @@ urtwn_attach(device_t self)
 	ic->ic_caps =
 		  IEEE80211_C_STA		/* station mode */
 		| IEEE80211_C_MONITOR		/* monitor mode */
+		| IEEE80211_C_HOSTAP		/* hostap mode */
 		| IEEE80211_C_SHPREAMBLE	/* short preamble supported */
 		| IEEE80211_C_SHSLOT		/* short slot time supported */
 		| IEEE80211_C_BGSCAN		/* capable of bg scanning */
@@ -565,6 +574,7 @@ urtwn_vap_create(struct ieee80211com *ic
     const uint8_t bssid[IEEE80211_ADDR_LEN],
     const uint8_t mac[IEEE80211_ADDR_LEN])
 {
+	struct urtwn_softc *sc = ic->ic_softc;
 	struct urtwn_vap *uvp;
 	struct ieee80211vap *vap;
 
@@ -582,9 +592,13 @@ urtwn_vap_create(struct ieee80211com *ic
 		return (NULL);
 	}
 
+	if (opmode == IEEE80211_M_HOSTAP)
+		urtwn_init_beacon(sc, uvp);
+
 	/* override state transition machine */
 	uvp->newstate = vap->iv_newstate;
 	vap->iv_newstate = urtwn_newstate;
+	vap->iv_update_beacon = urtwn_update_beacon;
 
 	/* complete setup */
 	ieee80211_vap_attach(vap, ieee80211_media_change,
@@ -597,7 +611,12 @@ static void
 urtwn_vap_delete(struct ieee80211vap *vap)
 {
 	struct urtwn_vap *uvp = URTWN_VAP(vap);
+	enum ieee80211_opmode opmode = vap->iv_opmode;
 
+	if (opmode == IEEE80211_M_HOSTAP) {
+		if (uvp->bcn_mbuf != NULL)
+			m_freem(uvp->bcn_mbuf);
+	}
 	ieee80211_vap_detach(vap);
 	free(uvp, M_80211_VAP);
 }
@@ -831,7 +850,8 @@ urtwn_txeof(struct urtwn_softc *sc, stru
 
 	URTWN_ASSERT_LOCKED(sc);
 
-	ieee80211_tx_complete(data->ni, data->m, status);
+	if (data->ni != NULL)	/* not a beacon frame */
+		ieee80211_tx_complete(data->ni, data->m, status);
 
 	data->ni = NULL;
 	data->m = NULL;
@@ -1477,10 +1497,144 @@ urtwn_ra_init(struct urtwn_softc *sc)
 }
 
 static void
-urtwn_tsf_sync_enable(struct urtwn_softc *sc)
+urtwn_init_beacon(struct urtwn_softc *sc, struct urtwn_vap *uvp)
 {
-	urtwn_write_1(sc, R92C_BCN_CTRL,
-	    urtwn_read_1(sc, R92C_BCN_CTRL) & ~R92C_BCN_CTRL_DIS_TSF_UDT0);
+	struct r92c_tx_desc *txd = &uvp->bcn_desc;
+
+	txd->txdw0 = htole32(
+	    SM(R92C_TXDW0_OFFSET, sizeof(*txd)) | R92C_TXDW0_BMCAST |
+	    R92C_TXDW0_OWN | R92C_TXDW0_FSG | R92C_TXDW0_LSG);
+	txd->txdw1 = htole32(
+	    SM(R92C_TXDW1_QSEL, R92C_TXDW1_QSEL_BEACON) |
+	    SM(R92C_TXDW1_RAID, R92C_RAID_11B));
+
+	if (sc->chip & URTWN_CHIP_88E)
+		txd->txdw1 |= htole32(SM(R88E_TXDW1_MACID, URTWN_MACID_BC));
+	else
+		txd->txdw1 |= htole32(SM(R92C_TXDW1_MACID, URTWN_MACID_BC));
+
+	txd->txdw4 = htole32(R92C_TXDW4_DRVRATE);
+	txd->txdw5 = htole32(SM(R92C_TXDW5_DATARATE, URTWN_RIDX_CCK1));
+	txd->txdseq = htole16(R92C_TXDSEQ_HWSEQ_EN);
+}
+
+static int
+urtwn_setup_beacon(struct urtwn_softc *sc, struct ieee80211_node *ni)
+{
+ 	struct ieee80211vap *vap = ni->ni_vap;
+	struct urtwn_vap *uvp = URTWN_VAP(vap);
+	struct mbuf *m;
+	int error;
+
+	URTWN_ASSERT_LOCKED(sc);
+
+	if (ni->ni_chan == IEEE80211_CHAN_ANYC)
+		return (EINVAL);
+
+	m = ieee80211_beacon_alloc(ni);
+	if (m == NULL) {
+		device_printf(sc->sc_dev,
+		    "%s: could not allocate beacon frame\n", __func__);
+		return (ENOMEM);
+	}
+
+	if (uvp->bcn_mbuf != NULL)
+		m_freem(uvp->bcn_mbuf);
+
+	uvp->bcn_mbuf = m;
+
+	if ((error = urtwn_tx_beacon(sc, uvp)) != 0)
+		return (error);
+
+	/* XXX bcnq stuck workaround */
+	if ((error = urtwn_tx_beacon(sc, uvp)) != 0)
+		return (error);
+
+	return (0);
+}
+
+static void
+urtwn_update_beacon(struct ieee80211vap *vap, int item)
+{
+	struct urtwn_softc *sc = vap->iv_ic->ic_softc;
+	struct urtwn_vap *uvp = URTWN_VAP(vap);
+	struct ieee80211_beacon_offsets *bo = &vap->iv_bcn_off;
+	struct ieee80211_node *ni = vap->iv_bss;
+	int mcast = 0;
+
+	URTWN_LOCK(sc);
+	if (uvp->bcn_mbuf == NULL) {
+		uvp->bcn_mbuf = ieee80211_beacon_alloc(ni);
+		if (uvp->bcn_mbuf == NULL) {
+			device_printf(sc->sc_dev,
+			    "%s: could not allocate beacon frame\n", __func__);
+			URTWN_UNLOCK(sc);
+			return;
+		}
+	}
+	URTWN_UNLOCK(sc);
+
+	if (item == IEEE80211_BEACON_TIM)
+		mcast = 1;	/* XXX */
+
+	setbit(bo->bo_flags, item);
+	ieee80211_beacon_update(ni, uvp->bcn_mbuf, mcast);
+
+	URTWN_LOCK(sc);
+	urtwn_tx_beacon(sc, uvp);
+	URTWN_UNLOCK(sc);
+}
+
+/*
+ * Push a beacon frame into the chip. Beacon will
+ * be repeated by the chip every R92C_BCN_INTERVAL.
+ */
+static int
+urtwn_tx_beacon(struct urtwn_softc *sc, struct urtwn_vap *uvp)
+{
+	struct r92c_tx_desc *desc = &uvp->bcn_desc;
+	struct urtwn_data *bf;
+
+	URTWN_ASSERT_LOCKED(sc);
+
+	bf = urtwn_getbuf(sc);
+	if (bf == NULL)
+		return (ENOMEM);
+
+	memcpy(bf->buf, desc, sizeof(*desc));
+	urtwn_tx_start(sc, uvp->bcn_mbuf, IEEE80211_FC0_TYPE_MGT, bf);
+
+	sc->sc_txtimer = 5;
+	callout_reset(&sc->sc_watchdog_ch, hz, urtwn_watchdog, sc);
+
+	return (0);
+}
+
+static void
+urtwn_tsf_sync_enable(struct urtwn_softc *sc, struct ieee80211vap *vap)
+{
+	/* Reset TSF. */
+	urtwn_write_1(sc, R92C_DUAL_TSF_RST, R92C_DUAL_TSF_RST0);
+
+	switch (vap->iv_opmode) {
+	case IEEE80211_M_STA:
+		/* Enable TSF synchronization. */
+		urtwn_write_1(sc, R92C_BCN_CTRL,
+		    urtwn_read_1(sc, R92C_BCN_CTRL) &
+		    ~R92C_BCN_CTRL_DIS_TSF_UDT0);
+		break;
+	case IEEE80211_M_HOSTAP:
+		/* Enable beaconing. */
+		urtwn_write_1(sc, R92C_MBID_NUM,
+		    urtwn_read_1(sc, R92C_MBID_NUM) | R92C_MBID_TXBCN_RPT0);
+		urtwn_write_1(sc, R92C_BCN_CTRL,
+		    urtwn_read_1(sc, R92C_BCN_CTRL) | R92C_BCN_CTRL_EN_BCN);
+		break;
+	default:
+		device_printf(sc->sc_dev, "undefined opmode %d\n",
+		    vap->iv_opmode);
+		return;
+	}
 }
 
 static void
@@ -1528,6 +1682,9 @@ urtwn_newstate(struct ieee80211vap *vap,
 	struct urtwn_softc *sc = ic->ic_softc;
 	struct ieee80211_node *ni;
 	enum ieee80211_state ostate;
+	uint32_t reg;
+	uint8_t mode;
+	int error = 0;
 
 	ostate = vap->iv_state;
 	DPRINTF("%s -> %s\n", ieee80211_state_name[ostate],
@@ -1547,14 +1704,18 @@ urtwn_newstate(struct ieee80211vap *vap,
 		/* Stop Rx of data frames. */
 		urtwn_write_2(sc, R92C_RXFLTMAP2, 0);
 
-		/* Rest TSF. */
-		urtwn_write_1(sc, R92C_DUAL_TSF_RST, 0x03);
-
 		/* Disable TSF synchronization. */
 		urtwn_write_1(sc, R92C_BCN_CTRL,
-		    urtwn_read_1(sc, R92C_BCN_CTRL) |
+		    (urtwn_read_1(sc, R92C_BCN_CTRL) & ~R92C_BCN_CTRL_EN_BCN) |
 		    R92C_BCN_CTRL_DIS_TSF_UDT0);
 
+		/* Disable beaconing. */
+		urtwn_write_1(sc, R92C_MBID_NUM,
+		    urtwn_read_1(sc, R92C_MBID_NUM) & ~R92C_MBID_TXBCN_RPT0);
+
+		/* Reset TSF. */
+		urtwn_write_1(sc, R92C_DUAL_TSF_RST, R92C_DUAL_TSF_RST0);
+
 		/* Reset EDCA parameters. */
 		urtwn_write_4(sc, R92C_EDCA_VO_PARAM, 0x002f3217);
 		urtwn_write_4(sc, R92C_EDCA_VI_PARAM, 0x005e4317);
@@ -1583,8 +1744,31 @@ urtwn_newstate(struct ieee80211vap *vap,
 		}
 
 		ni = ieee80211_ref_node(vap->iv_bss);
+
+		if (ic->ic_bsschan == IEEE80211_CHAN_ANYC ||
+		    ni->ni_chan == IEEE80211_CHAN_ANYC) {
+			device_printf(sc->sc_dev,
+			    "%s: could not move to RUN state\n", __func__);
+			error = EINVAL;
+			goto end_run;
+		}
+
+		switch (vap->iv_opmode) {
+		case IEEE80211_M_STA:
+			mode = R92C_MSR_INFRA;
+			break;
+		case IEEE80211_M_HOSTAP:
+			mode = R92C_MSR_AP;
+			break;
+		default:
+			device_printf(sc->sc_dev, "undefined opmode %d\n",
+			    vap->iv_opmode);
+			error = EINVAL;
+			goto end_run;
+		}
+
 		/* Set media status to 'Associated'. */
-		urtwn_set_mode(sc, R92C_MSR_INFRA);
+		urtwn_set_mode(sc, mode);
 
 		/* Set BSSID. */
 		urtwn_write_4(sc, R92C_BSSID + 0, LE_READ_4(&ni->ni_bssid[0]));
@@ -1606,13 +1790,28 @@ urtwn_newstate(struct ieee80211vap *vap,
 
 		/* Allow Rx from our BSSID only. */
 		if (ic->ic_promisc == 0) {
-			urtwn_write_4(sc, R92C_RCR,
-			    urtwn_read_4(sc, R92C_RCR) |
-			    R92C_RCR_CBSSID_DATA | R92C_RCR_CBSSID_BCN);
+			reg = urtwn_read_4(sc, R92C_RCR);
+
+			if (vap->iv_opmode != IEEE80211_M_HOSTAP)
+				reg |= R92C_RCR_CBSSID_DATA;
+
+			reg |= R92C_RCR_CBSSID_BCN;
+
+			urtwn_write_4(sc, R92C_RCR, reg);
+		}
+
+		if (vap->iv_opmode == IEEE80211_M_HOSTAP) {
+			error = urtwn_setup_beacon(sc, ni);
+			if (error != 0) {
+				device_printf(sc->sc_dev,
+				    "unable to push beacon into the chip, "
+				    "error %d\n", error);
+				goto end_run;
+			}
 		}
 
 		/* Enable TSF synchronization. */
-		urtwn_tsf_sync_enable(sc);
+		urtwn_tsf_sync_enable(sc, vap);
 
 		urtwn_write_1(sc, R92C_SIFS_CCK + 1, 10);
 		urtwn_write_1(sc, R92C_SIFS_OFDM + 1, 10);
@@ -1634,14 +1833,17 @@ urtwn_newstate(struct ieee80211vap *vap,
 		/* Reset temperature calibration state machine. */
 		sc->thcal_state = 0;
 		sc->thcal_lctemp = 0;
+
+end_run:
 		ieee80211_free_node(ni);
 		break;
 	default:
 		break;
 	}
+
 	URTWN_UNLOCK(sc);
 	IEEE80211_LOCK(ic);
-	return(uvp->newstate(vap, nstate, arg));
+	return (error != 0 ? error : uvp->newstate(vap, nstate, arg));
 }
 
 static void
@@ -2791,11 +2993,25 @@ urtwn_rxfilter_init(struct urtwn_softc *
 
 	/* Filter for management frames. */
 	filter = 0x7f3f;
-	if (vap->iv_opmode == IEEE80211_M_STA) {
+	switch (vap->iv_opmode) {
+	case IEEE80211_M_STA:
 		filter &= ~(
 		    R92C_RXFLTMAP_SUBTYPE(IEEE80211_FC0_SUBTYPE_ASSOC_REQ) |
 		    R92C_RXFLTMAP_SUBTYPE(IEEE80211_FC0_SUBTYPE_REASSOC_REQ) |
 		    R92C_RXFLTMAP_SUBTYPE(IEEE80211_FC0_SUBTYPE_PROBE_REQ));
+		break;
+	case IEEE80211_M_HOSTAP:
+		filter &= ~(
+		    R92C_RXFLTMAP_SUBTYPE(IEEE80211_FC0_SUBTYPE_ASSOC_RESP) |
+		    R92C_RXFLTMAP_SUBTYPE(IEEE80211_FC0_SUBTYPE_REASSOC_RESP) |
+		    R92C_RXFLTMAP_SUBTYPE(IEEE80211_FC0_SUBTYPE_BEACON));
+		break;
+	case IEEE80211_M_MONITOR:
+		break;
+	default:
+		device_printf(sc->sc_dev, "%s: undefined opmode %d\n",
+		    __func__, vap->iv_opmode);
+		break;
 	}
 	urtwn_write_2(sc, R92C_RXFLTMAP0, filter);
 
@@ -3172,7 +3388,10 @@ urtwn_set_promisc(struct urtwn_softc *sc
 	if (vap->iv_state == IEEE80211_S_RUN) {
 		switch (vap->iv_opmode) {
 		case IEEE80211_M_STA:
-			mask2 |= R92C_RCR_CBSSID_BCN | R92C_RCR_CBSSID_DATA;
+			mask2 |= R92C_RCR_CBSSID_DATA;
+			/* FALLTHROUGH */
+		case IEEE80211_M_HOSTAP:
+			mask2 |= R92C_RCR_CBSSID_BCN;
 			break;
 		default:
 			device_printf(sc->sc_dev, "%s: undefined opmode %d\n",

Modified: head/sys/dev/usb/wlan/if_urtwnreg.h
==============================================================================
--- head/sys/dev/usb/wlan/if_urtwnreg.h	Mon Nov  9 23:46:13 2015	(r290630)
+++ head/sys/dev/usb/wlan/if_urtwnreg.h	Tue Nov 10 00:12:00 2015	(r290631)
@@ -495,6 +495,14 @@
 #define R92C_BCN_CTRL_EN_BCN		0x08
 #define R92C_BCN_CTRL_DIS_TSF_UDT0	0x10
 
+/* Bits for R92C_MBID_NUM. */
+#define R92C_MBID_TXBCN_RPT0		0x08
+#define R92C_MBID_TXBCN_RPT1		0x10
+
+/* Bits for R92C_DUAL_TSF_RST. */
+#define R92C_DUAL_TSF_RST0		0x01
+#define R92C_DUAL_TSF_RST1		0x02
+
 /* Bits for R92C_APSD_CTRL. */
 #define R92C_APSD_CTRL_OFF		0x40
 #define R92C_APSD_CTRL_OFF_STATUS	0x80

Modified: head/sys/dev/usb/wlan/if_urtwnvar.h
==============================================================================
--- head/sys/dev/usb/wlan/if_urtwnvar.h	Mon Nov  9 23:46:13 2015	(r290630)
+++ head/sys/dev/usb/wlan/if_urtwnvar.h	Tue Nov 10 00:12:00 2015	(r290631)
@@ -89,6 +89,9 @@ struct urtwn_fw_info {
 struct urtwn_vap {
 	struct ieee80211vap		vap;
 
+	struct r92c_tx_desc		bcn_desc;
+	struct mbuf			*bcn_mbuf;
+
 	int				(*newstate)(struct ieee80211vap *,
 					    enum ieee80211_state, int);
 };



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201511100012.tAA0C1mI096810>