Subject: Support of USB/WiFi D-Link DWL-G122 802.11g on NetBSD
To: None <tech-kern@netbsd.org>
From: Ian Zagorskih <ianzag@megasignal.com>
List: tech-kern
Date: 04/27/2005 18:54:09
--Boundary-00=_h13bC3MzttkCcJc
Content-Type: text/plain;
  charset="us-ascii"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline


$ uname -a
NetBSD IANZAG 3.99.3 NetBSD 3.99.3 (DWL-G122) #38: Wed Apr 27 16:48:49 NOVST 
2005  ianzag@IANZAG:/home/ianzag/NetBSD/kernel/DWL-G122 i386

Some time ago i got two D-Link DWL-G122 USB-WiFi adapters as described at:

http://www.dlink.com/products/?pid=334

Unfortunately, NetBSD-current does not have suport for this cards yet, but 
looks like OpenBSD-current has. So i desided to port OpenBSD's code on 
NetBSD. After some work looks like ported driver works quite fine [except 
problems described below].

Changes relatively to NetBSD-current:
1. dev/usb/if_ral* - new files from OpenBSD with NetBSD specific changes. All 
changes are wrapped with  __OpenBSD__ /__NetBSD__ defines.
2. dev/usb/files.usb - added new device ural.
3. usbdevs - added new USB device codes.
4. net80211 - added ieee80211_beacon_alloc() from OpenBSD.
5. ral.4 - man page for *ral cards. I guess it should be fixed to include only 
USB version. Well, or port PCI/CardBus cards code too :)

For testing i have two machines, A and B, with identical DSL-G122 cards. On 
both machines USB 1.1 was used.

dmesg when card is inserted:
---cut---
ural0 at uhub4 port 5
ural0: ANI 802.11g W, rev 2.00/0.01, addr 6
ural0: MAC/BBP RT2570 (rev 0x03), RF RT2526, address 00:11:95:87:5d:88
ural0: 11b rates: 1Mbps 2Mbps 5.5Mbps 11Mbps
ural0: 11g rates: 1Mbps 2Mbps 5.5Mbps 11Mbps 6Mbps 9Mbps 12Mbps 18Mbps 24Mbps 
36Mbps 48Mbps 54Mbps
---cut---

Configure machine A as Host AP with IP 10.0.0.1

# ifconfig ural0 ssid foo mediaopt hostap
# ifconfig ural0 inet 10.0.0.1
# ifconfig ural0
ural0: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> mtu 1500
        ssid foo
        powersave off
        bssid 00:11:95:87:5d:88 chan 1
        address: 00:11:95:87:5d:88
        media: IEEE802.11 autoselect hostap (autoselect mode 11b hostap)
        status: active
        inet 10.0.0.1 netmask 0xff000000 broadcast 10.255.255.255

Configure machine B as BSS with IP 10.0.0.2

# ifconfig ural0 ssid foo
# ifconfig ural0 inet 10.0.0.2
# ifconfig ural0
ural0: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> mtu 1500
        ssid foo
        powersave off
        bssid 00:11:95:87:5d:88 chan 1
        address: 00:11:95:89:99:27
        media: IEEE802.11 autoselect (DS11)
        status: active
        inet 10.0.0.2 netmask 0xff000000 broadcast 10.255.255.255

Check for online nodes around from machine A:

# wiconfig ural0 -D
scanning ...
AP Information
ap[0]:
        netname (SSID):                 [ foo ]
        BSSID:                          [ 00:11:95:89:99:27 ]
        Channel:                        [ 1 ]
        Quality/Signal/Noise [signal]:  [ 0 / 20 / 0 ]
                                [dBm]:  [ 0 / -129 / -149 ]
        BSS Beacon Interval [msec]:     [ 100 ]
        Capinfo:                        [ ESS ]
        DataRate [Mbps]:                [ 2 ]

So looks like both A and B find eachother just fine. Great :)

The problem is that when i ping another machine, for example, B from A, i get 
the following in my dmesg:

# ping 10.0.0.1
PING 10.0.0.1 (10.0.0.1): 56 data bytes
^C
----10.0.0.1 PING Statistics----
8 packets transmitted, 0 packets received, 100.0% packet loss

arpresolve: can't allocate llinfo on ural0 for 10.0.0.1
arpresolve: can't allocate llinfo on ural0 for 10.0.0.1
arpresolve: can't allocate llinfo on ural0 for 10.0.0.1
arpresolve: can't allocate llinfo on ural0 for 10.0.0.1
arpresolve: can't allocate llinfo on ural0 for 10.0.0.1
arpresolve: can't allocate llinfo on ural0 for 10.0.0.1
arpresolve: can't allocate llinfo on ural0 for 10.0.0.1
arpresolve: can't allocate llinfo on ural0 for 10.0.0.1

Looks like ARP level + new driver are broken at some point.

Would be great if anybody with experience in NetBSD inet stack and WiFi would 
review my patches and gave me some hints/comments/etc. I'm trying to discover 
why ARP is broken but my current knowledge isn't enough for this.

But anyway, looks like OpenBSD D-Link & K driver almost works on NetBSD :)

// wbr

--Boundary-00=_h13bC3MzttkCcJc
Content-Type: text/x-chdr;
  charset="us-ascii";
  name="if_ralvar.h"
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
	filename="if_ralvar.h"

/*	$NetBSD$ */
/*	$OpenBSD: if_ralvar.h,v 1.1 2005/03/16 20:17:10 damien Exp $  */

/*-
 * Copyright (c) 2005
 *	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 RAL_RX_LIST_COUNT	1
#define RAL_TX_LIST_COUNT	1

struct ural_rx_radiotap_header {
	struct ieee80211_radiotap_header wr_ihdr;
	uint8_t		wr_flags;
	uint16_t	wr_chan_freq;
	uint16_t	wr_chan_flags;
	uint8_t		wr_antenna;
	uint8_t		wr_antsignal;
} __packed;

#define RAL_RX_RADIOTAP_PRESENT						\
	((1 << IEEE80211_RADIOTAP_FLAGS) |				\
	 (1 << IEEE80211_RADIOTAP_CHANNEL) |				\
	 (1 << IEEE80211_RADIOTAP_ANTENNA) |				\
	 (1 << IEEE80211_RADIOTAP_DB_ANTSIGNAL))

struct ural_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 RAL_TX_RADIOTAP_PRESENT						\
	((1 << IEEE80211_RADIOTAP_FLAGS) |				\
	 (1 << IEEE80211_RADIOTAP_RATE) |				\
	 (1 << IEEE80211_RADIOTAP_CHANNEL) |				\
	 (1 << IEEE80211_RADIOTAP_ANTENNA))

struct ural_softc;

struct ural_tx_data {
	struct ural_softc	*sc;
	usbd_xfer_handle	xfer;
	uint8_t			*buf;
	struct mbuf		*m;
	struct ieee80211_node	*ni;
};

struct ural_rx_data {
	struct ural_softc	*sc;
	usbd_xfer_handle	xfer;
	uint8_t			*buf;
	struct mbuf		*m;
};

struct ural_softc {
	USBBASEDEVICE		sc_dev;
	struct ieee80211com	sc_ic;
	int			(*sc_newstate)(struct ieee80211com *,
				    enum ieee80211_state, int);

	usbd_device_handle	sc_udev;
	usbd_interface_handle	sc_iface;

	uint8_t			sc_rx_no;
	uint8_t			sc_tx_no;

	uint32_t		asic_rev;
	uint8_t			rf_rev;

	usbd_pipe_handle	sc_rx_pipeh;
	usbd_pipe_handle	sc_tx_pipeh;

	enum ieee80211_state	sc_state;
	struct usb_task		sc_task;

	struct ural_rx_data	rx_data[RAL_RX_LIST_COUNT];
	struct ural_tx_data	tx_data[RAL_TX_LIST_COUNT];
	int			tx_queued;

#ifdef __OpenBSD__
	struct timeout		scan_ch;
#else
	struct callout		scan_ch;
#endif

	int			sc_tx_timer;

	uint32_t		rf_regs[4];
	uint8_t			txpow[14];

	struct {
		uint8_t	val;
		uint8_t	reg;
	} __packed		bbp_prom[16];

	int			led_mode;
	int			hw_radio;
	int			rx_ant;
	int			tx_ant;
	int			nb_ant;

#if NBPFILTER > 0
	caddr_t			sc_drvbpf;

	union {
		struct ural_rx_radiotap_header th;
		uint8_t	pad[64];
	}			sc_rxtapu;
#define sc_rxtap	sc_rxtapu.th
	int			sc_rxtap_len;

	union {
		struct ural_tx_radiotap_header th;
		uint8_t	pad[64];
	}			sc_txtapu;
#define sc_txtap	sc_txtapu.th
	int			sc_txtap_len;
#endif
};

--Boundary-00=_h13bC3MzttkCcJc
Content-Type: text/x-chdr;
  charset="us-ascii";
  name="if_ralreg.h"
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
	filename="if_ralreg.h"

/*	$NetBSD$ */
/*	$OpenBSD: if_ralreg.h,v 1.5 2005/04/01 13:13:43 damien Exp $  */

/*-
 * Copyright (c) 2005
 *	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 RAL_RX_DESC_SIZE	(sizeof (struct ural_rx_desc))
#define RAL_TX_DESC_SIZE	(sizeof (struct ural_tx_desc))

#define RAL_CONFIG_NO	1
#define RAL_IFACE_INDEX 0

#define RAL_WRITE_MAC		0x02
#define RAL_READ_MAC		0x03
#define RAL_WRITE_MULTI_MAC	0x06
#define RAL_READ_MULTI_MAC	0x07
#define RAL_READ_EEPROM		0x09

/*
 * MAC registers.
 */
#define RAL_MAC_CSR0	0x0400	/* ASIC Version */
#define RAL_MAC_CSR1	0x0402	/* System control */
#define RAL_MAC_CSR2	0x0404	/* MAC addr0 */
#define RAL_MAC_CSR3	0x0406	/* MAC addr1 */
#define RAL_MAC_CSR4	0x0408	/* MAC addr2 */
#define RAL_MAC_CSR5	0x040a	/* BSSID0 */
#define RAL_MAC_CSR6	0x040c	/* BSSID1 */
#define RAL_MAC_CSR7	0x040e	/* BSSID2 */
#define RAL_MAC_CSR8	0x0410	/* Max frame length */
#define RAL_MAC_CSR9	0x0412	/* Timer control */
#define RAL_MAC_CSR11	0x0416	/* IFS */
#define RAL_MAC_CSR12	0x0418	/* EIFS */
#define RAL_MAC_CSR13	0x041a	/* Power mode0 */
#define RAL_MAC_CSR14	0x041c	/* Power mode1 */
#define RAL_MAC_CSR15	0x041e	/* Power saving transition0 */
#define RAL_MAC_CSR16	0x0420	/* Power saving transition1 */
#define RAL_MAC_CSR17	0x0422	/* Power state control */
#define RAL_MAC_CSR18	0x0424	/* Auto wake-up control */
#define RAL_MAC_CSR19	0x0426	/* GPIO control */
#define RAL_MAC_CSR20	0x0428	/* LED control0 */
#define RAL_MAC_CSR22	0x042c	/* XXX not documented */

/*
 * Tx/Rx Registers.
 */
#define RAL_TXRX_CSR0	0x0440	/* Security control */
#define RAL_TXRX_CSR2	0x0444	/* Rx control */
#define RAL_TXRX_CSR5	0x044a	/* CCK Tx BBP ID0 */
#define RAL_TXRX_CSR6	0x044c	/* CCK Tx BBP ID1 */
#define RAL_TXRX_CSR7	0x044e	/* OFDM Tx BBP ID0 */
#define RAL_TXRX_CSR8	0x0450	/* OFDM Tx BBP ID1 */
#define RAL_TXRX_CSR11	0x0456	/* Auto responder basic rate */
#define RAL_TXRX_CSR18	0x0464	/* Beacon interval */
#define RAL_TXRX_CSR19	0x0466	/* Beacon/sync control */
#define RAL_TXRX_CSR20	0x0468	/* Beacon alignment */
#define RAL_TXRX_CSR21	0x046a	/* XXX not documented */

/*
 * Security registers.
 */
#define RAL_SEC_CSR0	0x0480	/* Shared key 0, word 0 */

/*
 * PHY registers.
 */
#define RAL_PHY_CSR2	0x04c4	/* Tx MAC configuration */
#define RAL_PHY_CSR4	0x04c8	/* Interface configuration */
#define RAL_PHY_CSR5	0x04ca	/* BBP Pre-Tx CCK */
#define RAL_PHY_CSR6	0x04cc	/* BBP Pre-Tx OFDM */
#define RAL_PHY_CSR7	0x04ce	/* BBP serial control */
#define RAL_PHY_CSR8	0x04d0	/* BBP serial status */
#define RAL_PHY_CSR9	0x04d2	/* RF serial control0 */
#define RAL_PHY_CSR10	0x04d4	/* RF serial control1 */

/*
 * Statistics registers.
 */
#define RAL_STA_CSR0	0x04e0	/* FCS error */


