summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authoravos <avos@FreeBSD.org>2015-11-10 00:12:00 +0000
committeravos <avos@FreeBSD.org>2015-11-10 00:12:00 +0000
commitca5f285ba46e88b7c2d64982aec77c5647bc5717 (patch)
tree39f3f7cdf01b284ed08a88078b63ea3df973776c
parent820825160a2e550b12f084256f78512c36ef6819 (diff)
downloadFreeBSD-src-ca5f285ba46e88b7c2d64982aec77c5647bc5717.zip
FreeBSD-src-ca5f285ba46e88b7c2d64982aec77c5647bc5717.tar.gz
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
-rw-r--r--sys/dev/usb/wlan/if_urtwn.c253
-rw-r--r--sys/dev/usb/wlan/if_urtwnreg.h8
-rw-r--r--sys/dev/usb/wlan/if_urtwnvar.h3
3 files changed, 247 insertions, 17 deletions
diff --git a/sys/dev/usb/wlan/if_urtwn.c b/sys/dev/usb/wlan/if_urtwn.c
index 675810a..9a71e72 100644
--- a/sys/dev/usb/wlan/if_urtwn.c
+++ b/sys/dev/usb/wlan/if_urtwn.c
@@ -221,7 +221,15 @@ static int urtwn_read_chipid(struct urtwn_softc *);
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 char name[IFNAMSIZ], int unit,
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, const char name[IFNAMSIZ], int unit,
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, struct urtwn_data *data, int status)
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, enum ieee80211_state nstate, int arg)
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, enum ieee80211_state nstate, int arg)
/* 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, enum ieee80211_state nstate, int arg)
}
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, enum ieee80211_state nstate, int arg)
/* 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, enum ieee80211_state nstate, int arg)
/* 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 *sc)
/* 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",
diff --git a/sys/dev/usb/wlan/if_urtwnreg.h b/sys/dev/usb/wlan/if_urtwnreg.h
index f48f959..89e0dbc 100644
--- a/sys/dev/usb/wlan/if_urtwnreg.h
+++ b/sys/dev/usb/wlan/if_urtwnreg.h
@@ -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
diff --git a/sys/dev/usb/wlan/if_urtwnvar.h b/sys/dev/usb/wlan/if_urtwnvar.h
index f2426ff..b24c437 100644
--- a/sys/dev/usb/wlan/if_urtwnvar.h
+++ b/sys/dev/usb/wlan/if_urtwnvar.h
@@ -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);
};
OpenPOWER on IntegriCloud