Subject: patch to send raw IEEE802.11 frames
To: None <tech-kern@netbsd.org>
From: Karl Janmar <karl@utopiafoundation.org>
List: tech-kern
Date: 10/15/2005 18:56:26
This is a multi-part message in MIME format.
--------------020904010001080108090803
Content-Type: text/plain; charset=ISO-8859-1
Content-Transfer-Encoding: 7bit

Hi,

A couple of weeks ago I did some work to allow bpf(4) send raw IEEE802.11 frames
with the ath(4) driver. This patch could easy be extended to support more
wireless cards, the only change that is needed by drivers is to poll the queue
ic_rawq and send them before managment frames are polled in the if_start
routine. One thing I left out in this patch is, what happen if the wireless
driver can't support outputing raw frames. What is the most elegant way to let
net80211 know it can't put packets on ic_rawq?

One fundamental change on struct if_net was to add a if_output_ll. With this new
output routine it's possible to select on which link-layer the interface should
output, if the interface support multiple link-layer types. I kept the original
if_output, this send packets on the default link-layer type as it's used to do.
A if_output with this patch would be same as issue a:
(*ifp->if_output_ll)(...,...,...,..., ifp->if_dlt);

I have access to a prism card, so I think I could add support for wi(4) to send
raw frames there as well.

What do you think of this patch, anybody?

Regards,
Karl Janmar

--------------020904010001080108090803
Content-Type: text/plain;
 name="netbsd3_with_send_raw_ieee80211.diff"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
 filename="netbsd3_with_send_raw_ieee80211.diff"

Index: sys/dev/ic/ath.c
===================================================================
RCS file: /cvsroot/src/sys/dev/ic/ath.c,v
retrieving revision 1.59
diff -u -r1.59 ath.c
--- sys/dev/ic/ath.c	13 Sep 2005 05:50:29 -0000	1.59
+++ sys/dev/ic/ath.c	15 Sep 2005 22:26:00 -0000
@@ -166,6 +166,8 @@
 static void	ath_tx_cleanup(struct ath_softc *);
 static int	ath_tx_start(struct ath_softc *, struct ieee80211_node *,
 			     struct ath_buf *, struct mbuf *);
+static int	ath_tx_start_raw(struct ath_softc *,
+			struct ath_buf *, struct mbuf *);
 static void	ath_tx_proc_q0(void *, int);
 static void	ath_tx_proc_q0123(void *, int);
 static void	ath_tx_proc(void *, int);
@@ -1188,6 +1190,25 @@
 			break;
 		}
 		/*
+		 * Poll the raw-frame queue; they have the highest
+		 * priority.
+		 */
+		IF_DEQUEUE(&ic->ic_rawq, m);
+		if (m != NULL) {
+			if (ath_tx_start_raw(sc, bf, m)) {
+				ifp->if_oerrors++;
+				ATH_TXBUF_LOCK(sc);
+				STAILQ_INSERT_TAIL(&sc->sc_txbuf, bf, bf_list);
+				ATH_TXBUF_UNLOCK(sc);
+				/* poll queues again */
+				continue;
+			}
+			sc->sc_tx_timer = 5;
+			ifp->if_timer = 1;			
+			/* poll queues again */
+			continue;
+		}
+		/*
 		 * Poll the management queue for frames; they
 		 * have priority over normal data frames.
 		 */
@@ -3783,6 +3804,293 @@
 #undef CTS_DURATION
 }
 
