1/*-
2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3 *
4 * Copyright (c) 2010-2011 Monthadar Al Jaberi, TerraNet AB
5 * All rights reserved.
6 *
7 * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting
8 * All rights reserved.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 *    notice, this list of conditions and the following disclaimer,
15 *    without modification.
16 * 2. Redistributions in binary form must reproduce at minimum a disclaimer
17 *    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
18 *    redistribution must be conditioned upon including a substantially
19 *    similar Disclaimer requirement for further binary redistribution.
20 *
21 * NO WARRANTY
22 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24 * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
25 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
26 * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
27 * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
30 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
32 * THE POSSIBILITY OF SUCH DAMAGES.
33 *
34 * $FreeBSD$
35 */
36#include "if_wtapvar.h"
37#include <sys/uio.h>    /* uio struct */
38#include <sys/jail.h>
39#include <net/if_var.h>
40#include <net/vnet.h>
41
42#include <net80211/ieee80211_ratectl.h>
43#include "if_medium.h"
44
45/*
46 * This _requires_ vimage to be useful.
47 */
48#ifndef	VIMAGE
49#error	if_wtap requires VIMAGE.
50#endif	/* VIMAGE */
51
52/* device for IOCTL and read/write for debuggin purposes */
53/* Function prototypes */
54static	d_open_t	wtap_node_open;
55static	d_close_t	wtap_node_close;
56static	d_write_t	wtap_node_write;
57static	d_ioctl_t	wtap_node_ioctl;
58
59static struct cdevsw wtap_cdevsw = {
60	.d_version =	D_VERSION,
61	.d_flags =	0,
62	.d_open = 	wtap_node_open,
63	.d_close = 	wtap_node_close,
64	.d_write = 	wtap_node_write,
65	.d_ioctl =	wtap_node_ioctl,
66	.d_name =	"wtapnode",
67};
68
69static int
70wtap_node_open(struct cdev *dev, int oflags, int devtype, struct thread *p)
71{
72
73	int err = 0;
74	uprintf("Opened device \"echo\" successfully.\n");
75	return(err);
76}
77
78static int
79wtap_node_close(struct cdev *dev, int fflag, int devtype, struct thread *p)
80{
81
82	uprintf("Closing device \"echo.\"\n");
83	return(0);
84}
85
86static int
87wtap_node_write(struct cdev *dev, struct uio *uio, int ioflag)
88{
89	int err = 0;
90	struct mbuf *m;
91	struct ifnet *ifp;
92	struct wtap_softc *sc;
93	uint8_t buf[1024];
94	struct epoch_tracker et;
95	int buf_len;
96
97	uprintf("write device %s \"echo.\"\n", devtoname(dev));
98	buf_len = MIN(uio->uio_iov->iov_len, 1024);
99	err = copyin(uio->uio_iov->iov_base, buf, buf_len);
100
101	if (err != 0) {
102		uprintf("Write failed: bad address!\n");
103		return (err);
104	}
105
106	MGETHDR(m, M_NOWAIT, MT_DATA);
107	m_copyback(m, 0, buf_len, buf);
108
109	CURVNET_SET(TD_TO_VNET(curthread));
110	NET_EPOCH_ENTER(et);
111
112	CK_STAILQ_FOREACH(ifp, &V_ifnet, if_link) {
113		printf("ifp->if_xname = %s\n", ifp->if_xname);
114		if(strcmp(devtoname(dev), ifp->if_xname) == 0){
115			printf("found match, correspoding wtap = %s\n",
116			    ifp->if_xname);
117			sc = (struct wtap_softc *)ifp->if_softc;
118			printf("wtap id = %d\n", sc->id);
119			wtap_inject(sc, m);
120		}
121	}
122
123	NET_EPOCH_EXIT(et);
124	CURVNET_RESTORE();
125
126	return(err);
127}
128
129int
130wtap_node_ioctl(struct cdev *dev, u_long cmd, caddr_t data,
131    int fflag, struct thread *td)
132{
133	int error = 0;
134
135	switch(cmd) {
136	default:
137		DWTAP_PRINTF("Unknown WTAP IOCTL\n");
138		error = EINVAL;
139	}
140	return error;
141}
142
143static int wtap_raw_xmit(struct ieee80211_node *ni, struct mbuf *m,
144	const struct ieee80211_bpf_params *params);
145
146static int
147wtap_medium_enqueue(struct wtap_vap *avp, struct mbuf *m)
148{
149
150	return medium_transmit(avp->av_md, avp->id, m);
151}
152
153/*
154 * Intercept management frames to collect beacon rssi data
155 * and to do ibss merges.
156 */
157static void
158wtap_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m,
159    int subtype, const struct ieee80211_rx_stats *stats, int rssi, int nf)
160{
161	struct ieee80211vap *vap = ni->ni_vap;
162#if 0
163	DWTAP_PRINTF("[%d] %s\n", myath_id(ni), __func__);
164#endif
165	WTAP_VAP(vap)->av_recv_mgmt(ni, m, subtype, stats, rssi, nf);
166}
167
168static int
169wtap_reset_vap(struct ieee80211vap *vap, u_long cmd)
170{
171
172	DWTAP_PRINTF("%s\n", __func__);
173	return 0;
174}
175
176static void
177wtap_beacon_update(struct ieee80211vap *vap, int item)
178{
179	struct ieee80211_beacon_offsets *bo = &vap->iv_bcn_off;
180
181	DWTAP_PRINTF("%s\n", __func__);
182	setbit(bo->bo_flags, item);
183}
184
185/*
186 * Allocate and setup an initial beacon frame.
187 */
188static int
189wtap_beacon_alloc(struct wtap_softc *sc, struct ieee80211_node *ni)
190{
191	struct ieee80211vap *vap = ni->ni_vap;
192	struct wtap_vap *avp = WTAP_VAP(vap);
193
194	DWTAP_PRINTF("[%s] %s\n", ether_sprintf(ni->ni_macaddr), __func__);
195
196	/*
197	 * NB: the beacon data buffer must be 32-bit aligned;
198	 * we assume the mbuf routines will return us something
199	 * with this alignment (perhaps should assert).
200	 */
201	avp->beacon = ieee80211_beacon_alloc(ni);
202	if (avp->beacon == NULL) {
203		printf("%s: cannot get mbuf\n", __func__);
204		return ENOMEM;
205	}
206	callout_init(&avp->av_swba, 0);
207	avp->bf_node = ieee80211_ref_node(ni);
208
209	return 0;
210}
211
212static void
213wtap_beacon_config(struct wtap_softc *sc, struct ieee80211vap *vap)
214{
215
216	DWTAP_PRINTF("%s\n", __func__);
217}
218
219static void
220wtap_beacon_intrp(void *arg)
221{
222	struct wtap_vap *avp = arg;
223	struct ieee80211vap *vap = arg;
224	struct mbuf *m;
225
226	if (vap->iv_state < IEEE80211_S_RUN) {
227	    DWTAP_PRINTF("Skip beacon, not running, state %d", vap->iv_state);
228	    return ;
229	}
230	DWTAP_PRINTF("[%d] beacon intrp\n", avp->id);	//burst mode
231	/*
232	 * Update dynamic beacon contents.  If this returns
233	 * non-zero then we need to remap the memory because
234	 * the beacon frame changed size (probably because
235	 * of the TIM bitmap).
236	 */
237	m = m_dup(avp->beacon, M_NOWAIT);
238	if (ieee80211_beacon_update(avp->bf_node, m, 0)) {
239		printf("%s, need to remap the memory because the beacon frame"
240		    " changed size.\n",__func__);
241	}
242
243	if (ieee80211_radiotap_active_vap(vap))
244	    ieee80211_radiotap_tx(vap, m);
245
246#if 0
247	medium_transmit(avp->av_md, avp->id, m);
248#endif
249	wtap_medium_enqueue(avp, m);
250	callout_schedule(&avp->av_swba, avp->av_bcinterval);
251}
252
253static int
254wtap_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
255{
256	struct ieee80211com *ic = vap->iv_ic;
257	struct wtap_softc *sc = ic->ic_softc;
258	struct wtap_vap *avp = WTAP_VAP(vap);
259	struct ieee80211_node *ni = NULL;
260	int error;
261
262	DWTAP_PRINTF("%s\n", __func__);
263
264	ni = ieee80211_ref_node(vap->iv_bss);
265	/*
266	 * Invoke the parent method to do net80211 work.
267	 */
268	error = avp->av_newstate(vap, nstate, arg);
269	if (error != 0)
270		goto bad;
271
272	if (nstate == IEEE80211_S_RUN) {
273		/* NB: collect bss node again, it may have changed */
274		ieee80211_free_node(ni);
275		ni = ieee80211_ref_node(vap->iv_bss);
276		switch (vap->iv_opmode) {
277		case IEEE80211_M_MBSS:
278			error = wtap_beacon_alloc(sc, ni);
279			if (error != 0)
280				goto bad;
281			wtap_beacon_config(sc, vap);
282			callout_reset(&avp->av_swba, avp->av_bcinterval,
283			    wtap_beacon_intrp, vap);
284			break;
285		default:
286			goto bad;
287		}
288	} else if (nstate == IEEE80211_S_INIT) {
289		callout_stop(&avp->av_swba);
290	}
291	ieee80211_free_node(ni);
292	return 0;
293bad:
294	printf("%s: bad\n", __func__);
295	ieee80211_free_node(ni);
296	return error;
297}
298
299static void
300wtap_bmiss(struct ieee80211vap *vap)
301{
302	struct wtap_vap *avp = (struct wtap_vap *)vap;
303
304	DWTAP_PRINTF("%s\n", __func__);
305	avp->av_bmiss(vap);
306}
307
308static struct ieee80211vap *
309wtap_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ],
310    int unit, enum ieee80211_opmode opmode, int flags,
311    const uint8_t bssid[IEEE80211_ADDR_LEN],
312    const uint8_t mac[IEEE80211_ADDR_LEN])
313{
314	 struct wtap_softc *sc = ic->ic_softc;
315	 struct ieee80211vap *vap;
316	 struct wtap_vap *avp;
317	 int error;
318	struct ieee80211_node *ni;
319
320	 DWTAP_PRINTF("%s\n", __func__);
321
322	avp = malloc(sizeof(struct wtap_vap), M_80211_VAP, M_WAITOK | M_ZERO);
323	avp->id = sc->id;
324	avp->av_md = sc->sc_md;
325	avp->av_bcinterval = msecs_to_ticks(BEACON_INTRERVAL + 100*sc->id);
326	vap = (struct ieee80211vap *) avp;
327	error = ieee80211_vap_setup(ic, vap, name, unit, IEEE80211_M_MBSS,
328	    flags | IEEE80211_CLONE_NOBEACONS, bssid);
329	if (error) {
330		free(avp, M_80211_VAP);
331		return (NULL);
332	}
333
334	/* override various methods */
335	avp->av_recv_mgmt = vap->iv_recv_mgmt;
336	vap->iv_recv_mgmt = wtap_recv_mgmt;
337	vap->iv_reset = wtap_reset_vap;
338	vap->iv_update_beacon = wtap_beacon_update;
339	avp->av_newstate = vap->iv_newstate;
340	vap->iv_newstate = wtap_newstate;
341	avp->av_bmiss = vap->iv_bmiss;
342	vap->iv_bmiss = wtap_bmiss;
343
344	/* complete setup */
345	ieee80211_vap_attach(vap, ieee80211_media_change,
346	    ieee80211_media_status, mac);
347	avp->av_dev = make_dev(&wtap_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600,
348	    "%s", (const char *)sc->name);
349
350	/* TODO this is a hack to force it to choose the rate we want */
351	ni = ieee80211_ref_node(vap->iv_bss);
352	ni->ni_txrate = 130;
353	ieee80211_free_node(ni);
354	return vap;
355}
356
357static void
358wtap_vap_delete(struct ieee80211vap *vap)
359{
360	struct wtap_vap *avp = WTAP_VAP(vap);
361
362	DWTAP_PRINTF("%s\n", __func__);
363	destroy_dev(avp->av_dev);
364	callout_stop(&avp->av_swba);
365	ieee80211_vap_detach(vap);
366	free(avp, M_80211_VAP);
367}
368
369static void
370wtap_parent(struct ieee80211com *ic)
371{
372	struct wtap_softc *sc = ic->ic_softc;
373
374	if (ic->ic_nrunning > 0) {
375		sc->up = 1;
376		ieee80211_start_all(ic);
377	} else
378		sc->up = 0;
379}
380
381static void
382wtap_scan_start(struct ieee80211com *ic)
383{
384
385#if 0
386	DWTAP_PRINTF("%s\n", __func__);
387#endif
388}
389
390static void
391wtap_scan_end(struct ieee80211com *ic)
392{
393
394#if 0
395	DWTAP_PRINTF("%s\n", __func__);
396#endif
397}
398
399static void
400wtap_set_channel(struct ieee80211com *ic)
401{
402
403#if 0
404	DWTAP_PRINTF("%s\n", __func__);
405#endif
406}
407
408static int
409wtap_raw_xmit(struct ieee80211_node *ni, struct mbuf *m,
410	const struct ieee80211_bpf_params *params)
411{
412#if 0
413	DWTAP_PRINTF("%s, %p\n", __func__, m);
414#endif
415	struct ieee80211vap	*vap = ni->ni_vap;
416	struct wtap_vap 	*avp = WTAP_VAP(vap);
417
418	if (ieee80211_radiotap_active_vap(vap)) {
419		ieee80211_radiotap_tx(vap, m);
420	}
421	if (m->m_flags & M_TXCB)
422		ieee80211_process_callback(ni, m, 0);
423	ieee80211_free_node(ni);
424	return wtap_medium_enqueue(avp, m);
425}
426
427void
428wtap_inject(struct wtap_softc *sc, struct mbuf *m)
429{
430      struct wtap_buf *bf = (struct wtap_buf *)malloc(sizeof(struct wtap_buf),
431          M_WTAP_RXBUF, M_NOWAIT | M_ZERO);
432      KASSERT(bf != NULL, ("could not allocated a new wtap_buf\n"));
433      bf->m = m;
434
435      mtx_lock(&sc->sc_mtx);
436      STAILQ_INSERT_TAIL(&sc->sc_rxbuf, bf, bf_list);
437      taskqueue_enqueue(sc->sc_tq, &sc->sc_rxtask);
438      mtx_unlock(&sc->sc_mtx);
439}
440
441static void
442wtap_rx_proc(void *arg, int npending)
443{
444	struct epoch_tracker et;
445	struct wtap_softc *sc = (struct wtap_softc *)arg;
446	struct ieee80211com *ic = &sc->sc_ic;
447	struct mbuf *m;
448	struct ieee80211_node *ni;
449	int type;
450	struct wtap_buf *bf;
451
452#if 0
453	DWTAP_PRINTF("%s\n", __func__);
454#endif
455
456	for(;;) {
457		mtx_lock(&sc->sc_mtx);
458		bf = STAILQ_FIRST(&sc->sc_rxbuf);
459		if (bf == NULL) {
460			mtx_unlock(&sc->sc_mtx);
461			return;
462		}
463		STAILQ_REMOVE_HEAD(&sc->sc_rxbuf, bf_list);
464		mtx_unlock(&sc->sc_mtx);
465		KASSERT(bf != NULL, ("wtap_buf is NULL\n"));
466		m = bf->m;
467		DWTAP_PRINTF("[%d] receiving m=%p\n", sc->id, bf->m);
468		if (m == NULL) {		/* NB: shouldn't happen */
469			ic_printf(ic, "%s: no mbuf!\n", __func__);
470			free(bf, M_WTAP_RXBUF);
471			return;
472		}
473#if 0
474		ieee80211_dump_pkt(ic, mtod(m, caddr_t), 0,0,0);
475#endif
476
477		/*
478		 * Locate the node for sender, track state, and then
479		 * pass the (referenced) node up to the 802.11 layer
480		 * for its use.
481		 */
482		ni = ieee80211_find_rxnode_withkey(ic,
483		    mtod(m, const struct ieee80211_frame_min *),
484		    IEEE80211_KEYIX_NONE);
485		NET_EPOCH_ENTER(et);
486		if (ni != NULL) {
487			/*
488			 * Sending station is known, dispatch directly.
489			 */
490			type = ieee80211_input(ni, m, 1<<7, 10);
491			ieee80211_free_node(ni);
492		} else {
493			type = ieee80211_input_all(ic, m, 1<<7, 10);
494		}
495		NET_EPOCH_EXIT(et);
496
497		/* The mbufs are freed by the Net80211 stack */
498		free(bf, M_WTAP_RXBUF);
499	}
500}
501
502static void
503wtap_newassoc(struct ieee80211_node *ni, int isnew)
504{
505
506	DWTAP_PRINTF("%s\n", __func__);
507}
508
509/*
510 * Callback from the 802.11 layer to update WME parameters.
511 */
512static int
513wtap_wme_update(struct ieee80211com *ic)
514{
515
516	DWTAP_PRINTF("%s\n", __func__);
517	return 0;
518}
519
520static void
521wtap_update_mcast(struct ieee80211com *ic)
522{
523
524	DWTAP_PRINTF("%s\n", __func__);
525}
526
527static void
528wtap_update_promisc(struct ieee80211com *ic)
529{
530
531	DWTAP_PRINTF("%s\n", __func__);
532}
533
534static int
535wtap_transmit(struct ieee80211com *ic, struct mbuf *m)
536{
537	struct ieee80211_node *ni =
538	    (struct ieee80211_node *) m->m_pkthdr.rcvif;
539	struct ieee80211vap *vap = ni->ni_vap;
540	struct wtap_vap *avp = WTAP_VAP(vap);
541
542	if(ni == NULL){
543		printf("m->m_pkthdr.rcvif is NULL we cant radiotap_tx\n");
544	}else{
545		if (ieee80211_radiotap_active_vap(vap))
546			ieee80211_radiotap_tx(vap, m);
547	}
548	if (m->m_flags & M_TXCB)
549		ieee80211_process_callback(ni, m, 0);
550	ieee80211_free_node(ni);
551	return wtap_medium_enqueue(avp, m);
552}
553
554static struct ieee80211_node *
555wtap_node_alloc(struct ieee80211vap *vap, const uint8_t mac[IEEE80211_ADDR_LEN])
556{
557	struct ieee80211_node *ni;
558
559	DWTAP_PRINTF("%s\n", __func__);
560
561	ni = malloc(sizeof(struct ieee80211_node), M_80211_NODE,
562	    M_NOWAIT|M_ZERO);
563	if (ni == NULL)
564		return (NULL);
565
566	ni->ni_txrate = 130;
567	return ni;
568}
569
570static void
571wtap_node_free(struct ieee80211_node *ni)
572{
573	struct ieee80211com *ic = ni->ni_ic;
574	struct wtap_softc *sc = ic->ic_softc;
575
576	DWTAP_PRINTF("%s\n", __func__);
577	sc->sc_node_free(ni);
578}
579
580int32_t
581wtap_attach(struct wtap_softc *sc, const uint8_t *macaddr)
582{
583	struct ieee80211com *ic = &sc->sc_ic;
584
585	DWTAP_PRINTF("%s\n", __func__);
586
587	sc->up = 0;
588	STAILQ_INIT(&sc->sc_rxbuf);
589	sc->sc_tq = taskqueue_create("wtap_taskq", M_NOWAIT | M_ZERO,
590	    taskqueue_thread_enqueue, &sc->sc_tq);
591	taskqueue_start_threads(&sc->sc_tq, 1, PI_SOFT, "%s taskQ", sc->name);
592	NET_TASK_INIT(&sc->sc_rxtask, 0, wtap_rx_proc, sc);
593
594	ic->ic_softc = sc;
595	ic->ic_name = sc->name;
596	ic->ic_phytype = IEEE80211_T_DS;
597	ic->ic_opmode = IEEE80211_M_MBSS;
598	ic->ic_caps = IEEE80211_C_MBSS;
599
600	ic->ic_max_keyix = 128; /* A value read from Atheros ATH_KEYMAX */
601
602	ic->ic_regdomain.regdomain = SKU_ETSI;
603	ic->ic_regdomain.country = CTRY_SWEDEN;
604	ic->ic_regdomain.location = 1; /* Indoors */
605	ic->ic_regdomain.isocc[0] = 'S';
606	ic->ic_regdomain.isocc[1] = 'E';
607
608	ic->ic_nchans = 1;
609	ic->ic_channels[0].ic_flags = IEEE80211_CHAN_B;
610	ic->ic_channels[0].ic_freq = 2412;
611
612	IEEE80211_ADDR_COPY(ic->ic_macaddr, macaddr);
613	ieee80211_ifattach(ic);
614
615	/* override default methods */
616	ic->ic_newassoc = wtap_newassoc;
617	ic->ic_wme.wme_update = wtap_wme_update;
618	ic->ic_vap_create = wtap_vap_create;
619	ic->ic_vap_delete = wtap_vap_delete;
620	ic->ic_raw_xmit = wtap_raw_xmit;
621	ic->ic_update_mcast = wtap_update_mcast;
622	ic->ic_update_promisc = wtap_update_promisc;
623	ic->ic_transmit = wtap_transmit;
624	ic->ic_parent = wtap_parent;
625
626	sc->sc_node_alloc = ic->ic_node_alloc;
627	ic->ic_node_alloc = wtap_node_alloc;
628	sc->sc_node_free = ic->ic_node_free;
629	ic->ic_node_free = wtap_node_free;
630
631	ic->ic_scan_start = wtap_scan_start;
632	ic->ic_scan_end = wtap_scan_end;
633	ic->ic_set_channel = wtap_set_channel;
634
635	ieee80211_radiotap_attach(ic,
636	    &sc->sc_tx_th.wt_ihdr, sizeof(sc->sc_tx_th),
637	    WTAP_TX_RADIOTAP_PRESENT,
638	    &sc->sc_rx_th.wr_ihdr, sizeof(sc->sc_rx_th),
639	    WTAP_RX_RADIOTAP_PRESENT);
640
641	/* Work here, we must find a way to populate the rate table */
642#if 0
643	if(ic->ic_rt == NULL){
644		printf("no table for ic_curchan\n");
645		ic->ic_rt = ieee80211_get_ratetable(&ic->ic_channels[0]);
646	}
647	printf("ic->ic_rt =%p\n", ic->ic_rt);
648	printf("rate count %d\n", ic->ic_rt->rateCount);
649
650	uint8_t code = ic->ic_rt->info[0].dot11Rate;
651	uint8_t cix = ic->ic_rt->info[0].ctlRateIndex;
652	uint8_t ctl_rate = ic->ic_rt->info[cix].dot11Rate;
653	printf("code=%d, cix=%d, ctl_rate=%d\n", code, cix, ctl_rate);
654
655	uint8_t rix0 = ic->ic_rt->rateCodeToIndex[130];
656	uint8_t rix1 = ic->ic_rt->rateCodeToIndex[132];
657	uint8_t rix2 = ic->ic_rt->rateCodeToIndex[139];
658	uint8_t rix3 = ic->ic_rt->rateCodeToIndex[150];
659	printf("rix0 %u,rix1 %u,rix2 %u,rix3 %u\n", rix0,rix1,rix2,rix3);
660	printf("lpAckDuration=%u\n", ic->ic_rt->info[0].lpAckDuration);
661	printf("rate=%d\n", ic->ic_rt->info[0].rateKbps);
662#endif
663	return 0;
664}
665
666int32_t
667wtap_detach(struct wtap_softc *sc)
668{
669	struct ieee80211com *ic = &sc->sc_ic;
670
671	DWTAP_PRINTF("%s\n", __func__);
672	ieee80211_ageq_drain(&ic->ic_stageq);
673	ieee80211_ifdetach(ic);
674	return 0;
675}
676
677void
678wtap_resume(struct wtap_softc *sc)
679{
680
681	DWTAP_PRINTF("%s\n", __func__);
682}
683
684void
685wtap_suspend(struct wtap_softc *sc)
686{
687
688	DWTAP_PRINTF("%s\n", __func__);
689}
690
691void
692wtap_shutdown(struct wtap_softc *sc)
693{
694
695	DWTAP_PRINTF("%s\n", __func__);
696}
697
698void
699wtap_intr(struct wtap_softc *sc)
700{
701
702	DWTAP_PRINTF("%s\n", __func__);
703}
704