Date: Mon, 25 Feb 2008 23:26:19 GMT From: Sam Leffler <sam@FreeBSD.org> To: Perforce Change Reviews <perforce@freebsd.org> Subject: PERFORCE change 136194 for review Message-ID: <200802252326.m1PNQJWu059431@repoman.freebsd.org>
index | next in thread | raw e-mail
http://perforce.freebsd.org/chv.cgi?CH=136194 Change 136194 by sam@sam_ebb on 2008/02/25 23:25:36 o vap'ify iwn; this exposed some issues that need more thought for drivers that need to defer state changes o cleanup timer handling: there are 3 timers, one for periodic calibration (60 secs), tx watchdog (5 secs), and one for the rate control (500 ms); instead of running calibration off the rate control timer combine calbration and tx watchdog as these require the softc lock and have closer duty cycyles; also the rate control timer is totally bogus and needs to be removed but by splitting it out we can at least run it w/o acquiring a mutex (since rate control is now per-vap and the update work is race free or at least race tolerant) o add raw xmit support (initially for mgt frames but also for raw packet injection--untested) o strip out channel sorting since net80211 has it in this branch Affected files ... .. //depot/projects/vap/sys/conf/files#18 edit .. //depot/projects/vap/sys/dev/iwn/if_iwn.c#2 edit .. //depot/projects/vap/sys/dev/iwn/if_iwnvar.h#2 edit Differences ... ==== //depot/projects/vap/sys/conf/files#18 (text+ko) ==== @@ -744,6 +744,7 @@ dev/isp/isp_target.c optional isp dev/ispfw/ispfw.c optional ispfw dev/iwi/if_iwi.c optional iwi +dev/iwn/if_iwn.c optional iwn dev/ixgb/if_ixgb.c optional ixgb dev/ixgb/ixgb_ee.c optional ixgb dev/ixgb/ixgb_hw.c optional ixgb ==== //depot/projects/vap/sys/dev/iwn/if_iwn.c#2 (text+kox) ==== @@ -68,6 +68,7 @@ #include <net80211/ieee80211_var.h> #include <net80211/ieee80211_amrr.h> #include <net80211/ieee80211_radiotap.h> +#include <net80211/ieee80211_regdomain.h> #include <dev/iwn/if_iwnreg.h> #include <dev/iwn/if_iwnvar.h> @@ -76,6 +77,11 @@ static int iwn_attach(device_t); static int iwn_detach(device_t); static int iwn_cleanup(device_t); +static struct ieee80211vap *iwn_vap_create(struct ieee80211com *, + const char name[IFNAMSIZ], int unit, int opmode, + int flags, const uint8_t bssid[IEEE80211_ADDR_LEN], + const uint8_t mac[IEEE80211_ADDR_LEN]); +static void iwn_vap_delete(struct ieee80211vap *); static int iwn_shutdown(device_t); static int iwn_suspend(device_t); static int iwn_resume(device_t); @@ -102,7 +108,7 @@ struct ieee80211_node *iwn_node_alloc(struct ieee80211_node_table *); void iwn_newassoc(struct ieee80211_node *, int); int iwn_media_change(struct ifnet *); -int iwn_newstate(struct ieee80211com *, enum ieee80211_state, int); +int iwn_newstate(struct ieee80211vap *, enum ieee80211_state, int); void iwn_mem_lock(struct iwn_softc *); void iwn_mem_unlock(struct iwn_softc *); uint32_t iwn_mem_read(struct iwn_softc *, uint32_t); @@ -116,8 +122,11 @@ int iwn_transfer_firmware(struct iwn_softc *); int iwn_load_firmware(struct iwn_softc *); void iwn_unload_firmware(struct iwn_softc *); -void iwn_calib_timeout(void *); -void iwn_iter_func(void *, struct ieee80211_node *); +static void iwn_timer_timeout(void *); +static void iwn_calib_reset(struct iwn_softc *); +static void iwn_amrr_reset(struct ieee80211vap *); +static void iwn_amrr_iter_func(void *, struct ieee80211_node *); +static void iwn_amrr_timeout(void *); void iwn_ampdu_rx_start(struct iwn_softc *, struct iwn_rx_desc *); void iwn_rx_intr(struct iwn_softc *, struct iwn_rx_desc *, struct iwn_rx_data *); @@ -133,7 +142,9 @@ int iwn_tx_data(struct iwn_softc *, struct mbuf *, struct ieee80211_node *, struct iwn_tx_ring *); void iwn_start(struct ifnet *); -void iwn_watchdog(struct ifnet *); +static int iwn_raw_xmit(struct ieee80211_node *, struct mbuf *, + const struct ieee80211_bpf_params *); +static void iwn_watchdog(struct iwn_softc *); int iwn_ioctl(struct ifnet *, u_long, caddr_t); int iwn_cmd(struct iwn_softc *, int, const void *, int, int); int iwn_setup_node_mrr(struct iwn_softc *, uint8_t, int); @@ -168,11 +179,10 @@ static void iwn_scan_start(struct ieee80211com *); static void iwn_scan_end(struct ieee80211com *); static void iwn_set_channel(struct ieee80211com *); -static void iwn_scan_curchan(struct ieee80211com *, unsigned long); -static void iwn_scan_mindwell(struct ieee80211com *); +static void iwn_scan_curchan(struct ieee80211_scan_state *, unsigned long); +static void iwn_scan_mindwell(struct ieee80211_scan_state *); static void iwn_ops(void *, int); static int iwn_queue_cmd( struct iwn_softc *, int, int, int); -static void iwn_tick(void *); static void iwn_bpfattach(struct iwn_softc *); static void iwn_sysctlattach(struct iwn_softc *); @@ -280,8 +290,7 @@ IWN_LOCK_INIT(sc); IWN_CMD_LOCK_INIT(sc); - callout_init_mtx(&sc->calib_to, &sc->sc_mtx, 0); - callout_init_mtx(&sc->watchdog_to, &sc->sc_mtx, 0); + callout_init_mtx(&sc->sc_timer_to, &sc->sc_mtx, 0); /* * Create the taskqueues used by the driver. Primarily @@ -354,7 +363,7 @@ goto fail; } - ifp = sc->sc_ifp = if_alloc(IFT_ETHER); + ifp = sc->sc_ifp = if_alloc(IFT_IEEE80211); if (ifp == NULL) { device_printf(dev, "can not allocate ifnet structure\n"); goto fail; @@ -363,7 +372,6 @@ ic->ic_ifp = ifp; ic->ic_phytype = IEEE80211_T_OFDM; /* not only, but not used */ ic->ic_opmode = IEEE80211_M_STA; /* default to BSS mode */ - ic->ic_state = IEEE80211_S_INIT; /* set device capabilities */ ic->ic_caps = @@ -406,7 +414,9 @@ IFQ_SET_READY(&ifp->if_snd); ieee80211_ifattach(ic); - ic->ic_bmissthreshold = 10; /* override default */ + ic->ic_vap_create = iwn_vap_create; + ic->ic_vap_delete = iwn_vap_delete; + ic->ic_raw_xmit = iwn_raw_xmit; ic->ic_node_alloc = iwn_node_alloc; ic->ic_newassoc = iwn_newassoc; ic->ic_wme.wme_update = iwn_wme_update; @@ -416,15 +426,6 @@ ic->ic_scan_curchan = iwn_scan_curchan; ic->ic_scan_mindwell = iwn_scan_mindwell; - /* override state transition machine */ - sc->sc_newstate = ic->ic_newstate; - ic->ic_newstate = iwn_newstate; - ieee80211_media_init(ic, iwn_media_change, ieee80211_media_status); - - ieee80211_amrr_init(&sc->amrr, ic, - IEEE80211_AMRR_MIN_SUCCESS_THRESHOLD, - IEEE80211_AMRR_MAX_SUCCESS_THRESHOLD); - iwn_bpfattach(sc); iwn_sysctlattach(sc); @@ -465,8 +466,7 @@ if (ifp != NULL) { iwn_stop(sc); - callout_drain(&sc->calib_to); - callout_drain(&sc->watchdog_to); + callout_drain(&sc->sc_timer_to); bpfdetach(ifp); ieee80211_ifdetach(ic); } @@ -492,6 +492,49 @@ return 0; } +static struct ieee80211vap * +iwn_vap_create(struct ieee80211com *ic, + const char name[IFNAMSIZ], int unit, int opmode, int flags, + const uint8_t bssid[IEEE80211_ADDR_LEN], + const uint8_t mac[IEEE80211_ADDR_LEN]) +{ + struct iwn_vap *ivp; + struct ieee80211vap *vap; + + if (!TAILQ_EMPTY(&ic->ic_vaps)) /* only one at a time */ + return NULL; + ivp = (struct iwn_vap *) malloc(sizeof(struct iwn_vap), + M_80211_VAP, M_NOWAIT | M_ZERO); + if (ivp == NULL) + return NULL; + vap = &ivp->iv_vap; + ieee80211_vap_setup(ic, vap, name, unit, opmode, flags, bssid, mac); + vap->iv_bmissthreshold = 10; /* override default */ + /* override with driver methods */ + ivp->iv_newstate = vap->iv_newstate; + vap->iv_newstate = iwn_newstate; + + callout_init(&ivp->iv_amrr_to, CALLOUT_MPSAFE); + ieee80211_amrr_init(&ivp->iv_amrr, vap, + IEEE80211_AMRR_MIN_SUCCESS_THRESHOLD, + IEEE80211_AMRR_MAX_SUCCESS_THRESHOLD); + + /* complete setup */ + ieee80211_vap_attach(vap, ieee80211_media_change, ieee80211_media_status); + ic->ic_opmode = opmode; + return vap; +} + +static void +iwn_vap_delete(struct ieee80211vap *vap) +{ + struct iwn_vap *ivp = IWN_VAP(vap); + + callout_drain(&ivp->iv_amrr_to); + ieee80211_vap_detach(vap); + free(ivp, M_80211_VAP); +} + static int iwn_shutdown(device_t dev) { @@ -904,10 +947,10 @@ void iwn_newassoc(struct ieee80211_node *ni, int isnew) { - struct iwn_softc *sc = ni->ni_ic->ic_ifp->if_softc; + struct ieee80211vap *vap = ni->ni_vap; int i; - ieee80211_amrr_node_init(&sc->amrr, &((struct iwn_node *)ni)->amn); + ieee80211_amrr_node_init(&IWN_VAP(vap)->iv_amrr, &IWN_NODE(ni)->amn); /* set rate to some reasonable initial value */ for (i = ni->ni_rates.rs_nrates - 1; @@ -919,43 +962,55 @@ int iwn_media_change(struct ifnet *ifp) { - int error; - - error = ieee80211_media_change(ifp); - if (error == ENETRESET) { - if ((ifp->if_flags & IFF_UP) && - (ifp->if_drv_flags & IFF_DRV_RUNNING)) - iwn_init(ifp->if_softc); - error = 0; - } - return error; + int error = ieee80211_media_change(ifp); + /* NB: only the fixed rate can change and that doesn't need a reset */ + return (error == ENETRESET ? 0 : error); } int -iwn_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg) +iwn_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) { - struct ifnet *ifp = ic->ic_ifp; - struct iwn_softc *sc = ifp->if_softc; + struct iwn_vap *ivp = IWN_VAP(vap); + struct ieee80211com *ic = vap->iv_ic; + struct iwn_softc *sc = ic->ic_ifp->if_softc; DPRINTF(sc, IWN_DEBUG_STATE, "%s: %s -> %s\n", __func__, - ieee80211_state_name[ic->ic_state], + ieee80211_state_name[vap->iv_state], ieee80211_state_name[nstate]); + callout_stop(&sc->sc_timer_to); + callout_stop(&ivp->iv_amrr_to); + /* * Some state transitions require issuing a configure request * to the adapter. This must be done in a blocking context * so we toss control to the task q thread where the state * change will be finished after the command completes. */ - if (nstate == IEEE80211_S_AUTH && ic->ic_state != IEEE80211_S_AUTH) { + if (nstate == IEEE80211_S_AUTH && vap->iv_state != IEEE80211_S_AUTH) { /* !AUTH -> AUTH requires adapter config */ return iwn_queue_cmd(sc, IWN_AUTH, arg, IWN_QUEUE_NORMAL); } - if (nstate == IEEE80211_S_RUN && ic->ic_state != IEEE80211_S_RUN) { - /* !RUN -> RUN requires setting the association id */ + if (nstate == IEEE80211_S_RUN && vap->iv_state != IEEE80211_S_RUN) { + /* + * !RUN -> RUN requires setting the association id + * which is done with a firmware cmd. We also defer + * starting the timers until that work is done. + */ return iwn_queue_cmd(sc, IWN_RUN, arg, IWN_QUEUE_NORMAL); } - return sc->sc_newstate(ic, nstate, arg); + if (nstate == IEEE80211_S_RUN) { + const struct ieee80211_txparam *tp; + /* + * RUN -> RUN transition; just restart the timers. + */ + iwn_calib_reset(sc); + tp = &vap->iv_txparms[ + ieee80211_chan2mode(vap->iv_bss->ni_chan)]; + if (tp->ucastrate != IEEE80211_FIXED_RATE_NONE) + iwn_amrr_reset(vap); + } + return ivp->iv_newstate(vap, nstate, arg); } /* @@ -1269,44 +1324,56 @@ } } -void -iwn_calib_timeout(void *arg) +static void +iwn_timer_timeout(void *arg) { struct iwn_softc *sc = arg; - struct ieee80211com *ic = &sc->sc_ic; - int s; - if (ic->ic_state != IEEE80211_S_RUN) - return; + IWN_LOCK_ASSERT(sc); - /* automatic rate control triggered every 500ms */ - if (ic->ic_fixed_rate == IEEE80211_FIXED_RATE_NONE) { - /* XXX */ - s = splnet(); - if (ic->ic_opmode == IEEE80211_M_STA) - iwn_iter_func(sc, ic->ic_bss); - else - ieee80211_iterate_nodes(&ic->ic_sta, iwn_iter_func, sc); - splx(s); - } - - /* automatic calibration every 60s */ - if (++sc->calib_cnt >= 120) { + if (sc->calib_cnt && --sc->calib_cnt == 0) { DPRINTF(sc, IWN_DEBUG_CALIBRATE, "%s\n", - "sending request for statistics"); + "send statistics request"); (void) iwn_cmd(sc, IWN_CMD_GET_STATISTICS, NULL, 0, 1); - sc->calib_cnt = 0; + sc->calib_cnt = 60; /* do calibration every 60s */ } - callout_reset(&sc->calib_to, hz / 2, iwn_calib_timeout, sc); + iwn_watchdog(sc); /* NB: piggyback tx watchdog */ + callout_reset(&sc->sc_timer_to, hz, iwn_timer_timeout, sc); +} + +static void +iwn_calib_reset(struct iwn_softc *sc) +{ + callout_reset(&sc->sc_timer_to, hz, iwn_timer_timeout, sc); + sc->calib_cnt = 60; /* do calibration every 60s */ +} + +static void +iwn_amrr_reset(struct ieee80211vap *vap) +{ + /* rate control updated every 500ms */ + callout_reset(&IWN_VAP(vap)->iv_amrr_to, hz / 2, iwn_amrr_timeout, vap); +} + +static void +iwn_amrr_iter_func(void *arg, struct ieee80211_node *ni) +{ + struct ieee80211vap *vap = arg; + + ieee80211_amrr_choose(&IWN_VAP(vap)->iv_amrr, ni, &IWN_NODE(ni)->amn); } -void -iwn_iter_func(void *arg, struct ieee80211_node *ni) +static void +iwn_amrr_timeout(void *arg) { - struct iwn_softc *sc = arg; - struct iwn_node *wn = IWN_NODE(ni); + struct ieee80211vap *vap = arg; - ieee80211_amrr_choose(&sc->amrr, ni, &wn->amn); + if (vap->iv_opmode != IEEE80211_M_STA) { + struct ieee80211com *ic = vap->iv_ic; + ieee80211_iterate_nodes(&ic->ic_sta, iwn_amrr_iter_func, vap); + } else + iwn_amrr_iter_func(vap, vap->iv_bss); + iwn_amrr_reset(vap); } void @@ -1440,10 +1507,15 @@ ring->desc[ring->cur] = htole32(paddr >> 8); rssi = iwn_get_rssi(sc, stat); - nf = (ic->ic_state == IEEE80211_S_RUN && + + /* grab a reference to the source node */ + wh = mtod(m, struct ieee80211_frame *); + ni = ieee80211_find_rxnode(ic, (struct ieee80211_frame_min *)wh); + + nf = (ni != NULL && ni->ni_vap->iv_state == IEEE80211_S_RUN && (ic->ic_flags & IEEE80211_F_SCAN) == 0) ? sc->noise : -95; - if (bpf_peers_present(sc->sc_drvbpf)) { + if (bpf_peers_present(ifp->if_bpf)) { struct iwn_rx_radiotap_header *tap = &sc->sc_rxtap; tap->wr_flags = 0; @@ -1455,19 +1527,18 @@ if (stat->flags & htole16(IWN_CONFIG_SHPREAMBLE)) tap->wr_flags |= IEEE80211_RADIOTAP_F_SHORTPRE; - bpf_mtap2(sc->sc_drvbpf, tap, sc->sc_rxtap_len, m); + bpf_mtap2(ifp->if_bpf, tap, sc->sc_rxtap_len, m); } - /* grab a reference to the source node */ - wh = mtod(m, struct ieee80211_frame *); - ni = ieee80211_find_rxnode(ic, (struct ieee80211_frame_min *)wh); + IWN_UNLOCK(sc); - IWN_UNLOCK(sc); /* send the frame to the 802.11 layer */ - ieee80211_input(ic, m, ni, rssi - nf, nf, 0); + if (ni != NULL) { + (void) ieee80211_input(ni, m, rssi - nf, nf, 0); + ieee80211_free_node(ni); + } else + (void) ieee80211_input_all(ic, m, rssi - nf, nf, 0); - /* node is no longer needed */ - ieee80211_free_node(ni); IWN_LOCK(sc); } @@ -1475,16 +1546,17 @@ iwn_rx_statistics(struct iwn_softc *sc, struct iwn_rx_desc *desc) { struct ieee80211com *ic = &sc->sc_ic; + struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); struct iwn_calib_state *calib = &sc->calib; struct iwn_stats *stats = (struct iwn_stats *)(desc + 1); /* beacon stats are meaningful only when associated and not scanning */ - if (ic->ic_state != IEEE80211_S_RUN || + if (vap->iv_state != IEEE80211_S_RUN || (ic->ic_flags & IEEE80211_F_SCAN)) return; DPRINTF(sc, IWN_DEBUG_CALIBRATE, "%s: cmd %d\n", __func__, desc->type); - sc->calib_cnt = 0; /* reset timeout */ + iwn_calib_reset(sc); /* test if temperature has changed */ if (stats->general.temp != sc->rawtemp) { @@ -1552,8 +1624,7 @@ DPRINTF(sc, IWN_DEBUG_ANY, "%s: status 0x%x\n", __func__, le32toh(stat->status)); ifp->if_oerrors++; - } else - ifp->if_opackets++; + } bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_POSTWRITE); bus_dmamap_unload(ring->data_dmat, data->map); @@ -1568,7 +1639,6 @@ ring->queued--; sc->sc_tx_timer = 0; - callout_stop(&sc->watchdog_to); ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; iwn_start(ifp); } @@ -1598,6 +1668,7 @@ iwn_notif_intr(struct iwn_softc *sc) { struct ieee80211com *ic = &sc->sc_ic; + struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); uint16_t hw; hw = le16toh(sc->shared->closed_count) & 0xfff; @@ -1649,9 +1720,9 @@ * If more than 5 consecutive beacons are missed, * reinitialize the sensitivity state machine. */ - if (ic->ic_state == IEEE80211_S_RUN && misses > 5) + if (vap->iv_state == IEEE80211_S_RUN && misses > 5) (void) iwn_init_sensitivity(sc); - if (misses >= ic->ic_bmissthreshold) + if (misses >= vap->iv_bmissthreshold) ieee80211_beacon_miss(ic); break; } @@ -1807,7 +1878,10 @@ iwn_tx_data(struct iwn_softc *sc, struct mbuf *m0, struct ieee80211_node *ni, struct iwn_tx_ring *ring) { - struct ieee80211com *ic = &sc->sc_ic; + struct ieee80211vap *vap = ni->ni_vap; + struct ieee80211com *ic = ni->ni_ic; + struct ifnet *ifp = sc->sc_ifp; + const struct ieee80211_txparam *tp; struct iwn_tx_desc *desc; struct iwn_tx_data *data; struct iwn_tx_cmd *cmd; @@ -1823,24 +1897,28 @@ int rate, error, pad, nsegs, i, ismcast, id; bus_dma_segment_t segs[IWN_MAX_SCATTER]; + IWN_LOCK_ASSERT(sc); + wh = mtod(m0, struct ieee80211_frame *); type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK; ismcast = IEEE80211_IS_MULTICAST(wh->i_addr1); hdrlen = ieee80211_anyhdrsize(wh); /* pick a tx rate */ + /* XXX ni_chan */ + tp = &vap->iv_txparms[ieee80211_chan2mode(ic->ic_curchan)]; if (type == IEEE80211_FC0_TYPE_MGT) - rate = ni->ni_rates.rs_rates[0]; + rate = tp->mgmtrate; else if (ismcast) - rate = ic->ic_mcast_rate; - else if (ic->ic_fixed_rate != IEEE80211_FIXED_RATE_NONE) - rate = ic->ic_fixed_rate; + rate = tp->mcastrate; + else if (tp->ucastrate != IEEE80211_FIXED_RATE_NONE) + rate = tp->ucastrate; else rate = ni->ni_rates.rs_rates[ni->ni_txrate]; rate &= IEEE80211_RATE_VAL; if (wh->i_fc[1] & IEEE80211_FC1_WEP) { - k = ieee80211_crypto_encap(ic, ni, m0); + k = ieee80211_crypto_encap(ni, m0); if (k == NULL) { m_freem(m0); return ENOBUFS; @@ -1850,7 +1928,7 @@ } else k = NULL; - if (bpf_peers_present(sc->sc_drvbpf)) { + if (bpf_peers_present(ifp->if_bpf)) { struct iwn_tx_radiotap_header *tap = &sc->sc_txtap; tap->wt_flags = 0; @@ -1858,7 +1936,7 @@ if (k != NULL) tap->wt_flags |= IEEE80211_RADIOTAP_F_WEP; - bpf_mtap2(sc->sc_drvbpf, tap, sc->sc_txtap_len, m0); + bpf_mtap2(ifp->if_bpf, tap, sc->sc_txtap_len, m0); } flags = IWN_TX_AUTO_SEQ; @@ -1874,7 +1952,7 @@ /* check if RTS/CTS or CTS-to-self protection must be used */ if (!ismcast) { /* multicast frames are not sent at OFDM rates in 802.11b/g */ - if (m0->m_pkthdr.len+IEEE80211_CRC_LEN > ic->ic_rtsthreshold) { + if (m0->m_pkthdr.len+IEEE80211_CRC_LEN > vap->iv_rtsthreshold) { flags |= IWN_TX_NEED_RTS | IWN_TX_FULL_TXOP; } else if ((ic->ic_flags & IEEE80211_F_USEPROT) && IWN_RATE_IS_OFDM(rate)) { @@ -1907,7 +1985,6 @@ } else pad = 0; - IWN_LOCK(sc); desc = &ring->desc[ring->cur]; data = &ring->data[ring->cur]; @@ -2006,7 +2083,9 @@ /* kick Tx ring */ ring->cur = (ring->cur + 1) % IWN_TX_RING_COUNT; IWN_WRITE(sc, IWN_TX_WIDX, ring->qid << 8 | ring->cur); - IWN_UNLOCK(sc); + + ifp->if_opackets++; + sc->sc_tx_timer = 5; return 0; } @@ -2015,100 +2094,268 @@ iwn_start(struct ifnet *ifp) { struct iwn_softc *sc = ifp->if_softc; - struct ieee80211com *ic = &sc->sc_ic; struct ieee80211_node *ni; struct iwn_tx_ring *txq; - struct ether_header *eh; struct mbuf *m; int pri; for (;;) { - IF_POLL(&ic->ic_mgtq, m); - if (m != NULL) { - pri = M_WME_GETAC(m); - txq = &sc->txq[pri]; - if (txq->queued >= IWN_TX_RING_COUNT - 8) { - /* XXX not right */ - ifp->if_drv_flags |= IFF_DRV_OACTIVE; - break; + IFQ_DRV_DEQUEUE(&ifp->if_snd, m); + if (m == NULL) + break; + ni = (struct ieee80211_node *)m->m_pkthdr.rcvif; + pri = M_WME_GETAC(m); + txq = &sc->txq[pri]; + m = ieee80211_encap(ni, m); + if (m == NULL) { + ifp->if_oerrors++; + ieee80211_free_node(ni); + continue; + } + IWN_LOCK(sc); + if (txq->queued >= IWN_TX_RING_COUNT - 8) { + /* XXX not right */ + /* ring is nearly full, stop flow */ + ifp->if_drv_flags |= IFF_DRV_OACTIVE; + } + if (iwn_tx_data(sc, m, ni, txq) != 0) { + ifp->if_oerrors++; + ieee80211_free_node(ni); + IWN_UNLOCK(sc); + break; + } + IWN_UNLOCK(sc); + } +} + +static int +iwn_tx_handoff(struct iwn_softc *sc, + struct iwn_tx_ring *ring, + struct iwn_tx_cmd *cmd, + struct iwn_cmd_data *tx, + struct ieee80211_node *ni, + struct mbuf *m0, u_int hdrlen, int pad) +{ + struct ifnet *ifp = sc->sc_ifp; + struct iwn_tx_desc *desc; + struct iwn_tx_data *data; + bus_addr_t paddr; + struct mbuf *mnew; + int error, nsegs, i; + bus_dma_segment_t segs[IWN_MAX_SCATTER]; + + /* copy and trim IEEE802.11 header */ + memcpy((uint8_t *)(tx + 1), mtod(m0, uint8_t *), hdrlen); + m_adj(m0, hdrlen); + + desc = &ring->desc[ring->cur]; + data = &ring->data[ring->cur]; + + error = bus_dmamap_load_mbuf_sg(ring->data_dmat, data->map, m0, segs, + &nsegs, BUS_DMA_NOWAIT); + if (error != 0) { + if (error == EFBIG) { + /* too many fragments, linearize */ + mnew = m_collapse(m0, M_DONTWAIT, IWN_MAX_SCATTER); + if (mnew == NULL) { + IWN_UNLOCK(sc); + device_printf(sc->sc_dev, + "%s: could not defrag mbuf\n", __func__); + m_freem(m0); + return ENOBUFS; } - IF_DEQUEUE(&ic->ic_mgtq, m); + m0 = mnew; + error = bus_dmamap_load_mbuf_sg(ring->data_dmat, + data->map, m0, segs, &nsegs, BUS_DMA_NOWAIT); + } + if (error != 0) { + IWN_UNLOCK(sc); + device_printf(sc->sc_dev, + "%s: bus_dmamap_load_mbuf_sg failed, error %d\n", + __func__, error); + m_freem(m0); + return error; + } + } + + data->m = m0; + data->ni = ni; + + DPRINTF(sc, IWN_DEBUG_XMIT, "%s: qid %d idx %d len %d nsegs %d\n", + __func__, ring->qid, ring->cur, m0->m_pkthdr.len, nsegs); + + paddr = ring->cmd_dma.paddr + ring->cur * sizeof (struct iwn_tx_cmd); + tx->loaddr = htole32(paddr + 4 + + offsetof(struct iwn_cmd_data, ntries)); + tx->hiaddr = 0; /* limit to 32-bit physical addresses */ + + /* first scatter/gather segment is used by the tx data command */ + IWN_SET_DESC_NSEGS(desc, 1 + nsegs); + IWN_SET_DESC_SEG(desc, 0, paddr, 4 + sizeof (*tx) + hdrlen + pad); + for (i = 1; i <= nsegs; i++) { + IWN_SET_DESC_SEG(desc, i, segs[i - 1].ds_addr, + segs[i - 1].ds_len); + } + sc->shared->len[ring->qid][ring->cur] = + htole16(hdrlen + m0->m_pkthdr.len + 8); + + if (ring->cur < IWN_TX_WINDOW) + sc->shared->len[ring->qid][ring->cur + IWN_TX_RING_COUNT] = + htole16(hdrlen + m0->m_pkthdr.len + 8); + + ring->queued++; + + /* kick Tx ring */ + ring->cur = (ring->cur + 1) % IWN_TX_RING_COUNT; + IWN_WRITE(sc, IWN_TX_WIDX, ring->qid << 8 | ring->cur); + + ifp->if_opackets++; + sc->sc_tx_timer = 5; + + return 0; +} - ni = (struct ieee80211_node *)m->m_pkthdr.rcvif; - m->m_pkthdr.rcvif = NULL; +static int +iwn_tx_data_raw(struct iwn_softc *sc, struct mbuf *m0, + struct ieee80211_node *ni, struct iwn_tx_ring *ring, + const struct ieee80211_bpf_params *params) +{ + struct ifnet *ifp = sc->sc_ifp; + struct iwn_tx_cmd *cmd; + struct iwn_cmd_data *tx; + struct ieee80211_frame *wh; + uint32_t flags; + uint8_t type, subtype; + u_int hdrlen; + int rate, pad; - if (iwn_tx_data(sc, m, ni, txq) != 0) - break; - } else { - if (ic->ic_state != IEEE80211_S_RUN) - break; + IWN_LOCK_ASSERT(sc); - IFQ_DRV_DEQUEUE(&ifp->if_snd, m); - if (m == NULL) - break; - /* - * Cancel any background scan. - */ - if (ic->ic_flags & IEEE80211_F_SCAN) - ieee80211_cancel_scan(ic); + wh = mtod(m0, struct ieee80211_frame *); + type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK; + subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK; + hdrlen = ieee80211_anyhdrsize(wh); - if (m->m_len < sizeof (*eh) && - (m = m_pullup(m, sizeof (*eh))) != NULL) { - ifp->if_oerrors++; - continue; - } + flags = IWN_TX_AUTO_SEQ; + if ((params->ibp_flags & IEEE80211_BPF_NOACK) == 0) + flags |= IWN_TX_NEED_ACK; + if (params->ibp_flags & IEEE80211_BPF_RTS) + flags |= IWN_TX_NEED_RTS | IWN_TX_FULL_TXOP; + if (params->ibp_flags & IEEE80211_BPF_CTS) + flags |= IWN_TX_NEED_CTS | IWN_TX_FULL_TXOP; + if (type == IEEE80211_FC0_TYPE_MGT && + subtype == IEEE80211_FC0_SUBTYPE_PROBE_RESP) { + /* tell h/w to set timestamp in probe responses */ + flags |= IWN_TX_INSERT_TSTAMP; + } + if (hdrlen & 3) { + /* first segment's length must be a multiple of 4 */ + flags |= IWN_TX_NEED_PADDING; + pad = 4 - (hdrlen & 3); + } else + pad = 0; - eh = mtod(m, struct ether_header *); - ni = ieee80211_find_txnode(ic, eh->ether_dhost); - if (ni == NULL) { - /* NB: ieee80211_find_txnode does stat+msg */ - m_freem(m); - goto bad; - } - if (ieee80211_classify(ic, m, ni)) { - m_freem(m); - goto reclaim; - } - pri = M_WME_GETAC(m); - txq = &sc->txq[pri]; - if (txq->queued >= IWN_TX_RING_COUNT - 8) { - /* XXX not right */ - /* there is no place left in this ring */ - ifp->if_drv_flags |= IFF_DRV_OACTIVE; - m_freem(m); - goto reclaim; - } - BPF_MTAP(ifp, m); + /* pick a tx rate */ + rate = params->ibp_rate0; - m = ieee80211_encap(ic, m, ni); - if (m == NULL) - goto bad; + if (bpf_peers_present(ifp->if_bpf)) { + struct iwn_tx_radiotap_header *tap = &sc->sc_txtap; - if (bpf_peers_present(ic->ic_rawbpf)) - bpf_mtap(ic->ic_rawbpf, m); + tap->wt_flags = 0; + tap->wt_rate = rate; - if (iwn_tx_data(sc, m, ni, txq) != 0) { - bad: - ifp->if_oerrors++; - reclaim: - if (ni != NULL) - ieee80211_free_node(ni); - break; - } - } - sc->sc_tx_timer = 5; - ic->ic_lastdata = ticks; + bpf_mtap2(ifp->if_bpf, tap, sc->sc_txtap_len, m0); } + + cmd = &ring->cmd[ring->cur]; + cmd->code = IWN_CMD_TX_DATA; + cmd->flags = 0; + cmd->qid = ring->qid; + cmd->idx = ring->cur; + + tx = (struct iwn_cmd_data *)cmd->data; + /* NB: no need to bzero tx, all fields are reinitialized here */ + tx->id = IWN_ID_BROADCAST; + tx->flags = htole32(flags); + tx->len = htole16(m0->m_pkthdr.len); + tx->rate = iwn_plcp_signal(rate); + tx->rts_ntries = params->ibp_try1; /* XXX? */ + tx->data_ntries = params->ibp_try0; + tx->lifetime = htole32(IWN_LIFETIME_INFINITE); + /* XXX use try count? */ + if (type == IEEE80211_FC0_TYPE_MGT) { + if (subtype == IEEE80211_FC0_SUBTYPE_ASSOC_REQ || + subtype == IEEE80211_FC0_SUBTYPE_REASSOC_REQ) + tx->timeout = htole16(3); + else + tx->timeout = htole16(2); + } else + tx->timeout = htole16(0); + tx->security = 0; + /* XXX alternate between Ant A and Ant B ? */ + tx->rflags = IWN_RFLAG_ANT_B; /* XXX params->ibp_pri >> 2 */ + tx->ridx = IWN_MAX_TX_RETRIES - 1; + if (!IWN_RATE_IS_OFDM(rate)) + tx->rflags |= IWN_RFLAG_CCK; + + return iwn_tx_handoff(sc, ring, cmd, tx, ni, m0, hdrlen, pad); } -void -iwn_watchdog(struct ifnet *ifp) +static int +iwn_raw_xmit(struct ieee80211_node *ni, struct mbuf *m, + const struct ieee80211_bpf_params *params) { + struct ieee80211com *ic = ni->ni_ic; + struct ifnet *ifp = ic->ic_ifp; struct iwn_softc *sc = ifp->if_softc; + struct iwn_tx_ring *txq; + int error; + + if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) { + ieee80211_free_node(ni); + m_freem(m); + return ENETDOWN; + } + IWN_LOCK(sc); + if (params == NULL) + txq = &sc->txq[M_WME_GETAC(m)]; + else + txq = &sc->txq[params->ibp_pri & 3]; + if (txq->queued >= IWN_TX_RING_COUNT - 8) { + /* XXX not right */ + /* ring is nearly full, stop flow */ + ifp->if_drv_flags |= IFF_DRV_OACTIVE; + } + if (params == NULL) { + /* + * Legacy path; interpret frame contents to decide + * precisely how to send the frame. + */ + error = iwn_tx_data(sc, m, ni, txq); + } else { + /* + * Caller supplied explicit parameters to use in + * sending the frame. + */ + error = iwn_tx_data_raw(sc, m, ni, txq, params); + } + if (error != 0) { + /* NB: m is reclaimed on tx failure */ + ieee80211_free_node(ni); + ifp->if_oerrors++; + } + IWN_UNLOCK(sc); + return error; +} + +static void +iwn_watchdog(struct iwn_softc *sc) +{ if (sc->sc_tx_timer > 0 && --sc->sc_tx_timer == 0) { - device_printf(sc->sc_dev, "device timeout\n"); - ifp->if_oerrors++; + struct ifnet *ifp = sc->sc_ifp; + + if_printf(ifp, "device timeout\n"); iwn_queue_cmd(sc, IWN_REINIT, 0, IWN_QUEUE_CLEAR); } } @@ -2118,6 +2365,7 @@ { struct iwn_softc *sc = ifp->if_softc; struct ieee80211com *ic = &sc->sc_ic; + struct ifreq *ifr = (struct ifreq *) data; int error = 0; switch (cmd) { @@ -2143,16 +2391,13 @@ error = 0; break; #endif - + case SIOCGIFMEDIA: + case SIOCSIFMEDIA: + error = ifmedia_ioctl(ifp, ifr, &ic->ic_media, cmd); + break; default: - error = ieee80211_ioctl(ic, cmd, data); - } - if (error == ENETRESET) { - if ((ifp->if_flags & IFF_UP ) && - (ifp->if_drv_flags & IFF_DRV_RUNNING) && - (ic->ic_roaming != IEEE80211_ROAMING_MANUAL)) - iwn_init(sc); - error = 0; + error = ether_ioctl(ifp, cmd, data); + break; } return error; } @@ -2337,61 +2582,7 @@ } } -static __inline int -chancompar(const void *a, const void *b) -{ - const struct ieee80211_channel *ca = a; - const struct ieee80211_channel *cb = b; - - return (ca->ic_freq == cb->ic_freq) ? - (ca->ic_flags & IEEE80211_CHAN_ALL) - - (cb->ic_flags & IEEE80211_CHAN_ALL) : - ca->ic_freq - cb->ic_freq; -} - -/* - * Insertion sort. - */ -#define swap(_a, _b, _size) { \ - uint8_t *s = _b; \ - int i = _size; \ - do { \ - uint8_t tmp = *_a; \ - *_a++ = *s; \ - *s++ = tmp; \ - } while (--i); \ - _a -= _size; \ -} - static void -sort_channels(void *a, size_t n, size_t size) -{ - uint8_t *aa = a; - uint8_t *ai, *t; - - for (ai = aa+size; --n >= 1; ai += size) - for (t = ai; t > aa; t -= size) { - uint8_t *u = t - size; - if (chancompar(u, t) <= 0) - break; - swap(u, t, size); >>> TRUNCATED FOR MAIL (1000 lines) <<<help
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200802252326.m1PNQJWu059431>