+static int
+ath_tx_start_raw(struct ath_softc *sc, struct ath_buf *bf,
+    struct mbuf *m0)
+{
+#define	CTS_DURATION \
+	ath_hal_computetxtime(ah, rt, IEEE80211_ACK_LEN, cix, AH_TRUE)
+#define	updateCTSForBursting(_ah, _ds, _txq) \
+	ath_hal_updateCTSForBursting(_ah, _ds, \
+	    _txq->axq_linkbuf != NULL ? _txq->axq_linkbuf->bf_desc : NULL, \
+	    _txq->axq_lastdsWithCTS, _txq->axq_gatingds, \
+	    txopLimit, CTS_DURATION)
+	struct ieee80211com *ic = &sc->sc_ic;
+	struct ath_hal *ah = sc->sc_ah;
+	struct ifnet *ifp = &sc->sc_if;
+	const struct chanAccParams *cap = &ic->ic_wme.wme_chanParams;
+	int i, error, iswep, keyix, hdrlen, pktlen, try0;
+	u_int8_t txrate, ctsrate;
+	u_int8_t cix = 0xff;		/* NB: silence compiler */
+	struct ath_desc *ds, *ds0;
+	struct ath_txq *txq;
+	struct ieee80211_frame *wh;
+	u_int subtype, flags, ctsduration;
+	HAL_PKT_TYPE atype;
+	const HAL_RATE_TABLE *rt;
+	HAL_BOOL shortPreamble;
+	struct mbuf *m;
+	u_int pri;
+
+	DPRINTF(sc, ATH_DEBUG_XMIT, "%s: m0 = %p\n", __func__, m0);
+
+	wh = mtod(m0, struct ieee80211_frame *);
+	iswep = wh->i_fc[1] & IEEE80211_FC1_WEP;
+	hdrlen = ieee80211_anyhdrsize(wh);
+	/*
+	 * Packet length must not include any
+	 * pad bytes; deduct them here.
+	 */
+	pktlen = m0->m_pkthdr.len - (hdrlen & 3);
+	if (pktlen < 0) {
+		sc->sc_stats.ast_tx_encap++;
+		m_freem(m0);
+		return EINVAL;
+	}
+	/*
+	 * Load the DMA map so any coalescing is done.  This
+	 * also calculates the number of descriptors we need.
+	 */
+	error = bus_dmamap_load_mbuf(sc->sc_dmat, bf->bf_dmamap, m0,
+				     BUS_DMA_NOWAIT);
+	if (error == EFBIG) {
+		/* XXX packet requires too many descriptors */
+		bf->bf_nseg = ATH_TXDESC+1;
+	} else if (error != 0) {
+		sc->sc_stats.ast_tx_busdma++;
+		m_freem(m0);
+		return error;
+	}
+	/*
+	 * Discard null packets and check for packets that
+	 * require too many TX descriptors.  We try to convert
+	 * the latter to a cluster.
+	 */
+	if (error == EFBIG) {		/* too many desc's, linearize */
+		sc->sc_stats.ast_tx_linear++;
+		m = ath_defrag(m0, M_DONTWAIT, ATH_TXDESC);
+		if (m == NULL) {
+			m_freem(m0);
+			sc->sc_stats.ast_tx_nombuf++;
+			return ENOMEM;
+		}
+		m0 = m;
+		error = bus_dmamap_load_mbuf(sc->sc_dmat, bf->bf_dmamap, m0,
+					     BUS_DMA_NOWAIT);
+		if (error != 0) {
+			sc->sc_stats.ast_tx_busdma++;
+			m_freem(m0);
+			return error;
+		}
+		KASSERT(bf->bf_nseg <= ATH_TXDESC,
+		    ("too many segments after defrag; nseg %u", bf->bf_nseg));
+	} else if (bf->bf_nseg == 0) {		/* null packet, discard */
+		sc->sc_stats.ast_tx_nodata++;
+		m_freem(m0);
+		return EIO;
+	}
+	DPRINTF(sc, ATH_DEBUG_XMIT, "%s: m %p len %u\n", __func__, m0, pktlen);
+	bus_dmamap_sync(sc->sc_dmat, bf->bf_dmamap, 0,
+            bf->bf_dmamap->dm_mapsize, BUS_DMASYNC_PREWRITE);
+	bf->bf_m = m0;
+	bf->bf_node = NULL;
+
+	/* setup descriptors */
+	ds = bf->bf_desc;
+	rt = sc->sc_currates;
+	KASSERT(rt != NULL, ("no rate table, mode %u", sc->sc_curmode));
+
+	/*
+	 * NB: the 802.11 layer marks whether or not we should
+	 * use short preamble based on the current mode and
+	 * negotiated parameters.
+	 */
+	shortPreamble = AH_FALSE;
+	flags = HAL_TXDESC_CLRDMASK;		/* XXX needed for crypto errs */
+	/*
+	 * Calculate Atheros packet type from IEEE80211 packet header
+	 * and select h/w transmit queue.
+	 */
+	switch (wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) {
+	case IEEE80211_FC0_TYPE_MGT:
+		subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK;
+		if (subtype == IEEE80211_FC0_SUBTYPE_BEACON)
+			atype = HAL_PKT_TYPE_BEACON;
+		else if (subtype == IEEE80211_FC0_SUBTYPE_PROBE_RESP)
+			atype = HAL_PKT_TYPE_PROBE_RESP;
+		else if (subtype == IEEE80211_FC0_SUBTYPE_ATIM)
+			atype = HAL_PKT_TYPE_ATIM;
+		else
+			atype = HAL_PKT_TYPE_NORMAL;	/* XXX */
+		break;
+	case IEEE80211_FC0_TYPE_CTL:
+		atype = HAL_PKT_TYPE_PSPOLL;	/* stop setting of duration */
+		break;
+	case IEEE80211_FC0_TYPE_DATA:
+		atype = HAL_PKT_TYPE_NORMAL;		/* default */
+		break;
+	default:
+		if_printf(ifp, "bogus frame type 0x%x (%s)\n",
+			wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK, __func__);
+		/* XXX statistic */
+		m_freem(m0);
+		return EIO;
+	}
+
+	/*
+	 * Setup some reasonable values for a raw frame.
+	 */
+	ctsduration = 0;
+	ctsrate = 0;
+	pri = WME_AC_BE;
+	try0 = ATH_TXMAXTRY; /* XXX should we use any retry's on raw frames? */
+	txq = sc->sc_ac2q[pri];
+	txrate = sc->sc_txrate;
+	keyix = HAL_TXKEYIX_INVALID; /* we don't want h/w to do encryption. */
+
+	flags |= HAL_TXDESC_NOACK;	/* no ack on blind sending */
+	sc->sc_stats.ast_tx_noack++;
+
+	if (IFF_DUMPPKTS(sc, ATH_DEBUG_XMIT))
+		ieee80211_dump_pkt(mtod(m0, caddr_t), m0->m_len,
+			sc->sc_hwmap[txrate].ieeerate, -1);
+
+	if (ic->ic_rawbpf)
+		bpf_mtap(ic->ic_rawbpf, m0);
+	if (sc->sc_drvbpf) {
+		sc->sc_tx_th.wt_flags = sc->sc_hwmap[txrate].txflags;
+		if (iswep)
+			sc->sc_tx_th.wt_flags |= IEEE80211_RADIOTAP_F_WEP;
+		sc->sc_tx_th.wt_rate = sc->sc_hwmap[txrate].ieeerate;
+		sc->sc_tx_th.wt_txpower = sc->sc_curtxpow;
+		sc->sc_tx_th.wt_antenna = sc->sc_txantenna;
+
+		bpf_mtap2(sc->sc_drvbpf,
+			&sc->sc_tx_th, sc->sc_tx_th_len, m0);
+	}
+
+	/* 
+	 * Determine if a tx interrupt should be generated for
+	 * this descriptor.  We take a tx interrupt to reap
+	 * descriptors when the h/w hits an EOL condition or
+	 * when the descriptor is specifically marked to generate
+	 * an interrupt.  We periodically mark descriptors in this
+	 * way to insure timely replenishing of the supply needed
+	 * for sending frames.  Defering interrupts reduces system
+	 * load and potentially allows more concurrent work to be
+	 * done but if done to aggressively can cause senders to
+	 * backup.
+	 *
+	 * NB: use >= to deal with sc_txintrperiod changing
+	 *     dynamically through sysctl.
+	 */
+	if (flags & HAL_TXDESC_INTREQ) {
+		txq->axq_intrcnt = 0;
+	} else if (++txq->axq_intrcnt >= sc->sc_txintrperiod) {
+		flags |= HAL_TXDESC_INTREQ;
+		txq->axq_intrcnt = 0;
+	}
+
+	/*
+	 * Formulate first tx descriptor with tx controls.
+	 */
+	/* XXX check return value? */
+	ath_hal_setuptxdesc(ah, ds
+		, pktlen		/* packet length */
+		, hdrlen		/* header length */
+		, atype			/* Atheros packet type */
+		, sc->sc_curtxpow	/* txpower */
+		, txrate, try0		/* series 0 rate/tries */
+		, keyix			/* key cache index */
+		, sc->sc_txantenna	/* antenna mode */
+		, flags			/* flags */
+		, ctsrate		/* rts/cts rate */
+		, ctsduration		/* rts/cts duration */
+	);
+	bf->bf_flags = flags;
+
+	/*
+	 * Fillin the remainder of the descriptor info.
+	 */
+	ds0 = ds;
+	for (i = 0; i < bf->bf_nseg; i++, ds++) {
+		ds->ds_data = bf->bf_segs[i].ds_addr;
+		if (i == bf->bf_nseg - 1)
+			ds->ds_link = 0;
+		else
+			ds->ds_link = bf->bf_daddr + sizeof(*ds) * (i + 1);
+		ath_hal_filltxdesc(ah, ds
+			, bf->bf_segs[i].ds_len	/* segment length */
+			, (i == 0)				/* first segment */
+			, (i == (bf->bf_nseg - 1))	/* last segment */
+			, ds0					/* first descriptor */
+		);
+		DPRINTF(sc, ATH_DEBUG_XMIT,
+			"%s: %d: %08x %08x %08x %08x %08x %08x\n",
+			__func__, i, ds->ds_link, ds->ds_data,
+			ds->ds_ctl0, ds->ds_ctl1, ds->ds_hw[0], ds->ds_hw[1]);
+	}
+	/*
+	 * Insert the frame on the outbound list and
+	 * pass it on to the hardware.
+	 */
+	ATH_TXQ_LOCK(txq);
+	if (flags & (HAL_TXDESC_RTSENA | HAL_TXDESC_CTSENA)) {
+		u_int32_t txopLimit = IEEE80211_TXOP_TO_US(
+			cap->cap_wmeParams[pri].wmep_txopLimit);
+		/*
+		 * When bursting, potentially extend the CTS duration
+		 * of a previously queued frame to cover this frame
+		 * and not exceed the txopLimit.  If that can be done
+		 * then disable RTS/CTS on this frame since it's now
+		 * covered (burst extension).  Otherwise we must terminate
+		 * the burst before this frame goes out so as not to
+		 * violate the WME parameters.  All this is complicated
+		 * as we need to update the state of packets on the
+		 * (live) hardware queue.  The logic is buried in the hal
+		 * because it's highly chip-specific.
+		 */
+		if (txopLimit != 0) {
+			sc->sc_stats.ast_tx_ctsburst++;
+			if (updateCTSForBursting(ah, ds0, txq) == 0) {
+				/*
+				 * This frame was not covered by RTS/CTS from
+				 * the previous frame in the burst; update the
+				 * descriptor pointers so this frame is now
+				 * treated as the last frame for extending a
+				 * burst.
+				 */
+				txq->axq_lastdsWithCTS = ds0;
+				/* set gating Desc to final desc */
+				txq->axq_gatingds =
+					(struct ath_desc *)txq->axq_link;
+			} else
+				sc->sc_stats.ast_tx_ctsext++;
+		}
+	}
+	ATH_TXQ_INSERT_TAIL(txq, bf, bf_list);
+	if (txq->axq_link == NULL) {
+		ath_hal_puttxbuf(ah, txq->axq_qnum, bf->bf_daddr);
+		DPRINTF(sc, ATH_DEBUG_XMIT,
+			"%s: TXDP[%u] = %p (%p) depth %d\n", __func__,
+			txq->axq_qnum, (caddr_t)bf->bf_daddr, bf->bf_desc,
+			txq->axq_depth);
+	} else {
+		*txq->axq_link = bf->bf_daddr;
+		DPRINTF(sc, ATH_DEBUG_XMIT,
+			"%s: link[%u](%p)=%p (%p) depth %d\n", __func__,
+			txq->axq_qnum, txq->axq_link,
+			(caddr_t)bf->bf_daddr, bf->bf_desc, txq->axq_depth);
+	}
+	txq->axq_link = &bf->bf_desc[bf->bf_nseg - 1].ds_link;
+	ath_hal_txstart(ah, txq->axq_qnum);
+	ATH_TXQ_UNLOCK(txq);
+
+	return 0;
+#undef updateCTSForBursting
+#undef CTS_DURATION
+}
+
 /*
  * Process completed xmit descriptors from the specified queue.
  */