#define RAL_DISABLE_RX		(1 << 0)
#define RAL_DROP_CRC_ERROR	(1 << 1)
#define RAL_DROP_PHY_ERROR	(1 << 2)
#define RAL_DROP_CTL		(1 << 3)
#define RAL_DROP_NOT_TO_ME	(1 << 4)
#define RAL_DROP_TODS		(1 << 5)
#define RAL_DROP_VERSION_ERROR	(1 << 6)
#define RAL_DROP_MULTICAST	(1 << 9)
#define RAL_DROP_BROADCAST	(1 << 10)

#define RAL_HOST_READY	(1 << 2)
#define RAL_RESET_ASIC	(1 << 0)
#define RAL_RESET_BBP	(1 << 1)

#define RAL_ENABLE_TSF			(1 << 0)
#define RAL_ENABLE_TSF_SYNC(x)		(((x) & 0x3) << 1)
#define RAL_ENABLE_TBCN			(1 << 3)
#define RAL_ENABLE_BEACON_GENERATOR	(1 << 4)

#define RAL_RF_AWAKE	(3 << 7)
#define RAL_BBP_AWAKE	(3 << 5)

#define RAL_BBP_WRITE	(1 << 15)
#define RAL_BBP_BUSY	(1 << 0)

#define RAL_RF1_AUTOTUNE	0x08000
#define RAL_RF3_AUTOTUNE	0x00040

#define RAL_RF_2522	0x00
#define RAL_RF_2523	0x01
#define RAL_RF_2524	0x02
#define RAL_RF_2525	0x03
#define RAL_RF_2525E	0x04
#define RAL_RF_2526	0x05
/* dual-band RF */
#define RAL_RF_5222	0x10

#define RAL_BBP_VERSION	0
#define RAL_BBP_TX	2
#define RAL_BBP_RX	14

#define RAL_BBP_ANTA		0x00
#define RAL_BBP_DIVERSITY	0x01
#define RAL_BBP_ANTB		0x02
#define RAL_BBP_ANTMASK		0x03
#define RAL_BBP_FLIPIQ		0x04

#define RAL_JAPAN_FILTER	0x08

struct ural_tx_desc {
	uint32_t	flags;
#define RAL_TX_RETRY(x)		((x) << 4)
#define RAL_TX_MORE_FRAG	(1 << 8)
#define RAL_TX_ACK		(1 << 9)
#define RAL_TX_TIMESTAMP	(1 << 10)
#define RAL_TX_OFDM		(1 << 11)
#define RAL_TX_NEWSEQ		(1 << 12)

#define RAL_TX_IFS_MASK		0x00006000
#define RAL_TX_IFS_BACKOFF	(0 << 13)
#define RAL_TX_IFS_SIFS		(1 << 13)
#define RAL_TX_IFS_NEWBACKOFF	(2 << 13)
#define RAL_TX_IFS_NONE		(3 << 13)

	uint16_t	wme;
#define RAL_LOGCWMAX(x)		(((x) & 0xf) << 12)
#define RAL_LOGCWMIN(x)		(((x) & 0xf) << 8)
#define RAL_AIFSN(x)		(((x) & 0x3) << 6)
#define RAL_IVOFFSET(x)		(((x) & 0x3f))

	uint16_t	reserved;
	uint8_t		plcp_signal;
	uint8_t		plcp_service;
#define RAL_PLCP_LENGEXT	0x80

	uint16_t	plcp_length;
	uint32_t	iv;
	uint32_t	eiv;
} __packed;

struct ural_rx_desc {
	uint32_t	flags;
#define RAL_RX_CRC_ERROR	(1 << 5)
#define RAL_RX_PHY_ERROR	(1 << 7)

	uint8_t		rate;
	uint8_t		rssi;
	uint16_t	reserved;

	uint32_t	iv;
	uint32_t	eiv;
} __packed;

#define RAL_RF_LOBUSY	(1 << 15)
#define RAL_RF_BUSY	(1 << 31)
#define RAL_RF_20BIT	(20 << 24)

#define RAL_RF1	0
#define RAL_RF2	2
#define RAL_RF3	1
#define RAL_RF4	3

#define RAL_EEPROM_ADDRESS	0x0004
#define RAL_EEPROM_TXPOWER	0x003c
#define RAL_EEPROM_CONFIG0	0x0016
#define RAL_EEPROM_BBP_BASE	0x001c

--Boundary-00=_h13bC3MzttkCcJc
Content-Type: text/x-csrc;
  charset="us-ascii";
  name="if_ral.c"
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
	filename="if_ral.c"

/*	$NetBSD$ */
/*	$OpenBSD: if_ral.c,v 1.28 2005/04/17 13:41:51 damien Exp $  */

/*-
 * Copyright (c) 2005
 *	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.
 */

/*-
 * Ralink Technology RT2500USB chipset driver
 * http://www.ralinktech.com/
 *
 * Ported to NetBSD by Ian Zagorskih
 */

#ifdef __NetBSD__
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD$");
#endif

#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>
#ifdef __OpenBSD__
#include <sys/timeout.h>
#else
#include <sys/callout.h>
#endif
#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 <net/if_ether.h>

#ifdef INET
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <netinet/in_var.h>
#ifdef __OpenBSD__
#include <netinet/if_ether.h>
#endif
#include <netinet/ip.h>
#endif

#include <net80211/ieee80211_var.h>
#include <net80211/ieee80211_rssadapt.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_ralreg.h>
#include <dev/usb/if_ralvar.h>

#ifdef USB_DEBUG
#define URAL_DEBUG
#endif

#ifdef URAL_DEBUG
#define DPRINTF(x)	do { if (ural_debug) printf x; } while (0)
#define DPRINTFN(n, x)	do { if (ural_debug >= (n)) printf x; } while (0)
int ural_debug = 1;
#else
#define DPRINTF(x)
#define DPRINTFN(n, x)
#endif

#ifdef __NetBSD__
  #define M_DUP_PKTHDR		M_COPY_PKTHDR /* XXX */
  #define IEEE80211_F_WEPON	IEEE80211_F_PRIVACY
  #define letoh32 htole32
  #define letoh16 htole16
#endif

/* various supported device vendors/products */
static const struct usb_devno ural_devs[] = {
	{ USB_VENDOR_ASUS,		USB_PRODUCT_ASUS_RT2570 },
	{ USB_VENDOR_ASUS,		USB_PRODUCT_RALINK_RT2570 },
	{ USB_VENDOR_CISCOLINKSYS,	USB_PRODUCT_CISCOLINKSYS_WUSB54G },
	{ USB_VENDOR_CISCOLINKSYS,	USB_PRODUCT_CISCOLINKSYS_WUSB54GP },
	{ USB_VENDOR_CONCEPTRONIC2,	USB_PRODUCT_CONCEPTRONIC2_C54RU },
	{ USB_VENDOR_DLINK,		USB_PRODUCT_DLINK_RT2570 },
	{ USB_VENDOR_MELCO,		USB_PRODUCT_MELCO_KG54 },
	{ USB_VENDOR_RALINK,		USB_PRODUCT_RALINK_RT2570 },
	{ USB_VENDOR_RALINK,		USB_PRODUCT_RALINK_RT2570_2 },
	{ USB_VENDOR_SMC,		USB_PRODUCT_SMC_RT2570 }
};

Static int		ural_alloc_tx_list(struct ural_softc *);
Static void		ural_free_tx_list(struct ural_softc *);
Static int		ural_alloc_rx_list(struct ural_softc *);
Static void		ural_free_rx_list(struct ural_softc *);
Static int		ural_media_change(struct ifnet *);
Static void		ural_next_scan(void *);
Static void		ural_task(void *);
Static int		ural_newstate(struct ieee80211com *,
			    enum ieee80211_state, int);
Static void		ural_txeof(usbd_xfer_handle, usbd_private_handle,
			    usbd_status);
Static void		ural_rxeof(usbd_xfer_handle, usbd_private_handle,
			    usbd_status);
Static int		ural_ack_rate(int);
Static uint16_t		ural_txtime(int, int, uint32_t);
Static uint8_t		ural_plcp_signal(int);
Static void		ural_setup_tx_desc(struct ural_softc *,
			    struct ural_tx_desc *, uint32_t, int, int);
Static int		ural_tx_bcn(struct ural_softc *, struct mbuf *,
			    struct ieee80211_node *);
Static int		ural_tx_mgt(struct ural_softc *, struct mbuf *,
			    struct ieee80211_node *);
Static int		ural_tx_data(struct ural_softc *, struct mbuf *,
			    struct ieee80211_node *);
Static void		ural_start(struct ifnet *);
Static void		ural_watchdog(struct ifnet *);
Static int		ural_ioctl(struct ifnet *, u_long, caddr_t);
Static void		ural_eeprom_read(struct ural_softc *, uint16_t, void *,
			    int);
Static uint16_t		ural_read(struct ural_softc *, uint16_t);
Static void		ural_read_multi(struct ural_softc *, uint16_t, void *,
			    int);
Static void		ural_write(struct ural_softc *, uint16_t, uint16_t);
Static void		ural_write_multi(struct ural_softc *, uint16_t, void *,
			    int);
Static void		ural_bbp_write(struct ural_softc *, uint8_t, uint8_t);
Static uint8_t		ural_bbp_read(struct ural_softc *, uint8_t);
Static void		ural_rf_write(struct ural_softc *, uint8_t, uint32_t);
Static void		ural_set_chan(struct ural_softc *,
			    struct ieee80211_channel *);
Static void		ural_disable_rf_tune(struct ural_softc *);
Static void		ural_enable_tsf_sync(struct ural_softc *);
Static void		ural_set_bssid(struct ural_softc *, uint8_t *);
Static void		ural_set_macaddr(struct ural_softc *, uint8_t *);
Static void		ural_update_promisc(struct ural_softc *);
Static const char	*ural_get_rf(int);
Static void		ural_read_eeprom(struct ural_softc *);
Static int		ural_bbp_init(struct ural_softc *);
Static void		ural_set_txantenna(struct ural_softc *, int);
Static void		ural_set_rxantenna(struct ural_softc *, int);
Static int		ural_init(struct ifnet *);
Static void		ural_stop(struct ifnet *, int);

/*
 * Supported rates for 802.11a/b/g modes (in 500Kbps unit).
 */
static const struct ieee80211_rateset ural_rateset_11a =
	{ 8, { 12, 18, 24, 36, 48, 72, 96, 108 } };

static const struct ieee80211_rateset ural_rateset_11b =
	{ 4, { 2, 4, 11, 22 } };

static const struct ieee80211_rateset ural_rateset_11g =
	{ 12, { 2, 4, 11, 22, 12, 18, 24, 36, 48, 72, 96, 108 } };

/*
 * Default values for MAC registers; values taken from the reference driver.
 */
static const struct {
	uint16_t	reg;
	uint16_t	val;
} ural_def_mac[] = {
	{ RAL_TXRX_CSR5,  0x8c8d },
	{ RAL_TXRX_CSR6,  0x8b8a },
	{ RAL_TXRX_CSR7,  0x8687 },
	{ RAL_TXRX_CSR8,  0x0085 },
	{ RAL_MAC_CSR13,  0x1111 },
	{ RAL_MAC_CSR14,  0x1e11 },
	{ RAL_TXRX_CSR21, 0xe78f },
	{ RAL_MAC_CSR9,   0xff1d },
	{ RAL_MAC_CSR11,  0x0002 },
	{ RAL_MAC_CSR22,  0x0053 },
	{ RAL_MAC_CSR15,  0x0000 },
	{ RAL_MAC_CSR8,   0x0780 },
	{ RAL_TXRX_CSR19, 0x0000 },
	{ RAL_TXRX_CSR18, 0x005a },
	{ RAL_PHY_CSR2,   0x0000 },
	{ RAL_TXRX_CSR0,  0x1ec0 },
	{ RAL_PHY_CSR4,   0x000f }
};

/*
 * Default values for BBP registers; values taken from the reference driver.
 */
static const struct {
	uint8_t	reg;
	uint8_t	val;
} ural_def_bbp[] = {
	{  3, 0x02 },
	{  4, 0x19 },
	{ 14, 0x1c },
	{ 15, 0x30 },
	{ 16, 0xac },
	{ 17, 0x48 },
	{ 18, 0x18 },
	{ 19, 0xff },
	{ 20, 0x1e },
	{ 21, 0x08 },
	{ 22, 0x08 },
	{ 23, 0x08 },
	{ 24, 0x80 },
	{ 25, 0x50 },
	{ 26, 0x08 },
	{ 27, 0x23 },
	{ 30, 0x10 },
	{ 31, 0x2b },
	{ 32, 0xb9 },
	{ 34, 0x12 },
	{ 35, 0x50 },
	{ 39, 0xc4 },
	{ 40, 0x02 },
	{ 41, 0x60 },
	{ 53, 0x10 },
	{ 54, 0x18 },
	{ 56, 0x08 },
	{ 57, 0x10 },
	{ 58, 0x08 },
	{ 61, 0x60 },
	{ 62, 0x10 },
	{ 75, 0xff }
};

/*
 * Default values for RF register R2 indexed by channel numbers.
 */
static const uint32_t ural_rf2522_r2[] = {
	0x307f6, 0x307fb, 0x30800, 0x30805, 0x3080a, 0x3080f, 0x30814,
	0x30819, 0x3081e, 0x30823, 0x30828, 0x3082d, 0x30832, 0x3083e
};

