summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarcus Glocker <mglocker@cvs.openbsd.org>2007-12-16 09:05:54 +0000
committerMarcus Glocker <mglocker@cvs.openbsd.org>2007-12-16 09:05:54 +0000
commit9605c5d469d1183642707d7c1a5b26c9a46d4ba5 (patch)
tree2a38e795c2da129338a49ae12307531957c172ab
parent7953511a19ed4b49680282685824609caa029b7a (diff)
Inital driver for the Conexant/Intersil PrismGT SoftMAC USB 2.0 devices
based on the GW3887 chipset (b/g). We are able to communicate more or less reliable with DS1 (1Mbps) yet. Tested on i386 and macppc. Short term TODOs: - Find out how to set TX rates. - Fix device hangup while high RX load. - Improve device reset (reload firmware on init, watchdog, etc.). - Fill man page with supported devices (need reports). - Add AMRR rate control. - Add HostAP mode. - Add IBSS mode. - Add LED support. - ... Long term TODOs: - Support the USB 1.0 devices (NET2280, ISL3880, ISL3886 chipsets). OK deraadt@, OK laurent@
-rw-r--r--share/man/man4/Makefile8
-rw-r--r--share/man/man4/upgt.4332
-rw-r--r--share/man/man4/usb.46
-rw-r--r--sys/dev/usb/files.usb7
-rw-r--r--sys/dev/usb/if_upgt.c2051
-rw-r--r--sys/dev/usb/if_upgtvar.h432
-rw-r--r--sys/dev/usb/usbdevs3
7 files changed, 2831 insertions, 8 deletions
diff --git a/share/man/man4/Makefile b/share/man/man4/Makefile
index 5bfeec38431..4b88c0203fb 100644
--- a/share/man/man4/Makefile
+++ b/share/man/man4/Makefile
@@ -1,4 +1,4 @@
-# $OpenBSD: Makefile,v 1.438 2007/11/25 17:11:11 oga Exp $
+# $OpenBSD: Makefile,v 1.439 2007/12/16 09:05:52 mglocker Exp $
MAN= aac.4 ac97.4 acphy.4 \
acpi.4 acpiac.4 acpibat.4 acpibtn.4 acpicpu.4 acpidock.4 \
@@ -51,9 +51,9 @@ MAN= aac.4 ac97.4 acphy.4 \
ucom.4 uchcom.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 umbg.4 umct.4 umidi.4 umodem.4 ums.4 umsm.4 \
- unix.4 uow.4 upl.4 uplcom.4 ural.4 urio.4 url.4 urlphy.4 usb.4 usbf.4 \
- uscanner.4 uslcom.4 usscanner.4 uticom.4 uts.4 uvisor.4 uvscom.4 \
- uyap.4 vga.4 vgafb.4 vge.4 viaenv.4 viapm.4 viasio.4 vic.4 \
+ unix.4 uow.4 upgt.4 upl.4 uplcom.4 ural.4 urio.4 url.4 urlphy.4 usb.4 \
+ usbf.4 uscanner.4 uslcom.4 usscanner.4 uticom.4 uts.4 uvisor.4 \
+ uvscom.4 uyap.4 vga.4 vgafb.4 vge.4 viaenv.4 viapm.4 viasio.4 vic.4 \
vlan.4 vnd.4 vr.4 watchdog.4 wb.4 wbenv.4 wbng.4 wd.4 wdc.4 \
we.4 wi.4tbl wpi.4 wscons.4 wsdisplay.4 wskbd.4 wsmouse.4 wsmux.4 \
xe.4 xf86.4 xge.4 xl.4 xmphy.4 yds.4 ym.4 zero.4 zyd.4
diff --git a/share/man/man4/upgt.4 b/share/man/man4/upgt.4
new file mode 100644
index 00000000000..25db370a161
--- /dev/null
+++ b/share/man/man4/upgt.4
@@ -0,0 +1,332 @@
+.\" $OpenBSD: upgt.4,v 1.1 2007/12/16 09:05:52 mglocker Exp $
+.\"
+.\" Copyright (c) 2007 Marcus Glocker <mglocker@openbsd.org>
+.\" Copyright (c) 2005-2007
+.\" 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.
+.\"
+
+.\"
+.\" Copyright (c) 2006 Theo de Raadt.
+.\" Copyright (c) 2006 The DragonFly Project. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\"
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in
+.\" the documentation and/or other materials provided with the
+.\" distribution.
+.\" 3. Neither the name of The DragonFly Project nor the names of its
+.\" contributors may be used to endorse or promote products derived
+.\" from this software without specific, prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+.\" ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+.\" LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+.\" FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+.\" COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+.\" INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
+.\" BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+.\" LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+.\" AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+.\" OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+.\" OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.Dd $Mdocdate: December 16 2007 $
+.Os
+.Dt UPGT 4
+.Sh NAME
+.Nm upgt
+.Nd Conexant/Intersil PrismGT SoftMAC USB IEEE 802.11b/g wireless network
+device
+.Sh SYNOPSIS
+.Cd "upgt* at uhub? port ?"
+.Sh DESCRIPTION
+The
+.Nm
+driver supports the USB 2.0 Conexant/Intersil PrismGT series wireless
+adapters based on the GW3887 chipset.
+.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 IBSS mode
+.\" Also known as
+.\" .Em IEEE ad-hoc
+.\" mode or
+.\" .Em peer-to-peer
+.\" mode.
+.\" This is the standardized method of operating without an access point.
+.\" Stations associate with a service set.
+.\" However, actual connections between stations are peer-to-peer.
+.\" .It Host AP
+.\" In this mode the driver acts as an access point (base station)
+.\" for other cards.
+.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 software 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.
+.\".Pp
+.\"The transmit speed is user-selectable or can be adapted automatically by the
+.\"driver depending on the received signal strength and on the number of hardware
+.\"transmission retries.
+.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.11g OFDM 6Mbps operation.
+.It Cm OFDM9
+Set 802.11g OFDM 9Mbps operation.
+.It Cm OFDM12
+Set 802.11g OFDM 12Mbps operation.
+.It Cm OFDM18
+Set 802.11g OFDM 18Mbps operation.
+.It Cm OFDM24
+Set 802.11g OFDM 24Mbps operation.
+.It Cm OFDM36
+Set 802.11g OFDM 36Mbps operation.
+.It Cm OFDM48
+Set 802.11g OFDM 48Mbps operation.
+.It Cm OFDM54
+Set 802.11g 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 hostap
+.\"Select Host AP operation.
+.\".It Cm ibss
+.\"Select IBSS operation.
+.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 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 Fl nwid
+Set the network ID to the empty string to allow the interface to connect
+to any available access point.
+.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 driver needs a firmware file which is loaded when an interface is
+brought up:
+.Pp
+.Bl -tag -width Ds -offset indent -compact
+.It Pa /etc/firmware/upgt-v2
+.El
+.\".Pp
+.\"These firmware files are not free because Conexant/Intersil refuses
+.\"to grant distribution rights.
+.\"As a result, even though
+.\".Ox
+.\"includes the driver, the firmware files cannot be included and
+.\"users have to download these files on their own.
+.Pp
+A prepackaged version of the firmware, designed to be used with
+.Xr pkg_add 1 ,
+can be found at:
+.Bd -literal -offset indent
+http://www.nazgul.ch/upgt/upgt-firmware-1.0.tgz
+.Ed
+.Sh HARDWARE
+The following adapters should work:
+.Pp
+.Bl -tag -width Ds -offset indent -compact
+.It SMC EZ ConnectG SMC2862W-G
+.El
+.Sh EXAMPLES
+The following
+.Xr hostname.if 5
+example configures upgt0 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 upgt0 for WEP, using hex key
+.Dq 0x1deadbeef1 :
+.Bd -literal -offset indent
+# ifconfig upgt0 nwkey 0x1deadbeef1
+.Ed
+.Pp
+Return upgt0 to its default settings:
+.Bd -literal -offset indent
+# ifconfig upgt0 -bssid -chan media autoselect \e
+ nwid "" -nwkey
+.Ed
+.Pp
+Join an existing BSS network,
+.Dq my_net :
+.Bd -literal -offset indent
+# ifconfig upgt0 192.168.1.1 netmask 0xffffff00 nwid my_net
+.Ed
+.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
+.Sh HISTORY
+The
+.Nm
+driver first appeared in
+.Ox 4.3 .
+.Sh AUTHORS
+.An -nosplit
+The
+.Nm
+driver was written by
+.An Marcus Glocker Aq mglocker@openbsd.org .
+.Pp
+The hardware specification was reverse engineered by the people at
+.Pa http://www.prism54.org .
+.Sh CAVEATS
+The
+.Nm
+driver just supports the USB 2.0 devices (GW3887 chipset) but not the
+USB 1.0 devices containing the NET2280, ISL3880, and ISL3886 chipsets.
+Some further efforts would be necessary to add USB 1.0 support to the
+driver.
diff --git a/share/man/man4/usb.4 b/share/man/man4/usb.4
index e6c18fef9c2..39dd070b541 100644
--- a/share/man/man4/usb.4
+++ b/share/man/man4/usb.4
@@ -1,4 +1,4 @@
-.\" $OpenBSD: usb.4,v 1.99 2007/11/27 22:11:05 jmc Exp $
+.\" $OpenBSD: usb.4,v 1.100 2007/12/16 09:05:52 mglocker Exp $
.\" $NetBSD: usb.4,v 1.15 1999/07/29 14:20:32 augustss Exp $
.\"
.\" Copyright (c) 1999 The NetBSD Foundation, Inc.
@@ -35,7 +35,7 @@
.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
.\" POSSIBILITY OF SUCH DAMAGE.
.\"
-.Dd $Mdocdate: November 27 2007 $
+.Dd $Mdocdate: December 16 2007 $
.Dt USB 4
.Os
.Sh NAME
@@ -151,6 +151,8 @@ Ralink Technology USB IEEE 802.11a/b/g wireless network device
Atheros USB IEEE 802.11a/b/g wireless network device
.It Xr ubt 4
Bluetooth adapters
+.It Xr upgt 4
+Conexant/Intersil PrismGT SoftMAC USB IEEE 802.11b/g wireless network device
.It Xr ural 4
Ralink Technology USB IEEE 802.11b/g wireless network device
.It Xr wi 4
diff --git a/sys/dev/usb/files.usb b/sys/dev/usb/files.usb
index 7b86cbdb134..00d88666100 100644
--- a/sys/dev/usb/files.usb
+++ b/sys/dev/usb/files.usb
@@ -1,4 +1,4 @@
-# $OpenBSD: files.usb,v 1.73 2007/11/16 16:25:31 deraadt Exp $
+# $OpenBSD: files.usb,v 1.74 2007/12/16 09:05:53 mglocker 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.
@@ -287,6 +287,11 @@ device zyd: ether, ifnet, ifmedia, wlan, firmload
attach zyd at uhub
file dev/usb/if_zyd.c zyd
+# PrismGT
+device upgt: ether, ifnet, ifmedia, wlan, firmload
+attach upgt at uhub
+file dev/usb/if_upgt.c
+
# Analog Devices Eagle driver
device ueagle: atm, ifnet, ezload, firmload
attach ueagle at uhub
diff --git a/sys/dev/usb/if_upgt.c b/sys/dev/usb/if_upgt.c
new file mode 100644
index 00000000000..eb498ac0c8e
--- /dev/null
+++ b/sys/dev/usb/if_upgt.c
@@ -0,0 +1,2051 @@
+/* $OpenBSD: if_upgt.c,v 1.1 2007/12/16 09:05:53 mglocker Exp $ */
+
+/*
+ * Copyright (c) 2007 Marcus Glocker <mglocker@openbsd.org>
+ *
+ * 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.
+ */
+
+#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/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/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdi_util.h>
+#include <dev/usb/usbdevs.h>
+
+#include <dev/usb/if_upgtvar.h>
+
+/*
+ * Driver for the USB PrismGT devices.
+ *
+ * For now just USB 2.0 devices with the GW3887 chipset are supported.
+ */
+
+#ifdef UPGT_DEBUG
+int upgt_debug = 2;
+#define DPRINTF(l, x...) do { if ((l) <= upgt_debug) printf(x); } while (0)
+#else
+#define DPRINTF(l, x...)
+#endif
+
+/*
+ * Prototypes.
+ */
+int upgt_match(struct device *, void *, void *);
+void upgt_attach(struct device *, struct device *, void *);
+void upgt_attachhook(void *);
+int upgt_detach(struct device *, int);
+int upgt_activate(struct device *, enum devact);
+
+int upgt_device_init(struct upgt_softc *);
+int upgt_mem_init(struct upgt_softc *);
+uint32_t upgt_mem_alloc(struct upgt_softc *);
+void upgt_mem_free(struct upgt_softc *, uint32_t);
+int upgt_fw_alloc(struct upgt_softc *);
+void upgt_fw_free(struct upgt_softc *);
+int upgt_fw_verify(struct upgt_softc *);
+int upgt_fw_load(struct upgt_softc *);
+int upgt_fw_copy(char *, char *, int);
+int upgt_eeprom_read(struct upgt_softc *);
+int upgt_eeprom_parse(struct upgt_softc *);
+void upgt_eeprom_parse_hwrx(struct upgt_softc *, uint8_t *);
+void upgt_eeprom_parse_freq3(struct upgt_softc *, uint8_t *, int);
+void upgt_eeprom_parse_freq4(struct upgt_softc *, uint8_t *, int);
+void upgt_eeprom_parse_freq6(struct upgt_softc *, uint8_t *, int);
+
+int upgt_ioctl(struct ifnet *, u_long, caddr_t);
+int upgt_init(struct ifnet *);
+void upgt_stop(struct upgt_softc *);
+int upgt_media_change(struct ifnet *);
+void upgt_newassoc(struct ieee80211com *, struct ieee80211_node *,
+ int);
+int upgt_newstate(struct ieee80211com *, enum ieee80211_state, int);
+void upgt_newstate_task(void *);
+void upgt_next_scan(void *);
+void upgt_start(struct ifnet *);
+void upgt_watchdog(struct ifnet *);
+void upgt_tx_task(void *);
+void upgt_tx_done(struct upgt_softc *, uint8_t *);
+void upgt_rx_cb(usbd_xfer_handle, usbd_private_handle, usbd_status);
+void upgt_rx(struct upgt_softc *, uint8_t *, int);
+int upgt_set_macfilter(struct upgt_softc *, uint8_t state);
+int upgt_set_channel(struct upgt_softc *, unsigned);
+
+int upgt_alloc_tx(struct upgt_softc *);
+int upgt_alloc_rx(struct upgt_softc *);
+int upgt_alloc_cmd(struct upgt_softc *);
+void upgt_free_tx(struct upgt_softc *);
+void upgt_free_rx(struct upgt_softc *);
+void upgt_free_cmd(struct upgt_softc *);
+int upgt_bulk_xmit(struct upgt_softc *, struct upgt_data *,
+ usbd_pipe_handle, uint32_t *, int);
+
+void upgt_hexdump(void *, int);
+uint32_t upgt_crc32(const void *, size_t);
+
+struct cfdriver upgt_cd = {
+ NULL, "upgt", DV_IFNET
+};
+
+const struct cfattach upgt_ca = {
+ sizeof(struct upgt_softc),
+ upgt_match,
+ upgt_attach,
+ upgt_detach,
+ upgt_activate,
+};
+
+static const struct usb_devno upgt_devs[] = {
+ { USB_VENDOR_SMC, USB_PRODUCT_SMC_2862WG }
+};
+
+int
+upgt_match(struct device *parent, void *match, void *aux)
+{
+ struct usb_attach_arg *uaa = aux;
+
+ if (uaa->iface != NULL)
+ return (UMATCH_NONE);
+
+ return ((usb_lookup(upgt_devs, uaa->vendor, uaa->product) != NULL) ?
+ UMATCH_VENDOR_PRODUCT : UMATCH_NONE);
+}
+
+void
+upgt_attach(struct device *parent, struct device *self, void *aux)
+{
+ struct upgt_softc *sc = (struct upgt_softc *)self;
+ struct usb_attach_arg *uaa = aux;
+ usb_interface_descriptor_t *id;
+ usb_endpoint_descriptor_t *ed;
+ usbd_status error;
+ int i;
+
+ /*
+ * Attach USB device.
+ */
+ sc->sc_udev = uaa->device;
+
+ /* set configuration number */
+ if (usbd_set_config_no(sc->sc_udev, UPGT_CONFIG_NO, 0) != 0) {
+ printf("%s: could not set configuration no!\n",
+ sc->sc_dev.dv_xname);
+ return;
+ }
+
+ /* get the first interface handle */
+ error = usbd_device2interface_handle(sc->sc_udev, UPGT_IFACE_INDEX,
+ &sc->sc_iface);
+ if (error != 0) {
+ printf("%s: could not get interface handle!\n",
+ sc->sc_dev.dv_xname);
+ return;
+ }
+
+ /* find endpoints */
+ id = usbd_get_interface_descriptor(sc->sc_iface);
+ sc->sc_rx_no = sc->sc_tx_no = -1;
+ for (i = 0; i < id->bNumEndpoints; i++) {
+ ed = usbd_interface2endpoint_descriptor(sc->sc_iface, i);
+ if (ed == NULL) {
+ printf("%s: no endpoint descriptor for iface %d!\n",
+ sc->sc_dev.dv_xname, i);
+ return;
+ }
+
+ if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT &&
+ UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK)
+ sc->sc_tx_no = ed->bEndpointAddress;
+ if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
+ UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK)
+ sc->sc_rx_no = ed->bEndpointAddress;
+
+ /*
+ * XXX
+ * Just get the version 2 bulk pipes for now.
+ * 0x01 TX pipe
+ * 0x81 RX pipe
+ * 0x02 TX MGMT pipe (not used with fw version >2.5.x)
+ * 0x82 TX MGMT pipe (not used with fw version >2.5.x)
+ */
+ if (sc->sc_tx_no != -1 && sc->sc_rx_no != -1)
+ break;
+ }
+ if (sc->sc_rx_no == -1 || sc->sc_tx_no == -1) {
+ printf("%s: missing endpoint!\n", sc->sc_dev.dv_xname);
+ return;
+ }
+
+ /* init tasks */
+ usb_init_task(&sc->sc_task_newstate, upgt_newstate_task, sc);
+ usb_init_task(&sc->sc_task_tx, upgt_tx_task, sc);
+ timeout_set(&sc->scan_to, upgt_next_scan, sc);
+
+ /*
+ * Open TX and RX USB bulk pipes.
+ */
+ error = usbd_open_pipe(sc->sc_iface, sc->sc_tx_no, USBD_EXCLUSIVE_USE,
+ &sc->sc_tx_pipeh);
+ if (error != 0) {
+ printf("%s: could not open TX pipe: %s!\n",
+ sc->sc_dev.dv_xname, usbd_errstr(error));
+ goto fail;
+ }
+ error = usbd_open_pipe(sc->sc_iface, sc->sc_rx_no, USBD_EXCLUSIVE_USE,
+ &sc->sc_rx_pipeh);
+ if (error != 0) {
+ printf("%s: could not open RX pipe: %s!\n",
+ sc->sc_dev.dv_xname, usbd_errstr(error));
+ goto fail;
+ }
+
+ /*
+ * Allocate TX, RX, and CMD xfers.
+ */
+ if (upgt_alloc_tx(sc) != 0)
+ goto fail;
+ if (upgt_alloc_rx(sc) != 0)
+ goto fail;
+ if (upgt_alloc_cmd(sc) != 0)
+ goto fail;
+
+ /*
+ * We need the firmware loaded to complete the attach.
+ */
+ if (rootvp == NULL)
+ mountroothook_establish(upgt_attachhook, sc);
+ else
+ upgt_attachhook(sc);
+
+ return;
+fail:
+ printf("%s: %s failed!\n", sc->sc_dev.dv_xname, __func__);
+}
+
+void
+upgt_attachhook(void *arg)
+{
+ struct upgt_softc *sc = arg;
+ struct ieee80211com *ic = &sc->sc_ic;
+ struct ifnet *ifp = &ic->ic_if;
+ usbd_status error;
+ int i;
+
+ /*
+ * Load firmware file into memory.
+ */
+ if (upgt_fw_alloc(sc) != 0)
+ goto fail;
+
+ /*
+ * Initialize the device.
+ */
+ if (upgt_device_init(sc) != 0)
+ goto fail;
+
+ /*
+ * Verify the firmware.
+ */
+ if (upgt_fw_verify(sc) != 0)
+ goto fail;
+
+ /*
+ * Calculate device memory space.
+ */
+ if (sc->sc_memaddr_frame_start == 0 || sc->sc_memaddr_frame_end == 0) {
+ printf("%s: could not find memory space addresses on FW!\n",
+ sc->sc_dev.dv_xname);
+ goto fail;
+ }
+ sc->sc_memaddr_frame_end -= UPGT_MEMSIZE_RX + 1;
+ sc->sc_memaddr_rx_start = sc->sc_memaddr_frame_end + 1;
+
+ DPRINTF(1, "%s: memory address frame start=0x%08x\n",
+ sc->sc_dev.dv_xname, sc->sc_memaddr_frame_start);
+ DPRINTF(1, "%s: memory address frame end=0x%08x\n",
+ sc->sc_dev.dv_xname, sc->sc_memaddr_frame_end);
+ DPRINTF(1, "%s: memory address rx start=0x%08x\n",
+ sc->sc_dev.dv_xname, sc->sc_memaddr_rx_start);
+
+ upgt_mem_init(sc);
+
+ /*
+ * Load the firmware.
+ */
+ if (upgt_fw_load(sc) != 0)
+ goto fail;
+
+ /*
+ * Startup the RX pipe.
+ */
+ struct upgt_data *data_rx = &sc->rx_data;
+
+ usbd_setup_xfer(data_rx->xfer, sc->sc_rx_pipeh, data_rx, data_rx->buf,
+ MCLBYTES, USBD_SHORT_XFER_OK, USBD_NO_TIMEOUT, upgt_rx_cb);
+ error = usbd_transfer(data_rx->xfer);
+ if (error != 0 && error != USBD_IN_PROGRESS) {
+ printf("%s: could not queue RX transfer!\n",
+ sc->sc_dev.dv_xname);
+ goto fail;
+ }
+
+ /*
+ * Read the whole EEPROM content and parse it.
+ */
+ if (upgt_eeprom_read(sc) != 0)
+ goto fail;
+ if (upgt_eeprom_parse(sc) != 0)
+ goto fail;
+
+ /*
+ * Setup the 802.11 device.
+ */
+ ic->ic_phytype = IEEE80211_T_OFDM;
+ ic->ic_opmode = IEEE80211_M_STA;
+ ic->ic_state = IEEE80211_S_INIT;
+ ic->ic_caps =
+ IEEE80211_C_MONITOR |
+ IEEE80211_C_SHPREAMBLE |
+ IEEE80211_C_SHSLOT |
+ IEEE80211_C_WEP;
+
+ ic->ic_sup_rates[IEEE80211_MODE_11B] = ieee80211_std_rateset_11b;
+ ic->ic_sup_rates[IEEE80211_MODE_11G] = ieee80211_std_rateset_11g;
+
+ 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 = upgt_init;
+ ifp->if_ioctl = upgt_ioctl;
+ ifp->if_start = upgt_start;
+ ifp->if_watchdog = upgt_watchdog;
+ IFQ_SET_READY(&ifp->if_snd);
+ memcpy(ifp->if_xname, sc->sc_dev.dv_xname, IFNAMSIZ);
+
+ if_attach(ifp);
+ ieee80211_ifattach(ifp);
+ ic->ic_newassoc = upgt_newassoc;
+
+ sc->sc_newstate = ic->ic_newstate;
+ ic->ic_newstate = upgt_newstate;
+ ieee80211_media_init(ifp, upgt_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(UPGT_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(UPGT_TX_RADIOTAP_PRESENT);
+#endif
+
+ usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH, sc->sc_udev, &sc->sc_dev);
+
+ printf("%s: address %s\n",
+ sc->sc_dev.dv_xname, ether_sprintf(ic->ic_myaddr));
+
+ /* device attached */
+ sc->sc_flags |= UPGT_DEVICE_ATTACHED;
+
+ return;
+fail:
+ printf("%s: %s failed!\n", sc->sc_dev.dv_xname, __func__);
+}
+
+int
+upgt_detach(struct device *self, int flags)
+{
+ struct upgt_softc *sc = (struct upgt_softc *)self;
+ struct ifnet *ifp = &sc->sc_ic.ic_if;
+ int s;
+
+ if (!(sc->sc_flags & UPGT_DEVICE_ATTACHED))
+ /* device was not properly attached */
+ return (0);
+
+ s = splusb();
+
+ /* remove tasks and timeouts */
+ usb_rem_task(sc->sc_udev, &sc->sc_task_newstate);
+ usb_rem_task(sc->sc_udev, &sc->sc_task_tx);
+ timeout_del(&sc->scan_to);
+
+ /* abort and close TX / RX pipes */
+ if (sc->sc_tx_pipeh != NULL) {
+ usbd_abort_pipe(sc->sc_tx_pipeh);
+ usbd_close_pipe(sc->sc_tx_pipeh);
+ }
+ if (sc->sc_rx_pipeh != NULL) {
+ usbd_abort_pipe(sc->sc_rx_pipeh);
+ usbd_close_pipe(sc->sc_rx_pipeh);
+ }
+
+ /* free xfers */
+ upgt_free_tx(sc);
+ upgt_free_rx(sc);
+ upgt_free_cmd(sc);
+
+ /* free firmware */
+ upgt_fw_free(sc);
+
+ /* detach interface */
+ ieee80211_ifdetach(ifp);
+ if_detach(ifp);
+
+ splx(s);
+
+ usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, sc->sc_udev, &sc->sc_dev);
+
+ return (0);
+}
+
+int
+upgt_activate(struct device *self, enum devact act)
+{
+ switch (act) {
+ case DVACT_ACTIVATE:
+ return (EOPNOTSUPP);
+ case (DVACT_DEACTIVATE):
+ break;
+ }
+
+ return (0);
+}
+
+int
+upgt_device_init(struct upgt_softc *sc)
+{
+ struct upgt_data *data_cmd = &sc->cmd_data;
+ char init_cmd[] = { 0x7e, 0x7e, 0x7e, 0x7e };
+ int len;
+
+ len = sizeof(init_cmd);
+ bcopy(init_cmd, data_cmd->buf, len);
+ if (upgt_bulk_xmit(sc, data_cmd, sc->sc_tx_pipeh, &len, 0) != 0) {
+ printf("%s: could not send device init string!\n",
+ sc->sc_dev.dv_xname);
+ return (EIO);
+ }
+ usbd_delay_ms(sc->sc_udev, 100);
+
+ DPRINTF(1, "%s: device initialized\n", sc->sc_dev.dv_xname);
+
+ return (0);
+}
+
+int
+upgt_mem_init(struct upgt_softc *sc)
+{
+ int i;
+
+ for (i = 0; i < UPGT_MEMORY_MAX_PAGES; i++) {
+ sc->sc_memory.page[i].used = 0;
+
+ if (i == 0) {
+ /*
+ * The first memory page is always reserved for
+ * command data.
+ */
+ sc->sc_memory.page[i].addr =
+ sc->sc_memaddr_frame_start + MCLBYTES;
+ } else {
+ sc->sc_memory.page[i].addr =
+ sc->sc_memory.page[i - 1].addr + MCLBYTES;
+ }
+
+ if (sc->sc_memory.page[i].addr + MCLBYTES >=
+ sc->sc_memaddr_frame_end)
+ break;
+
+ DPRINTF(2, "%s: memory address page %d=0x%08x\n",
+ sc->sc_dev.dv_xname, i, sc->sc_memory.page[i].addr);
+ }
+
+ sc->sc_memory.pages = i;
+
+ DPRINTF(2, "%s: memory pages=%d\n",
+ sc->sc_dev.dv_xname, sc->sc_memory.pages);
+
+ return (0);
+}
+
+uint32_t
+upgt_mem_alloc(struct upgt_softc *sc)
+{
+ int i;
+
+ for (i = 0; i < sc->sc_memory.pages; i++) {
+ if (sc->sc_memory.page[i].used == 0) {
+ sc->sc_memory.page[i].used = 1;
+ return (sc->sc_memory.page[i].addr);
+ }
+ }
+
+ return (0);
+}
+
+void
+upgt_mem_free(struct upgt_softc *sc, uint32_t addr)
+{
+ int i;
+
+ for (i = 0; i < sc->sc_memory.pages; i++) {
+ if (sc->sc_memory.page[i].addr == addr) {
+ sc->sc_memory.page[i].used = 0;
+ return;
+ }
+ }
+
+ printf("%s: could not free memory address 0x%08x!\n",
+ sc->sc_dev.dv_xname, addr);
+}
+
+
+int
+upgt_fw_alloc(struct upgt_softc *sc)
+{
+ const char *name = "upgt-gw3887";
+ int error;
+
+ if (sc->sc_fw == NULL) {
+ error = loadfirmware(name, &sc->sc_fw, &sc->sc_fw_size);
+ if (error != 0) {
+ printf("%s: error %d, could not read firmware %s!\n",
+ sc->sc_dev.dv_xname, error, name);
+ return (EIO);
+ }
+ }
+
+ DPRINTF(1, "%s: firmware %s allocated\n", sc->sc_dev.dv_xname, name);
+
+ return (0);
+}
+
+void
+upgt_fw_free(struct upgt_softc *sc)
+{
+ if (sc->sc_fw != NULL) {
+ free(sc->sc_fw, M_DEVBUF);
+ sc->sc_fw = NULL;
+ DPRINTF(1, "%s: firmware freed\n", sc->sc_dev.dv_xname);
+ }
+}
+
+int
+upgt_fw_verify(struct upgt_softc *sc)
+{
+ struct upgt_fw_bra_option *bra_option;
+ uint32_t bra_option_type, bra_option_len;
+ uint32_t *uc;
+ int offset, bra_end = 0;
+
+ /*
+ * Seek to beginning of Boot Record Area (BRA).
+ */
+ for (offset = 0; offset < sc->sc_fw_size; offset += sizeof(uc)) {
+ uc = (uint32_t *)(sc->sc_fw + offset);
+ if (*uc == 0)
+ break;
+ }
+ for (; offset < sc->sc_fw_size; offset += sizeof(uc)) {
+ uc = (uint32_t *)(sc->sc_fw + offset);
+ if (*uc != 0)
+ break;
+ }
+ if (offset == sc->sc_fw_size) {
+ printf("%s: firmware Boot Record Area not found!\n",
+ sc->sc_dev.dv_xname);
+ return (EIO);
+ }
+ DPRINTF(1, "%s: firmware Boot Record Area found at offset %d\n",
+ sc->sc_dev.dv_xname, offset);
+
+ /*
+ * Parse Boot Record Area (BRA) options.
+ */
+ while (offset < sc->sc_fw_size && bra_end == 0) {
+ /* get current BRA option */
+ bra_option = (struct upgt_fw_bra_option *)(sc->sc_fw + offset);
+ bra_option_type = letoh32(bra_option->type);
+ bra_option_len = letoh32(bra_option->len) * sizeof(uc);
+
+ switch (bra_option_type) {
+ case UPGT_BRA_TYPE_FW:
+ DPRINTF(1, "%s: UPGT_BRA_TYPE_FW len=%d\n",
+ sc->sc_dev.dv_xname, bra_option_len);
+
+ if (bra_option_len != UPGT_BRA_FWTYPE_SIZE) {
+ printf("%s: wrong UPGT_BRA_TYPE_FW len!\n",
+ sc->sc_dev.dv_xname);
+ return (EIO);
+ }
+ if (memcmp(UPGT_BRA_FWTYPE_LM86, bra_option->data,
+ bra_option_len) == 0) {
+ sc->sc_fw_type = UPGT_FWTYPE_LM86;
+ break;
+ }
+ if (memcmp(UPGT_BRA_FWTYPE_LM87, bra_option->data,
+ bra_option_len) == 0) {
+ sc->sc_fw_type = UPGT_FWTYPE_LM87;
+ break;
+ }
+ if (memcmp(UPGT_BRA_FWTYPE_FMAC, bra_option->data,
+ bra_option_len) == 0) {
+ sc->sc_fw_type = UPGT_FWTYPE_FMAC;
+ break;
+ }
+ printf("%s: unsupported firmware type!\n",
+ sc->sc_dev.dv_xname);
+ return (EIO);
+ case UPGT_BRA_TYPE_VERSION:
+ DPRINTF(1, "%s: UPGT_BRA_TYPE_VERSION len=%d\n",
+ sc->sc_dev.dv_xname, bra_option_len);
+ break;
+ case UPGT_BRA_TYPE_DEPIF:
+ DPRINTF(1, "%s: UPGT_BRA_TYPE_DEPIF len=%d\n",
+ sc->sc_dev.dv_xname, bra_option_len);
+ break;
+ case UPGT_BRA_TYPE_EXPIF:
+ DPRINTF(1, "%s: UPGT_BRA_TYPE_EXPIF len=%d\n",
+ sc->sc_dev.dv_xname, bra_option_len);
+ break;
+ case UPGT_BRA_TYPE_DESCR:
+ DPRINTF(1, "%s: UPGT_BRA_TYPE_DESCR len=%d\n",
+ sc->sc_dev.dv_xname, bra_option_len);
+
+ struct upgt_fw_bra_descr *descr =
+ (struct upgt_fw_bra_descr *)bra_option->data;
+
+ sc->sc_memaddr_frame_start =
+ letoh32(descr->memaddr_space_start);
+ sc->sc_memaddr_frame_end =
+ letoh32(descr->memaddr_space_end);
+
+ DPRINTF(2, "%s: memory address space start=0x%08x\n",
+ sc->sc_dev.dv_xname, sc->sc_memaddr_frame_start);
+ DPRINTF(2, "%s: memory address space end=0x%08x\n",
+ sc->sc_dev.dv_xname, sc->sc_memaddr_frame_end);
+ break;
+ case UPGT_BRA_TYPE_END:
+ DPRINTF(1, "%s: UPGT_BRA_TYPE_END len=%d\n",
+ sc->sc_dev.dv_xname, bra_option_len);
+ bra_end = 1;
+ break;
+ default:
+ DPRINTF(1, "%s: unknown BRA option len=%d\n",
+ sc->sc_dev.dv_xname, bra_option_len);
+ return (EIO);
+ }
+
+ /* jump to next BRA option */
+ offset += sizeof(struct upgt_fw_bra_option) + bra_option_len;
+ }
+
+ DPRINTF(1, "%s: firmware verified\n", sc->sc_dev.dv_xname);
+
+ return (0);
+}
+
+int
+upgt_fw_load(struct upgt_softc *sc)
+{
+ struct upgt_data *data_cmd = &sc->cmd_data;
+ struct upgt_data *data_rx = &sc->rx_data;
+ char start_fwload_cmd[] = { 0x3c, 0x0d };
+ int offset, bsize, n, i, len;
+ uint32_t crc32;
+
+ /* send firmware start load command */
+ len = sizeof(start_fwload_cmd);
+ bcopy(start_fwload_cmd, data_cmd->buf, len);
+ if (upgt_bulk_xmit(sc, data_cmd, sc->sc_tx_pipeh, &len, 0) != 0) {
+ printf("%s: could not send start_firmware_load command!\n",
+ sc->sc_dev.dv_xname);
+ return (EIO);
+ }
+
+ /* send X2 header */
+ len = sizeof(struct upgt_fw_x2_header);
+ struct upgt_fw_x2_header *x2 = data_cmd->buf;
+ bcopy(UPGT_X2_SIGNATURE, x2->signature, UPGT_X2_SIGNATURE_SIZE);
+ x2->startaddr = htole32(UPGT_MEMADDR_FIRMWARE_START);
+ x2->len = htole32(sc->sc_fw_size);
+ x2->crc = htole32(upgt_crc32(data_cmd->buf + UPGT_X2_SIGNATURE_SIZE,
+ sizeof(struct upgt_fw_x2_header) - UPGT_X2_SIGNATURE_SIZE -
+ sizeof(uint32_t)));
+ if (upgt_bulk_xmit(sc, data_cmd, sc->sc_tx_pipeh, &len, 0) != 0) {
+ printf("%s: could not send firmware X2 header!\n",
+ sc->sc_dev.dv_xname);
+ return (EIO);
+ }
+
+ /* download firmware */
+ for (offset = 0; offset < sc->sc_fw_size; offset += bsize) {
+ if (sc->sc_fw_size - offset > UPGT_FW_BLOCK_SIZE)
+ bsize = UPGT_FW_BLOCK_SIZE;
+ else
+ bsize = sc->sc_fw_size - offset;
+
+ n = upgt_fw_copy(sc->sc_fw + offset, data_cmd->buf, bsize);
+
+ DPRINTF(1, "%s: FW offset=%d, read=%d, sent=%d\n",
+ sc->sc_dev.dv_xname, offset, n, bsize);
+
+ if (upgt_bulk_xmit(sc, data_cmd, sc->sc_tx_pipeh, &bsize, 0)
+ != 0) {
+ printf("%s: error while downloading firmware block!\n",
+ sc->sc_dev.dv_xname);
+ return (EIO);
+ }
+
+ bsize = n;
+ }
+ DPRINTF(1, "%s: firmware downloaded\n", sc->sc_dev.dv_xname);
+
+ /* load firmware */
+ crc32 = htole32(upgt_crc32(sc->sc_fw, sc->sc_fw_size));
+ *((uint32_t *)(data_cmd->buf) ) = crc32;
+ *((uint8_t *)(data_cmd->buf) + 4) = 'g';
+ *((uint8_t *)(data_cmd->buf) + 5) = '\r';
+ len = 6;
+ if (upgt_bulk_xmit(sc, data_cmd, sc->sc_tx_pipeh, &len, 0) != 0) {
+ printf("%s: could not send load_firmware command!\n",
+ sc->sc_dev.dv_xname);
+ return (EIO);
+ }
+
+ for (i = 0; i < UPGT_FIRMWARE_TIMEOUT; i++) {
+ len = UPGT_FW_BLOCK_SIZE;
+ bzero(data_rx->buf, MCLBYTES);
+ if (upgt_bulk_xmit(sc, data_rx, sc->sc_rx_pipeh, &len,
+ USBD_SHORT_XFER_OK) != 0) {
+ printf("%s: could not read firmware response!\n",
+ sc->sc_dev.dv_xname);
+ return (EIO);
+ }
+
+ if (memcmp(data_rx->buf, "OK", 2) == 0)
+ break; /* firmware load was successful */
+ }
+ if (i == UPGT_FIRMWARE_TIMEOUT) {
+ printf("%s: firmware load failed!\n", sc->sc_dev.dv_xname);
+ return (EIO);
+ }
+ DPRINTF(1, "%s: firmware loaded\n", sc->sc_dev.dv_xname);
+
+ return (0);
+}
+
+/*
+ * While copying the version 2 firmware, we need to replace two characters:
+ *
+ * 0x7e -> 0x7d 0x5e
+ * 0x7d -> 0x7d 0x5d
+ */
+int
+upgt_fw_copy(char *src, char *dst, int size)
+{
+ int i, j;
+
+ for (i = 0, j = 0; i < size && j < size; i++) {
+ switch (src[i]) {
+ case 0x7e:
+ dst[j] = 0x7d;
+ j++;
+ dst[j] = 0x5e;
+ j++;
+ break;
+ case 0x7d:
+ dst[j] = 0x7d;
+ j++;
+ dst[j] = 0x5d;
+ j++;
+ break;
+ default:
+ dst[j] = src[i];
+ j++;
+ break;
+ }
+ }
+
+ return (i);
+}
+
+int
+upgt_eeprom_read(struct upgt_softc *sc)
+{
+ struct upgt_data *data_cmd = &sc->cmd_data;
+ struct upgt_lmac_eeprom *eeprom;
+ int offset, block, len;
+
+ block = UPGT_EEPROM_BLOCK_SIZE;
+ offset = UPGT_EEPROM_SIZE;
+ while (offset > 0) {
+ offset = offset - block;
+
+ DPRINTF(1, "%s: request EEPROM block (offset=%d, len=%d)\n",
+ sc->sc_dev.dv_xname, offset, block);
+
+ /*
+ * Transmit the first URB containing the Prism memory address.
+ */
+ *((uint32_t *)data_cmd->buf) =
+ htole32(sc->sc_memaddr_frame_start +
+ UPGT_MEMSIZE_FRAME_HEAD);
+
+ len = sizeof(uint32_t);
+ if (upgt_bulk_xmit(sc, data_cmd, sc->sc_tx_pipeh, &len, 0)
+ != 0) {
+ printf("%s: could not transmit EEPROM memaddr URB!\n",
+ sc->sc_dev.dv_xname);
+ return (EIO);
+ }
+
+ /*
+ * Transmit the second URB containing the CMD data itself.
+ */
+ eeprom = (struct upgt_lmac_eeprom *)data_cmd->buf;
+ eeprom->header1.flags = 0;
+ eeprom->header1.type = UPGT_H1_TYPE_CTRL;
+ eeprom->header1.len = htole16((
+ sizeof(struct upgt_lmac_eeprom) -
+ sizeof(struct upgt_lmac_header)) + block);
+
+ eeprom->header2.reqid = htole32(sc->sc_memaddr_frame_start);
+ eeprom->header2.type = htole16(UPGT_H2_TYPE_EEPROM);
+ eeprom->header2.flags = 0;
+
+ eeprom->offset = htole16(offset);
+ eeprom->len = htole16(block);
+
+ len = sizeof(struct upgt_lmac_eeprom) + block;
+ if (upgt_bulk_xmit(sc, data_cmd, sc->sc_tx_pipeh, &len, 0)
+ != 0) {
+ printf("%s: could not transmit EEPROM data URB!\n",
+ sc->sc_dev.dv_xname);
+ return (EIO);
+ }
+ if (tsleep(sc, 0, "eeprom_request", UPGT_USB_TIMEOUT)) {
+ printf("%s: timeout while waiting for EEPROM data!\n",
+ sc->sc_dev.dv_xname);
+ return (EIO);
+ }
+
+ if (offset < block)
+ block = offset;
+ }
+
+ return (0);
+}
+
+int
+upgt_eeprom_parse(struct upgt_softc *sc)
+{
+ struct ieee80211com *ic = &sc->sc_ic;
+ struct upgt_eeprom_header *eeprom_header;
+ struct upgt_eeprom_option *eeprom_option;
+ uint16_t option_len;
+ uint16_t option_type;
+ uint16_t preamble_len;
+ int option_end = 0;
+
+ /* calculate eeprom options start offset */
+ eeprom_header = (struct upgt_eeprom_header *)sc->sc_eeprom;
+ preamble_len = letoh16(eeprom_header->preamble_len);
+ eeprom_option = (struct upgt_eeprom_option *)(sc->sc_eeprom +
+ (sizeof(struct upgt_eeprom_header) + preamble_len));
+
+ while (!option_end) {
+ /* the eeprom option length is stored in words */
+ option_len =
+ (letoh16(eeprom_option->len) - 1) * sizeof(uint16_t);
+ option_type =
+ letoh16(eeprom_option->type);
+
+ switch (option_type) {
+ case UPGT_EEPROM_TYPE_NAME:
+ DPRINTF(1, "%s: EEPROM name len=%d\n",
+ sc->sc_dev.dv_xname, option_len);
+ break;
+ case UPGT_EEPROM_TYPE_SERIAL:
+ DPRINTF(1, "%s: EEPROM serial len=%d\n",
+ sc->sc_dev.dv_xname, option_len);
+ break;
+ case UPGT_EEPROM_TYPE_MAC:
+ DPRINTF(1, "%s: EEPROM mac len=%d\n",
+ sc->sc_dev.dv_xname, option_len);
+
+ IEEE80211_ADDR_COPY(ic->ic_myaddr, eeprom_option->data);
+ break;
+ case UPGT_EEPROM_TYPE_HWRX:
+ DPRINTF(1, "%s: EEPROM hwrx len=%d\n",
+ sc->sc_dev.dv_xname, option_len);
+
+ upgt_eeprom_parse_hwrx(sc, eeprom_option->data);
+ break;
+ case UPGT_EEPROM_TYPE_CHIP:
+ DPRINTF(1, "%s: EEPROM chip len=%d\n",
+ sc->sc_dev.dv_xname, option_len);
+ break;
+ case UPGT_EEPROM_TYPE_FREQ3:
+ DPRINTF(1, "%s: EEPROM freq3 len=%d\n",
+ sc->sc_dev.dv_xname, option_len);
+
+ upgt_eeprom_parse_freq3(sc, eeprom_option->data,
+ option_len);
+ break;
+ case UPGT_EEPROM_TYPE_FREQ4:
+ DPRINTF(1, "%s: EEPROM freq4 len=%d\n",
+ sc->sc_dev.dv_xname, option_len);
+
+ upgt_eeprom_parse_freq4(sc, eeprom_option->data,
+ option_len);
+ break;
+ case UPGT_EEPROM_TYPE_FREQ5:
+ DPRINTF(1, "%s: EEPROM freq5 len=%d\n",
+ sc->sc_dev.dv_xname, option_len);
+ break;
+ case UPGT_EEPROM_TYPE_FREQ6:
+ DPRINTF(1, "%s: EEPROM freq6 len=%d\n",
+ sc->sc_dev.dv_xname, option_len);
+
+ upgt_eeprom_parse_freq6(sc, eeprom_option->data,
+ option_len);
+ break;
+ case UPGT_EEPROM_TYPE_END:
+ DPRINTF(1, "%s: EEPROM end len=%d\n",
+ sc->sc_dev.dv_xname, option_len);
+ option_end = 1;
+ break;
+ case UPGT_EEPROM_TYPE_OFF:
+ DPRINTF(1, "%s: EEPROM off without end option!\n",
+ sc->sc_dev.dv_xname);
+ return (EIO);
+ default:
+ DPRINTF(1, "%s: EEPROM unknown type 0x%04x len=%d\n",
+ sc->sc_dev.dv_xname, option_type, option_len);
+ break;
+ }
+
+ /* jump to next EEPROM option */
+ eeprom_option = (struct upgt_eeprom_option *)
+ (eeprom_option->data + option_len);
+ }
+
+ return (0);
+}
+
+void
+upgt_eeprom_parse_hwrx(struct upgt_softc *sc, uint8_t *data)
+{
+ struct upgt_eeprom_option_hwrx *option_hwrx;
+
+ option_hwrx = (struct upgt_eeprom_option_hwrx *)data;
+
+ sc->sc_eeprom_hwrx = option_hwrx->rxfilter - UPGT_EEPROM_RX_CONST;
+
+ DPRINTF(2, "%s: hwrx option value=0x%04x\n",
+ sc->sc_dev.dv_xname, sc->sc_eeprom_hwrx);
+}
+
+void
+upgt_eeprom_parse_freq3(struct upgt_softc *sc, uint8_t *data, int len)
+{
+ struct upgt_eeprom_freq3_header *freq3_header;
+ struct upgt_lmac_freq3 *freq3;
+ int i, elements, flags;
+ unsigned channel;
+
+ freq3_header = (struct upgt_eeprom_freq3_header *)data;
+ freq3 = (struct upgt_lmac_freq3 *)(freq3_header + 1);
+
+ flags = freq3_header->flags;
+ elements = freq3_header->elements;
+
+ DPRINTF(2, "%s: flags=0x%02x\n", sc->sc_dev.dv_xname, flags);
+ DPRINTF(2, "%s: elements=%d\n", sc->sc_dev.dv_xname, elements);
+
+ for (i = 0; i < elements; i++) {
+ channel = ieee80211_mhz2ieee(letoh16(freq3[i].freq), 0);
+
+ sc->sc_eeprom_freq3[channel] = freq3[i];
+
+ DPRINTF(2, "%s: frequence=%d, channel=%d\n",
+ sc->sc_dev.dv_xname,
+ letoh16(sc->sc_eeprom_freq3[channel].freq), channel);
+ }
+}
+
+void
+upgt_eeprom_parse_freq4(struct upgt_softc *sc, uint8_t *data, int len)
+{
+ struct upgt_eeprom_freq4_header *freq4_header;
+ struct upgt_eeprom_freq4_1 *freq4_1;
+ struct upgt_eeprom_freq4_2 *freq4_2;
+ int i, j, elements, settings, flags;
+ unsigned channel;
+
+ freq4_header = (struct upgt_eeprom_freq4_header *)data;
+ freq4_1 = (struct upgt_eeprom_freq4_1 *)(freq4_header + 1);
+
+ flags = freq4_header->flags;
+ elements = freq4_header->elements;
+ settings = freq4_header->settings;
+
+ /* we need this value later */
+ sc->sc_eeprom_freq6_settings = freq4_header->settings;
+
+ DPRINTF(2, "%s: flags=0x%02x\n", sc->sc_dev.dv_xname, flags);
+ DPRINTF(2, "%s: elements=%d\n", sc->sc_dev.dv_xname, elements);
+ DPRINTF(2, "%s: settings=%d\n", sc->sc_dev.dv_xname, settings);
+
+ for (i = 0; i < elements; i++) {
+ channel = ieee80211_mhz2ieee(letoh16(freq4_1[i].freq), 0);
+
+ freq4_2 = (struct upgt_eeprom_freq4_2 *)freq4_1[i].data;
+
+ for (j = 0; j < settings; j++) {
+ sc->sc_eeprom_freq4[channel][j].cmd = freq4_2[j];
+ sc->sc_eeprom_freq4[channel][j].pad = 0;
+ }
+
+ DPRINTF(2, "%s: frequence=%d, channel=%d\n",
+ sc->sc_dev.dv_xname,
+ letoh16(freq4_1[i].freq), channel);
+ }
+}
+
+void
+upgt_eeprom_parse_freq6(struct upgt_softc *sc, uint8_t *data, int len)
+{
+ struct upgt_lmac_freq6 *freq6;
+ int i, elements;
+ unsigned channel;
+
+ freq6 = (struct upgt_lmac_freq6 *)data;
+
+ elements = len / sizeof(struct upgt_lmac_freq6);
+
+ DPRINTF(2, "%s: elements=%d\n", sc->sc_dev.dv_xname, elements);
+
+ for (i = 0; i < elements; i++) {
+ channel = ieee80211_mhz2ieee(letoh16(freq6[i].freq), 0);
+
+ sc->sc_eeprom_freq6[channel] = freq6[i];
+
+ DPRINTF(2, "%s: frequence=%d, channel=%d\n",
+ sc->sc_dev.dv_xname,
+ letoh16(sc->sc_eeprom_freq6[channel].freq), channel);
+ }
+}
+
+int
+upgt_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
+{
+ struct upgt_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) == 0)
+ upgt_init(ifp);
+ } else {
+ if (ifp->if_flags & IFF_RUNNING)
+ upgt_stop(sc);
+ }
+ 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);
+ break;
+ }
+
+ if (error == ENETRESET) {
+ if (ifp->if_flags & (IFF_UP | IFF_RUNNING))
+ upgt_init(ifp);
+ error = 0;
+ }
+
+ splx(s);
+
+ return (error);
+}
+
+int
+upgt_init(struct ifnet *ifp)
+{
+ struct upgt_softc *sc = ifp->if_softc;
+ struct ieee80211com *ic = &sc->sc_ic;
+
+ DPRINTF(1, "%s: %s\n", sc->sc_dev.dv_xname, __func__);
+
+ IEEE80211_ADDR_COPY(ic->ic_myaddr, LLADDR(ifp->if_sadl));
+
+ /* select default channel */
+ ic->ic_bss->ni_chan = ic->ic_ibss_chan;
+ sc->sc_curchan = ieee80211_chan2ieee(ic, ic->ic_bss->ni_chan);
+
+ /* set default channel */
+ upgt_set_channel(sc, sc->sc_curchan);
+
+ upgt_set_macfilter(sc, IEEE80211_S_SCAN);
+
+ ifp->if_flags |= IFF_RUNNING;
+ ifp->if_flags &= ~IFF_OACTIVE;
+
+ 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);
+}
+
+void
+upgt_stop(struct upgt_softc *sc)
+{
+ struct ieee80211com *ic = &sc->sc_ic;
+ struct ifnet *ifp = &ic->ic_if;
+
+ /* device down */
+ ifp->if_timer = 0;
+ ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE);
+
+ /* change device back to initial state */
+ ieee80211_new_state(ic, IEEE80211_S_INIT, -1);
+}
+
+int
+upgt_media_change(struct ifnet *ifp)
+{
+ int error;
+
+ if ((error = ieee80211_media_change(ifp) != ENETRESET))
+ return (error);
+
+ if (ifp->if_flags & (IFF_UP | IFF_RUNNING))
+ upgt_init(ifp);
+
+ return (0);
+}
+
+void
+upgt_newassoc(struct ieee80211com *ic, struct ieee80211_node *ni, int isnew)
+{
+ ni->ni_txrate = 0;
+}
+
+int
+upgt_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg)
+{
+ struct upgt_softc *sc = ic->ic_if.if_softc;
+
+ usb_rem_task(sc->sc_udev, &sc->sc_task_newstate);
+ timeout_del(&sc->scan_to);
+
+ /* do it in a process context */
+ sc->sc_state = nstate;
+ sc->sc_arg = arg;
+ usb_add_task(sc->sc_udev, &sc->sc_task_newstate);
+
+ return (0);
+}
+
+void
+upgt_newstate_task(void *arg)
+{
+ struct upgt_softc *sc = arg;
+ struct ieee80211com *ic = &sc->sc_ic;
+ struct ieee80211_node *ni;
+ enum ieee80211_state ostate;
+ unsigned channel;
+
+ ostate = ic->ic_state;
+
+ switch (sc->sc_state) {
+ case IEEE80211_S_INIT:
+ DPRINTF(1, "%s: newstate is IEEE80211_S_INIT\n",
+ sc->sc_dev.dv_xname);
+ break;
+ case IEEE80211_S_SCAN:
+ DPRINTF(1, "%s: newstate is IEEE80211_S_SCAN\n",
+ sc->sc_dev.dv_xname);
+
+ channel = ieee80211_chan2ieee(ic, ic->ic_bss->ni_chan);
+ upgt_set_channel(sc, channel);
+
+ timeout_add(&sc->scan_to, hz / 5);
+ break;
+ case IEEE80211_S_AUTH:
+ DPRINTF(1, "%s: newstate is IEEE80211_S_AUTH\n",
+ sc->sc_dev.dv_xname);
+
+ channel = ieee80211_chan2ieee(ic, ic->ic_bss->ni_chan);
+ upgt_set_channel(sc, channel);
+ break;
+ case IEEE80211_S_ASSOC:
+ DPRINTF(1, "%s: newstate is IEEE80211_S_ASSOC\n",
+ sc->sc_dev.dv_xname);
+ break;
+ case IEEE80211_S_RUN:
+ DPRINTF(1, "%s: newstate is IEEE80211_S_RUN\n",
+ sc->sc_dev.dv_xname);
+
+ ni = ic->ic_bss;
+
+ upgt_set_macfilter(sc, IEEE80211_S_RUN);
+ break;
+ }
+
+ sc->sc_newstate(ic, sc->sc_state, sc->sc_arg);
+}
+
+void
+upgt_next_scan(void *arg)
+{
+ struct upgt_softc *sc = arg;
+ struct ieee80211com *ic = &sc->sc_ic;
+ struct ifnet *ifp = &ic->ic_if;
+
+ DPRINTF(2, "%s: %s\n", sc->sc_dev.dv_xname, __func__);
+
+ if (ic->ic_state == IEEE80211_S_SCAN)
+ ieee80211_next_scan(ifp);
+}
+
+void
+upgt_start(struct ifnet *ifp)
+{
+ struct upgt_softc *sc = ifp->if_softc;
+ struct ieee80211com *ic = &sc->sc_ic;
+ struct ieee80211_node *ni;
+ struct mbuf *m;
+ int i;
+
+ /* don't transmit packets if interface is busy or down */
+ if ((ifp->if_flags & (IFF_RUNNING | IFF_OACTIVE)) != IFF_RUNNING)
+ return;
+
+ DPRINTF(2, "%s: %s\n", sc->sc_dev.dv_xname, __func__);
+
+ usb_rem_task(sc->sc_udev, &sc->sc_task_tx);
+
+ for (i = 0; i < UPGT_TX_COUNT; i++) {
+ struct upgt_data *data_tx = &sc->tx_data[i];
+
+ IF_POLL(&ic->ic_mgtq, m);
+ if (m != NULL) {
+ /* management frame */
+ IF_DEQUEUE(&ic->ic_mgtq, m);
+
+ ni = (struct ieee80211_node *)m->m_pkthdr.rcvif;
+ m->m_pkthdr.rcvif = NULL;
+#if NBPFILTER > 0
+ if (ic->ic_rawbpf != NULL)
+ bpf_mtap(ic->ic_rawbpf, m, BPF_DIRECTION_OUT);
+#endif
+ if ((data_tx->addr = upgt_mem_alloc(sc)) == 0) {
+ printf("%s: no free memory!\n",
+ sc->sc_dev.dv_xname);
+ return;
+ }
+ data_tx->ni = ni;
+ data_tx->m = m;
+ sc->tx_queued++;
+ } else {
+ /* data frame */
+ if (ic->ic_state != IEEE80211_S_RUN)
+ break;
+
+ IFQ_POLL(&ifp->if_snd, m);
+ if (m == NULL)
+ break;
+
+ IFQ_DEQUEUE(&ifp->if_snd, m);
+#if NBPFILTER > 0
+ if (ifp->if_bpf != NULL)
+ bpf_mtap(ifp->if_bpf, m, BPF_DIRECTION_OUT);
+#endif
+ m = ieee80211_encap(ifp, m, &ni);
+ if (m == NULL)
+ continue;
+#if NBPFILTER > 0
+ if (ic->ic_rawbpf != NULL)
+ bpf_mtap(ic->ic_rawbpf, m, BPF_DIRECTION_OUT);
+#endif
+ if ((data_tx->addr = upgt_mem_alloc(sc)) == 0) {
+ printf("%s: no free memory!\n",
+ sc->sc_dev.dv_xname);
+ return;
+ }
+ data_tx->ni = ni;
+ data_tx->m = m;
+ sc->tx_queued++;
+ }
+ }
+
+ if (sc->tx_queued > 0) {
+ DPRINTF(2, "%s: tx_queued=%d\n",
+ sc->sc_dev.dv_xname, sc->tx_queued);
+ /* process the TX queue in process context */
+ ifp->if_timer = 5;
+ ifp->if_flags |= IFF_OACTIVE;
+ usb_add_task(sc->sc_udev, &sc->sc_task_tx);
+ }
+}
+
+void
+upgt_watchdog(struct ifnet *ifp)
+{
+ struct upgt_softc *sc;
+
+ sc = ifp->if_softc;
+
+ DPRINTF(1, "%s: %s\n", sc->sc_dev.dv_xname, __func__);
+
+ /* TODO: what shall we do on TX timeout? */
+
+ ieee80211_watchdog(ifp);
+}
+
+void
+upgt_tx_task(void *arg)
+{
+ struct upgt_softc *sc = arg;
+ struct ieee80211com *ic = &sc->sc_ic;
+ struct ifnet *ifp = &ic->ic_if;
+ struct ieee80211_frame *wh;
+ struct upgt_lmac_tx_desc *txdesc;
+ struct mbuf *m;
+ uint32_t addr;
+ int len, i;
+
+ for (i = 0; i < UPGT_TX_COUNT; i++) {
+ struct upgt_data *data_tx = &sc->tx_data[i];
+
+ if (data_tx->m == NULL)
+ continue;
+
+ m = data_tx->m;
+ addr = data_tx->addr + UPGT_MEMSIZE_FRAME_HEAD;
+
+ /*
+ * Software WEP.
+ */
+ wh = mtod(m, struct ieee80211_frame *);
+
+ if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
+ m = ieee80211_wep_crypt(ifp, m, 1);
+ if (m == NULL)
+ return;
+ }
+
+ /*
+ * Transmit the first URB containing the Prism memory address.
+ */
+ *((uint32_t *)data_tx->buf) = htole32(addr);
+
+ len = sizeof(uint32_t);
+ if (upgt_bulk_xmit(sc, data_tx, sc->sc_tx_pipeh, &len, 0)
+ != 0) {
+ printf("%s: could not transmit TX memaddr URB!\n",
+ sc->sc_dev.dv_xname);
+ return;
+ }
+
+ DPRINTF(2, "%s: TX memaddr sent\n", sc->sc_dev.dv_xname);
+
+ /*
+ * Transmit the second URB containing the TX data itself.
+ */
+ bzero(data_tx->buf, MCLBYTES);
+
+ txdesc = (struct upgt_lmac_tx_desc *)data_tx->buf;
+
+ /* XXX differ between data and mgmt frames? */
+ txdesc->header1.flags = UPGT_H1_FLAGS_TX_DATA;
+ txdesc->header1.type = UPGT_H1_TYPE_DATA_TX;
+ txdesc->header1.len = htole16(m->m_pkthdr.len);
+
+ txdesc->header2.reqid = htole32(data_tx->addr);
+ txdesc->header2.type = htole16(UPGT_H2_TYPE_TX_NOACK);
+ txdesc->header2.flags = 0;
+
+ txdesc->type = htole32(UPGT_TX_DESC_TYPE_DATA);
+ txdesc->unknown1 = 0;
+ txdesc->unknown2 = 0;
+ /* round TX descriptor header up to 4 byte boundary */
+ txdesc->frame_pad = UPGT_TX_DESC_FRAME_PAD;
+
+#if NBPFILTER > 0
+ if (sc->sc_drvbpf != NULL) {
+ struct mbuf mb;
+ struct upgt_tx_radiotap_header *tap = &sc->sc_txtap;
+
+ tap->wt_flags = 0;
+ tap->wt_rate = 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);
+ tap->wt_antenna = 0;
+
+ mb.m_data = (caddr_t)tap;
+ mb.m_len = sc->sc_txtap_len;
+ mb.m_next = m;
+ mb.m_nextpkt = NULL;
+ mb.m_type = 0;
+ mb.m_flags = 0;
+ bpf_mtap(sc->sc_drvbpf, &mb, BPF_DIRECTION_OUT);
+ }
+#endif
+
+ /* copy frame below our TX descriptor header */
+ m_copydata(m, 0, m->m_pkthdr.len,
+ data_tx->buf + sizeof(struct upgt_lmac_tx_desc));
+
+ len = sizeof(struct upgt_lmac_tx_desc) + m->m_pkthdr.len;
+ if (upgt_bulk_xmit(sc, data_tx, sc->sc_tx_pipeh, &len, 0)
+ != 0) {
+ printf("%s: could not transmit TX data URB!\n",
+ sc->sc_dev.dv_xname);
+ return;
+ }
+
+ /* we do not need the mbuf anymore */
+ m_freem(m);
+
+ DPRINTF(2, "%s: TX sent: len=%d\n", sc->sc_dev.dv_xname, len);
+ }
+}
+
+void
+upgt_tx_done(struct upgt_softc *sc, uint8_t *data)
+{
+ struct ieee80211com *ic = &sc->sc_ic;
+ struct ifnet *ifp = &ic->ic_if;
+ struct upgt_lmac_tx_done_desc *desc;
+ int i, s;
+
+ s = splnet();
+
+ desc = (struct upgt_lmac_tx_done_desc *)data;
+
+ for (i = 0; i < UPGT_TX_COUNT; i++) {
+ struct upgt_data *data_tx = &sc->tx_data[i];
+
+ if (data_tx->addr == letoh32(desc->header2.reqid)) {
+ ieee80211_release_node(ic, data_tx->ni);
+ data_tx->ni = NULL;
+ data_tx->m = NULL;
+ upgt_mem_free(sc, data_tx->addr);
+ data_tx->addr = 0;
+
+ sc->tx_queued--;
+ ifp->if_opackets++;
+
+ DPRINTF(2, "%s: TX done: ", sc->sc_dev.dv_xname);
+ DPRINTF(2, "memaddr=0x%08x, status=0x%04x, rssi=%d, ",
+ letoh32(desc->header2.reqid),
+ letoh16(desc->status),
+ letoh16(desc->rssi));
+ DPRINTF(2, "seq=%d\n", letoh16(desc->seq));
+
+ break;
+ }
+ }
+
+ if (sc->tx_queued == 0) {
+ /* TX queued was processed, continue */
+ ifp->if_timer = 0;
+ ifp->if_flags &= ~IFF_OACTIVE;
+ upgt_start(ifp);
+ }
+
+ splx(s);
+}
+
+void
+upgt_rx_cb(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status)
+{
+ struct upgt_data *data_rx = priv;
+ struct upgt_softc *sc = data_rx->sc;
+ int len;
+ struct upgt_lmac_header *header;
+ struct upgt_lmac_eeprom *eeprom;
+ uint8_t h1_type;
+ uint16_t h2_type;
+
+ DPRINTF(3, "%s: %s\n", sc->sc_dev.dv_xname, __func__);
+
+ if (status != USBD_NORMAL_COMPLETION) {
+ if (status == USBD_NOT_STARTED || status == USBD_CANCELLED)
+ return;
+ if (status == USBD_STALLED)
+ usbd_clear_endpoint_stall_async(sc->sc_rx_pipeh);
+ goto skip;
+ }
+ usbd_get_xfer_status(xfer, NULL, NULL, &len, NULL);
+
+ /*
+ * Check what type of frame came in.
+ */
+ header = (struct upgt_lmac_header *)data_rx->buf;
+
+ h1_type = header->header1.type;
+ h2_type = letoh16(header->header2.type);
+
+ if (h1_type == UPGT_H1_TYPE_CTRL &&
+ h2_type == UPGT_H2_TYPE_EEPROM) {
+ eeprom = (struct upgt_lmac_eeprom *)data_rx->buf;
+ uint16_t eeprom_offset = letoh16(eeprom->offset);
+ uint16_t eeprom_len = letoh16(eeprom->len);
+
+ DPRINTF(2, "%s: received EEPROM block (offset=%d, len=%d)\n",
+ sc->sc_dev.dv_xname, eeprom_offset, eeprom_len);
+
+ bcopy(data_rx->buf + sizeof(struct upgt_lmac_eeprom),
+ sc->sc_eeprom + eeprom_offset, eeprom_len);
+
+ /* EEPROM data has arrived in time, wakeup tsleep() */
+ wakeup(sc);
+ } else
+ if (h1_type == UPGT_H1_TYPE_CTRL &&
+ h2_type == UPGT_H2_TYPE_TX_DONE) {
+ DPRINTF(2, "%s: received 802.11 TX done\n",
+ sc->sc_dev.dv_xname);
+
+ upgt_tx_done(sc, data_rx->buf);
+ } else
+ if (h1_type == UPGT_H1_TYPE_DATA_RX ||
+ h1_type == UPGT_H1_TYPE_DATA_RX_MGMT) {
+ DPRINTF(3, "%s: received 802.11 RX data\n",
+ sc->sc_dev.dv_xname);
+
+ upgt_rx(sc, data_rx->buf, letoh16(header->header1.len));
+ } else {
+ /* ignore unknown frame types */
+ DPRINTF(1, "%s: received unknown frame type 0x%02x\n",
+ sc->sc_dev.dv_xname, header->header1.type);
+ }
+
+skip: /* setup new transfer */
+ usbd_setup_xfer(xfer, sc->sc_rx_pipeh, data_rx, data_rx->buf, MCLBYTES,
+ USBD_SHORT_XFER_OK, USBD_NO_TIMEOUT, upgt_rx_cb);
+ (void)usbd_transfer(xfer);
+}
+
+void
+upgt_rx(struct upgt_softc *sc, uint8_t *data, int pkglen)
+{
+ struct ieee80211com *ic = &sc->sc_ic;
+ struct ifnet *ifp = &ic->ic_if;
+ struct upgt_lmac_rx_desc *rxdesc;
+ struct ieee80211_frame *wh;
+ struct ieee80211_node *ni;
+ struct mbuf *m;
+ int s;
+
+ /* access RX packet descriptor */
+ rxdesc = (struct upgt_lmac_rx_desc *)data;
+
+ /* create mbuf which is suitable for strict alignment archs */
+ m = m_devget(rxdesc->data - ETHER_ALIGN, pkglen + ETHER_ALIGN, 0, ifp,
+ NULL);
+ if (m == NULL) {
+ printf("%s: could not create RX mbuf!\n", sc->sc_dev.dv_xname);
+ return;
+ }
+ m_adj(m, ETHER_ALIGN);
+
+ /* trim FCS */
+ m_adj(m, -IEEE80211_CRC_LEN);
+
+ s = splnet();
+
+#if NBPFILTER > 0
+ if (sc->sc_drvbpf != NULL) {
+ struct mbuf mb;
+ struct upgt_rx_radiotap_header *tap = &sc->sc_rxtap;
+
+ tap->wr_flags = 0;
+ tap->wr_rate = 0;
+ tap->wr_chan_freq = htole16(ic->ic_bss->ni_chan->ic_freq);
+ tap->wr_chan_flags = htole16(ic->ic_bss->ni_chan->ic_flags);
+ tap->wr_antenna = 0;
+ tap->wr_antsignal = rxdesc->rssi;
+
+ mb.m_data = (caddr_t)tap;
+ mb.m_len = sc->sc_rxtap_len;
+ mb.m_next = m;
+ mb.m_nextpkt = NULL;
+ mb.m_type = 0;
+ mb.m_flags = 0;
+ bpf_mtap(sc->sc_drvbpf, &mb, BPF_DIRECTION_IN);
+ }
+#endif
+
+ wh = mtod(m, struct ieee80211_frame *);
+ ni = ieee80211_find_rxnode(ic, wh);
+
+ /* push the frame up to the 802.11 stack */
+ ieee80211_input(ifp, m, ni, rxdesc->rssi, 0);
+
+ /* node is no longer needed */
+ ieee80211_release_node(ic, ni);
+
+ /* increment success incoming packet counter */
+ ifp->if_ipackets++;
+
+ splx(s);
+
+ DPRINTF(3, "%s: RX done\n", sc->sc_dev.dv_xname);
+}
+
+int
+upgt_set_macfilter(struct upgt_softc *sc, uint8_t state)
+{
+ struct ieee80211com *ic = &sc->sc_ic;
+ struct ieee80211_node *ni = ic->ic_bss;
+ struct upgt_data *data_cmd = &sc->cmd_data;
+ struct upgt_lmac_filter *filter;
+ int len;
+ uint8_t broadcast[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
+
+ /*
+ * Transmit the first URB containing the Prism memory address.
+ */
+ *((uint32_t *)data_cmd->buf) =
+ htole32(sc->sc_memaddr_frame_start + UPGT_MEMSIZE_FRAME_HEAD);
+
+ len = sizeof(uint32_t);
+ if (upgt_bulk_xmit(sc, data_cmd, sc->sc_tx_pipeh, &len, 0) != 0) {
+ printf("%s: could not transmit macfilter CMD memaddr URB!\n",
+ sc->sc_dev.dv_xname);
+ return (EIO);
+ }
+
+ /*
+ * Transmit the second URB containing the CMD data itself.
+ */
+ bzero(data_cmd->buf, MCLBYTES);
+
+ filter = (struct upgt_lmac_filter *)data_cmd->buf;
+
+ filter->header1.flags = UPGT_H1_FLAGS_TX_NO_CALLBACK;
+ filter->header1.type = UPGT_H1_TYPE_CTRL;
+ filter->header1.len = htole16(
+ sizeof(struct upgt_lmac_filter) -
+ sizeof(struct upgt_lmac_header));
+
+ filter->header2.reqid = htole32(sc->sc_memaddr_frame_start);
+ filter->header2.type = htole16(UPGT_H2_TYPE_MACFILTER);
+ filter->header2.flags = 0;
+
+ switch (state) {
+ case IEEE80211_S_SCAN:
+ DPRINTF(1, "%s: set MAC filter to SCAN (bssid %s)\n",
+ sc->sc_dev.dv_xname, ether_sprintf(broadcast));
+
+ filter->type = htole16(UPGT_FILTER_TYPE_NONE);
+ IEEE80211_ADDR_COPY(filter->dst, ic->ic_myaddr);
+ IEEE80211_ADDR_COPY(filter->src, broadcast);
+ filter->rxaddr = htole32(sc->sc_memaddr_rx_start);
+ filter->rxhw = htole16(sc->sc_eeprom_hwrx);
+ filter->unknown5 = htole16(UPGT_FILTER_UNKNOWN5_V2);
+ break;
+ case IEEE80211_S_RUN:
+ DPRINTF(1, "%s: set MAC filter to RUN (bssid %s)\n",
+ sc->sc_dev.dv_xname, ether_sprintf(ni->ni_bssid));
+
+ filter->type = htole16(UPGT_FILTER_TYPE_STA);
+ IEEE80211_ADDR_COPY(filter->dst, ic->ic_myaddr);
+ IEEE80211_ADDR_COPY(filter->src, ni->ni_bssid);
+ filter->unknown2 = htole32(UPGT_FILTER_UNKNOWN2_STA);
+ filter->rxaddr = htole32(sc->sc_memaddr_rx_start);
+ filter->unknown5 = htole16(UPGT_FILTER_UNKNOWN5_V2);
+ filter->rxhw = htole16(sc->sc_eeprom_hwrx);
+ filter->unknown6 = htole16(UPGT_FILTER_UNKNOWN6_STA);
+ break;
+ default:
+ printf("%s: MAC filter does not know that state!\n",
+ sc->sc_dev.dv_xname);
+ break;
+ }
+
+ len = sizeof(struct upgt_lmac_filter);
+ if (upgt_bulk_xmit(sc, data_cmd, sc->sc_tx_pipeh, &len, 0) != 0) {
+ printf("%s: could not transmit macfilter CMD data URB!\n",
+ sc->sc_dev.dv_xname);
+ return (EIO);
+ }
+
+ return (0);
+}
+
+int
+upgt_set_channel(struct upgt_softc *sc, unsigned channel)
+{
+ struct upgt_data *data_cmd = &sc->cmd_data;
+ struct upgt_lmac_channel *chan;
+ int len;
+
+ DPRINTF(1, "%s: %s: %d\n", sc->sc_dev.dv_xname, __func__, channel);
+
+ /*
+ * Transmit the first URB containing the Prism memory address.
+ */
+ *((uint32_t *)data_cmd->buf) =
+ htole32(sc->sc_memaddr_frame_start + UPGT_MEMSIZE_FRAME_HEAD);
+
+ len = sizeof(uint32_t);
+ if (upgt_bulk_xmit(sc, data_cmd, sc->sc_tx_pipeh, &len, 0) != 0) {
+ printf("%s: could not transmit channel CMD memaddr URB!\n",
+ sc->sc_dev.dv_xname);
+ return (EIO);
+ }
+
+ /*
+ * Transmit the second URB containing the CMD data itself.
+ */
+ bzero(data_cmd->buf, MCLBYTES);
+
+ chan = (struct upgt_lmac_channel *)data_cmd->buf;
+
+ chan->header1.flags = UPGT_H1_FLAGS_TX_NO_CALLBACK;
+ chan->header1.type = UPGT_H1_TYPE_CTRL;
+ chan->header1.len = htole16(
+ sizeof(struct upgt_lmac_channel) -
+ sizeof(struct upgt_lmac_header));
+
+ chan->header2.reqid = htole32(sc->sc_memaddr_frame_start);
+ chan->header2.type = htole16(UPGT_H2_TYPE_CHANNEL);
+ chan->header2.flags = 0;
+
+ chan->unknown1 = htole16(UPGT_CHANNEL_UNKNOWN1);
+ chan->unknown2 = htole16(UPGT_CHANNEL_UNKNOWN2);
+ chan->freq6 = sc->sc_eeprom_freq6[channel];
+ chan->settings = sc->sc_eeprom_freq6_settings;
+ chan->unknown3 = UPGT_CHANNEL_UNKNOWN3;
+
+ bcopy(&sc->sc_eeprom_freq3[channel].data, chan->freq3_1,
+ sizeof(chan->freq3_1));
+
+ bcopy(&sc->sc_eeprom_freq4[channel], chan->freq4,
+ sizeof(sc->sc_eeprom_freq4[channel]));
+
+ bcopy(&sc->sc_eeprom_freq3[channel].data, chan->freq3_2,
+ sizeof(chan->freq3_2));
+
+ len = sizeof(struct upgt_lmac_channel);
+ if (upgt_bulk_xmit(sc, data_cmd, sc->sc_tx_pipeh, &len, 0) != 0) {
+ printf("%s: could not transmit channel CMD data URB!\n",
+ sc->sc_dev.dv_xname);
+ return (EIO);
+ }
+
+ return (0);
+}
+
+int
+upgt_alloc_tx(struct upgt_softc *sc)
+{
+ int i;
+
+ sc->tx_queued = 0;
+
+ for (i = 0; i < UPGT_TX_COUNT; i++) {
+ struct upgt_data *data_tx = &sc->tx_data[i];
+
+ data_tx->sc = sc;
+
+ data_tx->xfer = usbd_alloc_xfer(sc->sc_udev);
+ if (data_tx->xfer == NULL) {
+ printf("%s: could not allocate TX xfer!\n",
+ sc->sc_dev.dv_xname);
+ return (ENOMEM);
+ }
+
+ data_tx->buf = usbd_alloc_buffer(data_tx->xfer, MCLBYTES);
+ if (data_tx->buf == NULL) {
+ printf("%s: could not allocate TX buffer!\n",
+ sc->sc_dev.dv_xname);
+ return (ENOMEM);
+ }
+
+ bzero(data_tx->buf, MCLBYTES);
+ }
+
+ return (0);
+}
+
+int
+upgt_alloc_rx(struct upgt_softc *sc)
+{
+ struct upgt_data *data_rx = &sc->rx_data;
+
+ data_rx->sc = sc;
+
+ data_rx->xfer = usbd_alloc_xfer(sc->sc_udev);
+ if (data_rx->xfer == NULL) {
+ printf("%s: could not allocate RX xfer!\n",
+ sc->sc_dev.dv_xname);
+ return (ENOMEM);
+ }
+
+ data_rx->buf = usbd_alloc_buffer(data_rx->xfer, MCLBYTES);
+ if (data_rx->buf == NULL) {
+ printf("%s: could not allocate RX buffer!\n",
+ sc->sc_dev.dv_xname);
+ return (ENOMEM);
+ }
+
+ bzero(data_rx->buf, MCLBYTES);
+
+ return (0);
+}
+
+int
+upgt_alloc_cmd(struct upgt_softc *sc)
+{
+ struct upgt_data *data_cmd = &sc->cmd_data;
+
+ data_cmd->sc = sc;
+
+ data_cmd->xfer = usbd_alloc_xfer(sc->sc_udev);
+ if (data_cmd->xfer == NULL) {
+ printf("%s: could not allocate RX xfer!\n",
+ sc->sc_dev.dv_xname);
+ return (ENOMEM);
+ }
+
+ data_cmd->buf = usbd_alloc_buffer(data_cmd->xfer, MCLBYTES);
+ if (data_cmd->buf == NULL) {
+ printf("%s: could not allocate RX buffer!\n",
+ sc->sc_dev.dv_xname);
+ return (ENOMEM);
+ }
+
+ bzero(data_cmd->buf, MCLBYTES);
+
+ return (0);
+}
+
+void
+upgt_free_tx(struct upgt_softc *sc)
+{
+ int i;
+
+ for (i = 0; i < UPGT_TX_COUNT; i++) {
+ struct upgt_data *data_tx = &sc->tx_data[i];
+
+ if (data_tx->xfer != NULL) {
+ usbd_free_xfer(data_tx->xfer);
+ data_tx->xfer = NULL;
+ }
+
+ data_tx->ni = NULL;
+ }
+}
+
+void
+upgt_free_rx(struct upgt_softc *sc)
+{
+ struct upgt_data *data_rx = &sc->rx_data;
+
+ if (data_rx->xfer != NULL) {
+ usbd_free_xfer(data_rx->xfer);
+ data_rx->xfer = NULL;
+ }
+
+ data_rx->ni = NULL;
+}
+
+void
+upgt_free_cmd(struct upgt_softc *sc)
+{
+ struct upgt_data *data_cmd = &sc->cmd_data;
+
+ if (data_cmd->xfer != NULL) {
+ usbd_free_xfer(data_cmd->xfer);
+ data_cmd->xfer = NULL;
+ }
+}
+
+/*
+ * For this device we use usbd_bulk_transfer() for all TX transfers.
+ */
+int
+upgt_bulk_xmit(struct upgt_softc *sc, struct upgt_data *data,
+ usbd_pipe_handle pipeh, uint32_t *size, int flags)
+{
+ usbd_status status;
+
+ status = usbd_bulk_transfer(data->xfer, pipeh,
+ USBD_NO_COPY | flags, UPGT_USB_TIMEOUT, data->buf, size,
+ "upgt_bulk_xmit");
+ if (status != USBD_NORMAL_COMPLETION) {
+ printf("%s: %s: error %s!\n",
+ sc->sc_dev.dv_xname, __func__, usbd_errstr(status));
+ return (EIO);
+ }
+
+ return (0);
+}
+
+void
+upgt_hexdump(void *buf, int len)
+{
+ int i;
+
+ for (i = 0; i < len; i++) {
+ if (i % 16 == 0)
+ printf("%s%5i:", i ? "\n" : "", i);
+ if (i % 4 == 0)
+ printf(" ");
+ printf("%02x", (int)*((u_char *)buf + i));
+ }
+ printf("\n");
+}
+
+/*
+ * COPYRIGHT (C) 1986 Gary S. Brown. You may use this program, or
+ * code or tables extracted from it, as desired without restriction.
+ */
+static uint32_t upgt_crc32_tab[] = {
+ 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
+ 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
+ 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2,
+ 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
+ 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
+ 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
+ 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c,
+ 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
+ 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423,
+ 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
+ 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106,
+ 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
+ 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d,
+ 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
+ 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
+ 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
+ 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7,
+ 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
+ 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa,
+ 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
+ 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81,
+ 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
+ 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84,
+ 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
+ 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
+ 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
+ 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e,
+ 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
+ 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55,
+ 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
+ 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28,
+ 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
+ 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f,
+ 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
+ 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
+ 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
+ 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69,
+ 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
+ 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc,
+ 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
+ 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693,
+ 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
+ 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
+};
+
+uint32_t
+upgt_crc32(const void *buf, size_t size)
+{
+ const uint8_t *p;
+ uint32_t crc;
+
+ p = buf;
+ crc = ~0U;
+
+ while (size--)
+ crc = upgt_crc32_tab[(crc ^ *p++) & 0xff] ^ (crc >> 8);
+
+ return (crc ^ ~0U);
+}
diff --git a/sys/dev/usb/if_upgtvar.h b/sys/dev/usb/if_upgtvar.h
new file mode 100644
index 00000000000..405fd232406
--- /dev/null
+++ b/sys/dev/usb/if_upgtvar.h
@@ -0,0 +1,432 @@
+/* $OpenBSD: if_upgtvar.h,v 1.1 2007/12/16 09:05:53 mglocker Exp $ */
+
+/*
+ * Copyright (c) 2007 Marcus Glocker <mglocker@openbsd.org>
+ *
+ * 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.
+ */
+
+struct upgt_softc;
+
+/*
+ * Radio tap.
+ */
+struct upgt_rx_radiotap_header {
+ struct ieee80211_radiotap_header wr_ihdr;
+ uint8_t wr_flags;
+ uint8_t wr_rate;
+ uint16_t wr_chan_freq;
+ uint16_t wr_chan_flags;
+ uint8_t wr_antenna;
+ uint8_t wr_antsignal;
+} __packed;
+
+#define UPGT_RX_RADIOTAP_PRESENT \
+ ((1 << IEEE80211_RADIOTAP_FLAGS) | \
+ (1 << IEEE80211_RADIOTAP_RATE) | \
+ (1 << IEEE80211_RADIOTAP_CHANNEL) | \
+ (1 << IEEE80211_RADIOTAP_ANTENNA) | \
+ (1 << IEEE80211_RADIOTAP_DB_ANTSIGNAL))
+
+struct upgt_tx_radiotap_header {
+ struct ieee80211_radiotap_header wt_ihdr;
+ uint8_t wt_flags;
+ uint8_t wt_rate;
+ uint16_t wt_chan_freq;
+ uint16_t wt_chan_flags;
+ uint8_t wt_antenna;
+} __packed;
+
+#define UPGT_TX_RADIOTAP_PRESENT \
+ ((1 << IEEE80211_RADIOTAP_FLAGS) | \
+ (1 << IEEE80211_RADIOTAP_RATE) | \
+ (1 << IEEE80211_RADIOTAP_CHANNEL) | \
+ (1 << IEEE80211_RADIOTAP_ANTENNA))
+
+/*
+ * General values.
+ */
+#define UPGT_IFACE_INDEX 0
+#define UPGT_CONFIG_NO 1
+#define UPGT_USB_TIMEOUT 1000
+#define UPGT_FIRMWARE_TIMEOUT 10
+
+#define UPGT_MEMADDR_FIRMWARE_START 0x00020000 /* 512 bytes large */
+#define UPGT_MEMSIZE_FRAME_HEAD 0x0070
+#define UPGT_MEMSIZE_RX 0x3500
+
+#define UPGT_TX_COUNT 6
+
+/* device flags */
+#define UPGT_DEVICE_ATTACHED (1 << 0)
+
+/*
+ * USB xfers.
+ */
+struct upgt_data {
+ struct upgt_softc *sc;
+ usbd_xfer_handle xfer;
+ void *buf;
+ struct ieee80211_node *ni;
+ struct mbuf *m;
+ uint32_t addr;
+};
+
+/*
+ * Firmware.
+ */
+#define UPGT_FW_BLOCK_SIZE 512
+
+#define UPGT_BRA_FWTYPE_SIZE 4
+#define UPGT_BRA_FWTYPE_LM86 "LM86"
+#define UPGT_BRA_FWTYPE_LM87 "LM87"
+#define UPGT_BRA_FWTYPE_FMAC "FMAC"
+enum upgt_fw_type {
+ UPGT_FWTYPE_LM86,
+ UPGT_FWTYPE_LM87,
+ UPGT_FWTYPE_FMAC
+};
+
+#define UPGT_BRA_TYPE_FW 0x80000001
+#define UPGT_BRA_TYPE_VERSION 0x80000002
+#define UPGT_BRA_TYPE_DEPIF 0x80000003
+#define UPGT_BRA_TYPE_EXPIF 0x80000004
+#define UPGT_BRA_TYPE_DESCR 0x80000101
+#define UPGT_BRA_TYPE_END 0xff0000ff
+struct upgt_fw_bra_option {
+ uint32_t type;
+ uint32_t len;
+ uint8_t data[];
+} __packed;
+
+struct upgt_fw_bra_descr {
+ uint32_t unknown1;
+ uint32_t memaddr_space_start;
+ uint32_t memaddr_space_end;
+ uint32_t unknown2;
+ uint32_t unknown3;
+ uint8_t rates[20];
+} __packed;
+
+#define UPGT_X2_SIGNATURE_SIZE 4
+#define UPGT_X2_SIGNATURE "x2 "
+struct upgt_fw_x2_header {
+ uint8_t signature[4];
+ uint32_t startaddr;
+ uint32_t len;
+ uint32_t crc;
+} __packed;
+
+/*
+ * EEPROM.
+ */
+#define UPGT_EEPROM_SIZE 8192
+#define UPGT_EEPROM_BLOCK_SIZE 1020
+
+struct upgt_eeprom_header {
+ /* 14 bytes */
+ uint32_t magic;
+ uint16_t pad1;
+ uint16_t preamble_len;
+ uint32_t pad2;
+ /* data */
+} __packed;
+
+#define UPGT_EEPROM_TYPE_END 0x0000
+#define UPGT_EEPROM_TYPE_NAME 0x0001
+#define UPGT_EEPROM_TYPE_SERIAL 0x0003
+#define UPGT_EEPROM_TYPE_MAC 0x0101
+#define UPGT_EEPROM_TYPE_HWRX 0x1001
+#define UPGT_EEPROM_TYPE_CHIP 0x1002
+#define UPGT_EEPROM_TYPE_FREQ3 0x1903
+#define UPGT_EEPROM_TYPE_FREQ4 0x1904
+#define UPGT_EEPROM_TYPE_FREQ5 0x1905
+#define UPGT_EEPROM_TYPE_FREQ6 0x1906
+#define UPGT_EEPROM_TYPE_OFF 0xffff
+struct upgt_eeprom_option {
+ uint16_t len;
+ uint16_t type;
+ uint8_t data[];
+ /* data */
+} __packed;
+
+#define UPGT_EEPROM_RX_CONST 0x88
+struct upgt_eeprom_option_hwrx {
+ uint32_t pad1;
+ uint8_t rxfilter;
+ uint8_t pad2[15];
+} __packed;
+
+struct upgt_eeprom_freq3_header {
+ uint8_t flags;
+ uint8_t elements;
+} __packed;
+
+struct upgt_eeprom_freq4_header {
+ uint8_t flags;
+ uint8_t elements;
+ uint8_t settings;
+ uint8_t type;
+} __packed;
+
+struct upgt_eeprom_freq4_1 {
+ uint16_t freq;
+ uint8_t data[50];
+} __packed;
+
+struct upgt_eeprom_freq4_2 {
+ uint16_t head;
+ uint8_t subtails[4];
+ uint8_t tail;
+} __packed;
+
+/*
+ * LMAC protocol.
+ */
+#define UPGT_H1_FLAGS_TX_MGMT 0x00 /* for TX: mgmt frame */
+#define UPGT_H1_FLAGS_TX_NO_CALLBACK 0x01 /* for TX: no USB callback */
+#define UPGT_H1_FLAGS_TX_DATA 0x10 /* for TX: data frame */
+#define UPGT_H1_TYPE_DATA_RX 0x00 /* 802.11 RX data frame */
+#define UPGT_H1_TYPE_DATA_RX_MGMT 0x01 /* 802.11 RX mgmt frame */
+#define UPGT_H1_TYPE_DATA_TX 0x40 /* 802.11 TX data frame */
+#define UPGT_H1_TYPE_CTRL 0x80 /* control frame */
+struct upgt_lmac_h1 {
+ /* 4 bytes */
+ uint8_t flags;
+ uint8_t type;
+ uint16_t len;
+} __packed;
+
+#define UPGT_H2_TYPE_TX_NOACK 0x0000 /* for TX */
+#define UPGT_H2_TYPE_MACFILTER 0x0000
+#define UPGT_H2_TYPE_CHANNEL 0x0001
+#define UPGT_H2_TYPE_TX_DONE 0x0008
+#define UPGT_H2_TYPE_EEPROM 0x000c
+struct upgt_lmac_h2 {
+ /* 8 bytes */
+ uint32_t reqid;
+ uint16_t type;
+ uint16_t flags;
+} __packed;
+
+struct upgt_lmac_header {
+ /* 12 bytes */
+ struct upgt_lmac_h1 header1;
+ struct upgt_lmac_h2 header2;
+} __packed;
+
+struct upgt_lmac_eeprom {
+ /* 16 bytes */
+ struct upgt_lmac_h1 header1;
+ struct upgt_lmac_h2 header2;
+ uint16_t offset;
+ uint16_t len;
+ /* data */
+} __packed;
+
+#define UPGT_FILTER_TYPE_NONE 0x0000
+#define UPGT_FILTER_TYPE_STA 0x0001
+#define UPGT_FILTER_TYPE_IBSS 0x0002
+#define UPGT_FILTER_TYPE_HOSTAP 0x0004
+#define UPGT_FILTER_TYPE_MONITOR 0x0010
+#define UPGT_FILTER_UNKNOWN2_STA 0x0000015f
+#define UPGT_FILTER_UNKNOWN5_V2 0x0620
+#define UPGT_FILTER_UNKNOWN6_STA 0x01f4
+struct upgt_lmac_filter {
+ struct upgt_lmac_h1 header1;
+ struct upgt_lmac_h2 header2;
+ /* 40 bytes */
+ uint16_t type;
+ uint8_t dst[IEEE80211_ADDR_LEN];
+ uint8_t src[IEEE80211_ADDR_LEN];
+ uint16_t unknown1;
+ uint32_t unknown2;
+ uint32_t unknown3;
+ uint32_t unknown4;
+ uint32_t rxaddr;
+ uint16_t unknown5;
+ uint16_t rxhw;
+ uint16_t unknown6;
+ uint16_t unknown7;
+} __packed;
+
+/* frequence 3 data */
+struct upgt_lmac_freq3 {
+ uint16_t freq;
+ uint8_t data[6];
+} __packed;
+
+/* frequence 4 data */
+struct upgt_lmac_freq4 {
+ struct upgt_eeprom_freq4_2 cmd;
+ uint8_t pad;
+};
+
+/* frequence 6 data */
+struct upgt_lmac_freq6 {
+ uint16_t freq;
+ uint8_t data[8];
+} __packed;
+
+#define UPGT_CHANNEL_UNKNOWN1 0x0001
+#define UPGT_CHANNEL_UNKNOWN2 0x0000
+#define UPGT_CHANNEL_UNKNOWN3 0x48
+struct upgt_lmac_channel {
+ struct upgt_lmac_h1 header1;
+ struct upgt_lmac_h2 header2;
+ /* 112 bytes */
+ uint16_t unknown1;
+ uint16_t unknown2;
+ uint8_t pad1[20];
+ struct upgt_lmac_freq6 freq6;
+ uint8_t settings;
+ uint8_t unknown3;
+ uint8_t freq3_1[4]; /* XXX wrong? */
+ struct upgt_lmac_freq4 freq4[8];
+ uint8_t freq3_2[4]; /* XXX wrong? */
+ uint32_t pad2;
+} __packed;
+
+struct upgt_lmac_rx_desc {
+ struct upgt_lmac_h1 header1;
+ /* 16 bytes */
+ uint16_t freq;
+ uint8_t unknown1;
+ uint8_t rate;
+ uint8_t rssi;
+ uint8_t pad;
+ uint16_t unknown2;
+ uint32_t timestamp;
+ uint32_t unknown3;
+ uint8_t data[];
+} __packed;
+
+#define UPGT_TX_DESC_KEY_EXISTS 0x01
+struct upgt_lmac_tx_desc_wep {
+ uint8_t key_exists;
+ uint8_t key_len;
+ uint8_t key_val[16];
+} __packed;
+
+#define UPGT_TX_DESC_TYPE_BEACON 0x00000000
+#define UPGT_TX_DESC_TYPE_PROBE 0x00000001
+#define UPGT_TX_DESC_TYPE_MGMT 0x00000002
+#define UPGT_TX_DESC_TYPE_DATA 0x00000004
+#define UPGT_TX_DESC_FRAME_PAD 4
+struct upgt_lmac_tx_desc {
+ struct upgt_lmac_h1 header1;
+ struct upgt_lmac_h2 header2;
+ uint8_t rates[8];
+ uint16_t pad1;
+ struct upgt_lmac_tx_desc_wep wep_key;
+ uint32_t type;
+ uint32_t pad2;
+ uint32_t unknown1;
+ uint32_t unknown2;
+ uint8_t frame_pad;
+ uint8_t pad3[3];
+ /* 802.11 frame data */
+} __packed;
+
+#define UPGT_TX_DONE_DESC_STATUS_OK 0x0001
+struct upgt_lmac_tx_done_desc {
+ struct upgt_lmac_h1 header1;
+ struct upgt_lmac_h2 header2;
+ uint16_t status;
+ uint16_t rssi;
+ uint16_t seq;
+ uint16_t unknown;
+} __packed;
+
+/*
+ * Prism memory.
+ */
+struct upgt_memory_page {
+ uint8_t used;
+ uint32_t addr;
+} __packed;
+
+#define UPGT_MEMORY_MAX_PAGES 8
+struct upgt_memory {
+ uint8_t pages;
+ struct upgt_memory_page page[UPGT_MEMORY_MAX_PAGES];
+} __packed;
+
+/*
+ * Softc.
+ */
+struct upgt_softc {
+ struct device sc_dev;
+
+ usbd_device_handle sc_udev;
+ usbd_interface_handle sc_iface;
+ int sc_rx_no;
+ int sc_tx_no;
+ struct usb_task sc_task_newstate;
+ struct usb_task sc_task_tx;
+ usbd_pipe_handle sc_rx_pipeh;
+ usbd_pipe_handle sc_tx_pipeh;
+
+ struct upgt_data tx_data[UPGT_TX_COUNT];
+ struct upgt_data rx_data;
+ struct upgt_data cmd_data;
+ int tx_queued;
+
+ struct ieee80211com sc_ic;
+ enum ieee80211_state sc_state;
+ int sc_arg;
+ int (*sc_newstate)(struct ieee80211com *,
+ enum ieee80211_state, int);
+ struct timeout scan_to;
+ unsigned sc_curchan;
+
+ int sc_flags;
+ uint8_t *sc_fw;
+ size_t sc_fw_size;
+ int sc_fw_type;
+
+ /* memory addresses on device */
+ uint32_t sc_memaddr_frame_start;
+ uint32_t sc_memaddr_frame_end;
+ uint32_t sc_memaddr_rx_start;
+ struct upgt_memory sc_memory;
+
+ /* data which we found in the EEPROM */
+ uint8_t sc_eeprom[UPGT_EEPROM_SIZE];
+ uint16_t sc_eeprom_hwrx;
+ struct upgt_lmac_freq3 sc_eeprom_freq3[IEEE80211_CHAN_MAX];
+ struct upgt_lmac_freq4 sc_eeprom_freq4[IEEE80211_CHAN_MAX][8];
+ struct upgt_lmac_freq6 sc_eeprom_freq6[IEEE80211_CHAN_MAX];
+ uint8_t sc_eeprom_freq6_settings;
+
+ /* radio tap */
+#if NBPFILTER > 0
+ caddr_t sc_drvbpf;
+
+ /* RX */
+ union {
+ struct upgt_rx_radiotap_header th;
+ uint8_t pad[64];
+ } sc_rxtapu;
+#define sc_rxtap sc_rxtapu.th
+ int sc_rxtap_len;
+
+ /* TX */
+ union {
+ struct upgt_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 29143c44520..4f22194bbf7 100644
--- a/sys/dev/usb/usbdevs
+++ b/sys/dev/usb/usbdevs
@@ -1,4 +1,4 @@
-$OpenBSD: usbdevs,v 1.324 2007/12/07 18:11:11 deraadt Exp $
+$OpenBSD: usbdevs,v 1.325 2007/12/16 09:05:53 mglocker Exp $
/* $NetBSD: usbdevs,v 1.322 2003/05/10 17:47:14 hamajima Exp $ */
/*
@@ -2209,6 +2209,7 @@ product SMC2 2020HUB 0x2020 Hub
product SMC2 2504HUB 0x2504 Hub
product SMC3 2662WV1 0xa001 EZ Connect 11Mbps
product SMC3 2662WV2 0xa002 EZ Connect 11Mbps v2
+product SMC 2862WG 0xee13 EZ Connect 54Mbps
/* SOHOware products */
product SOHOWARE NUB100 0x9100 NUB100 Ethernet