Index: sys/net/bpf.c
===================================================================
RCS file: /cvsroot/src/sys/net/bpf.c,v
retrieving revision 1.111
diff -u -r1.111 bpf.c
--- sys/net/bpf.c	5 Sep 2005 18:32:24 -0000	1.111
+++ sys/net/bpf.c	12 Oct 2005 17:53:16 -0000
@@ -73,6 +73,7 @@
 
 #include <net/if_arc.h>
 #include <net/if_ether.h>
+#include <net/if_types.h>
 
 #include <netinet/in.h>
 #include <netinet/if_inarp.h>
@@ -233,6 +234,12 @@
 		hlen = 0;
 		align = 0;
 		break;
+		
+	case DLT_IEEE802_11:
+		sockp->sa_family = AF_UNSPEC;
+		hlen = 0;
+		align = 4;
+		break;
 
 	default:
 		return (EIO);
@@ -581,7 +588,6 @@
 	splx(s);
 }
 
-
 static int
 bpf_write(struct file *fp, off_t *offp, struct uio *uio,
 	  struct ucred *cred, int flags)
@@ -614,8 +620,18 @@
 		dst.ss_family = pseudo_AF_HDRCMPLT;
 
 	s = splsoftnet();
-	error = (*ifp->if_output)(ifp, m, (struct sockaddr *) &dst, NULL);
+	switch (d->bd_bif->bif_dlt) {
+		case DLT_IEEE802_11:
+			error = (*ifp->if_output_ll)(ifp, m, (struct sockaddr *) &dst, NULL, IFT_IEEE80211);
+			break;
+
+		case DLT_EN10MB:
+		default:
+			error = (*ifp->if_output)(ifp, m, (struct sockaddr *) &dst, NULL);
+			break;
+	}
 	splx(s);