static const uint32_t ural_rf2523_r2[] = {
	0x00327, 0x00328, 0x00329, 0x0032a, 0x0032b, 0x0032c, 0x0032d,
	0x0032e, 0x0032f, 0x00340, 0x00341, 0x00342, 0x00343, 0x00346
};

static const uint32_t ural_rf2524_r2[] = {
	0x00327, 0x00328, 0x00329, 0x0032a, 0x0032b, 0x0032c, 0x0032d,
	0x0032e, 0x0032f, 0x00340, 0x00341, 0x00342, 0x00343, 0x00346
};

static const uint32_t ural_rf2525_r2[] = {
	0x20327, 0x20328, 0x20329, 0x2032a, 0x2032b, 0x2032c, 0x2032d,
	0x2032e, 0x2032f, 0x20340, 0x20341, 0x20342, 0x20343, 0x20346
};

static const uint32_t ural_rf2525_hi_r2[] = {
	0x2032f, 0x20340, 0x20341, 0x20342, 0x20343, 0x20344, 0x20345,
	0x20346, 0x20347, 0x20348, 0x20349, 0x2034a, 0x2034b, 0x2034e
};

static const uint32_t ural_rf2525e_r2[] = {
	0x2044d, 0x2044e, 0x2044f, 0x20460, 0x20461, 0x20462, 0x20463,
	0x20464, 0x20465, 0x20466, 0x20467, 0x20468, 0x20469, 0x2046b
};

static const uint32_t ural_rf2526_hi_r2[] = {
	0x0022a, 0x0022b, 0x0022b, 0x0022c, 0x0022c, 0x0022d, 0x0022d,
	0x0022e, 0x0022e, 0x0022f, 0x0022d, 0x00240, 0x00240, 0x00241
};

static const uint32_t ural_rf2526_r2[] = {
	0x00226, 0x00227, 0x00227, 0x00228, 0x00228, 0x00229, 0x00229,
	0x0022a, 0x0022a, 0x0022b, 0x0022b, 0x0022c, 0x0022c, 0x0022d
};

/*
 * For dual-band RF, RF registers R1 and R4 also depend on channel number;
 * values taken from the reference driver.
 */
static const struct {
	uint8_t		chan;
	uint32_t	r1;
	uint32_t	r2;
	uint32_t	r4;
} ural_rf5222[] = {
	/* channels in the 2.4GHz band */
	{   1, 0x08808, 0x0044d, 0x00282 },
	{   2, 0x08808, 0x0044e, 0x00282 },
	{   3, 0x08808, 0x0044f, 0x00282 },
	{   4, 0x08808, 0x00460, 0x00282 },
	{   5, 0x08808, 0x00461, 0x00282 },
	{   6, 0x08808, 0x00462, 0x00282 },
	{   7, 0x08808, 0x00463, 0x00282 },
	{   8, 0x08808, 0x00464, 0x00282 },
	{   9, 0x08808, 0x00465, 0x00282 },
	{  10, 0x08808, 0x00466, 0x00282 },
	{  11, 0x08808, 0x00467, 0x00282 },
	{  12, 0x08808, 0x00468, 0x00282 },
	{  13, 0x08808, 0x00469, 0x00282 },
	{  14, 0x08808, 0x0046b, 0x00286 },

	/* channels in the 5.2GHz band */
	{  36, 0x08804, 0x06225, 0x00287 },
	{  40, 0x08804, 0x06226, 0x00287 },
	{  44, 0x08804, 0x06227, 0x00287 },
	{  48, 0x08804, 0x06228, 0x00287 },
	{  52, 0x08804, 0x06229, 0x00287 },
	{  56, 0x08804, 0x0622a, 0x00287 },
	{  60, 0x08804, 0x0622b, 0x00287 },
	{  64, 0x08804, 0x0622c, 0x00287 },

	{ 100, 0x08804, 0x02200, 0x00283 },
	{ 104, 0x08804, 0x02201, 0x00283 },
	{ 108, 0x08804, 0x02202, 0x00283 },
	{ 112, 0x08804, 0x02203, 0x00283 },
	{ 116, 0x08804, 0x02204, 0x00283 },
	{ 120, 0x08804, 0x02205, 0x00283 },
	{ 124, 0x08804, 0x02206, 0x00283 },
	{ 128, 0x08804, 0x02207, 0x00283 },
	{ 132, 0x08804, 0x02208, 0x00283 },
	{ 136, 0x08804, 0x02209, 0x00283 },
	{ 140, 0x08804, 0x0220a, 0x00283 },

	{ 149, 0x08808, 0x02429, 0x00281 },
	{ 153, 0x08808, 0x0242b, 0x00281 },
	{ 157, 0x08808, 0x0242d, 0x00281 },
	{ 161, 0x08808, 0x0242f, 0x00281 }
};

USB_DECLARE_DRIVER(ural);

USB_MATCH(ural)
{
	USB_MATCH_START(ural, uaa);

	if (uaa->iface != NULL)
		return UMATCH_NONE;

	return (usb_lookup(ural_devs, uaa->vendor, uaa->product) != NULL) ?
	    UMATCH_VENDOR_PRODUCT : UMATCH_NONE;
}

USB_ATTACH(ural)
{
	USB_ATTACH_START(ural, sc, uaa);
	struct ieee80211com *ic = &sc->sc_ic;
	struct ifnet *ifp = &ic->ic_if;
	usb_interface_descriptor_t *id;
	usb_endpoint_descriptor_t *ed;
	usbd_status error;
	char devinfo[1024];
	int i;

	sc->sc_udev = uaa->device;

	usbd_devinfo(sc->sc_udev, 0, devinfo, sizeof devinfo);
	USB_ATTACH_SETUP;
	printf("%s: %s\n", USBDEVNAME(sc->sc_dev), devinfo);

	if (usbd_set_config_no(sc->sc_udev, RAL_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, RAL_IFACE_INDEX,
	    &sc->sc_iface);
	if (error != 0) {
		printf("%s: could not get interface handle\n",
		    USBDEVNAME(sc->sc_dev));
		USB_ATTACH_ERROR_RETURN;
	}

	/*
	 * Find endpoints.
	 */
	id = usbd_get_interface_descriptor(sc->sc_iface);

	for (i = 0; i < id->bNumEndpoints; i++) {
		ed = usbd_interface2endpoint_descriptor(sc->sc_iface, i);
		if (ed == NULL) {
			printf("%s: no endpoint descriptor for %d\n",
			    USBDEVNAME(sc->sc_dev), i);
			USB_ATTACH_ERROR_RETURN;
		}

		if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
		    UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK)
			sc->sc_rx_no = ed->bEndpointAddress;
		else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT &&
		    UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK)
			sc->sc_tx_no = ed->bEndpointAddress;
	}

	usb_init_task(&sc->sc_task, ural_task, sc);
#ifdef __OpenBSD__
	timeout_set(&sc->scan_ch, ural_next_scan, sc);
#else
	callout_init(&sc->scan_ch);
	callout_setfunc(&sc->scan_ch, ural_next_scan, sc);
#endif

	/* retrieve RT2570 rev. no */
	sc->asic_rev = ural_read(sc, RAL_MAC_CSR0);

	/* retrieve MAC address and various other things from EEPROM */
	ural_read_eeprom(sc);

	printf("%s: MAC/BBP RT2570 (rev 0x%02x), RF %s, address %s\n",
	    USBDEVNAME(sc->sc_dev), sc->asic_rev, ural_get_rf(sc->rf_rev),
	    ether_sprintf(ic->ic_myaddr));

	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_MONITOR | IEEE80211_C_IBSS |
	    IEEE80211_C_HOSTAP | IEEE80211_C_SHPREAMBLE | IEEE80211_C_PMGT |
	    IEEE80211_C_TXPMGT | IEEE80211_C_WEP;

	if (sc->rf_rev == RAL_RF_5222) {
		/* set supported .11a rates */
		ic->ic_sup_rates[IEEE80211_MODE_11A] = ural_rateset_11a;

		/* set supported .11a channels */
		for (i = 36; i <= 64; i += 4) {
			ic->ic_channels[i].ic_freq =
			    ieee80211_ieee2mhz(i, IEEE80211_CHAN_5GHZ);
			ic->ic_channels[i].ic_flags = IEEE80211_CHAN_A;
		}
		for (i = 100; i <= 140; i += 4) {
			ic->ic_channels[i].ic_freq =
			    ieee80211_ieee2mhz(i, IEEE80211_CHAN_5GHZ);
			ic->ic_channels[i].ic_flags = IEEE80211_CHAN_A;
		}
		for (i = 149; i <= 161; i += 4) {
			ic->ic_channels[i].ic_freq =
			    ieee80211_ieee2mhz(i, IEEE80211_CHAN_5GHZ);
			ic->ic_channels[i].ic_flags = IEEE80211_CHAN_A;
		}
	}

	/* set supported .11b and .11g rates */
	ic->ic_sup_rates[IEEE80211_MODE_11B] = ural_rateset_11b;
	ic->ic_sup_rates[IEEE80211_MODE_11G] = ural_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 = ural_init;
	ifp->if_stop = ural_stop;
	ifp->if_start = ural_start;
	ifp->if_ioctl = ural_ioctl;
	ifp->if_watchdog = ural_watchdog;
	IFQ_SET_READY(&ifp->if_snd);
	memcpy(ifp->if_xname, USBDEVNAME(sc->sc_dev), IFNAMSIZ);

	/* Call MI attach routine */
	if_attach(ifp);
	ieee80211_ifattach(ifp);

	/* override state transition machine */
	sc->sc_newstate = ic->ic_newstate;
	ic->ic_newstate = ural_newstate;

	/* Setup ifmedia interface */
	ieee80211_media_init(ifp, ural_media_change, ieee80211_media_status);

#if NBPFILTER > 0
#ifdef __OpenBSD__
	bpfattach(&sc->sc_drvbpf, ifp, DLT_IEEE802_11_RADIO,
	    sizeof (struct ieee80211_frame) + 64);
#else
	bpfattach(ifp, DLT_IEEE802_11_RADIO,
		sizeof (struct ieee80211_frame) + 64);
#endif
#endif

	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(RAL_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(RAL_TX_RADIOTAP_PRESENT);

	usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH, sc->sc_udev,
	    USBDEV(sc->sc_dev));

	USB_ATTACH_SUCCESS_RETURN;
}

USB_DETACH(ural)
{
	USB_DETACH_START(ural, sc);
	struct ifnet *ifp = &sc->sc_ic.ic_if;
	int s;

	s = splusb();

	usb_rem_task(sc->sc_udev, &sc->sc_task);
#ifdef __OpenBSD__
	timeout_del(&sc->scan_ch);
#else
	callout_stop(&sc->scan_ch);
#endif

	if (sc->sc_rx_pipeh != NULL) {
		usbd_abort_pipe(sc->sc_rx_pipeh);
		usbd_close_pipe(sc->sc_rx_pipeh);
	}

	if (sc->sc_tx_pipeh != NULL) {
		usbd_abort_pipe(sc->sc_tx_pipeh);
		usbd_close_pipe(sc->sc_tx_pipeh);
	}

	ural_free_rx_list(sc);
	ural_free_tx_list(sc);

#if NBPFILTER > 0
	bpfdetach(ifp);
#endif
	ieee80211_ifdetach(ifp);
	if_detach(ifp);

	splx(s);

	usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, sc->sc_udev,
	    USBDEV(sc->sc_dev));

	return 0;
}

Static int
ural_alloc_tx_list(struct ural_softc *sc)
{
	struct ural_tx_data *data;
	int i, error;

	sc->tx_queued = 0;

	for (i = 0; i < RAL_TX_LIST_COUNT; i++) {
		data = &sc->tx_data[i];

		data->sc = sc;

		data->xfer = usbd_alloc_xfer(sc->sc_udev);
		if (data->xfer == NULL) {
			printf("%s: could not allocate tx xfer\n",
			    USBDEVNAME(sc->sc_dev));
			error = ENOMEM;
			goto fail;
		}

		data->buf = usbd_alloc_buffer(data->xfer,
		    RAL_TX_DESC_SIZE + MCLBYTES);
		if (data->buf == NULL) {
			printf("%s: could not allocate tx buffer\n",
			    USBDEVNAME(sc->sc_dev));
			error = ENOMEM;
			goto fail;
		}
	}

	return 0;

fail:	ural_free_tx_list(sc);
	return error;
}

Static void
ural_free_tx_list(struct ural_softc *sc)
{
	struct ieee80211com *ic = &sc->sc_ic;
	struct ural_tx_data *data;
	int i;

	for (i = 0; i < RAL_TX_LIST_COUNT; i++) {
		data = &sc->tx_data[i];

		if (data->xfer != NULL) {
			usbd_free_xfer(data->xfer);
			data->xfer = NULL;
		}

		if (data->ni != NULL) {
			ieee80211_release_node(ic, data->ni);
			data->ni = NULL;
		}
	}
}

