PERFORCE change 136194 for review
Sam Leffler
sam at FreeBSD.org
Mon Feb 25 23:26:20 UTC 2008
http://perforce.freebsd.org/chv.cgi?CH=136194
Change 136194 by sam at 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) <<<
More information about the p4-projects
mailing list