+
 	/*
 	 * The driver frees the mbuf.
 	 */
Index: sys/net/if.c
===================================================================
RCS file: /cvsroot/src/sys/net/if.c,v
retrieving revision 1.160
diff -u -r1.160 if.c
--- sys/net/if.c	19 Jul 2005 12:58:24 -0000	1.160
+++ sys/net/if.c	15 Sep 2005 22:26:02 -0000
@@ -207,6 +207,23 @@
 	return (ENXIO);
 }
 
+int
+if_nulloutput_ll(ifp, m, so, rt, ll_type)
+	struct ifnet *ifp;
+	struct mbuf *m;
+	struct sockaddr *so;
+	struct rtentry *rt;
+	u_char ll_type;
+{
+
+	/* default link layer */
+	if (ll_type == ifp->if_type) {
+		return (*ifp->if_output)(ifp, m, so, rt);
+	}
+
+	return (ENXIO);
+}
+
 void
 if_nullinput(ifp, m)
 	struct ifnet *ifp;
@@ -533,6 +550,7 @@
 	s = splnet();
 
 	ifp->if_output	 = if_nulloutput;
+	ifp->if_output_ll = if_nulloutput_ll;
 	ifp->if_input	 = if_nullinput;
 	ifp->if_start	 = if_nullstart;
 	ifp->if_ioctl	 = if_nullioctl;