Static int
ural_alloc_rx_list(struct ural_softc *sc)
{
	struct ural_rx_data *data;
	int i, error;

	for (i = 0; i < RAL_RX_LIST_COUNT; i++) {
		data = &sc->rx_data[i];

		data->sc = sc;

		data->xfer = usbd_alloc_xfer(sc->sc_udev);
		if (data->xfer == NULL) {
			printf("%s: could not allocate rx xfer\n",
			    USBDEVNAME(sc->sc_dev));
			error = ENOMEM;
			goto fail;
		}

		if (usbd_alloc_buffer(data->xfer, MCLBYTES) == NULL) {
			printf("%s: could not allocate rx 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:	ural_free_tx_list(sc);
	return error;
}

Static void
ural_free_rx_list(struct ural_softc *sc)
{
	struct ural_rx_data *data;
	int i;

	for (i = 0; i < RAL_RX_LIST_COUNT; i++) {
		data = &sc->rx_data[i];

		if (data->xfer != NULL) {
			usbd_free_xfer(data->xfer);
			data->xfer = NULL;
		}

		if (data->m != NULL) {
			m_freem(data->m);
			data->m = NULL;
		}
	}
}

Static int
ural_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))
		ural_init(ifp);

	return 0;
}

/*
 * This function is called periodically (every 200ms) during scanning to
 * switch from one channel to another.
 */
Static void
ural_next_scan(void *arg)
{
	struct ural_softc *sc = arg;
	struct ieee80211com *ic = &sc->sc_ic;

	if (ic->ic_state == IEEE80211_S_SCAN)
		ieee80211_next_scan(ic);
}

Static void
ural_task(void *arg)
{
	struct ural_softc *sc = arg;
	struct ieee80211com *ic = &sc->sc_ic;
	enum ieee80211_state ostate;
	struct mbuf *m;

	ostate = ic->ic_state;

	switch (sc->sc_state) {
	case IEEE80211_S_INIT:
		if (ostate == IEEE80211_S_RUN) {
			/* abort TSF synchronization */
			ural_write(sc, RAL_TXRX_CSR19, 0);

			/* force tx led to stop blinking */
			ural_write(sc, RAL_MAC_CSR20, 0);
		}
		break;

	case IEEE80211_S_SCAN:
		ural_set_chan(sc, ic->ic_bss->ni_chan);
#ifdef __OpenBSD__
		timeout_add(&sc->scan_ch, hz / 5);
#else
		callout_schedule(&sc->scan_ch, hz / 5);
#endif
		break;

	case IEEE80211_S_AUTH:
		ural_set_chan(sc, ic->ic_bss->ni_chan);
		break;

	case IEEE80211_S_ASSOC:
		ural_set_chan(sc, ic->ic_bss->ni_chan);
		break;

	case IEEE80211_S_RUN:
		ural_set_chan(sc, ic->ic_bss->ni_chan);

		if (ic->ic_opmode != IEEE80211_M_MONITOR)
			ural_set_bssid(sc, ic->ic_bss->ni_bssid);

		if (ic->ic_opmode == IEEE80211_M_HOSTAP ||
		    ic->ic_opmode == IEEE80211_M_IBSS) {
			m = ieee80211_beacon_alloc(ic, ic->ic_bss);
			if (m == NULL) {
				printf("%s: could not allocate beacon\n",
				    USBDEVNAME(sc->sc_dev));
				return;
			}

			if (ural_tx_bcn(sc, m, ic->ic_bss) != 0) {
				m_freem(m);
				printf("%s: could not transmit beacon\n",
				    USBDEVNAME(sc->sc_dev));
				return;
			}

			/* beacon is no longer needed */
			m_freem(m);
		}

		/* make tx led blink on tx (controlled by ASIC) */
		ural_write(sc, RAL_MAC_CSR20, 1);

		if (ic->ic_opmode != IEEE80211_M_MONITOR)
			ural_enable_tsf_sync(sc);
		break;
	}

	sc->sc_newstate(ic, sc->sc_state, -1);
}

Static int
ural_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg)
{
	struct ural_softc *sc = ic->ic_if.if_softc;

	usb_rem_task(sc->sc_udev, &sc->sc_task);
#ifdef __OpenBSD__
	timeout_del(&sc->scan_ch);
#else
	callout_stop(&sc->scan_ch);
#endif

	/* do it in a process context */
	sc->sc_state = nstate;
	usb_add_task(sc->sc_udev, &sc->sc_task);

	return 0;
}

/* quickly determine if a given rate is CCK or OFDM */
#define RAL_RATE_IS_OFDM(rate) ((rate) >= 12 && (rate) != 22)

#define RAL_ACK_SIZE	14	/* 10 + 4(FCS) */
#define RAL_CTS_SIZE	14	/* 10 + 4(FCS) */
#define RAL_SIFS	10

Static void
ural_txeof(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status)
{
	struct ural_tx_data *data = priv;
	struct ural_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(sc->sc_tx_pipeh);

		ifp->if_oerrors++;
		return;
	}

	s = splnet();

	m_freem(data->m);
	data->m = NULL;
	ieee80211_release_node(ic, data->ni);
	data->ni = NULL;

	sc->tx_queued--;
	ifp->if_opackets++;

	DPRINTFN(10, ("tx done\n"));

	sc->sc_tx_timer = 0;
	ifp->if_flags &= ~IFF_OACTIVE;
	ural_start(ifp);

	splx(s);
}

Static void
ural_rxeof(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status)
{
	struct ural_rx_data *data = priv;
	struct ural_softc *sc = data->sc;
	struct ieee80211com *ic = &sc->sc_ic;
	struct ifnet *ifp = &ic->ic_if;
	struct ural_rx_desc *desc;
	struct ieee80211_frame *wh;
	struct ieee80211_node *ni;
	struct mbuf *m;
	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(sc->sc_rx_pipeh);
		goto skip;
	}

	usbd_get_xfer_status(xfer, NULL, NULL, &len, NULL);

	if (len < RAL_RX_DESC_SIZE) {
		printf("%s: xfer too short %d\n", USBDEVNAME(sc->sc_dev), len);
		ifp->if_ierrors++;
		goto skip;
	}

	/* rx descriptor is located at the end */
	desc = (struct ural_rx_desc *)(data->buf + len - RAL_RX_DESC_SIZE);

	if ((letoh32(desc->flags) & RAL_RX_PHY_ERROR) ||
	    (letoh32(desc->flags) & RAL_RX_CRC_ERROR)) {
		/*
		 * This should not happen since we did not request to receive
		 * those frames when we filled RAL_TXRX_CSR2.
		 */
		DPRINTFN(5, ("PHY or CRC error\n"));
		ifp->if_ierrors++;
		goto skip;
	}

	/* finalize mbuf */
	m = data->m;
	m->m_pkthdr.rcvif = ifp;
	m->m_pkthdr.len = m->m_len = (letoh32(desc->flags) >> 16) & 0xfff;
	m->m_flags |= M_HASFCS; /* hardware appends FCS */

	s = splnet();

#if NBPFILTER > 0
	if (sc->sc_drvbpf != NULL) {
		struct mbuf mb;
		struct ural_rx_radiotap_header *tap = &sc->sc_rxtap;

		tap->wr_flags = 0;
		tap->wr_chan_freq = htole16(ic->ic_ibss_chan->ic_freq);
		tap->wr_chan_flags = htole16(ic->ic_ibss_chan->ic_flags);
		tap->wr_antenna = sc->rx_ant;
		tap->wr_antsignal = desc->rssi;

		M_DUP_PKTHDR(&mb, m);
		mb.m_data = (caddr_t)tap;
		mb.m_len = sc->sc_txtap_len;
		mb.m_next = m;
		mb.m_pkthdr.len += mb.m_len;
		bpf_mtap(sc->sc_drvbpf, &mb);
	}
#endif

	wh = mtod(m, struct ieee80211_frame *);
	ni = ieee80211_find_rxnode(ic, wh);

	/* send the frame to the 802.11 layer */
	ieee80211_input(ifp, m, ni, desc->rssi, 0);

	/* node is no longer needed */
	ieee80211_release_node(ic, ni);

	splx(s);

	MGETHDR(data->m, M_DONTWAIT, MT_DATA);
	if (data->m == NULL) {
		printf("%s: could not allocate rx mbuf\n",
		    USBDEVNAME(sc->sc_dev));
		return;
	}

	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));
		m_freem(data->m);
		data->m = NULL;
		return;
	}

	data->buf = mtod(data->m, uint8_t *);

	DPRINTFN(15, ("rx done\n"));

skip:	/* setup a new transfer */
	usbd_setup_xfer(xfer, sc->sc_rx_pipeh, data, data->buf, MCLBYTES,
	    USBD_SHORT_XFER_OK, USBD_NO_TIMEOUT, ural_rxeof);
	usbd_transfer(xfer);
}

/*
 * Return the expected ack rate for a frame transmitted at rate `rate'.
 * XXX: this should depend on the destination node basic rate set.
 */
Static int
ural_ack_rate(int rate)
{
	switch (rate) {
	/* CCK rates */
	case 2:
		return 2;
	case 4:
	case 11:
	case 22:
		return 4;

	/* OFDM rates */
	case 12:
	case 18:
		return 12;
	case 24:
	case 36:
		return 24;
	case 48:
	case 72:
	case 96:
	case 108:
		return 48;
	}

	/* default to 1Mbps */
	return 2;
}

/*
 * Compute the duration (in us) needed to transmit `len' bytes at rate `rate'.
 * The function automatically determines the operating mode depending on the
 * given rate. `flags' indicates whether short preamble is in use or not.
 */
Static uint16_t
ural_txtime(int len, int rate, uint32_t flags)
{
	uint16_t txtime;
	int ceil, dbps;

	if (RAL_RATE_IS_OFDM(rate)) {
		/*
		 * OFDM TXTIME calculation.
		 * From IEEE Std 802.11a-1999, pp. 37.
		 */
		dbps = rate * 2; /* data bits per OFDM symbol */

		ceil = (16 + 8 * len + 6) / dbps;
		if ((16 + 8 * len + 6) % dbps != 0)
			ceil++;

		txtime = 16 + 4 + 4 * ceil + 6;
	} else {
		/*
		 * High Rate TXTIME calculation.
		 * From IEEE Std 802.11b-1999, pp. 28.
		 */
		ceil = (8 * len * 2) / rate;
		if ((8 * len * 2) % rate != 0)
			ceil++;

		if (rate != 2 && (flags & IEEE80211_F_SHPREAMBLE))
			txtime =  72 + 24 + ceil;
		else
			txtime = 144 + 48 + ceil;
	}

	return txtime;
}

Static uint8_t
ural_plcp_signal(int rate)
{
	switch (rate) {
	/* CCK rates (returned values are device-dependent) */
	case 2:		return 0x0;
	case 4:		return 0x1;
	case 11:	return 0x2;
	case 22:	return 0x3;

	/* OFDM rates (cf IEEE Std 802.11a-1999, pp. 14 Table 80) */
	case 12:	return 0xb;
	case 18:	return 0xf;
	case 24:	return 0xa;
	case 36:	return 0xe;
	case 48:	return 0x9;
	case 72:	return 0xd;
	case 96:	return 0x8;
	case 108:	return 0xc;

	/* unsupported rates (should not get there) */
	default:	return 0xff;
	}
}

Static void
ural_setup_tx_desc(struct ural_softc *sc, struct ural_tx_desc *desc,
    uint32_t flags, int len, int rate)
{
	struct ieee80211com *ic = &sc->sc_ic;
	uint16_t plcp_length;
	int remainder;

	desc->flags = htole32(flags);
	desc->flags |= htole32(RAL_TX_NEWSEQ);
	desc->flags |= htole32(len << 16);

	if (RAL_RATE_IS_OFDM(rate))
		desc->flags |= htole32(RAL_TX_OFDM);

	desc->wme = htole16(RAL_LOGCWMAX(5) | RAL_LOGCWMIN(3) | RAL_AIFSN(2));

	/*
	 * Fill PLCP fields.
	 */
	desc->plcp_service = 4;

	len += 4; /* account for FCS */
	if (RAL_RATE_IS_OFDM(rate)) {
		/*
		 * PLCP length field (LENGTH).
		 * From IEEE Std 802.11a-1999, pp. 14.
		 */
		plcp_length = len & 0xfff;
		desc->plcp_length = htole16((plcp_length >> 6) << 8 |
		    (plcp_length & 0x3f));
	} else {
		/*
		 * Long PLCP LENGTH field.
		 * From IEEE Std 802.11b-1999, pp. 16.
		 */
		plcp_length = (8 * len * 2) / rate;
		remainder = (8 * len * 2) % rate;
		if (remainder != 0) {
			if (rate == 22 && (rate - remainder) / 16 != 0)
				desc->plcp_service |= RAL_PLCP_LENGEXT;
			plcp_length++;
		}
		desc->plcp_length = htole16(plcp_length);
	}

	desc->plcp_signal = ural_plcp_signal(rate);
	if (rate != 2 && (ic->ic_flags & IEEE80211_F_SHPREAMBLE))
		desc->plcp_signal |= 0x08;

	desc->iv = 0;
	desc->eiv = 0;
}

#define RAL_TX_TIMEOUT	5000

