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);
 };
_______________________________________________
svn-src-all@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to