Index: sys/net/if.h
===================================================================
RCS file: /cvsroot/src/sys/net/if.h,v
retrieving revision 1.111
diff -u -r1.111 if.h
--- sys/net/if.h	27 Jul 2005 06:36:15 -0000	1.111
+++ sys/net/if.h	15 Sep 2005 22:26:02 -0000
@@ -254,9 +254,13 @@
 	 * Procedure handles.  If you add more of these, don't forget the
 	 * corresponding NULL stub in if.c.
 	 */
-	int	(*if_output)		/* output routine (enqueue) */
+	int	(*if_output)		/* output routine (enqueue on default ll) */
 		__P((struct ifnet *, struct mbuf *, struct sockaddr *,
 		     struct rtentry *));
+	int	(*if_output_ll)		/* output routine (enqueue on specific ll) */
+		__P((struct ifnet *, struct mbuf *, struct sockaddr *,
+		     struct rtentry *, u_char ll_type));
+
 	void	(*if_input)		/* input routine (from h/w driver) */
 		__P((struct ifnet *, struct mbuf *));
 	void	(*if_start)		/* initiate output routine */
@@ -829,6 +833,8 @@
  */
 int	if_nulloutput __P((struct ifnet *, struct mbuf *,
 	    struct sockaddr *, struct rtentry *));
+int	if_nulloutput_ll __P((struct ifnet *, struct mbuf *,
+	    struct sockaddr *, struct rtentry *, u_char ll_type));
 void	if_nullinput __P((struct ifnet *, struct mbuf *));
 void	if_nullstart __P((struct ifnet *));
 int	if_nullioctl __P((struct ifnet *, u_long, caddr_t));
Index: sys/net80211/ieee80211.c
===================================================================
RCS file: /cvsroot/src/sys/net80211/ieee80211.c,v
retrieving revision 1.40
diff -u -r1.40 ieee80211.c
--- sys/net80211/ieee80211.c	26 Jul 2005 22:52:48 -0000	1.40
+++ sys/net80211/ieee80211.c	15 Sep 2005 22:26:02 -0000
@@ -153,6 +153,9 @@
 	int i;
 
 	ether_ifattach(ifp, ic->ic_myaddr);
+	/* attach our link-layer output that can IEEE802.11 and EN10B  */
+	ifp->if_output_ll = ieee80211_output_ll; 
+
 #if NBPFILTER > 0
 	bpfattach2(ifp, DLT_IEEE802_11,
 	    sizeof(struct ieee80211_frame_addr4), &ic->ic_rawbpf);
