diff --git a/sys/dev/wtap/if_wtap.c b/sys/dev/wtap/if_wtap.c --- a/sys/dev/wtap/if_wtap.c +++ b/sys/dev/wtap/if_wtap.c @@ -41,6 +41,7 @@ #include #include "if_medium.h" +#include "wtap_hal/hal.h" /* * This _requires_ vimage to be useful. @@ -149,10 +150,40 @@ int subtype, const struct ieee80211_rx_stats *stats, int rssi, int nf) { struct ieee80211vap *vap = ni->ni_vap; + struct wtap_softc *sc = vap->iv_ic->ic_softc; #if 0 DWTAP_PRINTF("[%d] %s\n", myath_id(ni), __func__); #endif + /* + * Call up first so subsequent work can use information + * potentially stored in the node (e.g. for ibss merge). + */ WTAP_VAP(vap)->av_recv_mgmt(ni, m, subtype, stats, rssi, nf); + + switch (subtype) { + case IEEE80211_FC0_SUBTYPE_BEACON: + case IEEE80211_FC0_SUBTYPE_PROBE_RESP: + if (vap->iv_opmode == IEEE80211_M_IBSS && + vap->iv_state == IEEE80211_S_RUN && + ieee80211_ibss_merge_check(ni)) { + uint64_t tsf = wtap_hal_get_tsf(sc->hal); + + /* + * Handle ibss merge as needed; check the tsf on the + * frame before attempting the merge. The 802.11 spec + * says the station should change it's bssid to match + * the oldest station with the same ssid, where oldest + * is determined by the tsf. Note that hardware + * reconfiguration happens through callback to + * ath_newstate as the state machine will go from + * RUN -> RUN when this happens. + */ + if (le64toh(ni->ni_tstamp.tsf) >= tsf) { + (void) ieee80211_ibss_merge(ni); + } + } + break; + } } static int @@ -193,7 +224,6 @@ printf("%s: cannot get mbuf\n", __func__); return ENOMEM; } - callout_init(&avp->av_swba, 0); avp->bf_node = ieee80211_ref_node(ni); return 0; @@ -202,7 +232,6 @@ static void wtap_beacon_config(struct wtap_softc *sc, struct ieee80211vap *vap) { - DWTAP_PRINTF("%s\n", __func__); } @@ -211,7 +240,10 @@ { struct wtap_vap *avp = arg; struct ieee80211vap *vap = arg; + struct wtap_softc *sc = vap->iv_ic->ic_softc; + struct ieee80211_frame *wh; struct mbuf *m; + uint64_t tsf; if (vap->iv_state < IEEE80211_S_RUN) { DWTAP_PRINTF("Skip beacon, not running, state %d", vap->iv_state); @@ -230,6 +262,11 @@ " changed size.\n",__func__); } + /* Get TSF from HAL, and insert it into beacon frame */ + tsf = wtap_hal_get_tsf(sc->hal); + wh = mtod(m, struct ieee80211_frame *); + memcpy(&wh[1], &tsf, sizeof(tsf)); + if (ieee80211_radiotap_active_vap(vap)) ieee80211_radiotap_tx(vap, m); @@ -264,11 +301,37 @@ ieee80211_free_node(ni); ni = ieee80211_ref_node(vap->iv_bss); switch (vap->iv_opmode) { + case IEEE80211_M_IBSS: case IEEE80211_M_MBSS: + /* + * Stop any previous beacon callout. This may be + * necessary, for example, when an ibss merge + * causes reconfiguration; there will be a state + * transition from RUN->RUN that means we may + * be called with beacon transmission active. + */ + callout_stop(&avp->av_swba); + error = wtap_beacon_alloc(sc, ni); if (error != 0) goto bad; + + /* + * If joining an adhoc network defer beacon timer + * configuration to the next beacon frame so we + * have a current TSF to use. Otherwise we're + * starting an ibss/bss so there's no need to delay; + * if this is the first vap moving to RUN state, then + * beacon state needs to be [re]configured. + */ + if (vap->iv_opmode == IEEE80211_M_IBSS && + ni->ni_tstamp.tsf != 0) + break; + wtap_beacon_config(sc, vap); + + /* Start TSF timer from now, and start s/w beacon alert */ + wtap_hal_reset_tsf(sc->hal); callout_reset(&avp->av_swba, avp->av_bcinterval, wtap_beacon_intrp, vap); break; @@ -314,7 +377,7 @@ avp->av_md = sc->sc_md; avp->av_bcinterval = msecs_to_ticks(BEACON_INTRERVAL + 100*sc->id); vap = (struct ieee80211vap *) avp; - error = ieee80211_vap_setup(ic, vap, name, unit, IEEE80211_M_MBSS, + error = ieee80211_vap_setup(ic, vap, name, unit, opmode, flags | IEEE80211_CLONE_NOBEACONS, bssid); if (error) { free(avp, M_80211_VAP); @@ -337,6 +400,7 @@ avp->av_dev = make_dev(&wtap_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600, "%s", (const char *)vap->iv_ifp->if_xname); avp->av_dev->si_drv1 = sc; + callout_init(&avp->av_swba, 0); /* TODO this is a hack to force it to choose the rate we want */ ni = ieee80211_ref_node(vap->iv_bss); @@ -460,6 +524,13 @@ free(bf, M_WTAP_RXBUF); return; } + + /* + * It's weird to do this, but sometimes wtap will + * receive AMPDU packets (like ping(8)) even when + * the ic does not supports 11n HT. + */ + m->m_flags &= ~M_AMPDU; #if 0 ieee80211_dump_pkt(ic, mtod(m, caddr_t), 0,0,0); #endif @@ -585,7 +656,7 @@ ic->ic_name = sc->name; ic->ic_phytype = IEEE80211_T_DS; ic->ic_opmode = IEEE80211_M_MBSS; - ic->ic_caps = IEEE80211_C_MBSS; + ic->ic_caps = IEEE80211_C_MBSS | IEEE80211_C_IBSS; ic->ic_max_keyix = 128; /* A value read from Atheros ATH_KEYMAX */ diff --git a/sys/dev/wtap/if_wtapvar.h b/sys/dev/wtap/if_wtapvar.h --- a/sys/dev/wtap/if_wtapvar.h +++ b/sys/dev/wtap/if_wtapvar.h @@ -134,6 +134,7 @@ int32_t id; int32_t up; struct wtap_medium *sc_md; /* interface medium */ + struct wtap_hal *hal; struct ieee80211_node* (* sc_node_alloc) (struct ieee80211vap *, const uint8_t [IEEE80211_ADDR_LEN]); void (*sc_node_free)(struct ieee80211_node *); diff --git a/sys/dev/wtap/wtap_hal/hal.h b/sys/dev/wtap/wtap_hal/hal.h --- a/sys/dev/wtap/wtap_hal/hal.h +++ b/sys/dev/wtap/wtap_hal/hal.h @@ -38,11 +38,19 @@ #include "../plugins/wtap_plugin.h" #include "handler.h" +#define HAL_TIMER_INTVAL 50 /* in msecs */ + struct wtap_hal { struct wtap_medium *hal_md; struct mtx hal_mtx; struct wtap_plugin *plugin; struct wtap_softc *hal_devs[MAX_NBR_WTAP]; + /* hardware information */ + struct hw { + struct callout timer_intr; + uint32_t timer_intr_intval; + uint64_t tsf; + } hw; }; void init_hal(struct wtap_hal *); @@ -51,5 +59,8 @@ void deregister_plugin(struct wtap_hal *); int32_t new_wtap(struct wtap_hal *, int32_t id); int32_t free_wtap(struct wtap_hal *, int32_t id); +void wtap_hal_timer_intr(void *); +void wtap_hal_reset_tsf(struct wtap_hal *); +uint64_t wtap_hal_get_tsf(struct wtap_hal *); #endif diff --git a/sys/dev/wtap/wtap_hal/hal.c b/sys/dev/wtap/wtap_hal/hal.c --- a/sys/dev/wtap/wtap_hal/hal.c +++ b/sys/dev/wtap/wtap_hal/hal.c @@ -73,6 +73,9 @@ init_medium(hal->hal_md); /* register event handler for packets */ TASK_INIT(&hal->hal_md->tx_handler->proc, 0, hal_tx_proc, hal); + + callout_init_mtx(&hal->hw.timer_intr, &hal->hal_mtx, 0); + hal->hw.timer_intr_intval = msecs_to_ticks(HAL_TIMER_INTVAL); } void @@ -182,6 +185,7 @@ sizeof(struct wtap_softc), M_WTAP, M_NOWAIT | M_ZERO); hal->hal_devs[id]->sc_md = hal->hal_md; hal->hal_devs[id]->id = id; + hal->hal_devs[id]->hal = hal; snprintf(hal->hal_devs[id]->name, sizeof(hal->hal_devs[id]->name), "wtap%d", id); mtx_init(&hal->hal_devs[id]->sc_mtx, "wtap_softc mtx", NULL, @@ -212,3 +216,33 @@ hal->hal_devs[id] = NULL; return 0; } + +void +wtap_hal_timer_intr(void *arg) +{ + struct wtap_hal *hal = arg; + uint32_t intval = hal->hw.timer_intr_intval; + + hal->hw.tsf += ticks_to_msecs(intval); + + callout_schedule(&hal->hw.timer_intr, intval); +} + +void +wtap_hal_reset_tsf(struct wtap_hal *hal) +{ + mtx_lock(&hal->hal_mtx); + + callout_stop(&hal->hw.timer_intr); + hal->hw.tsf = 0; + callout_reset(&hal->hw.timer_intr, hal->hw.timer_intr_intval, + wtap_hal_timer_intr, hal); + + mtx_unlock(&hal->hal_mtx); +} + +uint64_t +wtap_hal_get_tsf(struct wtap_hal *hal) +{ + return (hal->hw.tsf); +}