xref: /openbsd/sys/dev/usb/if_uath.c (revision 81508fe3)
1*81508fe3Sjsg /*	$OpenBSD: if_uath.c,v 1.89 2024/05/23 03:21:08 jsg Exp $	*/
217849d02Sdamien 
317849d02Sdamien /*-
417849d02Sdamien  * Copyright (c) 2006
517849d02Sdamien  *	Damien Bergamini <damien.bergamini@free.fr>
617849d02Sdamien  *
717849d02Sdamien  * Permission to use, copy, modify, and distribute this software for any
817849d02Sdamien  * purpose with or without fee is hereby granted, provided that the above
917849d02Sdamien  * copyright notice and this permission notice appear in all copies.
1017849d02Sdamien  *
1117849d02Sdamien  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1217849d02Sdamien  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1317849d02Sdamien  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1417849d02Sdamien  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1517849d02Sdamien  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1617849d02Sdamien  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1717849d02Sdamien  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1817849d02Sdamien  */
1917849d02Sdamien 
2017849d02Sdamien /*-
2117849d02Sdamien  * Driver for Atheros AR5005UG/AR5005UX chipsets.
2217849d02Sdamien  *
2317849d02Sdamien  * IMPORTANT NOTICE:
2417849d02Sdamien  * This driver was written without any documentation or support from Atheros
2517849d02Sdamien  * Communications. It is based on a black-box analysis of the Windows binary
2617849d02Sdamien  * driver. It handles both pre and post-firmware devices.
2717849d02Sdamien  */
2817849d02Sdamien 
2917849d02Sdamien #include "bpfilter.h"
3017849d02Sdamien 
3117849d02Sdamien #include <sys/param.h>
3217849d02Sdamien #include <sys/sockio.h>
3317849d02Sdamien #include <sys/mbuf.h>
3417849d02Sdamien #include <sys/systm.h>
3517849d02Sdamien #include <sys/timeout.h>
3617849d02Sdamien #include <sys/device.h>
379b18ffb8Sguenther #include <sys/endian.h>
3817849d02Sdamien 
3917849d02Sdamien #include <machine/bus.h>
4017849d02Sdamien #include <machine/intr.h>
4117849d02Sdamien 
4217849d02Sdamien #if NBPFILTER > 0
4317849d02Sdamien #include <net/bpf.h>
4417849d02Sdamien #endif
4517849d02Sdamien #include <net/if.h>
4617849d02Sdamien #include <net/if_dl.h>
4717849d02Sdamien #include <net/if_media.h>
4817849d02Sdamien 
4917849d02Sdamien #include <netinet/in.h>
5017849d02Sdamien #include <netinet/if_ether.h>
5117849d02Sdamien 
5217849d02Sdamien #include <net80211/ieee80211_var.h>
5317849d02Sdamien #include <net80211/ieee80211_radiotap.h>
5417849d02Sdamien 
5517849d02Sdamien #include <dev/usb/usb.h>
5617849d02Sdamien #include <dev/usb/usbdi.h>
576a54674fSdamien #include <dev/usb/usbdivar.h>	/* needs_reattach() */
5817849d02Sdamien #include <dev/usb/usbdevs.h>
5917849d02Sdamien 
6017849d02Sdamien #include <dev/usb/if_uathreg.h>
6117849d02Sdamien #include <dev/usb/if_uathvar.h>
6217849d02Sdamien 
6317849d02Sdamien #ifdef UATH_DEBUG
64695146ceSjsg #define DPRINTF(x)	do { if (uath_debug) printf x; } while (0)
65695146ceSjsg #define DPRINTFN(n, x)	do { if (uath_debug >= (n)) printf x; } while (0)
6617849d02Sdamien int uath_debug = 1;
6717849d02Sdamien #else
6817849d02Sdamien #define DPRINTF(x)
6917849d02Sdamien #define DPRINTFN(n, x)
7017849d02Sdamien #endif
7117849d02Sdamien 
729b11f1a4Sdamien /*-
739b11f1a4Sdamien  * Various supported device vendors/products.
74e9fdcc1aSjsg  * UB51: AR5005UG 802.11b/g, UB52: AR5005UX 802.11a/b/g
75e9fdcc1aSjsg  */
76d03627b6Sdamien #define UATH_DEV(v, p, f)						\
77e2d7d02cSdamien 	{ { USB_VENDOR_##v, USB_PRODUCT_##v##_##p }, (f) },		\
782bfb63f7Sdamien 	{ { USB_VENDOR_##v, USB_PRODUCT_##v##_##p##_NF },		\
792bfb63f7Sdamien 	    (f) | UATH_FLAG_PRE_FIRMWARE }
80e2d7d02cSdamien #define UATH_DEV_UG(v, p)	UATH_DEV(v, p, 0)
81e2d7d02cSdamien #define UATH_DEV_UX(v, p)	UATH_DEV(v, p, UATH_FLAG_ABG)
8217849d02Sdamien static const struct uath_type {
8317849d02Sdamien 	struct usb_devno	dev;
8417849d02Sdamien 	unsigned int		flags;
85d03627b6Sdamien #define UATH_FLAG_PRE_FIRMWARE	(1 << 0)
86e2d7d02cSdamien #define UATH_FLAG_ABG		(1 << 1)
8717849d02Sdamien } uath_devs[] = {
8823b218c0Sjsg 	UATH_DEV_UG(ACCTON,		SMCWUSBTG2),
89e2d7d02cSdamien 	UATH_DEV_UG(ATHEROS,		AR5523),
90e2d7d02cSdamien 	UATH_DEV_UG(ATHEROS2,		AR5523_1),
91e2d7d02cSdamien 	UATH_DEV_UG(ATHEROS2,		AR5523_2),
92e2d7d02cSdamien 	UATH_DEV_UX(ATHEROS2,		AR5523_3),
93e2d7d02cSdamien 	UATH_DEV_UG(CONCEPTRONIC,	AR5523_1),
94e2d7d02cSdamien 	UATH_DEV_UX(CONCEPTRONIC,	AR5523_2),
95e2d7d02cSdamien 	UATH_DEV_UX(DLINK,		DWLAG122),
96e2d7d02cSdamien 	UATH_DEV_UX(DLINK,		DWLAG132),
97e2d7d02cSdamien 	UATH_DEV_UG(DLINK,		DWLG132),
9853cb321cSsthen 	UATH_DEV_UG(DLINK2,		WUA2340),
99e2d7d02cSdamien 	UATH_DEV_UG(GIGASET,		AR5523),
100f276d25eSdamien 	UATH_DEV_UG(GIGASET,		SMCWUSBTG),
101e2d7d02cSdamien 	UATH_DEV_UG(GLOBALSUN,		AR5523_1),
102e2d7d02cSdamien 	UATH_DEV_UX(GLOBALSUN,		AR5523_2),
10363c8563cSyuo 	UATH_DEV_UG(IODATA,		USBWNG54US),
10463c8563cSyuo 	UATH_DEV_UG(MELCO,		WLIU2KAMG54),
105e2d7d02cSdamien 	UATH_DEV_UX(NETGEAR,		WG111U),
106e2d7d02cSdamien 	UATH_DEV_UG(NETGEAR3,		WG111T),
107e2d7d02cSdamien 	UATH_DEV_UG(NETGEAR3,		WPN111),
10859319340Scanacar 	UATH_DEV_UG(PHILIPS,		SNU6500),
109e2d7d02cSdamien 	UATH_DEV_UX(UMEDIA,		AR5523_2),
110e2d7d02cSdamien 	UATH_DEV_UG(UMEDIA,		TEW444UBEU),
111e2d7d02cSdamien 	UATH_DEV_UG(WISTRONNEWEB,	AR5523_1),
112e2d7d02cSdamien 	UATH_DEV_UX(WISTRONNEWEB,	AR5523_2),
11367153c9dSmpi 	UATH_DEV_UG(ZCOM,		AR5523),
11467153c9dSmpi 
11567153c9dSmpi 	/* Devices that share one of the IDs above. */
11667153c9dSmpi 	{ { USB_VENDOR_NETGEAR3, USB_PRODUCT_NETGEAR3_WG111T_1 }, 0 }		\
11717849d02Sdamien };
11817849d02Sdamien #define uath_lookup(v, p)	\
1196a54674fSdamien 	((const struct uath_type *)usb_lookup(uath_devs, v, p))
12017849d02Sdamien 
121ef89f9e6Smpi void	uath_attachhook(struct device *);
12278315254Smbalmer int	uath_open_pipes(struct uath_softc *);
12378315254Smbalmer void	uath_close_pipes(struct uath_softc *);
12478315254Smbalmer int	uath_alloc_tx_data_list(struct uath_softc *);
12578315254Smbalmer void	uath_free_tx_data_list(struct uath_softc *);
12678315254Smbalmer int	uath_alloc_rx_data_list(struct uath_softc *);
12778315254Smbalmer void	uath_free_rx_data_list(struct uath_softc *);
12878315254Smbalmer int	uath_alloc_tx_cmd_list(struct uath_softc *);
12978315254Smbalmer void	uath_free_tx_cmd_list(struct uath_softc *);
13078315254Smbalmer int	uath_alloc_rx_cmd_list(struct uath_softc *);
13178315254Smbalmer void	uath_free_rx_cmd_list(struct uath_softc *);
13278315254Smbalmer int	uath_media_change(struct ifnet *);
13378315254Smbalmer void	uath_stat(void *);
13478315254Smbalmer void	uath_next_scan(void *);
13578315254Smbalmer void	uath_task(void *);
13645eff2ebSdamien int	uath_newstate(struct ieee80211com *, enum ieee80211_state, int);
13717849d02Sdamien #ifdef UATH_DEBUG
13878315254Smbalmer void	uath_dump_cmd(const uint8_t *, int, char);
13917849d02Sdamien #endif
14045eff2ebSdamien int	uath_cmd(struct uath_softc *, uint32_t, const void *, int, void *,
14117849d02Sdamien 	    int);
14245eff2ebSdamien int	uath_cmd_write(struct uath_softc *, uint32_t, const void *, int, int);
14345eff2ebSdamien int	uath_cmd_read(struct uath_softc *, uint32_t, const void *, int, void *,
14445eff2ebSdamien 	    int);
14545eff2ebSdamien int	uath_write_reg(struct uath_softc *, uint32_t, uint32_t);
14645eff2ebSdamien int	uath_write_multi(struct uath_softc *, uint32_t, const void *, int);
14778315254Smbalmer int	uath_read_reg(struct uath_softc *, uint32_t, uint32_t *);
14878315254Smbalmer int	uath_read_eeprom(struct uath_softc *, uint32_t, void *);
149ab0b1be7Smglocker void	uath_cmd_rxeof(struct usbd_xfer *, void *, usbd_status);
150ab0b1be7Smglocker void	uath_data_rxeof(struct usbd_xfer *, void *, usbd_status);
151ab0b1be7Smglocker void	uath_data_txeof(struct usbd_xfer *, void *, usbd_status);
15278315254Smbalmer int	uath_tx_null(struct uath_softc *);
15378315254Smbalmer int	uath_tx_data(struct uath_softc *, struct mbuf *,
15417849d02Sdamien 	    struct ieee80211_node *);
15578315254Smbalmer void	uath_start(struct ifnet *);
15678315254Smbalmer void	uath_watchdog(struct ifnet *);
15778315254Smbalmer int	uath_ioctl(struct ifnet *, u_long, caddr_t);
15878315254Smbalmer int	uath_query_eeprom(struct uath_softc *);
15978315254Smbalmer int	uath_reset(struct uath_softc *);
16078315254Smbalmer int	uath_reset_tx_queues(struct uath_softc *);
16178315254Smbalmer int	uath_wme_init(struct uath_softc *);
16278315254Smbalmer int	uath_set_chan(struct uath_softc *, struct ieee80211_channel *);
16383da4af0Sdamien int	uath_set_key(struct uath_softc *, const struct ieee80211_key *, int);
16478315254Smbalmer int	uath_set_keys(struct uath_softc *);
16545eff2ebSdamien int	uath_set_rates(struct uath_softc *, const struct ieee80211_rateset *);
16678315254Smbalmer int	uath_set_rxfilter(struct uath_softc *, uint32_t, uint32_t);
16778315254Smbalmer int	uath_set_led(struct uath_softc *, int, int);
16845eff2ebSdamien int	uath_switch_channel(struct uath_softc *, struct ieee80211_channel *);
16978315254Smbalmer int	uath_init(struct ifnet *);
17078315254Smbalmer void	uath_stop(struct ifnet *, int);
17178315254Smbalmer int	uath_loadfirmware(struct uath_softc *, const u_char *, int);
17217849d02Sdamien 
1739f5f6d50Smbalmer int uath_match(struct device *, void *, void *);
1749f5f6d50Smbalmer void uath_attach(struct device *, struct device *, void *);
1759f5f6d50Smbalmer int uath_detach(struct device *, int);
1769f5f6d50Smbalmer 
1779f5f6d50Smbalmer struct cfdriver uath_cd = {
1782dbb2583Smatthew 	NULL, "uath", DV_IFNET
1799f5f6d50Smbalmer };
1809f5f6d50Smbalmer 
1819f5f6d50Smbalmer const struct cfattach uath_ca = {
18253c6612dSmpi 	sizeof(struct uath_softc), uath_match, uath_attach, uath_detach
1839f5f6d50Smbalmer };
18417849d02Sdamien 
185de5d9ff0Sjsg int
uath_match(struct device * parent,void * match,void * aux)186de5d9ff0Sjsg uath_match(struct device *parent, void *match, void *aux)
18717849d02Sdamien {
188de5d9ff0Sjsg 	struct usb_attach_arg *uaa = aux;
18917849d02Sdamien 
190f4b7d08eSmpi 	if (uaa->iface == NULL || uaa->configno != UATH_CONFIG_NO)
19117849d02Sdamien 		return UMATCH_NONE;
19217849d02Sdamien 
19317849d02Sdamien 	return (uath_lookup(uaa->vendor, uaa->product) != NULL) ?
19417849d02Sdamien 	    UMATCH_VENDOR_PRODUCT : UMATCH_NONE;
19517849d02Sdamien }
19617849d02Sdamien 
19778315254Smbalmer void
uath_attachhook(struct device * self)198ef89f9e6Smpi uath_attachhook(struct device *self)
19917849d02Sdamien {
200ef89f9e6Smpi 	struct uath_softc *sc = (struct uath_softc *)self;
20117849d02Sdamien 	u_char *fw;
20217849d02Sdamien 	size_t size;
20317849d02Sdamien 	int error;
20417849d02Sdamien 
20517849d02Sdamien 	if ((error = loadfirmware("uath-ar5523", &fw, &size)) != 0) {
206b2f1b584Sderaadt 		printf("%s: error %d, could not read firmware %s\n",
207b2f1b584Sderaadt 		    sc->sc_dev.dv_xname, error, "uath-ar5523");
20817849d02Sdamien 		return;
20917849d02Sdamien 	}
21017849d02Sdamien 
2116a54674fSdamien 	error = uath_loadfirmware(sc, fw, size);
212c9ee9455Sderaadt 	free(fw, M_DEVBUF, size);
2136a54674fSdamien 
2146a54674fSdamien 	if (error == 0) {
2156a54674fSdamien 		/*
2166a54674fSdamien 		 * Hack alert: the device doesn't always gracefully detach
2176a54674fSdamien 		 * from the bus after a firmware upload.  We need to force
2186a54674fSdamien 		 * a port reset and a re-exploration on the parent hub.
2196a54674fSdamien 		 */
22041651d1aSmpi 		usbd_reset_port(sc->sc_uhub, sc->sc_port);
2216a54674fSdamien 		usb_needs_reattach(sc->sc_udev);
2226a54674fSdamien 	} else {
22317849d02Sdamien 		printf("%s: could not load firmware (error=%s)\n",
2244ab2b9feSmbalmer 		    sc->sc_dev.dv_xname, usbd_errstr(error));
22517849d02Sdamien 	}
22617849d02Sdamien }
22717849d02Sdamien 
228de5d9ff0Sjsg void
uath_attach(struct device * parent,struct device * self,void * aux)229de5d9ff0Sjsg uath_attach(struct device *parent, struct device *self, void *aux)
23017849d02Sdamien {
231de5d9ff0Sjsg 	struct uath_softc *sc = (struct uath_softc *)self;
232de5d9ff0Sjsg 	struct usb_attach_arg *uaa = aux;
23317849d02Sdamien 	struct ieee80211com *ic = &sc->sc_ic;
23417849d02Sdamien 	struct ifnet *ifp = &ic->ic_if;
23517849d02Sdamien 	usbd_status error;
23617849d02Sdamien 	int i;
23717849d02Sdamien 
23817849d02Sdamien 	sc->sc_udev = uaa->device;
2396a54674fSdamien 	sc->sc_uhub = uaa->device->myhub;
2406a54674fSdamien 	sc->sc_port = uaa->port;
24117849d02Sdamien 
24217849d02Sdamien 	sc->sc_flags = uath_lookup(uaa->vendor, uaa->product)->flags;
24317849d02Sdamien 
24417849d02Sdamien 	/* get the first interface handle */
24517849d02Sdamien 	error = usbd_device2interface_handle(sc->sc_udev, UATH_IFACE_INDEX,
24617849d02Sdamien 	    &sc->sc_iface);
24717849d02Sdamien 	if (error != 0) {
24817849d02Sdamien 		printf("%s: could not get interface handle\n",
2494ab2b9feSmbalmer 		    sc->sc_dev.dv_xname);
250de5d9ff0Sjsg 		return;
25117849d02Sdamien 	}
25217849d02Sdamien 
25317849d02Sdamien 	/*
25417849d02Sdamien 	 * We must open the pipes early because they're used to upload the
25517849d02Sdamien 	 * firmware (pre-firmware devices) or to send firmware commands.
25617849d02Sdamien 	 */
25717849d02Sdamien 	if (uath_open_pipes(sc) != 0) {
2584ab2b9feSmbalmer 		printf("%s: could not open pipes\n", sc->sc_dev.dv_xname);
259de5d9ff0Sjsg 		return;
26017849d02Sdamien 	}
26117849d02Sdamien 
26217849d02Sdamien 	if (sc->sc_flags & UATH_FLAG_PRE_FIRMWARE) {
263ef89f9e6Smpi 		config_mountroot(self, uath_attachhook);
264de5d9ff0Sjsg 		return;
26517849d02Sdamien 	}
26617849d02Sdamien 
26717849d02Sdamien 	/*
26817849d02Sdamien 	 * Only post-firmware devices here.
26917849d02Sdamien 	 */
270c33449aaSjakemsr 	usb_init_task(&sc->sc_task, uath_task, sc, USB_TASK_TYPE_GENERIC);
27117849d02Sdamien 	timeout_set(&sc->scan_to, uath_next_scan, sc);
27217849d02Sdamien 	timeout_set(&sc->stat_to, uath_stat, sc);
27317849d02Sdamien 
27417849d02Sdamien 	/*
27517849d02Sdamien 	 * Allocate xfers for firmware commands.
27617849d02Sdamien 	 */
27717849d02Sdamien 	if (uath_alloc_tx_cmd_list(sc) != 0) {
27817849d02Sdamien 		printf("%s: could not allocate Tx command list\n",
2794ab2b9feSmbalmer 		    sc->sc_dev.dv_xname);
2802dac2055Sstsp 		goto fail;
28117849d02Sdamien 	}
28217849d02Sdamien 	if (uath_alloc_rx_cmd_list(sc) != 0) {
28317849d02Sdamien 		printf("%s: could not allocate Rx command list\n",
2844ab2b9feSmbalmer 		    sc->sc_dev.dv_xname);
2852dac2055Sstsp 		goto fail;
28617849d02Sdamien 	}
28717849d02Sdamien 
28817849d02Sdamien 	/*
28917849d02Sdamien 	 * Queue Rx command xfers.
29017849d02Sdamien 	 */
29117849d02Sdamien 	for (i = 0; i < UATH_RX_CMD_LIST_COUNT; i++) {
29217849d02Sdamien 		struct uath_rx_cmd *cmd = &sc->rx_cmd[i];
29317849d02Sdamien 
29417849d02Sdamien 		usbd_setup_xfer(cmd->xfer, sc->cmd_rx_pipe, cmd, cmd->buf,
29517849d02Sdamien 		    UATH_MAX_RXCMDSZ, USBD_SHORT_XFER_OK | USBD_NO_COPY,
29617849d02Sdamien 		    USBD_NO_TIMEOUT, uath_cmd_rxeof);
29717849d02Sdamien 		error = usbd_transfer(cmd->xfer);
29817849d02Sdamien 		if (error != USBD_IN_PROGRESS && error != 0) {
29917849d02Sdamien 			printf("%s: could not queue Rx command xfer\n",
3004ab2b9feSmbalmer 			    sc->sc_dev.dv_xname);
3012dac2055Sstsp 			goto fail;
30217849d02Sdamien 		}
30317849d02Sdamien 	}
30417849d02Sdamien 
30517849d02Sdamien 	/*
30617849d02Sdamien 	 * We're now ready to send/receive firmware commands.
30717849d02Sdamien 	 */
30817849d02Sdamien 	if (uath_reset(sc) != 0) {
30917849d02Sdamien 		printf("%s: could not initialize adapter\n",
3104ab2b9feSmbalmer 		    sc->sc_dev.dv_xname);
3112dac2055Sstsp 		goto fail;
31217849d02Sdamien 	}
31317849d02Sdamien 	if (uath_query_eeprom(sc) != 0) {
3144ab2b9feSmbalmer 		printf("%s: could not read EEPROM\n", sc->sc_dev.dv_xname);
3152dac2055Sstsp 		goto fail;
31617849d02Sdamien 	}
31717849d02Sdamien 
31817849d02Sdamien 	printf("%s: MAC/BBP AR5523, RF AR%c112, address %s\n",
3194ab2b9feSmbalmer 	    sc->sc_dev.dv_xname, (sc->sc_flags & UATH_FLAG_ABG) ? '5': '2',
32017849d02Sdamien 	    ether_sprintf(ic->ic_myaddr));
32117849d02Sdamien 
32217849d02Sdamien 	/*
32317849d02Sdamien 	 * Allocate xfers for Tx/Rx data pipes.
32417849d02Sdamien 	 */
32517849d02Sdamien 	if (uath_alloc_tx_data_list(sc) != 0) {
32617849d02Sdamien 		printf("%s: could not allocate Tx data list\n",
3274ab2b9feSmbalmer 		    sc->sc_dev.dv_xname);
3282dac2055Sstsp 		goto fail;
32917849d02Sdamien 	}
33017849d02Sdamien 	if (uath_alloc_rx_data_list(sc) != 0) {
33117849d02Sdamien 		printf("%s: could not allocate Rx data list\n",
3324ab2b9feSmbalmer 		    sc->sc_dev.dv_xname);
3332dac2055Sstsp 		goto fail;
33417849d02Sdamien 	}
33517849d02Sdamien 
33617849d02Sdamien 	ic->ic_phytype = IEEE80211_T_OFDM;	/* not only, but not used */
33717849d02Sdamien 	ic->ic_opmode = IEEE80211_M_STA;	/* default to BSS mode */
33817849d02Sdamien 	ic->ic_state = IEEE80211_S_INIT;
33917849d02Sdamien 
34017849d02Sdamien 	/* set device capabilities */
34117849d02Sdamien 	ic->ic_caps =
34255496a37Sbrad 	    IEEE80211_C_MONITOR |	/* monitor mode supported */
34317849d02Sdamien 	    IEEE80211_C_TXPMGT |	/* tx power management */
34417849d02Sdamien 	    IEEE80211_C_SHPREAMBLE |	/* short preamble supported */
34517849d02Sdamien 	    IEEE80211_C_SHSLOT |	/* short slot time supported */
34617849d02Sdamien 	    IEEE80211_C_WEP;		/* h/w WEP */
34717849d02Sdamien 
34817849d02Sdamien 	/* set supported .11b and .11g rates */
349f2cd5b67Sderaadt 	ic->ic_sup_rates[IEEE80211_MODE_11B] = ieee80211_std_rateset_11b;
350f2cd5b67Sderaadt 	ic->ic_sup_rates[IEEE80211_MODE_11G] = ieee80211_std_rateset_11g;
35117849d02Sdamien 
35217849d02Sdamien 	/* set supported .11b and .11g channels (1 through 14) */
35317849d02Sdamien 	for (i = 1; i <= 14; i++) {
35417849d02Sdamien 		ic->ic_channels[i].ic_freq =
35517849d02Sdamien 		    ieee80211_ieee2mhz(i, IEEE80211_CHAN_2GHZ);
35617849d02Sdamien 		ic->ic_channels[i].ic_flags =
35717849d02Sdamien 		    IEEE80211_CHAN_CCK | IEEE80211_CHAN_OFDM |
35817849d02Sdamien 		    IEEE80211_CHAN_DYN | IEEE80211_CHAN_2GHZ;
35917849d02Sdamien 	}
36017849d02Sdamien 
36117849d02Sdamien 	ifp->if_softc = sc;
36217849d02Sdamien 	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
36317849d02Sdamien 	ifp->if_ioctl = uath_ioctl;
36417849d02Sdamien 	ifp->if_start = uath_start;
36517849d02Sdamien 	ifp->if_watchdog = uath_watchdog;
3664ab2b9feSmbalmer 	memcpy(ifp->if_xname, sc->sc_dev.dv_xname, IFNAMSIZ);
36717849d02Sdamien 
36817849d02Sdamien 	if_attach(ifp);
36917849d02Sdamien 	ieee80211_ifattach(ifp);
37017849d02Sdamien 
37117849d02Sdamien 	/* override state transition machine */
37217849d02Sdamien 	sc->sc_newstate = ic->ic_newstate;
37317849d02Sdamien 	ic->ic_newstate = uath_newstate;
37417849d02Sdamien 	ieee80211_media_init(ifp, uath_media_change, ieee80211_media_status);
37517849d02Sdamien 
37617849d02Sdamien #if NBPFILTER > 0
37717849d02Sdamien 	bpfattach(&sc->sc_drvbpf, ifp, DLT_IEEE802_11_RADIO,
37817849d02Sdamien 	    sizeof (struct ieee80211_frame) + IEEE80211_RADIOTAP_HDRLEN);
37917849d02Sdamien 
38017849d02Sdamien 	sc->sc_rxtap_len = sizeof sc->sc_rxtapu;
38117849d02Sdamien 	sc->sc_rxtap.wr_ihdr.it_len = htole16(sc->sc_rxtap_len);
38217849d02Sdamien 	sc->sc_rxtap.wr_ihdr.it_present = htole32(UATH_RX_RADIOTAP_PRESENT);
38317849d02Sdamien 
38417849d02Sdamien 	sc->sc_txtap_len = sizeof sc->sc_txtapu;
38517849d02Sdamien 	sc->sc_txtap.wt_ihdr.it_len = htole16(sc->sc_txtap_len);
38617849d02Sdamien 	sc->sc_txtap.wt_ihdr.it_present = htole32(UATH_TX_RADIOTAP_PRESENT);
38717849d02Sdamien #endif
38817849d02Sdamien 
389de5d9ff0Sjsg 	return;
39017849d02Sdamien 
3912dac2055Sstsp fail:	uath_close_pipes(sc);
3922dac2055Sstsp 	uath_free_tx_data_list(sc);
3932dac2055Sstsp 	uath_free_rx_cmd_list(sc);
3942dac2055Sstsp 	uath_free_tx_cmd_list(sc);
3957b043b62Sjakemsr 	usbd_deactivate(sc->sc_udev);
39617849d02Sdamien }
39717849d02Sdamien 
398de5d9ff0Sjsg int
uath_detach(struct device * self,int flags)399de5d9ff0Sjsg uath_detach(struct device *self, int flags)
40017849d02Sdamien {
401de5d9ff0Sjsg 	struct uath_softc *sc = (struct uath_softc *)self;
40217849d02Sdamien 	struct ifnet *ifp = &sc->sc_ic.ic_if;
40317849d02Sdamien 	int s;
40417849d02Sdamien 
4059b11f1a4Sdamien 	s = splnet();
40617849d02Sdamien 
40717849d02Sdamien 	if (sc->sc_flags & UATH_FLAG_PRE_FIRMWARE) {
40817849d02Sdamien 		uath_close_pipes(sc);
40917849d02Sdamien 		splx(s);
41017849d02Sdamien 		return 0;
41117849d02Sdamien 	}
41217849d02Sdamien 
41317849d02Sdamien 	/* post-firmware device */
41417849d02Sdamien 
41517849d02Sdamien 	usb_rem_task(sc->sc_udev, &sc->sc_task);
416c7438bddSjakemsr 	if (timeout_initialized(&sc->scan_to))
41717849d02Sdamien 		timeout_del(&sc->scan_to);
418c7438bddSjakemsr 	if (timeout_initialized(&sc->stat_to))
41917849d02Sdamien 		timeout_del(&sc->stat_to);
42017849d02Sdamien 
4212dac2055Sstsp 	/* close Tx/Rx pipes */
4222dac2055Sstsp 	uath_close_pipes(sc);
4232dac2055Sstsp 
4242dac2055Sstsp 	/* free xfers */
42517849d02Sdamien 	uath_free_tx_data_list(sc);
42617849d02Sdamien 	uath_free_rx_data_list(sc);
42717849d02Sdamien 	uath_free_tx_cmd_list(sc);
42817849d02Sdamien 	uath_free_rx_cmd_list(sc);
42917849d02Sdamien 
4305dd621eeSjakemsr 	if (ifp->if_softc != NULL) {
43174433b06Sdamien 		ieee80211_ifdetach(ifp);	/* free all nodes */
43274433b06Sdamien 		if_detach(ifp);
4335dd621eeSjakemsr 	}
43474433b06Sdamien 
43517849d02Sdamien 	splx(s);
43617849d02Sdamien 
43717849d02Sdamien 	return 0;
43817849d02Sdamien }
43917849d02Sdamien 
44078315254Smbalmer int
uath_open_pipes(struct uath_softc * sc)44117849d02Sdamien uath_open_pipes(struct uath_softc *sc)
44217849d02Sdamien {
44317849d02Sdamien 	int error;
44417849d02Sdamien 
44517849d02Sdamien 	/*
44617849d02Sdamien 	 * XXX pipes numbers are hardcoded because we don't have any way
44717849d02Sdamien 	 * to distinguish the data pipes from the firmware command pipes
44817849d02Sdamien 	 * (both are bulk pipes) using the endpoints descriptors.
44917849d02Sdamien 	 */
45017849d02Sdamien 	error = usbd_open_pipe(sc->sc_iface, 0x01, USBD_EXCLUSIVE_USE,
45117849d02Sdamien 	    &sc->cmd_tx_pipe);
45217849d02Sdamien 	if (error != 0) {
45317849d02Sdamien 		printf("%s: could not open Tx command pipe: %s\n",
4544ab2b9feSmbalmer 		    sc->sc_dev.dv_xname, usbd_errstr(error));
45517849d02Sdamien 		goto fail;
45617849d02Sdamien 	}
45717849d02Sdamien 
45817849d02Sdamien 	error = usbd_open_pipe(sc->sc_iface, 0x02, USBD_EXCLUSIVE_USE,
45917849d02Sdamien 	    &sc->data_tx_pipe);
46017849d02Sdamien 	if (error != 0) {
46117849d02Sdamien 		printf("%s: could not open Tx data pipe: %s\n",
4624ab2b9feSmbalmer 		    sc->sc_dev.dv_xname, usbd_errstr(error));
46317849d02Sdamien 		goto fail;
46417849d02Sdamien 	}
46517849d02Sdamien 
46617849d02Sdamien 	error = usbd_open_pipe(sc->sc_iface, 0x81, USBD_EXCLUSIVE_USE,
46717849d02Sdamien 	    &sc->cmd_rx_pipe);
46817849d02Sdamien 	if (error != 0) {
46917849d02Sdamien 		printf("%s: could not open Rx command pipe: %s\n",
4704ab2b9feSmbalmer 		    sc->sc_dev.dv_xname, usbd_errstr(error));
47117849d02Sdamien 		goto fail;
47217849d02Sdamien 	}
47317849d02Sdamien 
47417849d02Sdamien 	error = usbd_open_pipe(sc->sc_iface, 0x82, USBD_EXCLUSIVE_USE,
47517849d02Sdamien 	    &sc->data_rx_pipe);
47617849d02Sdamien 	if (error != 0) {
47717849d02Sdamien 		printf("%s: could not open Rx data pipe: %s\n",
4784ab2b9feSmbalmer 		    sc->sc_dev.dv_xname, usbd_errstr(error));
47917849d02Sdamien 		goto fail;
48017849d02Sdamien 	}
48117849d02Sdamien 
48217849d02Sdamien 	return 0;
48317849d02Sdamien 
48417849d02Sdamien fail:	uath_close_pipes(sc);
48517849d02Sdamien 	return error;
48617849d02Sdamien }
48717849d02Sdamien 
48878315254Smbalmer void
uath_close_pipes(struct uath_softc * sc)48917849d02Sdamien uath_close_pipes(struct uath_softc *sc)
49017849d02Sdamien {
4912dac2055Sstsp 	if (sc->data_tx_pipe != NULL) {
49217849d02Sdamien 		usbd_close_pipe(sc->data_tx_pipe);
4932dac2055Sstsp 		sc->data_tx_pipe = NULL;
4942dac2055Sstsp 	}
49517849d02Sdamien 
4962dac2055Sstsp 	if (sc->data_rx_pipe != NULL) {
49717849d02Sdamien 		usbd_close_pipe(sc->data_rx_pipe);
4982dac2055Sstsp 		sc->data_rx_pipe = NULL;
4992dac2055Sstsp 	}
50017849d02Sdamien 
5012dac2055Sstsp 	if (sc->cmd_tx_pipe != NULL) {
50217849d02Sdamien 		usbd_close_pipe(sc->cmd_tx_pipe);
5032dac2055Sstsp 		sc->cmd_tx_pipe = NULL;
5042dac2055Sstsp 	}
50517849d02Sdamien 
5062dac2055Sstsp 	if (sc->cmd_rx_pipe != NULL) {
50717849d02Sdamien 		usbd_close_pipe(sc->cmd_rx_pipe);
5082dac2055Sstsp 		sc->cmd_rx_pipe = NULL;
5092dac2055Sstsp 	}
51017849d02Sdamien }
51117849d02Sdamien 
51278315254Smbalmer int
uath_alloc_tx_data_list(struct uath_softc * sc)51317849d02Sdamien uath_alloc_tx_data_list(struct uath_softc *sc)
51417849d02Sdamien {
51517849d02Sdamien 	int i, error;
51617849d02Sdamien 
51717849d02Sdamien 	for (i = 0; i < UATH_TX_DATA_LIST_COUNT; i++) {
51817849d02Sdamien 		struct uath_tx_data *data = &sc->tx_data[i];
51917849d02Sdamien 
52017849d02Sdamien 		data->sc = sc;	/* backpointer for callbacks */
52117849d02Sdamien 
52217849d02Sdamien 		data->xfer = usbd_alloc_xfer(sc->sc_udev);
52317849d02Sdamien 		if (data->xfer == NULL) {
52417849d02Sdamien 			printf("%s: could not allocate xfer\n",
5254ab2b9feSmbalmer 			    sc->sc_dev.dv_xname);
52617849d02Sdamien 			error = ENOMEM;
52717849d02Sdamien 			goto fail;
52817849d02Sdamien 		}
52917849d02Sdamien 		data->buf = usbd_alloc_buffer(data->xfer, UATH_MAX_TXBUFSZ);
53017849d02Sdamien 		if (data->buf == NULL) {
53117849d02Sdamien 			printf("%s: could not allocate xfer buffer\n",
5324ab2b9feSmbalmer 			    sc->sc_dev.dv_xname);
53317849d02Sdamien 			error = ENOMEM;
53417849d02Sdamien 			goto fail;
53517849d02Sdamien 		}
53617849d02Sdamien 	}
53717849d02Sdamien 	return 0;
53817849d02Sdamien 
53917849d02Sdamien fail:	uath_free_tx_data_list(sc);
54017849d02Sdamien 	return error;
54117849d02Sdamien }
54217849d02Sdamien 
54378315254Smbalmer void
uath_free_tx_data_list(struct uath_softc * sc)54417849d02Sdamien uath_free_tx_data_list(struct uath_softc *sc)
54517849d02Sdamien {
54617849d02Sdamien 	int i;
54717849d02Sdamien 
54817849d02Sdamien 	for (i = 0; i < UATH_TX_DATA_LIST_COUNT; i++)
5492dac2055Sstsp 		if (sc->tx_data[i].xfer != NULL) {
55017849d02Sdamien 			usbd_free_xfer(sc->tx_data[i].xfer);
5512dac2055Sstsp 			sc->tx_data[i].xfer = NULL;
5522dac2055Sstsp 		}
55317849d02Sdamien }
55417849d02Sdamien 
55578315254Smbalmer int
uath_alloc_rx_data_list(struct uath_softc * sc)55617849d02Sdamien uath_alloc_rx_data_list(struct uath_softc *sc)
55717849d02Sdamien {
55817849d02Sdamien 	int i, error;
55917849d02Sdamien 
56074433b06Sdamien 	for (i = 0; i < UATH_RX_DATA_LIST_COUNT; i++) {
56117849d02Sdamien 		struct uath_rx_data *data = &sc->rx_data[i];
56217849d02Sdamien 
56317849d02Sdamien 		data->sc = sc;	/* backpointer for callbacks */
56417849d02Sdamien 
56517849d02Sdamien 		data->xfer = usbd_alloc_xfer(sc->sc_udev);
56617849d02Sdamien 		if (data->xfer == NULL) {
56717849d02Sdamien 			printf("%s: could not allocate xfer\n",
5684ab2b9feSmbalmer 			    sc->sc_dev.dv_xname);
56917849d02Sdamien 			error = ENOMEM;
57017849d02Sdamien 			goto fail;
57117849d02Sdamien 		}
57274433b06Sdamien 		if (usbd_alloc_buffer(data->xfer, sc->rxbufsz) == NULL) {
57317849d02Sdamien 			printf("%s: could not allocate xfer buffer\n",
5744ab2b9feSmbalmer 			    sc->sc_dev.dv_xname);
57517849d02Sdamien 			error = ENOMEM;
57617849d02Sdamien 			goto fail;
57717849d02Sdamien 		}
57874433b06Sdamien 
57974433b06Sdamien 		MGETHDR(data->m, M_DONTWAIT, MT_DATA);
58074433b06Sdamien 		if (data->m == NULL) {
58174433b06Sdamien 			printf("%s: could not allocate rx mbuf\n",
58274433b06Sdamien 			    sc->sc_dev.dv_xname);
58374433b06Sdamien 			error = ENOMEM;
58474433b06Sdamien 			goto fail;
58574433b06Sdamien 		}
586471f2571Sjan 		MCLGETL(data->m, M_DONTWAIT, sc->rxbufsz);
58774433b06Sdamien 		if (!(data->m->m_flags & M_EXT)) {
58874433b06Sdamien 			printf("%s: could not allocate rx mbuf cluster\n",
58974433b06Sdamien 			    sc->sc_dev.dv_xname);
59074433b06Sdamien 			error = ENOMEM;
59174433b06Sdamien 			goto fail;
59274433b06Sdamien 		}
59374433b06Sdamien 
59474433b06Sdamien 		data->buf = mtod(data->m, uint8_t *);
59517849d02Sdamien 	}
59617849d02Sdamien 	return 0;
59717849d02Sdamien 
59817849d02Sdamien fail:	uath_free_rx_data_list(sc);
59917849d02Sdamien 	return error;
60017849d02Sdamien }
60117849d02Sdamien 
60278315254Smbalmer void
uath_free_rx_data_list(struct uath_softc * sc)60317849d02Sdamien uath_free_rx_data_list(struct uath_softc *sc)
60417849d02Sdamien {
60517849d02Sdamien 	int i;
60617849d02Sdamien 
60774433b06Sdamien 	for (i = 0; i < UATH_RX_DATA_LIST_COUNT; i++) {
60874433b06Sdamien 		struct uath_rx_data *data = &sc->rx_data[i];
60974433b06Sdamien 
6102dac2055Sstsp 		if (data->xfer != NULL) {
61174433b06Sdamien 			usbd_free_xfer(data->xfer);
6122dac2055Sstsp 			data->xfer = NULL;
6132dac2055Sstsp 		}
61474433b06Sdamien 
6152dac2055Sstsp 		if (data->m != NULL) {
61674433b06Sdamien 			m_freem(data->m);
6172dac2055Sstsp 			data->m = NULL;
6182dac2055Sstsp 		}
61917849d02Sdamien 	}
62017849d02Sdamien }
62117849d02Sdamien 
62278315254Smbalmer int
uath_alloc_tx_cmd_list(struct uath_softc * sc)62317849d02Sdamien uath_alloc_tx_cmd_list(struct uath_softc *sc)
62417849d02Sdamien {
62517849d02Sdamien 	int i, error;
62617849d02Sdamien 
62717849d02Sdamien 	for (i = 0; i < UATH_TX_CMD_LIST_COUNT; i++) {
62817849d02Sdamien 		struct uath_tx_cmd *cmd = &sc->tx_cmd[i];
62917849d02Sdamien 
63017849d02Sdamien 		cmd->sc = sc;	/* backpointer for callbacks */
63117849d02Sdamien 
63217849d02Sdamien 		cmd->xfer = usbd_alloc_xfer(sc->sc_udev);
63317849d02Sdamien 		if (cmd->xfer == NULL) {
63417849d02Sdamien 			printf("%s: could not allocate xfer\n",
6354ab2b9feSmbalmer 			    sc->sc_dev.dv_xname);
63617849d02Sdamien 			error = ENOMEM;
63717849d02Sdamien 			goto fail;
63817849d02Sdamien 		}
63917849d02Sdamien 		cmd->buf = usbd_alloc_buffer(cmd->xfer, UATH_MAX_TXCMDSZ);
64017849d02Sdamien 		if (cmd->buf == NULL) {
64117849d02Sdamien 			printf("%s: could not allocate xfer buffer\n",
6424ab2b9feSmbalmer 			    sc->sc_dev.dv_xname);
64317849d02Sdamien 			error = ENOMEM;
64417849d02Sdamien 			goto fail;
64517849d02Sdamien 		}
64617849d02Sdamien 	}
64717849d02Sdamien 	return 0;
64817849d02Sdamien 
64917849d02Sdamien fail:	uath_free_tx_cmd_list(sc);
65017849d02Sdamien 	return error;
65117849d02Sdamien }
65217849d02Sdamien 
65378315254Smbalmer void
uath_free_tx_cmd_list(struct uath_softc * sc)65417849d02Sdamien uath_free_tx_cmd_list(struct uath_softc *sc)
65517849d02Sdamien {
65617849d02Sdamien 	int i;
65717849d02Sdamien 
65817849d02Sdamien 	for (i = 0; i < UATH_TX_CMD_LIST_COUNT; i++)
6592dac2055Sstsp 		if (sc->tx_cmd[i].xfer != NULL) {
66017849d02Sdamien 			usbd_free_xfer(sc->tx_cmd[i].xfer);
6612dac2055Sstsp 			sc->tx_cmd[i].xfer = NULL;
6622dac2055Sstsp 		}
66317849d02Sdamien }
66417849d02Sdamien 
66578315254Smbalmer int
uath_alloc_rx_cmd_list(struct uath_softc * sc)66617849d02Sdamien uath_alloc_rx_cmd_list(struct uath_softc *sc)
66717849d02Sdamien {
66817849d02Sdamien 	int i, error;
66917849d02Sdamien 
67017849d02Sdamien 	for (i = 0; i < UATH_RX_CMD_LIST_COUNT; i++) {
67117849d02Sdamien 		struct uath_rx_cmd *cmd = &sc->rx_cmd[i];
67217849d02Sdamien 
67317849d02Sdamien 		cmd->sc = sc;	/* backpointer for callbacks */
67417849d02Sdamien 
67517849d02Sdamien 		cmd->xfer = usbd_alloc_xfer(sc->sc_udev);
67617849d02Sdamien 		if (cmd->xfer == NULL) {
67717849d02Sdamien 			printf("%s: could not allocate xfer\n",
6784ab2b9feSmbalmer 			    sc->sc_dev.dv_xname);
67917849d02Sdamien 			error = ENOMEM;
68017849d02Sdamien 			goto fail;
68117849d02Sdamien 		}
68217849d02Sdamien 		cmd->buf = usbd_alloc_buffer(cmd->xfer, UATH_MAX_RXCMDSZ);
68317849d02Sdamien 		if (cmd->buf == NULL) {
68417849d02Sdamien 			printf("%s: could not allocate xfer buffer\n",
6854ab2b9feSmbalmer 			    sc->sc_dev.dv_xname);
68617849d02Sdamien 			error = ENOMEM;
68717849d02Sdamien 			goto fail;
68817849d02Sdamien 		}
68917849d02Sdamien 	}
69017849d02Sdamien 	return 0;
69117849d02Sdamien 
69217849d02Sdamien fail:	uath_free_rx_cmd_list(sc);
69317849d02Sdamien 	return error;
69417849d02Sdamien }
69517849d02Sdamien 
69678315254Smbalmer void
uath_free_rx_cmd_list(struct uath_softc * sc)69717849d02Sdamien uath_free_rx_cmd_list(struct uath_softc *sc)
69817849d02Sdamien {
69917849d02Sdamien 	int i;
70017849d02Sdamien 
70117849d02Sdamien 	for (i = 0; i < UATH_RX_CMD_LIST_COUNT; i++)
7022dac2055Sstsp 		if (sc->rx_cmd[i].xfer != NULL) {
70317849d02Sdamien 			usbd_free_xfer(sc->rx_cmd[i].xfer);
7042dac2055Sstsp 			sc->rx_cmd[i].xfer = NULL;
7052dac2055Sstsp 		}
70617849d02Sdamien }
70717849d02Sdamien 
70878315254Smbalmer int
uath_media_change(struct ifnet * ifp)70917849d02Sdamien uath_media_change(struct ifnet *ifp)
71017849d02Sdamien {
71117849d02Sdamien 	int error;
71217849d02Sdamien 
71317849d02Sdamien 	error = ieee80211_media_change(ifp);
71417849d02Sdamien 	if (error != ENETRESET)
71517849d02Sdamien 		return error;
71617849d02Sdamien 
71717849d02Sdamien 	if ((ifp->if_flags & (IFF_UP | IFF_RUNNING)) == (IFF_UP | IFF_RUNNING))
7182e342c84Skevlo 		error = uath_init(ifp);
71917849d02Sdamien 
7202e342c84Skevlo 	return error;
72117849d02Sdamien }
72217849d02Sdamien 
72317849d02Sdamien /*
72417849d02Sdamien  * This function is called periodically (every second) when associated to
72517849d02Sdamien  * query device statistics.
72617849d02Sdamien  */
72778315254Smbalmer void
uath_stat(void * arg)72817849d02Sdamien uath_stat(void *arg)
72917849d02Sdamien {
73017849d02Sdamien 	struct uath_softc *sc = arg;
73117849d02Sdamien 	int error;
73217849d02Sdamien 
73317849d02Sdamien 	/*
73417849d02Sdamien 	 * Send request for statistics asynchronously. The timer will be
73517849d02Sdamien 	 * restarted when we'll get the stats notification.
73617849d02Sdamien 	 */
73717849d02Sdamien 	error = uath_cmd_write(sc, UATH_CMD_STATS, NULL, 0,
73817849d02Sdamien 	    UATH_CMD_FLAG_ASYNC);
73917849d02Sdamien 	if (error != 0) {
74017849d02Sdamien 		printf("%s: could not query statistics (error=%d)\n",
7414ab2b9feSmbalmer 		    sc->sc_dev.dv_xname, error);
74217849d02Sdamien 	}
74317849d02Sdamien }
74417849d02Sdamien 
74517849d02Sdamien /*
74617849d02Sdamien  * This function is called periodically (every 250ms) during scanning to
74717849d02Sdamien  * switch from one channel to another.
74817849d02Sdamien  */
74978315254Smbalmer void
uath_next_scan(void * arg)75017849d02Sdamien uath_next_scan(void *arg)
75117849d02Sdamien {
75217849d02Sdamien 	struct uath_softc *sc = arg;
75317849d02Sdamien 	struct ieee80211com *ic = &sc->sc_ic;
75417849d02Sdamien 	struct ifnet *ifp = &ic->ic_if;
75517849d02Sdamien 
75617849d02Sdamien 	if (ic->ic_state == IEEE80211_S_SCAN)
75717849d02Sdamien 		ieee80211_next_scan(ifp);
75817849d02Sdamien }
75917849d02Sdamien 
76078315254Smbalmer void
uath_task(void * arg)76117849d02Sdamien uath_task(void *arg)
76217849d02Sdamien {
76317849d02Sdamien 	struct uath_softc *sc = arg;
76417849d02Sdamien 	struct ieee80211com *ic = &sc->sc_ic;
76517849d02Sdamien 	enum ieee80211_state ostate;
76617849d02Sdamien 
76717849d02Sdamien 	ostate = ic->ic_state;
76817849d02Sdamien 
76917849d02Sdamien 	switch (sc->sc_state) {
77017849d02Sdamien 	case IEEE80211_S_INIT:
77117849d02Sdamien 		if (ostate == IEEE80211_S_RUN) {
77217849d02Sdamien 			/* turn link and activity LEDs off */
77317849d02Sdamien 			(void)uath_set_led(sc, UATH_LED_LINK, 0);
77417849d02Sdamien 			(void)uath_set_led(sc, UATH_LED_ACTIVITY, 0);
77517849d02Sdamien 		}
77617849d02Sdamien 		break;
77717849d02Sdamien 
77817849d02Sdamien 	case IEEE80211_S_SCAN:
77917849d02Sdamien 		if (uath_switch_channel(sc, ic->ic_bss->ni_chan) != 0) {
78017849d02Sdamien 			printf("%s: could not switch channel\n",
7814ab2b9feSmbalmer 			    sc->sc_dev.dv_xname);
78217849d02Sdamien 			break;
78317849d02Sdamien 		}
78436d45870Sblambert 		timeout_add_msec(&sc->scan_to, 250);
78517849d02Sdamien 		break;
78617849d02Sdamien 
78717849d02Sdamien 	case IEEE80211_S_AUTH:
78817849d02Sdamien 	{
78917849d02Sdamien 		struct ieee80211_node *ni = ic->ic_bss;
79017849d02Sdamien 		struct uath_cmd_bssid bssid;
79117849d02Sdamien 		struct uath_cmd_0b cmd0b;
79217849d02Sdamien 		struct uath_cmd_0c cmd0c;
79317849d02Sdamien 
79417849d02Sdamien 		if (uath_switch_channel(sc, ni->ni_chan) != 0) {
79517849d02Sdamien 			printf("%s: could not switch channel\n",
7964ab2b9feSmbalmer 			    sc->sc_dev.dv_xname);
79717849d02Sdamien 			break;
79817849d02Sdamien 		}
79917849d02Sdamien 
80017849d02Sdamien 		(void)uath_cmd_write(sc, UATH_CMD_24, NULL, 0, 0);
80117849d02Sdamien 
80217849d02Sdamien 		bzero(&bssid, sizeof bssid);
80317849d02Sdamien 		bssid.len = htobe32(IEEE80211_ADDR_LEN);
80417849d02Sdamien 		IEEE80211_ADDR_COPY(bssid.bssid, ni->ni_bssid);
80517849d02Sdamien 		(void)uath_cmd_write(sc, UATH_CMD_SET_BSSID, &bssid,
80617849d02Sdamien 		    sizeof bssid, 0);
80717849d02Sdamien 
80817849d02Sdamien 		bzero(&cmd0b, sizeof cmd0b);
80917849d02Sdamien 		cmd0b.code = htobe32(2);
81017849d02Sdamien 		cmd0b.size = htobe32(sizeof (cmd0b.data));
81117849d02Sdamien 		(void)uath_cmd_write(sc, UATH_CMD_0B, &cmd0b, sizeof cmd0b, 0);
81217849d02Sdamien 
81317849d02Sdamien 		bzero(&cmd0c, sizeof cmd0c);
81417849d02Sdamien 		cmd0c.magic1 = htobe32(2);
81517849d02Sdamien 		cmd0c.magic2 = htobe32(7);
81617849d02Sdamien 		cmd0c.magic3 = htobe32(1);
81717849d02Sdamien 		(void)uath_cmd_write(sc, UATH_CMD_0C, &cmd0c, sizeof cmd0c, 0);
81817849d02Sdamien 
81917849d02Sdamien 		if (uath_set_rates(sc, &ni->ni_rates) != 0) {
82017849d02Sdamien 			printf("%s: could not set negotiated rate set\n",
8214ab2b9feSmbalmer 			    sc->sc_dev.dv_xname);
82217849d02Sdamien 			break;
82317849d02Sdamien 		}
82417849d02Sdamien 		break;
82517849d02Sdamien 	}
82617849d02Sdamien 
82717849d02Sdamien 	case IEEE80211_S_ASSOC:
82817849d02Sdamien 		break;
82917849d02Sdamien 
83017849d02Sdamien 	case IEEE80211_S_RUN:
83117849d02Sdamien 	{
83217849d02Sdamien 		struct ieee80211_node *ni = ic->ic_bss;
83317849d02Sdamien 		struct uath_cmd_bssid bssid;
83417849d02Sdamien 		struct uath_cmd_xled xled;
83517849d02Sdamien 		uint32_t val;
83617849d02Sdamien 
83717849d02Sdamien 		if (ic->ic_opmode == IEEE80211_M_MONITOR) {
83817849d02Sdamien 			/* make both LEDs blink while monitoring */
83917849d02Sdamien 			bzero(&xled, sizeof xled);
84017849d02Sdamien 			xled.which = htobe32(0);
84117849d02Sdamien 			xled.rate = htobe32(1);
84217849d02Sdamien 			xled.mode = htobe32(2);
84317849d02Sdamien 			(void)uath_cmd_write(sc, UATH_CMD_SET_XLED, &xled,
84417849d02Sdamien 			    sizeof xled, 0);
84517849d02Sdamien 			break;
84617849d02Sdamien 		}
84717849d02Sdamien 
84817849d02Sdamien 		/*
84917849d02Sdamien 		 * Tx rate is controlled by firmware, report the maximum
85017849d02Sdamien 		 * negotiated rate in ifconfig output.
85117849d02Sdamien 		 */
85217849d02Sdamien 		ni->ni_txrate = ni->ni_rates.rs_nrates - 1;
85317849d02Sdamien 
85417849d02Sdamien 		val = htobe32(1);
85517849d02Sdamien 		(void)uath_cmd_write(sc, UATH_CMD_2E, &val, sizeof val, 0);
85617849d02Sdamien 
85717849d02Sdamien 		bzero(&bssid, sizeof bssid);
85817849d02Sdamien 		bssid.flags1 = htobe32(0xc004);
85917849d02Sdamien 		bssid.flags2 = htobe32(0x003b);
86017849d02Sdamien 		bssid.len = htobe32(IEEE80211_ADDR_LEN);
86117849d02Sdamien 		IEEE80211_ADDR_COPY(bssid.bssid, ni->ni_bssid);
86217849d02Sdamien 		(void)uath_cmd_write(sc, UATH_CMD_SET_BSSID, &bssid,
86317849d02Sdamien 		    sizeof bssid, 0);
86417849d02Sdamien 
86517849d02Sdamien 		/* turn link LED on */
86617849d02Sdamien 		(void)uath_set_led(sc, UATH_LED_LINK, 1);
86717849d02Sdamien 
86817849d02Sdamien 		/* make activity LED blink */
86917849d02Sdamien 		bzero(&xled, sizeof xled);
87017849d02Sdamien 		xled.which = htobe32(1);
87117849d02Sdamien 		xled.rate = htobe32(1);
87217849d02Sdamien 		xled.mode = htobe32(2);
87317849d02Sdamien 		(void)uath_cmd_write(sc, UATH_CMD_SET_XLED, &xled, sizeof xled,
87417849d02Sdamien 		    0);
87517849d02Sdamien 
87617849d02Sdamien 		/* set state to associated */
87717849d02Sdamien 		val = htobe32(1);
87817849d02Sdamien 		(void)uath_cmd_write(sc, UATH_CMD_SET_STATE, &val, sizeof val,
87917849d02Sdamien 		    0);
88017849d02Sdamien 
88117849d02Sdamien 		/* start statistics timer */
88229e86e5eSblambert 		timeout_add_sec(&sc->stat_to, 1);
88317849d02Sdamien 		break;
88417849d02Sdamien 	}
88517849d02Sdamien 	}
88617849d02Sdamien 	sc->sc_newstate(ic, sc->sc_state, sc->sc_arg);
88717849d02Sdamien }
88817849d02Sdamien 
88978315254Smbalmer int
uath_newstate(struct ieee80211com * ic,enum ieee80211_state nstate,int arg)89017849d02Sdamien uath_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg)
89117849d02Sdamien {
89217849d02Sdamien 	struct uath_softc *sc = ic->ic_softc;
89317849d02Sdamien 
89417849d02Sdamien 	usb_rem_task(sc->sc_udev, &sc->sc_task);
89517849d02Sdamien 	timeout_del(&sc->scan_to);
89617849d02Sdamien 	timeout_del(&sc->stat_to);
89717849d02Sdamien 
898d267521fSderaadt 	/* do it in a process context */
89917849d02Sdamien 	sc->sc_state = nstate;
90017849d02Sdamien 	sc->sc_arg = arg;
90117849d02Sdamien 	usb_add_task(sc->sc_udev, &sc->sc_task);
90217849d02Sdamien 	return 0;
90317849d02Sdamien }
90417849d02Sdamien 
90517849d02Sdamien #ifdef UATH_DEBUG
90678315254Smbalmer void
uath_dump_cmd(const uint8_t * buf,int len,char prefix)90717849d02Sdamien uath_dump_cmd(const uint8_t *buf, int len, char prefix)
90817849d02Sdamien {
90917849d02Sdamien 	int i;
91017849d02Sdamien 
91117849d02Sdamien 	for (i = 0; i < len; i++) {
91217849d02Sdamien 		if ((i % 16) == 0)
91317849d02Sdamien 			printf("\n%c ", prefix);
91417849d02Sdamien 		else if ((i % 4) == 0)
91517849d02Sdamien 			printf(" ");
91617849d02Sdamien 		printf("%02x", buf[i]);
91717849d02Sdamien 	}
91817849d02Sdamien 	printf("\n");
91917849d02Sdamien }
92017849d02Sdamien #endif
92117849d02Sdamien 
92217849d02Sdamien /*
92317849d02Sdamien  * Low-level function to send read or write commands to the firmware.
92417849d02Sdamien  */
92578315254Smbalmer int
uath_cmd(struct uath_softc * sc,uint32_t code,const void * idata,int ilen,void * odata,int flags)92617849d02Sdamien uath_cmd(struct uath_softc *sc, uint32_t code, const void *idata, int ilen,
92717849d02Sdamien     void *odata, int flags)
92817849d02Sdamien {
92917849d02Sdamien 	struct uath_cmd_hdr *hdr;
93017849d02Sdamien 	struct uath_tx_cmd *cmd;
93117849d02Sdamien 	uint16_t xferflags;
93217849d02Sdamien 	int s, xferlen, error;
93317849d02Sdamien 
93417849d02Sdamien 	/* grab a xfer */
93517849d02Sdamien 	cmd = &sc->tx_cmd[sc->cmd_idx];
93617849d02Sdamien 
93717849d02Sdamien 	/* always bulk-out a multiple of 4 bytes */
93817849d02Sdamien 	xferlen = (sizeof (struct uath_cmd_hdr) + ilen + 3) & ~3;
93917849d02Sdamien 
94017849d02Sdamien 	hdr = (struct uath_cmd_hdr *)cmd->buf;
94117849d02Sdamien 	bzero(hdr, sizeof (struct uath_cmd_hdr));
94217849d02Sdamien 	hdr->len   = htobe32(xferlen);
94317849d02Sdamien 	hdr->code  = htobe32(code);
94417849d02Sdamien 	hdr->priv  = sc->cmd_idx;	/* don't care about endianness */
94517849d02Sdamien 	hdr->magic = htobe32((flags & UATH_CMD_FLAG_MAGIC) ? 1 << 24 : 0);
94617849d02Sdamien 	bcopy(idata, (uint8_t *)(hdr + 1), ilen);
94717849d02Sdamien 
94817849d02Sdamien #ifdef UATH_DEBUG
94917849d02Sdamien 	if (uath_debug >= 5) {
95017849d02Sdamien 		printf("sending command code=0x%02x flags=0x%x index=%u",
95117849d02Sdamien 		    code, flags, sc->cmd_idx);
95217849d02Sdamien 		uath_dump_cmd(cmd->buf, xferlen, '+');
95317849d02Sdamien 	}
95417849d02Sdamien #endif
95517849d02Sdamien 	xferflags = USBD_FORCE_SHORT_XFER | USBD_NO_COPY;
95617849d02Sdamien 	if (!(flags & UATH_CMD_FLAG_READ)) {
95717849d02Sdamien 		if (!(flags & UATH_CMD_FLAG_ASYNC))
95817849d02Sdamien 			xferflags |= USBD_SYNCHRONOUS;
95917849d02Sdamien 	} else
96017849d02Sdamien 		s = splusb();
96117849d02Sdamien 
96217849d02Sdamien 	cmd->odata = odata;
96317849d02Sdamien 
96417849d02Sdamien 	usbd_setup_xfer(cmd->xfer, sc->cmd_tx_pipe, cmd, cmd->buf, xferlen,
96517849d02Sdamien 	    xferflags, UATH_CMD_TIMEOUT, NULL);
96617849d02Sdamien 	error = usbd_transfer(cmd->xfer);
96717849d02Sdamien 	if (error != USBD_IN_PROGRESS && error != 0) {
96817849d02Sdamien 		if (flags & UATH_CMD_FLAG_READ)
96917849d02Sdamien 			splx(s);
9706a54674fSdamien 		printf("%s: could not send command 0x%x (error=%s)\n",
9714ab2b9feSmbalmer 		    sc->sc_dev.dv_xname, code, usbd_errstr(error));
97217849d02Sdamien 		return error;
97317849d02Sdamien 	}
97417849d02Sdamien 	sc->cmd_idx = (sc->cmd_idx + 1) % UATH_TX_CMD_LIST_COUNT;
97517849d02Sdamien 
97617849d02Sdamien 	if (!(flags & UATH_CMD_FLAG_READ))
97717849d02Sdamien 		return 0;	/* write: don't wait for reply */
97817849d02Sdamien 
97917849d02Sdamien 	/* wait at most two seconds for command reply */
98000f6cb32Smpi 	error = tsleep_nsec(cmd, PCATCH, "uathcmd", SEC_TO_NSEC(2));
9819b11f1a4Sdamien 	cmd->odata = NULL;	/* in case answer is received too late */
98217849d02Sdamien 	splx(s);
98317849d02Sdamien 	if (error != 0) {
98417849d02Sdamien 		printf("%s: timeout waiting for command reply\n",
9854ab2b9feSmbalmer 		    sc->sc_dev.dv_xname);
98617849d02Sdamien 	}
98717849d02Sdamien 	return error;
98817849d02Sdamien }
98917849d02Sdamien 
99078315254Smbalmer int
uath_cmd_write(struct uath_softc * sc,uint32_t code,const void * data,int len,int flags)99117849d02Sdamien uath_cmd_write(struct uath_softc *sc, uint32_t code, const void *data, int len,
99217849d02Sdamien     int flags)
99317849d02Sdamien {
99417849d02Sdamien 	flags &= ~UATH_CMD_FLAG_READ;
99517849d02Sdamien 	return uath_cmd(sc, code, data, len, NULL, flags);
99617849d02Sdamien }
99717849d02Sdamien 
99878315254Smbalmer int
uath_cmd_read(struct uath_softc * sc,uint32_t code,const void * idata,int ilen,void * odata,int flags)99917849d02Sdamien uath_cmd_read(struct uath_softc *sc, uint32_t code, const void *idata,
100017849d02Sdamien     int ilen, void *odata, int flags)
100117849d02Sdamien {
100217849d02Sdamien 	flags |= UATH_CMD_FLAG_READ;
100317849d02Sdamien 	return uath_cmd(sc, code, idata, ilen, odata, flags);
100417849d02Sdamien }
100517849d02Sdamien 
100678315254Smbalmer int
uath_write_reg(struct uath_softc * sc,uint32_t reg,uint32_t val)100717849d02Sdamien uath_write_reg(struct uath_softc *sc, uint32_t reg, uint32_t val)
100817849d02Sdamien {
100917849d02Sdamien 	struct uath_write_mac write;
101017849d02Sdamien 	int error;
101117849d02Sdamien 
101217849d02Sdamien 	write.reg = htobe32(reg);
101317849d02Sdamien 	write.len = htobe32(0);	/* 0 = single write */
101417849d02Sdamien 	*(uint32_t *)write.data = htobe32(val);
101517849d02Sdamien 
101617849d02Sdamien 	error = uath_cmd_write(sc, UATH_CMD_WRITE_MAC, &write,
101717849d02Sdamien 	    3 * sizeof (uint32_t), 0);
101817849d02Sdamien 	if (error != 0) {
101917849d02Sdamien 		printf("%s: could not write register 0x%02x\n",
10204ab2b9feSmbalmer 		    sc->sc_dev.dv_xname, reg);
102117849d02Sdamien 	}
102217849d02Sdamien 	return error;
102317849d02Sdamien }
102417849d02Sdamien 
102578315254Smbalmer int
uath_write_multi(struct uath_softc * sc,uint32_t reg,const void * data,int len)102617849d02Sdamien uath_write_multi(struct uath_softc *sc, uint32_t reg, const void *data,
102717849d02Sdamien     int len)
102817849d02Sdamien {
102917849d02Sdamien 	struct uath_write_mac write;
103017849d02Sdamien 	int error;
103117849d02Sdamien 
103217849d02Sdamien 	write.reg = htobe32(reg);
103317849d02Sdamien 	write.len = htobe32(len);
103417849d02Sdamien 	bcopy(data, write.data, len);
103517849d02Sdamien 
103617849d02Sdamien 	/* properly handle the case where len is zero (reset) */
103717849d02Sdamien 	error = uath_cmd_write(sc, UATH_CMD_WRITE_MAC, &write,
103817849d02Sdamien 	    (len == 0) ? sizeof (uint32_t) : 2 * sizeof (uint32_t) + len, 0);
103917849d02Sdamien 	if (error != 0) {
104017849d02Sdamien 		printf("%s: could not write %d bytes to register 0x%02x\n",
10414ab2b9feSmbalmer 		    sc->sc_dev.dv_xname, len, reg);
104217849d02Sdamien 	}
104317849d02Sdamien 	return error;
104417849d02Sdamien }
104517849d02Sdamien 
104678315254Smbalmer int
uath_read_reg(struct uath_softc * sc,uint32_t reg,uint32_t * val)104717849d02Sdamien uath_read_reg(struct uath_softc *sc, uint32_t reg, uint32_t *val)
104817849d02Sdamien {
104917849d02Sdamien 	struct uath_read_mac read;
105017849d02Sdamien 	int error;
105117849d02Sdamien 
105217849d02Sdamien 	reg = htobe32(reg);
105317849d02Sdamien 	error = uath_cmd_read(sc, UATH_CMD_READ_MAC, &reg, sizeof reg, &read,
105417849d02Sdamien 	    0);
105517849d02Sdamien 	if (error != 0) {
105617849d02Sdamien 		printf("%s: could not read register 0x%02x\n",
10574ab2b9feSmbalmer 		    sc->sc_dev.dv_xname, betoh32(reg));
105817849d02Sdamien 		return error;
105917849d02Sdamien 	}
106017849d02Sdamien 	*val = betoh32(*(uint32_t *)read.data);
106117849d02Sdamien 	return error;
106217849d02Sdamien }
106317849d02Sdamien 
106478315254Smbalmer int
uath_read_eeprom(struct uath_softc * sc,uint32_t reg,void * odata)106517849d02Sdamien uath_read_eeprom(struct uath_softc *sc, uint32_t reg, void *odata)
106617849d02Sdamien {
106717849d02Sdamien 	struct uath_read_mac read;
106817849d02Sdamien 	int len, error;
106917849d02Sdamien 
107017849d02Sdamien 	reg = htobe32(reg);
107117849d02Sdamien 	error = uath_cmd_read(sc, UATH_CMD_READ_EEPROM, &reg, sizeof reg,
107217849d02Sdamien 	    &read, 0);
107317849d02Sdamien 	if (error != 0) {
107417849d02Sdamien 		printf("%s: could not read EEPROM offset 0x%02x\n",
10754ab2b9feSmbalmer 		    sc->sc_dev.dv_xname, betoh32(reg));
107617849d02Sdamien 		return error;
107717849d02Sdamien 	}
107817849d02Sdamien 	len = betoh32(read.len);
107917849d02Sdamien 	bcopy(read.data, odata, (len == 0) ? sizeof (uint32_t) : len);
108017849d02Sdamien 	return error;
108117849d02Sdamien }
108217849d02Sdamien 
108378315254Smbalmer void
uath_cmd_rxeof(struct usbd_xfer * xfer,void * priv,usbd_status status)1084ab0b1be7Smglocker uath_cmd_rxeof(struct usbd_xfer *xfer, void *priv,
108517849d02Sdamien     usbd_status status)
108617849d02Sdamien {
108717849d02Sdamien 	struct uath_rx_cmd *cmd = priv;
108817849d02Sdamien 	struct uath_softc *sc = cmd->sc;
108917849d02Sdamien 	struct uath_cmd_hdr *hdr;
109017849d02Sdamien 
109117849d02Sdamien 	if (status != USBD_NORMAL_COMPLETION) {
109217849d02Sdamien 		if (status == USBD_STALLED)
109317849d02Sdamien 			usbd_clear_endpoint_stall_async(sc->cmd_rx_pipe);
109417849d02Sdamien 		return;
109517849d02Sdamien 	}
109617849d02Sdamien 
109717849d02Sdamien 	hdr = (struct uath_cmd_hdr *)cmd->buf;
109817849d02Sdamien 
109917849d02Sdamien #ifdef UATH_DEBUG
110017849d02Sdamien 	if (uath_debug >= 5) {
110117849d02Sdamien 		printf("received command code=0x%x index=%u len=%u",
110217849d02Sdamien 		    betoh32(hdr->code), hdr->priv, betoh32(hdr->len));
110317849d02Sdamien 		uath_dump_cmd(cmd->buf, betoh32(hdr->len), '-');
110417849d02Sdamien 	}
110517849d02Sdamien #endif
110617849d02Sdamien 
110717849d02Sdamien 	switch (betoh32(hdr->code) & 0xff) {
110817849d02Sdamien 	/* reply to a read command */
110917849d02Sdamien 	default:
111017849d02Sdamien 	{
111117849d02Sdamien 		struct uath_tx_cmd *txcmd = &sc->tx_cmd[hdr->priv];
111217849d02Sdamien 
111317849d02Sdamien 		if (txcmd->odata != NULL) {
111417849d02Sdamien 			/* copy answer into caller's supplied buffer */
111517849d02Sdamien 			bcopy((uint8_t *)(hdr + 1), txcmd->odata,
111617849d02Sdamien 			    betoh32(hdr->len) - sizeof (struct uath_cmd_hdr));
111717849d02Sdamien 		}
111817849d02Sdamien 		wakeup(txcmd);	/* wake up caller */
111917849d02Sdamien 		break;
112017849d02Sdamien 	}
112117849d02Sdamien 	/* spontaneous firmware notifications */
112217849d02Sdamien 	case UATH_NOTIF_READY:
112317849d02Sdamien 		DPRINTF(("received device ready notification\n"));
112417849d02Sdamien 		wakeup(UATH_COND_INIT(sc));
112517849d02Sdamien 		break;
112617849d02Sdamien 
112717849d02Sdamien 	case UATH_NOTIF_TX:
112817849d02Sdamien 		/* this notification is sent when UATH_TX_NOTIFY is set */
112917849d02Sdamien 		DPRINTF(("received Tx notification\n"));
113017849d02Sdamien 		break;
113117849d02Sdamien 
113217849d02Sdamien 	case UATH_NOTIF_STATS:
113317849d02Sdamien 		DPRINTFN(2, ("received device statistics\n"));
113429e86e5eSblambert 		timeout_add_sec(&sc->stat_to, 1);
113517849d02Sdamien 		break;
113617849d02Sdamien 	}
113717849d02Sdamien 
113817849d02Sdamien 	/* setup a new transfer */
113917849d02Sdamien 	usbd_setup_xfer(xfer, sc->cmd_rx_pipe, cmd, cmd->buf, UATH_MAX_RXCMDSZ,
114017849d02Sdamien 	    USBD_SHORT_XFER_OK | USBD_NO_COPY, USBD_NO_TIMEOUT,
114117849d02Sdamien 	    uath_cmd_rxeof);
114217849d02Sdamien 	(void)usbd_transfer(xfer);
114317849d02Sdamien }
114417849d02Sdamien 
114578315254Smbalmer void
uath_data_rxeof(struct usbd_xfer * xfer,void * priv,usbd_status status)1146ab0b1be7Smglocker uath_data_rxeof(struct usbd_xfer *xfer, void *priv,
114717849d02Sdamien     usbd_status status)
114817849d02Sdamien {
114917849d02Sdamien 	struct uath_rx_data *data = priv;
115017849d02Sdamien 	struct uath_softc *sc = data->sc;
115117849d02Sdamien 	struct ieee80211com *ic = &sc->sc_ic;
115217849d02Sdamien 	struct ifnet *ifp = &ic->ic_if;
115317849d02Sdamien 	struct ieee80211_frame *wh;
115430d05aacSdamien 	struct ieee80211_rxinfo rxi;
115517849d02Sdamien 	struct ieee80211_node *ni;
115617849d02Sdamien 	struct uath_rx_desc *desc;
115774433b06Sdamien 	struct mbuf *mnew, *m;
115817849d02Sdamien 	uint32_t hdr;
115917849d02Sdamien 	int s, len;
116017849d02Sdamien 
116117849d02Sdamien 	if (status != USBD_NORMAL_COMPLETION) {
116217849d02Sdamien 		if (status == USBD_NOT_STARTED || status == USBD_CANCELLED)
116317849d02Sdamien 			return;
116417849d02Sdamien 
116517849d02Sdamien 		if (status == USBD_STALLED)
116617849d02Sdamien 			usbd_clear_endpoint_stall_async(sc->data_rx_pipe);
116717849d02Sdamien 
116817849d02Sdamien 		ifp->if_ierrors++;
116917849d02Sdamien 		return;
117017849d02Sdamien 	}
117117849d02Sdamien 	usbd_get_xfer_status(xfer, NULL, NULL, &len, NULL);
117217849d02Sdamien 
1173f276d25eSdamien 	if (len < UATH_MIN_RXBUFSZ) {
1174f276d25eSdamien 		DPRINTF(("wrong xfer size (len=%d)\n", len));
117517849d02Sdamien 		ifp->if_ierrors++;
117617849d02Sdamien 		goto skip;
117717849d02Sdamien 	}
117817849d02Sdamien 
117917849d02Sdamien 	hdr = betoh32(*(uint32_t *)data->buf);
118017849d02Sdamien 
118117849d02Sdamien 	/* Rx descriptor is located at the end, 32-bit aligned */
118217849d02Sdamien 	desc = (struct uath_rx_desc *)
118317849d02Sdamien 	    (data->buf + len - sizeof (struct uath_rx_desc));
118417849d02Sdamien 
1185f276d25eSdamien 	if (betoh32(desc->len) > sc->rxbufsz) {
1186f276d25eSdamien 		DPRINTF(("bad descriptor (len=%d)\n", betoh32(desc->len)));
1187f276d25eSdamien 		ifp->if_ierrors++;
1188f276d25eSdamien 		goto skip;
1189f276d25eSdamien 	}
1190f276d25eSdamien 
119117849d02Sdamien 	/* there's probably a "bad CRC" flag somewhere in the descriptor.. */
119217849d02Sdamien 
119374433b06Sdamien 	MGETHDR(mnew, M_DONTWAIT, MT_DATA);
119474433b06Sdamien 	if (mnew == NULL) {
119574433b06Sdamien 		printf("%s: could not allocate rx mbuf\n",
11964ab2b9feSmbalmer 		    sc->sc_dev.dv_xname);
11979b11f1a4Sdamien 		ifp->if_ierrors++;
11989b11f1a4Sdamien 		goto skip;
11999b11f1a4Sdamien 	}
1200471f2571Sjan 	MCLGETL(mnew, M_DONTWAIT, sc->rxbufsz);
120174433b06Sdamien 	if (!(mnew->m_flags & M_EXT)) {
120274433b06Sdamien 		printf("%s: could not allocate rx mbuf cluster\n",
120374433b06Sdamien 		    sc->sc_dev.dv_xname);
120474433b06Sdamien 		m_freem(mnew);
120574433b06Sdamien 		ifp->if_ierrors++;
120674433b06Sdamien 		goto skip;
120774433b06Sdamien 	}
12089b11f1a4Sdamien 
120974433b06Sdamien 	m = data->m;
121074433b06Sdamien 	data->m = mnew;
121117849d02Sdamien 
121217849d02Sdamien 	/* finalize mbuf */
121317849d02Sdamien 	m->m_data = data->buf + sizeof (uint32_t);
1214c875518fSdamien 	m->m_pkthdr.len = m->m_len = betoh32(desc->len) -
1215c875518fSdamien 	    sizeof (struct uath_rx_desc) - IEEE80211_CRC_LEN;
121617849d02Sdamien 
121774433b06Sdamien 	data->buf = mtod(data->m, uint8_t *);
121817849d02Sdamien 
121917849d02Sdamien 	wh = mtod(m, struct ieee80211_frame *);
122052a13037Sstsp 	memset(&rxi, 0, sizeof(rxi));
122117849d02Sdamien 	if ((wh->i_fc[1] & IEEE80211_FC1_WEP) &&
122217849d02Sdamien 	    ic->ic_opmode != IEEE80211_M_MONITOR) {
122317849d02Sdamien 		/*
122417849d02Sdamien 		 * Hardware decrypts the frame itself but leaves the WEP bit
122517849d02Sdamien 		 * set in the 802.11 header and doesn't remove the IV and CRC
122617849d02Sdamien 		 * fields.
122717849d02Sdamien 		 */
122817849d02Sdamien 		wh->i_fc[1] &= ~IEEE80211_FC1_WEP;
12299b2ad23fSderaadt 		memmove((caddr_t)wh + IEEE80211_WEP_IVLEN +
12309b2ad23fSderaadt 		    IEEE80211_WEP_KIDLEN, wh, sizeof (struct ieee80211_frame));
123117849d02Sdamien 		m_adj(m, IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN);
123217849d02Sdamien 		m_adj(m, -IEEE80211_WEP_CRCLEN);
123317849d02Sdamien 		wh = mtod(m, struct ieee80211_frame *);
123430d05aacSdamien 
123530d05aacSdamien 		rxi.rxi_flags |= IEEE80211_RXI_HWDEC;
123617849d02Sdamien 	}
123717849d02Sdamien 
123817849d02Sdamien #if NBPFILTER > 0
123917849d02Sdamien 	/* there are a lot more fields in the Rx descriptor */
124017849d02Sdamien 	if (sc->sc_drvbpf != NULL) {
124117849d02Sdamien 		struct mbuf mb;
124217849d02Sdamien 		struct uath_rx_radiotap_header *tap = &sc->sc_rxtap;
124317849d02Sdamien 
124417849d02Sdamien 		tap->wr_flags = 0;
124517849d02Sdamien 		tap->wr_chan_freq = htole16(betoh32(desc->freq));
124617849d02Sdamien 		tap->wr_chan_flags = htole16(ic->ic_bss->ni_chan->ic_flags);
124717849d02Sdamien 		tap->wr_dbm_antsignal = (int8_t)betoh32(desc->rssi);
124817849d02Sdamien 
124917849d02Sdamien 		mb.m_data = (caddr_t)tap;
125017849d02Sdamien 		mb.m_len = sc->sc_rxtap_len;
125117849d02Sdamien 		mb.m_next = m;
1252dd2e8b02Sclaudio 		mb.m_nextpkt = NULL;
1253dd2e8b02Sclaudio 		mb.m_type = 0;
1254dd2e8b02Sclaudio 		mb.m_flags = 0;
125517849d02Sdamien 		bpf_mtap(sc->sc_drvbpf, &mb, BPF_DIRECTION_IN);
125617849d02Sdamien 	}
125717849d02Sdamien #endif
125817849d02Sdamien 
125917849d02Sdamien 	s = splnet();
126017849d02Sdamien 	ni = ieee80211_find_rxnode(ic, wh);
126130d05aacSdamien 	rxi.rxi_rssi = (int)betoh32(desc->rssi);
126230d05aacSdamien 	ieee80211_input(ifp, m, ni, &rxi);
126317849d02Sdamien 
126417849d02Sdamien 	/* node is no longer needed */
126517849d02Sdamien 	ieee80211_release_node(ic, ni);
126617849d02Sdamien 	splx(s);
126717849d02Sdamien 
126817849d02Sdamien skip:	/* setup a new transfer */
126974433b06Sdamien 	usbd_setup_xfer(xfer, sc->data_rx_pipe, data, data->buf, sc->rxbufsz,
127074433b06Sdamien 	    USBD_SHORT_XFER_OK, USBD_NO_TIMEOUT, uath_data_rxeof);
12719b11f1a4Sdamien 	(void)usbd_transfer(data->xfer);
127217849d02Sdamien }
127317849d02Sdamien 
127478315254Smbalmer int
uath_tx_null(struct uath_softc * sc)127517849d02Sdamien uath_tx_null(struct uath_softc *sc)
127617849d02Sdamien {
127717849d02Sdamien 	struct uath_tx_data *data;
127817849d02Sdamien 	struct uath_tx_desc *desc;
127917849d02Sdamien 
128017849d02Sdamien 	data = &sc->tx_data[sc->data_idx];
128117849d02Sdamien 
128217849d02Sdamien 	data->ni = NULL;
128317849d02Sdamien 
128417849d02Sdamien 	*(uint32_t *)data->buf = UATH_MAKECTL(1, sizeof (struct uath_tx_desc));
128517849d02Sdamien 	desc = (struct uath_tx_desc *)(data->buf + sizeof (uint32_t));
128617849d02Sdamien 
128717849d02Sdamien 	bzero(desc, sizeof (struct uath_tx_desc));
128817849d02Sdamien 	desc->len  = htobe32(sizeof (struct uath_tx_desc));
128917849d02Sdamien 	desc->type = htobe32(UATH_TX_NULL);
129017849d02Sdamien 
129117849d02Sdamien 	usbd_setup_xfer(data->xfer, sc->data_tx_pipe, data, data->buf,
129217849d02Sdamien 	    sizeof (uint32_t) + sizeof (struct uath_tx_desc), USBD_NO_COPY |
1293aa88c704Smpi 	    USBD_FORCE_SHORT_XFER | USBD_SYNCHRONOUS, UATH_DATA_TIMEOUT, NULL);
1294aa88c704Smpi 	if (usbd_transfer(data->xfer) != 0)
129517849d02Sdamien 		return EIO;
129617849d02Sdamien 
129717849d02Sdamien 	sc->data_idx = (sc->data_idx + 1) % UATH_TX_DATA_LIST_COUNT;
129817849d02Sdamien 
129917849d02Sdamien 	return uath_cmd_write(sc, UATH_CMD_0F, NULL, 0, UATH_CMD_FLAG_ASYNC);
130017849d02Sdamien }
130117849d02Sdamien 
130278315254Smbalmer void
uath_data_txeof(struct usbd_xfer * xfer,void * priv,usbd_status status)1303ab0b1be7Smglocker uath_data_txeof(struct usbd_xfer *xfer, void *priv,
130417849d02Sdamien     usbd_status status)
130517849d02Sdamien {
130617849d02Sdamien 	struct uath_tx_data *data = priv;
130717849d02Sdamien 	struct uath_softc *sc = data->sc;
130817849d02Sdamien 	struct ieee80211com *ic = &sc->sc_ic;
130917849d02Sdamien 	struct ifnet *ifp = &ic->ic_if;
131017849d02Sdamien 	int s;
131117849d02Sdamien 
131217849d02Sdamien 	if (status != USBD_NORMAL_COMPLETION) {
131317849d02Sdamien 		if (status == USBD_NOT_STARTED || status == USBD_CANCELLED)
131417849d02Sdamien 			return;
131517849d02Sdamien 
131617849d02Sdamien 		printf("%s: could not transmit buffer: %s\n",
13174ab2b9feSmbalmer 		    sc->sc_dev.dv_xname, usbd_errstr(status));
131817849d02Sdamien 
131917849d02Sdamien 		if (status == USBD_STALLED)
132017849d02Sdamien 			usbd_clear_endpoint_stall_async(sc->data_tx_pipe);
132117849d02Sdamien 
132217849d02Sdamien 		ifp->if_oerrors++;
132317849d02Sdamien 		return;
132417849d02Sdamien 	}
132517849d02Sdamien 
132617849d02Sdamien 	s = splnet();
132717849d02Sdamien 
132817849d02Sdamien 	ieee80211_release_node(ic, data->ni);
132917849d02Sdamien 	data->ni = NULL;
133017849d02Sdamien 
133117849d02Sdamien 	sc->tx_queued--;
133217849d02Sdamien 
133317849d02Sdamien 	sc->sc_tx_timer = 0;
1334de6cd8fbSdlg 	ifq_clr_oactive(&ifp->if_snd);
133517849d02Sdamien 	uath_start(ifp);
133617849d02Sdamien 
133717849d02Sdamien 	splx(s);
133817849d02Sdamien }
133917849d02Sdamien 
134078315254Smbalmer int
uath_tx_data(struct uath_softc * sc,struct mbuf * m0,struct ieee80211_node * ni)134117849d02Sdamien uath_tx_data(struct uath_softc *sc, struct mbuf *m0, struct ieee80211_node *ni)
134217849d02Sdamien {
134317849d02Sdamien 	struct ieee80211com *ic = &sc->sc_ic;
134417849d02Sdamien 	struct uath_tx_data *data;
134517849d02Sdamien 	struct uath_tx_desc *desc;
134617849d02Sdamien 	const struct ieee80211_frame *wh;
134717849d02Sdamien 	int paylen, totlen, xferlen, error;
134817849d02Sdamien 
134917849d02Sdamien 	data = &sc->tx_data[sc->data_idx];
135017849d02Sdamien 	desc = (struct uath_tx_desc *)(data->buf + sizeof (uint32_t));
135117849d02Sdamien 
135217849d02Sdamien 	data->ni = ni;
135317849d02Sdamien 
135417849d02Sdamien #if NBPFILTER > 0
135517849d02Sdamien 	if (sc->sc_drvbpf != NULL) {
135617849d02Sdamien 		struct mbuf mb;
135717849d02Sdamien 		struct uath_tx_radiotap_header *tap = &sc->sc_txtap;
135817849d02Sdamien 
135917849d02Sdamien 		tap->wt_flags = 0;
136017849d02Sdamien 		tap->wt_chan_freq = htole16(ic->ic_bss->ni_chan->ic_freq);
136117849d02Sdamien 		tap->wt_chan_flags = htole16(ic->ic_bss->ni_chan->ic_flags);
136217849d02Sdamien 
136317849d02Sdamien 		mb.m_data = (caddr_t)tap;
136417849d02Sdamien 		mb.m_len = sc->sc_txtap_len;
136517849d02Sdamien 		mb.m_next = m0;
1366dd2e8b02Sclaudio 		mb.m_nextpkt = NULL;
1367dd2e8b02Sclaudio 		mb.m_type = 0;
1368dd2e8b02Sclaudio 		mb.m_flags = 0;
136917849d02Sdamien 		bpf_mtap(sc->sc_drvbpf, &mb, BPF_DIRECTION_OUT);
137017849d02Sdamien 	}
137117849d02Sdamien #endif
137217849d02Sdamien 
137317849d02Sdamien 	paylen = m0->m_pkthdr.len;
137417849d02Sdamien 	xferlen = sizeof (uint32_t) + sizeof (struct uath_tx_desc) + paylen;
137517849d02Sdamien 
137617849d02Sdamien 	wh = mtod(m0, struct ieee80211_frame *);
137717849d02Sdamien 	if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
137817849d02Sdamien 		uint8_t *frm = (uint8_t *)(desc + 1);
137917849d02Sdamien 		uint32_t iv;
138017849d02Sdamien 
138117849d02Sdamien 		/* h/w WEP: it's up to the host to fill the IV field */
138217849d02Sdamien 		bcopy(wh, frm, sizeof (struct ieee80211_frame));
138317849d02Sdamien 		frm += sizeof (struct ieee80211_frame);
138417849d02Sdamien 
138517849d02Sdamien 		/* insert IV: code copied from net80211 */
138617849d02Sdamien 		iv = (ic->ic_iv != 0) ? ic->ic_iv : arc4random();
138717849d02Sdamien 		if (iv >= 0x03ff00 && (iv & 0xf8ff00) == 0x00ff00)
138817849d02Sdamien 			iv += 0x000100;
138917849d02Sdamien 		ic->ic_iv = iv + 1;
139017849d02Sdamien 
139117849d02Sdamien 		*frm++ = iv & 0xff;
139217849d02Sdamien 		*frm++ = (iv >>  8) & 0xff;
139317849d02Sdamien 		*frm++ = (iv >> 16) & 0xff;
139417849d02Sdamien 		*frm++ = ic->ic_wep_txkey << 6;
139517849d02Sdamien 
139617849d02Sdamien 		m_copydata(m0, sizeof(struct ieee80211_frame),
139717849d02Sdamien 		    m0->m_pkthdr.len - sizeof(struct ieee80211_frame), frm);
139817849d02Sdamien 
139917849d02Sdamien 		paylen  += IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN;
140017849d02Sdamien 		xferlen += IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN;
140117849d02Sdamien 		totlen = xferlen + IEEE80211_WEP_CRCLEN;
140217849d02Sdamien 	} else {
14035c7fed39Sdlg 		m_copydata(m0, 0, m0->m_pkthdr.len, desc + 1);
140417849d02Sdamien 		totlen = xferlen;
140517849d02Sdamien 	}
140617849d02Sdamien 
140717849d02Sdamien 	/* fill Tx descriptor */
140817849d02Sdamien 	*(uint32_t *)data->buf = UATH_MAKECTL(1, xferlen - sizeof (uint32_t));
140917849d02Sdamien 
141017849d02Sdamien 	desc->len    = htobe32(totlen);
141117849d02Sdamien 	desc->priv   = sc->data_idx;	/* don't care about endianness */
141217849d02Sdamien 	desc->paylen = htobe32(paylen);
141317849d02Sdamien 	desc->type   = htobe32(UATH_TX_DATA);
141417849d02Sdamien 	desc->flags  = htobe32(0);
141517849d02Sdamien 	if (IEEE80211_IS_MULTICAST(wh->i_addr1)) {
141617849d02Sdamien 		desc->dest  = htobe32(UATH_ID_BROADCAST);
141717849d02Sdamien 		desc->magic = htobe32(3);
141817849d02Sdamien 	} else {
141917849d02Sdamien 		desc->dest  = htobe32(UATH_ID_BSS);
142017849d02Sdamien 		desc->magic = htobe32(1);
142117849d02Sdamien 	}
142217849d02Sdamien 
142317849d02Sdamien 	m_freem(m0);	/* mbuf is no longer needed */
142417849d02Sdamien 
142517849d02Sdamien #ifdef UATH_DEBUG
142617849d02Sdamien 	if (uath_debug >= 6) {
142717849d02Sdamien 		printf("sending frame index=%u len=%d xferlen=%d",
142817849d02Sdamien 		    sc->data_idx, paylen, xferlen);
142917849d02Sdamien 		uath_dump_cmd(data->buf, xferlen, '+');
143017849d02Sdamien 	}
143117849d02Sdamien #endif
143217849d02Sdamien 	usbd_setup_xfer(data->xfer, sc->data_tx_pipe, data, data->buf, xferlen,
143317849d02Sdamien 	    USBD_FORCE_SHORT_XFER | USBD_NO_COPY, UATH_DATA_TIMEOUT,
143417849d02Sdamien 	    uath_data_txeof);
143517849d02Sdamien 	error = usbd_transfer(data->xfer);
143617849d02Sdamien 	if (error != USBD_IN_PROGRESS && error != 0) {
143717849d02Sdamien 		ic->ic_if.if_oerrors++;
143817849d02Sdamien 		return error;
143917849d02Sdamien 	}
144017849d02Sdamien 	sc->data_idx = (sc->data_idx + 1) % UATH_TX_DATA_LIST_COUNT;
144117849d02Sdamien 	sc->tx_queued++;
144217849d02Sdamien 
144317849d02Sdamien 	return 0;
144417849d02Sdamien }
144517849d02Sdamien 
144678315254Smbalmer void
uath_start(struct ifnet * ifp)144717849d02Sdamien uath_start(struct ifnet *ifp)
144817849d02Sdamien {
144917849d02Sdamien 	struct uath_softc *sc = ifp->if_softc;
145017849d02Sdamien 	struct ieee80211com *ic = &sc->sc_ic;
145117849d02Sdamien 	struct ieee80211_node *ni;
145217849d02Sdamien 	struct mbuf *m0;
145317849d02Sdamien 
145417849d02Sdamien 	/*
145517849d02Sdamien 	 * net80211 may still try to send management frames even if the
145617849d02Sdamien 	 * IFF_RUNNING flag is not set...
145717849d02Sdamien 	 */
1458de6cd8fbSdlg 	if (!(ifp->if_flags & IFF_RUNNING) && ifq_is_oactive(&ifp->if_snd))
145917849d02Sdamien 		return;
146017849d02Sdamien 
146117849d02Sdamien 	for (;;) {
146217849d02Sdamien 		if (sc->tx_queued >= UATH_TX_DATA_LIST_COUNT) {
1463de6cd8fbSdlg 			ifq_set_oactive(&ifp->if_snd);
146417849d02Sdamien 			break;
146517849d02Sdamien 		}
146617849d02Sdamien 
146732a12f8bSmpi 		m0 = mq_dequeue(&ic->ic_mgtq);
146832a12f8bSmpi 		if (m0 != NULL) {
14696da4b19dSmpi 			ni = m0->m_pkthdr.ph_cookie;
147017849d02Sdamien #if NBPFILTER > 0
147117849d02Sdamien 			if (ic->ic_rawbpf != NULL)
147217849d02Sdamien 				bpf_mtap(ic->ic_rawbpf, m0, BPF_DIRECTION_OUT);
147317849d02Sdamien #endif
147417849d02Sdamien 			if (uath_tx_data(sc, m0, ni) != 0)
147517849d02Sdamien 				break;
147617849d02Sdamien 		} else {
147717849d02Sdamien 			if (ic->ic_state != IEEE80211_S_RUN)
147817849d02Sdamien 				break;
147932a12f8bSmpi 
148063bcfa73Spatrick 			m0 = ifq_dequeue(&ifp->if_snd);
148117849d02Sdamien 			if (m0 == NULL)
148217849d02Sdamien 				break;
148317849d02Sdamien #if NBPFILTER > 0
148417849d02Sdamien 			if (ifp->if_bpf != NULL)
148517849d02Sdamien 				bpf_mtap(ifp->if_bpf, m0, BPF_DIRECTION_OUT);
148617849d02Sdamien #endif
148717849d02Sdamien 			m0 = ieee80211_encap(ifp, m0, &ni);
148817849d02Sdamien 			if (m0 == NULL)
148917849d02Sdamien 				continue;
149017849d02Sdamien #if NBPFILTER > 0
149117849d02Sdamien 			if (ic->ic_rawbpf != NULL)
149217849d02Sdamien 				bpf_mtap(ic->ic_rawbpf, m0, BPF_DIRECTION_OUT);
149317849d02Sdamien #endif
149417849d02Sdamien 			if (uath_tx_data(sc, m0, ni) != 0) {
149517849d02Sdamien 				if (ni != NULL)
149617849d02Sdamien 					ieee80211_release_node(ic, ni);
149717849d02Sdamien 				ifp->if_oerrors++;
149817849d02Sdamien 				break;
149917849d02Sdamien 			}
150017849d02Sdamien 		}
150117849d02Sdamien 
150217849d02Sdamien 		sc->sc_tx_timer = 5;
150317849d02Sdamien 		ifp->if_timer = 1;
150417849d02Sdamien 	}
150517849d02Sdamien }
150617849d02Sdamien 
150778315254Smbalmer void
uath_watchdog(struct ifnet * ifp)150817849d02Sdamien uath_watchdog(struct ifnet *ifp)
150917849d02Sdamien {
151017849d02Sdamien 	struct uath_softc *sc = ifp->if_softc;
151117849d02Sdamien 
151217849d02Sdamien 	ifp->if_timer = 0;
151317849d02Sdamien 
151417849d02Sdamien 	if (sc->sc_tx_timer > 0) {
151517849d02Sdamien 		if (--sc->sc_tx_timer == 0) {
15164ab2b9feSmbalmer 			printf("%s: device timeout\n", sc->sc_dev.dv_xname);
151717849d02Sdamien 			/*uath_init(ifp); XXX needs a process context! */
151817849d02Sdamien 			ifp->if_oerrors++;
151917849d02Sdamien 			return;
152017849d02Sdamien 		}
152117849d02Sdamien 		ifp->if_timer = 1;
152217849d02Sdamien 	}
152317849d02Sdamien 
152417849d02Sdamien 	ieee80211_watchdog(ifp);
152517849d02Sdamien }
152617849d02Sdamien 
152778315254Smbalmer int
uath_ioctl(struct ifnet * ifp,u_long cmd,caddr_t data)152817849d02Sdamien uath_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
152917849d02Sdamien {
153017849d02Sdamien 	int s, error = 0;
153117849d02Sdamien 
153217849d02Sdamien 	s = splnet();
153317849d02Sdamien 
153417849d02Sdamien 	switch (cmd) {
153517849d02Sdamien 	case SIOCSIFADDR:
153617849d02Sdamien 		ifp->if_flags |= IFF_UP;
153717849d02Sdamien 		/* FALLTHROUGH */
153817849d02Sdamien 	case SIOCSIFFLAGS:
153917849d02Sdamien 		if (ifp->if_flags & IFF_UP) {
154017849d02Sdamien 			if (!(ifp->if_flags & IFF_RUNNING))
154117849d02Sdamien 				uath_init(ifp);
154217849d02Sdamien 		} else {
154317849d02Sdamien 			if (ifp->if_flags & IFF_RUNNING)
154417849d02Sdamien 				uath_stop(ifp, 1);
154517849d02Sdamien 		}
154617849d02Sdamien 		break;
154717849d02Sdamien 
154817849d02Sdamien 	default:
154917849d02Sdamien 		error = ieee80211_ioctl(ifp, cmd, data);
155017849d02Sdamien 	}
155117849d02Sdamien 
155217849d02Sdamien 	if (error == ENETRESET) {
155317849d02Sdamien 		if ((ifp->if_flags & (IFF_UP | IFF_RUNNING)) ==
155417849d02Sdamien 		    (IFF_UP | IFF_RUNNING))
155517849d02Sdamien 			uath_init(ifp);
155617849d02Sdamien 		error = 0;
155717849d02Sdamien 	}
155817849d02Sdamien 
155917849d02Sdamien 	splx(s);
156017849d02Sdamien 
156117849d02Sdamien 	return error;
156217849d02Sdamien }
156317849d02Sdamien 
156478315254Smbalmer int
uath_query_eeprom(struct uath_softc * sc)156517849d02Sdamien uath_query_eeprom(struct uath_softc *sc)
156617849d02Sdamien {
156717849d02Sdamien 	uint32_t tmp;
156817849d02Sdamien 	int error;
156917849d02Sdamien 
157017849d02Sdamien 	/* retrieve MAC address */
157117849d02Sdamien 	error = uath_read_eeprom(sc, UATH_EEPROM_MACADDR, sc->sc_ic.ic_myaddr);
157217849d02Sdamien 	if (error != 0) {
157317849d02Sdamien 		printf("%s: could not read MAC address\n",
15744ab2b9feSmbalmer 		    sc->sc_dev.dv_xname);
157517849d02Sdamien 		return error;
157617849d02Sdamien 	}
157717849d02Sdamien 
157817849d02Sdamien 	/* retrieve the maximum frame size that the hardware can receive */
157917849d02Sdamien 	error = uath_read_eeprom(sc, UATH_EEPROM_RXBUFSZ, &tmp);
158017849d02Sdamien 	if (error != 0) {
158117849d02Sdamien 		printf("%s: could not read maximum Rx buffer size\n",
15824ab2b9feSmbalmer 		    sc->sc_dev.dv_xname);
158317849d02Sdamien 		return error;
158417849d02Sdamien 	}
158517849d02Sdamien 	sc->rxbufsz = betoh32(tmp) & 0xfff;
158617849d02Sdamien 	DPRINTF(("maximum Rx buffer size %d\n", sc->rxbufsz));
158717849d02Sdamien 	return 0;
158817849d02Sdamien }
158917849d02Sdamien 
159078315254Smbalmer int
uath_reset(struct uath_softc * sc)159117849d02Sdamien uath_reset(struct uath_softc *sc)
159217849d02Sdamien {
159317849d02Sdamien 	struct uath_cmd_setup setup;
159417849d02Sdamien 	uint32_t reg, val;
159517849d02Sdamien 	int s, error;
159617849d02Sdamien 
159717849d02Sdamien 	/* init device with some voodoo incantations.. */
159817849d02Sdamien 	setup.magic1 = htobe32(1);
159917849d02Sdamien 	setup.magic2 = htobe32(5);
160017849d02Sdamien 	setup.magic3 = htobe32(200);
160117849d02Sdamien 	setup.magic4 = htobe32(27);
160217849d02Sdamien 	s = splusb();
160317849d02Sdamien 	error = uath_cmd_write(sc, UATH_CMD_SETUP, &setup, sizeof setup,
160417849d02Sdamien 	    UATH_CMD_FLAG_ASYNC);
160517849d02Sdamien 	/* ..and wait until firmware notifies us that it is ready */
160617849d02Sdamien 	if (error == 0)
160700f6cb32Smpi 		error = tsleep_nsec(UATH_COND_INIT(sc), PCATCH, "uathinit",
160800f6cb32Smpi 		    SEC_TO_NSEC(5));
160917849d02Sdamien 	splx(s);
16102bfb63f7Sdamien 	if (error != 0)
16112bfb63f7Sdamien 		return error;
161217849d02Sdamien 
161317849d02Sdamien 	/* read PHY registers */
161417849d02Sdamien 	for (reg = 0x09; reg <= 0x24; reg++) {
161517849d02Sdamien 		if (reg == 0x0b || reg == 0x0c)
161617849d02Sdamien 			continue;
16176a54674fSdamien 		DELAY(100);
161817849d02Sdamien 		if ((error = uath_read_reg(sc, reg, &val)) != 0)
161917849d02Sdamien 			return error;
162017849d02Sdamien 		DPRINTFN(2, ("reg 0x%02x=0x%08x\n", reg, val));
162117849d02Sdamien 	}
162217849d02Sdamien 	return error;
162317849d02Sdamien }
162417849d02Sdamien 
162578315254Smbalmer int
uath_reset_tx_queues(struct uath_softc * sc)162617849d02Sdamien uath_reset_tx_queues(struct uath_softc *sc)
162717849d02Sdamien {
162817849d02Sdamien 	int ac, error;
162917849d02Sdamien 
163017849d02Sdamien 	for (ac = 0; ac < 4; ac++) {
163117849d02Sdamien 		const uint32_t qid = htobe32(UATH_AC_TO_QID(ac));
163217849d02Sdamien 
163317849d02Sdamien 		DPRINTF(("resetting Tx queue %d\n", UATH_AC_TO_QID(ac)));
163417849d02Sdamien 		error = uath_cmd_write(sc, UATH_CMD_RESET_QUEUE, &qid,
163517849d02Sdamien 		    sizeof qid, 0);
163617849d02Sdamien 		if (error != 0)
163717849d02Sdamien 			break;
163817849d02Sdamien 	}
163917849d02Sdamien 	return error;
164017849d02Sdamien }
164117849d02Sdamien 
164278315254Smbalmer int
uath_wme_init(struct uath_softc * sc)164317849d02Sdamien uath_wme_init(struct uath_softc *sc)
164417849d02Sdamien {
164517849d02Sdamien 	struct uath_qinfo qinfo;
164617849d02Sdamien 	int ac, error;
164717849d02Sdamien 	static const struct uath_wme_settings uath_wme_11g[4] = {
164817849d02Sdamien 		{ 7, 4, 10,  0, 0 },	/* Background */
164917849d02Sdamien 		{ 3, 4, 10,  0, 0 },	/* Best-Effort */
165017849d02Sdamien 		{ 3, 3,  4, 26, 0 },	/* Video */
165117849d02Sdamien 		{ 2, 2,  3, 47, 0 }	/* Voice */
165217849d02Sdamien 	};
165317849d02Sdamien 
165417849d02Sdamien 	bzero(&qinfo, sizeof qinfo);
165517849d02Sdamien 	qinfo.size   = htobe32(32);
165617849d02Sdamien 	qinfo.magic1 = htobe32(1);	/* XXX ack policy? */
165717849d02Sdamien 	qinfo.magic2 = htobe32(1);
165817849d02Sdamien 	for (ac = 0; ac < 4; ac++) {
165917849d02Sdamien 		qinfo.qid      = htobe32(UATH_AC_TO_QID(ac));
166017849d02Sdamien 		qinfo.ac       = htobe32(ac);
166117849d02Sdamien 		qinfo.aifsn    = htobe32(uath_wme_11g[ac].aifsn);
166217849d02Sdamien 		qinfo.logcwmin = htobe32(uath_wme_11g[ac].logcwmin);
166317849d02Sdamien 		qinfo.logcwmax = htobe32(uath_wme_11g[ac].logcwmax);
166417849d02Sdamien 		qinfo.txop     = htobe32(UATH_TXOP_TO_US(
166517849d02Sdamien 				     uath_wme_11g[ac].txop));
166617849d02Sdamien 		qinfo.acm      = htobe32(uath_wme_11g[ac].acm);
166717849d02Sdamien 
166817849d02Sdamien 		DPRINTF(("setting up Tx queue %d\n", UATH_AC_TO_QID(ac)));
166917849d02Sdamien 		error = uath_cmd_write(sc, UATH_CMD_SET_QUEUE, &qinfo,
167017849d02Sdamien 		    sizeof qinfo, 0);
167117849d02Sdamien 		if (error != 0)
167217849d02Sdamien 			break;
167317849d02Sdamien 	}
167417849d02Sdamien 	return error;
167517849d02Sdamien }
167617849d02Sdamien 
167778315254Smbalmer int
uath_set_chan(struct uath_softc * sc,struct ieee80211_channel * c)167817849d02Sdamien uath_set_chan(struct uath_softc *sc, struct ieee80211_channel *c)
167917849d02Sdamien {
168017849d02Sdamien 	struct uath_set_chan chan;
168117849d02Sdamien 
168217849d02Sdamien 	bzero(&chan, sizeof chan);
168317849d02Sdamien 	chan.flags  = htobe32(0x1400);
168417849d02Sdamien 	chan.freq   = htobe32(c->ic_freq);
168517849d02Sdamien 	chan.magic1 = htobe32(20);
168617849d02Sdamien 	chan.magic2 = htobe32(50);
168717849d02Sdamien 	chan.magic3 = htobe32(1);
168817849d02Sdamien 
16890d8d0da2Sdamien 	DPRINTF(("switching to channel %d\n",
16900d8d0da2Sdamien 	    ieee80211_chan2ieee(&sc->sc_ic, c)));
169117849d02Sdamien 	return uath_cmd_write(sc, UATH_CMD_SET_CHAN, &chan, sizeof chan, 0);
169217849d02Sdamien }
169317849d02Sdamien 
169478315254Smbalmer int
uath_set_key(struct uath_softc * sc,const struct ieee80211_key * k,int index)169583da4af0Sdamien uath_set_key(struct uath_softc *sc, const struct ieee80211_key *k, int index)
169617849d02Sdamien {
169717849d02Sdamien 	struct uath_cmd_crypto crypto;
169817849d02Sdamien 	int i;
169917849d02Sdamien 
170017849d02Sdamien 	bzero(&crypto, sizeof crypto);
170117849d02Sdamien 	crypto.keyidx = htobe32(index);
170217849d02Sdamien 	crypto.magic1 = htobe32(1);
170317849d02Sdamien 	crypto.size   = htobe32(368);
170417849d02Sdamien 	crypto.mask   = htobe32(0xffff);
170517849d02Sdamien 	crypto.flags  = htobe32(0x80000068);
170617849d02Sdamien 	if (index != UATH_DEFAULT_KEY)
170717849d02Sdamien 		crypto.flags |= htobe32(index << 16);
170817849d02Sdamien 	memset(crypto.magic2, 0xff, sizeof crypto.magic2);
170917849d02Sdamien 
171017849d02Sdamien 	/*
171117849d02Sdamien 	 * Each byte of the key must be XOR'ed with 10101010 before being
171217849d02Sdamien 	 * transmitted to the firmware.
171317849d02Sdamien 	 */
171483da4af0Sdamien 	for (i = 0; i < k->k_len; i++)
171583da4af0Sdamien 		crypto.key[i] = k->k_key[i] ^ 0xaa;
171617849d02Sdamien 
171783da4af0Sdamien 	DPRINTF(("setting crypto key index=%d len=%d\n", index, k->k_len));
171817849d02Sdamien 	return uath_cmd_write(sc, UATH_CMD_CRYPTO, &crypto, sizeof crypto, 0);
171917849d02Sdamien }
172017849d02Sdamien 
172178315254Smbalmer int
uath_set_keys(struct uath_softc * sc)172217849d02Sdamien uath_set_keys(struct uath_softc *sc)
172317849d02Sdamien {
172417849d02Sdamien 	const struct ieee80211com *ic = &sc->sc_ic;
172517849d02Sdamien 	int i, error;
172617849d02Sdamien 
172717849d02Sdamien 	for (i = 0; i < IEEE80211_WEP_NKID; i++) {
172883da4af0Sdamien 		const struct ieee80211_key *k = &ic->ic_nw_keys[i];
172917849d02Sdamien 
173083da4af0Sdamien 		if (k->k_len > 0 && (error = uath_set_key(sc, k, i)) != 0)
173117849d02Sdamien 			return error;
173217849d02Sdamien 	}
173317849d02Sdamien 	return uath_set_key(sc, &ic->ic_nw_keys[ic->ic_wep_txkey],
173417849d02Sdamien 	    UATH_DEFAULT_KEY);
173517849d02Sdamien }
173617849d02Sdamien 
173778315254Smbalmer int
uath_set_rates(struct uath_softc * sc,const struct ieee80211_rateset * rs)173817849d02Sdamien uath_set_rates(struct uath_softc *sc, const struct ieee80211_rateset *rs)
173917849d02Sdamien {
174017849d02Sdamien 	struct uath_cmd_rates rates;
174117849d02Sdamien 
174217849d02Sdamien 	bzero(&rates, sizeof rates);
174317849d02Sdamien 	rates.magic1 = htobe32(0x02);
17449b11f1a4Sdamien 	rates.size   = htobe32(1 + sizeof rates.rates);
174517849d02Sdamien 	rates.nrates = rs->rs_nrates;
174617849d02Sdamien 	bcopy(rs->rs_rates, rates.rates, rs->rs_nrates);
174717849d02Sdamien 
174817849d02Sdamien 	DPRINTF(("setting supported rates nrates=%d\n", rs->rs_nrates));
17490d8d0da2Sdamien 	return uath_cmd_write(sc, UATH_CMD_SET_RATES, &rates, sizeof rates, 0);
175017849d02Sdamien }
175117849d02Sdamien 
175278315254Smbalmer int
uath_set_rxfilter(struct uath_softc * sc,uint32_t filter,uint32_t flags)175317849d02Sdamien uath_set_rxfilter(struct uath_softc *sc, uint32_t filter, uint32_t flags)
175417849d02Sdamien {
175517849d02Sdamien 	struct uath_cmd_filter rxfilter;
175617849d02Sdamien 
175717849d02Sdamien 	rxfilter.filter = htobe32(filter);
175817849d02Sdamien 	rxfilter.flags  = htobe32(flags);
175917849d02Sdamien 
176017849d02Sdamien 	DPRINTF(("setting Rx filter=0x%x flags=0x%x\n", filter, flags));
176117849d02Sdamien 	return uath_cmd_write(sc, UATH_CMD_SET_FILTER, &rxfilter,
176217849d02Sdamien 	    sizeof rxfilter, 0);
176317849d02Sdamien }
176417849d02Sdamien 
176578315254Smbalmer int
uath_set_led(struct uath_softc * sc,int which,int on)176617849d02Sdamien uath_set_led(struct uath_softc *sc, int which, int on)
176717849d02Sdamien {
176817849d02Sdamien 	struct uath_cmd_led led;
176917849d02Sdamien 
177017849d02Sdamien 	led.which = htobe32(which);
177117849d02Sdamien 	led.state = htobe32(on ? UATH_LED_ON : UATH_LED_OFF);
177217849d02Sdamien 
177317849d02Sdamien 	DPRINTFN(2, ("switching %s led %s\n",
177417849d02Sdamien 	    (which == UATH_LED_LINK) ? "link" : "activity",
177517849d02Sdamien 	    on ? "on" : "off"));
177617849d02Sdamien 	return uath_cmd_write(sc, UATH_CMD_SET_LED, &led, sizeof led, 0);
177717849d02Sdamien }
177817849d02Sdamien 
177978315254Smbalmer int
uath_switch_channel(struct uath_softc * sc,struct ieee80211_channel * c)178017849d02Sdamien uath_switch_channel(struct uath_softc *sc, struct ieee80211_channel *c)
178117849d02Sdamien {
178217849d02Sdamien 	uint32_t val;
178317849d02Sdamien 	int error;
178417849d02Sdamien 
178517849d02Sdamien 	/* set radio frequency */
178617849d02Sdamien 	if ((error = uath_set_chan(sc, c)) != 0) {
17874ab2b9feSmbalmer 		printf("%s: could not set channel\n", sc->sc_dev.dv_xname);
178817849d02Sdamien 		return error;
178917849d02Sdamien 	}
179017849d02Sdamien 
179117849d02Sdamien 	/* reset Tx rings */
179217849d02Sdamien 	if ((error = uath_reset_tx_queues(sc)) != 0) {
179317849d02Sdamien 		printf("%s: could not reset Tx queues\n",
17944ab2b9feSmbalmer 		    sc->sc_dev.dv_xname);
179517849d02Sdamien 		return error;
179617849d02Sdamien 	}
179717849d02Sdamien 
179817849d02Sdamien 	/* set Tx rings WME properties */
179917849d02Sdamien 	if ((error = uath_wme_init(sc)) != 0) {
180017849d02Sdamien 		printf("%s: could not init Tx queues\n",
18014ab2b9feSmbalmer 		    sc->sc_dev.dv_xname);
180217849d02Sdamien 		return error;
180317849d02Sdamien 	}
180417849d02Sdamien 
180517849d02Sdamien 	val = htobe32(0);
180617849d02Sdamien 	error = uath_cmd_write(sc, UATH_CMD_SET_STATE, &val, sizeof val, 0);
180717849d02Sdamien 	if (error != 0) {
18084ab2b9feSmbalmer 		printf("%s: could not set state\n", sc->sc_dev.dv_xname);
180917849d02Sdamien 		return error;
181017849d02Sdamien 	}
181117849d02Sdamien 
181217849d02Sdamien 	return uath_tx_null(sc);
181317849d02Sdamien }
181417849d02Sdamien 
181578315254Smbalmer int
uath_init(struct ifnet * ifp)181617849d02Sdamien uath_init(struct ifnet *ifp)
181717849d02Sdamien {
181817849d02Sdamien 	struct uath_softc *sc = ifp->if_softc;
181917849d02Sdamien 	struct ieee80211com *ic = &sc->sc_ic;
182017849d02Sdamien 	struct uath_cmd_31 cmd31;
1821d03627b6Sdamien 	uint32_t val;
182217849d02Sdamien 	int i, error;
182317849d02Sdamien 
182417849d02Sdamien 	/* reset data and command rings */
182517849d02Sdamien 	sc->tx_queued = sc->data_idx = sc->cmd_idx = 0;
182617849d02Sdamien 
182717849d02Sdamien 	val = htobe32(0);
182817849d02Sdamien 	(void)uath_cmd_write(sc, UATH_CMD_02, &val, sizeof val, 0);
182917849d02Sdamien 
183017849d02Sdamien 	/* set MAC address */
183117849d02Sdamien 	IEEE80211_ADDR_COPY(ic->ic_myaddr, LLADDR(ifp->if_sadl));
183217849d02Sdamien 	(void)uath_write_multi(sc, 0x13, ic->ic_myaddr, IEEE80211_ADDR_LEN);
183317849d02Sdamien 
183417849d02Sdamien 	(void)uath_write_reg(sc, 0x02, 0x00000001);
183517849d02Sdamien 	(void)uath_write_reg(sc, 0x0e, 0x0000003f);
183617849d02Sdamien 	(void)uath_write_reg(sc, 0x10, 0x00000001);
183717849d02Sdamien 	(void)uath_write_reg(sc, 0x06, 0x0000001e);
183817849d02Sdamien 
183917849d02Sdamien 	/*
184017849d02Sdamien 	 * Queue Rx data xfers.
184117849d02Sdamien 	 */
184217849d02Sdamien 	for (i = 0; i < UATH_RX_DATA_LIST_COUNT; i++) {
184374433b06Sdamien 		struct uath_rx_data *data = &sc->rx_data[i];
184417849d02Sdamien 
184517849d02Sdamien 		usbd_setup_xfer(data->xfer, sc->data_rx_pipe, data, data->buf,
184674433b06Sdamien 		    sc->rxbufsz, USBD_SHORT_XFER_OK, USBD_NO_TIMEOUT,
184774433b06Sdamien 		    uath_data_rxeof);
184817849d02Sdamien 		error = usbd_transfer(data->xfer);
184917849d02Sdamien 		if (error != USBD_IN_PROGRESS && error != 0) {
185017849d02Sdamien 			printf("%s: could not queue Rx transfer\n",
18514ab2b9feSmbalmer 			    sc->sc_dev.dv_xname);
185217849d02Sdamien 			goto fail;
185317849d02Sdamien 		}
185417849d02Sdamien 	}
185517849d02Sdamien 
185688ee6abdSmiod 	error = uath_cmd_read(sc, UATH_CMD_07, NULL, 0, &val,
185717849d02Sdamien 	    UATH_CMD_FLAG_MAGIC);
185817849d02Sdamien 	if (error != 0) {
185917849d02Sdamien 		printf("%s: could not send read command 07h\n",
18604ab2b9feSmbalmer 		    sc->sc_dev.dv_xname);
186117849d02Sdamien 		goto fail;
186217849d02Sdamien 	}
186317849d02Sdamien 	DPRINTF(("command 07h return code: %x\n", betoh32(val)));
186417849d02Sdamien 
186517849d02Sdamien 	/* set default channel */
186617849d02Sdamien 	ic->ic_bss->ni_chan = ic->ic_ibss_chan;
186717849d02Sdamien 	if ((error = uath_set_chan(sc, ic->ic_bss->ni_chan)) != 0) {
18684ab2b9feSmbalmer 		printf("%s: could not set channel\n", sc->sc_dev.dv_xname);
186917849d02Sdamien 		goto fail;
187017849d02Sdamien 	}
187117849d02Sdamien 
187217849d02Sdamien 	if ((error = uath_wme_init(sc)) != 0) {
187317849d02Sdamien 		printf("%s: could not setup WME parameters\n",
18744ab2b9feSmbalmer 		    sc->sc_dev.dv_xname);
187517849d02Sdamien 		goto fail;
187617849d02Sdamien 	}
187717849d02Sdamien 
187817849d02Sdamien 	/* init MAC registers */
187917849d02Sdamien 	(void)uath_write_reg(sc, 0x19, 0x00000000);
188017849d02Sdamien 	(void)uath_write_reg(sc, 0x1a, 0x0000003c);
188117849d02Sdamien 	(void)uath_write_reg(sc, 0x1b, 0x0000003c);
188217849d02Sdamien 	(void)uath_write_reg(sc, 0x1c, 0x00000000);
188317849d02Sdamien 	(void)uath_write_reg(sc, 0x1e, 0x00000000);
188417849d02Sdamien 	(void)uath_write_reg(sc, 0x1f, 0x00000003);
188517849d02Sdamien 	(void)uath_write_reg(sc, 0x0c, 0x00000000);
188617849d02Sdamien 	(void)uath_write_reg(sc, 0x0f, 0x00000002);
188717849d02Sdamien 	(void)uath_write_reg(sc, 0x0a, 0x00000007);	/* XXX retry? */
188817849d02Sdamien 	(void)uath_write_reg(sc, 0x09, ic->ic_rtsthreshold);
188917849d02Sdamien 
189017849d02Sdamien 	val = htobe32(4);
189117849d02Sdamien 	(void)uath_cmd_write(sc, UATH_CMD_27, &val, sizeof val, 0);
189217849d02Sdamien 	(void)uath_cmd_write(sc, UATH_CMD_27, &val, sizeof val, 0);
189317849d02Sdamien 	(void)uath_cmd_write(sc, UATH_CMD_1B, NULL, 0, 0);
189417849d02Sdamien 
189517849d02Sdamien 	if ((error = uath_set_keys(sc)) != 0) {
189617849d02Sdamien 		printf("%s: could not set crypto keys\n",
18974ab2b9feSmbalmer 		    sc->sc_dev.dv_xname);
189817849d02Sdamien 		goto fail;
189917849d02Sdamien 	}
190017849d02Sdamien 
190117849d02Sdamien 	/* enable Rx */
190217849d02Sdamien 	(void)uath_set_rxfilter(sc, 0x0000, 4);
190317849d02Sdamien 	(void)uath_set_rxfilter(sc, 0x0817, 1);
190417849d02Sdamien 
190517849d02Sdamien 	cmd31.magic1 = htobe32(0xffffffff);
190617849d02Sdamien 	cmd31.magic2 = htobe32(0xffffffff);
190717849d02Sdamien 	(void)uath_cmd_write(sc, UATH_CMD_31, &cmd31, sizeof cmd31, 0);
190817849d02Sdamien 
190917849d02Sdamien 	ifp->if_flags |= IFF_RUNNING;
1910de6cd8fbSdlg 	ifq_clr_oactive(&ifp->if_snd);
191117849d02Sdamien 
191217849d02Sdamien 	if (ic->ic_opmode == IEEE80211_M_MONITOR)
191317849d02Sdamien 		ieee80211_new_state(ic, IEEE80211_S_RUN, -1);
191417849d02Sdamien 	else
191517849d02Sdamien 		ieee80211_new_state(ic, IEEE80211_S_SCAN, -1);
191617849d02Sdamien 
191717849d02Sdamien 	return 0;
191817849d02Sdamien 
191917849d02Sdamien fail:	uath_stop(ifp, 1);
192017849d02Sdamien 	return error;
192117849d02Sdamien }
192217849d02Sdamien 
192378315254Smbalmer void
uath_stop(struct ifnet * ifp,int disable)192417849d02Sdamien uath_stop(struct ifnet *ifp, int disable)
192517849d02Sdamien {
192617849d02Sdamien 	struct uath_softc *sc = ifp->if_softc;
192717849d02Sdamien 	struct ieee80211com *ic = &sc->sc_ic;
192817849d02Sdamien 	uint32_t val;
192917849d02Sdamien 	int s;
193017849d02Sdamien 
193117849d02Sdamien 	s = splusb();
193217849d02Sdamien 
193317849d02Sdamien 	sc->sc_tx_timer = 0;
193417849d02Sdamien 	ifp->if_timer = 0;
1935de6cd8fbSdlg 	ifp->if_flags &= ~IFF_RUNNING;
1936de6cd8fbSdlg 	ifq_clr_oactive(&ifp->if_snd);
193717849d02Sdamien 
193817849d02Sdamien 	ieee80211_new_state(ic, IEEE80211_S_INIT, -1);	/* free all nodes */
193917849d02Sdamien 
194017849d02Sdamien 	val = htobe32(0);
194117849d02Sdamien 	(void)uath_cmd_write(sc, UATH_CMD_SET_STATE, &val, sizeof val, 0);
194217849d02Sdamien 	(void)uath_cmd_write(sc, UATH_CMD_RESET, NULL, 0, 0);
194317849d02Sdamien 
194417849d02Sdamien 	val = htobe32(0);
194517849d02Sdamien 	(void)uath_cmd_write(sc, UATH_CMD_15, &val, sizeof val, 0);
194617849d02Sdamien 
194717849d02Sdamien #if 0
194817849d02Sdamien 	(void)uath_cmd_read(sc, UATH_CMD_SHUTDOWN, NULL, 0, NULL,
194917849d02Sdamien 	    UATH_CMD_FLAG_MAGIC);
195017849d02Sdamien #endif
195117849d02Sdamien 
195217849d02Sdamien 	/* abort any pending transfers */
195317849d02Sdamien 	usbd_abort_pipe(sc->data_tx_pipe);
195417849d02Sdamien 	usbd_abort_pipe(sc->data_rx_pipe);
195517849d02Sdamien 	usbd_abort_pipe(sc->cmd_tx_pipe);
195617849d02Sdamien 
195717849d02Sdamien 	splx(s);
195817849d02Sdamien }
195917849d02Sdamien 
196017849d02Sdamien /*
196117849d02Sdamien  * Load the MIPS R4000 microcode into the device.  Once the image is loaded,
196217849d02Sdamien  * the device will detach itself from the bus and reattach later with a new
196317849d02Sdamien  * product Id (a la ezusb).  XXX this could also be implemented in userland
196417849d02Sdamien  * through /dev/ugen.
196517849d02Sdamien  */
196678315254Smbalmer int
uath_loadfirmware(struct uath_softc * sc,const u_char * fw,int len)196717849d02Sdamien uath_loadfirmware(struct uath_softc *sc, const u_char *fw, int len)
196817849d02Sdamien {
1969ab0b1be7Smglocker 	struct usbd_xfer *ctlxfer, *txxfer, *rxxfer;
197017849d02Sdamien 	struct uath_fwblock *txblock, *rxblock;
197117849d02Sdamien 	uint8_t *txdata;
197217849d02Sdamien 	int error = 0;
197317849d02Sdamien 
197417849d02Sdamien 	if ((ctlxfer = usbd_alloc_xfer(sc->sc_udev)) == NULL) {
197517849d02Sdamien 		printf("%s: could not allocate Tx control xfer\n",
19764ab2b9feSmbalmer 		    sc->sc_dev.dv_xname);
197717849d02Sdamien 		error = USBD_NOMEM;
197817849d02Sdamien 		goto fail1;
197917849d02Sdamien 	}
198017849d02Sdamien 	txblock = usbd_alloc_buffer(ctlxfer, sizeof (struct uath_fwblock));
198117849d02Sdamien 	if (txblock == NULL) {
198217849d02Sdamien 		printf("%s: could not allocate Tx control block\n",
19834ab2b9feSmbalmer 		    sc->sc_dev.dv_xname);
198417849d02Sdamien 		error = USBD_NOMEM;
198517849d02Sdamien 		goto fail2;
198617849d02Sdamien 	}
198717849d02Sdamien 
198817849d02Sdamien 	if ((txxfer = usbd_alloc_xfer(sc->sc_udev)) == NULL) {
198917849d02Sdamien 		printf("%s: could not allocate Tx xfer\n",
19904ab2b9feSmbalmer 		    sc->sc_dev.dv_xname);
199117849d02Sdamien 		error = USBD_NOMEM;
199217849d02Sdamien 		goto fail2;
199317849d02Sdamien 	}
199417849d02Sdamien 	txdata = usbd_alloc_buffer(txxfer, UATH_MAX_FWBLOCK_SIZE);
199517849d02Sdamien 	if (txdata == NULL) {
199617849d02Sdamien 		printf("%s: could not allocate Tx buffer\n",
19974ab2b9feSmbalmer 		    sc->sc_dev.dv_xname);
199817849d02Sdamien 		error = USBD_NOMEM;
199917849d02Sdamien 		goto fail3;
200017849d02Sdamien 	}
200117849d02Sdamien 
200217849d02Sdamien 	if ((rxxfer = usbd_alloc_xfer(sc->sc_udev)) == NULL) {
200317849d02Sdamien 		printf("%s: could not allocate Rx control xfer\n",
20044ab2b9feSmbalmer 		    sc->sc_dev.dv_xname);
200517849d02Sdamien 		error = USBD_NOMEM;
200617849d02Sdamien 		goto fail3;
200717849d02Sdamien 	}
200817849d02Sdamien 	rxblock = usbd_alloc_buffer(rxxfer, sizeof (struct uath_fwblock));
200917849d02Sdamien 	if (rxblock == NULL) {
201017849d02Sdamien 		printf("%s: could not allocate Rx control block\n",
20114ab2b9feSmbalmer 		    sc->sc_dev.dv_xname);
201217849d02Sdamien 		error = USBD_NOMEM;
201317849d02Sdamien 		goto fail4;
201417849d02Sdamien 	}
201517849d02Sdamien 
201617849d02Sdamien 	bzero(txblock, sizeof (struct uath_fwblock));
201717849d02Sdamien 	txblock->flags = htobe32(UATH_WRITE_BLOCK);
201817849d02Sdamien 	txblock->total = htobe32(len);
201917849d02Sdamien 
202017849d02Sdamien 	while (len > 0) {
202117849d02Sdamien 		int mlen = min(len, UATH_MAX_FWBLOCK_SIZE);
202217849d02Sdamien 
202317849d02Sdamien 		txblock->remain = htobe32(len - mlen);
202417849d02Sdamien 		txblock->len = htobe32(mlen);
202517849d02Sdamien 
202617849d02Sdamien 		DPRINTF(("sending firmware block: %d bytes remaining\n",
202717849d02Sdamien 		    len - mlen));
202817849d02Sdamien 
202917849d02Sdamien 		/* send firmware block meta-data */
203017849d02Sdamien 		usbd_setup_xfer(ctlxfer, sc->cmd_tx_pipe, sc, txblock,
2031aa88c704Smpi 		    sizeof (struct uath_fwblock),
2032aa88c704Smpi 		    USBD_NO_COPY | USBD_SYNCHRONOUS,
203317849d02Sdamien 		    UATH_CMD_TIMEOUT, NULL);
2034aa88c704Smpi 		if ((error = usbd_transfer(ctlxfer)) != 0) {
203517849d02Sdamien 			printf("%s: could not send firmware block info\n",
20364ab2b9feSmbalmer 			    sc->sc_dev.dv_xname);
203717849d02Sdamien 			break;
203817849d02Sdamien 		}
203917849d02Sdamien 
204017849d02Sdamien 		/* send firmware block data */
204117849d02Sdamien 		bcopy(fw, txdata, mlen);
204217849d02Sdamien 		usbd_setup_xfer(txxfer, sc->data_tx_pipe, sc, txdata, mlen,
2043aa88c704Smpi 		    USBD_NO_COPY | USBD_SYNCHRONOUS, UATH_DATA_TIMEOUT, NULL);
2044aa88c704Smpi 		if ((error = usbd_transfer(txxfer)) != 0) {
204517849d02Sdamien 			printf("%s: could not send firmware block data\n",
20464ab2b9feSmbalmer 			    sc->sc_dev.dv_xname);
204717849d02Sdamien 			break;
204817849d02Sdamien 		}
204917849d02Sdamien 
205017849d02Sdamien 		/* wait for ack from firmware */
205117849d02Sdamien 		usbd_setup_xfer(rxxfer, sc->cmd_rx_pipe, sc, rxblock,
205217849d02Sdamien 		    sizeof (struct uath_fwblock), USBD_SHORT_XFER_OK |
2053aa88c704Smpi 		    USBD_NO_COPY | USBD_SYNCHRONOUS, UATH_CMD_TIMEOUT, NULL);
2054aa88c704Smpi 		if ((error = usbd_transfer(rxxfer)) != 0) {
205517849d02Sdamien 			printf("%s: could not read firmware answer\n",
20564ab2b9feSmbalmer 			    sc->sc_dev.dv_xname);
205717849d02Sdamien 			break;
205817849d02Sdamien 		}
205917849d02Sdamien 
206017849d02Sdamien 		DPRINTFN(2, ("rxblock flags=0x%x total=%d\n",
206117849d02Sdamien 		    betoh32(rxblock->flags), betoh32(rxblock->rxtotal)));
206217849d02Sdamien 		fw += mlen;
206317849d02Sdamien 		len -= mlen;
206417849d02Sdamien 	}
206517849d02Sdamien 
206617849d02Sdamien fail4:	usbd_free_xfer(rxxfer);
206717849d02Sdamien fail3:	usbd_free_xfer(txxfer);
206817849d02Sdamien fail2:	usbd_free_xfer(ctlxfer);
206917849d02Sdamien fail1:	return error;
207017849d02Sdamien }
2071