summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDamien Bergamini <damien@cvs.openbsd.org>2006-09-16 13:21:25 +0000
committerDamien Bergamini <damien@cvs.openbsd.org>2006-09-16 13:21:25 +0000
commit4c340c4e5bf1063bdaa5876736af552068b9a7b4 (patch)
treef90bc61759b301c61453749ff13a0ad6afdf4c19
parentb8c429aa2c4656bb7169f404c03edd73c8116958 (diff)
Initial import of uath(4), a driver for Atheros USB2.0 AR5005UG/AR5005UX
chipsets. Based on a black-box analysis of the Windows binary driver. Requires a firmware that is not freely redistributable (see man uath). The driver handles both pre- and post-firmware devices. Still a bit experimental but Tx/Rx works great in BSS mode (on i386). No 802.11a, IBSS, or HostAP modes yet but there's more to come. Great thanks to jsg@ for digging the USB IDs out of the Windows driver. Committed over a D-Link DWL-G132.
-rw-r--r--share/man/man4/Makefile6
-rw-r--r--share/man/man4/uath.4335
-rw-r--r--sys/arch/i386/conf/GENERIC3
-rw-r--r--sys/dev/usb/files.usb8
-rw-r--r--sys/dev/usb/if_uath.c2116
-rw-r--r--sys/dev/usb/if_uathreg.h248
-rw-r--r--sys/dev/usb/if_uathvar.h155
-rw-r--r--sys/dev/usb/usbdevs15
8 files changed, 2880 insertions, 6 deletions
diff --git a/share/man/man4/Makefile b/share/man/man4/Makefile
index 271d84411fe..0bea809c689 100644
--- a/share/man/man4/Makefile
+++ b/share/man/man4/Makefile
@@ -1,4 +1,4 @@
-# $OpenBSD: Makefile,v 1.378 2006/08/22 18:36:55 deraadt Exp $
+# $OpenBSD: Makefile,v 1.379 2006/09/16 13:21:23 damien Exp $
MAN= aac.4 ac97.4 acx.4 acphy.4 acpi.4 acpihpet.4 acpitimer.4 \
adc.4 addcom.4 admcts.4 admlc.4 admtemp.4 \
@@ -41,8 +41,8 @@ MAN= aac.4 ac97.4 acx.4 acphy.4 acpi.4 acpihpet.4 acpitimer.4 \
speaker.4 sppp.4 sqphy.4 ss.4 st.4 ste.4 stge.4 sti.4 stp.4 sv.4 \
systrace.4 tcic.4 tcp.4 termios.4 ti.4 tl.4 \
tlphy.4 tqphy.4 trm.4 trunk.4 tsl.4 tty.4 tun.4 twe.4 txp.4 \
- txphy.4 uaudio.4 uark.4 ubsa.4 ubsec.4 ubt.4 ucom.4 ucycom.4 udav.4 \
- udcf.4 udp.4 udsbr.4 \
+ txphy.4 uaudio.4 uark.4 uath.4 ubsa.4 ubsec.4 ubt.4 ucom.4 ucycom.4 \
+ udav.4 udcf.4 udp.4 udsbr.4 \
ueagle.4 uftdi.4 ugen.4 uhci.4 uhid.4 uhidev.4 uipaq.4 uk.4 ukbd.4 \
ukphy.4 ulpt.4 umass.4 umct.4 umidi.4 umodem.4 ums.4 umsm.4 \
unix.4 upl.4 uplcom.4 urio.4 url.4 urlphy.4 usb.4 uscanner.4 \
diff --git a/share/man/man4/uath.4 b/share/man/man4/uath.4
new file mode 100644
index 00000000000..7456195266f
--- /dev/null
+++ b/share/man/man4/uath.4
@@ -0,0 +1,335 @@
+.\" $OpenBSD: uath.4,v 1.1 2006/09/16 13:21:23 damien Exp $
+.\"
+.\" Copyright (c) 2006
+.\" Damien Bergamini <damien.bergamini@free.fr>
+.\"
+.\" Permission to use, copy, modify, and distribute this software for any
+.\" purpose with or without fee is hereby granted, provided that the above
+.\" copyright notice and this permission notice appear in all copies.
+.\"
+.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+.\"
+.Dd September 16, 2006
+.Os
+.Dt UATH 4
+.Sh NAME
+.Nm uath
+.Nd Atheros USB IEEE 802.11a/b/g wireless network device
+.Sh SYNOPSIS
+.Cd "uath* at uhub? port ?"
+.Sh DESCRIPTION
+The
+.Nm
+driver supports USB 2.0 wireless network devices based on Atheros
+Communications fifth generation AR5005UG and AR5005UX chipsets.
+.Pp
+The AR5005UG chipset is made of an AR5523 multiprotocol MAC/baseband processor
+and an AR2112 Radio-on-a-Chip that can operate between 2300 and 2500 MHz
+(802.11b/g).
+.Pp
+The AR5005UX chipset is made of an AR5523 multiprotocol MAC/baseband processor
+and an AR5112 dual band Radio-on-a-Chip that can operate between 2300 and
+2500 MHz (802.11b/g) or 4900 and 5850 MHz (802.11a).
+.Pp
+The AR5005UG and AR5005UX chipsets both have an integrated 32-bit MIPS
+R4000-class processor that runs a firmware and manages, among other things,
+the automatic control of the transmit rate and the calibration of the radio.
+.Pp
+These are the modes the
+.Nm
+driver can operate in:
+.Bl -tag -width "IBSS-masterXX"
+.It BSS mode
+Also known as
+.Em infrastructure
+mode, this is used when associating with an access point, through
+which all traffic passes.
+This mode is the default.
+.It monitor mode
+In this mode the driver is able to receive packets without
+associating with an access point.
+This disables the internal receive filter and enables the card to
+capture packets from networks which it wouldn't normally have access to,
+or to scan for access points.
+.El
+.Pp
+.Nm
+supports hardware WEP.
+Wired Equivalent Privacy (WEP) is the de facto encryption standard
+for wireless networks.
+It can be typically configured in one of three modes:
+no encryption; 40-bit encryption; or 104-bit encryption.
+Unfortunately, due to serious weaknesses in WEP protocol
+it is strongly recommended that it not be used as the
+sole mechanism to secure wireless communication.
+WEP is not enabled by default.
+.Sh CONFIGURATION
+The
+.Nm
+driver can be configured at runtime with
+.Xr ifconfig 8
+or on boot with
+.Xr hostname.if 5
+using the following parameters:
+.Bl -tag -width Ds
+.It Cm bssid Ar bssid
+Set the desired BSSID.
+.It Fl bssid
+Unset the desired BSSID.
+The interface will automatically select a BSSID in this mode, which is
+the default.
+.It Cm chan Ar n
+Set the channel (radio frequency) to be used by the driver based on
+the given channel ID
+.Ar n .
+.It Fl chan
+Unset the desired channel to be used by the driver.
+The driver will automatically select a channel in this mode, which is
+the default.
+.It Cm media Ar media
+The
+.Nm
+driver supports the following
+.Ar media
+types:
+.Pp
+.Bl -tag -width autoselect -compact
+.It Cm autoselect
+Enable autoselection of the media type and options.
+.It Cm DS1
+Set 802.11b DS 1Mbps operation.
+.It Cm DS2
+Set 802.11b DS 2Mbps operation.
+.It Cm DS5
+Set 802.11b DS 5.5Mbps operation.
+.It Cm DS11
+Set 802.11b DS 11Mbps operation.
+.It Cm OFDM6
+Set 802.11a/g OFDM 6Mbps operation.
+.It Cm OFDM9
+Set 802.11a/g OFDM 9Mbps operation.
+.It Cm OFDM12
+Set 802.11a/g OFDM 12Mbps operation.
+.It Cm OFDM18
+Set 802.11a/g OFDM 18Mbps operation.
+.It Cm OFDM24
+Set 802.11a/g OFDM 24Mbps operation.
+.It Cm OFDM36
+Set 802.11a/g OFDM 36Mbps operation.
+.It Cm OFDM48
+Set 802.11a/g OFDM 48Mbps operation.
+.It Cm OFDM54
+Set 802.11a/g OFDM 54Mbps operation.
+.El
+.It Cm mediaopt Ar opts
+The
+.Nm
+driver supports the following media options:
+.Pp
+.Bl -tag -width monitor -compact
+.It Cm monitor
+Select monitor mode.
+.El
+.It Fl mediaopt Ar opts
+Disable the specified media options on the driver and return it to the
+default mode of operation (BSS).
+.It Cm mode Ar mode
+The
+.Nm
+driver supports the following modes:
+.Pp
+.Bl -tag -width 11b -compact
+.It Cm 11a
+Force 802.11a operation.
+.It Cm 11b
+Force 802.11b operation.
+.It Cm 11g
+Force 802.11g operation.
+.El
+.It Cm nwid Ar id
+Set the network ID.
+The
+.Ar id
+can either be any text string up to 32 characters in length,
+or a series of hexadecimal digits up to 64 digits.
+An empty
+.Ar id
+string allows the interface to connect to any available access points.
+By default the
+.Nm
+driver uses an empty string.
+Note that network ID is synonymous with Extended Service Set ID (ESSID).
+.It Cm nwkey Ar key
+Enable WEP encryption using the specified
+.Ar key .
+The
+.Ar key
+can either be a string, a series of hexadecimal digits (preceded by
+.Sq 0x ) ,
+or a set of keys of the form
+.Dq n:k1,k2,k3,k4 ,
+where
+.Sq n
+specifies which of the keys will be used for transmitted packets,
+and the four keys,
+.Dq k1
+through
+.Dq k4 ,
+are configured as WEP keys.
+If a set of keys is specified, a comma
+.Pq Sq \&,
+within the key must be escaped with a backslash.
+Note that if multiple keys are used, their order must be the same within
+the network.
+.Nm
+is capable of using both 40-bit (5 characters or 10 hexadecimal digits)
+or 104-bit (13 characters or 26 hexadecimal digits) keys.
+.It Fl nwkey
+Disable WEP encryption.
+This is the default mode of operation.
+.El
+.Sh FILES
+The following firmware file is loaded when a device is plugged:
+.Pp
+.Bl -tag -width Ds -offset indent -compact
+.It /etc/firmware/uath-ar5523
+.El
+.Pp
+This firmware file is not freely redistributable.
+.Pp
+A prepackaged version of the firmware, designed to be used with
+.Xr pkg_add 1 ,
+can be found at:
+.Pp
+.Pa http://damien.bergamini.free.fr/packages/openbsd/uath-firmware-1.0.tgz
+.Sh HARDWARE
+The following adapters should work:
+.Pp
+.Bl -column -compact "TRENDware International TEW-444UB" "AR5005UX" -offset 6n
+.It Em "Adapter Chipset"
+.\".It Belkin F6D3050 AR5005UX
+.It Li "Compex WLU108AG" Ta AR5005UX
+.It Li "Compex WLU108G" Ta AR5005UG
+.\".It Li "D-Link DWL-AG132" Ta AR5005UX
+.It Li "D-Link DWL-G132" Ta AR5005UG
+.\".It Li "Edimax EW-7315Ug" Ta AR5005UG (AR2414???)
+.\".It Li "Lancom USB-54ag" Ta AR5005UX
+.\".It Li "NEC WL54TU" Ta AR5005UX
+.It Li "Netgear WG111T" Ta AR5005UG
+.It Li "Netgear WG111U" Ta AR5005UX
+.It Li "Netgear WPN111" Ta AR5005UG
+.\".It Li "Olitec 000544" Ta AR5005UG
+.It Li "Senao WUB-8004" Ta AR5005UX
+.\".It Li "SparkLAN WL-685GS" Ta AR5005UG
+.It Li "SparkLAN WL-785A" Ta AR5005UX
+.It Li "TP-Link TL-WN620G" Ta AR5005UG
+.It Li "TRENDware International TEW-444UB" Ta AR5005UG
+.It Li "TRENDware International TEW-504UB" Ta AR5005UX
+.It Li "Unex Technology UR054ag" Ta AR5005UX
+.\".It Li "Wistron NeWeb DCUA-81" Ta AR5005UX
+.\".It Li "Wistron NeWeb DRUA-81" Ta AR5005UG
+.\".It Li "Wistron NeWeb DRUA-82" Ta AR5005UX
+.\".It Li "ZyXEL G-200 v2" Ta AR5005UG
+.It Li "ZyXEL XtremeMIMO M-202" Ta AR5005UX
+.El
+.Pp
+An up to date list can be found at
+.Pa http://customerproducts.atheros.com/customerproducts .
+.Sh EXAMPLES
+The following
+.Xr hostname.if 5
+example configures uath0 to join whatever network is available on boot,
+using WEP key
+.Dq 0x1deadbeef1 ,
+channel 11, obtaining an IP address using DHCP:
+.Bd -literal -offset indent
+dhcp NONE NONE NONE nwkey 0x1deadbeef1 chan 11
+.Ed
+.Pp
+The following
+.Xr hostname.if 5
+example creates a host-based access point on boot:
+.Bd -literal -offset indent
+inet 192.168.1.1 255.255.255.0 NONE media autoselect \e
+ mediaopt hostap nwid my_net chan 11
+.Ed
+.Pp
+Configure uath0 for WEP, using hex key
+.Dq 0x1deadbeef1 :
+.Bd -literal -offset indent
+# ifconfig uath0 nwkey 0x1deadbeef1
+.Ed
+.Pp
+Return uath0 to its default settings:
+.Bd -literal -offset indent
+# ifconfig uath0 -bssid -chan media autoselect \e
+ nwid "" -nwkey
+.Ed
+.Pp
+Join an existing BSS network,
+.Dq my_net :
+.Bd -literal -offset indent
+# ifconfig uath0 192.168.1.1 netmask 0xffffff00 nwid my_net
+.Ed
+.Sh DIAGNOSTICS
+.Bl -diag
+.It "uath%d: could not read firmware (error=%d)"
+The driver was unable to read the firmware file from the filesystem.
+The file might be missing or corrupted.
+.It "uath%d: could not load firmware (error=%s)"
+An error occurred while attempting to upload the firmware to the onboard
+MIPS R4000 processor.
+.It "uath%d: could not initialize adapter (error=%d)"
+The firmware was uploaded successfully but did not initialize properly or
+in time.
+.It "uath%d: could not send command (error=%s)"
+An attempt to send a command to the firmware failed.
+.It "uath%d: timeout waiting for command reply"
+A read command was sent to the firmware but the firmware failed to reply in
+time.
+.It "uath%d: device timeout"
+A frame dispatched to the hardware for transmission did not complete in time.
+The driver will reset the hardware.
+This should not happen.
+.El
+.Sh SEE ALSO
+.Xr arp 4 ,
+.Xr ifmedia 4 ,
+.Xr intro 4 ,
+.Xr netintro 4 ,
+.Xr usb 4 ,
+.Xr hostname.if 5 ,
+.Xr hostapd 8 ,
+.Xr ifconfig 8
+.Pp
+Atheros Communications AR5005UG/AR5005UX:
+.Pa http://www.atheros.com/pt/bulletins/AR5005UGBulletin.pdf
+.Pa http://www.atheros.com/pt/bulletins/AR5005UXBulletin.pdf
+.Sh HISTORY
+The
+.Nm
+driver first appeared in
+.Ox 4.0 .
+.Sh AUTHORS
+The
+.Nm
+driver was written by
+.An Damien Bergamini Aq damien@openbsd.org .
+.Sh CAVEATS
+Atheros Communications refuse to release any documentation on their products.
+Atheros proprietary 108 Mbps mode (aka Super AG mode) is not supported.
+.Pp
+The
+.Nm
+driver does not attempt to do any regulation of radio frequencies.
+.Pp
+The
+.Nm
+driver is under active development and only a limited subset of the device
+capabilities are currently supported.
diff --git a/sys/arch/i386/conf/GENERIC b/sys/arch/i386/conf/GENERIC
index a0854428728..a892cf1c2cf 100644
--- a/sys/arch/i386/conf/GENERIC
+++ b/sys/arch/i386/conf/GENERIC
@@ -1,4 +1,4 @@
-# $OpenBSD: GENERIC,v 1.524 2006/09/16 13:09:16 deraadt Exp $
+# $OpenBSD: GENERIC,v 1.525 2006/09/16 13:21:24 damien Exp $
#
# For further information on compiling OpenBSD kernels, see the config(8)
# man page.
@@ -241,6 +241,7 @@ udsbr* at uhub? # D-Link DSB-R100 radio
radio* at udsbr? # USB radio
#ubt* at uhub? # USB Bluetooth
ugen* at uhub? # USB Generic driver
+uath* at uhub? # Atheros AR5005UG/AR5005UX
ural* at uhub? # Ralink RT2500USB
rum* at uhub? # Ralink RT2501USB/RT2601USB
#zyd* at uhub? # Zydas ZD1211
diff --git a/sys/dev/usb/files.usb b/sys/dev/usb/files.usb
index d522f7e92d9..f838a9cb94e 100644
--- a/sys/dev/usb/files.usb
+++ b/sys/dev/usb/files.usb
@@ -1,4 +1,4 @@
-# $OpenBSD: files.usb,v 1.62 2006/08/15 16:41:02 jason Exp $
+# $OpenBSD: files.usb,v 1.63 2006/09/16 13:21:23 damien Exp $
# $NetBSD: files.usb,v 1.16 2000/02/14 20:29:54 augustss Exp $
#
# Config file and device description for machine-independent USB code.
@@ -266,3 +266,9 @@ file dev/usb/if_zyd.c zyd
device ueagle: atm, ifnet, ezload, firmload
attach ueagle at uhub
file dev/usb/ueagle.c ueagle
+
+# Atheros AR5005UG/AR5005UX
+device uath: ether, ifnet, ifmedia, wlan, firmload
+attach uath at uhub
+file dev/usb/if_uath.c uath
+
diff --git a/sys/dev/usb/if_uath.c b/sys/dev/usb/if_uath.c
new file mode 100644
index 00000000000..8da5798b44a
--- /dev/null
+++ b/sys/dev/usb/if_uath.c
@@ -0,0 +1,2116 @@
+/* $OpenBSD: if_uath.c,v 1.1 2006/09/16 13:21:23 damien Exp $ */
+
+/*-
+ * Copyright (c) 2006
+ * Damien Bergamini <damien.bergamini@free.fr>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*-
+ * Driver for Atheros AR5005UG/AR5005UX chipsets.
+ * http://www.atheros.com/pt/bulletins/AR5005UGBulletin.pdf
+ * http://www.atheros.com/pt/bulletins/AR5005UXBulletin.pdf
+ *
+ * IMPORTANT NOTICE:
+ * This driver was written without any documentation or support from Atheros
+ * Communications. It is based on a black-box analysis of the Windows binary
+ * driver. It handles both pre and post-firmware devices.
+ */
+
+#include "bpfilter.h"
+
+#include <sys/param.h>
+#include <sys/sockio.h>
+#include <sys/sysctl.h>
+#include <sys/mbuf.h>
+#include <sys/kernel.h>
+#include <sys/socket.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/timeout.h>
+#include <sys/conf.h>
+#include <sys/device.h>
+
+#include <machine/bus.h>
+#include <machine/endian.h>
+#include <machine/intr.h>
+
+#if NBPFILTER > 0
+#include <net/bpf.h>
+#endif
+#include <net/if.h>
+#include <net/if_arp.h>
+#include <net/if_dl.h>
+#include <net/if_media.h>
+#include <net/if_types.h>
+
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/in_var.h>
+#include <netinet/if_ether.h>
+#include <netinet/ip.h>
+
+#include <net80211/ieee80211_var.h>
+#include <net80211/ieee80211_amrr.h>
+#include <net80211/ieee80211_radiotap.h>
+
+#include <dev/rndvar.h>
+#include <crypto/arc4.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdi_util.h>
+#include <dev/usb/usbdevs.h>
+
+#include <dev/usb/if_uathreg.h>
+#include <dev/usb/if_uathvar.h>
+
+#ifdef USB_DEBUG
+#define UATH_DEBUG
+#endif
+
+#ifdef UATH_DEBUG
+#define DPRINTF(x) do { if (uath_debug) logprintf x; } while (0)
+#define DPRINTFN(n, x) do { if (uath_debug >= (n)) logprintf x; } while (0)
+int uath_debug = 1;
+#else
+#define DPRINTF(x)
+#define DPRINTFN(n, x)
+#endif
+
+/* various supported device vendors/products */
+static const struct uath_type {
+ struct usb_devno dev;
+ unsigned int flags;
+} uath_devs[] = {
+ /* D-Link */
+ { { USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DWLAG122 },
+ UATH_FLAG_DUAL_BAND_RF },
+ { { USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DWLAG122_NF },
+ UATH_FLAG_PRE_FIRMWARE },
+ { { USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DWLAG132 },
+ UATH_FLAG_DUAL_BAND_RF },
+ { { USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DWLAG132_NF },
+ UATH_FLAG_PRE_FIRMWARE },
+ { { USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DWLG132 },
+ 0 },
+ { { USB_VENDOR_DLINK, USB_PRODUCT_DLINK_DWLG132_NF },
+ UATH_FLAG_PRE_FIRMWARE },
+
+ /* Netgear */
+ { { USB_VENDOR_NETGEAR, USB_PRODUCT_NETGEAR_WG111U },
+ UATH_FLAG_DUAL_BAND_RF },
+ { { USB_VENDOR_NETGEAR, USB_PRODUCT_NETGEAR_WG111U_NF },
+ UATH_FLAG_PRE_FIRMWARE },
+ { { USB_VENDOR_NETGEAR3, USB_PRODUCT_NETGEAR3_WG111T },
+ 0 },
+ { { USB_VENDOR_NETGEAR3, USB_PRODUCT_NETGEAR3_WG111T_NF },
+ UATH_FLAG_PRE_FIRMWARE },
+ { { USB_VENDOR_NETGEAR3, USB_PRODUCT_NETGEAR3_WPN111 },
+ 0 },
+ { { USB_VENDOR_NETGEAR3, USB_PRODUCT_NETGEAR3_WPN111_NF },
+ UATH_FLAG_PRE_FIRMWARE }
+};
+#define uath_lookup(v, p) \
+ ((struct uath_type *)usb_lookup(uath_devs, v, p))
+
+Static void uath_attachhook(void *);
+Static int uath_open_pipes(struct uath_softc *);
+Static void uath_close_pipes(struct uath_softc *);
+Static int uath_alloc_tx_data_list(struct uath_softc *);
+Static void uath_free_tx_data_list(struct uath_softc *);
+Static int uath_alloc_rx_data_list(struct uath_softc *);
+Static void uath_free_rx_data_list(struct uath_softc *);
+Static int uath_alloc_tx_cmd_list(struct uath_softc *);
+Static void uath_free_tx_cmd_list(struct uath_softc *);
+Static int uath_alloc_rx_cmd_list(struct uath_softc *);
+Static void uath_free_rx_cmd_list(struct uath_softc *);
+Static int uath_media_change(struct ifnet *);
+Static void uath_stat(void *);
+Static void uath_next_scan(void *);
+Static void uath_task(void *);
+Static int uath_newstate(struct ieee80211com *, enum ieee80211_state,
+ int);
+#ifdef UATH_DEBUG
+Static void uath_dump_cmd(const uint8_t *, int, char);
+#endif
+Static int uath_cmd(struct uath_softc *, uint32_t, const void *, int,
+ void *, int);
+Static int uath_cmd_write(struct uath_softc *, uint32_t, const void *,
+ int, int);
+Static int uath_cmd_read(struct uath_softc *, uint32_t, const void *,
+ int, void *, int);
+Static int uath_write_reg(struct uath_softc *, uint32_t, uint32_t);
+Static int uath_write_multi(struct uath_softc *, uint32_t, const void *,
+ int);
+Static int uath_read_reg(struct uath_softc *, uint32_t, uint32_t *);
+Static int uath_read_eeprom(struct uath_softc *, uint32_t, void *);
+Static void uath_cmd_rxeof(usbd_xfer_handle, usbd_private_handle,
+ usbd_status);
+Static void uath_data_rxeof(usbd_xfer_handle, usbd_private_handle,
+ usbd_status);
+Static void uath_data_txeof(usbd_xfer_handle, usbd_private_handle,
+ usbd_status);
+Static int uath_tx_null(struct uath_softc *);
+Static int uath_tx_data(struct uath_softc *, struct mbuf *,
+ struct ieee80211_node *);
+Static void uath_start(struct ifnet *);
+Static void uath_watchdog(struct ifnet *);
+Static int uath_ioctl(struct ifnet *, u_long, caddr_t);
+Static int uath_query_eeprom(struct uath_softc *);
+Static int uath_reset(struct uath_softc *);
+Static int uath_reset_tx_queues(struct uath_softc *);
+Static int uath_wme_init(struct uath_softc *);
+Static int uath_set_chan(struct uath_softc *, struct ieee80211_channel *);
+Static int uath_set_key(struct uath_softc *,
+ const struct ieee80211_wepkey *, int);
+Static int uath_set_keys(struct uath_softc *);
+Static int uath_set_rates(struct uath_softc *,
+ const struct ieee80211_rateset *);
+Static int uath_set_rxfilter(struct uath_softc *, uint32_t, uint32_t);
+Static int uath_set_led(struct uath_softc *, int, int);
+Static int uath_switch_channel(struct uath_softc *,
+ struct ieee80211_channel *);
+Static int uath_init(struct ifnet *);
+Static void uath_stop(struct ifnet *, int);
+Static int uath_loadfirmware(struct uath_softc *, const u_char *, int);
+Static int uath_activate(device_ptr_t, enum devact);
+
+/*
+ * Supported rates for 802.11b/g modes (in 500Kbps unit).
+ */
+static const struct ieee80211_rateset uath_rateset_11b =
+ { 4, { 2, 4, 11, 22 } };
+
+static const struct ieee80211_rateset uath_rateset_11g =
+ { 12, { 2, 4, 11, 22, 12, 18, 24, 36, 48, 72, 96, 108 } };
+
+USB_DECLARE_DRIVER(uath);
+
+USB_MATCH(uath)
+{
+ USB_MATCH_START(uath, uaa);
+
+ if (uaa->iface != NULL)
+ return UMATCH_NONE;
+
+ return (uath_lookup(uaa->vendor, uaa->product) != NULL) ?
+ UMATCH_VENDOR_PRODUCT : UMATCH_NONE;
+}
+
+Static void
+uath_attachhook(void *xsc)
+{
+ struct uath_softc *sc = xsc;
+ u_char *fw;
+ size_t size;
+ int error;
+
+ if ((error = loadfirmware("uath-ar5523", &fw, &size)) != 0) {
+ printf("%s: could not read firmware (error=%d)\n",
+ USBDEVNAME(sc->sc_dev), error);
+ return;
+ }
+
+ if ((error = uath_loadfirmware(sc, fw, size)) != 0) {
+ printf("%s: could not load firmware (error=%s)\n",
+ USBDEVNAME(sc->sc_dev), usbd_errstr(error));
+ }
+
+ free(fw, M_DEVBUF);
+}
+
+USB_ATTACH(uath)
+{
+ USB_ATTACH_START(uath, sc, uaa);
+ struct ieee80211com *ic = &sc->sc_ic;
+ struct ifnet *ifp = &ic->ic_if;
+ usbd_status error;
+ char *devinfop;
+ int i;
+
+ sc->sc_udev = uaa->device;
+
+ devinfop = usbd_devinfo_alloc(uaa->device, 0);
+ USB_ATTACH_SETUP;
+ printf("%s: %s\n", USBDEVNAME(sc->sc_dev), devinfop);
+ usbd_devinfo_free(devinfop);
+
+ sc->sc_flags = uath_lookup(uaa->vendor, uaa->product)->flags;
+
+ if (usbd_set_config_no(sc->sc_udev, UATH_CONFIG_NO, 0) != 0) {
+ printf("%s: could not set configuration no\n",
+ USBDEVNAME(sc->sc_dev));
+ USB_ATTACH_ERROR_RETURN;
+ }
+
+ /* get the first interface handle */
+ error = usbd_device2interface_handle(sc->sc_udev, UATH_IFACE_INDEX,
+ &sc->sc_iface);
+ if (error != 0) {
+ printf("%s: could not get interface handle\n",
+ USBDEVNAME(sc->sc_dev));
+ USB_ATTACH_ERROR_RETURN;
+ }
+
+ /*
+ * We must open the pipes early because they're used to upload the
+ * firmware (pre-firmware devices) or to send firmware commands.
+ */
+ if (uath_open_pipes(sc) != 0) {
+ printf("%s: could not open pipes\n", USBDEVNAME(sc->sc_dev));
+ USB_ATTACH_ERROR_RETURN;
+ }
+
+ if (sc->sc_flags & UATH_FLAG_PRE_FIRMWARE) {
+ if (rootvp == NULL)
+ mountroothook_establish(uath_attachhook, sc);
+ else
+ uath_attachhook(sc);
+ USB_ATTACH_SUCCESS_RETURN;
+ }
+
+ /*
+ * Only post-firmware devices here.
+ */
+ usb_init_task(&sc->sc_task, uath_task, sc);
+ timeout_set(&sc->scan_to, uath_next_scan, sc);
+ timeout_set(&sc->stat_to, uath_stat, sc);
+
+ /*
+ * Allocate xfers for firmware commands.
+ */
+ if (uath_alloc_tx_cmd_list(sc) != 0) {
+ printf("%s: could not allocate Tx command list\n",
+ USBDEVNAME(sc->sc_dev));
+ goto fail1;
+ }
+ if (uath_alloc_rx_cmd_list(sc) != 0) {
+ printf("%s: could not allocate Rx command list\n",
+ USBDEVNAME(sc->sc_dev));
+ goto fail2;
+ }
+
+ /*
+ * Queue Rx command xfers.
+ */
+ for (i = 0; i < UATH_RX_CMD_LIST_COUNT; i++) {
+ struct uath_rx_cmd *cmd = &sc->rx_cmd[i];
+
+ usbd_setup_xfer(cmd->xfer, sc->cmd_rx_pipe, cmd, cmd->buf,
+ UATH_MAX_RXCMDSZ, USBD_SHORT_XFER_OK | USBD_NO_COPY,
+ USBD_NO_TIMEOUT, uath_cmd_rxeof);
+ error = usbd_transfer(cmd->xfer);
+ if (error != USBD_IN_PROGRESS && error != 0) {
+ printf("%s: could not queue Rx command xfer\n",
+ USBDEVNAME(sc->sc_dev));
+ goto fail3;
+ }
+ }
+
+ /*
+ * We're now ready to send/receive firmware commands.
+ */
+ if (uath_reset(sc) != 0) {
+ printf("%s: could not initialize adapter\n",
+ USBDEVNAME(sc->sc_dev));
+ goto fail3;
+ }
+ if (uath_query_eeprom(sc) != 0) {
+ printf("%s: could not read EEPROM\n", USBDEVNAME(sc->sc_dev));
+ goto fail3;
+ }
+
+ printf("%s: MAC/BBP AR5523, RF AR%c112, address %s\n",
+ USBDEVNAME(sc->sc_dev),
+ (sc->sc_flags & UATH_FLAG_DUAL_BAND_RF) ? '5': '2',
+ ether_sprintf(ic->ic_myaddr));
+
+ /*
+ * Allocate xfers for Tx/Rx data pipes.
+ */
+ if (uath_alloc_tx_data_list(sc) != 0) {
+ printf("%s: could not allocate Tx data list\n",
+ USBDEVNAME(sc->sc_dev));
+ goto fail3;
+ }
+ if (uath_alloc_rx_data_list(sc) != 0) {
+ printf("%s: could not allocate Rx data list\n",
+ USBDEVNAME(sc->sc_dev));
+ goto fail4;
+ }
+
+ ic->ic_phytype = IEEE80211_T_OFDM; /* not only, but not used */
+ ic->ic_opmode = IEEE80211_M_STA; /* default to BSS mode */
+ ic->ic_state = IEEE80211_S_INIT;
+
+ /* set device capabilities */
+ ic->ic_caps =
+ IEEE80211_C_TXPMGT | /* tx power management */
+ IEEE80211_C_SHPREAMBLE | /* short preamble supported */
+ IEEE80211_C_SHSLOT | /* short slot time supported */
+ IEEE80211_C_WEP; /* h/w WEP */
+
+ /* set supported .11b and .11g rates */
+ ic->ic_sup_rates[IEEE80211_MODE_11B] = uath_rateset_11b;
+ ic->ic_sup_rates[IEEE80211_MODE_11G] = uath_rateset_11g;
+
+ /* set supported .11b and .11g channels (1 through 14) */
+ for (i = 1; i <= 14; i++) {
+ ic->ic_channels[i].ic_freq =
+ ieee80211_ieee2mhz(i, IEEE80211_CHAN_2GHZ);
+ ic->ic_channels[i].ic_flags =
+ IEEE80211_CHAN_CCK | IEEE80211_CHAN_OFDM |
+ IEEE80211_CHAN_DYN | IEEE80211_CHAN_2GHZ;
+ }
+
+ ifp->if_softc = sc;
+ ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
+ ifp->if_init = uath_init;
+ ifp->if_ioctl = uath_ioctl;
+ ifp->if_start = uath_start;
+ ifp->if_watchdog = uath_watchdog;
+ IFQ_SET_READY(&ifp->if_snd);
+ memcpy(ifp->if_xname, USBDEVNAME(sc->sc_dev), IFNAMSIZ);
+
+ if_attach(ifp);
+ ieee80211_ifattach(ifp);
+
+ /* override state transition machine */
+ sc->sc_newstate = ic->ic_newstate;
+ ic->ic_newstate = uath_newstate;
+ ieee80211_media_init(ifp, uath_media_change, ieee80211_media_status);
+
+#if NBPFILTER > 0
+ bpfattach(&sc->sc_drvbpf, ifp, DLT_IEEE802_11_RADIO,
+ sizeof (struct ieee80211_frame) + IEEE80211_RADIOTAP_HDRLEN);
+
+ sc->sc_rxtap_len = sizeof sc->sc_rxtapu;
+ sc->sc_rxtap.wr_ihdr.it_len = htole16(sc->sc_rxtap_len);
+ sc->sc_rxtap.wr_ihdr.it_present = htole32(UATH_RX_RADIOTAP_PRESENT);
+
+ sc->sc_txtap_len = sizeof sc->sc_txtapu;
+ sc->sc_txtap.wt_ihdr.it_len = htole16(sc->sc_txtap_len);
+ sc->sc_txtap.wt_ihdr.it_present = htole32(UATH_TX_RADIOTAP_PRESENT);
+#endif
+
+ usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH, sc->sc_udev,
+ USBDEV(sc->sc_dev));
+
+ USB_ATTACH_SUCCESS_RETURN;
+
+fail4: uath_free_tx_data_list(sc);
+fail3: uath_free_rx_cmd_list(sc);
+fail2: uath_free_tx_cmd_list(sc);
+fail1: uath_close_pipes(sc);
+
+ USB_ATTACH_ERROR_RETURN;
+}
+
+USB_DETACH(uath)
+{
+ USB_DETACH_START(uath, sc);
+ struct ifnet *ifp = &sc->sc_ic.ic_if;
+ int s;
+
+ s = splusb();
+
+ if (sc->sc_flags & UATH_FLAG_PRE_FIRMWARE) {
+ uath_close_pipes(sc);
+ splx(s);
+ return 0;
+ }
+
+ /* post-firmware device */
+
+ usb_rem_task(sc->sc_udev, &sc->sc_task);
+ timeout_del(&sc->scan_to);
+ timeout_del(&sc->stat_to);
+
+ /* abort and free xfers */
+ uath_free_tx_data_list(sc);
+ uath_free_rx_data_list(sc);
+ uath_free_tx_cmd_list(sc);
+ uath_free_rx_cmd_list(sc);
+
+ /* close Tx/Rx pipes */
+ uath_close_pipes(sc);
+
+ ieee80211_ifdetach(ifp); /* free all nodes */
+ if_detach(ifp);
+
+ splx(s);
+
+ usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, sc->sc_udev,
+ USBDEV(sc->sc_dev));
+
+ return 0;
+}
+
+Static int
+uath_open_pipes(struct uath_softc *sc)
+{
+ int error;
+
+ /*
+ * XXX pipes numbers are hardcoded because we don't have any way
+ * to distinguish the data pipes from the firmware command pipes
+ * (both are bulk pipes) using the endpoints descriptors.
+ */
+ error = usbd_open_pipe(sc->sc_iface, 0x01, USBD_EXCLUSIVE_USE,
+ &sc->cmd_tx_pipe);
+ if (error != 0) {
+ printf("%s: could not open Tx command pipe: %s\n",
+ USBDEVNAME(sc->sc_dev), usbd_errstr(error));
+ goto fail;
+ }
+
+ error = usbd_open_pipe(sc->sc_iface, 0x02, USBD_EXCLUSIVE_USE,
+ &sc->data_tx_pipe);
+ if (error != 0) {
+ printf("%s: could not open Tx data pipe: %s\n",
+ USBDEVNAME(sc->sc_dev), usbd_errstr(error));
+ goto fail;
+ }
+
+ error = usbd_open_pipe(sc->sc_iface, 0x81, USBD_EXCLUSIVE_USE,
+ &sc->cmd_rx_pipe);
+ if (error != 0) {
+ printf("%s: could not open Rx command pipe: %s\n",
+ USBDEVNAME(sc->sc_dev), usbd_errstr(error));
+ goto fail;
+ }
+
+ error = usbd_open_pipe(sc->sc_iface, 0x82, USBD_EXCLUSIVE_USE,
+ &sc->data_rx_pipe);
+ if (error != 0) {
+ printf("%s: could not open Rx data pipe: %s\n",
+ USBDEVNAME(sc->sc_dev), usbd_errstr(error));
+ goto fail;
+ }
+
+ return 0;
+
+fail: uath_close_pipes(sc);
+ return error;
+}
+
+Static void
+uath_close_pipes(struct uath_softc *sc)
+{
+ /* assumes no transfers are pending on the pipes */
+
+ if (sc->data_tx_pipe != NULL)
+ usbd_close_pipe(sc->data_tx_pipe);
+
+ if (sc->data_rx_pipe != NULL)
+ usbd_close_pipe(sc->data_rx_pipe);
+
+ if (sc->cmd_tx_pipe != NULL)
+ usbd_close_pipe(sc->cmd_tx_pipe);
+
+ if (sc->cmd_rx_pipe != NULL)
+ usbd_close_pipe(sc->cmd_rx_pipe);
+}
+
+Static int
+uath_alloc_tx_data_list(struct uath_softc *sc)
+{
+ int i, error;
+
+ for (i = 0; i < UATH_TX_DATA_LIST_COUNT; i++) {
+ struct uath_tx_data *data = &sc->tx_data[i];
+
+ data->sc = sc; /* backpointer for callbacks */
+
+ data->xfer = usbd_alloc_xfer(sc->sc_udev);
+ if (data->xfer == NULL) {
+ printf("%s: could not allocate xfer\n",
+ USBDEVNAME(sc->sc_dev));
+ error = ENOMEM;
+ goto fail;
+ }
+ data->buf = usbd_alloc_buffer(data->xfer, UATH_MAX_TXBUFSZ);
+ if (data->buf == NULL) {
+ printf("%s: could not allocate xfer buffer\n",
+ USBDEVNAME(sc->sc_dev));
+ error = ENOMEM;
+ goto fail;
+ }
+ }
+ return 0;
+
+fail: uath_free_tx_data_list(sc);
+ return error;
+}
+
+Static void
+uath_free_tx_data_list(struct uath_softc *sc)
+{
+ int i;
+
+ /* make sure no transfers are pending */
+ usbd_abort_pipe(sc->data_tx_pipe);
+
+ for (i = 0; i < UATH_TX_DATA_LIST_COUNT; i++)
+ if (sc->tx_data[i].xfer != NULL)
+ usbd_free_xfer(sc->tx_data[i].xfer);
+}
+
+Static int
+uath_alloc_rx_data_list(struct uath_softc *sc)
+{
+ int i, error;
+
+ for (i = 0; i < UATH_RX_DATA_LIST_COUNT; i++) {
+ struct uath_rx_data *data = &sc->rx_data[i];
+
+ data->sc = sc; /* backpointer for callbacks */
+
+ data->xfer = usbd_alloc_xfer(sc->sc_udev);
+ if (data->xfer == NULL) {
+ printf("%s: could not allocate xfer\n",
+ USBDEVNAME(sc->sc_dev));
+ error = ENOMEM;
+ goto fail;
+ }
+ if (usbd_alloc_buffer(data->xfer, sc->rxbufsz) == NULL) {
+ printf("%s: could not allocate xfer buffer\n",
+ USBDEVNAME(sc->sc_dev));
+ error = ENOMEM;
+ goto fail;
+ }
+
+ MGETHDR(data->m, M_DONTWAIT, MT_DATA);
+ if (data->m == NULL) {
+ printf("%s: could not allocate rx mbuf\n",
+ USBDEVNAME(sc->sc_dev));
+ error = ENOMEM;
+ goto fail;
+ }
+ MCLGET(data->m, M_DONTWAIT);
+ if (!(data->m->m_flags & M_EXT)) {
+ printf("%s: could not allocate rx mbuf cluster\n",
+ USBDEVNAME(sc->sc_dev));
+ error = ENOMEM;
+ goto fail;
+ }
+
+ data->buf = mtod(data->m, uint8_t *);
+ }
+ return 0;
+
+fail: uath_free_rx_data_list(sc);
+ return error;
+}
+
+Static void
+uath_free_rx_data_list(struct uath_softc *sc)
+{
+ int i;
+
+ /* make sure no transfers are pending */
+ usbd_abort_pipe(sc->data_rx_pipe);
+
+ for (i = 0; i < UATH_RX_DATA_LIST_COUNT; i++) {
+ struct uath_rx_data *data = &sc->rx_data[i];
+
+ if (data->xfer != NULL)
+ usbd_free_xfer(data->xfer);
+
+ if (data->m != NULL)
+ m_freem(data->m);
+ }
+}
+
+Static int
+uath_alloc_tx_cmd_list(struct uath_softc *sc)
+{
+ int i, error;
+
+ for (i = 0; i < UATH_TX_CMD_LIST_COUNT; i++) {
+ struct uath_tx_cmd *cmd = &sc->tx_cmd[i];
+
+ cmd->sc = sc; /* backpointer for callbacks */
+
+ cmd->xfer = usbd_alloc_xfer(sc->sc_udev);
+ if (cmd->xfer == NULL) {
+ printf("%s: could not allocate xfer\n",
+ USBDEVNAME(sc->sc_dev));
+ error = ENOMEM;
+ goto fail;
+ }
+ cmd->buf = usbd_alloc_buffer(cmd->xfer, UATH_MAX_TXCMDSZ);
+ if (cmd->buf == NULL) {
+ printf("%s: could not allocate xfer buffer\n",
+ USBDEVNAME(sc->sc_dev));
+ error = ENOMEM;
+ goto fail;
+ }
+ }
+ return 0;
+
+fail: uath_free_tx_cmd_list(sc);
+ return error;
+}
+
+Static void
+uath_free_tx_cmd_list(struct uath_softc *sc)
+{
+ int i;
+
+ /* make sure no transfers are pending */
+ usbd_abort_pipe(sc->cmd_tx_pipe);
+
+ for (i = 0; i < UATH_TX_CMD_LIST_COUNT; i++)
+ if (sc->tx_cmd[i].xfer != NULL)
+ usbd_free_xfer(sc->tx_cmd[i].xfer);
+}
+
+Static int
+uath_alloc_rx_cmd_list(struct uath_softc *sc)
+{
+ int i, error;
+
+ for (i = 0; i < UATH_RX_CMD_LIST_COUNT; i++) {
+ struct uath_rx_cmd *cmd = &sc->rx_cmd[i];
+
+ cmd->sc = sc; /* backpointer for callbacks */
+
+ cmd->xfer = usbd_alloc_xfer(sc->sc_udev);
+ if (cmd->xfer == NULL) {
+ printf("%s: could not allocate xfer\n",
+ USBDEVNAME(sc->sc_dev));
+ error = ENOMEM;
+ goto fail;
+ }
+ cmd->buf = usbd_alloc_buffer(cmd->xfer, UATH_MAX_RXCMDSZ);
+ if (cmd->buf == NULL) {
+ printf("%s: could not allocate xfer buffer\n",
+ USBDEVNAME(sc->sc_dev));
+ error = ENOMEM;
+ goto fail;
+ }
+ }
+ return 0;
+
+fail: uath_free_rx_cmd_list(sc);
+ return error;
+}
+
+Static void
+uath_free_rx_cmd_list(struct uath_softc *sc)
+{
+ int i;
+
+ /* make sure no transfers are pending */
+ usbd_abort_pipe(sc->cmd_rx_pipe);
+
+ for (i = 0; i < UATH_RX_CMD_LIST_COUNT; i++)
+ if (sc->rx_cmd[i].xfer != NULL)
+ usbd_free_xfer(sc->rx_cmd[i].xfer);
+}
+
+Static int
+uath_media_change(struct ifnet *ifp)
+{
+ int error;
+
+ error = ieee80211_media_change(ifp);
+ if (error != ENETRESET)
+ return error;
+
+ if ((ifp->if_flags & (IFF_UP | IFF_RUNNING)) == (IFF_UP | IFF_RUNNING))
+ uath_init(ifp);
+
+ return 0;
+}
+
+/*
+ * This function is called periodically (every second) when associated to
+ * query device statistics.
+ */
+Static void
+uath_stat(void *arg)
+{
+ struct uath_softc *sc = arg;
+ int error;
+
+ /*
+ * Send request for statistics asynchronously. The timer will be
+ * restarted when we'll get the stats notification.
+ */
+ error = uath_cmd_write(sc, UATH_CMD_STATS, NULL, 0,
+ UATH_CMD_FLAG_ASYNC);
+ if (error != 0) {
+ printf("%s: could not query statistics (error=%d)\n",
+ USBDEVNAME(sc->sc_dev), error);
+ }
+}
+
+/*
+ * This function is called periodically (every 250ms) during scanning to
+ * switch from one channel to another.
+ */
+Static void
+uath_next_scan(void *arg)
+{
+ struct uath_softc *sc = arg;
+ struct ieee80211com *ic = &sc->sc_ic;
+ struct ifnet *ifp = &ic->ic_if;
+
+ if (ic->ic_state == IEEE80211_S_SCAN)
+ ieee80211_next_scan(ifp);
+}
+
+Static void
+uath_task(void *arg)
+{
+ struct uath_softc *sc = arg;
+ struct ieee80211com *ic = &sc->sc_ic;
+ enum ieee80211_state ostate;
+
+ ostate = ic->ic_state;
+
+ switch (sc->sc_state) {
+ case IEEE80211_S_INIT:
+ if (ostate == IEEE80211_S_RUN) {
+ /* turn link and activity LEDs off */
+ (void)uath_set_led(sc, UATH_LED_LINK, 0);
+ (void)uath_set_led(sc, UATH_LED_ACTIVITY, 0);
+ }
+ break;
+
+ case IEEE80211_S_SCAN:
+ if (uath_switch_channel(sc, ic->ic_bss->ni_chan) != 0) {
+ printf("%s: could not switch channel\n",
+ USBDEVNAME(sc->sc_dev));
+ break;
+ }
+ timeout_add(&sc->scan_to, hz / 4);
+ break;
+
+ case IEEE80211_S_AUTH:
+ {
+ struct ieee80211_node *ni = ic->ic_bss;
+ struct uath_cmd_bssid bssid;
+ struct uath_cmd_0b cmd0b;
+ struct uath_cmd_0c cmd0c;
+
+ if (uath_switch_channel(sc, ni->ni_chan) != 0) {
+ printf("%s: could not switch channel\n",
+ USBDEVNAME(sc->sc_dev));
+ break;
+ }
+
+ (void)uath_cmd_write(sc, UATH_CMD_24, NULL, 0, 0);
+
+ bzero(&bssid, sizeof bssid);
+ bssid.len = htobe32(IEEE80211_ADDR_LEN);
+ IEEE80211_ADDR_COPY(bssid.bssid, ni->ni_bssid);
+ (void)uath_cmd_write(sc, UATH_CMD_SET_BSSID, &bssid,
+ sizeof bssid, 0);
+
+ bzero(&cmd0b, sizeof cmd0b);
+ cmd0b.code = htobe32(2);
+ cmd0b.size = htobe32(sizeof (cmd0b.data));
+ (void)uath_cmd_write(sc, UATH_CMD_0B, &cmd0b, sizeof cmd0b, 0);
+
+ bzero(&cmd0c, sizeof cmd0c);
+ cmd0c.magic1 = htobe32(2);
+ cmd0c.magic2 = htobe32(7);
+ cmd0c.magic3 = htobe32(1);
+ (void)uath_cmd_write(sc, UATH_CMD_0C, &cmd0c, sizeof cmd0c, 0);
+
+ if (uath_set_rates(sc, &ni->ni_rates) != 0) {
+ printf("%s: could not set negotiated rate set\n",
+ USBDEVNAME(sc->sc_dev));
+ break;
+ }
+ break;
+ }
+
+ case IEEE80211_S_ASSOC:
+ break;
+
+ case IEEE80211_S_RUN:
+ {
+ struct ieee80211_node *ni = ic->ic_bss;
+ struct uath_cmd_bssid bssid;
+ struct uath_cmd_xled xled;
+ uint32_t val;
+
+ if (ic->ic_opmode == IEEE80211_M_MONITOR) {
+ /* make both LEDs blink while monitoring */
+ bzero(&xled, sizeof xled);
+ xled.which = htobe32(0);
+ xled.rate = htobe32(1);
+ xled.mode = htobe32(2);
+ (void)uath_cmd_write(sc, UATH_CMD_SET_XLED, &xled,
+ sizeof xled, 0);
+ break;
+ }
+
+ /*
+ * Tx rate is controlled by firmware, report the maximum
+ * negotiated rate in ifconfig output.
+ */
+ ni->ni_txrate = ni->ni_rates.rs_nrates - 1;
+
+ val = htobe32(1);
+ (void)uath_cmd_write(sc, UATH_CMD_2E, &val, sizeof val, 0);
+
+ bzero(&bssid, sizeof bssid);
+ bssid.flags1 = htobe32(0xc004);
+ bssid.flags2 = htobe32(0x003b);
+ bssid.len = htobe32(IEEE80211_ADDR_LEN);
+ IEEE80211_ADDR_COPY(bssid.bssid, ni->ni_bssid);
+ (void)uath_cmd_write(sc, UATH_CMD_SET_BSSID, &bssid,
+ sizeof bssid, 0);
+
+ /* turn link LED on */
+ (void)uath_set_led(sc, UATH_LED_LINK, 1);
+
+ /* make activity LED blink */
+ bzero(&xled, sizeof xled);
+ xled.which = htobe32(1);
+ xled.rate = htobe32(1);
+ xled.mode = htobe32(2);
+ (void)uath_cmd_write(sc, UATH_CMD_SET_XLED, &xled, sizeof xled,
+ 0);
+
+ /* set state to associated */
+ val = htobe32(1);
+ (void)uath_cmd_write(sc, UATH_CMD_SET_STATE, &val, sizeof val,
+ 0);
+
+ /* start statistics timer */
+ timeout_add(&sc->stat_to, hz);
+ break;
+ }
+ }
+ sc->sc_newstate(ic, sc->sc_state, sc->sc_arg);
+}
+
+Static int
+uath_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg)
+{
+ struct uath_softc *sc = ic->ic_softc;
+
+ usb_rem_task(sc->sc_udev, &sc->sc_task);
+ timeout_del(&sc->scan_to);
+ timeout_del(&sc->stat_to);
+
+ /* do it in a process context */
+ sc->sc_state = nstate;
+ sc->sc_arg = arg;
+ usb_add_task(sc->sc_udev, &sc->sc_task);
+
+ return 0;
+}
+
+#ifdef UATH_DEBUG
+Static void
+uath_dump_cmd(const uint8_t *buf, int len, char prefix)
+{
+ int i;
+
+ for (i = 0; i < len; i++) {
+ if ((i % 16) == 0)
+ printf("\n%c ", prefix);
+ else if ((i % 4) == 0)
+ printf(" ");
+ printf("%02x", buf[i]);
+ }
+ printf("\n");
+}
+#endif
+
+/*
+ * Low-level function to send read or write commands to the firmware.
+ */
+Static int
+uath_cmd(struct uath_softc *sc, uint32_t code, const void *idata, int ilen,
+ void *odata, int flags)
+{
+ struct uath_cmd_hdr *hdr;
+ struct uath_tx_cmd *cmd;
+ uint16_t xferflags;
+ int s, xferlen, error;
+
+ /* grab a xfer */
+ cmd = &sc->tx_cmd[sc->cmd_idx];
+
+ /* always bulk-out a multiple of 4 bytes */
+ xferlen = (sizeof (struct uath_cmd_hdr) + ilen + 3) & ~3;
+
+ hdr = (struct uath_cmd_hdr *)cmd->buf;
+ bzero(hdr, sizeof (struct uath_cmd_hdr));
+ hdr->len = htobe32(xferlen);
+ hdr->code = htobe32(code);
+ hdr->priv = sc->cmd_idx; /* don't care about endianness */
+ hdr->magic = htobe32((flags & UATH_CMD_FLAG_MAGIC) ? 1 << 24 : 0);
+ bcopy(idata, (uint8_t *)(hdr + 1), ilen);
+
+#ifdef UATH_DEBUG
+ if (uath_debug >= 5) {
+ printf("sending command code=0x%02x flags=0x%x index=%u",
+ code, flags, sc->cmd_idx);
+ uath_dump_cmd(cmd->buf, xferlen, '+');
+ }
+#endif
+ xferflags = USBD_FORCE_SHORT_XFER | USBD_NO_COPY;
+ if (!(flags & UATH_CMD_FLAG_READ)) {
+ if (!(flags & UATH_CMD_FLAG_ASYNC))
+ xferflags |= USBD_SYNCHRONOUS;
+ } else
+ s = splusb();
+
+ cmd->odata = odata;
+
+ usbd_setup_xfer(cmd->xfer, sc->cmd_tx_pipe, cmd, cmd->buf, xferlen,
+ xferflags, UATH_CMD_TIMEOUT, NULL);
+ error = usbd_transfer(cmd->xfer);
+ if (error != USBD_IN_PROGRESS && error != 0) {
+ if (flags & UATH_CMD_FLAG_READ)
+ splx(s);
+ printf("%s: could not send command (error=%s)\n",
+ USBDEVNAME(sc->sc_dev), usbd_errstr(error));
+ return error;
+ }
+ sc->cmd_idx = (sc->cmd_idx + 1) % UATH_TX_CMD_LIST_COUNT;
+
+ if (!(flags & UATH_CMD_FLAG_READ))
+ return 0; /* write: don't wait for reply */
+
+ /* wait at most two seconds for command reply */
+ error = tsleep(cmd, PCATCH, "uathcmd", 2 * hz);
+ splx(s);
+ if (error != 0) {
+ printf("%s: timeout waiting for command reply\n",
+ USBDEVNAME(sc->sc_dev));
+ }
+ return error;
+}
+
+Static int
+uath_cmd_write(struct uath_softc *sc, uint32_t code, const void *data, int len,
+ int flags)
+{
+ flags &= ~UATH_CMD_FLAG_READ;
+ return uath_cmd(sc, code, data, len, NULL, flags);
+}
+
+Static int
+uath_cmd_read(struct uath_softc *sc, uint32_t code, const void *idata,
+ int ilen, void *odata, int flags)
+{
+ flags |= UATH_CMD_FLAG_READ;
+ return uath_cmd(sc, code, idata, ilen, odata, flags);
+}
+
+Static int
+uath_write_reg(struct uath_softc *sc, uint32_t reg, uint32_t val)
+{
+ struct uath_write_mac write;
+ int error;
+
+ write.reg = htobe32(reg);
+ write.len = htobe32(0); /* 0 = single write */
+ *(uint32_t *)write.data = htobe32(val);
+
+ error = uath_cmd_write(sc, UATH_CMD_WRITE_MAC, &write,
+ 3 * sizeof (uint32_t), 0);
+ if (error != 0) {
+ printf("%s: could not write register 0x%02x\n",
+ USBDEVNAME(sc->sc_dev), reg);
+ }
+ return error;
+}
+
+Static int
+uath_write_multi(struct uath_softc *sc, uint32_t reg, const void *data,
+ int len)
+{
+ struct uath_write_mac write;
+ int error;
+
+ write.reg = htobe32(reg);
+ write.len = htobe32(len);
+ bcopy(data, write.data, len);
+
+ /* properly handle the case where len is zero (reset) */
+ error = uath_cmd_write(sc, UATH_CMD_WRITE_MAC, &write,
+ (len == 0) ? sizeof (uint32_t) : 2 * sizeof (uint32_t) + len, 0);
+ if (error != 0) {
+ printf("%s: could not write %d bytes to register 0x%02x\n",
+ USBDEVNAME(sc->sc_dev), len, reg);
+ }
+ return error;
+}
+
+Static int
+uath_read_reg(struct uath_softc *sc, uint32_t reg, uint32_t *val)
+{
+ struct uath_read_mac read;
+ int error;
+
+ reg = htobe32(reg);
+ error = uath_cmd_read(sc, UATH_CMD_READ_MAC, &reg, sizeof reg, &read,
+ 0);
+ if (error != 0) {
+ printf("%s: could not read register 0x%02x\n",
+ USBDEVNAME(sc->sc_dev), betoh32(reg));
+ return error;
+ }
+ *val = betoh32(*(uint32_t *)read.data);
+ return error;
+}
+
+Static int
+uath_read_eeprom(struct uath_softc *sc, uint32_t reg, void *odata)
+{
+ struct uath_read_mac read;
+ int len, error;
+
+ reg = htobe32(reg);
+ error = uath_cmd_read(sc, UATH_CMD_READ_EEPROM, &reg, sizeof reg,
+ &read, 0);
+ if (error != 0) {
+ printf("%s: could not read EEPROM offset 0x%02x\n",
+ USBDEVNAME(sc->sc_dev), betoh32(reg));
+ return error;
+ }
+ len = betoh32(read.len);
+ bcopy(read.data, odata, (len == 0) ? sizeof (uint32_t) : len);
+ return error;
+}
+
+Static void
+uath_cmd_rxeof(usbd_xfer_handle xfer, usbd_private_handle priv,
+ usbd_status status)
+{
+ struct uath_rx_cmd *cmd = priv;
+ struct uath_softc *sc = cmd->sc;
+ struct uath_cmd_hdr *hdr;
+
+ if (status != USBD_NORMAL_COMPLETION) {
+ if (status == USBD_STALLED)
+ usbd_clear_endpoint_stall_async(sc->cmd_rx_pipe);
+ return;
+ }
+
+ hdr = (struct uath_cmd_hdr *)cmd->buf;
+
+#ifdef UATH_DEBUG
+ if (uath_debug >= 5) {
+ printf("received command code=0x%x index=%u len=%u",
+ betoh32(hdr->code), hdr->priv, betoh32(hdr->len));
+ uath_dump_cmd(cmd->buf, betoh32(hdr->len), '-');
+ }
+#endif
+
+ switch (betoh32(hdr->code) & 0xff) {
+ /* reply to a read command */
+ default:
+ {
+ struct uath_tx_cmd *txcmd = &sc->tx_cmd[hdr->priv];
+
+ if (txcmd->odata != NULL) {
+ /* copy answer into caller's supplied buffer */
+ bcopy((uint8_t *)(hdr + 1), txcmd->odata,
+ betoh32(hdr->len) - sizeof (struct uath_cmd_hdr));
+ }
+ wakeup(txcmd); /* wake up caller */
+ break;
+ }
+ /* spontaneous firmware notifications */
+ case UATH_NOTIF_READY:
+ DPRINTF(("received device ready notification\n"));
+ wakeup(UATH_COND_INIT(sc));
+ break;
+
+ case UATH_NOTIF_TX:
+ /* this notification is sent when UATH_TX_NOTIFY is set */
+ DPRINTF(("received Tx notification\n"));
+ break;
+
+ case UATH_NOTIF_STATS:
+ DPRINTFN(2, ("received device statistics\n"));
+ timeout_add(&sc->stat_to, hz);
+ break;
+ }
+
+ /* setup a new transfer */
+ usbd_setup_xfer(xfer, sc->cmd_rx_pipe, cmd, cmd->buf, UATH_MAX_RXCMDSZ,
+ USBD_SHORT_XFER_OK | USBD_NO_COPY, USBD_NO_TIMEOUT,
+ uath_cmd_rxeof);
+ (void)usbd_transfer(xfer);
+}
+
+Static void
+uath_data_rxeof(usbd_xfer_handle xfer, usbd_private_handle priv,
+ usbd_status status)
+{
+ struct uath_rx_data *data = priv;
+ struct uath_softc *sc = data->sc;
+ struct ieee80211com *ic = &sc->sc_ic;
+ struct ifnet *ifp = &ic->ic_if;
+ struct ieee80211_frame *wh;
+ struct ieee80211_node *ni;
+ struct uath_rx_desc *desc;
+ struct mbuf *mnew, *m;
+ uint32_t hdr;
+ int s, len;
+
+ if (status != USBD_NORMAL_COMPLETION) {
+ if (status == USBD_NOT_STARTED || status == USBD_CANCELLED)
+ return;
+
+ if (status == USBD_STALLED)
+ usbd_clear_endpoint_stall_async(sc->data_rx_pipe);
+
+ ifp->if_ierrors++;
+ return;
+ }
+ usbd_get_xfer_status(xfer, NULL, NULL, &len, NULL);
+
+ if (len < UATH_MIN_RXBUFSZ || len > sc->rxbufsz) {
+ DPRINTF(("wrong xfer size: !(%d <= %d <= %d)\n",
+ UATH_MIN_RXBUFSZ, len, sc->rxbufsz));
+ ifp->if_ierrors++;
+ goto skip;
+ }
+
+ hdr = betoh32(*(uint32_t *)data->buf);
+
+ /* Rx descriptor is located at the end, 32-bit aligned */
+ desc = (struct uath_rx_desc *)
+ (data->buf + len - sizeof (struct uath_rx_desc));
+
+ /* there's probably a "bad CRC" flag somewhere in the descriptor.. */
+
+ MGETHDR(mnew, M_DONTWAIT, MT_DATA);
+ if (mnew == NULL) {
+ printf("%s: could not allocate rx mbuf\n",
+ USBDEVNAME(sc->sc_dev));
+ ifp->if_ierrors++;
+ goto skip;
+ }
+ MCLGET(mnew, M_DONTWAIT);
+ if (!(mnew->m_flags & M_EXT)) {
+ printf("%s: could not allocate rx mbuf cluster\n",
+ USBDEVNAME(sc->sc_dev));
+ m_freem(mnew);
+ ifp->if_ierrors++;
+ goto skip;
+ }
+
+ m = data->m;
+ data->m = mnew;
+
+ /* finalize mbuf */
+ m->m_pkthdr.rcvif = ifp;
+ m->m_data = data->buf + sizeof (uint32_t);
+ m->m_pkthdr.len = m->m_len =
+ betoh32(desc->len) - sizeof (struct uath_rx_desc);
+
+ data->buf = mtod(data->m, uint8_t *);
+
+ wh = mtod(m, struct ieee80211_frame *);
+ if ((wh->i_fc[1] & IEEE80211_FC1_WEP) &&
+ ic->ic_opmode != IEEE80211_M_MONITOR) {
+ /*
+ * Hardware decrypts the frame itself but leaves the WEP bit
+ * set in the 802.11 header and doesn't remove the IV and CRC
+ * fields.
+ */
+ wh->i_fc[1] &= ~IEEE80211_FC1_WEP;
+ ovbcopy(wh, (caddr_t)wh + IEEE80211_WEP_IVLEN +
+ IEEE80211_WEP_KIDLEN, sizeof (struct ieee80211_frame));
+ m_adj(m, IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN);
+ m_adj(m, -IEEE80211_WEP_CRCLEN);
+ wh = mtod(m, struct ieee80211_frame *);
+ }
+
+#if NBPFILTER > 0
+ /* there are a lot more fields in the Rx descriptor */
+ if (sc->sc_drvbpf != NULL) {
+ struct mbuf mb;
+ struct uath_rx_radiotap_header *tap = &sc->sc_rxtap;
+
+ tap->wr_flags = 0;
+ tap->wr_chan_freq = htole16(betoh32(desc->freq));
+ tap->wr_chan_flags = htole16(ic->ic_bss->ni_chan->ic_flags);
+ tap->wr_dbm_antsignal = (int8_t)betoh32(desc->rssi);
+
+ M_DUP_PKTHDR(&mb, m);
+ mb.m_data = (caddr_t)tap;
+ mb.m_len = sc->sc_rxtap_len;
+ mb.m_next = m;
+ mb.m_pkthdr.len += mb.m_len;
+ bpf_mtap(sc->sc_drvbpf, &mb, BPF_DIRECTION_IN);
+ }
+#endif
+
+ s = splnet();
+ ni = ieee80211_find_rxnode(ic, wh);
+ ieee80211_input(ifp, m, ni, (int)betoh32(desc->rssi), 0);
+
+ /* node is no longer needed */
+ ieee80211_release_node(ic, ni);
+ splx(s);
+
+skip: /* setup a new transfer */
+ usbd_setup_xfer(xfer, sc->data_rx_pipe, data, data->buf, sc->rxbufsz,
+ USBD_SHORT_XFER_OK, USBD_NO_TIMEOUT, uath_data_rxeof);
+ (void)usbd_transfer(xfer);
+}
+
+Static int
+uath_tx_null(struct uath_softc *sc)
+{
+ struct uath_tx_data *data;
+ struct uath_tx_desc *desc;
+
+ data = &sc->tx_data[sc->data_idx];
+
+ data->ni = NULL;
+
+ *(uint32_t *)data->buf = UATH_MAKECTL(1, sizeof (struct uath_tx_desc));
+ desc = (struct uath_tx_desc *)(data->buf + sizeof (uint32_t));
+
+ bzero(desc, sizeof (struct uath_tx_desc));
+ desc->len = htobe32(sizeof (struct uath_tx_desc));
+ desc->type = htobe32(UATH_TX_NULL);
+
+ usbd_setup_xfer(data->xfer, sc->data_tx_pipe, data, data->buf,
+ sizeof (uint32_t) + sizeof (struct uath_tx_desc), USBD_NO_COPY |
+ USBD_FORCE_SHORT_XFER, UATH_DATA_TIMEOUT, NULL);
+ if (usbd_sync_transfer(data->xfer) != 0)
+ return EIO;
+
+ sc->data_idx = (sc->data_idx + 1) % UATH_TX_DATA_LIST_COUNT;
+
+ return uath_cmd_write(sc, UATH_CMD_0F, NULL, 0, UATH_CMD_FLAG_ASYNC);
+}
+
+Static void
+uath_data_txeof(usbd_xfer_handle xfer, usbd_private_handle priv,
+ usbd_status status)
+{
+ struct uath_tx_data *data = priv;
+ struct uath_softc *sc = data->sc;
+ struct ieee80211com *ic = &sc->sc_ic;
+ struct ifnet *ifp = &ic->ic_if;
+ int s;
+
+ if (status != USBD_NORMAL_COMPLETION) {
+ if (status == USBD_NOT_STARTED || status == USBD_CANCELLED)
+ return;
+
+ printf("%s: could not transmit buffer: %s\n",
+ USBDEVNAME(sc->sc_dev), usbd_errstr(status));
+
+ if (status == USBD_STALLED)
+ usbd_clear_endpoint_stall_async(sc->data_tx_pipe);
+
+ ifp->if_oerrors++;
+ return;
+ }
+
+ s = splnet();
+
+ ieee80211_release_node(ic, data->ni);
+ data->ni = NULL;
+
+ sc->tx_queued--;
+ ifp->if_opackets++;
+
+ sc->sc_tx_timer = 0;
+ ifp->if_flags &= ~IFF_OACTIVE;
+ uath_start(ifp);
+
+ splx(s);
+}
+
+Static int
+uath_tx_data(struct uath_softc *sc, struct mbuf *m0, struct ieee80211_node *ni)
+{
+ struct ieee80211com *ic = &sc->sc_ic;
+ struct uath_tx_data *data;
+ struct uath_tx_desc *desc;
+ const struct ieee80211_frame *wh;
+ int paylen, totlen, xferlen, error;
+
+ data = &sc->tx_data[sc->data_idx];
+ desc = (struct uath_tx_desc *)(data->buf + sizeof (uint32_t));
+
+ data->ni = ni;
+
+#if NBPFILTER > 0
+ if (sc->sc_drvbpf != NULL) {
+ struct mbuf mb;
+ struct uath_tx_radiotap_header *tap = &sc->sc_txtap;
+
+ tap->wt_flags = 0;
+ tap->wt_chan_freq = htole16(ic->ic_bss->ni_chan->ic_freq);
+ tap->wt_chan_flags = htole16(ic->ic_bss->ni_chan->ic_flags);
+
+ M_DUP_PKTHDR(&mb, m0);
+ mb.m_data = (caddr_t)tap;
+ mb.m_len = sc->sc_txtap_len;
+ mb.m_next = m0;
+ mb.m_pkthdr.len += mb.m_len;
+ bpf_mtap(sc->sc_drvbpf, &mb, BPF_DIRECTION_OUT);
+ }
+#endif
+
+ paylen = m0->m_pkthdr.len;
+ xferlen = sizeof (uint32_t) + sizeof (struct uath_tx_desc) + paylen;
+
+ wh = mtod(m0, struct ieee80211_frame *);
+ if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
+ uint8_t *frm = (uint8_t *)(desc + 1);
+ uint32_t iv;
+
+ /* h/w WEP: it's up to the host to fill the IV field */
+ bcopy(wh, frm, sizeof (struct ieee80211_frame));
+ frm += sizeof (struct ieee80211_frame);
+
+ /* insert IV: code copied from net80211 */
+ iv = (ic->ic_iv != 0) ? ic->ic_iv : arc4random();
+ if (iv >= 0x03ff00 && (iv & 0xf8ff00) == 0x00ff00)
+ iv += 0x000100;
+ ic->ic_iv = iv + 1;
+
+ *frm++ = iv & 0xff;
+ *frm++ = (iv >> 8) & 0xff;
+ *frm++ = (iv >> 16) & 0xff;
+ *frm++ = ic->ic_wep_txkey << 6;
+
+ m_copydata(m0, sizeof (struct ieee80211_frame),
+ m0->m_pkthdr.len - sizeof (struct ieee80211_frame), frm);
+
+ paylen += IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN;
+ xferlen += IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN;
+ totlen = xferlen + IEEE80211_WEP_CRCLEN;
+ } else {
+ m_copydata(m0, 0, m0->m_pkthdr.len, (uint8_t *)(desc + 1));
+ totlen = xferlen;
+ }
+
+ /* fill Tx descriptor */
+ *(uint32_t *)data->buf = UATH_MAKECTL(1, xferlen - sizeof (uint32_t));
+
+ desc->len = htobe32(totlen);
+ desc->priv = sc->data_idx; /* don't care about endianness */
+ desc->paylen = htobe32(paylen);
+ desc->type = htobe32(UATH_TX_DATA);
+ desc->flags = htobe32(0);
+ if (IEEE80211_IS_MULTICAST(wh->i_addr1)) {
+ desc->dest = htobe32(UATH_ID_BROADCAST);
+ desc->magic = htobe32(3);
+ } else {
+ desc->dest = htobe32(UATH_ID_BSS);
+ desc->magic = htobe32(1);
+ }
+
+ m_freem(m0); /* mbuf is no longer needed */
+
+#ifdef UATH_DEBUG
+ if (uath_debug >= 6) {
+ printf("sending frame index=%u len=%d xferlen=%d",
+ sc->data_idx, paylen, xferlen);
+ uath_dump_cmd(data->buf, xferlen, '+');
+ }
+#endif
+ usbd_setup_xfer(data->xfer, sc->data_tx_pipe, data, data->buf, xferlen,
+ USBD_FORCE_SHORT_XFER | USBD_NO_COPY, UATH_DATA_TIMEOUT,
+ uath_data_txeof);
+ error = usbd_transfer(data->xfer);
+ if (error != USBD_IN_PROGRESS && error != 0) {
+ ic->ic_if.if_oerrors++;
+ return error;
+ }
+ sc->data_idx = (sc->data_idx + 1) % UATH_TX_DATA_LIST_COUNT;
+ sc->tx_queued++;
+
+ return 0;
+}
+
+Static void
+uath_start(struct ifnet *ifp)
+{
+ struct uath_softc *sc = ifp->if_softc;
+ struct ieee80211com *ic = &sc->sc_ic;
+ struct ieee80211_node *ni;
+ struct mbuf *m0;
+
+ /*
+ * net80211 may still try to send management frames even if the
+ * IFF_RUNNING flag is not set...
+ */
+ if ((ifp->if_flags & (IFF_RUNNING | IFF_OACTIVE)) != IFF_RUNNING)
+ return;
+
+ for (;;) {
+ IF_POLL(&ic->ic_mgtq, m0);
+ if (m0 != NULL) {
+ if (sc->tx_queued >= UATH_TX_DATA_LIST_COUNT) {
+ ifp->if_flags |= IFF_OACTIVE;
+ break;
+ }
+ IF_DEQUEUE(&ic->ic_mgtq, m0);
+
+ ni = (struct ieee80211_node *)m0->m_pkthdr.rcvif;
+ m0->m_pkthdr.rcvif = NULL;
+#if NBPFILTER > 0
+ if (ic->ic_rawbpf != NULL)
+ bpf_mtap(ic->ic_rawbpf, m0, BPF_DIRECTION_OUT);
+#endif
+ if (uath_tx_data(sc, m0, ni) != 0)
+ break;
+ } else {
+ if (ic->ic_state != IEEE80211_S_RUN)
+ break;
+ IFQ_DEQUEUE(&ifp->if_snd, m0);
+ if (m0 == NULL)
+ break;
+ if (sc->tx_queued >= UATH_TX_DATA_LIST_COUNT) {
+ IF_PREPEND(&ifp->if_snd, m0);
+ ifp->if_flags |= IFF_OACTIVE;
+ break;
+ }
+#if NBPFILTER > 0
+ if (ifp->if_bpf != NULL)
+ bpf_mtap(ifp->if_bpf, m0, BPF_DIRECTION_OUT);
+#endif
+ m0 = ieee80211_encap(ifp, m0, &ni);
+ if (m0 == NULL)
+ continue;
+#if NBPFILTER > 0
+ if (ic->ic_rawbpf != NULL)
+ bpf_mtap(ic->ic_rawbpf, m0, BPF_DIRECTION_OUT);
+#endif
+ if (uath_tx_data(sc, m0, ni) != 0) {
+ if (ni != NULL)
+ ieee80211_release_node(ic, ni);
+ ifp->if_oerrors++;
+ break;
+ }
+ }
+
+ sc->sc_tx_timer = 5;
+ ifp->if_timer = 1;
+ }
+}
+
+Static void
+uath_watchdog(struct ifnet *ifp)
+{
+ struct uath_softc *sc = ifp->if_softc;
+
+ ifp->if_timer = 0;
+
+ if (sc->sc_tx_timer > 0) {
+ if (--sc->sc_tx_timer == 0) {
+ printf("%s: device timeout\n", USBDEVNAME(sc->sc_dev));
+ /*uath_init(ifp); XXX needs a process context! */
+ ifp->if_oerrors++;
+ return;
+ }
+ ifp->if_timer = 1;
+ }
+
+ ieee80211_watchdog(ifp);
+}
+
+Static int
+uath_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
+{
+ struct uath_softc *sc = ifp->if_softc;
+ struct ieee80211com *ic = &sc->sc_ic;
+ struct ifaddr *ifa;
+ struct ifreq *ifr;
+ int s, error = 0;
+
+ s = splnet();
+
+ switch (cmd) {
+ case SIOCSIFADDR:
+ ifa = (struct ifaddr *)data;
+ ifp->if_flags |= IFF_UP;
+#ifdef INET
+ if (ifa->ifa_addr->sa_family == AF_INET)
+ arp_ifinit(&ic->ic_ac, ifa);
+#endif
+ /* FALLTHROUGH */
+ case SIOCSIFFLAGS:
+ if (ifp->if_flags & IFF_UP) {
+ if (!(ifp->if_flags & IFF_RUNNING))
+ uath_init(ifp);
+ } else {
+ if (ifp->if_flags & IFF_RUNNING)
+ uath_stop(ifp, 1);
+ }
+ break;
+
+ case SIOCADDMULTI:
+ case SIOCDELMULTI:
+ ifr = (struct ifreq *)data;
+ error = (cmd == SIOCADDMULTI) ?
+ ether_addmulti(ifr, &ic->ic_ac) :
+ ether_delmulti(ifr, &ic->ic_ac);
+ if (error == ENETRESET)
+ error = 0;
+ break;
+
+ default:
+ error = ieee80211_ioctl(ifp, cmd, data);
+ }
+
+ if (error == ENETRESET) {
+ if ((ifp->if_flags & (IFF_UP | IFF_RUNNING)) ==
+ (IFF_UP | IFF_RUNNING))
+ uath_init(ifp);
+ error = 0;
+ }
+
+ splx(s);
+
+ return error;
+}
+
+Static int
+uath_query_eeprom(struct uath_softc *sc)
+{
+ uint32_t tmp;
+ int error;
+
+ /* retrieve MAC address */
+ error = uath_read_eeprom(sc, UATH_EEPROM_MACADDR, sc->sc_ic.ic_myaddr);
+ if (error != 0) {
+ printf("%s: could not read MAC address\n",
+ USBDEVNAME(sc->sc_dev));
+ return error;
+ }
+
+ /* retrieve the maximum frame size that the hardware can receive */
+ error = uath_read_eeprom(sc, UATH_EEPROM_RXBUFSZ, &tmp);
+ if (error != 0) {
+ printf("%s: could not read maximum Rx buffer size\n",
+ USBDEVNAME(sc->sc_dev));
+ return error;
+ }
+ sc->rxbufsz = betoh32(tmp) & 0xfff;
+ DPRINTF(("maximum Rx buffer size %d\n", sc->rxbufsz));
+ return 0;
+}
+
+Static int
+uath_reset(struct uath_softc *sc)
+{
+ struct uath_cmd_setup setup;
+uint32_t reg, val;
+ int s, error;
+
+ /* init device with some voodoo incantations.. */
+ setup.magic1 = htobe32(1);
+ setup.magic2 = htobe32(5);
+ setup.magic3 = htobe32(200);
+ setup.magic4 = htobe32(27);
+ s = splusb();
+ error = uath_cmd_write(sc, UATH_CMD_SETUP, &setup, sizeof setup,
+ UATH_CMD_FLAG_ASYNC);
+ /* ..and wait until firmware notifies us that it is ready */
+ if (error == 0)
+ error = tsleep(UATH_COND_INIT(sc), PCATCH, "uathinit", 5 * hz);
+ splx(s);
+
+ /* read PHY registers */
+ for (reg = 0x09; reg <= 0x24; reg++) {
+ if (reg == 0x0b || reg == 0x0c)
+ continue;
+ if ((error = uath_read_reg(sc, reg, &val)) != 0)
+ return error;
+ DPRINTFN(2, ("reg 0x%02x=0x%08x\n", reg, val));
+ }
+ return error;
+}
+
+Static int
+uath_reset_tx_queues(struct uath_softc *sc)
+{
+ int ac, error;
+
+ for (ac = 0; ac < 4; ac++) {
+ const uint32_t qid = htobe32(UATH_AC_TO_QID(ac));
+
+ DPRINTF(("resetting Tx queue %d\n", UATH_AC_TO_QID(ac)));
+ error = uath_cmd_write(sc, UATH_CMD_RESET_QUEUE, &qid,
+ sizeof qid, 0);
+ if (error != 0)
+ break;
+ }
+ return error;
+}
+
+Static int
+uath_wme_init(struct uath_softc *sc)
+{
+ struct uath_qinfo qinfo;
+ int ac, error;
+ static const struct uath_wme_settings uath_wme_11g[4] = {
+ { 7, 4, 10, 0, 0 }, /* Background */
+ { 3, 4, 10, 0, 0 }, /* Best-Effort */
+ { 3, 3, 4, 26, 0 }, /* Video */
+ { 2, 2, 3, 47, 0 } /* Voice */
+ };
+
+ bzero(&qinfo, sizeof qinfo);
+ qinfo.size = htobe32(32);
+ qinfo.magic1 = htobe32(1); /* XXX ack policy? */
+ qinfo.magic2 = htobe32(1);
+ for (ac = 0; ac < 4; ac++) {
+ qinfo.qid = htobe32(UATH_AC_TO_QID(ac));
+ qinfo.ac = htobe32(ac);
+ qinfo.aifsn = htobe32(uath_wme_11g[ac].aifsn);
+ qinfo.logcwmin = htobe32(uath_wme_11g[ac].logcwmin);
+ qinfo.logcwmax = htobe32(uath_wme_11g[ac].logcwmax);
+ qinfo.txop = htobe32(UATH_TXOP_TO_US(
+ uath_wme_11g[ac].txop));
+ qinfo.acm = htobe32(uath_wme_11g[ac].acm);
+
+ DPRINTF(("setting up Tx queue %d\n", UATH_AC_TO_QID(ac)));
+ error = uath_cmd_write(sc, UATH_CMD_SET_QUEUE, &qinfo,
+ sizeof qinfo, 0);
+ if (error != 0)
+ break;
+ }
+ return error;
+}
+
+Static int
+uath_set_chan(struct uath_softc *sc, struct ieee80211_channel *c)
+{
+#ifdef UATH_DEBUG
+ struct ieee80211com *ic = &sc->sc_ic;
+#endif
+ struct uath_set_chan chan;
+
+ bzero(&chan, sizeof chan);
+ chan.flags = htobe32(0x1400);
+ chan.freq = htobe32(c->ic_freq);
+ chan.magic1 = htobe32(20);
+ chan.magic2 = htobe32(50);
+ chan.magic3 = htobe32(1);
+
+ DPRINTF(("switching to channel %d\n", ieee80211_chan2ieee(ic, c)));
+ return uath_cmd_write(sc, UATH_CMD_SET_CHAN, &chan, sizeof chan, 0);
+}
+
+Static int
+uath_set_key(struct uath_softc *sc, const struct ieee80211_wepkey *wk,
+ int index)
+{
+ struct uath_cmd_crypto crypto;
+ int i;
+
+ bzero(&crypto, sizeof crypto);
+ crypto.keyidx = htobe32(index);
+ crypto.magic1 = htobe32(1);
+ crypto.size = htobe32(368);
+ crypto.mask = htobe32(0xffff);
+ crypto.flags = htobe32(0x80000068);
+ if (index != UATH_DEFAULT_KEY)
+ crypto.flags |= htobe32(index << 16);
+ memset(crypto.magic2, 0xff, sizeof crypto.magic2);
+
+ /*
+ * Each byte of the key must be XOR'ed with 10101010 before being
+ * transmitted to the firmware.
+ */
+ for (i = 0; i < wk->wk_len; i++)
+ crypto.key[i] = wk->wk_key[i] ^ 0xaa;
+
+ DPRINTF(("setting crypto key index=%d len=%d\n", index, wk->wk_len));
+ return uath_cmd_write(sc, UATH_CMD_CRYPTO, &crypto, sizeof crypto, 0);
+}
+
+Static int
+uath_set_keys(struct uath_softc *sc)
+{
+ const struct ieee80211com *ic = &sc->sc_ic;
+ int i, error;
+
+ for (i = 0; i < IEEE80211_WEP_NKID; i++) {
+ const struct ieee80211_wepkey *wk = &ic->ic_nw_keys[i];
+
+ if (wk->wk_len > 0 &&
+ (error = uath_set_key(sc, wk, i)) != 0)
+ return error;
+ }
+ return uath_set_key(sc, &ic->ic_nw_keys[ic->ic_wep_txkey],
+ UATH_DEFAULT_KEY);
+}
+
+Static int
+uath_set_rates(struct uath_softc *sc, const struct ieee80211_rateset *rs)
+{
+ struct uath_cmd_rates rates;
+
+ bzero(&rates, sizeof rates);
+ rates.magic1 = htobe32(0x02);
+ rates.magic2 = htobe32(0x21);
+ rates.nrates = rs->rs_nrates;
+ bcopy(rs->rs_rates, rates.rates, rs->rs_nrates);
+
+ DPRINTF(("setting supported rates nrates=%d\n", rs->rs_nrates));
+ return uath_cmd_write(sc, UATH_CMD_SET_RATES, &rates,
+ 3 * 4 + 1 + rs->rs_nrates, 0);
+}
+
+Static int
+uath_set_rxfilter(struct uath_softc *sc, uint32_t filter, uint32_t flags)
+{
+ struct uath_cmd_filter rxfilter;
+
+ rxfilter.filter = htobe32(filter);
+ rxfilter.flags = htobe32(flags);
+
+ DPRINTF(("setting Rx filter=0x%x flags=0x%x\n", filter, flags));
+ return uath_cmd_write(sc, UATH_CMD_SET_FILTER, &rxfilter,
+ sizeof rxfilter, 0);
+}
+
+Static int
+uath_set_led(struct uath_softc *sc, int which, int on)
+{
+ struct uath_cmd_led led;
+
+ led.which = htobe32(which);
+ led.state = htobe32(on ? UATH_LED_ON : UATH_LED_OFF);
+
+ DPRINTFN(2, ("switching %s led %s\n",
+ (which == UATH_LED_LINK) ? "link" : "activity",
+ on ? "on" : "off"));
+ return uath_cmd_write(sc, UATH_CMD_SET_LED, &led, sizeof led, 0);
+}
+
+Static int
+uath_switch_channel(struct uath_softc *sc, struct ieee80211_channel *c)
+{
+ uint32_t val;
+ int error;
+
+ /* set radio frequency */
+ if ((error = uath_set_chan(sc, c)) != 0) {
+ printf("%s: could not set channel\n", USBDEVNAME(sc->sc_dev));
+ return error;
+ }
+
+ /* reset Tx rings */
+ if ((error = uath_reset_tx_queues(sc)) != 0) {
+ printf("%s: could not reset Tx queues\n",
+ USBDEVNAME(sc->sc_dev));
+ return error;
+ }
+
+ /* set Tx rings WME properties */
+ if ((error = uath_wme_init(sc)) != 0) {
+ printf("%s: could not init Tx queues\n",
+ USBDEVNAME(sc->sc_dev));
+ return error;
+ }
+
+ val = htobe32(0);
+ error = uath_cmd_write(sc, UATH_CMD_SET_STATE, &val, sizeof val, 0);
+ if (error != 0) {
+ printf("%s: could not set state\n", USBDEVNAME(sc->sc_dev));
+ return error;
+ }
+
+ return uath_tx_null(sc);
+}
+
+Static int
+uath_init(struct ifnet *ifp)
+{
+ struct uath_softc *sc = ifp->if_softc;
+ struct ieee80211com *ic = &sc->sc_ic;
+ struct uath_cmd_31 cmd31;
+ uint32_t /*reg,*/ val;
+ int i, error;
+
+ /* reset data and command rings */
+ sc->tx_queued = sc->data_idx = sc->cmd_idx = 0;
+
+ val = htobe32(0);
+ (void)uath_cmd_write(sc, UATH_CMD_02, &val, sizeof val, 0);
+
+ /* set MAC address */
+ IEEE80211_ADDR_COPY(ic->ic_myaddr, LLADDR(ifp->if_sadl));
+ (void)uath_write_multi(sc, 0x13, ic->ic_myaddr, IEEE80211_ADDR_LEN);
+
+ (void)uath_write_reg(sc, 0x02, 0x00000001);
+ (void)uath_write_reg(sc, 0x0e, 0x0000003f);
+ (void)uath_write_reg(sc, 0x10, 0x00000001);
+ (void)uath_write_reg(sc, 0x06, 0x0000001e);
+
+ /*
+ * Queue Rx data xfers.
+ */
+ for (i = 0; i < UATH_RX_DATA_LIST_COUNT; i++) {
+ struct uath_rx_data *data = &sc->rx_data[i];
+
+ usbd_setup_xfer(data->xfer, sc->data_rx_pipe, data, data->buf,
+ sc->rxbufsz, USBD_SHORT_XFER_OK, USBD_NO_TIMEOUT,
+ uath_data_rxeof);
+ error = usbd_transfer(data->xfer);
+ if (error != USBD_IN_PROGRESS && error != 0) {
+ printf("%s: could not queue Rx transfer\n",
+ USBDEVNAME(sc->sc_dev));
+ goto fail;
+ }
+ }
+
+ error = uath_cmd_read(sc, UATH_CMD_07, 0, NULL, &val,
+ UATH_CMD_FLAG_MAGIC);
+ if (error != 0) {
+ printf("%s: could not send read command 07h\n",
+ USBDEVNAME(sc->sc_dev));
+ goto fail;
+ }
+ DPRINTF(("command 07h return code: %x\n", betoh32(val)));
+
+ /* set default channel */
+ ic->ic_bss->ni_chan = ic->ic_ibss_chan;
+ if ((error = uath_set_chan(sc, ic->ic_bss->ni_chan)) != 0) {
+ printf("%s: could not set channel\n", USBDEVNAME(sc->sc_dev));
+ goto fail;
+ }
+
+ if ((error = uath_wme_init(sc)) != 0) {
+ printf("%s: could not setup WME parameters\n",
+ USBDEVNAME(sc->sc_dev));
+ goto fail;
+ }
+
+ /* init MAC registers */
+ (void)uath_write_reg(sc, 0x19, 0x00000000);
+ (void)uath_write_reg(sc, 0x1a, 0x0000003c);
+ (void)uath_write_reg(sc, 0x1b, 0x0000003c);
+ (void)uath_write_reg(sc, 0x1c, 0x00000000);
+ (void)uath_write_reg(sc, 0x1e, 0x00000000);
+ (void)uath_write_reg(sc, 0x1f, 0x00000003);
+ (void)uath_write_reg(sc, 0x0c, 0x00000000);
+ (void)uath_write_reg(sc, 0x0f, 0x00000002);
+ (void)uath_write_reg(sc, 0x0a, 0x00000007); /* XXX retry? */
+ (void)uath_write_reg(sc, 0x09, ic->ic_rtsthreshold);
+
+ val = htobe32(4);
+ (void)uath_cmd_write(sc, UATH_CMD_27, &val, sizeof val, 0);
+ (void)uath_cmd_write(sc, UATH_CMD_27, &val, sizeof val, 0);
+ (void)uath_cmd_write(sc, UATH_CMD_1B, NULL, 0, 0);
+
+ if ((error = uath_set_keys(sc)) != 0) {
+ printf("%s: could not set crypto keys\n",
+ USBDEVNAME(sc->sc_dev));
+ goto fail;
+ }
+
+ /* enable Rx */
+ (void)uath_set_rxfilter(sc, 0x0000, 4);
+ (void)uath_set_rxfilter(sc, 0x0817, 1);
+
+ cmd31.magic1 = htobe32(0xffffffff);
+ cmd31.magic2 = htobe32(0xffffffff);
+ (void)uath_cmd_write(sc, UATH_CMD_31, &cmd31, sizeof cmd31, 0);
+
+ ifp->if_flags &= ~IFF_OACTIVE;
+ ifp->if_flags |= IFF_RUNNING;
+
+ if (ic->ic_opmode == IEEE80211_M_MONITOR)
+ ieee80211_new_state(ic, IEEE80211_S_RUN, -1);
+ else
+ ieee80211_new_state(ic, IEEE80211_S_SCAN, -1);
+
+ return 0;
+
+fail: uath_stop(ifp, 1);
+ return error;
+}
+
+Static void
+uath_stop(struct ifnet *ifp, int disable)
+{
+ struct uath_softc *sc = ifp->if_softc;
+ struct ieee80211com *ic = &sc->sc_ic;
+ uint32_t val;
+ int s;
+
+ s = splusb();
+
+ sc->sc_tx_timer = 0;
+ ifp->if_timer = 0;
+ ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE);
+
+ ieee80211_new_state(ic, IEEE80211_S_INIT, -1); /* free all nodes */
+
+ val = htobe32(0);
+ (void)uath_cmd_write(sc, UATH_CMD_SET_STATE, &val, sizeof val, 0);
+ (void)uath_cmd_write(sc, UATH_CMD_RESET, NULL, 0, 0);
+
+ val = htobe32(0);
+ (void)uath_cmd_write(sc, UATH_CMD_15, &val, sizeof val, 0);
+
+#if 0
+ (void)uath_cmd_read(sc, UATH_CMD_SHUTDOWN, NULL, 0, NULL,
+ UATH_CMD_FLAG_MAGIC);
+#endif
+
+ /* abort any pending transfers */
+ usbd_abort_pipe(sc->data_tx_pipe);
+ usbd_abort_pipe(sc->data_rx_pipe);
+ usbd_abort_pipe(sc->cmd_tx_pipe);
+ usbd_abort_pipe(sc->cmd_rx_pipe);
+
+ splx(s);
+}
+
+/*
+ * Load the MIPS R4000 microcode into the device. Once the image is loaded,
+ * the device will detach itself from the bus and reattach later with a new
+ * product Id (a la ezusb). XXX this could also be implemented in userland
+ * through /dev/ugen.
+ */
+Static int
+uath_loadfirmware(struct uath_softc *sc, const u_char *fw, int len)
+{
+ usbd_xfer_handle ctlxfer, txxfer, rxxfer;
+ struct uath_fwblock *txblock, *rxblock;
+ uint8_t *txdata;
+ int error = 0;
+
+ if ((ctlxfer = usbd_alloc_xfer(sc->sc_udev)) == NULL) {
+ printf("%s: could not allocate Tx control xfer\n",
+ USBDEVNAME(sc->sc_dev));
+ error = USBD_NOMEM;
+ goto fail1;
+ }
+ txblock = usbd_alloc_buffer(ctlxfer, sizeof (struct uath_fwblock));
+ if (txblock == NULL) {
+ printf("%s: could not allocate Tx control block\n",
+ USBDEVNAME(sc->sc_dev));
+ error = USBD_NOMEM;
+ goto fail2;
+ }
+
+ if ((txxfer = usbd_alloc_xfer(sc->sc_udev)) == NULL) {
+ printf("%s: could not allocate Tx xfer\n",
+ USBDEVNAME(sc->sc_dev));
+ error = USBD_NOMEM;
+ goto fail2;
+ }
+ txdata = usbd_alloc_buffer(txxfer, UATH_MAX_FWBLOCK_SIZE);
+ if (txdata == NULL) {
+ printf("%s: could not allocate Tx buffer\n",
+ USBDEVNAME(sc->sc_dev));
+ error = USBD_NOMEM;
+ goto fail3;
+ }
+
+ if ((rxxfer = usbd_alloc_xfer(sc->sc_udev)) == NULL) {
+ printf("%s: could not allocate Rx control xfer\n",
+ USBDEVNAME(sc->sc_dev));
+ error = USBD_NOMEM;
+ goto fail3;
+ }
+ rxblock = usbd_alloc_buffer(rxxfer, sizeof (struct uath_fwblock));
+ if (rxblock == NULL) {
+ printf("%s: could not allocate Rx control block\n",
+ USBDEVNAME(sc->sc_dev));
+ error = USBD_NOMEM;
+ goto fail4;
+ }
+
+ bzero(txblock, sizeof (struct uath_fwblock));
+ txblock->flags = htobe32(UATH_WRITE_BLOCK);
+ txblock->total = htobe32(len);
+
+ while (len > 0) {
+ int mlen = min(len, UATH_MAX_FWBLOCK_SIZE);
+
+ txblock->remain = htobe32(len - mlen);
+ txblock->len = htobe32(mlen);
+
+ DPRINTF(("sending firmware block: %d bytes remaining\n",
+ len - mlen));
+
+ /* send firmware block meta-data */
+ usbd_setup_xfer(ctlxfer, sc->cmd_tx_pipe, sc, txblock,
+ sizeof (struct uath_fwblock), USBD_NO_COPY,
+ UATH_CMD_TIMEOUT, NULL);
+ if ((error = usbd_sync_transfer(ctlxfer)) != 0) {
+ printf("%s: could not send firmware block info\n",
+ USBDEVNAME(sc->sc_dev));
+ break;
+ }
+
+ /* send firmware block data */
+ bcopy(fw, txdata, mlen);
+ usbd_setup_xfer(txxfer, sc->data_tx_pipe, sc, txdata, mlen,
+ USBD_NO_COPY, UATH_DATA_TIMEOUT, NULL);
+ if ((error = usbd_sync_transfer(txxfer)) != 0) {
+ printf("%s: could not send firmware block data\n",
+ USBDEVNAME(sc->sc_dev));
+ break;
+ }
+
+ /* wait for ack from firmware */
+ usbd_setup_xfer(rxxfer, sc->cmd_rx_pipe, sc, rxblock,
+ sizeof (struct uath_fwblock), USBD_SHORT_XFER_OK |
+ USBD_NO_COPY, UATH_CMD_TIMEOUT, NULL);
+ if ((error = usbd_sync_transfer(rxxfer)) != 0) {
+ printf("%s: could not read firmware answer\n",
+ USBDEVNAME(sc->sc_dev));
+ break;
+ }
+
+ DPRINTFN(2, ("rxblock flags=0x%x total=%d\n",
+ betoh32(rxblock->flags), betoh32(rxblock->rxtotal)));
+ fw += mlen;
+ len -= mlen;
+ }
+
+fail4: usbd_free_xfer(rxxfer);
+fail3: usbd_free_xfer(txxfer);
+fail2: usbd_free_xfer(ctlxfer);
+fail1: return error;
+}
+
+Static int
+uath_activate(device_ptr_t self, enum devact act)
+{
+ switch (act) {
+ case DVACT_ACTIVATE:
+ break;
+
+ case DVACT_DEACTIVATE:
+ /*if_deactivate(&sc->sc_ic.ic_if);*/
+ break;
+ }
+ return 0;
+}
diff --git a/sys/dev/usb/if_uathreg.h b/sys/dev/usb/if_uathreg.h
new file mode 100644
index 00000000000..1b46adaea45
--- /dev/null
+++ b/sys/dev/usb/if_uathreg.h
@@ -0,0 +1,248 @@
+/* $OpenBSD: if_uathreg.h,v 1.1 2006/09/16 13:21:24 damien Exp $ */
+
+/*-
+ * Copyright (c) 2006
+ * Damien Bergamini <damien.bergamini@free.fr>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#define UATH_CONFIG_NO 1
+#define UATH_IFACE_INDEX 0
+
+/* all fields are big endian */
+struct uath_fwblock {
+ uint32_t flags;
+#define UATH_WRITE_BLOCK (1 << 4)
+
+ uint32_t len;
+#define UATH_MAX_FWBLOCK_SIZE 2048
+
+ uint32_t total;
+ uint32_t remain;
+ uint32_t rxtotal;
+ uint32_t pad[123];
+} __packed;
+
+#define UATH_MAX_RXCMDSZ 512
+#define UATH_MAX_TXCMDSZ 512
+
+struct uath_cmd_hdr {
+ uint32_t len;
+ uint32_t code;
+#define UATH_CMD_SETUP 0x01
+#define UATH_CMD_02 0x02
+#define UATH_CMD_READ_MAC 0x03
+#define UATH_CMD_WRITE_MAC 0x04
+#define UATH_CMD_READ_EEPROM 0x05
+#define UATH_CMD_STATS 0x06
+#define UATH_CMD_07 0x07
+#define UATH_CMD_SHUTDOWN 0x08
+#define UATH_CMD_0B 0x0b
+#define UATH_CMD_0C 0x0c
+#define UATH_CMD_0F 0x0f
+#define UATH_NOTIF_STATS 0x10
+#define UATH_NOTIF_READY 0x12
+#define UATH_NOTIF_TX 0x13
+#define UATH_CMD_15 0x15
+#define UATH_CMD_SET_LED 0x17
+#define UATH_CMD_SET_XLED 0x18
+#define UATH_CMD_1B 0x1b
+#define UATH_CMD_1E 0x1e
+#define UATH_CMD_CRYPTO 0x1d
+#define UATH_CMD_SET_STATE 0x20
+#define UATH_CMD_SET_BSSID 0x21
+#define UATH_CMD_24 0x24
+#define UATH_CMD_SET_RATES 0x26
+#define UATH_CMD_27 0x27
+#define UATH_CMD_2E 0x2e
+#define UATH_CMD_31 0x31
+#define UATH_CMD_SET_FILTER 0x32
+#define UATH_CMD_SET_CHAN 0x34
+#define UATH_CMD_RESET 0x35
+#define UATH_CMD_SET_QUEUE 0x3a
+#define UATH_CMD_RESET_QUEUE 0x3b
+
+ uint32_t priv; /* driver private data */
+ uint32_t magic;
+ uint32_t reserved2[4];
+} __packed;
+
+struct uath_rx_desc {
+ uint32_t len;
+ uint32_t reserved1[8];
+ uint32_t rssi;
+ uint32_t freq;
+ uint32_t reserved2[5];
+} __packed;
+
+#define UATH_MAKECTL(qid, len) htobe32((qid) << 16 | (len))
+
+struct uath_tx_desc {
+ uint32_t len;
+ uint32_t priv; /* driver private data */
+ uint32_t type;
+#define UATH_TX_DATA 0xe
+#define UATH_TX_NULL 0xf
+
+ uint32_t magic;
+ uint32_t dest;
+#define UATH_ID_BSS 2
+#define UATH_ID_BROADCAST 0xffffffff
+
+ uint32_t flags;
+#define UATH_TX_NOTIFY (1 << 24) /* f/w will send a UATH_NOTIF_TX */
+
+ uint32_t paylen;
+} __packed;
+
+/* structure for command UATH_CMD_SETUP */
+struct uath_cmd_setup {
+ uint32_t magic1;
+ uint32_t magic2;
+ uint32_t magic3;
+ uint32_t magic4;
+} __packed;
+
+/* structure for commands UATH_CMD_READ_MAC and UATH_CMD_READ_EEPROM */
+struct uath_read_mac {
+ uint32_t len;
+ uint8_t data[32];
+} __packed;
+
+/* structure for command UATH_CMD_WRITE_MAC */
+struct uath_write_mac {
+ uint32_t reg;
+ uint32_t len;
+ uint8_t data[32];
+} __packed;
+
+/* structure for command UATH_CMD_0B */
+struct uath_cmd_0b {
+ uint32_t code;
+ uint32_t reserved;
+ uint32_t size;
+ uint8_t data[44];
+} __packed;
+
+/* structure for command UATH_CMD_0C */
+struct uath_cmd_0c {
+ uint32_t magic1;
+ uint32_t magic2;
+ uint32_t magic3;
+} __packed;
+
+/* structure for command UATH_CMD_SET_LED */
+struct uath_cmd_led {
+ uint32_t which;
+#define UATH_LED_LINK 0
+#define UATH_LED_ACTIVITY 1
+
+ uint32_t state;
+#define UATH_LED_OFF 0
+#define UATH_LED_ON 1
+} __packed;
+
+/* structure for command UATH_CMD_SET_XLED */
+struct uath_cmd_xled {
+ uint32_t which;
+ uint32_t rate;
+ uint32_t mode;
+} __packed;
+
+/* structure for command UATH_CMD_CRYPTO */
+struct uath_cmd_crypto {
+ uint32_t keyidx;
+#define UATH_DEFAULT_KEY 6
+
+ uint32_t magic1;
+ uint32_t size;
+ uint32_t reserved1;
+ uint32_t mask;
+ uint8_t addr[IEEE80211_ADDR_LEN];
+ uint16_t reserved2;
+ uint32_t flags;
+ uint32_t reserved3[2];
+ uint8_t key[68];
+ uint8_t magic2[136];
+ uint8_t magic3[136];
+} __packed;
+
+/* structure for command UATH_CMD_SET_RATES */
+struct uath_cmd_rates {
+ uint32_t magic1;
+ uint32_t reserved;
+ uint32_t magic2;
+ uint8_t nrates;
+ uint8_t rates[IEEE80211_RATE_MAXSIZE];
+} __packed;
+
+/* structure for command UATH_CMD_SET_CHAN */
+struct uath_set_chan {
+ uint32_t flags;
+ uint32_t freq;
+ uint32_t magic1;
+ uint32_t magic2;
+ uint32_t reserved1;
+ uint32_t magic3;
+ uint32_t reserved2;
+} __packed;
+
+/* structure for command UATH_CMD_SET_QUEUE */
+struct uath_qinfo {
+ uint32_t qid;
+#define UATH_AC_TO_QID(ac) (ac) /* id function */
+
+ uint32_t size;
+ uint32_t ac;
+ uint32_t aifsn;
+ uint32_t logcwmin;
+ uint32_t logcwmax;
+ uint32_t txop;
+ uint32_t acm;
+ uint32_t magic1;
+ uint32_t magic2;
+} __packed;
+
+/* structure for command UATH_CMD_31 */
+struct uath_cmd_31 {
+ uint32_t magic1;
+ uint32_t magic2;
+} __packed;
+
+/* structure for command UATH_CMD_SET_FILTER */
+struct uath_cmd_filter {
+ uint32_t filter;
+ uint32_t flags;
+} __packed;
+
+/* structure for command UATH_CMD_SET_BSSID */
+struct uath_cmd_bssid {
+ uint32_t reserved1;
+ uint32_t flags1;
+ uint32_t flags2;
+ uint32_t reserved2;
+ uint32_t len;
+ uint8_t bssid[IEEE80211_ADDR_LEN];
+} __packed;
+
+
+#define UATH_EEPROM_MACADDR 0x0b
+#define UATH_EEPROM_RXBUFSZ 0x0f
+
+#define UATH_MAX_TXBUFSZ \
+ (sizeof (uint32_t) + sizeof (struct uath_tx_desc) + IEEE80211_MAX_LEN)
+
+#define UATH_MIN_RXBUFSZ \
+ (((sizeof (uint32_t) + sizeof (struct ieee80211_frame_min) + \
+ sizeof (struct uath_rx_desc)) + 3) & ~3)
diff --git a/sys/dev/usb/if_uathvar.h b/sys/dev/usb/if_uathvar.h
new file mode 100644
index 00000000000..fb38566b5b3
--- /dev/null
+++ b/sys/dev/usb/if_uathvar.h
@@ -0,0 +1,155 @@
+/* $OpenBSD: if_uathvar.h,v 1.1 2006/09/16 13:21:24 damien Exp $ */
+
+/*-
+ * Copyright (c) 2006
+ * Damien Bergamini <damien.bergamini@free.fr>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#define UATH_TX_DATA_LIST_COUNT 8 /* 16 */
+#define UATH_TX_CMD_LIST_COUNT 8 /* 30 */
+
+/* XXX ehci will panic on abort_pipe if set to anything > 1 */
+#define UATH_RX_DATA_LIST_COUNT 1 /* 128 */
+#define UATH_RX_CMD_LIST_COUNT 1 /* 30 */
+
+#define UATH_DATA_TIMEOUT 10000
+#define UATH_CMD_TIMEOUT 1000
+
+struct uath_rx_radiotap_header {
+ struct ieee80211_radiotap_header wr_ihdr;
+ uint8_t wr_flags;
+ uint16_t wr_chan_freq;
+ uint16_t wr_chan_flags;
+ int8_t wr_dbm_antsignal;
+} __packed;
+
+#define UATH_RX_RADIOTAP_PRESENT \
+ ((1 << IEEE80211_RADIOTAP_FLAGS) | \
+ (1 << IEEE80211_RADIOTAP_CHANNEL) | \
+ (1 << IEEE80211_RADIOTAP_DBM_ANTSIGNAL))
+
+struct uath_tx_radiotap_header {
+ struct ieee80211_radiotap_header wt_ihdr;
+ uint8_t wt_flags;
+ uint16_t wt_chan_freq;
+ uint16_t wt_chan_flags;
+} __packed;
+
+#define UATH_TX_RADIOTAP_PRESENT \
+ ((1 << IEEE80211_RADIOTAP_FLAGS) | \
+ (1 << IEEE80211_RADIOTAP_CHANNEL))
+
+struct uath_tx_data {
+ struct uath_softc *sc;
+ usbd_xfer_handle xfer;
+ uint8_t *buf;
+ struct ieee80211_node *ni;
+};
+
+struct uath_rx_data {
+ struct uath_softc *sc;
+ usbd_xfer_handle xfer;
+ uint8_t *buf;
+ struct mbuf *m;
+};
+
+struct uath_tx_cmd {
+ struct uath_softc *sc;
+ usbd_xfer_handle xfer;
+ uint8_t *buf;
+ void *odata;
+};
+
+struct uath_rx_cmd {
+ struct uath_softc *sc;
+ usbd_xfer_handle xfer;
+ uint8_t *buf;
+};
+
+struct uath_wme_settings {
+ uint8_t aifsn;
+ uint8_t logcwmin;
+ uint8_t logcwmax;
+ uint16_t txop;
+#define UATH_TXOP_TO_US(txop) ((txop) << 5)
+
+ uint8_t acm;
+};
+
+/* condvars */
+#define UATH_COND_INIT(sc) ((caddr_t)sc + 1)
+
+/* flags for sending firmware commands */
+#define UATH_CMD_FLAG_ASYNC (1 << 0)
+#define UATH_CMD_FLAG_READ (1 << 1)
+#define UATH_CMD_FLAG_MAGIC (1 << 2)
+
+struct uath_softc {
+ USBBASEDEVICE sc_dev;
+ struct ieee80211com sc_ic;
+ int (*sc_newstate)(struct ieee80211com *,
+ enum ieee80211_state, int);
+
+ struct uath_tx_data tx_data[UATH_TX_DATA_LIST_COUNT];
+ struct uath_rx_data rx_data[UATH_RX_DATA_LIST_COUNT];
+
+ struct uath_tx_cmd tx_cmd[UATH_TX_CMD_LIST_COUNT];
+ struct uath_rx_cmd rx_cmd[UATH_RX_CMD_LIST_COUNT];
+
+ int sc_flags;
+#define UATH_FLAG_PRE_FIRMWARE (1 << 0)
+#define UATH_FLAG_DUAL_BAND_RF (1 << 1)
+
+ int data_idx;
+ int cmd_idx;
+ int tx_queued;
+
+ usbd_device_handle sc_udev;
+ usbd_interface_handle sc_iface;
+
+ usbd_pipe_handle data_tx_pipe;
+ usbd_pipe_handle data_rx_pipe;
+ usbd_pipe_handle cmd_tx_pipe;
+ usbd_pipe_handle cmd_rx_pipe;
+
+ enum ieee80211_state sc_state;
+ int sc_arg;
+ struct usb_task sc_task;
+
+ struct timeout scan_to;
+ struct timeout stat_to;
+
+ int sc_tx_timer;
+
+ int rxbufsz;
+
+#if NBPFILTER > 0
+ caddr_t sc_drvbpf;
+
+ union {
+ struct uath_rx_radiotap_header th;
+ uint8_t pad[64];
+ } sc_rxtapu;
+#define sc_rxtap sc_rxtapu.th
+ int sc_rxtap_len;
+
+ union {
+ struct uath_tx_radiotap_header th;
+ uint8_t pad[64];
+ } sc_txtapu;
+#define sc_txtap sc_txtapu.th
+ int sc_txtap_len;
+#endif
+};
diff --git a/sys/dev/usb/usbdevs b/sys/dev/usb/usbdevs
index 816bcd167db..9c3cd48629a 100644
--- a/sys/dev/usb/usbdevs
+++ b/sys/dev/usb/usbdevs
@@ -1,4 +1,4 @@
-$OpenBSD: usbdevs,v 1.218 2006/08/24 03:14:43 brad Exp $
+$OpenBSD: usbdevs,v 1.219 2006/09/16 13:21:23 damien Exp $
/* $NetBSD: usbdevs,v 1.322 2003/05/10 17:47:14 hamajima Exp $ */
/*
@@ -436,6 +436,7 @@ vendor TAPWAVE 0x12ef Tapwave
vendor AINCOMM 0x12fd Aincomm
vendor MOBILITY 0x1342 Mobility
vendor DICKSMITH 0x1371 Dick Smith Electronics
+vendor NETGEAR3 0x1385 Netgear
vendor CISCOLINKSYS 0x13b1 Cisco-Linksys
vendor SHARK 0x13d2 Shark
vendor NOVATEL 0x1410 Novatel Wireless
@@ -917,6 +918,12 @@ product DLINK DSB650TX4 0x200c 10/100 ethernet
product DLINK DWL120E 0x3200 DWL-120 rev E
product DLINK DWL122 0x3700 DWL-122
product DLINK DWL120F 0x3702 DWL-120 rev F
+product DLINK DWLAG132 0x3a00 DWL-AG132
+product DLINK DWLAG132_NF 0x3a01 DWL-AG132
+product DLINK DWLG132 0x3a02 DWL-G132
+product DLINK DWLG132_NF 0x3a03 DWL-G132
+product DLINK DWLAG122 0x3a04 DWL-AG122
+product DLINK DWLAG122_NF 0x3a05 DWL-AG122
product DLINK RT2570 0x3c00 RT2570
product DLINK2 DWLG122C1 0x3c03 DWL-G122 rev C1
product DLINK2 WUA1340 0x3c04 WUA-1340
@@ -1557,6 +1564,12 @@ product NETGEAR2 MA101 0x4100 MA101
product NETGEAR2 MA101B 0x4102 MA101 Rev B
product NETGEAR MA111NA 0x4110 802.11b
product NETGEAR MA111V2 0x4230 802.11b V2
+product NETGEAR3 WG111T 0x4250 WG111T
+product NETGEAR3 WG111T_NF 0x4251 WG111T
+product NETGEAR WG111U 0x4300 WG111U
+product NETGEAR WG111U_NF 0x4301 WG111U
+product NETGEAR3 WPN111 0x5f00 WPN111
+product NETGEAR3 WPN111_NF 0x5f01 WPN111
/* Nikon products */
product NIKON E990 0x0102 E990