@@ -498,7 +501,7 @@
 	return ic;
 }
 
-static struct ieee80211com *
+struct ieee80211com *
 ieee80211_find_instance(struct ifnet *ifp)
 {
 	int s;
Index: sys/net80211/ieee80211_output.c
===================================================================
RCS file: /cvsroot/src/sys/net80211/ieee80211_output.c,v
retrieving revision 1.37
diff -u -r1.37 ieee80211_output.c
--- sys/net80211/ieee80211_output.c	21 Aug 2005 00:07:57 -0000	1.37
+++ sys/net80211/ieee80211_output.c	15 Sep 2005 22:26:03 -0000
@@ -61,6 +61,7 @@
 #include <net/if_ether.h>
 #include <net/if_llc.h>
 #include <net/if_vlanvar.h>
+#include <net/if_types.h>
 
 #include <net80211/ieee80211_netbsd.h>
 #include <net80211/ieee80211_var.h>
@@ -182,6 +183,30 @@
 	return 0;
 }
 
+int
+ieee80211_output_ll(struct ifnet *ifp, struct mbuf *m,struct sockaddr *dst,
+	struct rtentry *rt0, u_char ll_type)
+{
+	struct ieee80211com *ic;
+
+	ic = ieee80211_find_instance(ifp);
+	if (!ic) {
+		if_printf(ifp, "%s: no 802.11 instance!\n", __func__);
+		return EINVAL;
+	}
+
+	if (ll_type == 0 || ll_type == IFT_ETHER) {
+		return (*ifp->if_output)(ifp,m,dst,rt0);
+	} else if (ll_type == IFT_IEEE80211) {
+		IF_ENQUEUE(&ic->ic_rawq, m);
+		ifp->if_timer = 1;
+		(*ifp->if_start)(ifp);
+		return 0;
+	}
+
+	return ENXIO;
+}
+
 /*
  * Send a null data frame to the specified node.
  */
Index: sys/net80211/ieee80211_proto.h
===================================================================
RCS file: /cvsroot/src/sys/net80211/ieee80211_proto.h,v
retrieving revision 1.12
diff -u -r1.12 ieee80211_proto.h
--- sys/net80211/ieee80211_proto.h	26 Jul 2005 22:52:48 -0000	1.12
+++ sys/net80211/ieee80211_proto.h	15 Sep 2005 22:26:03 -0000
@@ -60,6 +60,8 @@
 struct ieee80211_node;
 int	ieee80211_input(struct ieee80211com *, struct mbuf *,
 		struct ieee80211_node *, int, u_int32_t);
+int ieee80211_output_ll(struct ifnet *, struct mbuf *, struct sockaddr *,
+		struct rtentry *, u_char);
 void	ieee80211_recv_mgmt(struct ieee80211com *, struct mbuf *,
 		struct ieee80211_node *, int, int, u_int32_t);
 int	ieee80211_send_nulldata(struct ieee80211com *, struct ieee80211_node *);
@@ -71,6 +73,7 @@
 		struct ieee80211_node *);
 void	ieee80211_pwrsave(struct ieee80211com *, struct ieee80211_node *, 
 		struct mbuf *);
+struct ieee80211com * ieee80211_find_instance(struct ifnet *ifp);
 
 void	ieee80211_reset_erp(struct ieee80211com *);
 void	ieee80211_set_shortslottime(struct ieee80211com *, int onoff);
Index: sys/net80211/ieee80211_var.h
===================================================================
RCS file: /cvsroot/src/sys/net80211/ieee80211_var.h,v
retrieving revision 1.20
diff -u -r1.20 ieee80211_var.h
--- sys/net80211/ieee80211_var.h	26 Jul 2005 22:52:48 -0000	1.20
+++ sys/net80211/ieee80211_var.h	15 Sep 2005 22:26:03 -0000
@@ -115,6 +115,7 @@
 	u_int8_t		ic_chan_scan[IEEE80211_CHAN_BYTES];
 	struct ieee80211_node_table ic_scan;	/* scan candidates */
 	struct ifqueue		ic_mgtq;
+	struct ifqueue		ic_rawq; /* output queue for raw 802.11 frames */
 	u_int32_t		ic_flags;	/* state flags */
 	u_int32_t		ic_caps;	/* capabilities */
 	u_int16_t		ic_modecaps;	/* set of mode capabilities */

--------------020904010001080108090803--