Static int
ural_tx_bcn(struct ural_softc *sc, struct mbuf *m0, struct ieee80211_node *ni)
{
	struct ural_tx_desc *desc;
	usbd_xfer_handle xfer;
	usbd_status error;
	uint8_t cmd = 0;
	uint8_t *buf;
	int xferlen, rate;

	rate = IEEE80211_IS_CHAN_5GHZ(ni->ni_chan) ? 12 : 4;

	xfer = usbd_alloc_xfer(sc->sc_udev);
	if (xfer == NULL)
		return ENOMEM;

	/* xfer length needs to be a multiple of two! */
	xferlen = (RAL_TX_DESC_SIZE + m0->m_pkthdr.len + 1) & ~1;

	buf = usbd_alloc_buffer(xfer, xferlen);
	if (buf == NULL) {
		usbd_free_xfer(xfer);
		return ENOMEM;
	}

	usbd_setup_xfer(xfer, sc->sc_tx_pipeh, NULL, &cmd, sizeof cmd,
	    USBD_FORCE_SHORT_XFER, RAL_TX_TIMEOUT, NULL);

	error = usbd_sync_transfer(xfer);
	if (error != 0) {
		usbd_free_xfer(xfer);
		return error;
	}

	desc = (struct ural_tx_desc *)buf;

	m_copydata(m0, 0, m0->m_pkthdr.len, buf + RAL_TX_DESC_SIZE);
	ural_setup_tx_desc(sc, desc, RAL_TX_IFS_NEWBACKOFF | RAL_TX_TIMESTAMP,
	    m0->m_pkthdr.len, rate);

	DPRINTFN(10, ("sending beacon frame len=%u rate=%u xfer len=%u\n",
	    m0->m_pkthdr.len, rate, xferlen));

	usbd_setup_xfer(xfer, sc->sc_tx_pipeh, NULL, buf, xferlen,
	    USBD_FORCE_SHORT_XFER | USBD_NO_COPY, RAL_TX_TIMEOUT, NULL);

	error = usbd_sync_transfer(xfer);
	usbd_free_xfer(xfer);

	return error;
}

Static int
ural_tx_mgt(struct ural_softc *sc, struct mbuf *m0, struct ieee80211_node *ni)
{
	struct ieee80211com *ic = &sc->sc_ic;
	struct ural_tx_desc *desc;
	struct ural_tx_data *data;
	struct ieee80211_frame *wh;
	uint32_t flags = 0;
	uint16_t dur;
	usbd_status error;
	int xferlen, rate;

	data = &sc->tx_data[0];
	desc = (struct ural_tx_desc *)data->buf;

	rate = IEEE80211_IS_CHAN_5GHZ(ni->ni_chan) ? 12 : 4;

#if NBPFILTER > 0
	if (sc->sc_drvbpf != NULL) {
		struct mbuf mb;
		struct ural_tx_radiotap_header *tap = &sc->sc_txtap;

		tap->wt_flags = 0;
		tap->wt_rate = rate;
		tap->wt_chan_freq = htole16(ic->ic_ibss_chan->ic_freq);
		tap->wt_chan_flags = htole16(ic->ic_ibss_chan->ic_flags);
		tap->wt_antenna = sc->tx_ant;

		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);
	}
#endif

	data->m = m0;
	data->ni = ni;

	wh = mtod(m0, struct ieee80211_frame *);

	if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) {
		flags |= RAL_TX_ACK;

		dur = ural_txtime(RAL_ACK_SIZE, rate, ic->ic_flags) + RAL_SIFS;
		*(uint16_t *)wh->i_dur = htole16(dur);

		/* tell hardware to add timestamp for probe responses */
		if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) ==
		    IEEE80211_FC0_TYPE_MGT &&
		    (wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) ==
		    IEEE80211_FC0_SUBTYPE_PROBE_RESP)
			flags |= RAL_TX_TIMESTAMP;
	}

	m_copydata(m0, 0, m0->m_pkthdr.len, data->buf + RAL_TX_DESC_SIZE);
	ural_setup_tx_desc(sc, desc, flags, m0->m_pkthdr.len, rate);

	/* xfer length needs to be a multiple of two! */
	xferlen = (RAL_TX_DESC_SIZE + m0->m_pkthdr.len + 1) & ~1;

	DPRINTFN(10, ("sending mgt frame len=%u rate=%u xfer len=%u\n",
	    m0->m_pkthdr.len, rate, xferlen));

	usbd_setup_xfer(data->xfer, sc->sc_tx_pipeh, data, data->buf, xferlen,
	    USBD_FORCE_SHORT_XFER | USBD_NO_COPY, RAL_TX_TIMEOUT, ural_txeof);

	error = usbd_transfer(data->xfer);
	if (error != USBD_NORMAL_COMPLETION && error != USBD_IN_PROGRESS) {
		m_freem(m0);
		return error;
	}

	sc->tx_queued++;

	return 0;
}

Static int
ural_tx_data(struct ural_softc *sc, struct mbuf *m0, struct ieee80211_node *ni)
{
	struct ieee80211com *ic = &sc->sc_ic;
	struct ifnet *ifp = &ic->ic_if;
	struct ieee80211_rateset *rs;
	struct ural_tx_desc *desc;
	struct ural_tx_data *data;
	struct ieee80211_frame *wh;
	uint32_t flags = 0;
	uint16_t dur;
	usbd_status error;
	int xferlen, rate;

	if (ic->ic_fixed_rate != -1) {
		rs = &ic->ic_sup_rates[ic->ic_curmode];
		rate = rs->rs_rates[ic->ic_fixed_rate];
	} else {
		rs = &ni->ni_rates;
		rate = rs->rs_rates[ni->ni_txrate];
	}
	rate &= IEEE80211_RATE_VAL;

	if (ic->ic_flags & IEEE80211_F_WEPON) {
		m0 = ieee80211_wep_crypt(ifp, m0, 1);
		if (m0 == NULL)
			return ENOBUFS;
	}

#if NBPFILTER > 0
	if (sc->sc_drvbpf != NULL) {
		struct mbuf mb;
		struct ural_tx_radiotap_header *tap = &sc->sc_txtap;

		tap->wt_flags = 0;
		tap->wt_rate = rate;
		tap->wt_chan_freq = htole16(ic->ic_ibss_chan->ic_freq);
		tap->wt_chan_flags = htole16(ic->ic_ibss_chan->ic_flags);
		tap->wt_antenna = sc->tx_ant;

		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);
	}
#endif

	data = &sc->tx_data[0];
	desc = (struct ural_tx_desc *)data->buf;

	data->m = m0;
	data->ni = ni;

	wh = mtod(m0, struct ieee80211_frame *);

	if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) {
		flags |= RAL_TX_ACK;
		flags |= RAL_TX_RETRY(7);

		dur = ural_txtime(RAL_ACK_SIZE, ural_ack_rate(rate),
		    ic->ic_flags) + RAL_SIFS;
		*(uint16_t *)wh->i_dur = htole16(dur);
	}

	m_copydata(m0, 0, m0->m_pkthdr.len, data->buf + RAL_TX_DESC_SIZE);
	ural_setup_tx_desc(sc, desc, flags, m0->m_pkthdr.len, rate);

	/* xfer length needs to be a multiple of two! */
	xferlen = (RAL_TX_DESC_SIZE + m0->m_pkthdr.len + 1) & ~1;

	DPRINTFN(10, ("sending data frame len=%u rate=%u xfer len=%u\n",
	    m0->m_pkthdr.len, rate, xferlen));

	usbd_setup_xfer(data->xfer, sc->sc_tx_pipeh, data, data->buf, xferlen,
	    USBD_FORCE_SHORT_XFER | USBD_NO_COPY, RAL_TX_TIMEOUT, ural_txeof);

	error = usbd_transfer(data->xfer);
	if (error != USBD_NORMAL_COMPLETION && error != USBD_IN_PROGRESS) {
		m_freem(m0);
		return error;
	}

	sc->tx_queued++;

	return 0;
}

Static void
ural_start(struct ifnet *ifp)
{
	struct ural_softc *sc = ifp->if_softc;
	struct ieee80211com *ic = &sc->sc_ic;
	struct ieee80211_node *ni;
	struct mbuf *m0;

	for (;;) {
		IF_POLL(&ic->ic_mgtq, m0);
		if (m0 != NULL) {
			if (sc->tx_queued >= RAL_TX_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);
#endif
			if (ural_tx_mgt(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 >= RAL_TX_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);
#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);
#endif
			if (ural_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
ural_watchdog(struct ifnet *ifp)
{
	struct ural_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));
			/*ural_init(ifp); XXX needs a process context! */
			ifp->if_oerrors++;
			return;
		}
		ifp->if_timer = 1;
	}

	ieee80211_watchdog(ifp);
}

Static int
ural_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
{
	struct ural_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)
#ifdef __NetBSD__
			arp_ifinit(ifp, ifa);
#else
			arp_ifinit(&ic->ic_ac, ifa);
#endif
#endif
		/* FALLTHROUGH */
	case SIOCSIFFLAGS:
		if (ifp->if_flags & IFF_UP) {
			if (ifp->if_flags & IFF_RUNNING)
				ural_update_promisc(sc);
			else
				ural_init(ifp);
		} else {
			if (ifp->if_flags & IFF_RUNNING)
				ural_stop(ifp, 1);
		}
		break;

	case SIOCADDMULTI:
	case SIOCDELMULTI:
		ifr = (struct ifreq *)data;
#ifdef __OpenBSD__
		error = (cmd == SIOCADDMULTI) ?
		    ether_addmulti(ifr, &ic->ic_ac) :
		    ether_delmulti(ifr, &ic->ic_ac);
#else
		error = (cmd == SIOCADDMULTI) ?
		    ether_addmulti(ifr, &ic->ic_ec) :
		    ether_delmulti(ifr, &ic->ic_ec);
#endif
		if (error == ENETRESET)
			error = 0;
		break;

	case SIOCS80211CHANNEL:
		/*
		 * This allows for fast channel switching in monitor mode
		 * (used by kismet). In IBSS mode, we must explicitly reset
		 * the interface to generate a new beacon frame.
		 */
		error = ieee80211_ioctl(ifp, cmd, data);
		if (error == ENETRESET &&
		    ic->ic_opmode == IEEE80211_M_MONITOR) {
			ural_set_chan(sc, ic->ic_ibss_chan);
			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))
			ural_init(ifp);
		error = 0;
	}

	splx(s);

	return error;
}

Static void
ural_eeprom_read(struct ural_softc *sc, uint16_t addr, void *buf, int len)
{
	usb_device_request_t req;
	usbd_status error;

	req.bmRequestType = UT_READ_VENDOR_DEVICE;
	req.bRequest = RAL_READ_EEPROM;
	USETW(req.wValue, 0);
	USETW(req.wIndex, addr);
	USETW(req.wLength, len);

	error = usbd_do_request(sc->sc_udev, &req, buf);
	if (error != 0) {
		printf("%s: could not read EEPROM: %s\n",
		    USBDEVNAME(sc->sc_dev), usbd_errstr(error));
	}
}

Static uint16_t
ural_read(struct ural_softc *sc, uint16_t reg)
{
	usb_device_request_t req;
	usbd_status error;
	uint16_t val;

	req.bmRequestType = UT_READ_VENDOR_DEVICE;
	req.bRequest = RAL_READ_MAC;
	USETW(req.wValue, 0);
	USETW(req.wIndex, reg);
	USETW(req.wLength, sizeof (uint16_t));

	error = usbd_do_request(sc->sc_udev, &req, &val);
	if (error != 0) {
		printf("%s: could not read MAC register: %s\n",
		    USBDEVNAME(sc->sc_dev), usbd_errstr(error));
		return 0;
	}

	return le16toh(val);
}

Static void
ural_read_multi(struct ural_softc *sc, uint16_t reg, void *buf, int len)
{
	usb_device_request_t req;
	usbd_status error;

	req.bmRequestType = UT_READ_VENDOR_DEVICE;
	req.bRequest = RAL_READ_MULTI_MAC;
	USETW(req.wValue, 0);
	USETW(req.wIndex, reg);
	USETW(req.wLength, len);

	error = usbd_do_request(sc->sc_udev, &req, buf);
	if (error != 0) {
		printf("%s: could not read MAC register: %s\n",
		    USBDEVNAME(sc->sc_dev), usbd_errstr(error));
		return;
	}
}

Static void
ural_write(struct ural_softc *sc, uint16_t reg, uint16_t val)
{
	usb_device_request_t req;
	usbd_status error;

	req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
	req.bRequest = RAL_WRITE_MAC;
	USETW(req.wValue, val);
	USETW(req.wIndex, reg);
	USETW(req.wLength, 0);

	error = usbd_do_request(sc->sc_udev, &req, NULL);
	if (error != 0) {
		printf("%s: could not write MAC register: %s\n",
		    USBDEVNAME(sc->sc_dev), usbd_errstr(error));
	}
}

Static void
ural_write_multi(struct ural_softc *sc, uint16_t reg, void *buf, int len)
{
	usb_device_request_t req;
	usbd_status error;

	req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
	req.bRequest = RAL_WRITE_MULTI_MAC;
	USETW(req.wValue, 0);
	USETW(req.wIndex, reg);
	USETW(req.wLength, len);

	error = usbd_do_request(sc->sc_udev, &req, buf);
	if (error != 0) {
		printf("%s: could not write MAC register: %s\n",
		    USBDEVNAME(sc->sc_dev), usbd_errstr(error));
	}
}

