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, ®, 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, ®, 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