From f58ca84122d7579ee434087f06a6e42bf262809e Mon Sep 17 00:00:00 2001 From: Weongyo Jeong Date: Wed, 27 May 2009 03:57:38 +0000 Subject: [PATCH] ports urtw(4) for USB2. Additionally it supports a 8187B chipset weakly that it needs more stabilization. --- sys/dev/usb/wlan/if_urtw.c | 4241 +++++++++++++++++++++++++++++++++ sys/dev/usb/wlan/if_urtwreg.h | 303 +++ sys/dev/usb/wlan/if_urtwvar.h | 174 ++ sys/modules/usb/urtw/Makefile | 10 + 4 files changed, 4728 insertions(+) create mode 100644 sys/dev/usb/wlan/if_urtw.c create mode 100644 sys/dev/usb/wlan/if_urtwreg.h create mode 100644 sys/dev/usb/wlan/if_urtwvar.h create mode 100644 sys/modules/usb/urtw/Makefile diff --git a/sys/dev/usb/wlan/if_urtw.c b/sys/dev/usb/wlan/if_urtw.c new file mode 100644 index 000000000000..0124023d45f8 --- /dev/null +++ b/sys/dev/usb/wlan/if_urtw.c @@ -0,0 +1,4241 @@ +/*- + * Copyright (c) 2008 Weongyo Jeong + * + * 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 +__FBSDID("$FreeBSD$"); +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#ifdef INET +#include +#include +#include +#include +#include +#endif + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include "usbdevs.h" + +#include +#include + +SYSCTL_NODE(_hw_usb, OID_AUTO, urtw, CTLFLAG_RW, 0, "USB Realtek 8187L"); +#ifdef URTW_DEBUG +int urtw_debug = 0; +SYSCTL_INT(_hw_usb_urtw, OID_AUTO, debug, CTLFLAG_RW, &urtw_debug, 0, + "control debugging printfs"); +TUNABLE_INT("hw.usb.urtw.debug", &urtw_debug); +enum { + URTW_DEBUG_XMIT = 0x00000001, /* basic xmit operation */ + URTW_DEBUG_RECV = 0x00000002, /* basic recv operation */ + URTW_DEBUG_RESET = 0x00000004, /* reset processing */ + URTW_DEBUG_TX_PROC = 0x00000008, /* tx ISR proc */ + URTW_DEBUG_RX_PROC = 0x00000010, /* rx ISR proc */ + URTW_DEBUG_STATE = 0x00000020, /* 802.11 state transitions */ + URTW_DEBUG_STAT = 0x00000040, /* statistic */ + URTW_DEBUG_INIT = 0x00000080, /* initialization of dev */ + URTW_DEBUG_ANY = 0xffffffff +}; +#define DPRINTF(sc, m, fmt, ...) do { \ + if (sc->sc_debug & (m)) \ + printf(fmt, __VA_ARGS__); \ +} while (0) +#else +#define DPRINTF(sc, m, fmt, ...) do { \ + (void) sc; \ +} while (0) +#endif +int urtw_preamble_mode = URTW_PREAMBLE_MODE_LONG; +SYSCTL_INT(_hw_usb_urtw, OID_AUTO, preamble_mode, CTLFLAG_RW, + &urtw_preamble_mode, 0, "set the preable mode (long or short)"); +TUNABLE_INT("hw.usb.urtw.preamble_mode", &urtw_preamble_mode); + +/* recognized device vendors/products */ +#define urtw_lookup(v, p) \ + ((const struct urtw_type *)usb_lookup(urtw_devs, v, p)) +#define URTW_DEV_B(v,p) \ + { USB_VPI(USB_VENDOR_##v, USB_PRODUCT_##v##_##p, URTW_REV_RTL8187B) } +#define URTW_DEV_L(v,p) \ + { USB_VPI(USB_VENDOR_##v, USB_PRODUCT_##v##_##p, URTW_REV_RTL8187L) } +#define URTW_REV_RTL8187B 0 +#define URTW_REV_RTL8187L 1 +static const struct usb2_device_id urtw_devs[] = { + { USB_VPI(USB_VENDOR_BELKIN, 0x705e, URTW_REV_RTL8187B) }, + { USB_VPI(USB_VENDOR_REALTEK, 0x8189, URTW_REV_RTL8187B) }, + { USB_VPI(USB_VENDOR_REALTEK, 0x8197, URTW_REV_RTL8187B) }, + { USB_VPI(USB_VENDOR_REALTEK, 0x8198, URTW_REV_RTL8187B) }, + { USB_VPI(USB_VENDOR_NETGEAR, 0x4260, URTW_REV_RTL8187B) }, + { USB_VPI(0x1b75, 0x8187, URTW_REV_RTL8187L) }, + { USB_VPI(USB_VENDOR_ASUS, 0x171d, URTW_REV_RTL8187L) }, + { USB_VPI(USB_VENDOR_DICKSMITH, 0x9401, URTW_REV_RTL8187L) }, + { USB_VPI(USB_VENDOR_HP, 0xca02, URTW_REV_RTL8187L) }, + { USB_VPI(USB_VENDOR_LOGITEC, 0x010c, URTW_REV_RTL8187L) }, + { USB_VPI(USB_VENDOR_NETGEAR, 0x6100, URTW_REV_RTL8187L) }, + URTW_DEV_L(NETGEAR, WG111V2), + URTW_DEV_L(REALTEK, RTL8187), + { USB_VPI(USB_VENDOR_SITECOMEU, 0x000d, URTW_REV_RTL8187L) }, + { USB_VPI(USB_VENDOR_SPHAIRON, 0x0150, URTW_REV_RTL8187L) }, + { USB_VPI(USB_VENDOR_SURECOM, 0x11f2, URTW_REV_RTL8187L) }, + { USB_VPI(USB_VENDOR_QCOM, 0x6232, URTW_REV_RTL8187L) }, +#undef URTW_DEV_L +#undef URTW_DEV_B +}; + +#define urtw_read8_m(sc, val, data) do { \ + error = urtw_read8_c(sc, val, data); \ + if (error != 0) \ + goto fail; \ +} while (0) +#define urtw_write8_m(sc, val, data) do { \ + error = urtw_write8_c(sc, val, data); \ + if (error != 0) \ + goto fail; \ +} while (0) +#define urtw_read16_m(sc, val, data) do { \ + error = urtw_read16_c(sc, val, data); \ + if (error != 0) \ + goto fail; \ +} while (0) +#define urtw_write16_m(sc, val, data) do { \ + error = urtw_write16_c(sc, val, data); \ + if (error != 0) \ + goto fail; \ +} while (0) +#define urtw_read32_m(sc, val, data) do { \ + error = urtw_read32_c(sc, val, data); \ + if (error != 0) \ + goto fail; \ +} while (0) +#define urtw_write32_m(sc, val, data) do { \ + error = urtw_write32_c(sc, val, data); \ + if (error != 0) \ + goto fail; \ +} while (0) +#define urtw_8187_write_phy_ofdm(sc, val, data) do { \ + error = urtw_8187_write_phy_ofdm_c(sc, val, data); \ + if (error != 0) \ + goto fail; \ +} while (0) +#define urtw_8187_write_phy_cck(sc, val, data) do { \ + error = urtw_8187_write_phy_cck_c(sc, val, data); \ + if (error != 0) \ + goto fail; \ +} while (0) +#define urtw_8225_write(sc, val, data) do { \ + error = urtw_8225_write_c(sc, val, data); \ + if (error != 0) \ + goto fail; \ +} while (0) + +struct urtw_pair { + uint32_t reg; + uint32_t val; +}; + +static uint8_t urtw_8225_agc[] = { + 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9d, 0x9c, 0x9b, + 0x9a, 0x99, 0x98, 0x97, 0x96, 0x95, 0x94, 0x93, 0x92, 0x91, 0x90, + 0x8f, 0x8e, 0x8d, 0x8c, 0x8b, 0x8a, 0x89, 0x88, 0x87, 0x86, 0x85, + 0x84, 0x83, 0x82, 0x81, 0x80, 0x3f, 0x3e, 0x3d, 0x3c, 0x3b, 0x3a, + 0x39, 0x38, 0x37, 0x36, 0x35, 0x34, 0x33, 0x32, 0x31, 0x30, 0x2f, + 0x2e, 0x2d, 0x2c, 0x2b, 0x2a, 0x29, 0x28, 0x27, 0x26, 0x25, 0x24, + 0x23, 0x22, 0x21, 0x20, 0x1f, 0x1e, 0x1d, 0x1c, 0x1b, 0x1a, 0x19, + 0x18, 0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x0f, 0x0e, + 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, + 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 +}; + +static uint8_t urtw_8225z2_agc[] = { + 0x5e, 0x5e, 0x5e, 0x5e, 0x5d, 0x5b, 0x59, 0x57, 0x55, 0x53, 0x51, + 0x4f, 0x4d, 0x4b, 0x49, 0x47, 0x45, 0x43, 0x41, 0x3f, 0x3d, 0x3b, + 0x39, 0x37, 0x35, 0x33, 0x31, 0x2f, 0x2d, 0x2b, 0x29, 0x27, 0x25, + 0x23, 0x21, 0x1f, 0x1d, 0x1b, 0x19, 0x17, 0x15, 0x13, 0x11, 0x0f, + 0x0d, 0x0b, 0x09, 0x07, 0x05, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x19, 0x19, + 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x20, 0x21, 0x22, 0x23, + 0x24, 0x25, 0x26, 0x26, 0x27, 0x27, 0x28, 0x28, 0x29, 0x2a, 0x2a, + 0x2a, 0x2b, 0x2b, 0x2b, 0x2c, 0x2c, 0x2c, 0x2d, 0x2d, 0x2d, 0x2d, + 0x2e, 0x2e, 0x2e, 0x2e, 0x2f, 0x2f, 0x2f, 0x30, 0x30, 0x31, 0x31, + 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, + 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31 +}; + +static uint32_t urtw_8225_channel[] = { + 0x0000, /* dummy channel 0 */ + 0x085c, /* 1 */ + 0x08dc, /* 2 */ + 0x095c, /* 3 */ + 0x09dc, /* 4 */ + 0x0a5c, /* 5 */ + 0x0adc, /* 6 */ + 0x0b5c, /* 7 */ + 0x0bdc, /* 8 */ + 0x0c5c, /* 9 */ + 0x0cdc, /* 10 */ + 0x0d5c, /* 11 */ + 0x0ddc, /* 12 */ + 0x0e5c, /* 13 */ + 0x0f72, /* 14 */ +}; + +static uint8_t urtw_8225_gain[] = { + 0x23, 0x88, 0x7c, 0xa5, /* -82dbm */ + 0x23, 0x88, 0x7c, 0xb5, /* -82dbm */ + 0x23, 0x88, 0x7c, 0xc5, /* -82dbm */ + 0x33, 0x80, 0x79, 0xc5, /* -78dbm */ + 0x43, 0x78, 0x76, 0xc5, /* -74dbm */ + 0x53, 0x60, 0x73, 0xc5, /* -70dbm */ + 0x63, 0x58, 0x70, 0xc5, /* -66dbm */ +}; + +static struct urtw_pair urtw_8225_rf_part1[] = { + { 0x00, 0x0067 }, { 0x01, 0x0fe0 }, { 0x02, 0x044d }, { 0x03, 0x0441 }, + { 0x04, 0x0486 }, { 0x05, 0x0bc0 }, { 0x06, 0x0ae6 }, { 0x07, 0x082a }, + { 0x08, 0x001f }, { 0x09, 0x0334 }, { 0x0a, 0x0fd4 }, { 0x0b, 0x0391 }, + { 0x0c, 0x0050 }, { 0x0d, 0x06db }, { 0x0e, 0x0029 }, { 0x0f, 0x0914 }, +}; + +static struct urtw_pair urtw_8225_rf_part2[] = { + { 0x00, 0x01 }, { 0x01, 0x02 }, { 0x02, 0x42 }, { 0x03, 0x00 }, + { 0x04, 0x00 }, { 0x05, 0x00 }, { 0x06, 0x40 }, { 0x07, 0x00 }, + { 0x08, 0x40 }, { 0x09, 0xfe }, { 0x0a, 0x09 }, { 0x0b, 0x80 }, + { 0x0c, 0x01 }, { 0x0e, 0xd3 }, { 0x0f, 0x38 }, { 0x10, 0x84 }, + { 0x11, 0x06 }, { 0x12, 0x20 }, { 0x13, 0x20 }, { 0x14, 0x00 }, + { 0x15, 0x40 }, { 0x16, 0x00 }, { 0x17, 0x40 }, { 0x18, 0xef }, + { 0x19, 0x19 }, { 0x1a, 0x20 }, { 0x1b, 0x76 }, { 0x1c, 0x04 }, + { 0x1e, 0x95 }, { 0x1f, 0x75 }, { 0x20, 0x1f }, { 0x21, 0x27 }, + { 0x22, 0x16 }, { 0x24, 0x46 }, { 0x25, 0x20 }, { 0x26, 0x90 }, + { 0x27, 0x88 } +}; + +static struct urtw_pair urtw_8225_rf_part3[] = { + { 0x00, 0x98 }, { 0x03, 0x20 }, { 0x04, 0x7e }, { 0x05, 0x12 }, + { 0x06, 0xfc }, { 0x07, 0x78 }, { 0x08, 0x2e }, { 0x10, 0x9b }, + { 0x11, 0x88 }, { 0x12, 0x47 }, { 0x13, 0xd0 }, { 0x19, 0x00 }, + { 0x1a, 0xa0 }, { 0x1b, 0x08 }, { 0x40, 0x86 }, { 0x41, 0x8d }, + { 0x42, 0x15 }, { 0x43, 0x18 }, { 0x44, 0x1f }, { 0x45, 0x1e }, + { 0x46, 0x1a }, { 0x47, 0x15 }, { 0x48, 0x10 }, { 0x49, 0x0a }, + { 0x4a, 0x05 }, { 0x4b, 0x02 }, { 0x4c, 0x05 } +}; + +static uint16_t urtw_8225_rxgain[] = { + 0x0400, 0x0401, 0x0402, 0x0403, 0x0404, 0x0405, 0x0408, 0x0409, + 0x040a, 0x040b, 0x0502, 0x0503, 0x0504, 0x0505, 0x0540, 0x0541, + 0x0542, 0x0543, 0x0544, 0x0545, 0x0580, 0x0581, 0x0582, 0x0583, + 0x0584, 0x0585, 0x0588, 0x0589, 0x058a, 0x058b, 0x0643, 0x0644, + 0x0645, 0x0680, 0x0681, 0x0682, 0x0683, 0x0684, 0x0685, 0x0688, + 0x0689, 0x068a, 0x068b, 0x068c, 0x0742, 0x0743, 0x0744, 0x0745, + 0x0780, 0x0781, 0x0782, 0x0783, 0x0784, 0x0785, 0x0788, 0x0789, + 0x078a, 0x078b, 0x078c, 0x078d, 0x0790, 0x0791, 0x0792, 0x0793, + 0x0794, 0x0795, 0x0798, 0x0799, 0x079a, 0x079b, 0x079c, 0x079d, + 0x07a0, 0x07a1, 0x07a2, 0x07a3, 0x07a4, 0x07a5, 0x07a8, 0x07a9, + 0x07aa, 0x07ab, 0x07ac, 0x07ad, 0x07b0, 0x07b1, 0x07b2, 0x07b3, + 0x07b4, 0x07b5, 0x07b8, 0x07b9, 0x07ba, 0x07bb, 0x07bb +}; + +static uint8_t urtw_8225_threshold[] = { + 0x8d, 0x8d, 0x8d, 0x8d, 0x9d, 0xad, 0xbd, +}; + +static uint8_t urtw_8225_tx_gain_cck_ofdm[] = { + 0x02, 0x06, 0x0e, 0x1e, 0x3e, 0x7e +}; + +static uint8_t urtw_8225_txpwr_cck[] = { + 0x18, 0x17, 0x15, 0x11, 0x0c, 0x08, 0x04, 0x02, + 0x1b, 0x1a, 0x17, 0x13, 0x0e, 0x09, 0x04, 0x02, + 0x1f, 0x1e, 0x1a, 0x15, 0x10, 0x0a, 0x05, 0x02, + 0x22, 0x21, 0x1d, 0x18, 0x11, 0x0b, 0x06, 0x02, + 0x26, 0x25, 0x21, 0x1b, 0x14, 0x0d, 0x06, 0x03, + 0x2b, 0x2a, 0x25, 0x1e, 0x16, 0x0e, 0x07, 0x03 +}; + +static uint8_t urtw_8225_txpwr_cck_ch14[] = { + 0x18, 0x17, 0x15, 0x0c, 0x00, 0x00, 0x00, 0x00, + 0x1b, 0x1a, 0x17, 0x0e, 0x00, 0x00, 0x00, 0x00, + 0x1f, 0x1e, 0x1a, 0x0f, 0x00, 0x00, 0x00, 0x00, + 0x22, 0x21, 0x1d, 0x11, 0x00, 0x00, 0x00, 0x00, + 0x26, 0x25, 0x21, 0x13, 0x00, 0x00, 0x00, 0x00, + 0x2b, 0x2a, 0x25, 0x15, 0x00, 0x00, 0x00, 0x00 +}; + +static uint8_t urtw_8225_txpwr_ofdm[]={ + 0x80, 0x90, 0xa2, 0xb5, 0xcb, 0xe4 +}; + +static uint8_t urtw_8225v2_gain_bg[]={ + 0x23, 0x15, 0xa5, /* -82-1dbm */ + 0x23, 0x15, 0xb5, /* -82-2dbm */ + 0x23, 0x15, 0xc5, /* -82-3dbm */ + 0x33, 0x15, 0xc5, /* -78dbm */ + 0x43, 0x15, 0xc5, /* -74dbm */ + 0x53, 0x15, 0xc5, /* -70dbm */ + 0x63, 0x15, 0xc5, /* -66dbm */ +}; + +static struct urtw_pair urtw_8225v2_rf_part1[] = { + { 0x00, 0x02bf }, { 0x01, 0x0ee0 }, { 0x02, 0x044d }, { 0x03, 0x0441 }, + { 0x04, 0x08c3 }, { 0x05, 0x0c72 }, { 0x06, 0x00e6 }, { 0x07, 0x082a }, + { 0x08, 0x003f }, { 0x09, 0x0335 }, { 0x0a, 0x09d4 }, { 0x0b, 0x07bb }, + { 0x0c, 0x0850 }, { 0x0d, 0x0cdf }, { 0x0e, 0x002b }, { 0x0f, 0x0114 } +}; + +static struct urtw_pair urtw_8225v2b_rf_part1[] = { + { 0x00, 0x00b7 }, { 0x01, 0x0ee0 }, { 0x02, 0x044d }, { 0x03, 0x0441 }, + { 0x04, 0x08c3 }, { 0x05, 0x0c72 }, { 0x06, 0x00e6 }, { 0x07, 0x082a }, + { 0x08, 0x003f }, { 0x09, 0x0335 }, { 0x0a, 0x09d4 }, { 0x0b, 0x07bb }, + { 0x0c, 0x0850 }, { 0x0d, 0x0cdf }, { 0x0e, 0x002b }, { 0x0f, 0x0114 } +}; + +static struct urtw_pair urtw_8225v2_rf_part2[] = { + { 0x00, 0x01 }, { 0x01, 0x02 }, { 0x02, 0x42 }, { 0x03, 0x00 }, + { 0x04, 0x00 }, { 0x05, 0x00 }, { 0x06, 0x40 }, { 0x07, 0x00 }, + { 0x08, 0x40 }, { 0x09, 0xfe }, { 0x0a, 0x08 }, { 0x0b, 0x80 }, + { 0x0c, 0x01 }, { 0x0d, 0x43 }, { 0x0e, 0xd3 }, { 0x0f, 0x38 }, + { 0x10, 0x84 }, { 0x11, 0x07 }, { 0x12, 0x20 }, { 0x13, 0x20 }, + { 0x14, 0x00 }, { 0x15, 0x40 }, { 0x16, 0x00 }, { 0x17, 0x40 }, + { 0x18, 0xef }, { 0x19, 0x19 }, { 0x1a, 0x20 }, { 0x1b, 0x15 }, + { 0x1c, 0x04 }, { 0x1d, 0xc5 }, { 0x1e, 0x95 }, { 0x1f, 0x75 }, + { 0x20, 0x1f }, { 0x21, 0x17 }, { 0x22, 0x16 }, { 0x23, 0x80 }, + { 0x24, 0x46 }, { 0x25, 0x00 }, { 0x26, 0x90 }, { 0x27, 0x88 } +}; + +static struct urtw_pair urtw_8225v2b_rf_part2[] = { + { 0x00, 0x10 }, { 0x01, 0x0d }, { 0x02, 0x01 }, { 0x03, 0x00 }, + { 0x04, 0x14 }, { 0x05, 0xfb }, { 0x06, 0xfb }, { 0x07, 0x60 }, + { 0x08, 0x00 }, { 0x09, 0x60 }, { 0x0a, 0x00 }, { 0x0b, 0x00 }, + { 0x0c, 0x00 }, { 0x0d, 0x5c }, { 0x0e, 0x00 }, { 0x0f, 0x00 }, + { 0x10, 0x40 }, { 0x11, 0x00 }, { 0x12, 0x40 }, { 0x13, 0x00 }, + { 0x14, 0x00 }, { 0x15, 0x00 }, { 0x16, 0xa8 }, { 0x17, 0x26 }, + { 0x18, 0x32 }, { 0x19, 0x33 }, { 0x1a, 0x07 }, { 0x1b, 0xa5 }, + { 0x1c, 0x6f }, { 0x1d, 0x55 }, { 0x1e, 0xc8 }, { 0x1f, 0xb3 }, + { 0x20, 0x0a }, { 0x21, 0xe1 }, { 0x22, 0x2C }, { 0x23, 0x8a }, + { 0x24, 0x86 }, { 0x25, 0x83 }, { 0x26, 0x34 }, { 0x27, 0x0f }, + { 0x28, 0x4f }, { 0x29, 0x24 }, { 0x2a, 0x6f }, { 0x2b, 0xc2 }, + { 0x2c, 0x6b }, { 0x2d, 0x40 }, { 0x2e, 0x80 }, { 0x2f, 0x00 }, + { 0x30, 0xc0 }, { 0x31, 0xc1 }, { 0x32, 0x58 }, { 0x33, 0xf1 }, + { 0x34, 0x00 }, { 0x35, 0xe4 }, { 0x36, 0x90 }, { 0x37, 0x3e }, + { 0x38, 0x6d }, { 0x39, 0x3c }, { 0x3a, 0xfb }, { 0x3b, 0x07 } +}; + +static struct urtw_pair urtw_8225v2_rf_part3[] = { + { 0x00, 0x98 }, { 0x03, 0x20 }, { 0x04, 0x7e }, { 0x05, 0x12 }, + { 0x06, 0xfc }, { 0x07, 0x78 }, { 0x08, 0x2e }, { 0x09, 0x11 }, + { 0x0a, 0x17 }, { 0x0b, 0x11 }, { 0x10, 0x9b }, { 0x11, 0x88 }, + { 0x12, 0x47 }, { 0x13, 0xd0 }, { 0x19, 0x00 }, { 0x1a, 0xa0 }, + { 0x1b, 0x08 }, { 0x1d, 0x00 }, { 0x40, 0x86 }, { 0x41, 0x9d }, + { 0x42, 0x15 }, { 0x43, 0x18 }, { 0x44, 0x36 }, { 0x45, 0x35 }, + { 0x46, 0x2e }, { 0x47, 0x25 }, { 0x48, 0x1c }, { 0x49, 0x12 }, + { 0x4a, 0x09 }, { 0x4b, 0x04 }, { 0x4c, 0x05 } +}; + +static uint16_t urtw_8225v2_rxgain[] = { + 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0008, 0x0009, + 0x000a, 0x000b, 0x0102, 0x0103, 0x0104, 0x0105, 0x0140, 0x0141, + 0x0142, 0x0143, 0x0144, 0x0145, 0x0180, 0x0181, 0x0182, 0x0183, + 0x0184, 0x0185, 0x0188, 0x0189, 0x018a, 0x018b, 0x0243, 0x0244, + 0x0245, 0x0280, 0x0281, 0x0282, 0x0283, 0x0284, 0x0285, 0x0288, + 0x0289, 0x028a, 0x028b, 0x028c, 0x0342, 0x0343, 0x0344, 0x0345, + 0x0380, 0x0381, 0x0382, 0x0383, 0x0384, 0x0385, 0x0388, 0x0389, + 0x038a, 0x038b, 0x038c, 0x038d, 0x0390, 0x0391, 0x0392, 0x0393, + 0x0394, 0x0395, 0x0398, 0x0399, 0x039a, 0x039b, 0x039c, 0x039d, + 0x03a0, 0x03a1, 0x03a2, 0x03a3, 0x03a4, 0x03a5, 0x03a8, 0x03a9, + 0x03aa, 0x03ab, 0x03ac, 0x03ad, 0x03b0, 0x03b1, 0x03b2, 0x03b3, + 0x03b4, 0x03b5, 0x03b8, 0x03b9, 0x03ba, 0x03bb, 0x03bb +}; + +static uint16_t urtw_8225v2b_rxgain[] = { + 0x0400, 0x0401, 0x0402, 0x0403, 0x0404, 0x0405, 0x0408, 0x0409, + 0x040a, 0x040b, 0x0502, 0x0503, 0x0504, 0x0505, 0x0540, 0x0541, + 0x0542, 0x0543, 0x0544, 0x0545, 0x0580, 0x0581, 0x0582, 0x0583, + 0x0584, 0x0585, 0x0588, 0x0589, 0x058a, 0x058b, 0x0643, 0x0644, + 0x0645, 0x0680, 0x0681, 0x0682, 0x0683, 0x0684, 0x0685, 0x0688, + 0x0689, 0x068a, 0x068b, 0x068c, 0x0742, 0x0743, 0x0744, 0x0745, + 0x0780, 0x0781, 0x0782, 0x0783, 0x0784, 0x0785, 0x0788, 0x0789, + 0x078a, 0x078b, 0x078c, 0x078d, 0x0790, 0x0791, 0x0792, 0x0793, + 0x0794, 0x0795, 0x0798, 0x0799, 0x079a, 0x079b, 0x079c, 0x079d, + 0x07a0, 0x07a1, 0x07a2, 0x07a3, 0x07a4, 0x07a5, 0x07a8, 0x07a9, + 0x03aa, 0x03ab, 0x03ac, 0x03ad, 0x03b0, 0x03b1, 0x03b2, 0x03b3, + 0x03b4, 0x03b5, 0x03b8, 0x03b9, 0x03ba, 0x03bb, 0x03bb +}; + +static uint8_t urtw_8225v2_tx_gain_cck_ofdm[] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, + 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, + 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, + 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, + 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, +}; + +static uint8_t urtw_8225v2_txpwr_cck[] = { + 0x36, 0x35, 0x2e, 0x25, 0x1c, 0x12, 0x09, 0x04 +}; + +static uint8_t urtw_8225v2_txpwr_cck_ch14[] = { + 0x36, 0x35, 0x2e, 0x1b, 0x00, 0x00, 0x00, 0x00 +}; + +static uint8_t urtw_8225v2b_txpwr_cck[] = { + 0x36, 0x35, 0x2e, 0x25, 0x1c, 0x12, 0x09, 0x04, + 0x30, 0x2f, 0x29, 0x21, 0x19, 0x10, 0x08, 0x03, + 0x2b, 0x2a, 0x25, 0x1e, 0x16, 0x0e, 0x07, 0x03, + 0x26, 0x25, 0x21, 0x1b, 0x14, 0x0d, 0x06, 0x03 +}; + +static uint8_t urtw_8225v2b_txpwr_cck_ch14[] = { + 0x36, 0x35, 0x2e, 0x1b, 0x00, 0x00, 0x00, 0x00, + 0x30, 0x2f, 0x29, 0x15, 0x00, 0x00, 0x00, 0x00, + 0x30, 0x2f, 0x29, 0x15, 0x00, 0x00, 0x00, 0x00, + 0x30, 0x2f, 0x29, 0x15, 0x00, 0x00, 0x00, 0x00 +}; + +static struct urtw_pair urtw_ratetable[] = { + { 2, 0 }, { 4, 1 }, { 11, 2 }, { 12, 4 }, { 18, 5 }, + { 22, 3 }, { 24, 6 }, { 36, 7 }, { 48, 8 }, { 72, 9 }, + { 96, 10 }, { 108, 11 } +}; + +static const uint8_t urtw_8187b_reg_table[][3] = { + { 0xf0, 0x32, 0 }, { 0xf1, 0x32, 0 }, { 0xf2, 0x00, 0 }, + { 0xf3, 0x00, 0 }, { 0xf4, 0x32, 0 }, { 0xf5, 0x43, 0 }, + { 0xf6, 0x00, 0 }, { 0xf7, 0x00, 0 }, { 0xf8, 0x46, 0 }, + { 0xf9, 0xa4, 0 }, { 0xfa, 0x00, 0 }, { 0xfb, 0x00, 0 }, + { 0xfc, 0x96, 0 }, { 0xfd, 0xa4, 0 }, { 0xfe, 0x00, 0 }, + { 0xff, 0x00, 0 }, { 0x58, 0x4b, 1 }, { 0x59, 0x00, 1 }, + { 0x5a, 0x4b, 1 }, { 0x5b, 0x00, 1 }, { 0x60, 0x4b, 1 }, + { 0x61, 0x09, 1 }, { 0x62, 0x4b, 1 }, { 0x63, 0x09, 1 }, + { 0xce, 0x0f, 1 }, { 0xcf, 0x00, 1 }, { 0xe0, 0xff, 1 }, + { 0xe1, 0x0f, 1 }, { 0xe2, 0x00, 1 }, { 0xf0, 0x4e, 1 }, + { 0xf1, 0x01, 1 }, { 0xf2, 0x02, 1 }, { 0xf3, 0x03, 1 }, + { 0xf4, 0x04, 1 }, { 0xf5, 0x05, 1 }, { 0xf6, 0x06, 1 }, + { 0xf7, 0x07, 1 }, { 0xf8, 0x08, 1 }, { 0x4e, 0x00, 2 }, + { 0x0c, 0x04, 2 }, { 0x21, 0x61, 2 }, { 0x22, 0x68, 2 }, + { 0x23, 0x6f, 2 }, { 0x24, 0x76, 2 }, { 0x25, 0x7d, 2 }, + { 0x26, 0x84, 2 }, { 0x27, 0x8d, 2 }, { 0x4d, 0x08, 2 }, + { 0x50, 0x05, 2 }, { 0x51, 0xf5, 2 }, { 0x52, 0x04, 2 }, + { 0x53, 0xa0, 2 }, { 0x54, 0x1f, 2 }, { 0x55, 0x23, 2 }, + { 0x56, 0x45, 2 }, { 0x57, 0x67, 2 }, { 0x58, 0x08, 2 }, + { 0x59, 0x08, 2 }, { 0x5a, 0x08, 2 }, { 0x5b, 0x08, 2 }, + { 0x60, 0x08, 2 }, { 0x61, 0x08, 2 }, { 0x62, 0x08, 2 }, + { 0x63, 0x08, 2 }, { 0x64, 0xcf, 2 }, { 0x72, 0x56, 2 }, + { 0x73, 0x9a, 2 }, { 0x34, 0xf0, 0 }, { 0x35, 0x0f, 0 }, + { 0x5b, 0x40, 0 }, { 0x84, 0x88, 0 }, { 0x85, 0x24, 0 }, + { 0x88, 0x54, 0 }, { 0x8b, 0xb8, 0 }, { 0x8c, 0x07, 0 }, + { 0x8d, 0x00, 0 }, { 0x94, 0x1b, 0 }, { 0x95, 0x12, 0 }, + { 0x96, 0x00, 0 }, { 0x97, 0x06, 0 }, { 0x9d, 0x1a, 0 }, + { 0x9f, 0x10, 0 }, { 0xb4, 0x22, 0 }, { 0xbe, 0x80, 0 }, + { 0xdb, 0x00, 0 }, { 0xee, 0x00, 0 }, { 0x91, 0x03, 0 }, + { 0x4c, 0x00, 2 }, { 0x9f, 0x00, 3 }, { 0x8c, 0x01, 0 }, + { 0x8d, 0x10, 0 }, { 0x8e, 0x08, 0 }, { 0x8f, 0x00, 0 } +}; + +static usb2_callback_t urtw_bulk_rx_callback; +static usb2_callback_t urtw_bulk_tx_callback; + +static const struct usb2_config urtw_8187b_usbconfig[URTW_8187B_N_XFERS] = { + [URTW_8187B_BULK_RX] = { + .type = UE_BULK, + .endpoint = 0x83, + .direction = UE_DIR_IN, + .bufsize = MCLBYTES, + .flags = { + .ext_buffer = 1, + .pipe_bof = 1, + .short_xfer_ok = 1 + }, + .callback = urtw_bulk_rx_callback + }, + [URTW_8187B_BULK_TX_BE] = { + .type = UE_BULK, + .endpoint = URTW_8187B_TXPIPE_BE, + .direction = UE_DIR_OUT, + .bufsize = URTW_TX_MAXSIZE, + .flags = { + .ext_buffer = 1, + .force_short_xfer = 1, + .pipe_bof = 1, + }, + .callback = urtw_bulk_tx_callback, + .timeout = URTW_DATA_TIMEOUT + }, + [URTW_8187B_BULK_TX_BK] = { + .type = UE_BULK, + .endpoint = URTW_8187B_TXPIPE_BK, + .direction = UE_DIR_OUT, + .bufsize = URTW_TX_MAXSIZE, + .flags = { + .ext_buffer = 1, + .force_short_xfer = 1, + .pipe_bof = 1, + }, + .callback = urtw_bulk_tx_callback, + .timeout = URTW_DATA_TIMEOUT + }, + [URTW_8187B_BULK_TX_VI] = { + .type = UE_BULK, + .endpoint = URTW_8187B_TXPIPE_VI, + .direction = UE_DIR_OUT, + .bufsize = URTW_TX_MAXSIZE, + .flags = { + .ext_buffer = 1, + .force_short_xfer = 1, + .pipe_bof = 1, + }, + .callback = urtw_bulk_tx_callback, + .timeout = URTW_DATA_TIMEOUT + }, + [URTW_8187B_BULK_TX_VO] = { + .type = UE_BULK, + .endpoint = URTW_8187B_TXPIPE_VO, + .direction = UE_DIR_OUT, + .bufsize = URTW_TX_MAXSIZE, + .flags = { + .ext_buffer = 1, + .force_short_xfer = 1, + .pipe_bof = 1, + }, + .callback = urtw_bulk_tx_callback, + .timeout = URTW_DATA_TIMEOUT + }, + [URTW_8187B_BULK_TX_EP12] = { + .type = UE_BULK, + .endpoint = 0xc, + .direction = UE_DIR_OUT, + .bufsize = URTW_TX_MAXSIZE, + .flags = { + .ext_buffer = 1, + .force_short_xfer = 1, + .pipe_bof = 1, + }, + .callback = urtw_bulk_tx_callback, + .timeout = URTW_DATA_TIMEOUT + } +}; + +static const struct usb2_config urtw_8187l_usbconfig[URTW_8187L_N_XFERS] = { + [URTW_8187L_BULK_RX] = { + .type = UE_BULK, + .endpoint = 0x81, + .direction = UE_DIR_IN, + .bufsize = MCLBYTES, + .flags = { + .ext_buffer = 1, + .pipe_bof = 1, + .short_xfer_ok = 1 + }, + .callback = urtw_bulk_rx_callback + }, + [URTW_8187L_BULK_TX_LOW] = { + .type = UE_BULK, + .endpoint = 0x2, + .direction = UE_DIR_OUT, + .bufsize = URTW_TX_MAXSIZE, + .flags = { + .ext_buffer = 1, + .force_short_xfer = 1, + .pipe_bof = 1, + }, + .callback = urtw_bulk_tx_callback, + .timeout = URTW_DATA_TIMEOUT + }, + [URTW_8187L_BULK_TX_NORMAL] = { + .type = UE_BULK, + .endpoint = 0x3, + .direction = UE_DIR_OUT, + .bufsize = URTW_TX_MAXSIZE, + .flags = { + .ext_buffer = 1, + .force_short_xfer = 1, + .pipe_bof = 1, + }, + .callback = urtw_bulk_tx_callback, + .timeout = URTW_DATA_TIMEOUT + }, +}; + +static struct ieee80211vap *urtw_vap_create(struct ieee80211com *, + const char name[IFNAMSIZ], int unit, int opmode, + int flags, const uint8_t bssid[IEEE80211_ADDR_LEN], + const uint8_t mac[IEEE80211_ADDR_LEN]); +static void urtw_vap_delete(struct ieee80211vap *); +static void urtw_init(void *); +static void urtw_stop(struct ifnet *, int); +static void urtw_stop_locked(struct ifnet *, int); +static int urtw_ioctl(struct ifnet *, u_long, caddr_t); +static void urtw_start(struct ifnet *); +static int urtw_alloc_rx_data_list(struct urtw_softc *); +static int urtw_alloc_tx_data_list(struct urtw_softc *); +static int urtw_raw_xmit(struct ieee80211_node *, struct mbuf *, + const struct ieee80211_bpf_params *); +static void urtw_scan_start(struct ieee80211com *); +static void urtw_scan_end(struct ieee80211com *); +static void urtw_set_channel(struct ieee80211com *); +static void urtw_update_mcast(struct ifnet *); +static int urtw_tx_start(struct urtw_softc *, + struct ieee80211_node *, struct mbuf *, + struct urtw_data *, int); +static int urtw_newstate(struct ieee80211vap *, + enum ieee80211_state, int); +static void urtw_led_ch(void *); +static void urtw_ledtask(void *, int); +static void urtw_watchdog(void *); +static void urtw_set_multi(void *); +static int urtw_isbmode(uint16_t); +static uint16_t urtw_rate2rtl(int); +static uint16_t urtw_rtl2rate(int); +static usb2_error_t urtw_set_rate(struct urtw_softc *); +static usb2_error_t urtw_update_msr(struct urtw_softc *); +static usb2_error_t urtw_read8_c(struct urtw_softc *, int, uint8_t *); +static usb2_error_t urtw_read16_c(struct urtw_softc *, int, uint16_t *); +static usb2_error_t urtw_read32_c(struct urtw_softc *, int, uint32_t *); +static usb2_error_t urtw_write8_c(struct urtw_softc *, int, uint8_t); +static usb2_error_t urtw_write16_c(struct urtw_softc *, int, uint16_t); +static usb2_error_t urtw_write32_c(struct urtw_softc *, int, uint32_t); +static usb2_error_t urtw_eprom_cs(struct urtw_softc *, int); +static usb2_error_t urtw_eprom_ck(struct urtw_softc *); +static usb2_error_t urtw_eprom_sendbits(struct urtw_softc *, int16_t *, + int); +static usb2_error_t urtw_eprom_read32(struct urtw_softc *, uint32_t, + uint32_t *); +static usb2_error_t urtw_eprom_readbit(struct urtw_softc *, int16_t *); +static usb2_error_t urtw_eprom_writebit(struct urtw_softc *, int16_t); +static usb2_error_t urtw_get_macaddr(struct urtw_softc *); +static usb2_error_t urtw_get_txpwr(struct urtw_softc *); +static usb2_error_t urtw_get_rfchip(struct urtw_softc *); +static usb2_error_t urtw_led_init(struct urtw_softc *); +static usb2_error_t urtw_8185_rf_pins_enable(struct urtw_softc *); +static usb2_error_t urtw_8185_tx_antenna(struct urtw_softc *, uint8_t); +static usb2_error_t urtw_8187_write_phy(struct urtw_softc *, uint8_t, + uint32_t); +static usb2_error_t urtw_8187_write_phy_ofdm_c(struct urtw_softc *, + uint8_t, uint32_t); +static usb2_error_t urtw_8187_write_phy_cck_c(struct urtw_softc *, uint8_t, + uint32_t); +static usb2_error_t urtw_8225_setgain(struct urtw_softc *, int16_t); +static usb2_error_t urtw_8225_usb_init(struct urtw_softc *); +static usb2_error_t urtw_8225_write_c(struct urtw_softc *, uint8_t, + uint16_t); +static usb2_error_t urtw_8225_write_s16(struct urtw_softc *, uint8_t, int, + uint16_t *); +static usb2_error_t urtw_8225_read(struct urtw_softc *, uint8_t, + uint32_t *); +static usb2_error_t urtw_8225_rf_init(struct urtw_softc *); +static usb2_error_t urtw_8225_rf_set_chan(struct urtw_softc *, int); +static usb2_error_t urtw_8225_rf_set_sens(struct urtw_softc *, int); +static usb2_error_t urtw_8225_set_txpwrlvl(struct urtw_softc *, int); +static usb2_error_t urtw_8225_rf_stop(struct urtw_softc *); +static usb2_error_t urtw_8225v2_rf_init(struct urtw_softc *); +static usb2_error_t urtw_8225v2_rf_set_chan(struct urtw_softc *, int); +static usb2_error_t urtw_8225v2_set_txpwrlvl(struct urtw_softc *, int); +static usb2_error_t urtw_8225v2_setgain(struct urtw_softc *, int16_t); +static usb2_error_t urtw_8225_isv2(struct urtw_softc *, int *); +static usb2_error_t urtw_8225v2b_rf_init(struct urtw_softc *); +static usb2_error_t urtw_8225v2b_rf_set_chan(struct urtw_softc *, int); +static usb2_error_t urtw_read8e(struct urtw_softc *, int, uint8_t *); +static usb2_error_t urtw_write8e(struct urtw_softc *, int, uint8_t); +static usb2_error_t urtw_8180_set_anaparam(struct urtw_softc *, uint32_t); +static usb2_error_t urtw_8185_set_anaparam2(struct urtw_softc *, uint32_t); +static usb2_error_t urtw_intr_enable(struct urtw_softc *); +static usb2_error_t urtw_intr_disable(struct urtw_softc *); +static usb2_error_t urtw_reset(struct urtw_softc *); +static usb2_error_t urtw_led_on(struct urtw_softc *, int); +static usb2_error_t urtw_led_ctl(struct urtw_softc *, int); +static usb2_error_t urtw_led_blink(struct urtw_softc *); +static usb2_error_t urtw_led_mode0(struct urtw_softc *, int); +static usb2_error_t urtw_led_mode1(struct urtw_softc *, int); +static usb2_error_t urtw_led_mode2(struct urtw_softc *, int); +static usb2_error_t urtw_led_mode3(struct urtw_softc *, int); +static usb2_error_t urtw_rx_setconf(struct urtw_softc *); +static usb2_error_t urtw_rx_enable(struct urtw_softc *); +static usb2_error_t urtw_tx_enable(struct urtw_softc *sc); +static void urtw_free_tx_data_list(struct urtw_softc *); +static void urtw_free_rx_data_list(struct urtw_softc *); +static void urtw_free_data_list(struct urtw_softc *, + struct urtw_data data[], int, int); +static usb2_error_t urtw_adapter_start(struct urtw_softc *); +static usb2_error_t urtw_adapter_start_b(struct urtw_softc *); +static usb2_error_t urtw_set_mode(struct urtw_softc *, uint32_t); +static usb2_error_t urtw_8187b_cmd_reset(struct urtw_softc *); +static usb2_error_t urtw_write16_i(struct urtw_softc *, int, uint16_t, int); +static usb2_error_t urtw_write8_i(struct urtw_softc *, int, uint8_t, int); +static usb2_error_t urtw_write32_i(struct urtw_softc *, int, uint32_t, int); +static usb2_error_t urtw_do_request(struct urtw_softc *, + struct usb2_device_request *, void *); +static usb2_error_t urtw_8225v2b_set_txpwrlvl(struct urtw_softc *, int); +static usb2_error_t urtw_led_off(struct urtw_softc *, int); +static void urtw_abort_xfers(struct urtw_softc *); +static struct urtw_data * + urtw_getbuf(struct urtw_softc *sc); + +static int +urtw_match(device_t dev) +{ + struct usb2_attach_arg *uaa = device_get_ivars(dev); + + if (uaa->usb_mode != USB_MODE_HOST) + return (ENXIO); + if (uaa->info.bConfigIndex != URTW_CONFIG_INDEX) + return (ENXIO); + if (uaa->info.bIfaceIndex != URTW_IFACE_INDEX) + return (ENXIO); + + return (usb2_lookup_id_by_uaa(urtw_devs, sizeof(urtw_devs), uaa)); +} + +static int +urtw_attach(device_t dev) +{ + const struct usb2_config *setup_start; + int ret = ENXIO; + struct urtw_softc *sc = device_get_softc(dev); + struct usb2_attach_arg *uaa = device_get_ivars(dev); + struct ieee80211com *ic; + struct ifnet *ifp; + uint8_t bands, iface_index = URTW_IFACE_INDEX; /* XXX */ + uint16_t n_setup; + uint32_t data; + usb2_error_t error; + + device_set_usb2_desc(dev); + + sc->sc_dev = dev; + sc->sc_udev = uaa->device; + if (USB_GET_DRIVER_INFO(uaa) == URTW_REV_RTL8187B) + sc->sc_flags |= URTW_RTL8187B; +#ifdef URTW_DEBUG + sc->sc_debug = urtw_debug; +#endif + + mtx_init(&sc->sc_mtx, device_get_nameunit(sc->sc_dev), MTX_NETWORK_LOCK, + MTX_DEF); + usb2_callout_init_mtx(&sc->sc_led_ch, &sc->sc_mtx, 0); + TASK_INIT(&sc->sc_led_task, 0, urtw_ledtask, sc); + callout_init(&sc->sc_watchdog_ch, 0); + + if (sc->sc_flags & URTW_RTL8187B) { + setup_start = urtw_8187b_usbconfig; + n_setup = URTW_8187B_N_XFERS; + } else { + setup_start = urtw_8187l_usbconfig; + n_setup = URTW_8187L_N_XFERS; + } + + error = usb2_transfer_setup(uaa->device, &iface_index, sc->sc_xfer, + setup_start, n_setup, sc, &sc->sc_mtx); + if (error) { + device_printf(dev, "could not allocate USB transfers, " + "err=%s\n", usb2_errstr(error)); + ret = ENXIO; + goto fail0; + } + + URTW_LOCK(sc); + + urtw_read32_m(sc, URTW_RX, &data); + sc->sc_epromtype = (data & URTW_RX_9356SEL) ? URTW_EEPROM_93C56 : + URTW_EEPROM_93C46; + + error = urtw_get_rfchip(sc); + if (error != 0) + goto fail; + error = urtw_get_macaddr(sc); + if (error != 0) + goto fail; + error = urtw_get_txpwr(sc); + if (error != 0) + goto fail; + error = urtw_led_init(sc); + if (error != 0) + goto fail; + + URTW_UNLOCK(sc); + + sc->sc_rts_retry = URTW_DEFAULT_RTS_RETRY; + sc->sc_tx_retry = URTW_DEFAULT_TX_RETRY; + sc->sc_currate = 3; + sc->sc_preamble_mode = urtw_preamble_mode; + + ifp = sc->sc_ifp = if_alloc(IFT_IEEE80211); + if (ifp == NULL) { + device_printf(sc->sc_dev, "can not allocate ifnet\n"); + ret = ENOMEM; + goto fail1; + } + + ifp->if_softc = sc; + if_initname(ifp, "urtw", device_get_unit(sc->sc_dev)); + ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; + ifp->if_init = urtw_init; + ifp->if_ioctl = urtw_ioctl; + ifp->if_start = urtw_start; + /* XXX URTW_TX_DATA_LIST_COUNT */ + IFQ_SET_MAXLEN(&ifp->if_snd, IFQ_MAXLEN); + ifp->if_snd.ifq_drv_maxlen = IFQ_MAXLEN; + IFQ_SET_READY(&ifp->if_snd); + + ic = ifp->if_l2com; + ic->ic_ifp = ifp; + ic->ic_phytype = IEEE80211_T_OFDM; /* not only, but not used */ + ic->ic_opmode = IEEE80211_M_STA; /* default to BSS mode */ + + /* set device capabilities */ + ic->ic_caps = + IEEE80211_C_STA | /* station mode */ + IEEE80211_C_MONITOR | /* monitor mode supported */ + IEEE80211_C_TXPMGT | /* tx power management */ + IEEE80211_C_SHPREAMBLE | /* short preamble supported */ + IEEE80211_C_SHSLOT | /* short slot time supported */ + IEEE80211_C_BGSCAN | /* capable of bg scanning */ + IEEE80211_C_WPA; /* 802.11i */ + + bands = 0; + setbit(&bands, IEEE80211_MODE_11B); + setbit(&bands, IEEE80211_MODE_11G); + ieee80211_init_channels(ic, NULL, &bands); + + ieee80211_ifattach(ic, sc->sc_bssid); + ic->ic_raw_xmit = urtw_raw_xmit; + ic->ic_scan_start = urtw_scan_start; + ic->ic_scan_end = urtw_scan_end; + ic->ic_set_channel = urtw_set_channel; + + ic->ic_vap_create = urtw_vap_create; + ic->ic_vap_delete = urtw_vap_delete; + ic->ic_update_mcast = urtw_update_mcast; + + ieee80211_radiotap_attach(ic, + &sc->sc_txtap.wt_ihdr, sizeof(sc->sc_txtap), + URTW_TX_RADIOTAP_PRESENT, + &sc->sc_rxtap.wr_ihdr, sizeof(sc->sc_rxtap), + URTW_RX_RADIOTAP_PRESENT); + + if (bootverbose) + ieee80211_announce(ic); + return (0); + +fail: URTW_UNLOCK(sc); +fail1: usb2_transfer_unsetup(sc->sc_xfer, (sc->sc_flags & URTW_RTL8187B) ? + URTW_8187B_N_XFERS : URTW_8187L_N_XFERS); +fail0: + return (ret); +} + +static int +urtw_detach(device_t dev) +{ + struct urtw_softc *sc = device_get_softc(dev); + struct ifnet *ifp = sc->sc_ifp; + struct ieee80211com *ic = ifp->if_l2com; + + if (!device_is_attached(dev)) + return (0); + + urtw_stop(ifp, 1); + ieee80211_draintask(ic, &sc->sc_led_task); + + usb2_callout_drain(&sc->sc_led_ch); + callout_drain(&sc->sc_watchdog_ch); + + usb2_transfer_unsetup(sc->sc_xfer, (sc->sc_flags & URTW_RTL8187B) ? + URTW_8187B_N_XFERS : URTW_8187L_N_XFERS); + ieee80211_ifdetach(ic); + + urtw_free_tx_data_list(sc); + urtw_free_rx_data_list(sc); + + if_free(ifp); + mtx_destroy(&sc->sc_mtx); + + return (0); +} + +static void +urtw_free_tx_data_list(struct urtw_softc *sc) +{ + + urtw_free_data_list(sc, sc->sc_tx, URTW_TX_DATA_LIST_COUNT, 0); +} + +static void +urtw_free_rx_data_list(struct urtw_softc *sc) +{ + + urtw_free_data_list(sc, sc->sc_rx, URTW_RX_DATA_LIST_COUNT, 1); +} + +static void +urtw_free_data_list(struct urtw_softc *sc, struct urtw_data data[], int ndata, + int fillmbuf) +{ + int i; + + for (i = 0; i < ndata; i++) { + struct urtw_data *dp = &data[i]; + + if (fillmbuf == 1) { + if (dp->m != NULL) { + m_freem(dp->m); + dp->m = NULL; + dp->buf = NULL; + } + } else { + if (dp->buf != NULL) { + free(dp->buf, M_USBDEV); + dp->buf = NULL; + } + } + if (dp->ni != NULL) { + ieee80211_free_node(dp->ni); + dp->ni = NULL; + } + } +} + +static struct ieee80211vap * +urtw_vap_create(struct ieee80211com *ic, + const char name[IFNAMSIZ], int unit, int opmode, int flags, + const uint8_t bssid[IEEE80211_ADDR_LEN], + const uint8_t mac[IEEE80211_ADDR_LEN]) +{ + struct urtw_vap *uvp; + struct ieee80211vap *vap; + + if (!TAILQ_EMPTY(&ic->ic_vaps)) /* only one at a time */ + return (NULL); + uvp = (struct urtw_vap *) malloc(sizeof(struct urtw_vap), + M_80211_VAP, M_NOWAIT | M_ZERO); + if (uvp == NULL) + return (NULL); + vap = &uvp->vap; + /* enable s/w bmiss handling for sta mode */ + ieee80211_vap_setup(ic, vap, name, unit, opmode, + flags | IEEE80211_CLONE_NOBEACONS, bssid, mac); + + /* override state transition machine */ + uvp->newstate = vap->iv_newstate; + vap->iv_newstate = urtw_newstate; + + /* complete setup */ + ieee80211_vap_attach(vap, ieee80211_media_change, + ieee80211_media_status); + ic->ic_opmode = opmode; + return (vap); +} + +static void +urtw_vap_delete(struct ieee80211vap *vap) +{ + struct urtw_vap *uvp = URTW_VAP(vap); + + ieee80211_vap_detach(vap); + free(uvp, M_80211_VAP); +} + +static void +urtw_init_locked(void *arg) +{ + int ret; + struct urtw_softc *sc = arg; + struct ifnet *ifp = sc->sc_ifp; + usb2_error_t error; + + if (ifp->if_drv_flags & IFF_DRV_RUNNING) + urtw_stop_locked(ifp, 0); + + error = (sc->sc_flags & URTW_RTL8187B) ? urtw_adapter_start_b(sc) : + urtw_adapter_start(sc); + if (error != 0) + goto fail; + + /* reset softc variables */ + sc->sc_txtimer = 0; + + if (!(sc->sc_flags & URTW_INIT_ONCE)) { + ret = urtw_alloc_rx_data_list(sc); + if (error != 0) + goto fail; + ret = urtw_alloc_tx_data_list(sc); + if (error != 0) + goto fail; + sc->sc_flags |= URTW_INIT_ONCE; + } + + error = urtw_rx_enable(sc); + if (error != 0) + goto fail; + error = urtw_tx_enable(sc); + if (error != 0) + goto fail; + + ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; + ifp->if_drv_flags |= IFF_DRV_RUNNING; + + callout_reset(&sc->sc_watchdog_ch, hz, urtw_watchdog, sc); +fail: + return; +} + +static void +urtw_init(void *arg) +{ + struct urtw_softc *sc = arg; + + URTW_LOCK(sc); + urtw_init_locked(arg); + URTW_UNLOCK(sc); +} + +static usb2_error_t +urtw_adapter_start_b(struct urtw_softc *sc) +{ +#define N(a) (sizeof(a) / sizeof((a)[0])) + int i; + uint8_t data8; + usb2_error_t error; + + error = urtw_set_mode(sc, URTW_EPROM_CMD_CONFIG); + if (error) + goto fail; + + urtw_read8_m(sc, URTW_CONFIG3, &data8); + urtw_write8_m(sc, URTW_CONFIG3, + data8 | URTW_CONFIG3_ANAPARAM_WRITE | URTW_CONFIG3_GNT_SELECT); + urtw_write32_m(sc, URTW_ANAPARAM2, URTW_8187B_8225_ANAPARAM2_ON); + urtw_write32_m(sc, URTW_ANAPARAM, URTW_8187B_8225_ANAPARAM_ON); + urtw_write8_m(sc, URTW_ANAPARAM3, URTW_8187B_8225_ANAPARAM3_ON); + + urtw_write8_m(sc, 0x61, 0x10); + urtw_read8_m(sc, 0x62, &data8); + urtw_write8_m(sc, 0x62, data8 & ~(1 << 5)); + urtw_write8_m(sc, 0x62, data8 | (1 << 5)); + + urtw_read8_m(sc, URTW_CONFIG3, &data8); + data8 &= ~URTW_CONFIG3_ANAPARAM_WRITE; + urtw_write8_m(sc, URTW_CONFIG3, data8); + + error = urtw_set_mode(sc, URTW_EPROM_CMD_NORMAL); + if (error) + goto fail; + + error = urtw_8187b_cmd_reset(sc); + if (error) + goto fail; + + urtw_write16_m(sc, 0x2d, 0xfff); + urtw_read8_m(sc, URTW_CW_CONF, &data8); + urtw_write8_m(sc, URTW_CW_CONF, data8 | URTW_CW_CONF_PERPACKET_RETRY); + urtw_read8_m(sc, URTW_TX_AGC_CTL, &data8); + data8 |= URTW_TX_AGC_CTL_PERPACKET_GAIN | + URTW_TX_AGC_CTL_PERPACKET_ANTSEL; + urtw_write8_m(sc, URTW_TX_AGC_CTL, data8); + + error = urtw_write16_i(sc, 0xe0, 0xfff, 1); + if (error) + goto fail; + + urtw_read8_m(sc, URTW_RATE_FALLBACK, &data8); + urtw_write8_m(sc, URTW_RATE_FALLBACK, data8 | URTW_RATE_FALLBACK_ENABLE); + + urtw_write16_m(sc, URTW_ATIM_WND, 2); + urtw_write16_m(sc, URTW_BEACON_INTERVAL, 100); + error = urtw_write16_i(sc, 0xd4, 0xffff, 1); + if (error) + goto fail; + + error = urtw_set_mode(sc, URTW_EPROM_CMD_CONFIG); + if (error) + goto fail; + urtw_read8_m(sc, URTW_CONFIG1, &data8); + urtw_write8_m(sc, URTW_CONFIG1, (data8 & 0x3f) | 0x80); + error = urtw_set_mode(sc, URTW_EPROM_CMD_NORMAL); + if (error) + goto fail; + + urtw_write8_m(sc, URTW_WPA_CONFIG, 0); + for (i = 0; i < N(urtw_8187b_reg_table); i++) { + error = urtw_write8_i(sc, urtw_8187b_reg_table[i][0], + urtw_8187b_reg_table[i][1], urtw_8187b_reg_table[i][2]); + if (error) + goto fail; + } + + urtw_write16_m(sc, URTW_TID_AC_MAP, 0xfa50); + urtw_write16_m(sc, URTW_INT_MIG, 0); + + error = urtw_write32_i(sc, 0xf0, 0, 1); + if (error) + goto fail; + error = urtw_write32_i(sc, 0xf4, 0, 1); + if (error) + goto fail; + error = urtw_write8_i(sc, 0xf8, 0, 1); + if (error) + goto fail; + + urtw_write32_m(sc, URTW_RF_TIMING, 0x00004001); + + error = urtw_write16_i(sc, 0x72, 0x569a, 2); + if (error) + goto fail; + + error = urtw_set_mode(sc, URTW_EPROM_CMD_CONFIG); + if (error) + goto fail; + urtw_read8_m(sc, URTW_CONFIG3, &data8); + urtw_write8_m(sc, URTW_CONFIG3, data8 | URTW_CONFIG3_ANAPARAM_WRITE); + error = urtw_set_mode(sc, URTW_EPROM_CMD_NORMAL); + if (error) + goto fail; + + urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, 0x0480); + urtw_write16_m(sc, URTW_RF_PINS_SELECT, 0x2488); + urtw_write16_m(sc, URTW_RF_PINS_ENABLE, 0x1fff); + usb2_pause_mtx(&sc->sc_mtx, 100); + + error = sc->sc_rf_init(sc); + if (error != 0) + goto fail; + + error = urtw_intr_enable(sc); + if (error) + goto fail; + + error = urtw_write8e(sc, 0x41, 0xf4); + if (error) + goto fail; + error = urtw_write8e(sc, 0x40, 0x00); + if (error) + goto fail; + error = urtw_write8e(sc, 0x42, 0x00); + if (error) + goto fail; + error = urtw_write8e(sc, 0x42, 0x01); + if (error) + goto fail; + error = urtw_write8e(sc, 0x40, 0x0f); + if (error) + goto fail; + error = urtw_write8e(sc, 0x42, 0x00); + if (error) + goto fail; + error = urtw_write8e(sc, 0x42, 0x01); + if (error) + goto fail; + + urtw_read8_m(sc, 0xdb, &data8); + urtw_write8_m(sc, 0xdb, data8 | (1 << 2)); + error = urtw_write16_i(sc, 0x72, 0x59fa, 3); + if (error) + goto fail; + error = urtw_write16_i(sc, 0x74, 0x59d2, 3); + if (error) + goto fail; + error = urtw_write16_i(sc, 0x76, 0x59d2, 3); + if (error) + goto fail; + error = urtw_write16_i(sc, 0x78, 0x19fa, 3); + if (error) + goto fail; + error = urtw_write16_i(sc, 0x7a, 0x19fa, 3); + if (error) + goto fail; + error = urtw_write16_i(sc, 0x7c, 0x00d0, 3); + if (error) + goto fail; + urtw_write8_m(sc, 0x61, 0); + error = urtw_write8_i(sc, 0x80, 0x0f, 1); + if (error) + goto fail; + error = urtw_write8_i(sc, 0x83, 0x03, 1); + if (error) + goto fail; + urtw_write8_m(sc, 0xda, 0x10); + error = urtw_write8_i(sc, 0x4d, 0x08, 2); + if (error) + goto fail; + + urtw_write32_m(sc, URTW_HSSI_PARA, 0x0600321B); + + error = urtw_write16_i(sc, 0xec, 0x800, 1); + if (error) + goto fail; + +fail: + return (error); +#undef N +} + +static usb2_error_t +urtw_adapter_start(struct urtw_softc *sc) +{ + usb2_error_t error; + + error = urtw_reset(sc); + if (error) + goto fail; + + urtw_write8_m(sc, URTW_ADDR_MAGIC1, 0); + urtw_write8_m(sc, URTW_GPIO, 0); + + /* for led */ + urtw_write8_m(sc, URTW_ADDR_MAGIC1, 4); + error = urtw_led_ctl(sc, URTW_LED_CTL_POWER_ON); + if (error != 0) + goto fail; + + error = urtw_set_mode(sc, URTW_EPROM_CMD_CONFIG); + if (error) + goto fail; + /* applying MAC address again. */ + urtw_write32_m(sc, URTW_MAC0, ((uint32_t *)sc->sc_bssid)[0]); + urtw_write16_m(sc, URTW_MAC4, ((uint32_t *)sc->sc_bssid)[1] & 0xffff); + error = urtw_set_mode(sc, URTW_EPROM_CMD_NORMAL); + if (error) + goto fail; + + error = urtw_update_msr(sc); + if (error) + goto fail; + + urtw_write32_m(sc, URTW_INT_TIMEOUT, 0); + urtw_write8_m(sc, URTW_WPA_CONFIG, 0); + urtw_write8_m(sc, URTW_RATE_FALLBACK, URTW_RATE_FALLBACK_ENABLE | 0x1); + error = urtw_set_rate(sc); + if (error != 0) + goto fail; + + error = sc->sc_rf_init(sc); + if (error != 0) + goto fail; + if (sc->sc_rf_set_sens != NULL) + sc->sc_rf_set_sens(sc, sc->sc_sens); + + /* XXX correct? to call write16 */ + urtw_write16_m(sc, URTW_PSR, 1); + urtw_write16_m(sc, URTW_ADDR_MAGIC2, 0x10); + urtw_write8_m(sc, URTW_TALLY_SEL, 0x80); + urtw_write8_m(sc, URTW_ADDR_MAGIC3, 0x60); + /* XXX correct? to call write16 */ + urtw_write16_m(sc, URTW_PSR, 0); + urtw_write8_m(sc, URTW_ADDR_MAGIC1, 4); + + error = urtw_intr_enable(sc); + if (error != 0) + goto fail; + +fail: + return (error); +} + +static usb2_error_t +urtw_set_mode(struct urtw_softc *sc, uint32_t mode) +{ + uint8_t data; + usb2_error_t error; + + urtw_read8_m(sc, URTW_EPROM_CMD, &data); + data = (data & ~URTW_EPROM_CMD_MASK) | (mode << URTW_EPROM_CMD_SHIFT); + data = data & ~(URTW_EPROM_CS | URTW_EPROM_CK); + urtw_write8_m(sc, URTW_EPROM_CMD, data); +fail: + return (error); +} + +static usb2_error_t +urtw_8187b_cmd_reset(struct urtw_softc *sc) +{ + int i; + uint8_t data8; + usb2_error_t error; + + /* XXX the code can be duplicate with urtw_reset(). */ + urtw_read8_m(sc, URTW_CMD, &data8); + data8 = (data8 & 0x2) | URTW_CMD_RST; + urtw_write8_m(sc, URTW_CMD, data8); + + for (i = 0; i < 20; i++) { + usb2_pause_mtx(&sc->sc_mtx, 2); + urtw_read8_m(sc, URTW_CMD, &data8); + if (!(data8 & URTW_CMD_RST)) + break; + } + if (i >= 20) { + device_printf(sc->sc_dev, "reset timeout\n"); + goto fail; + } + + error = urtw_set_mode(sc, URTW_EPROM_CMD_LOAD); + if (error) + goto fail; + + for (i = 0; i < 20; i++) { + usb2_pause_mtx(&sc->sc_mtx, 4); + urtw_read8_m(sc, URTW_EPROM_CMD, &data8); + if (!(data8 & URTW_EPROM_CMD_CONFIG)) + break; + } + if (i >= 20) { + device_printf(sc->sc_dev, "eeprom reset timeout\n"); + goto fail; + } + +fail: + return (error); +} + +static usb2_error_t +urtw_write16_i(struct urtw_softc *sc, int val, uint16_t data, int idx) +{ + struct usb2_device_request req; + + req.bmRequestType = UT_WRITE_VENDOR_DEVICE; + req.bRequest = URTW_8187_SETREGS_REQ; + USETW(req.wValue, val | 0xff00); + USETW(req.wIndex, idx & 0x3); + USETW(req.wLength, sizeof(uint16_t)); + + return (urtw_do_request(sc, &req, &data)); +} + +static usb2_error_t +urtw_do_request(struct urtw_softc *sc, + struct usb2_device_request *req, void *data) +{ + usb2_error_t err; + int ntries = 10; + + URTW_ASSERT_LOCKED(sc); + + while (ntries--) { + err = usb2_do_request_flags(sc->sc_udev, &sc->sc_mtx, + req, data, 0, NULL, 250 /* ms */); + if (err == 0) + break; + + DPRINTF(sc, URTW_DEBUG_INIT, + "Control request failed, %s (retrying)\n", + usb2_errstr(err)); + usb2_pause_mtx(&sc->sc_mtx, hz / 100); + } + return (err); +} + +static usb2_error_t +urtw_write8_i(struct urtw_softc *sc, int val, uint8_t data, int idx) +{ + struct usb2_device_request req; + + req.bmRequestType = UT_WRITE_VENDOR_DEVICE; + req.bRequest = URTW_8187_SETREGS_REQ; + USETW(req.wValue, val | 0xff00); + USETW(req.wIndex, idx & 0x3); + USETW(req.wLength, sizeof(uint8_t)); + + return (urtw_do_request(sc, &req, &data)); +} + +static usb2_error_t +urtw_write32_i(struct urtw_softc *sc, int val, uint32_t data, int idx) +{ + struct usb2_device_request req; + + req.bmRequestType = UT_WRITE_VENDOR_DEVICE; + req.bRequest = URTW_8187_SETREGS_REQ; + USETW(req.wValue, val | 0xff00); + USETW(req.wIndex, idx & 0x3); + USETW(req.wLength, sizeof(uint32_t)); + + return (urtw_do_request(sc, &req, &data)); +} + +static void +urtw_stop_locked(struct ifnet *ifp, int disable) +{ + struct urtw_softc *sc = ifp->if_softc; + uint8_t data8; + usb2_error_t error; + + (void)disable; + ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); + + error = urtw_intr_disable(sc); + if (error) + goto fail; + urtw_read8_m(sc, URTW_CMD, &data8); + data8 &= ~(URTW_CMD_RX_ENABLE | URTW_CMD_TX_ENABLE); + urtw_write8_m(sc, URTW_CMD, data8); + + error = sc->sc_rf_stop(sc); + if (error != 0) + goto fail; + + error = urtw_set_mode(sc, URTW_EPROM_CMD_CONFIG); + if (error) + goto fail; + urtw_read8_m(sc, URTW_CONFIG4, &data8); + urtw_write8_m(sc, URTW_CONFIG4, data8 | URTW_CONFIG4_VCOOFF); + error = urtw_set_mode(sc, URTW_EPROM_CMD_NORMAL); + if (error) + goto fail; +fail: + if (error) + device_printf(sc->sc_dev, "failed to stop (%s)\n", + usb2_errstr(error)); + + usb2_callout_stop(&sc->sc_led_ch); + callout_stop(&sc->sc_watchdog_ch); + + urtw_abort_xfers(sc); +} + +static void +urtw_stop(struct ifnet *ifp, int disable) +{ + struct urtw_softc *sc = ifp->if_softc; + + URTW_LOCK(sc); + urtw_stop_locked(ifp, disable); + URTW_UNLOCK(sc); +} + +static void +urtw_abort_xfers(struct urtw_softc *sc) +{ + int i, max; + + URTW_ASSERT_LOCKED(sc); + + max = (sc->sc_flags & URTW_RTL8187B) ? URTW_8187B_N_XFERS : + URTW_8187L_N_XFERS; + + /* abort any pending transfers */ + for (i = 0; i < max; i++) + usb2_transfer_stop(sc->sc_xfer[i]); +} + +static int +urtw_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) +{ + struct urtw_softc *sc = ifp->if_softc; + struct ieee80211com *ic = ifp->if_l2com; + struct ifreq *ifr = (struct ifreq *) data; + int error = 0, startall = 0; + + switch (cmd) { + case SIOCSIFFLAGS: + if (ifp->if_flags & IFF_UP) { + if (ifp->if_drv_flags & IFF_DRV_RUNNING) { + if ((ifp->if_flags ^ sc->sc_if_flags) & + (IFF_ALLMULTI | IFF_PROMISC)) + urtw_set_multi(sc); + } else { + urtw_init(ifp->if_softc); + startall = 1; + } + } else { + if (ifp->if_drv_flags & IFF_DRV_RUNNING) + urtw_stop(ifp, 1); + } + sc->sc_if_flags = ifp->if_flags; + if (startall) + ieee80211_start_all(ic); + break; + case SIOCGIFMEDIA: + error = ifmedia_ioctl(ifp, ifr, &ic->ic_media, cmd); + break; + case SIOCGIFADDR: + error = ether_ioctl(ifp, cmd, data); + break; + default: + error = EINVAL; + break; + } + + return (error); +} + +static void +urtw_start(struct ifnet *ifp) +{ + struct urtw_data *bf; + struct urtw_softc *sc = ifp->if_softc; + struct ieee80211_node *ni; + struct mbuf *m; + + if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) + return; + + URTW_LOCK(sc); + for (;;) { + IFQ_DRV_DEQUEUE(&ifp->if_snd, m); + if (m == NULL) + break; + bf = urtw_getbuf(sc); + if (bf == NULL) { + IFQ_DRV_PREPEND(&ifp->if_snd, m); + break; + } + + ni = (struct ieee80211_node *)m->m_pkthdr.rcvif; + m->m_pkthdr.rcvif = NULL; + + if (urtw_tx_start(sc, ni, m, bf, URTW_PRIORITY_NORMAL) != 0) { + ifp->if_oerrors++; + STAILQ_INSERT_HEAD(&sc->sc_tx_inactive, bf, next); + ieee80211_free_node(ni); + break; + } + + sc->sc_txtimer = 5; + } + URTW_UNLOCK(sc); +} + +static int +urtw_alloc_data_list(struct urtw_softc *sc, struct urtw_data data[], + int ndata, int maxsz, int fillmbuf) +{ + int i, error; + + for (i = 0; i < ndata; i++) { + struct urtw_data *dp = &data[i]; + + dp->sc = sc; + if (fillmbuf) { + dp->m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR); + if (dp->m == NULL) { + device_printf(sc->sc_dev, + "could not allocate rx mbuf\n"); + error = ENOMEM; + goto fail; + } + dp->buf = mtod(dp->m, uint8_t *); + } else { + dp->m = NULL; + dp->buf = malloc(maxsz, M_USBDEV, M_NOWAIT); + if (dp->buf == NULL) { + device_printf(sc->sc_dev, + "could not allocate buffer\n"); + error = ENOMEM; + goto fail; + } + if (((unsigned long)dp->buf) % 4) + device_printf(sc->sc_dev, + "warn: unaligned buffer %p\n", dp->buf); + } + dp->ni = NULL; + } + + return 0; + +fail: urtw_free_data_list(sc, data, ndata, fillmbuf); + return error; +} + +static int +urtw_alloc_rx_data_list(struct urtw_softc *sc) +{ + int error, i; + + error = urtw_alloc_data_list(sc, + sc->sc_rx, URTW_RX_DATA_LIST_COUNT, MCLBYTES, 1 /* mbufs */); + if (error != 0) + return (error); + + STAILQ_INIT(&sc->sc_rx_active); + STAILQ_INIT(&sc->sc_rx_inactive); + + for (i = 0; i < URTW_RX_DATA_LIST_COUNT; i++) + STAILQ_INSERT_HEAD(&sc->sc_rx_inactive, &sc->sc_rx[i], next); + + return (0); +} + +static int +urtw_alloc_tx_data_list(struct urtw_softc *sc) +{ + int error, i; + + error = urtw_alloc_data_list(sc, + sc->sc_tx, URTW_TX_DATA_LIST_COUNT, URTW_TX_MAXSIZE, + 0 /* no mbufs */); + if (error != 0) + return (error); + + STAILQ_INIT(&sc->sc_tx_active); + STAILQ_INIT(&sc->sc_tx_inactive); + STAILQ_INIT(&sc->sc_tx_pending); + + for (i = 0; i < URTW_TX_DATA_LIST_COUNT; i++) + STAILQ_INSERT_HEAD(&sc->sc_tx_inactive, &sc->sc_tx[i], + next); + + return (0); +} + +static int +urtw_raw_xmit(struct ieee80211_node *ni, struct mbuf *m, + const struct ieee80211_bpf_params *params) +{ + struct ieee80211com *ic = ni->ni_ic; + struct ifnet *ifp = ic->ic_ifp; + struct urtw_data *bf; + struct urtw_softc *sc = ifp->if_softc; + + /* prevent management frames from being sent if we're not ready */ + if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) { + m_freem(m); + ieee80211_free_node(ni); + return ENETDOWN; + } + URTW_LOCK(sc); + bf = urtw_getbuf(sc); + if (bf == NULL) { + ieee80211_free_node(ni); + m_freem(m); + URTW_UNLOCK(sc); + return (ENOBUFS); /* XXX */ + } + + ifp->if_opackets++; + if (urtw_tx_start(sc, ni, m, bf, URTW_PRIORITY_LOW) != 0) { + ieee80211_free_node(ni); + ifp->if_oerrors++; + STAILQ_INSERT_HEAD(&sc->sc_tx_inactive, bf, next); + URTW_UNLOCK(sc); + return (EIO); + } + URTW_UNLOCK(sc); + + sc->sc_txtimer = 5; + return (0); +} + +static void +urtw_scan_start(struct ieee80211com *ic) +{ + + /* XXX do nothing? */ +} + +static void +urtw_scan_end(struct ieee80211com *ic) +{ + + /* XXX do nothing? */ +} + +static void +urtw_set_channel(struct ieee80211com *ic) +{ + struct urtw_softc *sc = ic->ic_ifp->if_softc; + struct ifnet *ifp = sc->sc_ifp; + uint32_t data, orig; + usb2_error_t error; + + /* + * if the user set a channel explicitly using ifconfig(8) this function + * can be called earlier than we're expected that in some cases the + * initialization would be failed if setting a channel is called before + * the init have done. + */ + if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) + return; + + if (sc->sc_curchan != NULL && sc->sc_curchan == ic->ic_curchan) + return; + + URTW_LOCK(sc); + + /* + * during changing th channel we need to temporarily be disable + * TX. + */ + urtw_read32_m(sc, URTW_TX_CONF, &orig); + data = orig & ~URTW_TX_LOOPBACK_MASK; + urtw_write32_m(sc, URTW_TX_CONF, data | URTW_TX_LOOPBACK_MAC); + + error = sc->sc_rf_set_chan(sc, ieee80211_chan2ieee(ic, ic->ic_curchan)); + if (error != 0) + goto fail; + usb2_pause_mtx(&sc->sc_mtx, 10); + urtw_write32_m(sc, URTW_TX_CONF, orig); + + urtw_write16_m(sc, URTW_ATIM_WND, 2); + urtw_write16_m(sc, URTW_ATIM_TR_ITV, 100); + urtw_write16_m(sc, URTW_BEACON_INTERVAL, 100); + urtw_write16_m(sc, URTW_BEACON_INTERVAL_TIME, 100); + +fail: + URTW_UNLOCK(sc); + + sc->sc_curchan = ic->ic_curchan; + + if (error != 0) + device_printf(sc->sc_dev, "could not change the channel\n"); +} + +static void +urtw_update_mcast(struct ifnet *ifp) +{ + + /* XXX do nothing? */ +} + +static int +urtw_tx_start(struct urtw_softc *sc, struct ieee80211_node *ni, struct mbuf *m0, + struct urtw_data *data, int prior) +{ + int xferlen; + struct ifnet *ifp = sc->sc_ifp; + struct ieee80211_frame *wh = mtod(m0, struct ieee80211_frame *); + struct ieee80211_key *k; + const struct ieee80211_txparam *tp; + struct ieee80211com *ic = ifp->if_l2com; + struct ieee80211vap *vap = ni->ni_vap; + struct urtw_8187b_txhdr *hdr; + struct usb2_xfer *rtl8187b_pipes[URTW_8187B_TXPIPE_MAX] = { + sc->sc_xfer[URTW_8187B_BULK_TX_BE], + sc->sc_xfer[URTW_8187B_BULK_TX_BK], + sc->sc_xfer[URTW_8187B_BULK_TX_VI], + sc->sc_xfer[URTW_8187B_BULK_TX_VO] + }; + struct usb2_xfer *xfer; + usb2_error_t error; + + URTW_ASSERT_LOCKED(sc); + + /* + * Software crypto. + */ + if (wh->i_fc[1] & IEEE80211_FC1_WEP) { + k = ieee80211_crypto_encap(ni, m0); + if (k == NULL) { + device_printf(sc->sc_dev, + "ieee80211_crypto_encap returns NULL.\n"); + /* XXX we don't expect the fragmented frames */ + m_freem(m0); + return (ENOBUFS); + } + + /* in case packet header moved, reset pointer */ + wh = mtod(m0, struct ieee80211_frame *); + } + + if (ieee80211_radiotap_active_vap(vap)) { + struct urtw_tx_radiotap_header *tap = &sc->sc_txtap; + + /* XXX Are variables correct? */ + tap->wt_flags = 0; + tap->wt_chan_freq = htole16(ic->ic_curchan->ic_freq); + tap->wt_chan_flags = htole16(ic->ic_curchan->ic_flags); + + ieee80211_radiotap_tx(vap, m0); + } + + xferlen = m0->m_pkthdr.len; + xferlen += (sc->sc_flags & URTW_RTL8187B) ? (4 * 8) : (4 * 3); + if ((0 == xferlen % 64) || (0 == xferlen % 512)) + xferlen += 1; + + bzero(data->buf, URTW_TX_MAXSIZE); + data->buf[0] = m0->m_pkthdr.len & 0xff; + data->buf[1] = (m0->m_pkthdr.len & 0x0f00) >> 8; + data->buf[1] |= (1 << 7); + + if ((ic->ic_flags & IEEE80211_F_SHPREAMBLE) && + (ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_PREAMBLE) && + (sc->sc_preamble_mode == URTW_PREAMBLE_MODE_SHORT) && + (sc->sc_currate != 0)) + data->buf[2] |= 1; + if ((m0->m_pkthdr.len > vap->iv_rtsthreshold) && + prior == URTW_PRIORITY_LOW) { + device_printf(sc->sc_dev, "TODO tx.\n"); + return (EIO); + } + if (wh->i_fc[1] & IEEE80211_FC1_MORE_FRAG) + data->buf[2] |= (1 << 1); + /* RTS rate - 10 means we use a basic rate. */ + data->buf[2] |= (urtw_rate2rtl(2) << 3); + /* + * XXX currently TX rate control depends on the rate value of + * RX descriptor because I don't know how to we can control TX rate + * in more smart way. Please fix me you find a thing. + */ + data->buf[3] = sc->sc_currate; + if (prior == URTW_PRIORITY_NORMAL) { + tp = &vap->iv_txparms[ieee80211_chan2mode(ni->ni_chan)]; + if (IEEE80211_IS_MULTICAST(wh->i_addr1)) + data->buf[3] = urtw_rate2rtl(tp->mcastrate); + else if (tp->ucastrate != IEEE80211_FIXED_RATE_NONE) + data->buf[3] = urtw_rate2rtl(tp->ucastrate); + } + + if (sc->sc_flags & URTW_RTL8187B) { + hdr = (struct urtw_8187b_txhdr *)data->buf; + hdr->rts_duration = 0; + hdr->len = 0; + hdr->retry = 3 | (7 << 4) | 11; + hdr->tx_duration = ieee80211_compute_duration(ic->ic_rt, + m0->m_pkthdr.len + IEEE80211_CRC_LEN, + urtw_rtl2rate(data->buf[3]), + (ic->ic_flags & IEEE80211_F_SHPREAMBLE) != 0); + /* XXX MUST fill another variables like rts_duration, tx_.. */ + m_copydata(m0, 0, m0->m_pkthdr.len, (uint8_t *)&data->buf[32]); + } else { + data->buf[8] = 3; /* CW minimum */ + data->buf[8] |= (7 << 4); /* CW maximum */ + data->buf[9] |= 11; /* retry limitation */ + m_copydata(m0, 0, m0->m_pkthdr.len, (uint8_t *)&data->buf[12]); + } + + data->buflen = xferlen; + data->ni = ni; + data->m = m0; + + if (sc->sc_flags & URTW_RTL8187B) { + switch (wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) { + case IEEE80211_FC0_TYPE_CTL: + case IEEE80211_FC0_TYPE_MGT: + xfer = sc->sc_xfer[URTW_8187B_BULK_TX_EP12]; + break; + default: + KASSERT(M_WME_GETAC(m0) < URTW_8187B_TXPIPE_MAX, + ("unsupported WME pipe %d", M_WME_GETAC(m0))); + xfer = rtl8187b_pipes[M_WME_GETAC(m0)]; + break; + } + } else + xfer = (prior == URTW_PRIORITY_LOW) ? + sc->sc_xfer[URTW_8187L_BULK_TX_LOW] : + sc->sc_xfer[URTW_8187L_BULK_TX_NORMAL]; + + STAILQ_INSERT_TAIL(&sc->sc_tx_pending, data, next); + usb2_transfer_start(xfer); + + error = urtw_led_ctl(sc, URTW_LED_CTL_TX); + if (error != 0) + device_printf(sc->sc_dev, "could not control LED (%d)\n", + error); + return (0); +} + +static int +urtw_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) +{ + struct ieee80211_node *ni = vap->iv_bss; + struct ieee80211com *ic = vap->iv_ic; + struct urtw_softc *sc = ic->ic_ifp->if_softc; + struct urtw_vap *uvp = URTW_VAP(vap); + usb2_error_t error = 0; + + DPRINTF(sc, URTW_DEBUG_STATE, "%s: %s -> %s\n", __func__, + ieee80211_state_name[vap->iv_state], + ieee80211_state_name[nstate]); + + sc->sc_state = nstate; + + IEEE80211_UNLOCK(ic); + URTW_LOCK(sc); + usb2_callout_stop(&sc->sc_led_ch); + callout_stop(&sc->sc_watchdog_ch); + + switch (nstate) { + case IEEE80211_S_INIT: + case IEEE80211_S_SCAN: + case IEEE80211_S_AUTH: + case IEEE80211_S_ASSOC: + break; + case IEEE80211_S_RUN: + /* setting bssid. */ + urtw_write32_m(sc, URTW_BSSID, ((uint32_t *)ni->ni_bssid)[0]); + urtw_write16_m(sc, URTW_BSSID + 4, + ((uint16_t *)ni->ni_bssid)[2]); + urtw_update_msr(sc); + /* XXX maybe the below would be incorrect. */ + urtw_write16_m(sc, URTW_ATIM_WND, 2); + urtw_write16_m(sc, URTW_ATIM_TR_ITV, 100); + urtw_write16_m(sc, URTW_BEACON_INTERVAL, 0x64); + urtw_write16_m(sc, URTW_BEACON_INTERVAL_TIME, 100); + error = urtw_led_ctl(sc, URTW_LED_CTL_LINK); + if (error != 0) + device_printf(sc->sc_dev, + "could not control LED (%d)\n", error); + break; + default: + break; + } +fail: + URTW_UNLOCK(sc); + IEEE80211_LOCK(ic); + return (uvp->newstate(vap, nstate, arg)); +} + +static void +urtw_watchdog(void *arg) +{ + struct urtw_softc *sc = arg; + struct ifnet *ifp = sc->sc_ifp; + + if (sc->sc_txtimer > 0) { + if (--sc->sc_txtimer == 0) { + device_printf(sc->sc_dev, "device timeout\n"); + ifp->if_oerrors++; + return; + } + callout_reset(&sc->sc_watchdog_ch, hz, urtw_watchdog, sc); + } +} + +static void +urtw_set_multi(void *arg) +{ + struct urtw_softc *sc = arg; + struct ifnet *ifp = sc->sc_ifp; + + if (!(ifp->if_flags & IFF_UP)) + return; + + /* + * XXX don't know how to set a device. Lack of docs. Just try to set + * IFF_ALLMULTI flag here. + */ + IF_ADDR_LOCK(ifp); + ifp->if_flags |= IFF_ALLMULTI; + IF_ADDR_UNLOCK(ifp); +} + +static usb2_error_t +urtw_set_rate(struct urtw_softc *sc) +{ + int i, basic_rate, min_rr_rate, max_rr_rate; + uint16_t data; + usb2_error_t error; + + basic_rate = urtw_rate2rtl(48); + min_rr_rate = urtw_rate2rtl(12); + max_rr_rate = urtw_rate2rtl(48); + + urtw_write8_m(sc, URTW_RESP_RATE, + max_rr_rate << URTW_RESP_MAX_RATE_SHIFT | + min_rr_rate << URTW_RESP_MIN_RATE_SHIFT); + + urtw_read16_m(sc, URTW_BRSR, &data); + data &= ~URTW_BRSR_MBR_8185; + + for (i = 0; i <= basic_rate; i++) + data |= (1 << i); + + urtw_write16_m(sc, URTW_BRSR, data); +fail: + return (error); +} + +static uint16_t +urtw_rate2rtl(int rate) +{ +#define N(a) (sizeof(a) / sizeof((a)[0])) + int i; + + for (i = 0; i < N(urtw_ratetable); i++) { + if (rate == urtw_ratetable[i].reg) + return urtw_ratetable[i].val; + } + + return (3); +#undef N +} + +static uint16_t +urtw_rtl2rate(int rate) +{ +#define N(a) (sizeof(a) / sizeof((a)[0])) + int i; + + for (i = 0; i < N(urtw_ratetable); i++) { + if (rate == urtw_ratetable[i].val) + return urtw_ratetable[i].reg; + } + + return (0); +#undef N +} + +static usb2_error_t +urtw_update_msr(struct urtw_softc *sc) +{ + struct ifnet *ifp = sc->sc_ifp; + struct ieee80211com *ic = ifp->if_l2com; + uint8_t data; + usb2_error_t error; + + urtw_read8_m(sc, URTW_MSR, &data); + data &= ~URTW_MSR_LINK_MASK; + + if (sc->sc_state == IEEE80211_S_RUN) { + switch (ic->ic_opmode) { + case IEEE80211_M_STA: + case IEEE80211_M_MONITOR: + data |= URTW_MSR_LINK_STA; + if (sc->sc_flags & URTW_RTL8187B) + data |= URTW_MSR_LINK_ENEDCA; + break; + case IEEE80211_M_IBSS: + data |= URTW_MSR_LINK_ADHOC; + break; + case IEEE80211_M_HOSTAP: + data |= URTW_MSR_LINK_HOSTAP; + break; + default: + panic("unsupported operation mode 0x%x\n", + ic->ic_opmode); + /* never reach */ + } + } else + data |= URTW_MSR_LINK_NONE; + + urtw_write8_m(sc, URTW_MSR, data); +fail: + return (error); +} + +static usb2_error_t +urtw_read8_c(struct urtw_softc *sc, int val, uint8_t *data) +{ + struct usb2_device_request req; + usb2_error_t error; + + URTW_ASSERT_LOCKED(sc); + + req.bmRequestType = UT_READ_VENDOR_DEVICE; + req.bRequest = URTW_8187_GETREGS_REQ; + USETW(req.wValue, val | 0xff00); + USETW(req.wIndex, 0); + USETW(req.wLength, sizeof(uint8_t)); + + error = urtw_do_request(sc, &req, data); + return (error); +} + +static usb2_error_t +urtw_read16_c(struct urtw_softc *sc, int val, uint16_t *data) +{ + struct usb2_device_request req; + usb2_error_t error; + + URTW_ASSERT_LOCKED(sc); + + req.bmRequestType = UT_READ_VENDOR_DEVICE; + req.bRequest = URTW_8187_GETREGS_REQ; + USETW(req.wValue, val | 0xff00); + USETW(req.wIndex, 0); + USETW(req.wLength, sizeof(uint16_t)); + + error = urtw_do_request(sc, &req, data); + return (error); +} + +static usb2_error_t +urtw_read32_c(struct urtw_softc *sc, int val, uint32_t *data) +{ + struct usb2_device_request req; + usb2_error_t error; + + URTW_ASSERT_LOCKED(sc); + + req.bmRequestType = UT_READ_VENDOR_DEVICE; + req.bRequest = URTW_8187_GETREGS_REQ; + USETW(req.wValue, val | 0xff00); + USETW(req.wIndex, 0); + USETW(req.wLength, sizeof(uint32_t)); + + error = urtw_do_request(sc, &req, data); + return (error); +} + +static usb2_error_t +urtw_write8_c(struct urtw_softc *sc, int val, uint8_t data) +{ + struct usb2_device_request req; + + URTW_ASSERT_LOCKED(sc); + + req.bmRequestType = UT_WRITE_VENDOR_DEVICE; + req.bRequest = URTW_8187_SETREGS_REQ; + USETW(req.wValue, val | 0xff00); + USETW(req.wIndex, 0); + USETW(req.wLength, sizeof(uint8_t)); + + return (urtw_do_request(sc, &req, &data)); +} + +static usb2_error_t +urtw_write16_c(struct urtw_softc *sc, int val, uint16_t data) +{ + struct usb2_device_request req; + + URTW_ASSERT_LOCKED(sc); + + req.bmRequestType = UT_WRITE_VENDOR_DEVICE; + req.bRequest = URTW_8187_SETREGS_REQ; + USETW(req.wValue, val | 0xff00); + USETW(req.wIndex, 0); + USETW(req.wLength, sizeof(uint16_t)); + + return (urtw_do_request(sc, &req, &data)); +} + +static usb2_error_t +urtw_write32_c(struct urtw_softc *sc, int val, uint32_t data) +{ + struct usb2_device_request req; + + URTW_ASSERT_LOCKED(sc); + + req.bmRequestType = UT_WRITE_VENDOR_DEVICE; + req.bRequest = URTW_8187_SETREGS_REQ; + USETW(req.wValue, val | 0xff00); + USETW(req.wIndex, 0); + USETW(req.wLength, sizeof(uint32_t)); + + return (urtw_do_request(sc, &req, &data)); +} + +static usb2_error_t +urtw_get_macaddr(struct urtw_softc *sc) +{ + uint32_t data; + usb2_error_t error; + + error = urtw_eprom_read32(sc, URTW_EPROM_MACADDR, &data); + if (error != 0) + goto fail; + sc->sc_bssid[0] = data & 0xff; + sc->sc_bssid[1] = (data & 0xff00) >> 8; + error = urtw_eprom_read32(sc, URTW_EPROM_MACADDR + 1, &data); + if (error != 0) + goto fail; + sc->sc_bssid[2] = data & 0xff; + sc->sc_bssid[3] = (data & 0xff00) >> 8; + error = urtw_eprom_read32(sc, URTW_EPROM_MACADDR + 2, &data); + if (error != 0) + goto fail; + sc->sc_bssid[4] = data & 0xff; + sc->sc_bssid[5] = (data & 0xff00) >> 8; +fail: + return (error); +} + +static usb2_error_t +urtw_eprom_read32(struct urtw_softc *sc, uint32_t addr, uint32_t *data) +{ +#define URTW_READCMD_LEN 3 + int addrlen, i; + int16_t addrstr[8], data16, readcmd[] = { 1, 1, 0 }; + usb2_error_t error; + + /* NB: make sure the buffer is initialized */ + *data = 0; + + /* enable EPROM programming */ + urtw_write8_m(sc, URTW_EPROM_CMD, URTW_EPROM_CMD_PROGRAM_MODE); + DELAY(URTW_EPROM_DELAY); + + error = urtw_eprom_cs(sc, URTW_EPROM_ENABLE); + if (error != 0) + goto fail; + error = urtw_eprom_ck(sc); + if (error != 0) + goto fail; + error = urtw_eprom_sendbits(sc, readcmd, URTW_READCMD_LEN); + if (error != 0) + goto fail; + if (sc->sc_epromtype == URTW_EEPROM_93C56) { + addrlen = 8; + addrstr[0] = addr & (1 << 7); + addrstr[1] = addr & (1 << 6); + addrstr[2] = addr & (1 << 5); + addrstr[3] = addr & (1 << 4); + addrstr[4] = addr & (1 << 3); + addrstr[5] = addr & (1 << 2); + addrstr[6] = addr & (1 << 1); + addrstr[7] = addr & (1 << 0); + } else { + addrlen=6; + addrstr[0] = addr & (1 << 5); + addrstr[1] = addr & (1 << 4); + addrstr[2] = addr & (1 << 3); + addrstr[3] = addr & (1 << 2); + addrstr[4] = addr & (1 << 1); + addrstr[5] = addr & (1 << 0); + } + error = urtw_eprom_sendbits(sc, addrstr, addrlen); + if (error != 0) + goto fail; + + error = urtw_eprom_writebit(sc, 0); + if (error != 0) + goto fail; + + for (i = 0; i < 16; i++) { + error = urtw_eprom_ck(sc); + if (error != 0) + goto fail; + error = urtw_eprom_readbit(sc, &data16); + if (error != 0) + goto fail; + + (*data) |= (data16 << (15 - i)); + } + + error = urtw_eprom_cs(sc, URTW_EPROM_DISABLE); + if (error != 0) + goto fail; + error = urtw_eprom_ck(sc); + if (error != 0) + goto fail; + + /* now disable EPROM programming */ + urtw_write8_m(sc, URTW_EPROM_CMD, URTW_EPROM_CMD_NORMAL_MODE); +fail: + return (error); +#undef URTW_READCMD_LEN +} + +static usb2_error_t +urtw_eprom_cs(struct urtw_softc *sc, int able) +{ + uint8_t data; + usb2_error_t error; + + urtw_read8_m(sc, URTW_EPROM_CMD, &data); + if (able == URTW_EPROM_ENABLE) + urtw_write8_m(sc, URTW_EPROM_CMD, data | URTW_EPROM_CS); + else + urtw_write8_m(sc, URTW_EPROM_CMD, data & ~URTW_EPROM_CS); + DELAY(URTW_EPROM_DELAY); +fail: + return (error); +} + +static usb2_error_t +urtw_eprom_ck(struct urtw_softc *sc) +{ + uint8_t data; + usb2_error_t error; + + /* masking */ + urtw_read8_m(sc, URTW_EPROM_CMD, &data); + urtw_write8_m(sc, URTW_EPROM_CMD, data | URTW_EPROM_CK); + DELAY(URTW_EPROM_DELAY); + /* unmasking */ + urtw_read8_m(sc, URTW_EPROM_CMD, &data); + urtw_write8_m(sc, URTW_EPROM_CMD, data & ~URTW_EPROM_CK); + DELAY(URTW_EPROM_DELAY); +fail: + return (error); +} + +static usb2_error_t +urtw_eprom_readbit(struct urtw_softc *sc, int16_t *data) +{ + uint8_t data8; + usb2_error_t error; + + urtw_read8_m(sc, URTW_EPROM_CMD, &data8); + *data = (data8 & URTW_EPROM_READBIT) ? 1 : 0; + DELAY(URTW_EPROM_DELAY); + +fail: + return (error); +} + +static usb2_error_t +urtw_eprom_writebit(struct urtw_softc *sc, int16_t bit) +{ + uint8_t data; + usb2_error_t error; + + urtw_read8_m(sc, URTW_EPROM_CMD, &data); + if (bit != 0) + urtw_write8_m(sc, URTW_EPROM_CMD, data | URTW_EPROM_WRITEBIT); + else + urtw_write8_m(sc, URTW_EPROM_CMD, data & ~URTW_EPROM_WRITEBIT); + DELAY(URTW_EPROM_DELAY); +fail: + return (error); +} + +static usb2_error_t +urtw_eprom_sendbits(struct urtw_softc *sc, int16_t *buf, int buflen) +{ + int i = 0; + usb2_error_t error = 0; + + for (i = 0; i < buflen; i++) { + error = urtw_eprom_writebit(sc, buf[i]); + if (error != 0) + goto fail; + error = urtw_eprom_ck(sc); + if (error != 0) + goto fail; + } +fail: + return (error); +} + + +static usb2_error_t +urtw_get_txpwr(struct urtw_softc *sc) +{ + int i, j; + uint32_t data; + usb2_error_t error; + + error = urtw_eprom_read32(sc, URTW_EPROM_TXPW_BASE, &data); + if (error != 0) + goto fail; + sc->sc_txpwr_cck_base = data & 0xf; + sc->sc_txpwr_ofdm_base = (data >> 4) & 0xf; + + for (i = 1, j = 0; i < 6; i += 2, j++) { + error = urtw_eprom_read32(sc, URTW_EPROM_TXPW0 + j, &data); + if (error != 0) + goto fail; + sc->sc_txpwr_cck[i] = data & 0xf; + sc->sc_txpwr_cck[i + 1] = (data & 0xf00) >> 8; + sc->sc_txpwr_ofdm[i] = (data & 0xf0) >> 4; + sc->sc_txpwr_ofdm[i + 1] = (data & 0xf000) >> 12; + } + for (i = 1, j = 0; i < 4; i += 2, j++) { + error = urtw_eprom_read32(sc, URTW_EPROM_TXPW1 + j, &data); + if (error != 0) + goto fail; + sc->sc_txpwr_cck[i + 6] = data & 0xf; + sc->sc_txpwr_cck[i + 6 + 1] = (data & 0xf00) >> 8; + sc->sc_txpwr_ofdm[i + 6] = (data & 0xf0) >> 4; + sc->sc_txpwr_ofdm[i + 6 + 1] = (data & 0xf000) >> 12; + } + if (sc->sc_flags & URTW_RTL8187B) { + error = urtw_eprom_read32(sc, URTW_EPROM_TXPW2, &data); + if (error != 0) + goto fail; + sc->sc_txpwr_cck[1 + 6 + 4] = data & 0xf; + sc->sc_txpwr_ofdm[1 + 6 + 4] = (data & 0xf0) >> 4; + error = urtw_eprom_read32(sc, 0x0a, &data); + if (error != 0) + goto fail; + sc->sc_txpwr_cck[2 + 6 + 4] = data & 0xf; + sc->sc_txpwr_ofdm[2 + 6 + 4] = (data & 0xf0) >> 4; + error = urtw_eprom_read32(sc, 0x1c, &data); + if (error != 0) + goto fail; + sc->sc_txpwr_cck[3 + 6 + 4] = data & 0xf; + sc->sc_txpwr_cck[3 + 6 + 4 + 1] = (data & 0xf00) >> 8; + sc->sc_txpwr_ofdm[3 + 6 + 4] = (data & 0xf0) >> 4; + sc->sc_txpwr_ofdm[3 + 6 + 4 + 1] = (data & 0xf000) >> 12; + } else { + for (i = 1, j = 0; i < 4; i += 2, j++) { + error = urtw_eprom_read32(sc, URTW_EPROM_TXPW2 + j, + &data); + if (error != 0) + goto fail; + sc->sc_txpwr_cck[i + 6 + 4] = data & 0xf; + sc->sc_txpwr_cck[i + 6 + 4 + 1] = (data & 0xf00) >> 8; + sc->sc_txpwr_ofdm[i + 6 + 4] = (data & 0xf0) >> 4; + sc->sc_txpwr_ofdm[i + 6 + 4 + 1] = (data & 0xf000) >> 12; + } + } +fail: + return (error); +} + + +static usb2_error_t +urtw_get_rfchip(struct urtw_softc *sc) +{ + int ret; + uint8_t data8; + uint32_t data; + usb2_error_t error; + + error = urtw_eprom_read32(sc, URTW_EPROM_RFCHIPID, &data); + if (error != 0) + goto fail; + switch (data & 0xff) { + case URTW_EPROM_RFCHIPID_RTL8225U: + error = urtw_8225_isv2(sc, &ret); + if (error != 0) + goto fail; + if (ret == 0) { + sc->sc_rf_init = urtw_8225_rf_init; + sc->sc_rf_set_sens = urtw_8225_rf_set_sens; + sc->sc_rf_set_chan = urtw_8225_rf_set_chan; + sc->sc_rf_stop = urtw_8225_rf_stop; + } else { + sc->sc_rf_init = urtw_8225v2_rf_init; + sc->sc_rf_set_chan = urtw_8225v2_rf_set_chan; + sc->sc_rf_stop = urtw_8225_rf_stop; + } + sc->sc_max_sens = URTW_8225_RF_MAX_SENS; + sc->sc_sens = URTW_8225_RF_DEF_SENS; + break; + case URTW_EPROM_RFCHIPID_RTL8225Z2: + sc->sc_rf_init = urtw_8225v2b_rf_init; + sc->sc_rf_set_chan = urtw_8225v2b_rf_set_chan; + sc->sc_max_sens = URTW_8225_RF_MAX_SENS; + sc->sc_sens = URTW_8225_RF_DEF_SENS; + sc->sc_rf_stop = urtw_8225_rf_stop; + break; + default: + panic("unsupported RF chip %d\n", data & 0xff); + /* never reach */ + } + + if (sc->sc_flags & URTW_RTL8187B) { + urtw_read8_m(sc, 0xe1, &data8); + sc->sc_flags |= (data8 == 0) ? URTW_RTL8187B_REV_B : + (data8 == 1) ? URTW_RTL8187B_REV_D : URTW_RTL8187B_REV_E; + } + + device_printf(sc->sc_dev, "%s rf %s hwrev %s\n", + (sc->sc_flags & URTW_RTL8187B) ? "rtl8187b" : "rtl8187l", + ((data & 0xff) == URTW_EPROM_RFCHIPID_RTL8225U) ? "rtl8225u" : + "rtl8225z2", + (sc->sc_flags & URTW_RTL8187B) ? ((data8 == 0) ? "b" : + (data8 == 1) ? "d" : "e") : "none"); + +fail: + return (error); +} + + +static usb2_error_t +urtw_led_init(struct urtw_softc *sc) +{ + uint32_t rev; + usb2_error_t error; + + urtw_read8_m(sc, URTW_PSR, &sc->sc_psr); + error = urtw_eprom_read32(sc, URTW_EPROM_SWREV, &rev); + if (error != 0) + goto fail; + + switch (rev & URTW_EPROM_CID_MASK) { + case URTW_EPROM_CID_ALPHA0: + sc->sc_strategy = URTW_SW_LED_MODE1; + break; + case URTW_EPROM_CID_SERCOMM_PS: + sc->sc_strategy = URTW_SW_LED_MODE3; + break; + case URTW_EPROM_CID_HW_LED: + sc->sc_strategy = URTW_HW_LED; + break; + case URTW_EPROM_CID_RSVD0: + case URTW_EPROM_CID_RSVD1: + default: + sc->sc_strategy = URTW_SW_LED_MODE0; + break; + } + + sc->sc_gpio_ledpin = URTW_LED_PIN_GPIO0; + +fail: + return (error); +} + + +static usb2_error_t +urtw_8225_rf_init(struct urtw_softc *sc) +{ +#define N(a) (sizeof(a) / sizeof((a)[0])) + int i; + uint16_t data; + usb2_error_t error; + + error = urtw_8180_set_anaparam(sc, URTW_8225_ANAPARAM_ON); + if (error) + goto fail; + + error = urtw_8225_usb_init(sc); + if (error) + goto fail; + + urtw_write32_m(sc, URTW_RF_TIMING, 0x000a8008); + urtw_read16_m(sc, URTW_BRSR, &data); /* XXX ??? */ + urtw_write16_m(sc, URTW_BRSR, 0xffff); + urtw_write32_m(sc, URTW_RF_PARA, 0x100044); + + error = urtw_set_mode(sc, URTW_EPROM_CMD_CONFIG); + if (error) + goto fail; + urtw_write8_m(sc, URTW_CONFIG3, 0x44); + error = urtw_set_mode(sc, URTW_EPROM_CMD_NORMAL); + if (error) + goto fail; + + error = urtw_8185_rf_pins_enable(sc); + if (error) + goto fail; + usb2_pause_mtx(&sc->sc_mtx, 1000); + + for (i = 0; i < N(urtw_8225_rf_part1); i++) { + urtw_8225_write(sc, urtw_8225_rf_part1[i].reg, + urtw_8225_rf_part1[i].val); + usb2_pause_mtx(&sc->sc_mtx, 1); + } + usb2_pause_mtx(&sc->sc_mtx, 100); + urtw_8225_write(sc, + URTW_8225_ADDR_2_MAGIC, URTW_8225_ADDR_2_DATA_MAGIC1); + usb2_pause_mtx(&sc->sc_mtx, 200); + urtw_8225_write(sc, + URTW_8225_ADDR_2_MAGIC, URTW_8225_ADDR_2_DATA_MAGIC2); + usb2_pause_mtx(&sc->sc_mtx, 200); + urtw_8225_write(sc, + URTW_8225_ADDR_0_MAGIC, URTW_8225_ADDR_0_DATA_MAGIC3); + + for (i = 0; i < 95; i++) { + urtw_8225_write(sc, URTW_8225_ADDR_1_MAGIC, (uint8_t)(i + 1)); + urtw_8225_write(sc, URTW_8225_ADDR_2_MAGIC, urtw_8225_rxgain[i]); + } + + urtw_8225_write(sc, + URTW_8225_ADDR_0_MAGIC, URTW_8225_ADDR_0_DATA_MAGIC4); + urtw_8225_write(sc, + URTW_8225_ADDR_0_MAGIC, URTW_8225_ADDR_0_DATA_MAGIC5); + + for (i = 0; i < 128; i++) { + urtw_8187_write_phy_ofdm(sc, 0xb, urtw_8225_agc[i]); + usb2_pause_mtx(&sc->sc_mtx, 1); + urtw_8187_write_phy_ofdm(sc, 0xa, (uint8_t)i + 0x80); + usb2_pause_mtx(&sc->sc_mtx, 1); + } + + for (i = 0; i < N(urtw_8225_rf_part2); i++) { + urtw_8187_write_phy_ofdm(sc, urtw_8225_rf_part2[i].reg, + urtw_8225_rf_part2[i].val); + usb2_pause_mtx(&sc->sc_mtx, 1); + } + + error = urtw_8225_setgain(sc, 4); + if (error) + goto fail; + + for (i = 0; i < N(urtw_8225_rf_part3); i++) { + urtw_8187_write_phy_cck(sc, urtw_8225_rf_part3[i].reg, + urtw_8225_rf_part3[i].val); + usb2_pause_mtx(&sc->sc_mtx, 1); + } + + urtw_write8_m(sc, URTW_TESTR, 0x0d); + + error = urtw_8225_set_txpwrlvl(sc, 1); + if (error) + goto fail; + + urtw_8187_write_phy_cck(sc, 0x10, 0x9b); + usb2_pause_mtx(&sc->sc_mtx, 1); + urtw_8187_write_phy_ofdm(sc, 0x26, 0x90); + usb2_pause_mtx(&sc->sc_mtx, 1); + + /* TX ant A, 0x0 for B */ + error = urtw_8185_tx_antenna(sc, 0x3); + if (error) + goto fail; + urtw_write32_m(sc, URTW_HSSI_PARA, 0x3dc00002); + + error = urtw_8225_rf_set_chan(sc, 1); +fail: + return (error); +#undef N +} + +static usb2_error_t +urtw_8185_rf_pins_enable(struct urtw_softc *sc) +{ + usb2_error_t error = 0; + + urtw_write16_m(sc, URTW_RF_PINS_ENABLE, 0x1ff7); +fail: + return (error); +} + +static usb2_error_t +urtw_8185_tx_antenna(struct urtw_softc *sc, uint8_t ant) +{ + usb2_error_t error; + + urtw_write8_m(sc, URTW_TX_ANTENNA, ant); + usb2_pause_mtx(&sc->sc_mtx, 1); +fail: + return (error); +} + +static usb2_error_t +urtw_8187_write_phy_ofdm_c(struct urtw_softc *sc, uint8_t addr, uint32_t data) +{ + + data = data & 0xff; + return urtw_8187_write_phy(sc, addr, data); +} + +static usb2_error_t +urtw_8187_write_phy_cck_c(struct urtw_softc *sc, uint8_t addr, uint32_t data) +{ + + data = data & 0xff; + return urtw_8187_write_phy(sc, addr, data | 0x10000); +} + +static usb2_error_t +urtw_8187_write_phy(struct urtw_softc *sc, uint8_t addr, uint32_t data) +{ + uint32_t phyw; + usb2_error_t error; + + phyw = ((data << 8) | (addr | 0x80)); + urtw_write8_m(sc, URTW_PHY_MAGIC4, ((phyw & 0xff000000) >> 24)); + urtw_write8_m(sc, URTW_PHY_MAGIC3, ((phyw & 0x00ff0000) >> 16)); + urtw_write8_m(sc, URTW_PHY_MAGIC2, ((phyw & 0x0000ff00) >> 8)); + urtw_write8_m(sc, URTW_PHY_MAGIC1, ((phyw & 0x000000ff))); + usb2_pause_mtx(&sc->sc_mtx, 1); +fail: + return (error); +} + +static usb2_error_t +urtw_8225_setgain(struct urtw_softc *sc, int16_t gain) +{ + usb2_error_t error; + + urtw_8187_write_phy_ofdm(sc, 0x0d, urtw_8225_gain[gain * 4]); + urtw_8187_write_phy_ofdm(sc, 0x1b, urtw_8225_gain[gain * 4 + 2]); + urtw_8187_write_phy_ofdm(sc, 0x1d, urtw_8225_gain[gain * 4 + 3]); + urtw_8187_write_phy_ofdm(sc, 0x23, urtw_8225_gain[gain * 4 + 1]); +fail: + return (error); +} + +static usb2_error_t +urtw_8225_usb_init(struct urtw_softc *sc) +{ + uint8_t data; + usb2_error_t error; + + urtw_write8_m(sc, URTW_RF_PINS_SELECT + 1, 0); + urtw_write8_m(sc, URTW_GPIO, 0); + error = urtw_read8e(sc, 0x53, &data); + if (error) + goto fail; + error = urtw_write8e(sc, 0x53, data | (1 << 7)); + if (error) + goto fail; + urtw_write8_m(sc, URTW_RF_PINS_SELECT + 1, 4); + urtw_write8_m(sc, URTW_GPIO, 0x20); + urtw_write8_m(sc, URTW_GP_ENABLE, 0); + + urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, 0x80); + urtw_write16_m(sc, URTW_RF_PINS_SELECT, 0x80); + urtw_write16_m(sc, URTW_RF_PINS_ENABLE, 0x80); + + usb2_pause_mtx(&sc->sc_mtx, 500); +fail: + return (error); +} + +static usb2_error_t +urtw_8225_write_c(struct urtw_softc *sc, uint8_t addr, uint16_t data) +{ + uint16_t d80, d82, d84; + usb2_error_t error; + + urtw_read16_m(sc, URTW_RF_PINS_OUTPUT, &d80); + d80 &= URTW_RF_PINS_MAGIC1; + urtw_read16_m(sc, URTW_RF_PINS_ENABLE, &d82); + urtw_read16_m(sc, URTW_RF_PINS_SELECT, &d84); + d84 &= URTW_RF_PINS_MAGIC2; + urtw_write16_m(sc, URTW_RF_PINS_ENABLE, d82 | URTW_RF_PINS_MAGIC3); + urtw_write16_m(sc, URTW_RF_PINS_SELECT, d84 | URTW_RF_PINS_MAGIC3); + DELAY(10); + + urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, d80 | URTW_BB_HOST_BANG_EN); + DELAY(2); + urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, d80); + DELAY(10); + + error = urtw_8225_write_s16(sc, addr, 0x8225, &data); + if (error != 0) + goto fail; + + urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, d80 | URTW_BB_HOST_BANG_EN); + DELAY(10); + urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, d80 | URTW_BB_HOST_BANG_EN); + urtw_write16_m(sc, URTW_RF_PINS_SELECT, d84); + usb2_pause_mtx(&sc->sc_mtx, 2); +fail: + return (error); +} + +/* XXX why we should allocalte memory buffer instead of using memory stack? */ +static usb2_error_t +urtw_8225_write_s16(struct urtw_softc *sc, uint8_t addr, int index, + uint16_t *data) +{ + uint8_t *buf; + uint16_t data16; + struct usb2_device_request *req; + usb2_error_t error = 0; + + data16 = *data; + req = (usb_device_request_t *)malloc(sizeof(usb_device_request_t), + M_80211_VAP, M_NOWAIT | M_ZERO); + if (req == NULL) { + device_printf(sc->sc_dev, "could not allocate a memory\n"); + goto fail0; + } + buf = (uint8_t *)malloc(2, M_80211_VAP, M_NOWAIT | M_ZERO); + if (req == NULL) { + device_printf(sc->sc_dev, "could not allocate a memory\n"); + goto fail1; + } + + req->bmRequestType = UT_WRITE_VENDOR_DEVICE; + req->bRequest = URTW_8187_SETREGS_REQ; + USETW(req->wValue, addr); + USETW(req->wIndex, index); + USETW(req->wLength, sizeof(uint16_t)); + buf[0] = (data16 & 0x00ff); + buf[1] = (data16 & 0xff00) >> 8; + + error = urtw_do_request(sc, req, buf); + + free(buf, M_80211_VAP); +fail1: free(req, M_80211_VAP); +fail0: return (error); +} + +static usb2_error_t +urtw_8225_rf_set_chan(struct urtw_softc *sc, int chan) +{ + struct ieee80211com *ic = sc->sc_ifp->if_l2com; + struct ieee80211_channel *c = ic->ic_curchan; + usb2_error_t error; + + error = urtw_8225_set_txpwrlvl(sc, chan); + if (error) + goto fail; + urtw_8225_write(sc, URTW_8225_ADDR_7_MAGIC, urtw_8225_channel[chan]); + usb2_pause_mtx(&sc->sc_mtx, 10); + + urtw_write8_m(sc, URTW_SIFS, 0x22); + + if (sc->sc_state == IEEE80211_S_ASSOC && + ic->ic_flags & IEEE80211_F_SHSLOT) + urtw_write8_m(sc, URTW_SLOT, 0x9); + else + urtw_write8_m(sc, URTW_SLOT, 0x14); + + if (IEEE80211_IS_CHAN_G(c)) { + /* for G */ + urtw_write8_m(sc, URTW_DIFS, 0x14); + urtw_write8_m(sc, URTW_EIFS, 0x5b - 0x14); + urtw_write8_m(sc, URTW_CW_VAL, 0x73); + } else { + /* for B */ + urtw_write8_m(sc, URTW_DIFS, 0x24); + urtw_write8_m(sc, URTW_EIFS, 0x5b - 0x24); + urtw_write8_m(sc, URTW_CW_VAL, 0xa5); + } + +fail: + return (error); +} + +static usb2_error_t +urtw_8225_rf_set_sens(struct urtw_softc *sc, int sens) +{ + usb2_error_t error; + + if (sens < 0 || sens > 6) + return -1; + + if (sens > 4) + urtw_8225_write(sc, + URTW_8225_ADDR_C_MAGIC, URTW_8225_ADDR_C_DATA_MAGIC1); + else + urtw_8225_write(sc, + URTW_8225_ADDR_C_MAGIC, URTW_8225_ADDR_C_DATA_MAGIC2); + + sens = 6 - sens; + error = urtw_8225_setgain(sc, sens); + if (error) + goto fail; + + urtw_8187_write_phy_cck(sc, 0x41, urtw_8225_threshold[sens]); + +fail: + return (error); +} + +static usb2_error_t +urtw_8225_set_txpwrlvl(struct urtw_softc *sc, int chan) +{ + int i, idx, set; + uint8_t *cck_pwltable; + uint8_t cck_pwrlvl_max, ofdm_pwrlvl_min, ofdm_pwrlvl_max; + uint8_t cck_pwrlvl = sc->sc_txpwr_cck[chan] & 0xff; + uint8_t ofdm_pwrlvl = sc->sc_txpwr_ofdm[chan] & 0xff; + usb2_error_t error; + + cck_pwrlvl_max = 11; + ofdm_pwrlvl_max = 25; /* 12 -> 25 */ + ofdm_pwrlvl_min = 10; + + /* CCK power setting */ + cck_pwrlvl = (cck_pwrlvl > cck_pwrlvl_max) ? cck_pwrlvl_max : cck_pwrlvl; + idx = cck_pwrlvl % 6; + set = cck_pwrlvl / 6; + cck_pwltable = (chan == 14) ? urtw_8225_txpwr_cck_ch14 : + urtw_8225_txpwr_cck; + + urtw_write8_m(sc, URTW_TX_GAIN_CCK, + urtw_8225_tx_gain_cck_ofdm[set] >> 1); + for (i = 0; i < 8; i++) { + urtw_8187_write_phy_cck(sc, 0x44 + i, + cck_pwltable[idx * 8 + i]); + } + usb2_pause_mtx(&sc->sc_mtx, 1); + + /* OFDM power setting */ + ofdm_pwrlvl = (ofdm_pwrlvl > (ofdm_pwrlvl_max - ofdm_pwrlvl_min)) ? + ofdm_pwrlvl_max : ofdm_pwrlvl + ofdm_pwrlvl_min; + ofdm_pwrlvl = (ofdm_pwrlvl > 35) ? 35 : ofdm_pwrlvl; + + idx = ofdm_pwrlvl % 6; + set = ofdm_pwrlvl / 6; + + error = urtw_8185_set_anaparam2(sc, URTW_8225_ANAPARAM2_ON); + if (error) + goto fail; + urtw_8187_write_phy_ofdm(sc, 2, 0x42); + urtw_8187_write_phy_ofdm(sc, 6, 0); + urtw_8187_write_phy_ofdm(sc, 8, 0); + + urtw_write8_m(sc, URTW_TX_GAIN_OFDM, + urtw_8225_tx_gain_cck_ofdm[set] >> 1); + urtw_8187_write_phy_ofdm(sc, 0x5, urtw_8225_txpwr_ofdm[idx]); + urtw_8187_write_phy_ofdm(sc, 0x7, urtw_8225_txpwr_ofdm[idx]); + usb2_pause_mtx(&sc->sc_mtx, 1); +fail: + return (error); +} + + +static usb2_error_t +urtw_8225_rf_stop(struct urtw_softc *sc) +{ + uint8_t data; + usb2_error_t error; + + urtw_8225_write(sc, 0x4, 0x1f); + + error = urtw_set_mode(sc, URTW_EPROM_CMD_CONFIG); + if (error) + goto fail; + + urtw_read8_m(sc, URTW_CONFIG3, &data); + urtw_write8_m(sc, URTW_CONFIG3, data | URTW_CONFIG3_ANAPARAM_WRITE); + if (sc->sc_flags & URTW_RTL8187B) { + urtw_write32_m(sc, URTW_ANAPARAM2, + URTW_8187B_8225_ANAPARAM2_OFF); + urtw_write32_m(sc, URTW_ANAPARAM, URTW_8187B_8225_ANAPARAM_OFF); + urtw_write32_m(sc, URTW_ANAPARAM3, + URTW_8187B_8225_ANAPARAM3_OFF); + } else { + urtw_write32_m(sc, URTW_ANAPARAM2, URTW_8225_ANAPARAM2_OFF); + urtw_write32_m(sc, URTW_ANAPARAM, URTW_8225_ANAPARAM_OFF); + } + + urtw_write8_m(sc, URTW_CONFIG3, data & ~URTW_CONFIG3_ANAPARAM_WRITE); + error = urtw_set_mode(sc, URTW_EPROM_CMD_NORMAL); + if (error) + goto fail; + +fail: + return (error); +} + +static usb2_error_t +urtw_8225v2_rf_init(struct urtw_softc *sc) +{ +#define N(a) (sizeof(a) / sizeof((a)[0])) + int i; + uint16_t data; + uint32_t data32; + usb2_error_t error; + + error = urtw_8180_set_anaparam(sc, URTW_8225_ANAPARAM_ON); + if (error) + goto fail; + + error = urtw_8225_usb_init(sc); + if (error) + goto fail; + + urtw_write32_m(sc, URTW_RF_TIMING, 0x000a8008); + urtw_read16_m(sc, URTW_BRSR, &data); /* XXX ??? */ + urtw_write16_m(sc, URTW_BRSR, 0xffff); + urtw_write32_m(sc, URTW_RF_PARA, 0x100044); + + error = urtw_set_mode(sc, URTW_EPROM_CMD_CONFIG); + if (error) + goto fail; + urtw_write8_m(sc, URTW_CONFIG3, 0x44); + error = urtw_set_mode(sc, URTW_EPROM_CMD_NORMAL); + if (error) + goto fail; + + error = urtw_8185_rf_pins_enable(sc); + if (error) + goto fail; + + usb2_pause_mtx(&sc->sc_mtx, 500); + + for (i = 0; i < N(urtw_8225v2_rf_part1); i++) { + urtw_8225_write(sc, urtw_8225v2_rf_part1[i].reg, + urtw_8225v2_rf_part1[i].val); + } + usb2_pause_mtx(&sc->sc_mtx, 50); + + urtw_8225_write(sc, + URTW_8225_ADDR_0_MAGIC, URTW_8225_ADDR_0_DATA_MAGIC1); + + for (i = 0; i < 95; i++) { + urtw_8225_write(sc, URTW_8225_ADDR_1_MAGIC, (uint8_t)(i + 1)); + urtw_8225_write(sc, URTW_8225_ADDR_2_MAGIC, + urtw_8225v2_rxgain[i]); + } + + urtw_8225_write(sc, + URTW_8225_ADDR_3_MAGIC, URTW_8225_ADDR_3_DATA_MAGIC1); + urtw_8225_write(sc, + URTW_8225_ADDR_5_MAGIC, URTW_8225_ADDR_5_DATA_MAGIC1); + urtw_8225_write(sc, + URTW_8225_ADDR_0_MAGIC, URTW_8225_ADDR_0_DATA_MAGIC2); + urtw_8225_write(sc, + URTW_8225_ADDR_2_MAGIC, URTW_8225_ADDR_2_DATA_MAGIC1); + usb2_pause_mtx(&sc->sc_mtx, 100); + urtw_8225_write(sc, + URTW_8225_ADDR_2_MAGIC, URTW_8225_ADDR_2_DATA_MAGIC2); + usb2_pause_mtx(&sc->sc_mtx, 100); + + error = urtw_8225_read(sc, URTW_8225_ADDR_6_MAGIC, &data32); + if (error != 0) + goto fail; + if (data32 != URTW_8225_ADDR_6_DATA_MAGIC1) + device_printf(sc->sc_dev, "expect 0xe6!! (0x%x)\n", data32); + if (!(data32 & URTW_8225_ADDR_6_DATA_MAGIC2)) { + urtw_8225_write(sc, + URTW_8225_ADDR_2_MAGIC, URTW_8225_ADDR_2_DATA_MAGIC1); + usb2_pause_mtx(&sc->sc_mtx, 100); + urtw_8225_write(sc, + URTW_8225_ADDR_2_MAGIC, URTW_8225_ADDR_2_DATA_MAGIC2); + usb2_pause_mtx(&sc->sc_mtx, 50); + error = urtw_8225_read(sc, URTW_8225_ADDR_6_MAGIC, &data32); + if (error != 0) + goto fail; + if (!(data32 & URTW_8225_ADDR_6_DATA_MAGIC2)) + device_printf(sc->sc_dev, "RF calibration failed\n"); + } + usb2_pause_mtx(&sc->sc_mtx, 100); + + urtw_8225_write(sc, + URTW_8225_ADDR_0_MAGIC, URTW_8225_ADDR_0_DATA_MAGIC6); + for (i = 0; i < 128; i++) { + urtw_8187_write_phy_ofdm(sc, 0xb, urtw_8225_agc[i]); + urtw_8187_write_phy_ofdm(sc, 0xa, (uint8_t)i + 0x80); + } + + for (i = 0; i < N(urtw_8225v2_rf_part2); i++) { + urtw_8187_write_phy_ofdm(sc, urtw_8225v2_rf_part2[i].reg, + urtw_8225v2_rf_part2[i].val); + } + + error = urtw_8225v2_setgain(sc, 4); + if (error) + goto fail; + + for (i = 0; i < N(urtw_8225v2_rf_part3); i++) { + urtw_8187_write_phy_cck(sc, urtw_8225v2_rf_part3[i].reg, + urtw_8225v2_rf_part3[i].val); + } + + urtw_write8_m(sc, URTW_TESTR, 0x0d); + + error = urtw_8225v2_set_txpwrlvl(sc, 1); + if (error) + goto fail; + + urtw_8187_write_phy_cck(sc, 0x10, 0x9b); + urtw_8187_write_phy_ofdm(sc, 0x26, 0x90); + + /* TX ant A, 0x0 for B */ + error = urtw_8185_tx_antenna(sc, 0x3); + if (error) + goto fail; + urtw_write32_m(sc, URTW_HSSI_PARA, 0x3dc00002); + + error = urtw_8225_rf_set_chan(sc, 1); +fail: + return (error); +#undef N +} + +static usb2_error_t +urtw_8225v2_rf_set_chan(struct urtw_softc *sc, int chan) +{ + struct ieee80211com *ic = sc->sc_ifp->if_l2com; + struct ieee80211_channel *c = ic->ic_curchan; + usb2_error_t error; + + error = urtw_8225v2_set_txpwrlvl(sc, chan); + if (error) + goto fail; + + urtw_8225_write(sc, URTW_8225_ADDR_7_MAGIC, urtw_8225_channel[chan]); + usb2_pause_mtx(&sc->sc_mtx, 10); + + urtw_write8_m(sc, URTW_SIFS, 0x22); + + if(sc->sc_state == IEEE80211_S_ASSOC && + ic->ic_flags & IEEE80211_F_SHSLOT) + urtw_write8_m(sc, URTW_SLOT, 0x9); + else + urtw_write8_m(sc, URTW_SLOT, 0x14); + + if (IEEE80211_IS_CHAN_G(c)) { + /* for G */ + urtw_write8_m(sc, URTW_DIFS, 0x14); + urtw_write8_m(sc, URTW_EIFS, 0x5b - 0x14); + urtw_write8_m(sc, URTW_CW_VAL, 0x73); + } else { + /* for B */ + urtw_write8_m(sc, URTW_DIFS, 0x24); + urtw_write8_m(sc, URTW_EIFS, 0x5b - 0x24); + urtw_write8_m(sc, URTW_CW_VAL, 0xa5); + } + +fail: + return (error); +} + +static usb2_error_t +urtw_8225_read(struct urtw_softc *sc, uint8_t addr, uint32_t *data) +{ + int i; + int16_t bit; + uint8_t rlen = 12, wlen = 6; + uint16_t o1, o2, o3, tmp; + uint32_t d2w = ((uint32_t)(addr & 0x1f)) << 27; + uint32_t mask = 0x80000000, value = 0; + usb2_error_t error; + + urtw_read16_m(sc, URTW_RF_PINS_OUTPUT, &o1); + urtw_read16_m(sc, URTW_RF_PINS_ENABLE, &o2); + urtw_read16_m(sc, URTW_RF_PINS_SELECT, &o3); + urtw_write16_m(sc, URTW_RF_PINS_ENABLE, o2 | URTW_RF_PINS_MAGIC4); + urtw_write16_m(sc, URTW_RF_PINS_SELECT, o3 | URTW_RF_PINS_MAGIC4); + o1 &= ~URTW_RF_PINS_MAGIC4; + urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, o1 | URTW_BB_HOST_BANG_EN); + DELAY(5); + urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, o1); + DELAY(5); + + for (i = 0; i < (wlen / 2); i++, mask = mask >> 1) { + bit = ((d2w & mask) != 0) ? 1 : 0; + + urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, bit | o1); + DELAY(2); + urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, bit | o1 | + URTW_BB_HOST_BANG_CLK); + DELAY(2); + urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, bit | o1 | + URTW_BB_HOST_BANG_CLK); + DELAY(2); + mask = mask >> 1; + if (i == 2) + break; + bit = ((d2w & mask) != 0) ? 1 : 0; + urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, bit | o1 | + URTW_BB_HOST_BANG_CLK); + DELAY(2); + urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, bit | o1 | + URTW_BB_HOST_BANG_CLK); + DELAY(2); + urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, bit | o1); + DELAY(1); + } + urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, bit | o1 | URTW_BB_HOST_BANG_RW | + URTW_BB_HOST_BANG_CLK); + DELAY(2); + urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, bit | o1 | URTW_BB_HOST_BANG_RW); + DELAY(2); + urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, o1 | URTW_BB_HOST_BANG_RW); + DELAY(2); + + mask = 0x800; + for (i = 0; i < rlen; i++, mask = mask >> 1) { + urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, + o1 | URTW_BB_HOST_BANG_RW); + DELAY(2); + urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, + o1 | URTW_BB_HOST_BANG_RW | URTW_BB_HOST_BANG_CLK); + DELAY(2); + urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, + o1 | URTW_BB_HOST_BANG_RW | URTW_BB_HOST_BANG_CLK); + DELAY(2); + urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, + o1 | URTW_BB_HOST_BANG_RW | URTW_BB_HOST_BANG_CLK); + DELAY(2); + + urtw_read16_m(sc, URTW_RF_PINS_INPUT, &tmp); + value |= ((tmp & URTW_BB_HOST_BANG_CLK) ? mask : 0); + urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, + o1 | URTW_BB_HOST_BANG_RW); + DELAY(2); + } + + urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, o1 | URTW_BB_HOST_BANG_EN | + URTW_BB_HOST_BANG_RW); + DELAY(2); + + urtw_write16_m(sc, URTW_RF_PINS_ENABLE, o2); + urtw_write16_m(sc, URTW_RF_PINS_SELECT, o3); + urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, URTW_RF_PINS_OUTPUT_MAGIC1); + + if (data != NULL) + *data = value; +fail: + return (error); +} + + +static usb2_error_t +urtw_8225v2_set_txpwrlvl(struct urtw_softc *sc, int chan) +{ + int i; + uint8_t *cck_pwrtable; + uint8_t cck_pwrlvl_max = 15, ofdm_pwrlvl_max = 25, ofdm_pwrlvl_min = 10; + uint8_t cck_pwrlvl = sc->sc_txpwr_cck[chan] & 0xff; + uint8_t ofdm_pwrlvl = sc->sc_txpwr_ofdm[chan] & 0xff; + usb2_error_t error; + + /* CCK power setting */ + cck_pwrlvl = (cck_pwrlvl > cck_pwrlvl_max) ? cck_pwrlvl_max : cck_pwrlvl; + cck_pwrlvl += sc->sc_txpwr_cck_base; + cck_pwrlvl = (cck_pwrlvl > 35) ? 35 : cck_pwrlvl; + cck_pwrtable = (chan == 14) ? urtw_8225v2_txpwr_cck_ch14 : + urtw_8225v2_txpwr_cck; + + for (i = 0; i < 8; i++) + urtw_8187_write_phy_cck(sc, 0x44 + i, cck_pwrtable[i]); + + urtw_write8_m(sc, URTW_TX_GAIN_CCK, + urtw_8225v2_tx_gain_cck_ofdm[cck_pwrlvl]); + usb2_pause_mtx(&sc->sc_mtx, 1); + + /* OFDM power setting */ + ofdm_pwrlvl = (ofdm_pwrlvl > (ofdm_pwrlvl_max - ofdm_pwrlvl_min)) ? + ofdm_pwrlvl_max : ofdm_pwrlvl + ofdm_pwrlvl_min; + ofdm_pwrlvl += sc->sc_txpwr_ofdm_base; + ofdm_pwrlvl = (ofdm_pwrlvl > 35) ? 35 : ofdm_pwrlvl; + + error = urtw_8185_set_anaparam2(sc, URTW_8225_ANAPARAM2_ON); + if (error) + goto fail; + + urtw_8187_write_phy_ofdm(sc, 2, 0x42); + urtw_8187_write_phy_ofdm(sc, 5, 0x0); + urtw_8187_write_phy_ofdm(sc, 6, 0x40); + urtw_8187_write_phy_ofdm(sc, 7, 0x0); + urtw_8187_write_phy_ofdm(sc, 8, 0x40); + + urtw_write8_m(sc, URTW_TX_GAIN_OFDM, + urtw_8225v2_tx_gain_cck_ofdm[ofdm_pwrlvl]); + usb2_pause_mtx(&sc->sc_mtx, 1); +fail: + return (error); +} + +static usb2_error_t +urtw_8225v2_setgain(struct urtw_softc *sc, int16_t gain) +{ + uint8_t *gainp; + usb2_error_t error; + + /* XXX for A? */ + gainp = urtw_8225v2_gain_bg; + urtw_8187_write_phy_ofdm(sc, 0x0d, gainp[gain * 3]); + usb2_pause_mtx(&sc->sc_mtx, 1); + urtw_8187_write_phy_ofdm(sc, 0x1b, gainp[gain * 3 + 1]); + usb2_pause_mtx(&sc->sc_mtx, 1); + urtw_8187_write_phy_ofdm(sc, 0x1d, gainp[gain * 3 + 2]); + usb2_pause_mtx(&sc->sc_mtx, 1); + urtw_8187_write_phy_ofdm(sc, 0x21, 0x17); + usb2_pause_mtx(&sc->sc_mtx, 1); +fail: + return (error); +} + +static usb2_error_t +urtw_8225_isv2(struct urtw_softc *sc, int *ret) +{ + uint32_t data; + usb2_error_t error; + + *ret = 1; + + urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, URTW_RF_PINS_MAGIC5); + urtw_write16_m(sc, URTW_RF_PINS_SELECT, URTW_RF_PINS_MAGIC5); + urtw_write16_m(sc, URTW_RF_PINS_ENABLE, URTW_RF_PINS_MAGIC5); + usb2_pause_mtx(&sc->sc_mtx, 500); + + urtw_8225_write(sc, URTW_8225_ADDR_0_MAGIC, + URTW_8225_ADDR_0_DATA_MAGIC1); + + error = urtw_8225_read(sc, URTW_8225_ADDR_8_MAGIC, &data); + if (error != 0) + goto fail; + if (data != URTW_8225_ADDR_8_DATA_MAGIC1) + *ret = 0; + else { + error = urtw_8225_read(sc, URTW_8225_ADDR_9_MAGIC, &data); + if (error != 0) + goto fail; + if (data != URTW_8225_ADDR_9_DATA_MAGIC1) + *ret = 0; + } + + urtw_8225_write(sc, URTW_8225_ADDR_0_MAGIC, + URTW_8225_ADDR_0_DATA_MAGIC2); +fail: + return (error); +} + +static usb2_error_t +urtw_8225v2b_rf_init(struct urtw_softc *sc) +{ +#define N(a) (sizeof(a) / sizeof((a)[0])) + int i; + usb2_error_t error; + + for (i = 0; i < N(urtw_8225v2b_rf_part1); i++) + urtw_8225_write(sc, urtw_8225v2b_rf_part1[i].reg, + urtw_8225v2b_rf_part1[i].val); + + urtw_8225_write(sc, + URTW_8225_ADDR_0_MAGIC, URTW_8225_ADDR_0_DATA_MAGIC1); + + for (i = 0; i < N(urtw_8225v2b_rxgain); i++) { + urtw_8225_write(sc, URTW_8225_ADDR_1_MAGIC, (uint8_t)(i + 1)); + urtw_8225_write(sc, URTW_8225_ADDR_2_MAGIC, + urtw_8225v2b_rxgain[i]); + } + + urtw_8225_write(sc, URTW_8225_ADDR_3_MAGIC, 0x080); + urtw_8225_write(sc, URTW_8225_ADDR_5_MAGIC, 0x004); + urtw_8225_write(sc, URTW_8225_ADDR_0_MAGIC, 0x0b7); + urtw_8225_write(sc, URTW_8225_ADDR_2_MAGIC, 0xc4d); + urtw_8225_write(sc, URTW_8225_ADDR_2_MAGIC, 0x44d); + urtw_8225_write(sc, URTW_8225_ADDR_0_MAGIC, 0x2bf); + + urtw_write8_m(sc, URTW_TX_GAIN_CCK, 0x03); + urtw_write8_m(sc, URTW_TX_GAIN_OFDM, 0x07); + urtw_write8_m(sc, URTW_TX_ANTENNA, 0x03); + + urtw_8187_write_phy_ofdm(sc, 0x80, 0x12); + for (i = 0; i < N(urtw_8225z2_agc); i++) { + urtw_8187_write_phy_ofdm(sc, 0xf, urtw_8225z2_agc[i]); + urtw_8187_write_phy_ofdm(sc, 0xe, 0x80 + i); + urtw_8187_write_phy_ofdm(sc, 0xe, 0); + } + urtw_8187_write_phy_ofdm(sc, 0x80, 0x10); + + for (i = 0; i < N(urtw_8225v2b_rf_part2); i++) + urtw_8187_write_phy_ofdm(sc, i, urtw_8225v2b_rf_part2[i].val); + + urtw_write32_m(sc, 0xf0, (7 << 12) | (3 << 8) | 0x1c); + urtw_write32_m(sc, 0xf4, (7 << 12) | (3 << 8) | 0x1c); + urtw_write32_m(sc, 0xf8, (7 << 12) | (3 << 8) | 0x1c); + urtw_write32_m(sc, 0xfc, (7 << 12) | (3 << 8) | 0x1c); + urtw_write8_m(sc, URTW_ACM_CONTROL, 0); + + urtw_8187_write_phy_ofdm(sc, 0x97, 0x46); + urtw_8187_write_phy_ofdm(sc, 0xa4, 0xb6); + urtw_8187_write_phy_ofdm(sc, 0x85, 0xfc); + urtw_8187_write_phy_cck(sc, 0xc1, 0x88); +fail: + return (error); +#undef N +} + +static usb2_error_t +urtw_8225v2b_rf_set_chan(struct urtw_softc *sc, int chan) +{ + int ack; + struct ieee80211com *ic = sc->sc_ifp->if_l2com; + usb2_error_t error; + + error = urtw_8225v2b_set_txpwrlvl(sc, chan); + if (error) + goto fail; + + urtw_8225_write(sc, URTW_8225_ADDR_7_MAGIC, urtw_8225_channel[chan]); + usb2_pause_mtx(&sc->sc_mtx, 10); + + urtw_write8_m(sc, URTW_SIFS, 0xa); + if (ic->ic_flags & IEEE80211_F_SHSLOT) { + urtw_write8_m(sc, URTW_SLOT, 0x9); + urtw_write8_m(sc, URTW_DIFS, 0x1c); + /* In 8187B, BRSR + 1 ==> EIFS register */ + urtw_write8_m(sc, URTW_BRSR + 1, 0x53); + + ack = 112 + 48 + 0x1c; + ack += (ic->ic_flags & IEEE80211_F_SHPREAMBLE) ? + 72 : 144; + urtw_write8_m(sc, URTW_CARRIER_SCOUNT, + roundup2(ack, 4)); + } else { + urtw_write8_m(sc, URTW_SLOT, 0x14); + urtw_write8_m(sc, URTW_DIFS, 0x32); + /* In 8187B, BRSR + 1 ==> EIFS register */ + urtw_write8_m(sc, URTW_BRSR + 1, 0x5b); + + ack = 112 + 48 + 0x32; + ack += (ic->ic_flags & IEEE80211_F_SHPREAMBLE) ? + 72 : 144; + urtw_write8_m(sc, URTW_CARRIER_SCOUNT, + roundup2(ack, 4)); + + } + +fail: + return (error); +} + +static usb2_error_t +urtw_8225v2b_set_txpwrlvl(struct urtw_softc *sc, int chan) +{ + int i; + uint8_t *cck_pwrtable; + uint8_t cck_pwrlvl_max = 15; + uint8_t cck_pwrlvl = sc->sc_txpwr_cck[chan] & 0xff; + uint8_t ofdm_pwrlvl = sc->sc_txpwr_ofdm[chan] & 0xff; + usb2_error_t error; + + /* CCK power setting */ + cck_pwrlvl = (cck_pwrlvl > cck_pwrlvl_max) ? + ((sc->sc_flags & URTW_RTL8187B_REV_B) ? cck_pwrlvl_max : 22) : + (cck_pwrlvl + ((sc->sc_flags & URTW_RTL8187B_REV_B) ? 0 : 7)); + cck_pwrlvl += sc->sc_txpwr_cck_base; + cck_pwrlvl = (cck_pwrlvl > 35) ? 35 : cck_pwrlvl; + cck_pwrtable = (chan == 14) ? urtw_8225v2b_txpwr_cck_ch14 : + urtw_8225v2b_txpwr_cck; + + if (sc->sc_flags & URTW_RTL8187B_REV_B) + cck_pwrtable += (cck_pwrlvl <= 6) ? 0 : + ((cck_pwrlvl <= 11) ? 8 : 16); + else + cck_pwrtable += (cck_pwrlvl <= 5) ? 0 : + ((cck_pwrlvl <= 11) ? 8 : ((cck_pwrlvl <= 17) ? 16 : 24)); + + for (i = 0; i < 8; i++) + urtw_8187_write_phy_cck(sc, 0x44 + i, cck_pwrtable[i]); + + urtw_write8_m(sc, URTW_TX_GAIN_CCK, + urtw_8225v2_tx_gain_cck_ofdm[cck_pwrlvl] << 1); + usb2_pause_mtx(&sc->sc_mtx, 1); + + /* OFDM power setting */ + ofdm_pwrlvl = (ofdm_pwrlvl > 15) ? + ((sc->sc_flags & URTW_RTL8187B_REV_B) ? 17 : 25) : + (ofdm_pwrlvl + ((sc->sc_flags & URTW_RTL8187B_REV_B) ? 2 : 10)); + ofdm_pwrlvl += sc->sc_txpwr_ofdm_base; + ofdm_pwrlvl = (ofdm_pwrlvl > 35) ? 35 : ofdm_pwrlvl; + + urtw_write8_m(sc, URTW_TX_GAIN_OFDM, + urtw_8225v2_tx_gain_cck_ofdm[ofdm_pwrlvl] << 1); + + if (sc->sc_flags & URTW_RTL8187B_REV_B) { + if (ofdm_pwrlvl <= 11) { + urtw_8187_write_phy_ofdm(sc, 0x87, 0x60); + urtw_8187_write_phy_ofdm(sc, 0x89, 0x60); + } else { + urtw_8187_write_phy_ofdm(sc, 0x87, 0x5c); + urtw_8187_write_phy_ofdm(sc, 0x89, 0x5c); + } + } else { + if (ofdm_pwrlvl <= 11) { + urtw_8187_write_phy_ofdm(sc, 0x87, 0x5c); + urtw_8187_write_phy_ofdm(sc, 0x89, 0x5c); + } else if (ofdm_pwrlvl <= 17) { + urtw_8187_write_phy_ofdm(sc, 0x87, 0x54); + urtw_8187_write_phy_ofdm(sc, 0x89, 0x54); + } else { + urtw_8187_write_phy_ofdm(sc, 0x87, 0x50); + urtw_8187_write_phy_ofdm(sc, 0x89, 0x50); + } + } + usb2_pause_mtx(&sc->sc_mtx, 1); +fail: + return (error); +} + +static usb2_error_t +urtw_read8e(struct urtw_softc *sc, int val, uint8_t *data) +{ + struct usb2_device_request req; + usb2_error_t error; + + req.bmRequestType = UT_READ_VENDOR_DEVICE; + req.bRequest = URTW_8187_GETREGS_REQ; + USETW(req.wValue, val | 0xfe00); + USETW(req.wIndex, 0); + USETW(req.wLength, sizeof(uint8_t)); + + error = urtw_do_request(sc, &req, data); + return (error); +} + +static usb2_error_t +urtw_write8e(struct urtw_softc *sc, int val, uint8_t data) +{ + struct usb2_device_request req; + + req.bmRequestType = UT_WRITE_VENDOR_DEVICE; + req.bRequest = URTW_8187_SETREGS_REQ; + USETW(req.wValue, val | 0xfe00); + USETW(req.wIndex, 0); + USETW(req.wLength, sizeof(uint8_t)); + + return (urtw_do_request(sc, &req, &data)); +} + +static usb2_error_t +urtw_8180_set_anaparam(struct urtw_softc *sc, uint32_t val) +{ + uint8_t data; + usb2_error_t error; + + error = urtw_set_mode(sc, URTW_EPROM_CMD_CONFIG); + if (error) + goto fail; + + urtw_read8_m(sc, URTW_CONFIG3, &data); + urtw_write8_m(sc, URTW_CONFIG3, data | URTW_CONFIG3_ANAPARAM_WRITE); + urtw_write32_m(sc, URTW_ANAPARAM, val); + urtw_read8_m(sc, URTW_CONFIG3, &data); + urtw_write8_m(sc, URTW_CONFIG3, data & ~URTW_CONFIG3_ANAPARAM_WRITE); + + error = urtw_set_mode(sc, URTW_EPROM_CMD_NORMAL); + if (error) + goto fail; +fail: + return (error); +} + +static usb2_error_t +urtw_8185_set_anaparam2(struct urtw_softc *sc, uint32_t val) +{ + uint8_t data; + usb2_error_t error; + + error = urtw_set_mode(sc, URTW_EPROM_CMD_CONFIG); + if (error) + goto fail; + + urtw_read8_m(sc, URTW_CONFIG3, &data); + urtw_write8_m(sc, URTW_CONFIG3, data | URTW_CONFIG3_ANAPARAM_WRITE); + urtw_write32_m(sc, URTW_ANAPARAM2, val); + urtw_read8_m(sc, URTW_CONFIG3, &data); + urtw_write8_m(sc, URTW_CONFIG3, data & ~URTW_CONFIG3_ANAPARAM_WRITE); + + error = urtw_set_mode(sc, URTW_EPROM_CMD_NORMAL); + if (error) + goto fail; +fail: + return (error); +} + +static usb2_error_t +urtw_intr_enable(struct urtw_softc *sc) +{ + usb2_error_t error; + + urtw_write16_m(sc, URTW_INTR_MASK, 0xffff); +fail: + return (error); +} + +static usb2_error_t +urtw_intr_disable(struct urtw_softc *sc) +{ + usb2_error_t error; + + urtw_write16_m(sc, URTW_INTR_MASK, 0); +fail: + return (error); +} + +static usb2_error_t +urtw_reset(struct urtw_softc *sc) +{ + uint8_t data; + usb2_error_t error; + + error = urtw_8180_set_anaparam(sc, URTW_8225_ANAPARAM_ON); + if (error) + goto fail; + error = urtw_8185_set_anaparam2(sc, URTW_8225_ANAPARAM2_ON); + if (error) + goto fail; + + error = urtw_intr_disable(sc); + if (error) + goto fail; + usb2_pause_mtx(&sc->sc_mtx, 100); + + error = urtw_write8e(sc, 0x18, 0x10); + if (error != 0) + goto fail; + error = urtw_write8e(sc, 0x18, 0x11); + if (error != 0) + goto fail; + error = urtw_write8e(sc, 0x18, 0x00); + if (error != 0) + goto fail; + usb2_pause_mtx(&sc->sc_mtx, 100); + + urtw_read8_m(sc, URTW_CMD, &data); + data = (data & 0x2) | URTW_CMD_RST; + urtw_write8_m(sc, URTW_CMD, data); + usb2_pause_mtx(&sc->sc_mtx, 100); + + urtw_read8_m(sc, URTW_CMD, &data); + if (data & URTW_CMD_RST) { + device_printf(sc->sc_dev, "reset timeout\n"); + goto fail; + } + + error = urtw_set_mode(sc, URTW_EPROM_CMD_LOAD); + if (error) + goto fail; + usb2_pause_mtx(&sc->sc_mtx, 100); + + error = urtw_8180_set_anaparam(sc, URTW_8225_ANAPARAM_ON); + if (error) + goto fail; + error = urtw_8185_set_anaparam2(sc, URTW_8225_ANAPARAM2_ON); + if (error) + goto fail; +fail: + return (error); +} + +static usb2_error_t +urtw_led_ctl(struct urtw_softc *sc, int mode) +{ + usb2_error_t error = 0; + + switch (sc->sc_strategy) { + case URTW_SW_LED_MODE0: + error = urtw_led_mode0(sc, mode); + break; + case URTW_SW_LED_MODE1: + error = urtw_led_mode1(sc, mode); + break; + case URTW_SW_LED_MODE2: + error = urtw_led_mode2(sc, mode); + break; + case URTW_SW_LED_MODE3: + error = urtw_led_mode3(sc, mode); + break; + default: + panic("unsupported LED mode %d\n", sc->sc_strategy); + /* never reach */ + } + + return (error); +} + +static usb2_error_t +urtw_led_mode0(struct urtw_softc *sc, int mode) +{ + + switch (mode) { + case URTW_LED_CTL_POWER_ON: + sc->sc_gpio_ledstate = URTW_LED_POWER_ON_BLINK; + break; + case URTW_LED_CTL_TX: + if (sc->sc_gpio_ledinprogress == 1) + return (0); + + sc->sc_gpio_ledstate = URTW_LED_BLINK_NORMAL; + sc->sc_gpio_blinktime = 2; + break; + case URTW_LED_CTL_LINK: + sc->sc_gpio_ledstate = URTW_LED_ON; + break; + default: + panic("unsupported LED mode 0x%x", mode); + /* never reach */ + } + + switch (sc->sc_gpio_ledstate) { + case URTW_LED_ON: + if (sc->sc_gpio_ledinprogress != 0) + break; + urtw_led_on(sc, URTW_LED_GPIO); + break; + case URTW_LED_BLINK_NORMAL: + if (sc->sc_gpio_ledinprogress != 0) + break; + sc->sc_gpio_ledinprogress = 1; + sc->sc_gpio_blinkstate = (sc->sc_gpio_ledon != 0) ? + URTW_LED_OFF : URTW_LED_ON; + usb2_callout_reset(&sc->sc_led_ch, hz, urtw_led_ch, sc); + break; + case URTW_LED_POWER_ON_BLINK: + urtw_led_on(sc, URTW_LED_GPIO); + usb2_pause_mtx(&sc->sc_mtx, 100); + urtw_led_off(sc, URTW_LED_GPIO); + break; + default: + panic("unknown LED status 0x%x", sc->sc_gpio_ledstate); + /* never reach */ + } + return (0); +} + +static usb2_error_t +urtw_led_mode1(struct urtw_softc *sc, int mode) +{ + + return (USB_ERR_INVAL); +} + +static usb2_error_t +urtw_led_mode2(struct urtw_softc *sc, int mode) +{ + + return (USB_ERR_INVAL); +} + +static usb2_error_t +urtw_led_mode3(struct urtw_softc *sc, int mode) +{ + + return (USB_ERR_INVAL); +} + +static usb2_error_t +urtw_led_on(struct urtw_softc *sc, int type) +{ + usb2_error_t error; + + if (type == URTW_LED_GPIO) { + switch (sc->sc_gpio_ledpin) { + case URTW_LED_PIN_GPIO0: + urtw_write8_m(sc, URTW_GPIO, 0x01); + urtw_write8_m(sc, URTW_GP_ENABLE, 0x00); + break; + default: + panic("unsupported LED PIN type 0x%x", + sc->sc_gpio_ledpin); + /* never reach */ + } + } else { + panic("unsupported LED type 0x%x", type); + /* never reach */ + } + + sc->sc_gpio_ledon = 1; +fail: + return (error); +} + +static usb2_error_t +urtw_led_off(struct urtw_softc *sc, int type) +{ + usb2_error_t error; + + if (type == URTW_LED_GPIO) { + switch (sc->sc_gpio_ledpin) { + case URTW_LED_PIN_GPIO0: + urtw_write8_m(sc, URTW_GPIO, URTW_GPIO_DATA_MAGIC1); + urtw_write8_m(sc, + URTW_GP_ENABLE, URTW_GP_ENABLE_DATA_MAGIC1); + break; + default: + panic("unsupported LED PIN type 0x%x", + sc->sc_gpio_ledpin); + /* never reach */ + } + } else { + panic("unsupported LED type 0x%x", type); + /* never reach */ + } + + sc->sc_gpio_ledon = 0; + +fail: + return (error); +} + +static void +urtw_led_ch(void *arg) +{ + struct urtw_softc *sc = arg; + struct ifnet *ifp = sc->sc_ifp; + struct ieee80211com *ic = ifp->if_l2com; + + ieee80211_runtask(ic, &sc->sc_led_task); +} + +static void +urtw_ledtask(void *arg, int pending) +{ + struct urtw_softc *sc = arg; + + if (sc->sc_strategy != URTW_SW_LED_MODE0) + panic("could not process a LED strategy 0x%x", sc->sc_strategy); + + URTW_LOCK(sc); + urtw_led_blink(sc); + URTW_UNLOCK(sc); +} + +static usb2_error_t +urtw_led_blink(struct urtw_softc *sc) +{ + uint8_t ing = 0; + usb2_error_t error; + + if (sc->sc_gpio_blinkstate == URTW_LED_ON) + error = urtw_led_on(sc, URTW_LED_GPIO); + else + error = urtw_led_off(sc, URTW_LED_GPIO); + sc->sc_gpio_blinktime--; + if (sc->sc_gpio_blinktime == 0) + ing = 1; + else { + if (sc->sc_gpio_ledstate != URTW_LED_BLINK_NORMAL && + sc->sc_gpio_ledstate != URTW_LED_BLINK_SLOWLY && + sc->sc_gpio_ledstate != URTW_LED_BLINK_CM3) + ing = 1; + } + if (ing == 1) { + if (sc->sc_gpio_ledstate == URTW_LED_ON && + sc->sc_gpio_ledon == 0) + error = urtw_led_on(sc, URTW_LED_GPIO); + else if (sc->sc_gpio_ledstate == URTW_LED_OFF && + sc->sc_gpio_ledon == 1) + error = urtw_led_off(sc, URTW_LED_GPIO); + + sc->sc_gpio_blinktime = 0; + sc->sc_gpio_ledinprogress = 0; + return (0); + } + + sc->sc_gpio_blinkstate = (sc->sc_gpio_blinkstate != URTW_LED_ON) ? + URTW_LED_ON : URTW_LED_OFF; + + switch (sc->sc_gpio_ledstate) { + case URTW_LED_BLINK_NORMAL: + usb2_callout_reset(&sc->sc_led_ch, hz, urtw_led_ch, sc); + break; + default: + panic("unknown LED status 0x%x", sc->sc_gpio_ledstate); + /* never reach */ + } + return (0); +} + +static usb2_error_t +urtw_rx_enable(struct urtw_softc *sc) +{ + uint8_t data; + usb2_error_t error; + + usb2_transfer_start((sc->sc_flags & URTW_RTL8187B) ? + sc->sc_xfer[URTW_8187B_BULK_RX] : sc->sc_xfer[URTW_8187L_BULK_RX]); + + error = urtw_rx_setconf(sc); + if (error != 0) + goto fail; + + urtw_read8_m(sc, URTW_CMD, &data); + urtw_write8_m(sc, URTW_CMD, data | URTW_CMD_RX_ENABLE); +fail: + return (error); +} + +static usb2_error_t +urtw_tx_enable(struct urtw_softc *sc) +{ + uint8_t data8; + uint32_t data; + usb2_error_t error; + + if (sc->sc_flags & URTW_RTL8187B) { + urtw_read32_m(sc, URTW_TX_CONF, &data); + data &= ~URTW_TX_LOOPBACK_MASK; + data &= ~(URTW_TX_DPRETRY_MASK | URTW_TX_RTSRETRY_MASK); + data &= ~(URTW_TX_NOCRC | URTW_TX_MXDMA_MASK); + data &= ~URTW_TX_SWPLCPLEN; + data |= URTW_TX_HW_SEQNUM | URTW_TX_DISREQQSIZE | + (7 << 8) | /* short retry limit */ + (7 << 0) | /* long retry limit */ + (7 << 21); /* MAX TX DMA */ + urtw_write32_m(sc, URTW_TX_CONF, data); + + urtw_read8_m(sc, URTW_CMD, &data8); + urtw_write8_m(sc, URTW_CMD, data8 | URTW_CMD_TX_ENABLE); + return (error); + } + + urtw_read8_m(sc, URTW_CW_CONF, &data8); + data8 &= ~(URTW_CW_CONF_PERPACKET_CW | URTW_CW_CONF_PERPACKET_RETRY); + urtw_write8_m(sc, URTW_CW_CONF, data8); + + urtw_read8_m(sc, URTW_TX_AGC_CTL, &data8); + data8 &= ~URTW_TX_AGC_CTL_PERPACKET_GAIN; + data8 &= ~URTW_TX_AGC_CTL_PERPACKET_ANTSEL; + data8 &= ~URTW_TX_AGC_CTL_FEEDBACK_ANT; + urtw_write8_m(sc, URTW_TX_AGC_CTL, data8); + + urtw_read32_m(sc, URTW_TX_CONF, &data); + data &= ~URTW_TX_LOOPBACK_MASK; + data |= URTW_TX_LOOPBACK_NONE; + data &= ~(URTW_TX_DPRETRY_MASK | URTW_TX_RTSRETRY_MASK); + data |= sc->sc_tx_retry << URTW_TX_DPRETRY_SHIFT; + data |= sc->sc_rts_retry << URTW_TX_RTSRETRY_SHIFT; + data &= ~(URTW_TX_NOCRC | URTW_TX_MXDMA_MASK); + data |= URTW_TX_MXDMA_2048 | URTW_TX_CWMIN | URTW_TX_DISCW; + data &= ~URTW_TX_SWPLCPLEN; + data |= URTW_TX_NOICV; + urtw_write32_m(sc, URTW_TX_CONF, data); + + urtw_read8_m(sc, URTW_CMD, &data8); + urtw_write8_m(sc, URTW_CMD, data8 | URTW_CMD_TX_ENABLE); +fail: + return (error); +} + +static usb2_error_t +urtw_rx_setconf(struct urtw_softc *sc) +{ + struct ifnet *ifp = sc->sc_ifp; + struct ieee80211com *ic = ifp->if_l2com; + uint32_t data; + usb2_error_t error; + + urtw_read32_m(sc, URTW_RX, &data); + data = data &~ URTW_RX_FILTER_MASK; + if (sc->sc_flags & URTW_RTL8187B) { + data = data | URTW_RX_FILTER_MNG | URTW_RX_FILTER_DATA | + URTW_RX_FILTER_MCAST | URTW_RX_FILTER_BCAST | + URTW_RX_FILTER_NICMAC | URTW_RX_CHECK_BSSID | + URTW_RX_FIFO_THRESHOLD_NONE | + URTW_MAX_RX_DMA_2048 | + URTW_RX_AUTORESETPHY | URTW_RCR_ONLYERLPKT; + } else { + data = data | URTW_RX_FILTER_MNG | URTW_RX_FILTER_DATA; + data = data | URTW_RX_FILTER_BCAST | URTW_RX_FILTER_MCAST; + + if (ic->ic_opmode == IEEE80211_M_MONITOR) { + data = data | URTW_RX_FILTER_ICVERR; + data = data | URTW_RX_FILTER_PWR; + } + if (sc->sc_crcmon == 1 && ic->ic_opmode == IEEE80211_M_MONITOR) + data = data | URTW_RX_FILTER_CRCERR; + + if (ic->ic_opmode == IEEE80211_M_MONITOR || + (ifp->if_flags & (IFF_ALLMULTI | IFF_PROMISC))) { + data = data | URTW_RX_FILTER_ALLMAC; + } else { + data = data | URTW_RX_FILTER_NICMAC; + data = data | URTW_RX_CHECK_BSSID; + } + + data = data &~ URTW_RX_FIFO_THRESHOLD_MASK; + data = data | URTW_RX_FIFO_THRESHOLD_NONE | + URTW_RX_AUTORESETPHY; + data = data &~ URTW_MAX_RX_DMA_MASK; + data = data | URTW_MAX_RX_DMA_2048 | URTW_RCR_ONLYERLPKT; + } + + urtw_write32_m(sc, URTW_RX, data); +fail: + return (error); +} + +static struct mbuf * +urtw_rxeof(struct usb2_xfer *xfer, struct urtw_data *data, int *rssi_p, + int8_t *nf_p) +{ + int actlen, flen, len, nf = -95, rssi; + struct ieee80211_frame *wh; + struct mbuf *m, *mnew; + struct urtw_8187b_rxhdr *bhdr; + struct urtw_softc *sc = data->sc; + struct ifnet *ifp = sc->sc_ifp; + struct ieee80211com *ic = ifp->if_l2com; + uint8_t *desc, quality = 0, rate; + + actlen = xfer->actlen; + if (actlen < URTW_MIN_RXBUFSZ) { + ifp->if_ierrors++; + return (NULL); + } + + if (sc->sc_flags & URTW_RTL8187B) { + len = actlen - (sizeof(struct urtw_8187b_rxhdr)); + bhdr = (struct urtw_8187b_rxhdr *)(data->buf + len); + desc = data->buf + len; + flen = ((desc[1] & 0x0f) << 8) + (desc[0] & 0xff); + if (flen > actlen) { + ifp->if_ierrors++; + return (NULL); + } + rate = (le32toh(bhdr->flags) >> 20) & 0xf; + rssi = 14 + (bhdr->rssi / 2); + if (rssi > 95) + rssi = 95; + } else { + /* 4 dword and 4 byte CRC */ + len = actlen - (4 * 4); + desc = data->buf + len; + flen = ((desc[1] & 0x0f) << 8) + (desc[0] & 0xff); + if (flen > actlen) { + ifp->if_ierrors++; + return (NULL); + } + + rate = (desc[2] & 0xf0) >> 4; + quality = desc[4] & 0xff; + /* XXX correct? */ + rssi = (desc[6] & 0xfe) >> 1; + if (!urtw_isbmode(rate)) { + rssi = (rssi > 90) ? 90 : ((rssi < 25) ? 25 : rssi); + rssi = ((90 - rssi) * 100) / 65; + } else { + rssi = (rssi > 90) ? 95 : ((rssi < 30) ? 30 : rssi); + rssi = ((95 - rssi) * 100) / 65; + } + } + + mnew = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR); + if (mnew == NULL) { + ifp->if_ierrors++; + return (NULL); + } + + m = data->m; + data->m = mnew; + data->buf = mtod(mnew, uint8_t *); + + /* finalize mbuf */ + m->m_pkthdr.rcvif = ifp; + m->m_pkthdr.len = m->m_len = flen - 4; + + if (ieee80211_radiotap_active(ic)) { + struct urtw_rx_radiotap_header *tap = &sc->sc_rxtap; + + /* XXX Are variables correct? */ + tap->wr_chan_freq = htole16(ic->ic_curchan->ic_freq); + tap->wr_chan_flags = htole16(ic->ic_curchan->ic_flags); + tap->wr_dbm_antsignal = (int8_t)rssi; + } + + wh = mtod(m, struct ieee80211_frame *); + if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) == IEEE80211_FC0_TYPE_DATA) + sc->sc_currate = (rate > 0) ? rate : sc->sc_currate; + /* XXX correct? */ + if ((sc->sc_flags & URTW_RTL8187B) == 0) + nf = (quality > 64) ? 0 : ((64 - quality) * 100) / 64; + + *rssi_p = rssi; + *nf_p = nf; + + return (m); +} + +static void +urtw_bulk_rx_callback(struct usb2_xfer *xfer) +{ + struct urtw_softc *sc = xfer->priv_sc; + struct ifnet *ifp = sc->sc_ifp; + struct ieee80211com *ic = ifp->if_l2com; + struct ieee80211_frame *wh; + struct ieee80211_node *ni; + struct mbuf *m = NULL; + struct urtw_data *data; + int8_t nf = -95; + int rssi = 1; + + URTW_ASSERT_LOCKED(sc); + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + data = STAILQ_FIRST(&sc->sc_rx_active); + if (data == NULL) + goto setup; + STAILQ_REMOVE_HEAD(&sc->sc_rx_active, next); + m = urtw_rxeof(xfer, data, &rssi, &nf); + STAILQ_INSERT_TAIL(&sc->sc_rx_inactive, data, next); + /* FALLTHROUGH */ + case USB_ST_SETUP: +setup: + data = STAILQ_FIRST(&sc->sc_rx_inactive); + if (data == NULL) { + KASSERT(m == NULL, ("mbuf isn't NULL")); + return; + } + STAILQ_REMOVE_HEAD(&sc->sc_rx_inactive, next); + STAILQ_INSERT_TAIL(&sc->sc_rx_active, data, next); + usb2_set_frame_data(xfer, data->buf, 0); + xfer->frlengths[0] = xfer->max_data_length; + usb2_start_hardware(xfer); + + /* + * To avoid LOR we should unlock our private mutex here to call + * ieee80211_input() because here is at the end of a USB + * callback and safe to unlock. + */ + URTW_UNLOCK(sc); + if (m != NULL) { + wh = mtod(m, struct ieee80211_frame *); + ni = ieee80211_find_rxnode(ic, + (struct ieee80211_frame_min *)wh); + if (ni != NULL) { + (void) ieee80211_input(ni, m, rssi, nf); + /* node is no longer needed */ + ieee80211_free_node(ni); + } else + (void) ieee80211_input_all(ic, m, rssi, nf); + m = NULL; + } + URTW_LOCK(sc); + break; + default: + /* needs it to the inactive queue due to a error. */ + data = STAILQ_FIRST(&sc->sc_rx_active); + if (data != NULL) { + STAILQ_REMOVE_HEAD(&sc->sc_rx_active, next); + STAILQ_INSERT_TAIL(&sc->sc_rx_inactive, data, next); + } + if (xfer->error != USB_ERR_CANCELLED) { + xfer->flags.stall_pipe = 1; + ifp->if_ierrors++; + goto setup; + } + break; + } +} + +static void +urtw_txeof(struct usb2_xfer *xfer, struct urtw_data *data) +{ + struct urtw_softc *sc = xfer->priv_sc; + struct ifnet *ifp = sc->sc_ifp; + struct mbuf *m; + + URTW_ASSERT_LOCKED(sc); + + /* + * Do any tx complete callback. Note this must be done before releasing + * the node reference. + */ + if (data->m) { + m = data->m; + if (m->m_flags & M_TXCB) { + /* XXX status? */ + ieee80211_process_callback(data->ni, m, 0); + } + m_freem(m); + data->m = NULL; + } + if (data->ni) { + ieee80211_free_node(data->ni); + data->ni = NULL; + } + sc->sc_txtimer = 0; + ifp->if_opackets++; + ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; +} + +static void +urtw_bulk_tx_callback(struct usb2_xfer *xfer) +{ + struct urtw_softc *sc = xfer->priv_sc; + struct ifnet *ifp = sc->sc_ifp; + struct urtw_data *data; + + URTW_ASSERT_LOCKED(sc); + + switch (USB_GET_STATE(xfer)) { + case USB_ST_TRANSFERRED: + data = STAILQ_FIRST(&sc->sc_tx_active); + if (data == NULL) + goto setup; + STAILQ_REMOVE_HEAD(&sc->sc_tx_active, next); + urtw_txeof(xfer, data); + STAILQ_INSERT_TAIL(&sc->sc_tx_inactive, data, next); + /* FALLTHROUGH */ + case USB_ST_SETUP: +setup: + data = STAILQ_FIRST(&sc->sc_tx_pending); + if (data == NULL) { + DPRINTF(sc, URTW_DEBUG_XMIT, + "%s: empty pending queue\n", __func__); + return; + } + STAILQ_REMOVE_HEAD(&sc->sc_tx_pending, next); + STAILQ_INSERT_TAIL(&sc->sc_tx_active, data, next); + + usb2_set_frame_data(xfer, data->buf, 0); + xfer->frlengths[0] = data->buflen; + usb2_start_hardware(xfer); + + URTW_UNLOCK(sc); + urtw_start(ifp); + URTW_LOCK(sc); + break; + default: + data = STAILQ_FIRST(&sc->sc_tx_active); + if (data == NULL) + goto setup; + if (data->ni != NULL) { + ieee80211_free_node(data->ni); + data->ni = NULL; + ifp->if_oerrors++; + } + if (xfer->error != USB_ERR_CANCELLED) { + xfer->flags.stall_pipe = 1; + goto setup; + } + break; + } +} + +static struct urtw_data * +_urtw_getbuf(struct urtw_softc *sc) +{ + struct urtw_data *bf; + + bf = STAILQ_FIRST(&sc->sc_tx_inactive); + if (bf != NULL) + STAILQ_REMOVE_HEAD(&sc->sc_tx_inactive, next); + else + bf = NULL; + if (bf == NULL) + DPRINTF(sc, URTW_DEBUG_XMIT, "%s: %s\n", __func__, + "out of xmit buffers"); + return (bf); +} + +static struct urtw_data * +urtw_getbuf(struct urtw_softc *sc) +{ + struct urtw_data *bf; + + URTW_ASSERT_LOCKED(sc); + + bf = _urtw_getbuf(sc); + if (bf == NULL) { + struct ifnet *ifp = sc->sc_ifp; + + DPRINTF(sc, URTW_DEBUG_XMIT, "%s: stop queue\n", __func__); + ifp->if_drv_flags |= IFF_DRV_OACTIVE; + } + return (bf); +} + +static int +urtw_isbmode(uint16_t rate) +{ + + rate = urtw_rtl2rate(rate); + + return ((rate <= 22 && rate != 12 && rate != 18) || + rate == 44) ? (1) : (0); +} + +static device_method_t urtw_methods[] = { + DEVMETHOD(device_probe, urtw_match), + DEVMETHOD(device_attach, urtw_attach), + DEVMETHOD(device_detach, urtw_detach), + { 0, 0 } +}; +static driver_t urtw_driver = { + "urtw", + urtw_methods, + sizeof(struct urtw_softc) +}; +static devclass_t urtw_devclass; + +DRIVER_MODULE(urtw, uhub, urtw_driver, urtw_devclass, NULL, 0); +MODULE_DEPEND(urtw, wlan, 1, 1, 1); +MODULE_DEPEND(urtw, usb, 1, 1, 1); diff --git a/sys/dev/usb/wlan/if_urtwreg.h b/sys/dev/usb/wlan/if_urtwreg.h new file mode 100644 index 000000000000..d3399b7eaf18 --- /dev/null +++ b/sys/dev/usb/wlan/if_urtwreg.h @@ -0,0 +1,303 @@ +/* $FreeBSD$ */ + +/*- + * Copyright (c) 2008 Weongyo Jeong + * + * 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 URTW_CONFIG_INDEX 0 +#define URTW_IFACE_INDEX 0 + +/* for 8187 */ +#define URTW_MAC0 0x0000 /* 1 byte */ +#define URTW_MAC1 0x0001 /* 1 byte */ +#define URTW_MAC2 0x0002 /* 1 byte */ +#define URTW_MAC3 0x0003 /* 1 byte */ +#define URTW_MAC4 0x0004 /* 1 byte */ +#define URTW_MAC5 0x0005 /* 1 byte */ +#define URTW_BRSR 0x002c /* 2 byte */ +#define URTW_BRSR_MBR_8185 (0x0fff) +#define URTW_BSSID 0x002e /* 6 byte */ +#define URTW_RESP_RATE 0x0034 /* 1 byte */ +#define URTW_RESP_MAX_RATE_SHIFT (4) +#define URTW_RESP_MIN_RATE_SHIFT (0) +#define URTW_EIFS 0x0035 /* 1 byte */ +#define URTW_INTR_MASK 0x003c /* 2 byte */ +#define URTW_CMD 0x0037 /* 1 byte */ +#define URTW_CMD_TX_ENABLE (0x4) +#define URTW_CMD_RX_ENABLE (0x8) +#define URTW_CMD_RST (0x10) +#define URTW_TX_CONF 0x0040 /* 4 byte */ +#define URTW_TX_LOOPBACK_SHIFT (17) +#define URTW_TX_LOOPBACK_NONE (0 << URTW_TX_LOOPBACK_SHIFT) +#define URTW_TX_LOOPBACK_MAC (1 << URTW_TX_LOOPBACK_SHIFT) +#define URTW_TX_LOOPBACK_BASEBAND (2 << URTW_TX_LOOPBACK_SHIFT) +#define URTW_TX_LOOPBACK_CONTINUE (3 << URTW_TX_LOOPBACK_SHIFT) +#define URTW_TX_LOOPBACK_MASK (0x60000) +#define URTW_TX_DPRETRY_MASK (0xff00) +#define URTW_TX_RTSRETRY_MASK (0xff) +#define URTW_TX_DPRETRY_SHIFT (0) +#define URTW_TX_RTSRETRY_SHIFT (8) +#define URTW_TX_NOCRC (0x10000) +#define URTW_TX_MXDMA_MASK (0xe00000) +#define URTW_TX_MXDMA_1024 (6 << URTW_TX_MXDMA_SHIFT) +#define URTW_TX_MXDMA_2048 (7 << URTW_TX_MXDMA_SHIFT) +#define URTW_TX_MXDMA_SHIFT (21) +#define URTW_TX_DISCW (1 << 20) +#define URTW_TX_SWPLCPLEN (1 << 24) +#define URTW_TX_DISREQQSIZE (1 << 28) +#define URTW_TX_HW_SEQNUM (1 << 30) +#define URTW_TX_CWMIN (1 << 31) +#define URTW_TX_NOICV (0x80000) +#define URTW_RX 0x0044 /* 4 byte */ +#define URTW_RX_9356SEL (1 << 6) +#define URTW_RX_FILTER_MASK \ + (URTW_RX_FILTER_ALLMAC | URTW_RX_FILTER_NICMAC | URTW_RX_FILTER_MCAST | \ + URTW_RX_FILTER_BCAST | URTW_RX_FILTER_CRCERR | URTW_RX_FILTER_ICVERR | \ + URTW_RX_FILTER_DATA | URTW_RX_FILTER_CTL | URTW_RX_FILTER_MNG | \ + (1 << 21) | \ + URTW_RX_FILTER_PWR | URTW_RX_CHECK_BSSID) +#define URTW_RX_FILTER_ALLMAC (0x00000001) +#define URTW_RX_FILTER_NICMAC (0x00000002) +#define URTW_RX_FILTER_MCAST (0x00000004) +#define URTW_RX_FILTER_BCAST (0x00000008) +#define URTW_RX_FILTER_CRCERR (0x00000020) +#define URTW_RX_FILTER_ICVERR (0x00001000) +#define URTW_RX_FILTER_DATA (0x00040000) +#define URTW_RX_FILTER_CTL (0x00080000) +#define URTW_RX_FILTER_MNG (0x00100000) +#define URTW_RX_FILTER_PWR (0x00400000) +#define URTW_RX_CHECK_BSSID (0x00800000) +#define URTW_RX_FIFO_THRESHOLD_MASK ((1 << 13) | (1 << 14) | (1 << 15)) +#define URTW_RX_FIFO_THRESHOLD_SHIFT (13) +#define URTW_RX_FIFO_THRESHOLD_128 (3) +#define URTW_RX_FIFO_THRESHOLD_256 (4) +#define URTW_RX_FIFO_THRESHOLD_512 (5) +#define URTW_RX_FIFO_THRESHOLD_1024 (6) +#define URTW_RX_FIFO_THRESHOLD_NONE (7 << URTW_RX_FIFO_THRESHOLD_SHIFT) +#define URTW_RX_AUTORESETPHY (1 << URTW_RX_AUTORESETPHY_SHIFT) +#define URTW_RX_AUTORESETPHY_SHIFT (28) +#define URTW_MAX_RX_DMA_MASK ((1<<8) | (1<<9) | (1<<10)) +#define URTW_MAX_RX_DMA_2048 (7 << URTW_MAX_RX_DMA_SHIFT) +#define URTW_MAX_RX_DMA_1024 (6) +#define URTW_MAX_RX_DMA_SHIFT (10) +#define URTW_RCR_ONLYERLPKT (1 << 31) +#define URTW_INT_TIMEOUT 0x0048 /* 4 byte */ +#define URTW_EPROM_CMD 0x0050 /* 1 byte */ +#define URTW_EPROM_CMD_NORMAL (0x0) +#define URTW_EPROM_CMD_NORMAL_MODE \ + (URTW_EPROM_CMD_NORMAL << URTW_EPROM_CMD_SHIFT) +#define URTW_EPROM_CMD_LOAD (0x1) +#define URTW_EPROM_CMD_PROGRAM (0x2) +#define URTW_EPROM_CMD_PROGRAM_MODE \ + (URTW_EPROM_CMD_PROGRAM << URTW_EPROM_CMD_SHIFT) +#define URTW_EPROM_CMD_CONFIG (0x3) +#define URTW_EPROM_CMD_SHIFT (6) +#define URTW_EPROM_CMD_MASK ((1 << 7) | (1 << 6)) +#define URTW_EPROM_READBIT (0x1) +#define URTW_EPROM_WRITEBIT (0x2) +#define URTW_EPROM_CK (0x4) +#define URTW_EPROM_CS (0x8) +#define URTW_CONFIG1 0x0052 /* 1 byte */ +#define URTW_CONFIG2 0x0053 /* 1 byte */ +#define URTW_ANAPARAM 0x0054 /* 4 byte */ +#define URTW_8225_ANAPARAM_ON (0xa0000a59) +#define URTW_8225_ANAPARAM_OFF (0xa00beb59) +#define URTW_8187B_8225_ANAPARAM_ON (0x45090658) +#define URTW_8187B_8225_ANAPARAM_OFF (0x55480658) +#define URTW_MSR 0x0058 /* 1 byte */ +#define URTW_MSR_LINK_MASK ((1 << 2) | (1 << 3)) +#define URTW_MSR_LINK_SHIFT (2) +#define URTW_MSR_LINK_NONE (0 << URTW_MSR_LINK_SHIFT) +#define URTW_MSR_LINK_ADHOC (1 << URTW_MSR_LINK_SHIFT) +#define URTW_MSR_LINK_STA (2 << URTW_MSR_LINK_SHIFT) +#define URTW_MSR_LINK_HOSTAP (3 << URTW_MSR_LINK_SHIFT) +#define URTW_MSR_LINK_ENEDCA (1 << 4) +#define URTW_CONFIG3 0x0059 /* 1 byte */ +#define URTW_CONFIG3_ANAPARAM_WRITE (0x40) +#define URTW_CONFIG3_GNT_SELECT (0x80) +#define URTW_CONFIG3_ANAPARAM_W_SHIFT (6) +#define URTW_CONFIG4 0x005a /* 1 byte */ +#define URTW_CONFIG4_VCOOFF (1 << 7) +#define URTW_TESTR 0x005b /* 1 byte */ +#define URTW_PSR 0x005e /* 1 byte */ +#define URTW_ANAPARAM2 0x0060 /* 4 byte */ +#define URTW_8225_ANAPARAM2_ON (0x860c7312) +#define URTW_8225_ANAPARAM2_OFF (0x840dec11) +#define URTW_8187B_8225_ANAPARAM2_ON (0x727f3f52) +#define URTW_8187B_8225_ANAPARAM2_OFF (0x72003f50) +#define URTW_BEACON_INTERVAL 0x0070 /* 2 byte */ +#define URTW_ATIM_WND 0x0072 /* 2 byte */ +#define URTW_BEACON_INTERVAL_TIME 0x0074 /* 2 byte */ +#define URTW_ATIM_TR_ITV 0x0076 /* 2 byte */ +#define URTW_CARRIER_SCOUNT 0x0079 /* 1 byte */ +#define URTW_PHY_MAGIC1 0x007c /* 1 byte */ +#define URTW_PHY_MAGIC2 0x007d /* 1 byte */ +#define URTW_PHY_MAGIC3 0x007e /* 1 byte */ +#define URTW_PHY_MAGIC4 0x007f /* 1 byte */ +#define URTW_RF_PINS_OUTPUT 0x0080 /* 2 byte */ +#define URTW_RF_PINS_OUTPUT_MAGIC1 (0x3a0) +#define URTW_BB_HOST_BANG_CLK (1 << 1) +#define URTW_BB_HOST_BANG_EN (1 << 2) +#define URTW_BB_HOST_BANG_RW (1 << 3) +#define URTW_RF_PINS_ENABLE 0x0082 /* 2 byte */ +#define URTW_RF_PINS_SELECT 0x0084 /* 2 byte */ +#define URTW_ADDR_MAGIC1 0x0085 /* broken? */ +#define URTW_RF_PINS_INPUT 0x0086 /* 2 byte */ +#define URTW_RF_PINS_MAGIC1 (0xfff3) +#define URTW_RF_PINS_MAGIC2 (0xfff0) +#define URTW_RF_PINS_MAGIC3 (0x0007) +#define URTW_RF_PINS_MAGIC4 (0xf) +#define URTW_RF_PINS_MAGIC5 (0x0080) +#define URTW_RF_PARA 0x0088 /* 4 byte */ +#define URTW_RF_TIMING 0x008c /* 4 byte */ +#define URTW_GP_ENABLE 0x0090 /* 1 byte */ +#define URTW_GP_ENABLE_DATA_MAGIC1 (0x1) +#define URTW_GPIO 0x0091 /* 1 byte */ +#define URTW_GPIO_DATA_MAGIC1 (0x1) +#define URTW_HSSI_PARA 0x0094 /* 4 byte */ +#define URTW_TX_AGC_CTL 0x009c /* 1 byte */ +#define URTW_TX_AGC_CTL_PERPACKET_GAIN (0x1) +#define URTW_TX_AGC_CTL_PERPACKET_ANTSEL (0x2) +#define URTW_TX_AGC_CTL_FEEDBACK_ANT (0x4) +#define URTW_TX_GAIN_CCK 0x009d /* 1 byte */ +#define URTW_TX_GAIN_OFDM 0x009e /* 1 byte */ +#define URTW_TX_ANTENNA 0x009f /* 1 byte */ +#define URTW_WPA_CONFIG 0x00b0 /* 1 byte */ +#define URTW_SIFS 0x00b4 /* 1 byte */ +#define URTW_DIFS 0x00b5 /* 1 byte */ +#define URTW_SLOT 0x00b6 /* 1 byte */ +#define URTW_CW_CONF 0x00bc /* 1 byte */ +#define URTW_CW_CONF_PERPACKET_RETRY (0x2) +#define URTW_CW_CONF_PERPACKET_CW (0x1) +#define URTW_CW_VAL 0x00bd /* 1 byte */ +#define URTW_RATE_FALLBACK 0x00be /* 1 byte */ +#define URTW_RATE_FALLBACK_ENABLE (0x80) +#define URTW_ACM_CONTROL 0x00bf /* 1 byte */ +#define URTW_INT_MIG 0x00e2 /* 2 byte */ +#define URTW_TID_AC_MAP 0x00e8 /* 2 byte */ +#define URTW_ANAPARAM3 0x00ee /* 1 byte */ +#define URTW_8187B_8225_ANAPARAM3_ON (0x0) +#define URTW_8187B_8225_ANAPARAM3_OFF (0x0) + +#define URTW_TALLY_SEL 0x00fc /* 1 byte */ +#define URTW_ADDR_MAGIC2 0x00fe /* 2 byte */ +#define URTW_ADDR_MAGIC3 0x00ff /* 1 byte */ + +/* for 8225 */ +#define URTW_8225_ADDR_0_MAGIC 0x0 +#define URTW_8225_ADDR_0_DATA_MAGIC1 (0x1b7) +#define URTW_8225_ADDR_0_DATA_MAGIC2 (0x0b7) +#define URTW_8225_ADDR_0_DATA_MAGIC3 (0x127) +#define URTW_8225_ADDR_0_DATA_MAGIC4 (0x027) +#define URTW_8225_ADDR_0_DATA_MAGIC5 (0x22f) +#define URTW_8225_ADDR_0_DATA_MAGIC6 (0x2bf) +#define URTW_8225_ADDR_1_MAGIC 0x1 +#define URTW_8225_ADDR_2_MAGIC 0x2 +#define URTW_8225_ADDR_2_DATA_MAGIC1 (0xc4d) +#define URTW_8225_ADDR_2_DATA_MAGIC2 (0x44d) +#define URTW_8225_ADDR_3_MAGIC 0x3 +#define URTW_8225_ADDR_3_DATA_MAGIC1 (0x2) +#define URTW_8225_ADDR_5_MAGIC 0x5 +#define URTW_8225_ADDR_5_DATA_MAGIC1 (0x4) +#define URTW_8225_ADDR_6_MAGIC 0x6 +#define URTW_8225_ADDR_6_DATA_MAGIC1 (0xe6) +#define URTW_8225_ADDR_6_DATA_MAGIC2 (0x80) +#define URTW_8225_ADDR_7_MAGIC 0x7 +#define URTW_8225_ADDR_8_MAGIC 0x8 +#define URTW_8225_ADDR_8_DATA_MAGIC1 (0x588) +#define URTW_8225_ADDR_9_MAGIC 0x9 +#define URTW_8225_ADDR_9_DATA_MAGIC1 (0x700) +#define URTW_8225_ADDR_C_MAGIC 0xc +#define URTW_8225_ADDR_C_DATA_MAGIC1 (0x850) +#define URTW_8225_ADDR_C_DATA_MAGIC2 (0x050) + +/* for EEPROM */ +#define URTW_EPROM_TXPW_BASE 0x05 +#define URTW_EPROM_RFCHIPID 0x06 +#define URTW_EPROM_RFCHIPID_RTL8225U (5) +#define URTW_EPROM_RFCHIPID_RTL8225Z2 (6) +#define URTW_EPROM_MACADDR 0x07 +#define URTW_EPROM_TXPW0 0x16 +#define URTW_EPROM_TXPW2 0x1b +#define URTW_EPROM_TXPW1 0x3d +#define URTW_EPROM_SWREV 0x3f +#define URTW_EPROM_CID_MASK (0xff) +#define URTW_EPROM_CID_RSVD0 (0x00) +#define URTW_EPROM_CID_RSVD1 (0xff) +#define URTW_EPROM_CID_ALPHA0 (0x01) +#define URTW_EPROM_CID_SERCOMM_PS (0x02) +#define URTW_EPROM_CID_HW_LED (0x03) + +/* LED */ +#define URTW_CID_DEFAULT 0 +#define URTW_CID_8187_ALPHA0 1 +#define URTW_CID_8187_SERCOMM_PS 2 +#define URTW_CID_8187_HW_LED 3 +#define URTW_SW_LED_MODE0 0 +#define URTW_SW_LED_MODE1 1 +#define URTW_SW_LED_MODE2 2 +#define URTW_SW_LED_MODE3 3 +#define URTW_HW_LED 4 +#define URTW_LED_CTL_POWER_ON 0 +#define URTW_LED_CTL_LINK 2 +#define URTW_LED_CTL_TX 4 +#define URTW_LED_PIN_GPIO0 0 +#define URTW_LED_PIN_LED0 1 +#define URTW_LED_PIN_LED1 2 +#define URTW_LED_UNKNOWN 0 +#define URTW_LED_ON 1 +#define URTW_LED_OFF 2 +#define URTW_LED_BLINK_NORMAL 3 +#define URTW_LED_BLINK_SLOWLY 4 +#define URTW_LED_POWER_ON_BLINK 5 +#define URTW_LED_SCAN_BLINK 6 +#define URTW_LED_NO_LINK_BLINK 7 +#define URTW_LED_BLINK_CM3 8 + +/* for extra area */ +#define URTW_EPROM_DISABLE 0 +#define URTW_EPROM_ENABLE 1 +#define URTW_EPROM_DELAY 10 +#define URTW_8187_GETREGS_REQ 5 +#define URTW_8187_SETREGS_REQ 5 +#define URTW_8225_RF_MAX_SENS 6 +#define URTW_8225_RF_DEF_SENS 4 +#define URTW_DEFAULT_RTS_RETRY 7 +#define URTW_DEFAULT_TX_RETRY 7 +#define URTW_DEFAULT_RTS_THRESHOLD 2342U + +struct urtw_8187b_rxhdr { + uint32_t flags; + uint64_t mactime; + uint8_t sq; + uint8_t rssi; + uint8_t agc; + uint8_t flags2; + uint16_t unknown; + int8_t pwdb; + uint8_t fot; +} __packed; + +struct urtw_8187b_txhdr { + uint32_t flags; + uint16_t rts_duration; + uint16_t len; + uint32_t unknown1; + uint16_t unknown2; + uint16_t tx_duration; + uint32_t unknown3; + uint32_t retry; + uint32_t unknown4[2]; +} __packed; diff --git a/sys/dev/usb/wlan/if_urtwvar.h b/sys/dev/usb/wlan/if_urtwvar.h new file mode 100644 index 000000000000..0835659647fe --- /dev/null +++ b/sys/dev/usb/wlan/if_urtwvar.h @@ -0,0 +1,174 @@ +/* $FreeBSD$ */ + +/*- + * Copyright (c) 2008 Weongyo Jeong + * + * 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. + */ + +enum { + URTW_8187B_BULK_RX, + URTW_8187B_BULK_TX_BE, + URTW_8187B_BULK_TX_BK, + URTW_8187B_BULK_TX_VI, + URTW_8187B_BULK_TX_VO, + URTW_8187B_BULK_TX_EP12, + URTW_8187B_N_XFERS = 6 +}; + +enum { + URTW_8187L_BULK_RX, + URTW_8187L_BULK_TX_LOW, + URTW_8187L_BULK_TX_NORMAL, + URTW_8187L_N_XFERS = 3 +}; + +/* XXX no definition at net80211? */ +#define URTW_MAX_CHANNELS 15 + +struct urtw_data { + struct urtw_softc *sc; + uint8_t *buf; + uint16_t buflen; + struct mbuf *m; + struct ieee80211_node *ni; /* NB: tx only */ + STAILQ_ENTRY(urtw_data) next; +}; +typedef STAILQ_HEAD(, urtw_data) urtw_datahead; + +/* XXX not correct.. */ +#define URTW_MIN_RXBUFSZ \ + (sizeof(struct ieee80211_frame_min)) + +#define URTW_RX_DATA_LIST_COUNT 4 +#define URTW_TX_DATA_LIST_COUNT 16 +#define URTW_RX_MAXSIZE 0x9c4 +#define URTW_TX_MAXSIZE 0x9c4 + +struct urtw_rx_radiotap_header { + struct ieee80211_radiotap_header wr_ihdr; + uint8_t wr_flags; + uint16_t wr_chan_freq; + uint16_t wr_chan_flags; + int8_t wr_dbm_antsignal; +} __packed; + +#define URTW_RX_RADIOTAP_PRESENT \ + ((1 << IEEE80211_RADIOTAP_FLAGS) | \ + (1 << IEEE80211_RADIOTAP_CHANNEL) | \ + (1 << IEEE80211_RADIOTAP_DBM_ANTSIGNAL)) + +struct urtw_tx_radiotap_header { + struct ieee80211_radiotap_header wt_ihdr; + uint8_t wt_flags; + uint16_t wt_chan_freq; + uint16_t wt_chan_flags; +} __packed; + +#define URTW_TX_RADIOTAP_PRESENT \ + ((1 << IEEE80211_RADIOTAP_FLAGS) | \ + (1 << IEEE80211_RADIOTAP_CHANNEL)) + +struct urtw_vap { + struct ieee80211vap vap; + int (*newstate)(struct ieee80211vap *, + enum ieee80211_state, int); +}; +#define URTW_VAP(vap) ((struct urtw_vap *)(vap)) + +struct urtw_softc { + struct ifnet *sc_ifp; + device_t sc_dev; + struct usb2_device *sc_udev; + struct mtx sc_mtx; + + int sc_debug; + int sc_if_flags; + int sc_flags; +#define URTW_INIT_ONCE (1 << 1) +#define URTW_RTL8187B (1 << 2) +#define URTW_RTL8187B_REV_B (1 << 3) +#define URTW_RTL8187B_REV_D (1 << 4) +#define URTW_RTL8187B_REV_E (1 << 5) + enum ieee80211_state sc_state; + + int sc_epromtype; +#define URTW_EEPROM_93C46 0 +#define URTW_EEPROM_93C56 1 + uint8_t sc_crcmon; + uint8_t sc_bssid[IEEE80211_ADDR_LEN]; + + struct ieee80211_channel *sc_curchan; + + /* for RF */ + usb2_error_t (*sc_rf_init)(struct urtw_softc *); + usb2_error_t (*sc_rf_set_chan)(struct urtw_softc *, + int); + usb2_error_t (*sc_rf_set_sens)(struct urtw_softc *, + int); + usb2_error_t (*sc_rf_stop)(struct urtw_softc *); + uint8_t sc_rfchip; + uint32_t sc_max_sens; + uint32_t sc_sens; + /* for LED */ + struct usb2_callout sc_led_ch; + struct task sc_led_task; + uint8_t sc_psr; + uint8_t sc_strategy; +#define URTW_LED_GPIO 1 + uint8_t sc_gpio_ledon; + uint8_t sc_gpio_ledinprogress; + uint8_t sc_gpio_ledstate; + uint8_t sc_gpio_ledpin; + uint8_t sc_gpio_blinktime; + uint8_t sc_gpio_blinkstate; + /* RX/TX */ + struct usb2_xfer *sc_xfer[URTW_8187B_N_XFERS]; +#define URTW_PRIORITY_LOW 0 +#define URTW_PRIORITY_NORMAL 1 +#define URTW_DATA_TIMEOUT 10000 /* 10 sec */ +#define URTW_8187B_TXPIPE_BE 0x6 /* best effort */ +#define URTW_8187B_TXPIPE_BK 0x7 /* background */ +#define URTW_8187B_TXPIPE_VI 0x5 /* video */ +#define URTW_8187B_TXPIPE_VO 0x4 /* voice */ +#define URTW_8187B_TXPIPE_MAX 4 + struct urtw_data sc_rx[URTW_RX_DATA_LIST_COUNT]; + urtw_datahead sc_rx_active; + urtw_datahead sc_rx_inactive; + struct urtw_data sc_tx[URTW_TX_DATA_LIST_COUNT]; + urtw_datahead sc_tx_active; + urtw_datahead sc_tx_inactive; + urtw_datahead sc_tx_pending; + uint8_t sc_rts_retry; + uint8_t sc_tx_retry; + uint8_t sc_preamble_mode; +#define URTW_PREAMBLE_MODE_SHORT 1 +#define URTW_PREAMBLE_MODE_LONG 2 + struct callout sc_watchdog_ch; + int sc_txtimer; + int sc_currate; + /* TX power */ + uint8_t sc_txpwr_cck[URTW_MAX_CHANNELS]; + uint8_t sc_txpwr_cck_base; + uint8_t sc_txpwr_ofdm[URTW_MAX_CHANNELS]; + uint8_t sc_txpwr_ofdm_base; + + struct urtw_rx_radiotap_header sc_rxtap; + int sc_rxtap_len; + struct urtw_tx_radiotap_header sc_txtap; + int sc_txtap_len; +}; + +#define URTW_LOCK(sc) mtx_lock(&(sc)->sc_mtx) +#define URTW_UNLOCK(sc) mtx_unlock(&(sc)->sc_mtx) +#define URTW_ASSERT_LOCKED(sc) mtx_assert(&(sc)->sc_mtx, MA_OWNED) diff --git a/sys/modules/usb/urtw/Makefile b/sys/modules/usb/urtw/Makefile new file mode 100644 index 000000000000..e06ad0622bef --- /dev/null +++ b/sys/modules/usb/urtw/Makefile @@ -0,0 +1,10 @@ +# $FreeBSD$ + +.PATH: ${.CURDIR}/../../../dev/usb/wlan + +KMOD = if_urtw +SRCS = if_urtw.c if_urtwreg.h if_urtwvar.h \ + bus_if.h device_if.h \ + opt_bus.h opt_usb.h usb_if.h usbdevs.h + +.include