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