Static void
ural_bbp_write(struct ural_softc *sc, uint8_t reg, uint8_t val)
{
	uint16_t tmp;
	int ntries;

	for (ntries = 0; ntries < 5; ntries++) {
		if (!(ural_read(sc, RAL_PHY_CSR8) & RAL_BBP_BUSY))
			break;
	}
	if (ntries == 5) {
		printf("%s: could not write to BBP\n", USBDEVNAME(sc->sc_dev));
		return;
	}

	tmp = reg << 8 | val;
	ural_write(sc, RAL_PHY_CSR7, tmp);
}

Static uint8_t
ural_bbp_read(struct ural_softc *sc, uint8_t reg)
{
	uint16_t val;
	int ntries;

	val = RAL_BBP_WRITE | reg << 8;
	ural_write(sc, RAL_PHY_CSR7, val);

	for (ntries = 0; ntries < 5; ntries++) {
		if (!(ural_read(sc, RAL_PHY_CSR8) & RAL_BBP_BUSY))
			break;
	}
	if (ntries == 5) {
		printf("%s: could not read BBP\n", USBDEVNAME(sc->sc_dev));
		return 0;
	}

	return ural_read(sc, RAL_PHY_CSR7) & 0xff;
}

Static void
ural_rf_write(struct ural_softc *sc, uint8_t reg, uint32_t val)
{
	uint32_t tmp;
	int ntries;

	for (ntries = 0; ntries < 5; ntries++) {
		if (!(ural_read(sc, RAL_PHY_CSR10) & RAL_RF_LOBUSY))
			break;
	}
	if (ntries == 5) {
		printf("%s: could not write to RF\n", USBDEVNAME(sc->sc_dev));
		return;
	}

	tmp = RAL_RF_BUSY | RAL_RF_20BIT | (val & 0xfffff) << 2 | (reg & 0x3);
	ural_write(sc, RAL_PHY_CSR9,  tmp & 0xffff);
	ural_write(sc, RAL_PHY_CSR10, tmp >> 16);

	/* remember last written value in sc */
	sc->rf_regs[reg] = val;

	DPRINTFN(15, ("RF R[%u] <- 0x%05x\n", reg & 0x3, val & 0xfffff));
}

Static void
ural_set_chan(struct ural_softc *sc, struct ieee80211_channel *c)
{
#define N(a)	(sizeof (a) / sizeof ((a)[0]))
	struct ieee80211com *ic = &sc->sc_ic;
	uint8_t power, tmp;
	u_int i, chan;

	chan = ieee80211_chan2ieee(ic, c);
	if (chan == 0 || chan == IEEE80211_CHAN_ANY)
		return;

	if (IEEE80211_IS_CHAN_2GHZ(c))
		power = min(sc->txpow[chan - 1], 31);
	else
		power = 31;

	DPRINTFN(2, ("setting channel to %u, txpower to %u\n", chan, power));

	switch (sc->rf_rev) {
	case RAL_RF_2522:
		ural_rf_write(sc, RAL_RF1, 0x00814);
		ural_rf_write(sc, RAL_RF2, ural_rf2522_r2[chan - 1]);
		ural_rf_write(sc, RAL_RF3, power << 7 | 0x00040);
		break;

	case RAL_RF_2523:
		ural_rf_write(sc, RAL_RF1, 0x08804);
		ural_rf_write(sc, RAL_RF2, ural_rf2523_r2[chan - 1]);
		ural_rf_write(sc, RAL_RF3, power << 7 | 0x38044);
		ural_rf_write(sc, RAL_RF4, (chan == 14) ? 0x00280 : 0x00286);
		break;

	case RAL_RF_2524:
		ural_rf_write(sc, RAL_RF1, 0x0c808);
		ural_rf_write(sc, RAL_RF2, ural_rf2524_r2[chan - 1]);
		ural_rf_write(sc, RAL_RF3, power << 7 | 0x00040);
		ural_rf_write(sc, RAL_RF4, (chan == 14) ? 0x00280 : 0x00286);
		break;

	case RAL_RF_2525:
		ural_rf_write(sc, RAL_RF1, 0x08808);
		ural_rf_write(sc, RAL_RF2, ural_rf2525_hi_r2[chan - 1]);
		ural_rf_write(sc, RAL_RF3, power << 7 | 0x18044);
		ural_rf_write(sc, RAL_RF4, (chan == 14) ? 0x00280 : 0x00286);

		ural_rf_write(sc, RAL_RF1, 0x08808);
		ural_rf_write(sc, RAL_RF2, ural_rf2525_r2[chan - 1]);
		ural_rf_write(sc, RAL_RF3, power << 7 | 0x18044);
		ural_rf_write(sc, RAL_RF4, (chan == 14) ? 0x00280 : 0x00286);
		break;

	case RAL_RF_2525E:
		ural_rf_write(sc, RAL_RF1, 0x08808);
		ural_rf_write(sc, RAL_RF2, ural_rf2525e_r2[chan - 1]);
		ural_rf_write(sc, RAL_RF3, power << 7 | 0x18044);
		ural_rf_write(sc, RAL_RF4, (chan == 14) ? 0x00286 : 0x00282);
		break;

	case RAL_RF_2526:
		ural_rf_write(sc, RAL_RF2, ural_rf2526_hi_r2[chan - 1]);
		ural_rf_write(sc, RAL_RF4, (chan & 1) ? 0x00386 : 0x00381);
		ural_rf_write(sc, RAL_RF1, 0x08804);

		ural_rf_write(sc, RAL_RF2, ural_rf2526_r2[chan - 1]);
		ural_rf_write(sc, RAL_RF3, power << 7 | 0x18044);
		ural_rf_write(sc, RAL_RF4, (chan & 1) ? 0x00386 : 0x00381);
		break;

	/* dual-band RF */
	case RAL_RF_5222:
		for (i = 0; i < N(ural_rf5222); i++)
			if (ural_rf5222[i].chan == chan)
				break;

		if (i < N(ural_rf5222)) {
			ural_rf_write(sc, RAL_RF1, ural_rf5222[i].r1);
			ural_rf_write(sc, RAL_RF2, ural_rf5222[i].r2);
			ural_rf_write(sc, RAL_RF3, power << 7 | 0x00040);
			ural_rf_write(sc, RAL_RF4, ural_rf5222[i].r4);
		}
		break;
	}

	if (ic->ic_opmode != IEEE80211_M_MONITOR &&
	    ic->ic_state != IEEE80211_S_SCAN) {
		/* set Japan filter bit for channel 14 */
		tmp = ural_bbp_read(sc, 70);

		tmp &= ~RAL_JAPAN_FILTER;
		if (chan == 14)
			tmp |= RAL_JAPAN_FILTER;

		ural_bbp_write(sc, 70, tmp);

		/* clear CRC errors */
		ural_read(sc, RAL_STA_CSR0);

		DELAY(1000); /* RF needs a 1ms delay here */
		ural_disable_rf_tune(sc);
	}
#undef N
}

/*
 * Disable RF auto-tuning.
 */
Static void
ural_disable_rf_tune(struct ural_softc *sc)
{
	uint32_t tmp;

	if (sc->rf_rev != RAL_RF_2523) {
		tmp = sc->rf_regs[RAL_RF1] & ~RAL_RF1_AUTOTUNE;
		ural_rf_write(sc, RAL_RF1, tmp);
	}

	tmp = sc->rf_regs[RAL_RF3] & ~RAL_RF3_AUTOTUNE;
	ural_rf_write(sc, RAL_RF3, tmp);

	DPRINTFN(2, ("disabling RF autotune\n"));
}

/*
 * Refer to IEEE Std 802.11-1999 pp. 123 for more information on TSF
 * synchronization.
 */
Static void
ural_enable_tsf_sync(struct ural_softc *sc)
{
	struct ieee80211com *ic = &sc->sc_ic;
	uint16_t logcwmin, preload, tmp;

	/* first, disable TSF synchronization */
	ural_write(sc, RAL_TXRX_CSR19, 0);

	tmp = (16 * ic->ic_bss->ni_intval) << 4;
	ural_write(sc, RAL_TXRX_CSR18, tmp);

	logcwmin = (ic->ic_opmode == IEEE80211_M_IBSS) ? 2 : 0;
	preload = (ic->ic_opmode == IEEE80211_M_IBSS) ? 320 : 6;
	tmp = logcwmin << 12 | preload;
	ural_write(sc, RAL_TXRX_CSR20, tmp);

	/* finally, enable TSF synchronization */
	tmp = RAL_ENABLE_TSF | RAL_ENABLE_TBCN;
	if (ic->ic_opmode == IEEE80211_M_STA)
		tmp |= RAL_ENABLE_TSF_SYNC(1);
	else
		tmp |= RAL_ENABLE_TSF_SYNC(2) | RAL_ENABLE_BEACON_GENERATOR;
	ural_write(sc, RAL_TXRX_CSR19, tmp);

	DPRINTF(("enabling TSF synchronization\n"));
}

Static void
ural_set_bssid(struct ural_softc *sc, uint8_t *bssid)
{
	uint16_t tmp;

	tmp = bssid[0] | bssid[1] << 8;
	ural_write(sc, RAL_MAC_CSR5, tmp);

	tmp = bssid[2] | bssid[3] << 8;
	ural_write(sc, RAL_MAC_CSR6, tmp);

	tmp = bssid[4] | bssid[5] << 8;
	ural_write(sc, RAL_MAC_CSR7, tmp);

#ifdef __OpenBSD__
	DPRINTF(("setting BSSID to %6D\n", bssid, ":"));
#else
	DPRINTF(("setting BSSID to %s\n", ether_sprintf(bssid)));
#endif
}

Static void
ural_set_macaddr(struct ural_softc *sc, uint8_t *addr)
{
	uint16_t tmp;

	tmp = addr[0] | addr[1] << 8;
	ural_write(sc, RAL_MAC_CSR2, tmp);

	tmp = addr[2] | addr[3] << 8;
	ural_write(sc, RAL_MAC_CSR3, tmp);

	tmp = addr[4] | addr[5] << 8;
	ural_write(sc, RAL_MAC_CSR4, tmp);

#ifdef __OpenBSD__
	DPRINTF(("setting MAC address to %6D\n", addr, ":"));
#else
	DPRINTF(("setting MAC address to %s\n", ether_sprintf(addr)));
#endif
}

Static void
ural_update_promisc(struct ural_softc *sc)
{
	struct ifnet *ifp = &sc->sc_ic.ic_if;
	uint16_t tmp;

	tmp = ural_read(sc, RAL_TXRX_CSR2);

	tmp &= ~RAL_DROP_NOT_TO_ME;
	if (!(ifp->if_flags & IFF_PROMISC))
		tmp |= RAL_DROP_NOT_TO_ME;

	ural_write(sc, RAL_TXRX_CSR2, tmp);

	DPRINTF(("%s promiscuous mode\n", (ifp->if_flags & IFF_PROMISC) ?
	    "entering" : "leaving"));
}

Static const char *
ural_get_rf(int rev)
{
	switch (rev) {
	case RAL_RF_2522:	return "RT2522";
	case RAL_RF_2523:	return "RT2523";
	case RAL_RF_2524:	return "RT2524";
	case RAL_RF_2525:	return "RT2525";
	case RAL_RF_2525E:	return "RT2525e";
	case RAL_RF_2526:	return "RT2526";
	case RAL_RF_5222:	return "RT5222";
	default:		return "unknown";
	}
}

Static void
ural_read_eeprom(struct ural_softc *sc)
{
	struct ieee80211com *ic = &sc->sc_ic;
	uint16_t val;

	ural_eeprom_read(sc, RAL_EEPROM_CONFIG0, &val, 2);
	val = letoh16(val);
	sc->rf_rev =   (val >> 11) & 0x7;
	sc->hw_radio = (val >> 10) & 0x1;
	sc->led_mode = (val >> 6)  & 0x7;
	sc->rx_ant =   (val >> 4)  & 0x3;
	sc->tx_ant =   (val >> 2)  & 0x3;
	sc->nb_ant =   val & 0x3;

	/* read MAC address */
	ural_eeprom_read(sc, RAL_EEPROM_ADDRESS, ic->ic_myaddr, 6);

	/* read default values for BBP registers */
	ural_eeprom_read(sc, RAL_EEPROM_BBP_BASE, sc->bbp_prom, 2 * 16);

	/* read Tx power for all b/g channels */
	ural_eeprom_read(sc, RAL_EEPROM_TXPOWER, sc->txpow, 14);
}

Static int
ural_bbp_init(struct ural_softc *sc)
{
#define N(a)	(sizeof (a) / sizeof ((a)[0]))
	int i, ntries;

	/* wait for BBP to be ready */
	for (ntries = 0; ntries < 100; ntries++) {
		if (ural_bbp_read(sc, RAL_BBP_VERSION) != 0)
			break;
		DELAY(1000);
	}
	if (ntries == 100) {
		printf("%s: timeout waiting for BBP\n", USBDEVNAME(sc->sc_dev));
		return EIO;
	}

	/* initialize BBP registers to default values */
	for (i = 0; i < N(ural_def_bbp); i++)
		ural_bbp_write(sc, ural_def_bbp[i].reg, ural_def_bbp[i].val);

#if 0
	/* initialize BBP registers to values stored in EEPROM */
	for (i = 0; i < 16; i++) {
		if (sc->bbp_prom[i].reg == 0xff)
			continue;
		ural_bbp_write(sc, sc->bbp_prom[i].reg, sc->bbp_prom[i].val);
	}
#endif

	return 0;
#undef N
}

