/*- * 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: head/sys/dev/usb/wlan/if_urtw.c 227461 2011-11-12 08:16:45Z hselasky $"); #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 "usbdevs.h" #include #include static 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_TXSTATUS = 0x00000100, /* tx status */ 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 static 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_USB_HOST_ID urtw_devs[] = { URTW_DEV_B(NETGEAR, WG111V3), URTW_DEV_B(REALTEK, RTL8187B_0), URTW_DEV_B(REALTEK, RTL8187B_1), URTW_DEV_B(REALTEK, RTL8187B_2), URTW_DEV_B(SITECOMEU, WL168V4), URTW_DEV_L(ASUS, P5B_WIFI), URTW_DEV_L(BELKIN, F5D7050E), URTW_DEV_L(LINKSYS4, WUSB54GCV2), URTW_DEV_L(NETGEAR, WG111V2), URTW_DEV_L(REALTEK, RTL8187), URTW_DEV_L(SITECOMEU, WL168V1), URTW_DEV_L(SURECOM, EP9001G2A), { USB_VPI(USB_VENDOR_OVISLINK, 0x8187, 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) }, { USB_VPI(USB_VENDOR_SPHAIRON, 0x0150, 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_part0[] = { { 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_8225v2b_rf_part1[] = { {0x0f0, 0x32}, {0x0f1, 0x32}, {0x0f2, 0x00}, {0x0f3, 0x00}, {0x0f4, 0x32}, {0x0f5, 0x43}, {0x0f6, 0x00}, {0x0f7, 0x00}, {0x0f8, 0x46}, {0x0f9, 0xa4}, {0x0fa, 0x00}, {0x0fb, 0x00}, {0x0fc, 0x96}, {0x0fd, 0xa4}, {0x0fe, 0x00}, {0x0ff, 0x00}, {0x158, 0x4b}, {0x159, 0x00}, {0x15a, 0x4b}, {0x15b, 0x00}, {0x160, 0x4b}, {0x161, 0x09}, {0x162, 0x4b}, {0x163, 0x09}, {0x1ce, 0x0f}, {0x1cf, 0x00}, {0x1e0, 0xff}, {0x1e1, 0x0f}, {0x1e2, 0x00}, {0x1f0, 0x4e}, {0x1f1, 0x01}, {0x1f2, 0x02}, {0x1f3, 0x03}, {0x1f4, 0x04}, {0x1f5, 0x05}, {0x1f6, 0x06}, {0x1f7, 0x07}, {0x1f8, 0x08}, {0x24e, 0x00}, {0x20c, 0x04}, {0x221, 0x61}, {0x222, 0x68}, {0x223, 0x6f}, {0x224, 0x76}, {0x225, 0x7d}, {0x226, 0x84}, {0x227, 0x8d}, {0x24d, 0x08}, {0x250, 0x05}, {0x251, 0xf5}, {0x252, 0x04}, {0x253, 0xa0}, {0x254, 0x1f}, {0x255, 0x23}, {0x256, 0x45}, {0x257, 0x67}, {0x258, 0x08}, {0x259, 0x08}, {0x25a, 0x08}, {0x25b, 0x08}, {0x260, 0x08}, {0x261, 0x08}, {0x262, 0x08}, {0x263, 0x08}, {0x264, 0xcf}, {0x272, 0x56}, {0x273, 0x9a}, {0x034, 0xf0}, {0x035, 0x0f}, {0x05b, 0x40}, {0x084, 0x88}, {0x085, 0x24}, {0x088, 0x54}, {0x08b, 0xb8}, {0x08c, 0x07}, {0x08d, 0x00}, {0x094, 0x1b}, {0x095, 0x12}, {0x096, 0x00}, {0x097, 0x06}, {0x09d, 0x1a}, {0x09f, 0x10}, {0x0b4, 0x22}, {0x0be, 0x80}, {0x0db, 0x00}, {0x0ee, 0x00}, {0x091, 0x03}, {0x24c, 0x00}, {0x39f, 0x00}, {0x08c, 0x01}, {0x08d, 0x10}, {0x08e, 0x08}, {0x08f, 0x00} }; 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 usb_callback_t urtw_bulk_rx_callback; static usb_callback_t urtw_bulk_tx_callback; static usb_callback_t urtw_bulk_tx_status_callback; static const struct usb_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_STATUS] = { .type = UE_BULK, .endpoint = 0x89, .direction = UE_DIR_IN, .bufsize = MCLBYTES, .flags = { .ext_buffer = 1, .pipe_bof = 1, .short_xfer_ok = 1 }, .callback = urtw_bulk_tx_status_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 usb_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 usb_error_t urtw_set_rate(struct urtw_softc *); static usb_error_t urtw_update_msr(struct urtw_softc *); static usb_error_t urtw_read8_c(struct urtw_softc *, int, uint8_t *); static usb_error_t urtw_read16_c(struct urtw_softc *, int, uint16_t *); static usb_error_t urtw_read32_c(struct urtw_softc *, int, uint32_t *); static usb_error_t urtw_write8_c(struct urtw_softc *, int, uint8_t); static usb_error_t urtw_write16_c(struct urtw_softc *, int, uint16_t); static usb_error_t urtw_write32_c(struct urtw_softc *, int, uint32_t); static usb_error_t urtw_eprom_cs(struct urtw_softc *, int); static usb_error_t urtw_eprom_ck(struct urtw_softc *); static usb_error_t urtw_eprom_sendbits(struct urtw_softc *, int16_t *, int); static usb_error_t urtw_eprom_read32(struct urtw_softc *, uint32_t, uint32_t *); static usb_error_t urtw_eprom_readbit(struct urtw_softc *, int16_t *); static usb_error_t urtw_eprom_writebit(struct urtw_softc *, int16_t); static usb_error_t urtw_get_macaddr(struct urtw_softc *); static usb_error_t urtw_get_txpwr(struct urtw_softc *); static usb_error_t urtw_get_rfchip(struct urtw_softc *); static usb_error_t urtw_led_init(struct urtw_softc *); static usb_error_t urtw_8185_rf_pins_enable(struct urtw_softc *); static usb_error_t urtw_8185_tx_antenna(struct urtw_softc *, uint8_t); static usb_error_t urtw_8187_write_phy(struct urtw_softc *, uint8_t, uint32_t); static usb_error_t urtw_8187_write_phy_ofdm_c(struct urtw_softc *, uint8_t, uint32_t); static usb_error_t urtw_8187_write_phy_cck_c(struct urtw_softc *, uint8_t, uint32_t); static usb_error_t urtw_8225_setgain(struct urtw_softc *, int16_t); static usb_error_t urtw_8225_usb_init(struct urtw_softc *); static usb_error_t urtw_8225_write_c(struct urtw_softc *, uint8_t, uint16_t); static usb_error_t urtw_8225_write_s16(struct urtw_softc *, uint8_t, int, uint16_t *); static usb_error_t urtw_8225_read(struct urtw_softc *, uint8_t, uint32_t *); static usb_error_t urtw_8225_rf_init(struct urtw_softc *); static usb_error_t urtw_8225_rf_set_chan(struct urtw_softc *, int); static usb_error_t urtw_8225_rf_set_sens(struct urtw_softc *, int); static usb_error_t urtw_8225_set_txpwrlvl(struct urtw_softc *, int); static usb_error_t urtw_8225_rf_stop(struct urtw_softc *); static usb_error_t urtw_8225v2_rf_init(struct urtw_softc *); static usb_error_t urtw_8225v2_rf_set_chan(struct urtw_softc *, int); static usb_error_t urtw_8225v2_set_txpwrlvl(struct urtw_softc *, int); static usb_error_t urtw_8225v2_setgain(struct urtw_softc *, int16_t); static usb_error_t urtw_8225_isv2(struct urtw_softc *, int *); static usb_error_t urtw_8225v2b_rf_init(struct urtw_softc *); static usb_error_t urtw_8225v2b_rf_set_chan(struct urtw_softc *, int); static usb_error_t urtw_read8e(struct urtw_softc *, int, uint8_t *); static usb_error_t urtw_write8e(struct urtw_softc *, int, uint8_t); static usb_error_t urtw_8180_set_anaparam(struct urtw_softc *, uint32_t); static usb_error_t urtw_8185_set_anaparam2(struct urtw_softc *, uint32_t); static usb_error_t urtw_intr_enable(struct urtw_softc *); static usb_error_t urtw_intr_disable(struct urtw_softc *); static usb_error_t urtw_reset(struct urtw_softc *); static usb_error_t urtw_led_on(struct urtw_softc *, int); static usb_error_t urtw_led_ctl(struct urtw_softc *, int); static usb_error_t urtw_led_blink(struct urtw_softc *); static usb_error_t urtw_led_mode0(struct urtw_softc *, int); static usb_error_t urtw_led_mode1(struct urtw_softc *, int); static usb_error_t urtw_led_mode2(struct urtw_softc *, int); static usb_error_t urtw_led_mode3(struct urtw_softc *, int); static usb_error_t urtw_rx_setconf(struct urtw_softc *); static usb_error_t urtw_rx_enable(struct urtw_softc *); static usb_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 usb_error_t urtw_adapter_start(struct urtw_softc *); static usb_error_t urtw_adapter_start_b(struct urtw_softc *); static usb_error_t urtw_set_mode(struct urtw_softc *, uint32_t); static usb_error_t urtw_8187b_cmd_reset(struct urtw_softc *); static usb_error_t urtw_do_request(struct urtw_softc *, struct usb_device_request *, void *); static usb_error_t urtw_8225v2b_set_txpwrlvl(struct urtw_softc *, int); static usb_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_compute_txtime(uint16_t, uint16_t, uint8_t, uint8_t); static void urtw_updateslot(struct ifnet *); static void urtw_updateslottask(void *, int); static void urtw_sysctl_node(struct urtw_softc *); static int urtw_match(device_t dev) { struct usb_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 (usbd_lookup_id_by_uaa(urtw_devs, sizeof(urtw_devs), uaa)); } static int urtw_attach(device_t dev) { const struct usb_config *setup_start; int ret = ENXIO; struct urtw_softc *sc = device_get_softc(dev); struct usb_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; usb_error_t error; device_set_usb_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); usb_callout_init_mtx(&sc->sc_led_ch, &sc->sc_mtx, 0); TASK_INIT(&sc->sc_led_task, 0, urtw_ledtask, sc); TASK_INIT(&sc->sc_updateslot_task, 0, urtw_updateslottask, 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 = usbd_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", usbd_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, ifqmaxlen); ifp->if_snd.ifq_drv_maxlen = ifqmaxlen; 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_updateslot = urtw_updateslot; 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); urtw_sysctl_node(sc); if (bootverbose) ieee80211_announce(ic); return (0); fail: URTW_UNLOCK(sc); fail1: usbd_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_updateslot_task); ieee80211_draintask(ic, &sc->sc_led_task); usb_callout_drain(&sc->sc_led_ch); callout_drain(&sc->sc_watchdog_ch); usbd_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; usb_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; if (sc->sc_flags & URTW_RTL8187B) usbd_transfer_start(sc->sc_xfer[URTW_8187B_BULK_TX_STATUS]); 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 usb_error_t urtw_adapter_start_b(struct urtw_softc *sc) { #define N(a) (sizeof(a) / sizeof((a)[0])) uint8_t data8; usb_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; error = sc->sc_rf_init(sc); if (error != 0) goto fail; urtw_write8_m(sc, URTW_CMD, URTW_CMD_RX_ENABLE | URTW_CMD_TX_ENABLE); /* fix RTL8187B RX stall */ 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)); urtw_write16_m(sc, 0x372, 0x59fa); urtw_write16_m(sc, 0x374, 0x59d2); urtw_write16_m(sc, 0x376, 0x59d2); urtw_write16_m(sc, 0x378, 0x19fa); urtw_write16_m(sc, 0x37a, 0x19fa); urtw_write16_m(sc, 0x37c, 0x00d0); urtw_write8_m(sc, 0x61, 0); urtw_write8_m(sc, 0x180, 0x0f); urtw_write8_m(sc, 0x183, 0x03); urtw_write8_m(sc, 0xda, 0x10); urtw_write8_m(sc, 0x24d, 0x08); urtw_write32_m(sc, URTW_HSSI_PARA, 0x0600321b); urtw_write16_m(sc, 0x1ec, 0x800); /* RX MAX SIZE */ fail: return (error); #undef N } static usb_error_t urtw_adapter_start(struct urtw_softc *sc) { usb_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 usb_error_t urtw_set_mode(struct urtw_softc *sc, uint32_t mode) { uint8_t data; usb_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 usb_error_t urtw_8187b_cmd_reset(struct urtw_softc *sc) { int i; uint8_t data8; usb_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++) { usb_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; } fail: return (error); } static usb_error_t urtw_do_request(struct urtw_softc *sc, struct usb_device_request *req, void *data) { usb_error_t err; int ntries = 10; URTW_ASSERT_LOCKED(sc); while (ntries--) { err = usbd_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", usbd_errstr(err)); usb_pause_mtx(&sc->sc_mtx, hz / 100); } return (err); } static void urtw_stop_locked(struct ifnet *ifp, int disable) { struct urtw_softc *sc = ifp->if_softc; uint8_t data8; usb_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", usbd_errstr(error)); usb_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++) usbd_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; callout_reset(&sc->sc_watchdog_ch, hz, urtw_watchdog, sc); } 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; usb_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; usb_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) { 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 usb_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 usb_xfer *xfer; int dur = 0, rtsdur = 0, rtsenable = 0, ctsenable = 0, rate, pkttime = 0, txdur = 0, isshort = 0, xferlen; uint16_t acktime, rtstime, ctstime; uint32_t flags; usb_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); } if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) == IEEE80211_FC0_TYPE_MGT || (wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) == IEEE80211_FC0_TYPE_CTL) { tp = &vap->iv_txparms[ieee80211_chan2mode(ic->ic_curchan)]; rate = tp->mgmtrate; } else { tp = &vap->iv_txparms[ieee80211_chan2mode(ni->ni_chan)]; /* for data frames */ if (IEEE80211_IS_MULTICAST(wh->i_addr1)) rate = tp->mcastrate; else if (tp->ucastrate != IEEE80211_FIXED_RATE_NONE) rate = tp->ucastrate; else rate = urtw_rtl2rate(sc->sc_currate); } sc->sc_stats.txrates[sc->sc_currate]++; if (IEEE80211_IS_MULTICAST(wh->i_addr1)) txdur = pkttime = urtw_compute_txtime(m0->m_pkthdr.len + IEEE80211_CRC_LEN, rate, 0, 0); else { acktime = urtw_compute_txtime(14, 2,0, 0); if ((m0->m_pkthdr.len + 4) > vap->iv_rtsthreshold) { rtsenable = 1; ctsenable = 0; rtstime = urtw_compute_txtime(URTW_ACKCTS_LEN, 2, 0, 0); ctstime = urtw_compute_txtime(14, 2, 0, 0); pkttime = urtw_compute_txtime(m0->m_pkthdr.len + IEEE80211_CRC_LEN, rate, 0, isshort); rtsdur = ctstime + pkttime + acktime + 3 * URTW_ASIFS_TIME; txdur = rtstime + rtsdur; } else { rtsenable = ctsenable = rtsdur = 0; pkttime = urtw_compute_txtime(m0->m_pkthdr.len + IEEE80211_CRC_LEN, rate, 0, isshort); txdur = pkttime + URTW_ASIFS_TIME + acktime; } if (wh->i_fc[1] & IEEE80211_FC1_MORE_FRAG) dur = urtw_compute_txtime(m0->m_pkthdr.len + IEEE80211_CRC_LEN, rate, 0, isshort) + 3 * URTW_ASIFS_TIME + 2 * acktime; else dur = URTW_ASIFS_TIME + acktime; } *(uint16_t *)wh->i_dur = htole16(dur); xferlen = m0->m_pkthdr.len; xferlen += (sc->sc_flags & URTW_RTL8187B) ? (4 * 8) : (4 * 3); if ((0 == xferlen % 64) || (0 == xferlen % 512)) xferlen += 1; memset(data->buf, 0, URTW_TX_MAXSIZE); flags = m0->m_pkthdr.len & 0xfff; flags |= URTW_TX_FLAG_NO_ENC; 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)) flags |= URTW_TX_FLAG_SPLCP; if (wh->i_fc[1] & IEEE80211_FC1_MORE_FRAG) flags |= URTW_TX_FLAG_MOREFRAG; flags |= (sc->sc_currate & 0xf) << URTW_TX_FLAG_TXRATE_SHIFT; if (sc->sc_flags & URTW_RTL8187B) { struct urtw_8187b_txhdr *tx; tx = (struct urtw_8187b_txhdr *)data->buf; if (ctsenable) flags |= URTW_TX_FLAG_CTS; if (rtsenable) { flags |= URTW_TX_FLAG_RTS; flags |= (urtw_rate2rtl(11) & 0xf) << URTW_TX_FLAG_RTSRATE_SHIFT; tx->rtsdur = rtsdur; } tx->flag = htole32(flags); tx->txdur = txdur; if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) == IEEE80211_FC0_TYPE_MGT && (wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) == IEEE80211_FC0_SUBTYPE_PROBE_RESP) tx->retry = 1; else tx->retry = URTW_TX_MAXRETRY; m_copydata(m0, 0, m0->m_pkthdr.len, (uint8_t *)(tx + 1)); } else { struct urtw_8187l_txhdr *tx; tx = (struct urtw_8187l_txhdr *)data->buf; if (rtsenable) { flags |= URTW_TX_FLAG_RTS; tx->rtsdur = rtsdur; } flags |= (urtw_rate2rtl(11) & 0xf) << URTW_TX_FLAG_RTSRATE_SHIFT; tx->flag = htole32(flags); tx->retry = 3; /* CW minimum */ tx->retry = 7 << 4; /* CW maximum */ tx->retry = URTW_TX_MAXRETRY << 8; /* retry limitation */ m_copydata(m0, 0, m0->m_pkthdr.len, (uint8_t *)(tx + 1)); } 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); usbd_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 ieee80211com *ic = vap->iv_ic; struct urtw_softc *sc = ic->ic_ifp->if_softc; struct urtw_vap *uvp = URTW_VAP(vap); struct ieee80211_node *ni; usb_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); usb_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: ni = ieee80211_ref_node(vap->iv_bss); /* 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); ieee80211_free_node(ni); 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. */ ifp->if_flags |= IFF_ALLMULTI; } static usb_error_t urtw_set_rate(struct urtw_softc *sc) { int i, basic_rate, min_rr_rate, max_rr_rate; uint16_t data; usb_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 usb_error_t urtw_update_msr(struct urtw_softc *sc) { struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; uint8_t data; usb_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 usb_error_t urtw_read8_c(struct urtw_softc *sc, int val, uint8_t *data) { struct usb_device_request req; usb_error_t error; URTW_ASSERT_LOCKED(sc); req.bmRequestType = UT_READ_VENDOR_DEVICE; req.bRequest = URTW_8187_GETREGS_REQ; USETW(req.wValue, (val & 0xff) | 0xff00); USETW(req.wIndex, (val >> 8) & 0x3); USETW(req.wLength, sizeof(uint8_t)); error = urtw_do_request(sc, &req, data); return (error); } static usb_error_t urtw_read16_c(struct urtw_softc *sc, int val, uint16_t *data) { struct usb_device_request req; usb_error_t error; URTW_ASSERT_LOCKED(sc); req.bmRequestType = UT_READ_VENDOR_DEVICE; req.bRequest = URTW_8187_GETREGS_REQ; USETW(req.wValue, (val & 0xff) | 0xff00); USETW(req.wIndex, (val >> 8) & 0x3); USETW(req.wLength, sizeof(uint16_t)); error = urtw_do_request(sc, &req, data); return (error); } static usb_error_t urtw_read32_c(struct urtw_softc *sc, int val, uint32_t *data) { struct usb_device_request req; usb_error_t error; URTW_ASSERT_LOCKED(sc); req.bmRequestType = UT_READ_VENDOR_DEVICE; req.bRequest = URTW_8187_GETREGS_REQ; USETW(req.wValue, (val & 0xff) | 0xff00); USETW(req.wIndex, (val >> 8) & 0x3); USETW(req.wLength, sizeof(uint32_t)); error = urtw_do_request(sc, &req, data); return (error); } static usb_error_t urtw_write8_c(struct urtw_softc *sc, int val, uint8_t data) { struct usb_device_request req; URTW_ASSERT_LOCKED(sc); req.bmRequestType = UT_WRITE_VENDOR_DEVICE; req.bRequest = URTW_8187_SETREGS_REQ; USETW(req.wValue, (val & 0xff) | 0xff00); USETW(req.wIndex, (val >> 8) & 0x3); USETW(req.wLength, sizeof(uint8_t)); return (urtw_do_request(sc, &req, &data)); } static usb_error_t urtw_write16_c(struct urtw_softc *sc, int val, uint16_t data) { struct usb_device_request req; URTW_ASSERT_LOCKED(sc); req.bmRequestType = UT_WRITE_VENDOR_DEVICE; req.bRequest = URTW_8187_SETREGS_REQ; USETW(req.wValue, (val & 0xff) | 0xff00); USETW(req.wIndex, (val >> 8) & 0x3); USETW(req.wLength, sizeof(uint16_t)); return (urtw_do_request(sc, &req, &data)); } static usb_error_t urtw_write32_c(struct urtw_softc *sc, int val, uint32_t data) { struct usb_device_request req; URTW_ASSERT_LOCKED(sc); req.bmRequestType = UT_WRITE_VENDOR_DEVICE; req.bRequest = URTW_8187_SETREGS_REQ; USETW(req.wValue, (val & 0xff) | 0xff00); USETW(req.wIndex, (val >> 8) & 0x3); USETW(req.wLength, sizeof(uint32_t)); return (urtw_do_request(sc, &req, &data)); } static usb_error_t urtw_get_macaddr(struct urtw_softc *sc) { uint32_t data; usb_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 usb_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 }; usb_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 usb_error_t urtw_eprom_cs(struct urtw_softc *sc, int able) { uint8_t data; usb_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 usb_error_t urtw_eprom_ck(struct urtw_softc *sc) { uint8_t data; usb_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 usb_error_t urtw_eprom_readbit(struct urtw_softc *sc, int16_t *data) { uint8_t data8; usb_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 usb_error_t urtw_eprom_writebit(struct urtw_softc *sc, int16_t bit) { uint8_t data; usb_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 usb_error_t urtw_eprom_sendbits(struct urtw_softc *sc, int16_t *buf, int buflen) { int i = 0; usb_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 usb_error_t urtw_get_txpwr(struct urtw_softc *sc) { int i, j; uint32_t data; usb_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 usb_error_t urtw_get_rfchip(struct urtw_softc *sc) { int ret; uint8_t data8; uint32_t data; usb_error_t error; if (sc->sc_flags & URTW_RTL8187B) { urtw_read8_m(sc, 0xe1, &data8); switch (data8) { case 0: sc->sc_flags |= URTW_RTL8187B_REV_B; break; case 1: sc->sc_flags |= URTW_RTL8187B_REV_D; break; case 2: sc->sc_flags |= URTW_RTL8187B_REV_E; break; default: device_printf(sc->sc_dev, "unknown type: %#x\n", data8); sc->sc_flags |= URTW_RTL8187B_REV_B; break; } } else { urtw_read32_m(sc, URTW_TX_CONF, &data); switch (data & URTW_TX_HWMASK) { case URTW_TX_R8187vD_B: sc->sc_flags |= URTW_RTL8187B; break; case URTW_TX_R8187vD: break; default: device_printf(sc->sc_dev, "unknown RTL8187L type: %#x\n", data & URTW_TX_HWMASK); break; } } 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 */ } 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 usb_error_t urtw_led_init(struct urtw_softc *sc) { uint32_t rev; usb_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 usb_error_t urtw_8225_rf_init(struct urtw_softc *sc) { #define N(a) (sizeof(a) / sizeof((a)[0])) int i; uint16_t data; usb_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; usb_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); usb_pause_mtx(&sc->sc_mtx, 1); } usb_pause_mtx(&sc->sc_mtx, 100); urtw_8225_write(sc, URTW_8225_ADDR_2_MAGIC, URTW_8225_ADDR_2_DATA_MAGIC1); usb_pause_mtx(&sc->sc_mtx, 200); urtw_8225_write(sc, URTW_8225_ADDR_2_MAGIC, URTW_8225_ADDR_2_DATA_MAGIC2); usb_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]); usb_pause_mtx(&sc->sc_mtx, 1); urtw_8187_write_phy_ofdm(sc, 0xa, (uint8_t)i + 0x80); usb_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); usb_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); usb_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); usb_pause_mtx(&sc->sc_mtx, 1); urtw_8187_write_phy_ofdm(sc, 0x26, 0x90); usb_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 usb_error_t urtw_8185_rf_pins_enable(struct urtw_softc *sc) { usb_error_t error = 0; urtw_write16_m(sc, URTW_RF_PINS_ENABLE, 0x1ff7); fail: return (error); } static usb_error_t urtw_8185_tx_antenna(struct urtw_softc *sc, uint8_t ant) { usb_error_t error; urtw_write8_m(sc, URTW_TX_ANTENNA, ant); usb_pause_mtx(&sc->sc_mtx, 1); fail: return (error); } static usb_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 usb_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 usb_error_t urtw_8187_write_phy(struct urtw_softc *sc, uint8_t addr, uint32_t data) { uint32_t phyw; usb_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))); usb_pause_mtx(&sc->sc_mtx, 1); fail: return (error); } static usb_error_t urtw_8225_setgain(struct urtw_softc *sc, int16_t gain) { usb_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 usb_error_t urtw_8225_usb_init(struct urtw_softc *sc) { uint8_t data; usb_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); usb_pause_mtx(&sc->sc_mtx, 500); fail: return (error); } static usb_error_t urtw_8225_write_c(struct urtw_softc *sc, uint8_t addr, uint16_t data) { uint16_t d80, d82, d84; usb_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); usb_pause_mtx(&sc->sc_mtx, 2); fail: return (error); } /* XXX why we should allocalte memory buffer instead of using memory stack? */ static usb_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 usb_device_request *req; usb_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 usb_error_t urtw_8225_rf_set_chan(struct urtw_softc *sc, int chan) { usb_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]); usb_pause_mtx(&sc->sc_mtx, 10); fail: return (error); } static usb_error_t urtw_8225_rf_set_sens(struct urtw_softc *sc, int sens) { usb_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 usb_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; usb_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]); } usb_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]); usb_pause_mtx(&sc->sc_mtx, 1); fail: return (error); } static usb_error_t urtw_8225_rf_stop(struct urtw_softc *sc) { uint8_t data; usb_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 usb_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; usb_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; usb_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); } usb_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); usb_pause_mtx(&sc->sc_mtx, 100); urtw_8225_write(sc, URTW_8225_ADDR_2_MAGIC, URTW_8225_ADDR_2_DATA_MAGIC2); usb_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); usb_pause_mtx(&sc->sc_mtx, 100); urtw_8225_write(sc, URTW_8225_ADDR_2_MAGIC, URTW_8225_ADDR_2_DATA_MAGIC2); usb_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"); } usb_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 usb_error_t urtw_8225v2_rf_set_chan(struct urtw_softc *sc, int chan) { usb_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]); usb_pause_mtx(&sc->sc_mtx, 10); fail: return (error); } static usb_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; usb_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 usb_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; usb_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]); usb_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]); usb_pause_mtx(&sc->sc_mtx, 1); fail: return (error); } static usb_error_t urtw_8225v2_setgain(struct urtw_softc *sc, int16_t gain) { uint8_t *gainp; usb_error_t error; /* XXX for A? */ gainp = urtw_8225v2_gain_bg; urtw_8187_write_phy_ofdm(sc, 0x0d, gainp[gain * 3]); usb_pause_mtx(&sc->sc_mtx, 1); urtw_8187_write_phy_ofdm(sc, 0x1b, gainp[gain * 3 + 1]); usb_pause_mtx(&sc->sc_mtx, 1); urtw_8187_write_phy_ofdm(sc, 0x1d, gainp[gain * 3 + 2]); usb_pause_mtx(&sc->sc_mtx, 1); urtw_8187_write_phy_ofdm(sc, 0x21, 0x17); usb_pause_mtx(&sc->sc_mtx, 1); fail: return (error); } static usb_error_t urtw_8225_isv2(struct urtw_softc *sc, int *ret) { uint32_t data; usb_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); usb_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 usb_error_t urtw_8225v2b_rf_init(struct urtw_softc *sc) { #define N(a) (sizeof(a) / sizeof((a)[0])) int i; uint8_t data8; usb_error_t error; error = urtw_set_mode(sc, URTW_EPROM_CMD_CONFIG); if (error) goto fail; /* * initialize extra registers on 8187 */ urtw_write16_m(sc, URTW_BRSR_8187B, 0xfff); /* retry limit */ urtw_read8_m(sc, URTW_CW_CONF, &data8); data8 |= URTW_CW_CONF_PERPACKET_RETRY; urtw_write8_m(sc, URTW_CW_CONF, data8); /* TX AGC */ urtw_read8_m(sc, URTW_TX_AGC_CTL, &data8); data8 |= URTW_TX_AGC_CTL_PERPACKET_GAIN; urtw_write8_m(sc, URTW_TX_AGC_CTL, data8); /* Auto Rate Fallback Control */ #define URTW_ARFR 0x1e0 urtw_write16_m(sc, URTW_ARFR, 0xfff); urtw_read8_m(sc, URTW_RATE_FALLBACK, &data8); urtw_write8_m(sc, URTW_RATE_FALLBACK, data8 | URTW_RATE_FALLBACK_ENABLE); urtw_read8_m(sc, URTW_MSR, &data8); urtw_write8_m(sc, URTW_MSR, data8 & 0xf3); urtw_read8_m(sc, URTW_MSR, &data8); urtw_write8_m(sc, URTW_MSR, data8 | URTW_MSR_LINK_ENEDCA); urtw_write8_m(sc, URTW_ACM_CONTROL, sc->sc_acmctl); urtw_write16_m(sc, URTW_ATIM_WND, 2); urtw_write16_m(sc, URTW_BEACON_INTERVAL, 100); #define URTW_FEMR_FOR_8187B 0x1d4 urtw_write16_m(sc, URTW_FEMR_FOR_8187B, 0xffff); /* led type */ urtw_read8_m(sc, URTW_CONFIG1, &data8); data8 = (data8 & 0x3f) | 0x80; urtw_write8_m(sc, URTW_CONFIG1, data8); /* 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; urtw_write8_m(sc, URTW_WPA_CONFIG, 0); /* * MAC configuration */ for (i = 0; i < N(urtw_8225v2b_rf_part1); i++) urtw_write8_m(sc, urtw_8225v2b_rf_part1[i].reg, urtw_8225v2b_rf_part1[i].val); urtw_write16_m(sc, URTW_TID_AC_MAP, 0xfa50); urtw_write16_m(sc, URTW_INT_MIG, 0x0000); urtw_write32_m(sc, 0x1f0, 0); urtw_write32_m(sc, 0x1f4, 0); urtw_write8_m(sc, 0x1f8, 0); urtw_write32_m(sc, URTW_RF_TIMING, 0x4001); #define URTW_RFSW_CTRL 0x272 urtw_write16_m(sc, URTW_RFSW_CTRL, 0x569a); /* * initialize PHY */ 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; /* setup RFE initial timing */ 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); usb_pause_mtx(&sc->sc_mtx, 1100); for (i = 0; i < N(urtw_8225v2b_rf_part0); i++) { urtw_8225_write(sc, urtw_8225v2b_rf_part0[i].reg, urtw_8225v2b_rf_part0[i].val); usb_pause_mtx(&sc->sc_mtx, 1); } urtw_8225_write(sc, 0x00, 0x01b7); for (i = 0; i < 95; i++) { urtw_8225_write(sc, URTW_8225_ADDR_1_MAGIC, (uint8_t)(i + 1)); usb_pause_mtx(&sc->sc_mtx, 1); urtw_8225_write(sc, URTW_8225_ADDR_2_MAGIC, urtw_8225v2b_rxgain[i]); usb_pause_mtx(&sc->sc_mtx, 1); } urtw_8225_write(sc, URTW_8225_ADDR_3_MAGIC, 0x080); usb_pause_mtx(&sc->sc_mtx, 1); urtw_8225_write(sc, URTW_8225_ADDR_5_MAGIC, 0x004); usb_pause_mtx(&sc->sc_mtx, 1); urtw_8225_write(sc, URTW_8225_ADDR_0_MAGIC, 0x0b7); usb_pause_mtx(&sc->sc_mtx, 1); usb_pause_mtx(&sc->sc_mtx, 3000); urtw_8225_write(sc, URTW_8225_ADDR_2_MAGIC, 0xc4d); usb_pause_mtx(&sc->sc_mtx, 2000); urtw_8225_write(sc, URTW_8225_ADDR_2_MAGIC, 0x44d); usb_pause_mtx(&sc->sc_mtx, 1); urtw_8225_write(sc, URTW_8225_ADDR_0_MAGIC, 0x2bf); usb_pause_mtx(&sc->sc_mtx, 1); 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 < 128; i++) { uint32_t addr, data; data = (urtw_8225z2_agc[i] << 8) | 0x0000008f; addr = ((i + 0x80) << 8) | 0x0000008e; urtw_8187_write_phy_ofdm(sc, data & 0x7f, (data >> 8) & 0xff); urtw_8187_write_phy_ofdm(sc, addr & 0x7f, (addr >> 8) & 0xff); urtw_8187_write_phy_ofdm(sc, 0x0e, 0x00); } 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, URTW_8187B_AC_VO, (7 << 12) | (3 << 8) | 0x1c); urtw_write32_m(sc, URTW_8187B_AC_VI, (7 << 12) | (3 << 8) | 0x1c); urtw_write32_m(sc, URTW_8187B_AC_BE, (7 << 12) | (3 << 8) | 0x1c); urtw_write32_m(sc, URTW_8187B_AC_BK, (7 << 12) | (3 << 8) | 0x1c); 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 usb_error_t urtw_8225v2b_rf_set_chan(struct urtw_softc *sc, int chan) { usb_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]); usb_pause_mtx(&sc->sc_mtx, 10); fail: return (error); } static usb_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; usb_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); usb_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); } } usb_pause_mtx(&sc->sc_mtx, 1); fail: return (error); } static usb_error_t urtw_read8e(struct urtw_softc *sc, int val, uint8_t *data) { struct usb_device_request req; usb_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 usb_error_t urtw_write8e(struct urtw_softc *sc, int val, uint8_t data) { struct usb_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 usb_error_t urtw_8180_set_anaparam(struct urtw_softc *sc, uint32_t val) { uint8_t data; usb_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 usb_error_t urtw_8185_set_anaparam2(struct urtw_softc *sc, uint32_t val) { uint8_t data; usb_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 usb_error_t urtw_intr_enable(struct urtw_softc *sc) { usb_error_t error; urtw_write16_m(sc, URTW_INTR_MASK, 0xffff); fail: return (error); } static usb_error_t urtw_intr_disable(struct urtw_softc *sc) { usb_error_t error; urtw_write16_m(sc, URTW_INTR_MASK, 0); fail: return (error); } static usb_error_t urtw_reset(struct urtw_softc *sc) { uint8_t data; usb_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; usb_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; usb_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); usb_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; usb_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 usb_error_t urtw_led_ctl(struct urtw_softc *sc, int mode) { usb_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 usb_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; usb_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); usb_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 usb_error_t urtw_led_mode1(struct urtw_softc *sc, int mode) { return (USB_ERR_INVAL); } static usb_error_t urtw_led_mode2(struct urtw_softc *sc, int mode) { return (USB_ERR_INVAL); } static usb_error_t urtw_led_mode3(struct urtw_softc *sc, int mode) { return (USB_ERR_INVAL); } static usb_error_t urtw_led_on(struct urtw_softc *sc, int type) { usb_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 usb_error_t urtw_led_off(struct urtw_softc *sc, int type) { usb_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 usb_error_t urtw_led_blink(struct urtw_softc *sc) { uint8_t ing = 0; usb_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: usb_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 usb_error_t urtw_rx_enable(struct urtw_softc *sc) { uint8_t data; usb_error_t error; usbd_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; if ((sc->sc_flags & URTW_RTL8187B) == 0) { urtw_read8_m(sc, URTW_CMD, &data); urtw_write8_m(sc, URTW_CMD, data | URTW_CMD_RX_ENABLE); } fail: return (error); } static usb_error_t urtw_tx_enable(struct urtw_softc *sc) { uint8_t data8; uint32_t data; usb_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_MSR, &data8); data8 |= URTW_MSR_LINK_ENEDCA; urtw_write8_m(sc, URTW_MSR, data8); 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 usb_error_t urtw_rx_setconf(struct urtw_softc *sc) { struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; uint32_t data; usb_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 usb_xfer *xfer, struct urtw_data *data, int *rssi_p, int8_t *nf_p) { int actlen, flen, rssi; struct ieee80211_frame *wh; struct mbuf *m, *mnew; struct urtw_softc *sc = data->sc; struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; uint8_t noise = 0, rate; usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); if (actlen < URTW_MIN_RXBUFSZ) { ifp->if_ierrors++; return (NULL); } if (sc->sc_flags & URTW_RTL8187B) { struct urtw_8187b_rxhdr *rx; rx = (struct urtw_8187b_rxhdr *)(data->buf + (actlen - (sizeof(struct urtw_8187b_rxhdr)))); flen = le32toh(rx->flag) & 0xfff; if (flen > actlen) { ifp->if_ierrors++; return (NULL); } rate = (le32toh(rx->flag) >> URTW_RX_FLAG_RXRATE_SHIFT) & 0xf; /* XXX correct? */ rssi = rx->rssi & URTW_RX_RSSI_MASK; noise = rx->noise; } else { struct urtw_8187l_rxhdr *rx; rx = (struct urtw_8187l_rxhdr *)(data->buf + (actlen - (sizeof(struct urtw_8187l_rxhdr)))); flen = le32toh(rx->flag) & 0xfff; if (flen > actlen) { ifp->if_ierrors++; return (NULL); } rate = (le32toh(rx->flag) >> URTW_RX_FLAG_RXRATE_SHIFT) & 0xf; /* XXX correct? */ rssi = rx->rssi & URTW_RX_8187L_RSSI_MASK; noise = rx->noise; } 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 - IEEE80211_CRC_LEN; 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; *rssi_p = rssi; *nf_p = noise; /* XXX correct? */ return (m); } static void urtw_bulk_rx_callback(struct usb_xfer *xfer, usb_error_t error) { struct urtw_softc *sc = usbd_xfer_softc(xfer); 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); usbd_xfer_set_frame_data(xfer, 0, data->buf, usbd_xfer_max_len(xfer)); usbd_transfer_submit(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 (error != USB_ERR_CANCELLED) { usbd_xfer_set_stall(xfer); ifp->if_ierrors++; goto setup; } break; } } #define URTW_STATUS_TYPE_TXCLOSE 1 #define URTW_STATUS_TYPE_BEACON_INTR 0 static void urtw_txstatus_eof(struct usb_xfer *xfer) { struct urtw_softc *sc = usbd_xfer_softc(xfer); struct ifnet *ifp = sc->sc_ifp; int actlen, type, pktretry, seq; uint64_t val; usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); if (actlen != sizeof(uint64_t)) return; val = le64toh(sc->sc_txstatus); type = (val >> 30) & 0x3; if (type == URTW_STATUS_TYPE_TXCLOSE) { pktretry = val & 0xff; seq = (val >> 16) & 0xff; if (pktretry == URTW_TX_MAXRETRY) ifp->if_oerrors++; DPRINTF(sc, URTW_DEBUG_TXSTATUS, "pktretry %d seq %#x\n", pktretry, seq); } } static void urtw_bulk_tx_status_callback(struct usb_xfer *xfer, usb_error_t error) { struct urtw_softc *sc = usbd_xfer_softc(xfer); struct ifnet *ifp = sc->sc_ifp; URTW_ASSERT_LOCKED(sc); switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: urtw_txstatus_eof(xfer); /* FALLTHROUGH */ case USB_ST_SETUP: setup: usbd_xfer_set_frame_data(xfer, 0, &sc->sc_txstatus, sizeof(int64_t)); usbd_transfer_submit(xfer); break; default: if (error != USB_ERR_CANCELLED) { usbd_xfer_set_stall(xfer); ifp->if_ierrors++; goto setup; } break; } } static void urtw_txeof(struct usb_xfer *xfer, struct urtw_data *data) { struct urtw_softc *sc = usbd_xfer_softc(xfer); 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 usb_xfer *xfer, usb_error_t error) { struct urtw_softc *sc = usbd_xfer_softc(xfer); 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); usbd_xfer_set_frame_data(xfer, 0, data->buf, data->buflen); usbd_transfer_submit(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 (error != USB_ERR_CANCELLED) { usbd_xfer_set_stall(xfer); 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) { return ((rate <= 22 && rate != 12 && rate != 18) || rate == 44) ? (1) : (0); } static uint16_t urtw_rate2dbps(uint16_t rate) { switch(rate) { case 12: case 18: case 24: case 36: case 48: case 72: case 96: case 108: return (rate * 2); default: break; } return (24); } static int urtw_compute_txtime(uint16_t framelen, uint16_t rate, uint8_t ismgt, uint8_t isshort) { uint16_t ceiling, frametime, n_dbps; if (urtw_isbmode(rate)) { if (ismgt || !isshort || rate == 2) frametime = (uint16_t)(144 + 48 + (framelen * 8 / (rate / 2))); else frametime = (uint16_t)(72 + 24 + (framelen * 8 / (rate / 2))); if ((framelen * 8 % (rate / 2)) != 0) frametime++; } else { n_dbps = urtw_rate2dbps(rate); ceiling = (16 + 8 * framelen + 6) / n_dbps + (((16 + 8 * framelen + 6) % n_dbps) ? 1 : 0); frametime = (uint16_t)(16 + 4 + 4 * ceiling + 6); } return (frametime); } /* * Callback from the 802.11 layer to update the * slot time based on the current setting. */ static void urtw_updateslot(struct ifnet *ifp) { struct urtw_softc *sc = ifp->if_softc; struct ieee80211com *ic = ifp->if_l2com; ieee80211_runtask(ic, &sc->sc_updateslot_task); } static void urtw_updateslottask(void *arg, int pending) { struct urtw_softc *sc = arg; struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; int error; if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) return; URTW_LOCK(sc); if (sc->sc_flags & URTW_RTL8187B) { urtw_write8_m(sc, URTW_SIFS, 0x22); if (IEEE80211_IS_CHAN_ANYG(ic->ic_curchan)) urtw_write8_m(sc, URTW_SLOT, 0x9); else urtw_write8_m(sc, URTW_SLOT, 0x14); urtw_write8_m(sc, URTW_8187B_EIFS, 0x5b); urtw_write8_m(sc, URTW_CARRIER_SCOUNT, 0x5b); } else { 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_ANYG(ic->ic_curchan)) { urtw_write8_m(sc, URTW_DIFS, 0x14); urtw_write8_m(sc, URTW_EIFS, 0x5b - 0x14); urtw_write8_m(sc, URTW_CW_VAL, 0x73); } else { urtw_write8_m(sc, URTW_DIFS, 0x24); urtw_write8_m(sc, URTW_EIFS, 0x5b - 0x24); urtw_write8_m(sc, URTW_CW_VAL, 0xa5); } } fail: URTW_UNLOCK(sc); } static void urtw_sysctl_node(struct urtw_softc *sc) { #define URTW_SYSCTL_STAT_ADD32(c, h, n, p, d) \ SYSCTL_ADD_UINT(c, h, OID_AUTO, n, CTLFLAG_RD, p, 0, d) struct sysctl_ctx_list *ctx; struct sysctl_oid_list *child, *parent; struct sysctl_oid *tree; struct urtw_stats *stats = &sc->sc_stats; ctx = device_get_sysctl_ctx(sc->sc_dev); child = SYSCTL_CHILDREN(device_get_sysctl_tree(sc->sc_dev)); tree = SYSCTL_ADD_NODE(ctx, child, OID_AUTO, "stats", CTLFLAG_RD, NULL, "URTW statistics"); parent = SYSCTL_CHILDREN(tree); /* Tx statistics. */ tree = SYSCTL_ADD_NODE(ctx, parent, OID_AUTO, "tx", CTLFLAG_RD, NULL, "Tx MAC statistics"); child = SYSCTL_CHILDREN(tree); URTW_SYSCTL_STAT_ADD32(ctx, child, "1m", &stats->txrates[0], "1 Mbit/s"); URTW_SYSCTL_STAT_ADD32(ctx, child, "2m", &stats->txrates[1], "2 Mbit/s"); URTW_SYSCTL_STAT_ADD32(ctx, child, "5.5m", &stats->txrates[2], "5.5 Mbit/s"); URTW_SYSCTL_STAT_ADD32(ctx, child, "6m", &stats->txrates[4], "6 Mbit/s"); URTW_SYSCTL_STAT_ADD32(ctx, child, "9m", &stats->txrates[5], "9 Mbit/s"); URTW_SYSCTL_STAT_ADD32(ctx, child, "11m", &stats->txrates[3], "11 Mbit/s"); URTW_SYSCTL_STAT_ADD32(ctx, child, "12m", &stats->txrates[6], "12 Mbit/s"); URTW_SYSCTL_STAT_ADD32(ctx, child, "18m", &stats->txrates[7], "18 Mbit/s"); URTW_SYSCTL_STAT_ADD32(ctx, child, "24m", &stats->txrates[8], "24 Mbit/s"); URTW_SYSCTL_STAT_ADD32(ctx, child, "36m", &stats->txrates[9], "36 Mbit/s"); URTW_SYSCTL_STAT_ADD32(ctx, child, "48m", &stats->txrates[10], "48 Mbit/s"); URTW_SYSCTL_STAT_ADD32(ctx, child, "54m", &stats->txrates[11], "54 Mbit/s"); #undef URTW_SYSCTL_STAT_ADD32 } 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); MODULE_VERSION(urtw, 1);