1132718Skan/*	$OpenBSD: if_urtwn.c,v 1.16 2011/02/10 17:26:40 jakemsr Exp $	*/
2117395Skan
3169689Skan/*-
4117395Skan * Copyright (c) 2010 Damien Bergamini <damien.bergamini@free.fr>
5117395Skan * Copyright (c) 2014 Kevin Lo <kevlo@FreeBSD.org>
6117395Skan * Copyright (c) 2015-2016 Andriy Voskoboinyk <avos@FreeBSD.org>
7117395Skan *
8117395Skan * Permission to use, copy, modify, and distribute this software for any
9117395Skan * purpose with or without fee is hereby granted, provided that the above
10117395Skan * copyright notice and this permission notice appear in all copies.
11117395Skan *
12117395Skan * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13117395Skan * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14117395Skan * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
15117395Skan * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16117395Skan * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17117395Skan * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
18169689Skan * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19169689Skan */
20117395Skan
21117395Skan#include <sys/cdefs.h>
22117395Skan__FBSDID("$FreeBSD$");
23117395Skan
24117395Skan#include <sys/param.h>
25117395Skan#include <sys/sysctl.h>
26117395Skan#include <sys/lock.h>
27117395Skan#include <sys/mutex.h>
28132718Skan#include <sys/mbuf.h>
29132718Skan#include <sys/kernel.h>
30117395Skan#include <sys/socket.h>
31132718Skan#include <sys/systm.h>
32117395Skan#include <sys/malloc.h>
33132718Skan#include <sys/module.h>
34132718Skan#include <sys/bus.h>
35117395Skan#include <sys/endian.h>
36132718Skan#include <sys/linker.h>
37132718Skan#include <sys/kdb.h>
38132718Skan
39132718Skan#include <net/if.h>
40169689Skan#include <net/if_var.h>
41132718Skan#include <net/ethernet.h>
42117395Skan#include <net/if_media.h>
43132718Skan
44132718Skan#include <net80211/ieee80211_var.h>
45132718Skan
46132718Skan#include <dev/usb/usb.h>
47132718Skan#include <dev/usb/usbdi.h>
48132718Skan#include "usbdevs.h"
49132718Skan
50132718Skan#include <dev/rtwn/if_rtwnvar.h>
51132718Skan#include <dev/rtwn/if_rtwn_nop.h>
52132718Skan
53132718Skan#include <dev/rtwn/usb/rtwn_usb_var.h>
54169689Skan
55132718Skan#include <dev/rtwn/usb/rtwn_usb_attach.h>
56117395Skan#include <dev/rtwn/usb/rtwn_usb_ep.h>
57117395Skan#include <dev/rtwn/usb/rtwn_usb_reg.h>
58132718Skan#include <dev/rtwn/usb/rtwn_usb_tx.h>
59169689Skan
60117395Skan#include <dev/rtwn/rtl8192c/r92c_reg.h>
61132718Skan
62117395Skanstatic device_probe_t	rtwn_usb_match;
63132718Skanstatic device_attach_t	rtwn_usb_attach;
64132718Skanstatic device_detach_t	rtwn_usb_detach;
65132718Skanstatic device_suspend_t	rtwn_usb_suspend;
66132718Skanstatic device_resume_t	rtwn_usb_resume;
67132718Skan
68117395Skanstatic int	rtwn_usb_alloc_list(struct rtwn_softc *,
69132718Skan		    struct rtwn_data[], int, int);
70132718Skanstatic int	rtwn_usb_alloc_rx_list(struct rtwn_softc *);
71132718Skanstatic int	rtwn_usb_alloc_tx_list(struct rtwn_softc *);
72117395Skanstatic void	rtwn_usb_free_list(struct rtwn_softc *,
73132718Skan		    struct rtwn_data data[], int);
74117395Skanstatic void	rtwn_usb_free_rx_list(struct rtwn_softc *);
75132718Skanstatic void	rtwn_usb_free_tx_list(struct rtwn_softc *);
76132718Skanstatic void	rtwn_usb_reset_lists(struct rtwn_softc *,
77117395Skan		    struct ieee80211vap *);
78132718Skanstatic void	rtwn_usb_reset_tx_list(struct rtwn_usb_softc *,
79132718Skan		    rtwn_datahead *, struct ieee80211vap *);
80117395Skanstatic void	rtwn_usb_reset_rx_list(struct rtwn_usb_softc *);
81132718Skanstatic void	rtwn_usb_start_xfers(struct rtwn_softc *);
82132718Skanstatic void	rtwn_usb_abort_xfers(struct rtwn_softc *);
83132718Skanstatic int	rtwn_usb_fw_write_block(struct rtwn_softc *,
84117395Skan		    const uint8_t *, uint16_t, int);
85132718Skanstatic void	rtwn_usb_drop_incorrect_tx(struct rtwn_softc *);
86117395Skanstatic void	rtwn_usb_attach_methods(struct rtwn_softc *);
87132718Skanstatic void	rtwn_usb_sysctlattach(struct rtwn_softc *);
88132718Skan
89117395Skan#define RTWN_CONFIG_INDEX	0
90132718Skan
91169689Skanstatic int
92132718Skanrtwn_usb_match(device_t self)
93117395Skan{
94132718Skan	struct usb_attach_arg *uaa = device_get_ivars(self);
95132718Skan
96132718Skan	if (uaa->usb_mode != USB_MODE_HOST)
97117395Skan		return (ENXIO);
98117395Skan	if (uaa->info.bConfigIndex != RTWN_CONFIG_INDEX)
99132718Skan		return (ENXIO);
100132718Skan	if (uaa->info.bIfaceIndex != RTWN_IFACE_INDEX)
101132718Skan		return (ENXIO);
102132718Skan
103117395Skan	return (usbd_lookup_id_by_uaa(rtwn_devs, sizeof(rtwn_devs), uaa));
104132718Skan}
105169689Skan
106132718Skanstatic int
107132718Skanrtwn_usb_alloc_list(struct rtwn_softc *sc, struct rtwn_data data[],
108132718Skan    int ndata, int maxsz)
109132718Skan{
110132718Skan	int i, error;
111117395Skan
112117395Skan	for (i = 0; i < ndata; i++) {
113169689Skan		struct rtwn_data *dp = &data[i];
114117395Skan		dp->m = NULL;
115132718Skan		dp->buf = malloc(maxsz, M_USBDEV, M_NOWAIT);
116132718Skan		if (dp->buf == NULL) {
117117395Skan			device_printf(sc->sc_dev,
118132718Skan			    "could not allocate buffer\n");
119117395Skan			error = ENOMEM;
120132718Skan			goto fail;
121132718Skan		}
122132718Skan		dp->ni = NULL;
123132718Skan	}
124132718Skan
125132718Skan	return (0);
126117395Skanfail:
127132718Skan	rtwn_usb_free_list(sc, data, ndata);
128132718Skan	return (error);
129132718Skan}
130132718Skan
131132718Skanstatic int
132132718Skanrtwn_usb_alloc_rx_list(struct rtwn_softc *sc)
133132718Skan{
134132718Skan	struct rtwn_usb_softc *uc = RTWN_USB_SOFTC(sc);
135132718Skan	int error, i;
136117395Skan
137132718Skan	error = rtwn_usb_alloc_list(sc, uc->uc_rx, RTWN_USB_RX_LIST_COUNT,
138169689Skan	    uc->uc_rx_buf_size * RTWN_USB_RXBUFSZ_UNIT);
139117395Skan	if (error != 0)
140132718Skan		return (error);
141132718Skan
142132718Skan	STAILQ_INIT(&uc->uc_rx_active);
143132718Skan	STAILQ_INIT(&uc->uc_rx_inactive);
144132718Skan
145117395Skan	for (i = 0; i < RTWN_USB_RX_LIST_COUNT; i++)
146169689Skan		STAILQ_INSERT_HEAD(&uc->uc_rx_inactive, &uc->uc_rx[i], next);
147169689Skan
148169689Skan	return (0);
149169689Skan}
150117395Skan
151132718Skanstatic int
152132718Skanrtwn_usb_alloc_tx_list(struct rtwn_softc *sc)
153169689Skan{
154169689Skan	struct rtwn_usb_softc *uc = RTWN_USB_SOFTC(sc);
155132718Skan	int error, i;
156117395Skan
157132718Skan	error = rtwn_usb_alloc_list(sc, uc->uc_tx, RTWN_USB_TX_LIST_COUNT,
158132718Skan	    RTWN_USB_TXBUFSZ);
159169689Skan	if (error != 0)
160169689Skan		return (error);
161132718Skan
162117395Skan	STAILQ_INIT(&uc->uc_tx_active);
163169689Skan	STAILQ_INIT(&uc->uc_tx_inactive);
164169689Skan	STAILQ_INIT(&uc->uc_tx_pending);
165169689Skan
166132718Skan	for (i = 0; i < RTWN_USB_TX_LIST_COUNT; i++)
167117395Skan		STAILQ_INSERT_HEAD(&uc->uc_tx_inactive, &uc->uc_tx[i], next);
168132718Skan
169117395Skan	return (0);
170132718Skan}
171132718Skan
172132718Skanstatic void
173132718Skanrtwn_usb_free_list(struct rtwn_softc *sc, struct rtwn_data data[], int ndata)
174132718Skan{
175132718Skan	int i;
176132718Skan
177132718Skan	for (i = 0; i < ndata; i++) {
178132718Skan		struct rtwn_data *dp = &data[i];
179117395Skan
180132718Skan		if (dp->buf != NULL) {
181117395Skan			free(dp->buf, M_USBDEV);
182132718Skan			dp->buf = NULL;
183132718Skan		}
184132718Skan		if (dp->ni != NULL) {
185132718Skan			ieee80211_free_node(dp->ni);
186132718Skan			dp->ni = NULL;
187117395Skan		}
188132718Skan		if (dp->m != NULL) {
189132718Skan			m_freem(dp->m);
190117395Skan			dp->m = NULL;
191132718Skan		}
192117395Skan	}
193169689Skan}
194169689Skan
195169689Skanstatic void
196169689Skanrtwn_usb_free_rx_list(struct rtwn_softc *sc)
197132718Skan{
198169689Skan	struct rtwn_usb_softc *uc = RTWN_USB_SOFTC(sc);
199169689Skan
200117395Skan	rtwn_usb_free_list(sc, uc->uc_rx, RTWN_USB_RX_LIST_COUNT);
201132718Skan
202117395Skan	uc->uc_rx_stat_len = 0;
203132718Skan	uc->uc_rx_off = 0;
204132718Skan
205132718Skan	STAILQ_INIT(&uc->uc_rx_active);
206132718Skan	STAILQ_INIT(&uc->uc_rx_inactive);
207117395Skan}
208132718Skan
209132718Skanstatic void
210132718Skanrtwn_usb_free_tx_list(struct rtwn_softc *sc)
211132718Skan{
212132718Skan	struct rtwn_usb_softc *uc = RTWN_USB_SOFTC(sc);
213132718Skan
214132718Skan	rtwn_usb_free_list(sc, uc->uc_tx, RTWN_USB_TX_LIST_COUNT);
215132718Skan
216117395Skan	STAILQ_INIT(&uc->uc_tx_active);
217117395Skan	STAILQ_INIT(&uc->uc_tx_inactive);
218132718Skan	STAILQ_INIT(&uc->uc_tx_pending);
219169689Skan}
220169689Skan
221169689Skanstatic void
222132718Skanrtwn_usb_reset_lists(struct rtwn_softc *sc, struct ieee80211vap *vap)
223132718Skan{
224132718Skan	struct rtwn_usb_softc *uc = RTWN_USB_SOFTC(sc);
225117395Skan
226132718Skan	RTWN_ASSERT_LOCKED(sc);
227117395Skan
228132718Skan	rtwn_usb_reset_tx_list(uc, &uc->uc_tx_active, vap);
229132718Skan	rtwn_usb_reset_tx_list(uc, &uc->uc_tx_pending, vap);
230132718Skan	if (vap == NULL) {
231132718Skan		rtwn_usb_reset_rx_list(uc);
232117395Skan		sc->qfullmsk = 0;
233169689Skan	}
234117395Skan}
235132718Skan
236132718Skanstatic void
237132718Skanrtwn_usb_reset_tx_list(struct rtwn_usb_softc *uc,
238132718Skan    rtwn_datahead *head, struct ieee80211vap *vap)
239132718Skan{
240132718Skan	struct rtwn_vap *uvp = RTWN_VAP(vap);
241132718Skan	struct rtwn_data *dp, *tmp;
242132718Skan	int id;
243132718Skan
244132718Skan	id = (uvp != NULL ? uvp->id : RTWN_VAP_ID_INVALID);
245132718Skan
246132718Skan	STAILQ_FOREACH_SAFE(dp, head, next, tmp) {
247132718Skan		if (vap == NULL || (dp->ni == NULL &&
248132718Skan		    (dp->id == id || id == RTWN_VAP_ID_INVALID)) ||
249132718Skan		    (dp->ni != NULL && dp->ni->ni_vap == vap)) {
250132718Skan			if (dp->ni != NULL) {
251132718Skan				ieee80211_free_node(dp->ni);
252132718Skan				dp->ni = NULL;
253132718Skan			}
254132718Skan
255132718Skan			if (dp->m != NULL) {
256132718Skan				m_freem(dp->m);
257132718Skan				dp->m = NULL;
258132718Skan			}
259132718Skan
260132718Skan			STAILQ_REMOVE(head, dp, rtwn_data, next);
261132718Skan			STAILQ_INSERT_TAIL(&uc->uc_tx_inactive, dp, next);
262132718Skan		}
263117395Skan	}
264132718Skan}
265132718Skan
266132718Skanstatic void
267132718Skanrtwn_usb_reset_rx_list(struct rtwn_usb_softc *uc)
268117395Skan{
269132718Skan	int i;
270169689Skan
271117395Skan	for (i = 0; i < RTWN_USB_RX_LIST_COUNT; i++) {
272132718Skan		struct rtwn_data *dp = &uc->uc_rx[i];
273132718Skan
274132718Skan		if (dp->m != NULL) {
275132718Skan			m_freem(dp->m);
276132718Skan			dp->m = NULL;
277117395Skan		}
278117395Skan	}
279169689Skan	uc->uc_rx_stat_len = 0;
280132718Skan	uc->uc_rx_off = 0;
281132718Skan}
282117395Skan
283117395Skanstatic void
284132718Skanrtwn_usb_start_xfers(struct rtwn_softc *sc)
285132718Skan{
286132718Skan	struct rtwn_usb_softc *uc = RTWN_USB_SOFTC(sc);
287117395Skan
288132718Skan	usbd_transfer_start(uc->uc_xfer[RTWN_BULK_RX]);
289117395Skan}
290132718Skan
291132718Skanstatic void
292117395Skanrtwn_usb_abort_xfers(struct rtwn_softc *sc)
293132718Skan{
294169689Skan	struct rtwn_usb_softc *uc = RTWN_USB_SOFTC(sc);
295132718Skan	int i;
296117395Skan
297132718Skan	RTWN_ASSERT_LOCKED(sc);
298117395Skan
299169689Skan	/* abort any pending transfers */
300117395Skan	RTWN_UNLOCK(sc);
301132718Skan	for (i = 0; i < RTWN_N_TRANSFER; i++)
302132718Skan		usbd_transfer_drain(uc->uc_xfer[i]);
303132718Skan	RTWN_LOCK(sc);
304132718Skan}
305132718Skan
306117395Skanstatic int
307132718Skanrtwn_usb_fw_write_block(struct rtwn_softc *sc, const uint8_t *buf,
308132718Skan    uint16_t reg, int mlen)
309132718Skan{
310132718Skan	int error;
311132718Skan
312132718Skan	/* XXX fix this deconst */
313117395Skan	error = rtwn_usb_write_region_1(sc, reg, __DECONST(uint8_t *, buf),
314132718Skan	    mlen);
315117395Skan
316132718Skan	return (error);
317132718Skan}
318117395Skan
319132718Skanstatic void
320117395Skanrtwn_usb_drop_incorrect_tx(struct rtwn_softc *sc)
321132718Skan{
322132718Skan
323132718Skan	rtwn_setbits_1_shift(sc, R92C_TXDMA_OFFSET_CHK, 0,
324132718Skan	    R92C_TXDMA_OFFSET_DROP_DATA_EN, 1);
325132718Skan}
326117395Skan
327132718Skanstatic void
328132718Skanrtwn_usb_attach_methods(struct rtwn_softc *sc)
329132718Skan{
330132718Skan	sc->sc_write_1		= rtwn_usb_write_1;
331132718Skan	sc->sc_write_2		= rtwn_usb_write_2;
332132718Skan	sc->sc_write_4		= rtwn_usb_write_4;
333132718Skan	sc->sc_read_1		= rtwn_usb_read_1;
334132718Skan	sc->sc_read_2		= rtwn_usb_read_2;
335132718Skan	sc->sc_read_4		= rtwn_usb_read_4;
336132718Skan	sc->sc_delay		= rtwn_usb_delay;
337132718Skan	sc->sc_tx_start		= rtwn_usb_tx_start;
338132718Skan	sc->sc_start_xfers	= rtwn_usb_start_xfers;
339132718Skan	sc->sc_reset_lists	= rtwn_usb_reset_lists;
340132718Skan	sc->sc_abort_xfers	= rtwn_usb_abort_xfers;
341132718Skan	sc->sc_fw_write_block	= rtwn_usb_fw_write_block;
342132718Skan	sc->sc_get_qmap		= rtwn_usb_get_qmap;
343117395Skan	sc->sc_set_desc_addr	= rtwn_nop_softc;
344132718Skan	sc->sc_drop_incorrect_tx = rtwn_usb_drop_incorrect_tx;
345132718Skan	sc->sc_beacon_update_begin = rtwn_nop_softc_vap;
346132718Skan	sc->sc_beacon_update_end = rtwn_nop_softc_vap;
347132718Skan	sc->sc_beacon_unload	= rtwn_nop_softc_int;
348132718Skan
349132718Skan	sc->bcn_check_interval	= 100;
350132718Skan}
351117395Skan
352132718Skanstatic void
353132718Skanrtwn_usb_sysctlattach(struct rtwn_softc *sc)
354132718Skan{
355132718Skan	struct rtwn_usb_softc *uc = RTWN_USB_SOFTC(sc);
356132718Skan	struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(sc->sc_dev);
357132718Skan	struct sysctl_oid *tree = device_get_sysctl_tree(sc->sc_dev);
358132718Skan	char str[64];
359132718Skan	int ret;
360132718Skan
361117395Skan	ret = snprintf(str, sizeof(str),
362132718Skan	    "Rx buffer size, 512-byte units [%d...%d]",
363132718Skan	    RTWN_USB_RXBUFSZ_MIN, RTWN_USB_RXBUFSZ_MAX);
364132718Skan	KASSERT(ret > 0, ("ret (%d) <= 0!\n", ret));
365132718Skan	(void) ret;
366132718Skan
367132718Skan	uc->uc_rx_buf_size = RTWN_USB_RXBUFSZ_DEF;
368132718Skan	SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
369117395Skan	    "rx_buf_size", CTLFLAG_RDTUN, &uc->uc_rx_buf_size,
370132718Skan	    uc->uc_rx_buf_size, str);
371132718Skan	if (uc->uc_rx_buf_size < RTWN_USB_RXBUFSZ_MIN)
372132718Skan		uc->uc_rx_buf_size = RTWN_USB_RXBUFSZ_MIN;
373132718Skan	if (uc->uc_rx_buf_size > RTWN_USB_RXBUFSZ_MAX)
374132718Skan		uc->uc_rx_buf_size = RTWN_USB_RXBUFSZ_MAX;
375132718Skan}
376132718Skan
377132718Skanstatic int
378132718Skanrtwn_usb_attach(device_t self)
379132718Skan{
380132718Skan	struct usb_attach_arg *uaa = device_get_ivars(self);
381132718Skan	struct rtwn_usb_softc *uc = device_get_softc(self);
382117395Skan	struct rtwn_softc *sc = &uc->uc_sc;
383132718Skan	struct ieee80211com *ic = &sc->sc_ic;
384132718Skan	int error;
385132718Skan
386132718Skan	device_set_usb_desc(self);
387132718Skan	uc->uc_udev = uaa->device;
388132718Skan	sc->sc_dev = self;
389132718Skan	ic->ic_name = device_get_nameunit(self);
390132718Skan
391132718Skan	/* Need to be initialized early. */
392132718Skan	rtwn_sysctlattach(sc);
393132718Skan	rtwn_usb_sysctlattach(sc);
394132718Skan	mtx_init(&sc->sc_mtx, ic->ic_name, MTX_NETWORK_LOCK, MTX_DEF);
395132718Skan
396132718Skan	rtwn_usb_attach_methods(sc);
397132718Skan	rtwn_usb_attach_private(uc, USB_GET_DRIVER_INFO(uaa));
398117395Skan
399132718Skan	error = rtwn_usb_setup_endpoints(uc);
400132718Skan	if (error != 0)
401132718Skan		goto detach;
402132718Skan
403132718Skan	/* Allocate Tx/Rx buffers. */
404132718Skan	error = rtwn_usb_alloc_rx_list(sc);
405132718Skan	if (error != 0)
406132718Skan		goto detach;
407132718Skan
408132718Skan	error = rtwn_usb_alloc_tx_list(sc);
409132718Skan	if (error != 0)
410132718Skan		goto detach;
411132718Skan
412132718Skan	/* Generic attach. */
413132718Skan	error = rtwn_attach(sc);
414132718Skan	if (error != 0)
415132718Skan		goto detach;
416132718Skan
417117395Skan	return (0);
418132718Skan
419132718Skandetach:
420132718Skan	rtwn_usb_detach(self);		/* failure */
421132718Skan	return (ENXIO);
422132718Skan}
423132718Skan
424132718Skanstatic int
425132718Skanrtwn_usb_detach(device_t self)
426132718Skan{
427132718Skan	struct rtwn_usb_softc *uc = device_get_softc(self);
428132718Skan	struct rtwn_softc *sc = &uc->uc_sc;
429132718Skan
430132718Skan	/* Generic detach. */
431132718Skan	rtwn_detach(sc);
432132718Skan
433117395Skan	/* Free Tx/Rx buffers. */
434117395Skan	rtwn_usb_free_tx_list(sc);
435132718Skan	rtwn_usb_free_rx_list(sc);
436132718Skan
437132718Skan	/* Detach all USB transfers. */
438132718Skan	usbd_transfer_unsetup(uc->uc_xfer, RTWN_N_TRANSFER);
439117395Skan
440117395Skan	rtwn_detach_private(sc);
441169689Skan	mtx_destroy(&sc->sc_mtx);
442132718Skan
443132718Skan	return (0);
444132718Skan}
445117395Skan
446132718Skanstatic int
447132718Skanrtwn_usb_suspend(device_t self)
448169689Skan{
449169689Skan	struct rtwn_usb_softc *uc = device_get_softc(self);
450169689Skan
451117395Skan	rtwn_suspend(&uc->uc_sc);
452132718Skan
453132718Skan	return (0);
454132718Skan}
455132718Skan
456117395Skanstatic int
457132718Skanrtwn_usb_resume(device_t self)
458132718Skan{
459132718Skan	struct rtwn_usb_softc *uc = device_get_softc(self);
460117395Skan
461132718Skan	rtwn_resume(&uc->uc_sc);
462117395Skan
463117395Skan	return (0);
464132718Skan}
465117395Skan
466132718Skanstatic device_method_t rtwn_usb_methods[] = {
467132718Skan	/* Device interface */
468132718Skan	DEVMETHOD(device_probe,		rtwn_usb_match),
469132718Skan	DEVMETHOD(device_attach,	rtwn_usb_attach),
470132718Skan	DEVMETHOD(device_detach,	rtwn_usb_detach),
471132718Skan	DEVMETHOD(device_suspend,	rtwn_usb_suspend),
472132718Skan	DEVMETHOD(device_resume,	rtwn_usb_resume),
473132718Skan
474117395Skan	DEVMETHOD_END
475132718Skan};
476132718Skan
477132718Skanstatic driver_t rtwn_usb_driver = {
478132718Skan	"rtwn",
479132718Skan	rtwn_usb_methods,
480117395Skan	sizeof(struct rtwn_usb_softc)
481132718Skan};
482132718Skan
483117395Skanstatic devclass_t rtwn_usb_devclass;
484132718Skan
485117395SkanDRIVER_MODULE(rtwn_usb, uhub, rtwn_usb_driver, rtwn_usb_devclass, NULL, NULL);
486117395SkanMODULE_VERSION(rtwn_usb, 1);
487132718SkanMODULE_DEPEND(rtwn_usb, usb, 1, 1, 1);
488117395SkanMODULE_DEPEND(rtwn_usb, wlan, 1, 1, 1);
489132718SkanMODULE_DEPEND(rtwn_usb, rtwn, 2, 2, 2);
490132718SkanUSB_PNP_HOST_INFO(rtwn_devs);
491117395Skan