Static void
ural_set_txantenna(struct ural_softc *sc, int antenna)
{
	uint16_t tmp;
	uint8_t tx;

	tx = ural_bbp_read(sc, RAL_BBP_TX) & ~RAL_BBP_ANTMASK;
	if (antenna == 1)
		tx |= RAL_BBP_ANTA;
	else if (antenna == 2)
		tx |= RAL_BBP_ANTB;
	else
		tx |= RAL_BBP_DIVERSITY;

	/* need to force I/Q flip for RF 2525e, 2526 and 5222 */
	if (sc->rf_rev == RAL_RF_2525E || sc->rf_rev == RAL_RF_2526 ||
	    sc->rf_rev == RAL_RF_5222)
		tx |= RAL_BBP_FLIPIQ;

	ural_bbp_write(sc, RAL_BBP_TX, tx);

	/* update flags in PHY_CSR5 and PHY_CSR6 too */
	tmp = ural_read(sc, RAL_PHY_CSR5) & ~0x7;
	ural_write(sc, RAL_PHY_CSR5, tmp | (tx & 0x7));

	tmp = ural_read(sc, RAL_PHY_CSR6) & ~0x7;
	ural_write(sc, RAL_PHY_CSR6, tmp | (tx & 0x7));
}

Static void
ural_set_rxantenna(struct ural_softc *sc, int antenna)
{
	uint8_t rx;

	rx = ural_bbp_read(sc, RAL_BBP_RX) & ~RAL_BBP_ANTMASK;
	if (antenna == 1)
		rx |= RAL_BBP_ANTA;
	else if (antenna == 2)
		rx |= RAL_BBP_ANTB;
	else
		rx |= RAL_BBP_DIVERSITY;

	/* need to force no I/Q flip for RF 2525e and 2526 */
	if (sc->rf_rev == RAL_RF_2525E || sc->rf_rev == RAL_RF_2526)
		rx &= ~RAL_BBP_FLIPIQ;

	ural_bbp_write(sc, RAL_BBP_RX, rx);
}

Static int
ural_init(struct ifnet *ifp)
{
#define N(a)	(sizeof (a) / sizeof ((a)[0]))
	struct ural_softc *sc = ifp->if_softc;
	struct ieee80211com *ic = &sc->sc_ic;
	struct ieee80211_wepkey *wk;
	struct ural_rx_data *data;
	uint16_t sta[11], tmp;
	usbd_status error;
	int i, ntries;

	ural_stop(ifp, 0);

	/* initialize MAC registers to default values */
	for (i = 0; i < N(ural_def_mac); i++)
		ural_write(sc, ural_def_mac[i].reg, ural_def_mac[i].val);

	/* wait for BBP and RF to wake up (this can take a long time!) */
	for (ntries = 0; ntries < 100; ntries++) {
		tmp = ural_read(sc, RAL_MAC_CSR17);
		if ((tmp & (RAL_BBP_AWAKE | RAL_RF_AWAKE)) ==
		    (RAL_BBP_AWAKE | RAL_RF_AWAKE))
			break;
		DELAY(1000);
	}
	if (ntries == 100) {
		printf("%s: timeout waiting for BBP/RF to wakeup\n",
		    USBDEVNAME(sc->sc_dev));
		error = EIO;
		goto fail;
	}

	/* we're ready! */
	ural_write(sc, RAL_MAC_CSR1, RAL_HOST_READY);

	/* set supported basic rates (1, 2, 6, 12, 24) */
	ural_write(sc, RAL_TXRX_CSR11, 0x153);

	error = ural_bbp_init(sc);
	if (error != 0)
		goto fail;

	/* set default BSS channel */
	ic->ic_bss->ni_chan = ic->ic_ibss_chan;
	ural_set_chan(sc, ic->ic_bss->ni_chan);

	/* clear statistic registers (STA_CSR0 to STA_CSR10) */
	ural_read_multi(sc, RAL_STA_CSR0, sta, sizeof sta);

	ural_set_txantenna(sc, 1);
	ural_set_rxantenna(sc, 1);

	IEEE80211_ADDR_COPY(ic->ic_myaddr, LLADDR(ifp->if_sadl));
	ural_set_macaddr(sc, ic->ic_myaddr);

	/*
	 * Copy WEP keys into adapter's memory (SEC_CSR0 to SEC_CSR31).
	 */
	for (i = 0; i < IEEE80211_WEP_NKID; i++) {
		wk = &ic->ic_nw_keys[i];
		ural_write_multi(sc, RAL_SEC_CSR0 + i * IEEE80211_KEYBUF_SIZE,
		    wk->wk_key, IEEE80211_KEYBUF_SIZE);
	}

	/*
	 * 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",
		    USBDEVNAME(sc->sc_dev), 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",
		    USBDEVNAME(sc->sc_dev), usbd_errstr(error));
		goto fail;
	}

	/*
	 * Allocate Tx and Rx xfer queues.
	 */
	error = ural_alloc_tx_list(sc);
	if (error != 0) {
		printf("%s: could not allocate Tx list\n",
		    USBDEVNAME(sc->sc_dev));
		goto fail;
	}

	error = ural_alloc_rx_list(sc);
	if (error != 0) {
		printf("%s: could not allocate Rx list\n",
		    USBDEVNAME(sc->sc_dev));
		goto fail;
	}

	/*
	 * Start up the receive pipe.
	 */
	for (i = 0; i < RAL_RX_LIST_COUNT; i++) {
		data = &sc->rx_data[i];

		usbd_setup_xfer(data->xfer, sc->sc_rx_pipeh, data, data->buf,
		    MCLBYTES, USBD_SHORT_XFER_OK, USBD_NO_TIMEOUT, ural_rxeof);
		usbd_transfer(data->xfer);
	}

	/* kick Rx */
	tmp = RAL_DROP_PHY_ERROR | RAL_DROP_CRC_ERROR;
	if (ic->ic_opmode != IEEE80211_M_MONITOR) {
		tmp |= RAL_DROP_CTL | RAL_DROP_VERSION_ERROR;
		if (ic->ic_opmode != IEEE80211_M_HOSTAP)
			tmp |= RAL_DROP_TODS;
		if (!(ifp->if_flags & IFF_PROMISC))
			tmp |= RAL_DROP_NOT_TO_ME;
	}
	ural_write(sc, RAL_TXRX_CSR2, tmp);

	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:	ural_stop(ifp, 1);
	return error;
#undef N
}

Static void
ural_stop(struct ifnet *ifp, int disable)
{
	struct ural_softc *sc = ifp->if_softc;
	struct ieee80211com *ic = &sc->sc_ic;

	/* disable Rx */
	ural_write(sc, RAL_TXRX_CSR2, RAL_DISABLE_RX);

	/* reset ASIC and BBP (but won't reset MAC registers!) */
	ural_write(sc, RAL_MAC_CSR1, RAL_RESET_ASIC | RAL_RESET_BBP);
	ural_write(sc, RAL_MAC_CSR1, 0);

	ieee80211_new_state(ic, IEEE80211_S_INIT, -1);

	sc->sc_tx_timer = 0;
	ifp->if_timer = 0;
	ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE);

	if (sc->sc_rx_pipeh != NULL) {
		usbd_abort_pipe(sc->sc_rx_pipeh);
		usbd_close_pipe(sc->sc_rx_pipeh);
		sc->sc_rx_pipeh = NULL;
	}

	if (sc->sc_tx_pipeh != NULL) {
		usbd_abort_pipe(sc->sc_tx_pipeh);
		usbd_close_pipe(sc->sc_tx_pipeh);
		sc->sc_tx_pipeh = NULL;
	}

	ural_free_rx_list(sc);
	ural_free_tx_list(sc);
}

int
ural_activate(device_ptr_t self, enum devact act)
{
	switch (act) {
	case DVACT_ACTIVATE:
		return EOPNOTSUPP;

	case DVACT_DEACTIVATE:
		/*if_deactivate(&sc->sc_ic.ic_if);*/
		break;
	}

	return 0;
}

--Boundary-00=_h13bC3MzttkCcJc
Content-Type: text/x-diff;
  charset="us-ascii";
  name="net80211.patch"
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
	filename="net80211.patch"

Index: ieee80211_output.c
===================================================================
RCS file: /cvsroot/src/sys/net80211/ieee80211_output.c,v
retrieving revision 1.28
diff -u -r1.28 ieee80211_output.c
--- ieee80211_output.c	26 Feb 2005 22:45:09 -0000	1.28
+++ ieee80211_output.c	27 Apr 2005 10:04:57 -0000
@@ -90,6 +90,10 @@
 #endif
 #endif
 
+#ifdef __NetBSD__
+  #define IEEE80211_F_WEPON	IEEE80211_F_PRIVACY
+#endif /* __NetBSD__ */
+
 #ifdef IEEE80211_DEBUG
 /*
  * Decide if an outbound management frame should be
@@ -816,6 +820,97 @@
 #undef senderr
 }
 
+struct mbuf *
+ieee80211_beacon_alloc(struct ieee80211com *ic, struct ieee80211_node *ni)
+{
+	struct ieee80211_frame *wh;
+	struct mbuf *m;
+	int pktlen;
+	u_int8_t *frm;
+	u_int16_t capinfo;
+	struct ieee80211_rateset *rs;
+
+	rs = &ni->ni_rates;
+	pktlen = sizeof (struct ieee80211_frame)
+	    + 8 + 2 + 2 + 2+ni->ni_esslen + 2+rs->rs_nrates + 3 + 6;
+	if (rs->rs_nrates > IEEE80211_RATE_SIZE)
+		pktlen += 2;
+	m = ieee80211_getmbuf(M_DONTWAIT, MT_DATA,
+		 2 + ic->ic_des_esslen
+	       + 2 + IEEE80211_RATE_SIZE
+	       + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE));
+	if (m == NULL)
+		return NULL;
+
+	wh = mtod(m, struct ieee80211_frame *);
+	wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_MGT |
+	    IEEE80211_FC0_SUBTYPE_BEACON;
+	wh->i_fc[1] = IEEE80211_FC1_DIR_NODS;
+	*(u_int16_t *)wh->i_dur = 0;
+	IEEE80211_ADDR_COPY(wh->i_addr1, etherbroadcastaddr);
+	IEEE80211_ADDR_COPY(wh->i_addr2, ic->ic_myaddr);
+	IEEE80211_ADDR_COPY(wh->i_addr3, ni->ni_bssid);
+	*(u_int16_t *)wh->i_seq = 0;
+
+	/*
+	 * beacon frame format
+	 *	[8] time stamp
+	 *	[2] beacon interval
+	 *	[2] cabability information
+	 *	[tlv] ssid
+	 *	[tlv] supported rates
+	 *	[tlv] parameter set (IBSS)
+	 *	[tlv] extended supported rates
+	 */
+	frm = (u_int8_t *)&wh[1];
+	bzero(frm, 8);	/* timestamp is set by hardware */
+	frm += 8;
+	*(u_int16_t *)frm = htole16(ni->ni_intval);
+	frm += 2;
+	if (ic->ic_opmode == IEEE80211_M_IBSS) {
+		capinfo = IEEE80211_CAPINFO_IBSS;
+	} else {
+		capinfo = IEEE80211_CAPINFO_ESS;
+	}
+	if (ic->ic_flags & IEEE80211_F_WEPON)
+		capinfo |= IEEE80211_CAPINFO_PRIVACY;
+	if ((ic->ic_flags & IEEE80211_F_SHPREAMBLE) &&
+	    IEEE80211_IS_CHAN_2GHZ(ni->ni_chan))
+		capinfo |= IEEE80211_CAPINFO_SHORT_PREAMBLE;
+	if (ic->ic_flags & IEEE80211_F_SHSLOT)
+		capinfo |= IEEE80211_CAPINFO_SHORT_SLOTTIME;
+	*(u_int16_t *)frm = htole16(capinfo);
+	frm += 2;
+	*frm++ = IEEE80211_ELEMID_SSID;
+	*frm++ = ni->ni_esslen;
+	memcpy(frm, ni->ni_essid, ni->ni_esslen);
+	frm += ni->ni_esslen;
+	frm = ieee80211_add_rates(frm, rs);
+	*frm++ = IEEE80211_ELEMID_DSPARMS;
+	*frm++ = 1;
+	*frm++ = ieee80211_chan2ieee(ic, ni->ni_chan);
+	if (ic->ic_opmode == IEEE80211_M_IBSS) {
+		*frm++ = IEEE80211_ELEMID_IBSSPARMS;
+		*frm++ = 2;
+		*frm++ = 0; *frm++ = 0;		/* TODO: ATIM window */
+	} else {
+		/* TODO: TIM */
+		*frm++ = IEEE80211_ELEMID_TIM;
+		*frm++ = 4;	/* length */
+		*frm++ = 0;	/* DTIM count */
+		*frm++ = 1;	/* DTIM period */
+		*frm++ = 0;	/* bitmap control */
+		*frm++ = 0;	/* Partial Virtual Bitmap (variable length) */
+	}
+	frm = ieee80211_add_xrates(frm, rs);
+	m->m_pkthdr.len = m->m_len = frm - mtod(m, u_int8_t *);
+	m->m_pkthdr.rcvif = (void *)ni;
+	IASSERT(m->m_pkthdr.len <= pktlen,
+		("beacon bigger than expected, len %u calculated %u",
+		m->m_pkthdr.len, pktlen));
+	return m;
+}
+
 void
 ieee80211_pwrsave(struct ieee80211com *ic, struct ieee80211_node *ni,
 		  struct mbuf *m)
Index: ieee80211_proto.h
===================================================================
RCS file: /cvsroot/src/sys/net80211/ieee80211_proto.h,v
retrieving revision 1.9
diff -u -r1.9 ieee80211_proto.h
--- ieee80211_proto.h	26 Feb 2005 22:45:09 -0000	1.9
+++ ieee80211_proto.h	27 Apr 2005 10:04:57 -0000
@@ -65,6 +65,8 @@
 		int, int);
 extern	struct mbuf *ieee80211_encap(struct ifnet *, struct mbuf *,
 		struct ieee80211_node **);
+extern	struct mbuf *ieee80211_beacon_alloc(struct ieee80211com *,
+		struct ieee80211_node *);
 extern	void ieee80211_pwrsave(struct ieee80211com *, struct ieee80211_node *,
 		struct mbuf *);
 extern	struct mbuf *ieee80211_decap(struct ifnet *, struct mbuf *);

--Boundary-00=_h13bC3MzttkCcJc
Content-Type: text/x-diff;
  charset="us-ascii";
  name="usb.patch"
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
	filename="usb.patch"

Index: files.usb
===================================================================
RCS file: /cvsroot/src/sys/dev/usb/files.usb,v
retrieving revision 1.60
diff -u -r1.60 files.usb
--- files.usb	15 Apr 2005 17:18:18 -0000	1.60
+++ files.usb	27 Apr 2005 10:05:53 -0000
@@ -247,3 +247,8 @@
 device	atu: ether, ifnet, arp, wlan
 attach	atu at uhub
 file	dev/usb/if_atu.c		atu
+
+# Ralink Technology RT2500USB
+device	ural: ether, ifnet, arp, wlan
+attach	ural at uhub
+file	dev/usb/if_ral.c		ural
Index: usbdevs
===================================================================
RCS file: /cvsroot/src/sys/dev/usb/usbdevs,v
retrieving revision 1.398
diff -u -r1.398 usbdevs
--- usbdevs	15 Mar 2005 17:52:18 -0000	1.398
+++ usbdevs	27 Apr 2005 10:05:55 -0000
@@ -359,6 +359,7 @@
 vendor NEODIO		0x0aec	Neodio
 vendor VODAFONE		0x0af0	Vodafone
 vendor TODOS		0x0b0c	Todos Data System
+vendor ASUS		0x0b05	ASUS
 vendor SIIG2		0x0b39	SIIG
 vendor TEKRAM		0x0b3b	Tekram Technology
 vendor HAL		0x0b41	HAL Corporation
@@ -401,8 +402,11 @@
 vendor BELKIN2		0x1293	Belkin Components
 vendor AINCOMM		0x12fd	Aincomm
 vendor MOBILITY		0x1342	Mobility
+vendor CISCOLINKSYS	0x13b1	Cisco-Linksys
 vendor SHARK		0x13d2	Shark
 vendor SILICOM		0x1485	Silicom
+vendor RALINK		0x148f	Ralink Technology
+vendor CONCEPTRONIC2	0x14b2	Conceptronic
 vendor SILICONPORTALS	0x1527	Silicon Portals
 vendor OQO		0x1557	OQO
 vendor SOHOWARE		0x15e8	SOHOware
@@ -582,6 +586,9 @@
 /* ASIX Electronics products */
 product ASIX AX88172		0x1720	USB 2.0 10/100 ethernet controller
 
+/* ASUS products */
+product ASUS RT2570		0x1707	RT2570
+
 /* ATen products */
 product ATEN UC1284		0x2001	Parallel printer adapter
 product ATEN UC10T		0x2002	10Mbps ethernet adapter
@@ -690,6 +697,10 @@
 product CHPRODUCTS FIGHTERSTICK	0x00f3	Fighterstick
 product CHPRODUCTS FLIGHTYOKE	0x00ff	Flight Sim Yoke
 
+/* Cisco-Linksys products */
+product CISCOLINKSYS WUSB54G 	0x000d	RT2570
+product CISCOLINKSYS WUSB54GP	0x0011	RT2570
+
 /* Compaq products */
 product COMPAQ IPAQPOCKETPC	0x0003	iPAQ PocketPC
 product COMPAQ A1500		0x0012	A1500
@@ -702,6 +713,9 @@
 /* Composite Corp products looks the same as "TANGTOP" */
 product COMPOSITE USBPS2	0x0001	USB to PS2 Adaptor
 
+/* Conceptronic products */
+product CONCEPTRONIC2 C54RU	0x3c02	C54RU WLAN
+
 /* Concord Camera products */
 product CONCORDCAMERA EYE_Q_3X	0x0100	Eye Q 3x
 
@@ -769,6 +783,7 @@
 product DLINK DUBE100		0x1a00	10/100 ethernet adapter
 product DLINK DSB650TX4		0x200c	10/100 ethernet adapter
 product DLINK DWL122		0x3700	Wireless DWL122
+product DLINK RT2570		0x3c00	RT2570
 product DLINK DSB650C		0x4000	10Mbps ethernet adapter
 product DLINK DSB650TX1		0x4001	10/100 ethernet adapter
 product DLINK DSB650TX		0x4002	10/100 ethernet adapter
@@ -1228,6 +1243,7 @@
 product MELCO KS11G		0x0027	WLI-USB-KS11G USB-wlan
 product MELCO LUAU2KTX		0x003d	LUA-U2-KTX Ethernet
 product MELCO KB11		0x0044	WLI-USB-KB11 WLAN
+product MELCO KG54		0x0066	WLI-U2-KG54 WLAN
 
 /* Metricom products */
 product METRICOM RICOCHET_GS	0x0001	Ricochet GS
@@ -1524,6 +1540,10 @@
 /* Rainbow Technologies products */
 product	RAINBOW IKEY2000	0x1200	i-Key 2000
 
+/* Ralink Technology products */
+product RALINK RT2570		0x1706	RT2570
+product RALINK RT2570_2		0x2570	RT2570
+
 /* RATOC Systems products */
 product RATOC REXUSB60		0xb000	USB serial REX-USB60
 
@@ -1657,6 +1677,7 @@
 product SMC 2206USB		0x0201	EZ Connect USB Ethernet Adapter
 product SMC2 2020HUB            0x2020  USB Hub
 product SMC3 2662WUSB		0xa002	2662W-AR Wireless Adapter
+product SMC RT2570		0xee13	RT2570
 
 /* SOHOware products */
 product SOHOWARE NUB100		0x9100	10/100 USB Ethernet

--Boundary-00=_h13bC3MzttkCcJc
Content-Type: application/x-troff;
  name="ral.4"
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
	filename="ral.4"

.\" $OpenBSD: ral.4,v 1.24 2005/03/18 20:28:27 damien Exp $
.\"
.\" Copyright (c) 2005
.\"	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 February 15, 2005
.Os
.Dt RAL 4
.Sh NAME
.Nm ral
.Nd Ralink Technology RT25x0 IEEE 802.11a/b/g wireless network driver
.Sh SYNOPSIS
.Cd "ral* at cardbus?"
.Cd "ral* at pci?"
.Cd "ural* at uhub? port ?"
.Sh DESCRIPTION
The
.Nm
driver supports PCI/CardBus wireless adapters based on the Ralink RT2500
chipset
and the
.Nm ural
driver supports USB 2.0 wireless adapters based on the Ralink RT2500USB
chipset.
The two drivers are similar and will be merged into one in the future.
.Pp
The RT2500 chipset consists of two integrated chips, a RT2560 or RT2570 MAC/BBP
and a radio transceiver (the model of which depends on the card revision).
.Pp
The RT2522, RT2523, RT2524, RT2525, RT2526 and RT2525e radio transceivers
operate in the 2.4GHz band (802.11b/g) whereas the RT5222 is a dual-band radio
transceiver that can operate in the 2.4GHz and 5.2GHz bands (802.11a).
.Pp
The transmit speed is user-selectable or can be adapted automatically by the
driver depending on the received-signal strength.
See
.Xr rssadapt 9
for more information.
.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.
.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 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 returns 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 (RT5222 only).
.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.
.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 HARDWARE
The following PCI adapters should work:
.Bd -filled
A-Link WL54H.
Amigo AWI-926W.
AMIT WL531P.
AOpen AOI-831.
ASUS WL-130g.
ASUS WIFI-G-AAY.
Atlantis Land A02-PCI-W54.
Belkin F5D7000 v3.
Canyon CN-WF511.
CNet CWP-854.
Compex WLP54G.
Conceptronic C54Ri.
Digitus DN-7006G-RA.
E-Tech WGPI02.
Edimax EW-7128g.
Eminent EM3037.
Encore ENLWI-G-RLAM.
Fiberline WL-400P.
Gigabyte GN-WPKG.
Hawking HWP54GR.
iNexQ CR054g-009 (R03).
JAHT WN-4054PCI.
LevelOne WNC-0301 v2.
Linksys WMP54G v4.
Micronet SP906GK.
Minitar MN54GPC-R.
MSI PC54G2.
OvisLink EVO-W54PCI.
PheeNet HWL-PCIG/RA.
Pro-Nets PC80211G.
Repotec RP-WP0854.
SATech SN-54P.
Sitecom WL-115.
Surecom EP-9321-g.
Surecom EP-9321-g1.
Sweex LC700030.
TekComm NE-9321-g.
Unex CR054g-R02.
Zinwell ZWX-G361.
Zonet ZEW1600.
.Ed
.Pp
The following CardBus adapters should work:
.Bd -filled
A-Link WL54PC.
Amigo AWI-914W.
AMIT WL531C.
ASUS WL-107G.
Atlantis Land A02-PCM-W54.
Belkin F5D7010 v2.
Canyon CN-WF513.
CC&C WL-2102.
CNet CWC-854.
Compex WL54G.
Conceptronic C54RC.
Digitus DN-7001G-RA.
E-Tech WGPC02.
Edimax EW-7108PCg.
Eminent EM3036.
Encore ENPWI-G-RLAM.
Fibreline WL-400X.
Gigabyte GN-WMKG.
Hawking HWC54GR.
JAHT WN-4054P.
LevelOne WPC-0301 v2.
Micronet SP908GK V3.
Minitar MN54GCB-R.
MSI CB54G2.
Pro-Nets CB80211G.
Repotec RP-WB7108.
SATech SN-54C.
Sitecom WL-112.
SparkLAN WL-685R.
Surecom EP-9428-g.
Sweex LC500050.
TekComm NE-9428-g.
Unex MR054g-R02.
Zinwell ZWX-G160.
Zonet ZEW1500.
.Ed
.Pp
The following Mini PCI adapters should work:
.Bd -filled
Amigo AWI-922W.
Billionton MIWLGRL.
Gigabyte GN-WIKG.
MSI MP54G2.
Zinwell ZWX-G360.
.Ed
.Pp
The following USB 2.0 adapters should work:
.Bd -filled
AMIT WL532U.
ASUS WL-167g.
Buffalo WLI-U2-KG54-AI.
CNet CWD-854.
Compex WLU54G.
Conceptronic C54RU.
D-Link DWL-G122 (b1).
E-Tech WGUS02.
Gigabyte GN-WBKG.
Linksys WUSB54G v4.
Linksys WUSB54GP v4.
MSI MS-6861.
MSI MS-6865.
MSI MS-6869.
Repotec RP-WU0402.
Surecom EP-9001-g.
Zinwell ZWX-G261.
.Ed
.Sh EXAMPLES
The following
.Xr hostname.if 5
example configures ral0 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
Configure ral0 for WEP, using hex key
.Dq 0x1deadbeef1 :
.Bd -literal -offset indent
# ifconfig ral0 nwkey 0x1deadbeef1
.Ed
.Pp
Return ral0 to its default settings:
.Bd -literal -offset indent
# ifconfig ral0 -bssid -chan media autoselect -mediaopt \e
	nwid "" -nwkey
.Ed
.Pp
Join an existing BSS network,
.Dq my_net :
.Bd -literal -offset indent
# ifconfig ral0 192.168.1.1 netmask 0xffffff00 nwid my_net
.Ed
.Sh DIAGNOSTICS
.Bl -diag
.It "ral%d: device timeout"
The driver will reset the hardware.
This should not happen.
.El
.Sh SEE ALSO
.Xr arp 4 ,
.Xr cardbus 4 ,
.Xr ifmedia 4 ,
.Xr intro 4 ,
.Xr netintro 4 ,
.Xr pci 4 ,
.Xr hostname.if 5 ,
.Xr ifconfig 8
.Rs
.%T Ralink Technology
.%O http://www.ralinktech.com
.Re
.Sh HISTORY
The
.Nm
driver first appeared in
.Ox 3.7 .
.Sh AUTHORS
The
.Nm
driver was written by
.An Damien Bergamini Aq damien@openbsd.org .
.Sh CAVEATS
PCI
.Nm
adapters seem to strictly require a system supporting PCI 2.2 or greater and
will likely not work in systems based on older revisions of the PCI
specification.
.Pp
The
.Nm ural
driver does not support automatic adaptation of the transmit speed.

--Boundary-00=_h13bC3MzttkCcJc--