/* $OpenBSD: if_urtw.c,v 1.66 2017/08/18 00:49:51 kevlo Exp $ */ /*- * Copyright (c) 2009 Martynas Venckus * 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 "bpfilter.h" #include #include #include #include #include #include #include #include #include #include #if NBPFILTER > 0 #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #ifdef URTW_DEBUG #define DPRINTF(x) do { if (urtw_debug) printf x; } while (0) #define DPRINTFN(n, x) do { if (urtw_debug >= (n)) printf x; } while (0) int urtw_debug = 0; #else #define DPRINTF(x) #define DPRINTFN(n, x) #endif /* * Recognized device vendors/products. */ static const struct urtw_type { struct usb_devno dev; uint8_t rev; } urtw_devs[] = { #define URTW_DEV_RTL8187(v, p) \ { { USB_VENDOR_##v, USB_PRODUCT_##v##_##p }, URTW_HWREV_8187 } #define URTW_DEV_RTL8187B(v, p) \ { { USB_VENDOR_##v, USB_PRODUCT_##v##_##p }, URTW_HWREV_8187B } /* Realtek RTL8187 devices. */ URTW_DEV_RTL8187(ASUS, P5B_WIFI), URTW_DEV_RTL8187(DICKSMITH, RTL8187), URTW_DEV_RTL8187(LINKSYS4, WUSB54GCV2), URTW_DEV_RTL8187(LOGITEC, RTL8187), URTW_DEV_RTL8187(NETGEAR, WG111V2), URTW_DEV_RTL8187(REALTEK, RTL8187), URTW_DEV_RTL8187(SITECOMEU, WL168V1), URTW_DEV_RTL8187(SPHAIRON, RTL8187), URTW_DEV_RTL8187(SURECOM, EP9001G2A), /* Realtek RTL8187B devices. */ URTW_DEV_RTL8187B(BELKIN, F5D7050E), URTW_DEV_RTL8187B(NETGEAR, WG111V3), URTW_DEV_RTL8187B(REALTEK, RTL8187B_0), URTW_DEV_RTL8187B(REALTEK, RTL8187B_1), URTW_DEV_RTL8187B(REALTEK, RTL8187B_2), URTW_DEV_RTL8187B(SITECOMEU, WL168V4) #undef URTW_DEV_RTL8187 #undef URTW_DEV_RTL8187B }; #define urtw_lookup(v, p) \ ((const struct urtw_type *)usb_lookup(urtw_devs, v, p)) /* * Helper read/write macros. */ #define urtw_read8_m(sc, val, data) do { \ error = urtw_read8_c(sc, val, data, 0); \ if (error != 0) \ goto fail; \ } while (0) #define urtw_read8_idx_m(sc, val, data, idx) do { \ error = urtw_read8_c(sc, val, data, idx); \ if (error != 0) \ goto fail; \ } while (0) #define urtw_write8_m(sc, val, data) do { \ error = urtw_write8_c(sc, val, data, 0); \ if (error != 0) \ goto fail; \ } while (0) #define urtw_write8_idx_m(sc, val, data, idx) do { \ error = urtw_write8_c(sc, val, data, idx); \ if (error != 0) \ goto fail; \ } while (0) #define urtw_read16_m(sc, val, data) do { \ error = urtw_read16_c(sc, val, data, 0); \ if (error != 0) \ goto fail; \ } while (0) #define urtw_read16_idx_m(sc, val, data, idx) do { \ error = urtw_read16_c(sc, val, data, idx); \ if (error != 0) \ goto fail; \ } while (0) #define urtw_write16_m(sc, val, data) do { \ error = urtw_write16_c(sc, val, data, 0); \ if (error != 0) \ goto fail; \ } while (0) #define urtw_write16_idx_m(sc, val, data, idx) do { \ error = urtw_write16_c(sc, val, data, idx); \ if (error != 0) \ goto fail; \ } while (0) #define urtw_read32_m(sc, val, data) do { \ error = urtw_read32_c(sc, val, data, 0); \ if (error != 0) \ goto fail; \ } while (0) #define urtw_read32_idx_m(sc, val, data, idx) do { \ error = urtw_read32_c(sc, val, data, idx); \ if (error != 0) \ goto fail; \ } while (0) #define urtw_write32_m(sc, val, data) do { \ error = urtw_write32_c(sc, val, data, 0); \ if (error != 0) \ goto fail; \ } while (0) #define urtw_write32_idx_m(sc, val, data, idx) do { \ error = urtw_write32_c(sc, val, data, idx); \ 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; }; struct urtw_pair_idx { uint8_t reg; uint8_t val; uint8_t idx; }; static struct urtw_pair_idx urtw_8187b_regtbl[] = { { 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 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 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_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 uint8_t urtw_8225v2_ofdm[] = { 0x10, 0x0d, 0x01, 0x00, 0x14, 0xfb, 0xfb, 0x60, 0x00, 0x60, 0x00, 0x00, 0x00, 0x5c, 0x00, 0x00, 0x40, 0x00, 0x40, 0x00, 0x00, 0x00, 0xa8, 0x26, 0x32, 0x33, 0x07, 0xa5, 0x6f, 0x55, 0xc8, 0xb3, 0x0a, 0xe1, 0x2c, 0x8a, 0x86, 0x83, 0x34, 0x0f, 0x4f, 0x24, 0x6f, 0xc2, 0x6b, 0x40, 0x80, 0x00, 0xc0, 0xc1, 0x58, 0xf1, 0x00, 0xe4, 0x90, 0x3e, 0x6d, 0x3c, 0xfb, 0x07 }; 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_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_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[] = { 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, 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_8225v2_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_8225v2_b_rf[] = { { 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 }, { 0x00, 0x01b7 } }; 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 } }; int urtw_init(struct ifnet *); void urtw_stop(struct ifnet *, int); int urtw_ioctl(struct ifnet *, u_long, caddr_t); void urtw_start(struct ifnet *); int urtw_alloc_rx_data_list(struct urtw_softc *); void urtw_free_rx_data_list(struct urtw_softc *); int urtw_alloc_tx_data_list(struct urtw_softc *); void urtw_free_tx_data_list(struct urtw_softc *); void urtw_rxeof(struct usbd_xfer *, void *, usbd_status); int urtw_tx_start(struct urtw_softc *, struct ieee80211_node *, struct mbuf *, int); void urtw_txeof_low(struct usbd_xfer *, void *, usbd_status); void urtw_txeof_normal(struct usbd_xfer *, void *, usbd_status); void urtw_next_scan(void *); void urtw_task(void *); void urtw_ledusbtask(void *); void urtw_ledtask(void *); int urtw_media_change(struct ifnet *); int urtw_newstate(struct ieee80211com *, enum ieee80211_state, int); void urtw_watchdog(struct ifnet *); void urtw_set_multi(struct urtw_softc *); void urtw_set_chan(struct urtw_softc *, struct ieee80211_channel *); int urtw_isbmode(uint16_t); uint16_t urtw_rate2rtl(int rate); uint16_t urtw_rtl2rate(int); usbd_status urtw_set_rate(struct urtw_softc *); usbd_status urtw_update_msr(struct urtw_softc *); usbd_status urtw_read8_c(struct urtw_softc *, int, uint8_t *, uint8_t); usbd_status urtw_read16_c(struct urtw_softc *, int, uint16_t *, uint8_t); usbd_status urtw_read32_c(struct urtw_softc *, int, uint32_t *, uint8_t); usbd_status urtw_write8_c(struct urtw_softc *, int, uint8_t, uint8_t); usbd_status urtw_write16_c(struct urtw_softc *, int, uint16_t, uint8_t); usbd_status urtw_write32_c(struct urtw_softc *, int, uint32_t, uint8_t); usbd_status urtw_eprom_cs(struct urtw_softc *, int); usbd_status urtw_eprom_ck(struct urtw_softc *); usbd_status urtw_eprom_sendbits(struct urtw_softc *, int16_t *, int); usbd_status urtw_eprom_read32(struct urtw_softc *, uint32_t, uint32_t *); usbd_status urtw_eprom_readbit(struct urtw_softc *, int16_t *); usbd_status urtw_eprom_writebit(struct urtw_softc *, int16_t); usbd_status urtw_get_macaddr(struct urtw_softc *); usbd_status urtw_get_txpwr(struct urtw_softc *); usbd_status urtw_get_rfchip(struct urtw_softc *); usbd_status urtw_led_init(struct urtw_softc *); usbd_status urtw_8185_rf_pins_enable(struct urtw_softc *); usbd_status urtw_8185_tx_antenna(struct urtw_softc *, uint8_t); usbd_status urtw_8187_write_phy(struct urtw_softc *, uint8_t, uint32_t); usbd_status urtw_8187_write_phy_ofdm_c(struct urtw_softc *, uint8_t, uint32_t); usbd_status urtw_8187_write_phy_cck_c(struct urtw_softc *, uint8_t, uint32_t); usbd_status urtw_8225_setgain(struct urtw_softc *, int16_t); usbd_status urtw_8225_usb_init(struct urtw_softc *); usbd_status urtw_8225_write_c(struct urtw_softc *, uint8_t, uint16_t); usbd_status urtw_8225_write_s16(struct urtw_softc *, uint8_t, int, uint16_t); usbd_status urtw_8225_read(struct urtw_softc *, uint8_t, uint32_t *); usbd_status urtw_8225_rf_init(struct urtw_rf *); usbd_status urtw_8225_rf_set_chan(struct urtw_rf *, int); usbd_status urtw_8225_rf_set_sens(struct urtw_rf *); usbd_status urtw_8225_set_txpwrlvl(struct urtw_softc *, int); usbd_status urtw_8225v2_rf_init(struct urtw_rf *); usbd_status urtw_8225v2_rf_set_chan(struct urtw_rf *, int); usbd_status urtw_8225v2_set_txpwrlvl(struct urtw_softc *, int); usbd_status urtw_8225v2_setgain(struct urtw_softc *, int16_t); usbd_status urtw_8225_isv2(struct urtw_softc *, int *); usbd_status urtw_read8e(struct urtw_softc *, int, uint8_t *); usbd_status urtw_write8e(struct urtw_softc *, int, uint8_t); usbd_status urtw_8180_set_anaparam(struct urtw_softc *, uint32_t); usbd_status urtw_8185_set_anaparam2(struct urtw_softc *, uint32_t); usbd_status urtw_open_pipes(struct urtw_softc *); usbd_status urtw_close_pipes(struct urtw_softc *); usbd_status urtw_intr_enable(struct urtw_softc *); usbd_status urtw_intr_disable(struct urtw_softc *); usbd_status urtw_reset(struct urtw_softc *); usbd_status urtw_led_on(struct urtw_softc *, int); usbd_status urtw_led_ctl(struct urtw_softc *, int); usbd_status urtw_led_blink(struct urtw_softc *); usbd_status urtw_led_mode0(struct urtw_softc *, int); usbd_status urtw_led_mode1(struct urtw_softc *, int); usbd_status urtw_led_mode2(struct urtw_softc *, int); usbd_status urtw_led_mode3(struct urtw_softc *, int); usbd_status urtw_rx_setconf(struct urtw_softc *); usbd_status urtw_rx_enable(struct urtw_softc *); usbd_status urtw_tx_enable(struct urtw_softc *); usbd_status urtw_8187b_update_wmm(struct urtw_softc *); usbd_status urtw_8187b_reset(struct urtw_softc *); int urtw_8187b_init(struct ifnet *); usbd_status urtw_8225v2_b_config_mac(struct urtw_softc *); usbd_status urtw_8225v2_b_init_rfe(struct urtw_softc *); usbd_status urtw_8225v2_b_update_chan(struct urtw_softc *); usbd_status urtw_8225v2_b_rf_init(struct urtw_rf *); usbd_status urtw_8225v2_b_rf_set_chan(struct urtw_rf *, int); usbd_status urtw_8225v2_b_set_txpwrlvl(struct urtw_softc *, int); int urtw_set_bssid(struct urtw_softc *, const uint8_t *); int urtw_set_macaddr(struct urtw_softc *, const uint8_t *); int urtw_match(struct device *, void *, void *); void urtw_attach(struct device *, struct device *, void *); int urtw_detach(struct device *, int); struct cfdriver urtw_cd = { NULL, "urtw", DV_IFNET }; const struct cfattach urtw_ca = { sizeof(struct urtw_softc), urtw_match, urtw_attach, urtw_detach }; int urtw_match(struct device *parent, void *match, void *aux) { struct usb_attach_arg *uaa = aux; if (uaa->iface == NULL || uaa->configno != 1) return (UMATCH_NONE); return ((urtw_lookup(uaa->vendor, uaa->product) != NULL) ? UMATCH_VENDOR_PRODUCT_CONF_IFACE : UMATCH_NONE); } void urtw_attach(struct device *parent, struct device *self, void *aux) { struct urtw_softc *sc = (struct urtw_softc *)self; struct usb_attach_arg *uaa = aux; struct ieee80211com *ic = &sc->sc_ic; struct ifnet *ifp = &ic->ic_if; usbd_status error; uint8_t data8; uint32_t data; int i; sc->sc_udev = uaa->device; sc->sc_iface = uaa->iface; sc->sc_hwrev = urtw_lookup(uaa->vendor, uaa->product)->rev; printf("%s: ", sc->sc_dev.dv_xname); if (sc->sc_hwrev & URTW_HWREV_8187) { urtw_read32_m(sc, URTW_TX_CONF, &data); data &= URTW_TX_HWREV_MASK; switch (data) { case URTW_TX_HWREV_8187_D: sc->sc_hwrev |= URTW_HWREV_8187_D; printf("RTL8187 rev D"); break; case URTW_TX_HWREV_8187B_D: /* * Detect Realtek RTL8187B devices that use * USB IDs of RTL8187. */ sc->sc_hwrev = URTW_HWREV_8187B | URTW_HWREV_8187B_B; printf("RTL8187B rev B (early)"); break; default: sc->sc_hwrev |= URTW_HWREV_8187_B; printf("RTL8187 rev 0x%02x", data >> 25); break; } } else { /* RTL8187B hwrev register. */ urtw_read8_m(sc, URTW_8187B_HWREV, &data8); switch (data8) { case URTW_8187B_HWREV_8187B_B: sc->sc_hwrev |= URTW_HWREV_8187B_B; printf("RTL8187B rev B"); break; case URTW_8187B_HWREV_8187B_D: sc->sc_hwrev |= URTW_HWREV_8187B_D; printf("RTL8187B rev D"); break; case URTW_8187B_HWREV_8187B_E: sc->sc_hwrev |= URTW_HWREV_8187B_E; printf("RTL8187B rev E"); break; default: sc->sc_hwrev |= URTW_HWREV_8187B_B; printf("RTL8187B rev 0x%02x", data8); break; } } 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); /* XXX incompleted */ if (error != 0) goto fail; sc->sc_rts_retry = URTW_DEFAULT_RTS_RETRY; sc->sc_tx_retry = URTW_DEFAULT_TX_RETRY; sc->sc_currate = 3; /* XXX for what? */ sc->sc_preamble_mode = 2; usb_init_task(&sc->sc_task, urtw_task, sc, USB_TASK_TYPE_GENERIC); usb_init_task(&sc->sc_ledtask, urtw_ledusbtask, sc, USB_TASK_TYPE_GENERIC); timeout_set(&sc->scan_to, urtw_next_scan, sc); timeout_set(&sc->sc_led_ch, urtw_ledtask, sc); ic->ic_phytype = IEEE80211_T_OFDM; /* not only, but not used */ ic->ic_opmode = IEEE80211_M_STA; /* default to BSS mode */ ic->ic_state = IEEE80211_S_INIT; /* set device capabilities */ ic->ic_caps = IEEE80211_C_MONITOR | /* monitor mode supported */ IEEE80211_C_TXPMGT | /* tx power management */ IEEE80211_C_SHPREAMBLE | /* short preamble supported */ IEEE80211_C_SHSLOT | /* short slot time supported */ IEEE80211_C_WEP | /* s/w WEP */ IEEE80211_C_RSN; /* WPA/RSN */ /* set supported .11b and .11g rates */ ic->ic_sup_rates[IEEE80211_MODE_11B] = ieee80211_std_rateset_11b; ic->ic_sup_rates[IEEE80211_MODE_11G] = ieee80211_std_rateset_11g; /* set supported .11b and .11g channels (1 through 14) */ for (i = 1; i <= 14; i++) { ic->ic_channels[i].ic_freq = ieee80211_ieee2mhz(i, IEEE80211_CHAN_2GHZ); ic->ic_channels[i].ic_flags = IEEE80211_CHAN_CCK | IEEE80211_CHAN_OFDM | IEEE80211_CHAN_DYN | IEEE80211_CHAN_2GHZ; } ifp->if_softc = sc; ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; if (sc->sc_hwrev & URTW_HWREV_8187) { sc->sc_init = urtw_init; } else { sc->sc_init = urtw_8187b_init; } ifp->if_ioctl = urtw_ioctl; ifp->if_start = urtw_start; ifp->if_watchdog = urtw_watchdog; memcpy(ifp->if_xname, sc->sc_dev.dv_xname, IFNAMSIZ); if_attach(ifp); ieee80211_ifattach(ifp); /* override state transition machine */ sc->sc_newstate = ic->ic_newstate; ic->ic_newstate = urtw_newstate; ieee80211_media_init(ifp, urtw_media_change, ieee80211_media_status); #if NBPFILTER > 0 bpfattach(&sc->sc_drvbpf, ifp, DLT_IEEE802_11_RADIO, sizeof (struct ieee80211_frame) + IEEE80211_RADIOTAP_HDRLEN); sc->sc_rxtap_len = sizeof sc->sc_rxtapu; sc->sc_rxtap.wr_ihdr.it_len = htole16(sc->sc_rxtap_len); sc->sc_rxtap.wr_ihdr.it_present = htole32(URTW_RX_RADIOTAP_PRESENT); sc->sc_txtap_len = sizeof sc->sc_txtapu; sc->sc_txtap.wt_ihdr.it_len = htole16(sc->sc_txtap_len); sc->sc_txtap.wt_ihdr.it_present = htole32(URTW_TX_RADIOTAP_PRESENT); #endif printf(", address %s\n", ether_sprintf(ic->ic_myaddr)); return; fail: printf(": %s failed!\n", __func__); } int urtw_detach(struct device *self, int flags) { struct urtw_softc *sc = (struct urtw_softc *)self; struct ifnet *ifp = &sc->sc_ic.ic_if; int s; s = splusb(); if (timeout_initialized(&sc->scan_to)) timeout_del(&sc->scan_to); if (timeout_initialized(&sc->sc_led_ch)) timeout_del(&sc->sc_led_ch); usb_rem_wait_task(sc->sc_udev, &sc->sc_task); usb_rem_wait_task(sc->sc_udev, &sc->sc_ledtask); usbd_ref_wait(sc->sc_udev); if (ifp->if_softc != NULL) { ieee80211_ifdetach(ifp); /* free all nodes */ if_detach(ifp); } /* abort and free xfers */ urtw_free_tx_data_list(sc); urtw_free_rx_data_list(sc); urtw_close_pipes(sc); splx(s); return (0); } usbd_status urtw_close_pipes(struct urtw_softc *sc) { usbd_status error = 0; if (sc->sc_rxpipe != NULL) { error = usbd_close_pipe(sc->sc_rxpipe); if (error != 0) goto fail; sc->sc_rxpipe = NULL; } if (sc->sc_txpipe_low != NULL) { error = usbd_close_pipe(sc->sc_txpipe_low); if (error != 0) goto fail; sc->sc_txpipe_low = NULL; } if (sc->sc_txpipe_normal != NULL) { error = usbd_close_pipe(sc->sc_txpipe_normal); if (error != 0) goto fail; sc->sc_txpipe_normal = NULL; } fail: return (error); } usbd_status urtw_open_pipes(struct urtw_softc *sc) { usbd_status error; /* * NB: there is no way to distinguish each pipes so we need to hardcode * pipe numbers */ /* tx pipe - low priority packets */ if (sc->sc_hwrev & URTW_HWREV_8187) error = usbd_open_pipe(sc->sc_iface, 0x2, USBD_EXCLUSIVE_USE, &sc->sc_txpipe_low); else error = usbd_open_pipe(sc->sc_iface, 0x6, USBD_EXCLUSIVE_USE, &sc->sc_txpipe_low); if (error != 0) { printf("%s: could not open Tx low pipe: %s\n", sc->sc_dev.dv_xname, usbd_errstr(error)); goto fail; } /* tx pipe - normal priority packets */ if (sc->sc_hwrev & URTW_HWREV_8187) error = usbd_open_pipe(sc->sc_iface, 0x3, USBD_EXCLUSIVE_USE, &sc->sc_txpipe_normal); else error = usbd_open_pipe(sc->sc_iface, 0x7, USBD_EXCLUSIVE_USE, &sc->sc_txpipe_normal); if (error != 0) { printf("%s: could not open Tx normal pipe: %s\n", sc->sc_dev.dv_xname, usbd_errstr(error)); goto fail; } /* rx pipe */ if (sc->sc_hwrev & URTW_HWREV_8187) error = usbd_open_pipe(sc->sc_iface, 0x81, USBD_EXCLUSIVE_USE, &sc->sc_rxpipe); else error = usbd_open_pipe(sc->sc_iface, 0x83, USBD_EXCLUSIVE_USE, &sc->sc_rxpipe); if (error != 0) { printf("%s: could not open Rx pipe: %s\n", sc->sc_dev.dv_xname, usbd_errstr(error)); goto fail; } return (0); fail: (void)urtw_close_pipes(sc); return (error); } int urtw_alloc_rx_data_list(struct urtw_softc *sc) { int i, error; for (i = 0; i < URTW_RX_DATA_LIST_COUNT; i++) { struct urtw_rx_data *data = &sc->sc_rx_data[i]; data->sc = sc; data->xfer = usbd_alloc_xfer(sc->sc_udev); if (data->xfer == NULL) { printf("%s: could not allocate rx xfer\n", sc->sc_dev.dv_xname); error = ENOMEM; goto fail; } if (usbd_alloc_buffer(data->xfer, URTW_RX_MAXSIZE) == NULL) { printf("%s: could not allocate rx buffer\n", sc->sc_dev.dv_xname); error = ENOMEM; goto fail; } MGETHDR(data->m, M_DONTWAIT, MT_DATA); if (data->m == NULL) { printf("%s: could not allocate rx mbuf\n", sc->sc_dev.dv_xname); error = ENOMEM; goto fail; } MCLGET(data->m, M_DONTWAIT); if (!(data->m->m_flags & M_EXT)) { printf("%s: could not allocate rx mbuf cluster\n", sc->sc_dev.dv_xname); error = ENOMEM; goto fail; } data->buf = mtod(data->m, uint8_t *); } return (0); fail: urtw_free_rx_data_list(sc); return (error); } void urtw_free_rx_data_list(struct urtw_softc *sc) { int i; /* Make sure no transfers are pending. */ if (sc->sc_rxpipe != NULL) usbd_abort_pipe(sc->sc_rxpipe); for (i = 0; i < URTW_RX_DATA_LIST_COUNT; i++) { struct urtw_rx_data *data = &sc->sc_rx_data[i]; if (data->xfer != NULL) { usbd_free_xfer(data->xfer); data->xfer = NULL; } if (data->m != NULL) { m_freem(data->m); data->m = NULL; } } } int urtw_alloc_tx_data_list(struct urtw_softc *sc) { int i, error; for (i = 0; i < URTW_TX_DATA_LIST_COUNT; i++) { struct urtw_tx_data *data = &sc->sc_tx_data[i]; data->sc = sc; data->ni = NULL; data->xfer = usbd_alloc_xfer(sc->sc_udev); if (data->xfer == NULL) { printf("%s: could not allocate tx xfer\n", sc->sc_dev.dv_xname); error = ENOMEM; goto fail; } data->buf = usbd_alloc_buffer(data->xfer, URTW_TX_MAXSIZE); if (data->buf == NULL) { printf("%s: could not allocate tx buffer\n", sc->sc_dev.dv_xname); error = ENOMEM; goto fail; } if (((unsigned long)data->buf) % 4) printf("%s: warn: unaligned buffer %p\n", sc->sc_dev.dv_xname, data->buf); } return (0); fail: urtw_free_tx_data_list(sc); return (error); } void urtw_free_tx_data_list(struct urtw_softc *sc) { struct ieee80211com *ic = &sc->sc_ic; int i; /* Make sure no transfers are pending. */ if (sc->sc_txpipe_low != NULL) usbd_abort_pipe(sc->sc_txpipe_low); if (sc->sc_txpipe_normal != NULL) usbd_abort_pipe(sc->sc_txpipe_normal); for (i = 0; i < URTW_TX_DATA_LIST_COUNT; i++) { struct urtw_tx_data *data = &sc->sc_tx_data[i]; if (data->xfer != NULL) { usbd_free_xfer(data->xfer); data->xfer = NULL; } if (data->ni != NULL) { ieee80211_release_node(ic, data->ni); data->ni = NULL; } } } int urtw_media_change(struct ifnet *ifp) { struct urtw_softc *sc = ifp->if_softc; int error; error = ieee80211_media_change(ifp); if (error != ENETRESET) return (error); if ((ifp->if_flags & (IFF_UP | IFF_RUNNING)) == (IFF_UP | IFF_RUNNING)) error = sc->sc_init(ifp); return (error); } int urtw_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg) { struct urtw_softc *sc = ic->ic_if.if_softc; usb_rem_task(sc->sc_udev, &sc->sc_task); timeout_del(&sc->scan_to); /* do it in a process context */ sc->sc_state = nstate; sc->sc_arg = arg; usb_add_task(sc->sc_udev, &sc->sc_task); return (0); } usbd_status urtw_led_init(struct urtw_softc *sc) { uint32_t rev; usbd_status 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); } usbd_status urtw_8225_write_s16(struct urtw_softc *sc, uint8_t addr, int index, uint16_t data) { usb_device_request_t req; 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)); data = htole16(data); return (usbd_do_request(sc->sc_udev, &req, &data)); } usbd_status 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; usbd_status 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 | 0xf); urtw_write16_m(sc, URTW_RF_PINS_SELECT, o3 | 0xf); o1 &= ~0xf; 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, 0x3a0); if (data != NULL) *data = value; fail: return (error); } usbd_status urtw_8225_write_c(struct urtw_softc *sc, uint8_t addr, uint16_t data) { uint16_t d80, d82, d84; usbd_status error; urtw_read16_m(sc, URTW_RF_PINS_OUTPUT, &d80); d80 &= 0xfff3; urtw_read16_m(sc, URTW_RF_PINS_ENABLE, &d82); urtw_read16_m(sc, URTW_RF_PINS_SELECT, &d84); d84 &= 0xfff0; urtw_write16_m(sc, URTW_RF_PINS_ENABLE, d82 | 0x0007); urtw_write16_m(sc, URTW_RF_PINS_SELECT, d84 | 0x0007); 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); usbd_delay_ms(sc->sc_udev, 2); fail: return (error); } usbd_status urtw_8225_isv2(struct urtw_softc *sc, int *ret) { uint32_t data; usbd_status error; *ret = 1; urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, 0x0080); urtw_write16_m(sc, URTW_RF_PINS_SELECT, 0x0080); urtw_write16_m(sc, URTW_RF_PINS_ENABLE, 0x0080); usbd_delay_ms(sc->sc_udev, 500); urtw_8225_write(sc, 0x0, 0x1b7); error = urtw_8225_read(sc, 0x8, &data); if (error != 0) goto fail; if (data != 0x588) *ret = 0; else { error = urtw_8225_read(sc, 0x9, &data); if (error != 0) goto fail; if (data != 0x700) *ret = 0; } urtw_8225_write(sc, 0x0, 0xb7); fail: return (error); } usbd_status urtw_get_rfchip(struct urtw_softc *sc) { struct urtw_rf *rf = &sc->sc_rf; int ret; uint32_t data; usbd_status error; rf->rf_sc = sc; if (sc->sc_hwrev & URTW_HWREV_8187) { 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) { rf->init = urtw_8225_rf_init; rf->set_chan = urtw_8225_rf_set_chan; rf->set_sens = urtw_8225_rf_set_sens; printf(", RFv1"); } else { rf->init = urtw_8225v2_rf_init; rf->set_chan = urtw_8225v2_rf_set_chan; rf->set_sens = NULL; printf(", RFv2"); } break; default: goto fail; } } else { rf->init = urtw_8225v2_b_rf_init; rf->set_chan = urtw_8225v2_b_rf_set_chan; rf->set_sens = NULL; } rf->max_sens = URTW_8225_RF_MAX_SENS; rf->sens = URTW_8225_RF_DEF_SENS; return (0); fail: printf("unsupported RF chip %d", data & 0xff); return (error); } usbd_status urtw_get_txpwr(struct urtw_softc *sc) { int i, j; uint32_t data; usbd_status 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_hwrev & URTW_HWREV_8187) { 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; } } else { /* Channel 11. */ error = urtw_eprom_read32(sc, 0x1b, &data); if (error != 0) goto fail; sc->sc_txpwr_cck[11] = data & 0xf; sc->sc_txpwr_ofdm[11] = (data & 0xf0) >> 4; /* Channel 12. */ error = urtw_eprom_read32(sc, 0xa, &data); if (error != 0) goto fail; sc->sc_txpwr_cck[12] = data & 0xf; sc->sc_txpwr_ofdm[12] = (data & 0xf0) >> 4; /* Channel 13, 14. */ error = urtw_eprom_read32(sc, 0x1c, &data); if (error != 0) goto fail; sc->sc_txpwr_cck[13] = data & 0xf; sc->sc_txpwr_ofdm[13] = (data & 0xf0) >> 4; sc->sc_txpwr_cck[14] = (data & 0xf00) >> 8; sc->sc_txpwr_ofdm[14] = (data & 0xf000) >> 12; } fail: return (error); } usbd_status urtw_get_macaddr(struct urtw_softc *sc) { struct ieee80211com *ic = &sc->sc_ic; usbd_status error; uint32_t data; error = urtw_eprom_read32(sc, URTW_EPROM_MACADDR, &data); if (error != 0) goto fail; ic->ic_myaddr[0] = data & 0xff; ic->ic_myaddr[1] = (data & 0xff00) >> 8; error = urtw_eprom_read32(sc, URTW_EPROM_MACADDR + 1, &data); if (error != 0) goto fail; ic->ic_myaddr[2] = data & 0xff; ic->ic_myaddr[3] = (data & 0xff00) >> 8; error = urtw_eprom_read32(sc, URTW_EPROM_MACADDR + 2, &data); if (error != 0) goto fail; ic->ic_myaddr[4] = data & 0xff; ic->ic_myaddr[5] = (data & 0xff00) >> 8; fail: return (error); } usbd_status 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 }; usbd_status 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 } usbd_status urtw_eprom_readbit(struct urtw_softc *sc, int16_t *data) { uint8_t data8; usbd_status error; urtw_read8_m(sc, URTW_EPROM_CMD, &data8); *data = (data8 & URTW_EPROM_READBIT) ? 1 : 0; DELAY(URTW_EPROM_DELAY); fail: return (error); } usbd_status urtw_eprom_sendbits(struct urtw_softc *sc, int16_t *buf, int buflen) { int i = 0; usbd_status 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); } usbd_status urtw_eprom_writebit(struct urtw_softc *sc, int16_t bit) { uint8_t data; usbd_status 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); } usbd_status urtw_eprom_ck(struct urtw_softc *sc) { uint8_t data; usbd_status 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); } usbd_status urtw_eprom_cs(struct urtw_softc *sc, int able) { uint8_t data; usbd_status 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); } usbd_status urtw_read8_c(struct urtw_softc *sc, int val, uint8_t *data, uint8_t idx) { usb_device_request_t req; usbd_status error; req.bmRequestType = UT_READ_VENDOR_DEVICE; req.bRequest = URTW_8187_GETREGS_REQ; USETW(req.wValue, val | 0xff00); USETW(req.wIndex, idx & 0x03); USETW(req.wLength, sizeof(uint8_t)); error = usbd_do_request(sc->sc_udev, &req, data); return (error); } usbd_status urtw_read8e(struct urtw_softc *sc, int val, uint8_t *data) { usb_device_request_t req; usbd_status 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 = usbd_do_request(sc->sc_udev, &req, data); return (error); } usbd_status urtw_read16_c(struct urtw_softc *sc, int val, uint16_t *data, uint8_t idx) { usb_device_request_t req; usbd_status error; req.bmRequestType = UT_READ_VENDOR_DEVICE; req.bRequest = URTW_8187_GETREGS_REQ; USETW(req.wValue, val | 0xff00); USETW(req.wIndex, idx & 0x03); USETW(req.wLength, sizeof(uint16_t)); error = usbd_do_request(sc->sc_udev, &req, data); *data = letoh16(*data); return (error); } usbd_status urtw_read32_c(struct urtw_softc *sc, int val, uint32_t *data, uint8_t idx) { usb_device_request_t req; usbd_status error; req.bmRequestType = UT_READ_VENDOR_DEVICE; req.bRequest = URTW_8187_GETREGS_REQ; USETW(req.wValue, val | 0xff00); USETW(req.wIndex, idx & 0x03); USETW(req.wLength, sizeof(uint32_t)); error = usbd_do_request(sc->sc_udev, &req, data); *data = letoh32(*data); return (error); } usbd_status urtw_write8_c(struct urtw_softc *sc, int val, uint8_t data, uint8_t idx) { usb_device_request_t req; req.bmRequestType = UT_WRITE_VENDOR_DEVICE; req.bRequest = URTW_8187_SETREGS_REQ; USETW(req.wValue, val | 0xff00); USETW(req.wIndex, idx & 0x03); USETW(req.wLength, sizeof(uint8_t)); return (usbd_do_request(sc->sc_udev, &req, &data)); } usbd_status urtw_write8e(struct urtw_softc *sc, int val, uint8_t data) { usb_device_request_t 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 (usbd_do_request(sc->sc_udev, &req, &data)); } usbd_status urtw_write16_c(struct urtw_softc *sc, int val, uint16_t data, uint8_t idx) { usb_device_request_t req; req.bmRequestType = UT_WRITE_VENDOR_DEVICE; req.bRequest = URTW_8187_SETREGS_REQ; USETW(req.wValue, val | 0xff00); USETW(req.wIndex, idx & 0x03); USETW(req.wLength, sizeof(uint16_t)); data = htole16(data); return (usbd_do_request(sc->sc_udev, &req, &data)); } usbd_status urtw_write32_c(struct urtw_softc *sc, int val, uint32_t data, uint8_t idx) { usb_device_request_t req; req.bmRequestType = UT_WRITE_VENDOR_DEVICE; req.bRequest = URTW_8187_SETREGS_REQ; USETW(req.wValue, val | 0xff00); USETW(req.wIndex, idx & 0x03); USETW(req.wLength, sizeof(uint32_t)); data = htole32(data); return (usbd_do_request(sc->sc_udev, &req, &data)); } static usbd_status urtw_set_mode(struct urtw_softc *sc, uint32_t mode) { uint8_t data; usbd_status 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); } usbd_status urtw_8180_set_anaparam(struct urtw_softc *sc, uint32_t val) { uint8_t data; usbd_status 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); } usbd_status urtw_8185_set_anaparam2(struct urtw_softc *sc, uint32_t val) { uint8_t data; usbd_status 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); } usbd_status urtw_intr_disable(struct urtw_softc *sc) { usbd_status error; urtw_write16_m(sc, URTW_INTR_MASK, 0); fail: return (error); } usbd_status urtw_reset(struct urtw_softc *sc) { uint8_t data; usbd_status error; error = urtw_8180_set_anaparam(sc, URTW_8187_8225_ANAPARAM_ON); if (error) goto fail; error = urtw_8185_set_anaparam2(sc, URTW_8187_8225_ANAPARAM2_ON); if (error) goto fail; error = urtw_intr_disable(sc); if (error) goto fail; usbd_delay_ms(sc->sc_udev, 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; usbd_delay_ms(sc->sc_udev, 100); urtw_read8_m(sc, URTW_CMD, &data); data = (data & 2) | URTW_CMD_RST; urtw_write8_m(sc, URTW_CMD, data); usbd_delay_ms(sc->sc_udev, 100); urtw_read8_m(sc, URTW_CMD, &data); if (data & URTW_CMD_RST) { printf("%s: reset timeout\n", sc->sc_dev.dv_xname); goto fail; } error = urtw_set_mode(sc, URTW_EPROM_CMD_LOAD); if (error) goto fail; usbd_delay_ms(sc->sc_udev, 100); error = urtw_8180_set_anaparam(sc, URTW_8187_8225_ANAPARAM_ON); if (error) goto fail; error = urtw_8185_set_anaparam2(sc, URTW_8187_8225_ANAPARAM2_ON); if (error) goto fail; fail: return (error); } usbd_status urtw_led_on(struct urtw_softc *sc, int type) { usbd_status error = 0; 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: break; } } sc->sc_gpio_ledon = 1; fail: return (error); } static usbd_status urtw_led_off(struct urtw_softc *sc, int type) { usbd_status error = 0; 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, 0x01); break; default: break; } } sc->sc_gpio_ledon = 0; fail: return (error); } usbd_status urtw_led_mode0(struct urtw_softc *sc, int mode) { struct timeval t; 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: break; } 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; t.tv_sec = 0; t.tv_usec = 100 * 1000L; if (!usbd_is_dying(sc->sc_udev)) timeout_add(&sc->sc_led_ch, tvtohz(&t)); break; case URTW_LED_POWER_ON_BLINK: urtw_led_on(sc, URTW_LED_GPIO); usbd_delay_ms(sc->sc_udev, 100); urtw_led_off(sc, URTW_LED_GPIO); break; default: break; } return (0); } usbd_status urtw_led_mode1(struct urtw_softc *sc, int mode) { return (USBD_INVAL); } usbd_status urtw_led_mode2(struct urtw_softc *sc, int mode) { return (USBD_INVAL); } usbd_status urtw_led_mode3(struct urtw_softc *sc, int mode) { return (USBD_INVAL); } void urtw_ledusbtask(void *arg) { struct urtw_softc *sc = arg; if (sc->sc_strategy != URTW_SW_LED_MODE0) return; urtw_led_blink(sc); } void urtw_ledtask(void *arg) { struct urtw_softc *sc = arg; /* * NB: to change a status of the led we need at least a sleep so we * can't do it here */ usb_add_task(sc->sc_udev, &sc->sc_ledtask); } usbd_status urtw_led_ctl(struct urtw_softc *sc, int mode) { usbd_status 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: break; } return (error); } usbd_status urtw_led_blink(struct urtw_softc *sc) { struct timeval t; uint8_t ing = 0; usbd_status 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: t.tv_sec = 0; t.tv_usec = 100 * 1000L; if (!usbd_is_dying(sc->sc_udev)) timeout_add(&sc->sc_led_ch, tvtohz(&t)); break; default: break; } return (0); } usbd_status urtw_update_msr(struct urtw_softc *sc) { struct ieee80211com *ic = &sc->sc_ic; uint8_t data; usbd_status error; urtw_read8_m(sc, URTW_MSR, &data); data &= ~URTW_MSR_LINK_MASK; /* Should always be set. */ if (sc->sc_hwrev & URTW_HWREV_8187B) data |= URTW_MSR_LINK_ENEDCA; if (sc->sc_state == IEEE80211_S_RUN) { switch (ic->ic_opmode) { case IEEE80211_M_STA: case IEEE80211_M_MONITOR: data |= URTW_MSR_LINK_STA; break; default: break; } } else data |= URTW_MSR_LINK_NONE; urtw_write8_m(sc, URTW_MSR, data); fail: return (error); } uint16_t urtw_rate2rtl(int rate) { int i; for (i = 0; i < nitems(urtw_ratetable); i++) { if (rate == urtw_ratetable[i].reg) return (urtw_ratetable[i].val); } return (3); } uint16_t urtw_rtl2rate(int rate) { int i; for (i = 0; i < nitems(urtw_ratetable); i++) { if (rate == urtw_ratetable[i].val) return (urtw_ratetable[i].reg); } return (0); } usbd_status urtw_set_rate(struct urtw_softc *sc) { int i, basic_rate, min_rr_rate, max_rr_rate; uint16_t data; usbd_status 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_8187_BRSR, &data); data &= ~URTW_BRSR_MBR_8185; for (i = 0; i <= basic_rate; i++) data |= (1 << i); urtw_write16_m(sc, URTW_8187_BRSR, data); fail: return (error); } usbd_status urtw_intr_enable(struct urtw_softc *sc) { usbd_status error; urtw_write16_m(sc, URTW_INTR_MASK, 0xffff); fail: return (error); } usbd_status urtw_rx_setconf(struct urtw_softc *sc) { struct ifnet *ifp = &sc->sc_ic.ic_if; struct ieee80211com *ic = &sc->sc_ic; uint32_t data; usbd_status error; urtw_read32_m(sc, URTW_RX, &data); data = data &~ URTW_RX_FILTER_MASK; #if 0 data = data | URTW_RX_FILTER_CTL; #endif 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); } usbd_status urtw_rx_enable(struct urtw_softc *sc) { int i; struct urtw_rx_data *rx_data; uint8_t data; usbd_status error; /* * Start up the receive pipe. */ for (i = 0; i < URTW_RX_DATA_LIST_COUNT; i++) { rx_data = &sc->sc_rx_data[i]; usbd_setup_xfer(rx_data->xfer, sc->sc_rxpipe, rx_data, rx_data->buf, MCLBYTES, USBD_SHORT_XFER_OK, USBD_NO_TIMEOUT, urtw_rxeof); error = usbd_transfer(rx_data->xfer); if (error != USBD_IN_PROGRESS && error != 0) { printf("%s: could not queue Rx transfer\n", sc->sc_dev.dv_xname); goto fail; } } error = urtw_rx_setconf(sc); if (error != 0) goto fail; urtw_read8_m(sc, URTW_CMD, &data); urtw_write8_m(sc, URTW_CMD, data | URTW_CMD_RX_ENABLE); fail: return (error); } usbd_status urtw_tx_enable(struct urtw_softc *sc) { uint8_t data8; uint32_t data; usbd_status error; if (sc->sc_hwrev & URTW_HWREV_8187) { 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); } else { data = URTW_TX_DURPROCMODE | URTW_TX_DISREQQSIZE | URTW_TX_MXDMA_2048 | URTW_TX_SHORTRETRY | URTW_TX_LONGRETRY; 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); } int urtw_init(struct ifnet *ifp) { struct urtw_softc *sc = ifp->if_softc; struct urtw_rf *rf = &sc->sc_rf; struct ieee80211com *ic = &sc->sc_ic; usbd_status error; urtw_stop(ifp, 0); error = urtw_reset(sc); if (error) goto fail; urtw_write8_m(sc, 0x85, 0); urtw_write8_m(sc, URTW_GPIO, 0); /* for led */ urtw_write8_m(sc, 0x85, 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. */ IEEE80211_ADDR_COPY(ic->ic_myaddr, LLADDR(ifp->if_sadl)); error = urtw_set_macaddr(sc, ic->ic_myaddr); if (error) goto fail; 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, 0x81); error = urtw_set_rate(sc); if (error != 0) goto fail; error = rf->init(rf); if (error != 0) goto fail; if (rf->set_sens != NULL) rf->set_sens(rf); urtw_write16_m(sc, 0x5e, 1); urtw_write16_m(sc, 0xfe, 0x10); urtw_write8_m(sc, URTW_TALLY_SEL, 0x80); urtw_write8_m(sc, 0xff, 0x60); urtw_write16_m(sc, 0x5e, 0); urtw_write8_m(sc, 0x85, 4); error = urtw_intr_enable(sc); if (error != 0) goto fail; /* reset softc variables */ sc->sc_txidx = sc->sc_tx_low_queued = sc->sc_tx_normal_queued = 0; sc->sc_txtimer = 0; if (!(sc->sc_flags & URTW_INIT_ONCE)) { error = urtw_open_pipes(sc); if (error != 0) goto fail; error = urtw_alloc_rx_data_list(sc); if (error != 0) goto fail; error = 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; ifq_clr_oactive(&ifp->if_snd); ifp->if_flags |= IFF_RUNNING; ifp->if_timer = 1; if (ic->ic_opmode == IEEE80211_M_MONITOR) ieee80211_new_state(ic, IEEE80211_S_RUN, -1); else ieee80211_new_state(ic, IEEE80211_S_SCAN, -1); return (0); fail: return (error); } void urtw_set_multi(struct urtw_softc *sc) { struct arpcom *ac = &sc->sc_ic.ic_ac; struct ifnet *ifp = &ac->ac_if; /* * 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; } int urtw_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) { struct urtw_softc *sc = ifp->if_softc; struct ieee80211com *ic = &sc->sc_ic; struct ifreq *ifr; int s, error = 0; if (usbd_is_dying(sc->sc_udev)) return (ENXIO); usbd_ref_incr(sc->sc_udev); s = splnet(); switch (cmd) { case SIOCSIFADDR: ifp->if_flags |= IFF_UP; /* FALLTHROUGH */ case SIOCSIFFLAGS: if (ifp->if_flags & IFF_UP) { /* * If only the PROMISC or ALLMULTI flag changes, then * don't do a full re-init of the chip, just update * the Rx filter. */ if ((ifp->if_flags & IFF_RUNNING) && ((ifp->if_flags ^ sc->sc_if_flags) & (IFF_ALLMULTI | IFF_PROMISC)) != 0) { urtw_set_multi(sc); } else { if (!(ifp->if_flags & IFF_RUNNING)) sc->sc_init(ifp); } } else { if (ifp->if_flags & IFF_RUNNING) urtw_stop(ifp, 1); } sc->sc_if_flags = ifp->if_flags; break; case SIOCADDMULTI: case SIOCDELMULTI: ifr = (struct ifreq *)data; error = (cmd == SIOCADDMULTI) ? ether_addmulti(ifr, &ic->ic_ac) : ether_delmulti(ifr, &ic->ic_ac); if (error == ENETRESET) { if (ifp->if_flags & IFF_RUNNING) urtw_set_multi(sc); error = 0; } break; case SIOCS80211CHANNEL: /* * This allows for fast channel switching in monitor mode * (used by kismet). In IBSS mode, we must explicitly reset * the interface to generate a new beacon frame. */ error = ieee80211_ioctl(ifp, cmd, data); if (error == ENETRESET && ic->ic_opmode == IEEE80211_M_MONITOR) { urtw_set_chan(sc, ic->ic_ibss_chan); error = 0; } break; default: error = ieee80211_ioctl(ifp, cmd, data); } if (error == ENETRESET) { if ((ifp->if_flags & (IFF_RUNNING | IFF_UP)) == (IFF_RUNNING | IFF_UP)) sc->sc_init(ifp); error = 0; } splx(s); usbd_ref_decr(sc->sc_udev); return (error); } void urtw_start(struct ifnet *ifp) { struct urtw_softc *sc = ifp->if_softc; struct ieee80211com *ic = &sc->sc_ic; struct ieee80211_node *ni; struct mbuf *m0; /* * net80211 may still try to send management frames even if the * IFF_RUNNING flag is not set... */ if (!(ifp->if_flags & IFF_RUNNING) || ifq_is_oactive(&ifp->if_snd)) return; for (;;) { if (sc->sc_tx_low_queued >= URTW_TX_DATA_LIST_COUNT || sc->sc_tx_normal_queued >= URTW_TX_DATA_LIST_COUNT) { ifq_set_oactive(&ifp->if_snd); break; } m0 = mq_dequeue(&ic->ic_mgtq); if (m0 != NULL) { ni = m0->m_pkthdr.ph_cookie; #if NBPFILTER > 0 if (ic->ic_rawbpf != NULL) bpf_mtap(ic->ic_rawbpf, m0, BPF_DIRECTION_OUT); #endif if (urtw_tx_start(sc, ni, m0, URTW_PRIORITY_NORMAL) != 0) break; } else { if (ic->ic_state != IEEE80211_S_RUN) break; IFQ_DEQUEUE(&ifp->if_snd, m0); if (m0 == NULL) break; #if NBPFILTER > 0 if (ifp->if_bpf != NULL) bpf_mtap(ifp->if_bpf, m0, BPF_DIRECTION_OUT); #endif m0 = ieee80211_encap(ifp, m0, &ni); if (m0 == NULL) continue; #if NBPFILTER > 0 if (ic->ic_rawbpf != NULL) bpf_mtap(ic->ic_rawbpf, m0, BPF_DIRECTION_OUT); #endif if (urtw_tx_start(sc, ni, m0, URTW_PRIORITY_NORMAL) != 0) { if (ni != NULL) ieee80211_release_node(ic, ni); ifp->if_oerrors++; break; } } sc->sc_txtimer = 5; } } void urtw_watchdog(struct ifnet *ifp) { struct urtw_softc *sc = ifp->if_softc; ifp->if_timer = 0; if (sc->sc_txtimer > 0) { if (--sc->sc_txtimer == 0) { printf("%s: device timeout\n", sc->sc_dev.dv_xname); ifp->if_oerrors++; return; } ifp->if_timer = 1; } ieee80211_watchdog(ifp); } void urtw_txeof_low(struct usbd_xfer *xfer, void *priv, usbd_status status) { struct urtw_tx_data *data = priv; struct urtw_softc *sc = data->sc; struct ieee80211com *ic = &sc->sc_ic; struct ifnet *ifp = &ic->ic_if; int s; if (status != USBD_NORMAL_COMPLETION) { if (status == USBD_NOT_STARTED || status == USBD_CANCELLED) return; printf("%s: could not transmit buffer: %s\n", sc->sc_dev.dv_xname, usbd_errstr(status)); if (status == USBD_STALLED) usbd_clear_endpoint_stall_async(sc->sc_txpipe_low); ifp->if_oerrors++; return; } s = splnet(); ieee80211_release_node(ic, data->ni); data->ni = NULL; sc->sc_txtimer = 0; sc->sc_tx_low_queued--; ifq_clr_oactive(&ifp->if_snd); urtw_start(ifp); splx(s); } void urtw_txeof_normal(struct usbd_xfer *xfer, void *priv, usbd_status status) { struct urtw_tx_data *data = priv; struct urtw_softc *sc = data->sc; struct ieee80211com *ic = &sc->sc_ic; struct ifnet *ifp = &ic->ic_if; int s; if (status != USBD_NORMAL_COMPLETION) { if (status == USBD_NOT_STARTED || status == USBD_CANCELLED) return; printf("%s: could not transmit buffer: %s\n", sc->sc_dev.dv_xname, usbd_errstr(status)); if (status == USBD_STALLED) usbd_clear_endpoint_stall_async(sc->sc_txpipe_normal); ifp->if_oerrors++; return; } s = splnet(); ieee80211_release_node(ic, data->ni); data->ni = NULL; sc->sc_txtimer = 0; sc->sc_tx_normal_queued--; ifq_clr_oactive(&ifp->if_snd); urtw_start(ifp); splx(s); } int urtw_tx_start(struct urtw_softc *sc, struct ieee80211_node *ni, struct mbuf *m0, int prior) { struct ieee80211com *ic = &sc->sc_ic; struct urtw_tx_data *data; struct ieee80211_frame *wh; struct ieee80211_key *k; usbd_status error; int xferlen; wh = mtod(m0, struct ieee80211_frame *); if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) { k = ieee80211_get_txkey(ic, wh, ni); if ((m0 = ieee80211_encrypt(ic, m0, k)) == NULL) return (ENOBUFS); /* packet header may have moved, reset our local pointer */ wh = mtod(m0, struct ieee80211_frame *); } #if NBPFILTER > 0 if (sc->sc_drvbpf != NULL) { struct mbuf mb; struct urtw_tx_radiotap_header *tap = &sc->sc_txtap; tap->wt_flags = 0; tap->wt_rate = 0; tap->wt_chan_freq = htole16(ic->ic_bss->ni_chan->ic_freq); tap->wt_chan_flags = htole16(ic->ic_bss->ni_chan->ic_flags); mb.m_data = (caddr_t)tap; mb.m_len = sc->sc_txtap_len; mb.m_next = m0; mb.m_nextpkt = NULL; mb.m_type = 0; mb.m_flags = 0; bpf_mtap(sc->sc_drvbpf, &mb, BPF_DIRECTION_OUT); } #endif if (sc->sc_hwrev & URTW_HWREV_8187) xferlen = m0->m_pkthdr.len + 4 * 3; else xferlen = m0->m_pkthdr.len + 4 * 8; if ((0 == xferlen % 64) || (0 == xferlen % 512)) xferlen += 1; data = &sc->sc_tx_data[sc->sc_txidx]; sc->sc_txidx = (sc->sc_txidx + 1) % URTW_TX_DATA_LIST_COUNT; bzero(data->buf, URTW_TX_MAXSIZE); data->buf[0] = m0->m_pkthdr.len & 0xff; data->buf[1] = (m0->m_pkthdr.len & 0x0f00) >> 8; data->buf[1] |= (1 << 7); /* XXX sc_preamble_mode is always 2. */ if ((ic->ic_flags & IEEE80211_F_SHPREAMBLE) && (ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_PREAMBLE) && (sc->sc_preamble_mode == 1) && (sc->sc_currate != 0)) data->buf[2] |= 1; if ((m0->m_pkthdr.len > ic->ic_rtsthreshold) && prior == URTW_PRIORITY_LOW) return ENOTSUP; /* TODO */ if (wh->i_fc[1] & IEEE80211_FC1_MORE_FRAG) data->buf[2] |= (1 << 1); /* RTS rate - 10 means we use a basic rate. */ data->buf[2] |= (urtw_rate2rtl(2) << 3); /* * XXX currently TX rate control depends on the rate value of * RX descriptor because I don't know how to we can control TX rate * in more smart way. Please fix me you find a thing. */ data->buf[3] = sc->sc_currate; if (prior == URTW_PRIORITY_NORMAL) { if (IEEE80211_IS_MULTICAST(wh->i_addr1)) data->buf[3] = urtw_rate2rtl(ni->ni_rates.rs_rates[0]); else if (ic->ic_fixed_rate != -1) data->buf[3] = urtw_rate2rtl(ic->ic_fixed_rate); } if (sc->sc_hwrev & URTW_HWREV_8187) { data->buf[8] = 3; /* CW minimum */ data->buf[8] |= (7 << 4); /* CW maximum */ data->buf[9] |= 11; /* retry limitation */ m_copydata(m0, 0, m0->m_pkthdr.len, (uint8_t *)&data->buf[12]); } else { data->buf[21] |= 11; /* retry limitation */ m_copydata(m0, 0, m0->m_pkthdr.len, (uint8_t *)&data->buf[32]); } data->ni = ni; /* mbuf is no longer needed. */ m_freem(m0); usbd_setup_xfer(data->xfer, (prior == URTW_PRIORITY_LOW) ? sc->sc_txpipe_low : sc->sc_txpipe_normal, data, data->buf, xferlen, USBD_FORCE_SHORT_XFER | USBD_NO_COPY, URTW_DATA_TIMEOUT, (prior == URTW_PRIORITY_LOW) ? urtw_txeof_low : urtw_txeof_normal); error = usbd_transfer(data->xfer); if (error != USBD_IN_PROGRESS && error != USBD_NORMAL_COMPLETION) { printf("%s: could not send frame: %s\n", sc->sc_dev.dv_xname, usbd_errstr(error)); return (EIO); } error = urtw_led_ctl(sc, URTW_LED_CTL_TX); if (error != 0) printf("%s: could not control LED (%d)\n", sc->sc_dev.dv_xname, error); if (prior == URTW_PRIORITY_LOW) sc->sc_tx_low_queued++; else sc->sc_tx_normal_queued++; return (0); } usbd_status urtw_8225_usb_init(struct urtw_softc *sc) { uint8_t data; usbd_status 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); usbd_delay_ms(sc->sc_udev, 500); fail: return (error); } usbd_status urtw_8185_rf_pins_enable(struct urtw_softc *sc) { usbd_status error = 0; urtw_write16_m(sc, URTW_RF_PINS_ENABLE, 0x1ff7); fail: return (error); } usbd_status urtw_8187_write_phy(struct urtw_softc *sc, uint8_t addr, uint32_t data) { uint32_t phyw; usbd_status error; phyw = ((data << 8) | (addr | 0x80)); urtw_write8_m(sc, 0x7f, ((phyw & 0xff000000) >> 24)); urtw_write8_m(sc, 0x7e, ((phyw & 0x00ff0000) >> 16)); urtw_write8_m(sc, 0x7d, ((phyw & 0x0000ff00) >> 8)); urtw_write8_m(sc, 0x7c, ((phyw & 0x000000ff))); /* * Delay removed from 8185 to 8187. * usbd_delay_ms(sc->sc_udev, 1); */ fail: return (error); } usbd_status 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)); } usbd_status 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)); } usbd_status urtw_8225_setgain(struct urtw_softc *sc, int16_t gain) { usbd_status 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); } usbd_status 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; usbd_status 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]); } usbd_delay_ms(sc->sc_udev, 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_8187_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]); usbd_delay_ms(sc->sc_udev, 1); fail: return (error); } usbd_status urtw_8185_tx_antenna(struct urtw_softc *sc, uint8_t ant) { usbd_status error; urtw_write8_m(sc, URTW_TX_ANTENNA, ant); usbd_delay_ms(sc->sc_udev, 1); fail: return (error); } usbd_status urtw_8225_rf_init(struct urtw_rf *rf) { struct urtw_softc *sc = rf->rf_sc; int i; uint16_t data; usbd_status error; error = urtw_8180_set_anaparam(sc, URTW_8187_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_8187_BRSR, &data); /* XXX ??? */ urtw_write16_m(sc, URTW_8187_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; usbd_delay_ms(sc->sc_udev, 500); for (i = 0; i < nitems(urtw_8225_rf_part1); i++) { urtw_8225_write(sc, urtw_8225_rf_part1[i].reg, urtw_8225_rf_part1[i].val); } usbd_delay_ms(sc->sc_udev, 50); urtw_8225_write(sc, 0x2, 0xc4d); usbd_delay_ms(sc->sc_udev, 200); urtw_8225_write(sc, 0x2, 0x44d); usbd_delay_ms(sc->sc_udev, 200); urtw_8225_write(sc, 0x0, 0x127); for (i = 0; i < nitems(urtw_8225_rxgain); i++) { urtw_8225_write(sc, 0x1, (uint8_t)(i + 1)); urtw_8225_write(sc, 0x2, urtw_8225_rxgain[i]); } urtw_8225_write(sc, 0x0, 0x27); urtw_8225_write(sc, 0x0, 0x22f); for (i = 0; i < nitems(urtw_8225_agc); 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 < nitems(urtw_8225_rf_part2); i++) { urtw_8187_write_phy_ofdm(sc, urtw_8225_rf_part2[i].reg, urtw_8225_rf_part2[i].val); usbd_delay_ms(sc->sc_udev, 1); } error = urtw_8225_setgain(sc, 4); if (error) goto fail; for (i = 0; i < nitems(urtw_8225_rf_part3); i++) { urtw_8187_write_phy_cck(sc, urtw_8225_rf_part3[i].reg, urtw_8225_rf_part3[i].val); usbd_delay_ms(sc->sc_udev, 1); } urtw_write8_m(sc, 0x5b, 0x0d); error = urtw_8225_set_txpwrlvl(sc, 1); if (error) goto fail; urtw_8187_write_phy_cck(sc, 0x10, 0x9b); usbd_delay_ms(sc->sc_udev, 1); urtw_8187_write_phy_ofdm(sc, 0x26, 0x90); usbd_delay_ms(sc->sc_udev, 1); /* TX ant A, 0x0 for B */ error = urtw_8185_tx_antenna(sc, 0x3); if (error) goto fail; urtw_write32_m(sc, 0x94, 0x3dc00002); error = urtw_8225_rf_set_chan(rf, 1); fail: return (error); } usbd_status urtw_8225_rf_set_chan(struct urtw_rf *rf, int chan) { struct urtw_softc *sc = rf->rf_sc; struct ieee80211com *ic = &sc->sc_ic; struct ieee80211_channel *c = ic->ic_ibss_chan; usbd_status error; error = urtw_8225_set_txpwrlvl(sc, chan); if (error) goto fail; urtw_8225_write(sc, 0x7, urtw_8225_channel[chan]); usbd_delay_ms(sc->sc_udev, 10); urtw_write8_m(sc, URTW_SIFS, 0x22); if (sc->sc_state == IEEE80211_S_ASSOC && ic->ic_flags & IEEE80211_F_SHSLOT) urtw_write8_m(sc, URTW_SLOT, IEEE80211_DUR_DS_SHSLOT); else urtw_write8_m(sc, URTW_SLOT, IEEE80211_DUR_DS_SLOT); if (IEEE80211_IS_CHAN_G(c)) { urtw_write8_m(sc, URTW_DIFS, 0x14); urtw_write8_m(sc, URTW_8187_EIFS, 0x5b - 0x14); urtw_write8_m(sc, URTW_CW_VAL, 0x73); } else { urtw_write8_m(sc, URTW_DIFS, 0x24); urtw_write8_m(sc, URTW_8187_EIFS, 0x5b - 0x24); urtw_write8_m(sc, URTW_CW_VAL, 0xa5); } fail: return (error); } usbd_status urtw_8225_rf_set_sens(struct urtw_rf *rf) { struct urtw_softc *sc = rf->rf_sc; usbd_status error; if (rf->sens > 6) return (-1); if (rf->sens > 4) urtw_8225_write(sc, 0x0c, 0x850); else urtw_8225_write(sc, 0x0c, 0x50); rf->sens = 6 - rf->sens; error = urtw_8225_setgain(sc, rf->sens); if (error) goto fail; urtw_8187_write_phy_cck(sc, 0x41, urtw_8225_threshold[rf->sens]); fail: return (error); } void urtw_stop(struct ifnet *ifp, int disable) { struct urtw_softc *sc = ifp->if_softc; struct ieee80211com *ic = &sc->sc_ic; uint8_t data; usbd_status error; ifp->if_flags &= ~IFF_RUNNING; ifq_clr_oactive(&ifp->if_snd); ieee80211_new_state(ic, IEEE80211_S_INIT, -1); timeout_del(&sc->scan_to); timeout_del(&sc->sc_led_ch); urtw_intr_disable(sc); urtw_read8_m(sc, URTW_CMD, &data); data &= ~URTW_CMD_TX_ENABLE; data &= ~URTW_CMD_RX_ENABLE; urtw_write8_m(sc, URTW_CMD, data); if (sc->sc_rxpipe != NULL) usbd_abort_pipe(sc->sc_rxpipe); if (sc->sc_txpipe_low != NULL) usbd_abort_pipe(sc->sc_txpipe_low); if (sc->sc_txpipe_normal != NULL) usbd_abort_pipe(sc->sc_txpipe_normal); fail: return; } int urtw_isbmode(uint16_t rate) { rate = urtw_rtl2rate(rate); return (((rate <= 22 && rate != 12 && rate != 18) || rate == 44) ? (1) : (0)); } void urtw_rxeof(struct usbd_xfer *xfer, void *priv, usbd_status status) { struct urtw_rx_data *data = priv; struct urtw_softc *sc = data->sc; struct ieee80211com *ic = &sc->sc_ic; struct ifnet *ifp = &ic->ic_if; struct ieee80211_frame *wh; struct ieee80211_node *ni; struct ieee80211_rxinfo rxi; struct mbuf *m, *mnew; uint8_t *desc, quality, rate; int actlen, flen, len, nf, rssi, s; if (status != USBD_NORMAL_COMPLETION) { if (status == USBD_NOT_STARTED || status == USBD_CANCELLED) return; if (status == USBD_STALLED) usbd_clear_endpoint_stall_async(sc->sc_rxpipe); ifp->if_ierrors++; goto skip; } usbd_get_xfer_status(xfer, NULL, NULL, &actlen, NULL); if (actlen < URTW_MIN_RXBUFSZ) { ifp->if_ierrors++; goto skip; } if (sc->sc_hwrev & URTW_HWREV_8187) /* 4 dword and 4 byte CRC */ len = actlen - (4 * 4); else /* 5 dword and 4 byte CRC */ len = actlen - (4 * 5); desc = data->buf + len; flen = ((desc[1] & 0x0f) << 8) + (desc[0] & 0xff); if (flen > actlen) { ifp->if_ierrors++; goto skip; } rate = (desc[2] & 0xf0) >> 4; if (sc->sc_hwrev & URTW_HWREV_8187) { quality = desc[4] & 0xff; rssi = (desc[6] & 0xfe) >> 1; /* XXX correct? */ if (!urtw_isbmode(rate)) { rssi = (rssi > 90) ? 90 : ((rssi < 25) ? 25 : rssi); rssi = ((90 - rssi) * 100) / 65; } else { rssi = (rssi > 90) ? 95 : ((rssi < 30) ? 30 : rssi); rssi = ((95 - rssi) * 100) / 65; } } else { quality = desc[12]; rssi = 14 - desc[14] / 2; } MGETHDR(mnew, M_DONTWAIT, MT_DATA); if (mnew == NULL) { printf("%s: could not allocate rx mbuf\n", sc->sc_dev.dv_xname); ifp->if_ierrors++; goto skip; } MCLGET(mnew, M_DONTWAIT); if (!(mnew->m_flags & M_EXT)) { printf("%s: could not allocate rx mbuf cluster\n", sc->sc_dev.dv_xname); m_freem(mnew); ifp->if_ierrors++; goto skip; } m = data->m; data->m = mnew; data->buf = mtod(mnew, uint8_t *); /* finalize mbuf */ m->m_pkthdr.len = m->m_len = flen - 4; s = splnet(); #if NBPFILTER > 0 if (sc->sc_drvbpf != NULL) { struct mbuf mb; struct urtw_rx_radiotap_header *tap = &sc->sc_rxtap; /* XXX Are variables correct? */ tap->wr_chan_freq = htole16(ic->ic_ibss_chan->ic_freq); tap->wr_chan_flags = htole16(ic->ic_ibss_chan->ic_flags); tap->wr_dbm_antsignal = (int8_t)rssi; mb.m_data = (caddr_t)tap; mb.m_len = sc->sc_rxtap_len; mb.m_next = m; mb.m_nextpkt = NULL; mb.m_type = 0; mb.m_flags = 0; bpf_mtap(sc->sc_drvbpf, &mb, BPF_DIRECTION_IN); } #endif wh = mtod(m, struct ieee80211_frame *); if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) == IEEE80211_FC0_TYPE_DATA) sc->sc_currate = (rate > 0) ? rate : sc->sc_currate; ni = ieee80211_find_rxnode(ic, wh); /* XXX correct? */ if (!urtw_isbmode(rate)) { if (quality > 127) quality = 0; else if (quality < 27) quality = 100; else quality = 127 - quality; } else quality = (quality > 64) ? 0 : ((64 - quality) * 100) / 64; nf = quality; /* send the frame to the 802.11 layer */ rxi.rxi_flags = 0; rxi.rxi_rssi = rssi; rxi.rxi_tstamp = 0; ieee80211_input(ifp, m, ni, &rxi); /* node is no longer needed */ ieee80211_release_node(ic, ni); splx(s); skip: /* setup a new transfer */ usbd_setup_xfer(xfer, sc->sc_rxpipe, data, data->buf, MCLBYTES, USBD_SHORT_XFER_OK, USBD_NO_TIMEOUT, urtw_rxeof); (void)usbd_transfer(xfer); } usbd_status urtw_8225v2_setgain(struct urtw_softc *sc, int16_t gain) { uint8_t *gainp; usbd_status error; /* XXX for A? */ gainp = urtw_8225v2_gain_bg; urtw_8187_write_phy_ofdm(sc, 0x0d, gainp[gain * 3]); usbd_delay_ms(sc->sc_udev, 1); urtw_8187_write_phy_ofdm(sc, 0x1b, gainp[gain * 3 + 1]); usbd_delay_ms(sc->sc_udev, 1); urtw_8187_write_phy_ofdm(sc, 0x1d, gainp[gain * 3 + 2]); usbd_delay_ms(sc->sc_udev, 1); urtw_8187_write_phy_ofdm(sc, 0x21, 0x17); usbd_delay_ms(sc->sc_udev, 1); fail: return (error); } usbd_status 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; usbd_status 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]); usbd_delay_ms(sc->sc_udev, 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_8187_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]); usbd_delay_ms(sc->sc_udev, 1); fail: return (error); } usbd_status urtw_8225v2_rf_init(struct urtw_rf *rf) { struct urtw_softc *sc = rf->rf_sc; int i; uint16_t data; uint32_t data32; usbd_status error; error = urtw_8180_set_anaparam(sc, URTW_8187_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_8187_BRSR, &data); /* XXX ??? */ urtw_write16_m(sc, URTW_8187_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; usbd_delay_ms(sc->sc_udev, 1000); for (i = 0; i < nitems(urtw_8225v2_rf_part1); i++) { urtw_8225_write(sc, urtw_8225v2_rf_part1[i].reg, urtw_8225v2_rf_part1[i].val); usbd_delay_ms(sc->sc_udev, 1); } usbd_delay_ms(sc->sc_udev, 50); urtw_8225_write(sc, 0x0, 0x1b7); for (i = 0; i < nitems(urtw_8225v2_rxgain); i++) { urtw_8225_write(sc, 0x1, (uint8_t)(i + 1)); urtw_8225_write(sc, 0x2, urtw_8225v2_rxgain[i]); } urtw_8225_write(sc, 0x3, 0x2); urtw_8225_write(sc, 0x5, 0x4); urtw_8225_write(sc, 0x0, 0xb7); urtw_8225_write(sc, 0x2, 0xc4d); usbd_delay_ms(sc->sc_udev, 100); urtw_8225_write(sc, 0x2, 0x44d); usbd_delay_ms(sc->sc_udev, 100); error = urtw_8225_read(sc, 0x6, &data32); if (error != 0) goto fail; if (data32 != 0xe6) printf("%s: expect 0xe6!! (0x%x)\n", sc->sc_dev.dv_xname, data32); if (!(data32 & 0x80)) { urtw_8225_write(sc, 0x02, 0x0c4d); usbd_delay_ms(sc->sc_udev, 200); urtw_8225_write(sc, 0x02, 0x044d); usbd_delay_ms(sc->sc_udev, 100); error = urtw_8225_read(sc, 0x6, &data32); if (error != 0) goto fail; if (!(data32 & 0x80)) printf("%s: RF calibration failed\n", sc->sc_dev.dv_xname); } usbd_delay_ms(sc->sc_udev, 100); urtw_8225_write(sc, 0x0, 0x2bf); for (i = 0; i < nitems(urtw_8225_agc); 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 < nitems(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 < nitems(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, 0x5b, 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, 0x94, 0x3dc00002); error = urtw_8225_rf_set_chan(rf, 1); fail: return (error); } usbd_status urtw_8225v2_rf_set_chan(struct urtw_rf *rf, int chan) { struct urtw_softc *sc = rf->rf_sc; struct ieee80211com *ic = &sc->sc_ic; struct ieee80211_channel *c = ic->ic_ibss_chan; usbd_status error; error = urtw_8225v2_set_txpwrlvl(sc, chan); if (error) goto fail; urtw_8225_write(sc, 0x7, urtw_8225_channel[chan]); usbd_delay_ms(sc->sc_udev, 10); urtw_write8_m(sc, URTW_SIFS, 0x22); if(sc->sc_state == IEEE80211_S_ASSOC && ic->ic_flags & IEEE80211_F_SHSLOT) urtw_write8_m(sc, URTW_SLOT, IEEE80211_DUR_DS_SHSLOT); else urtw_write8_m(sc, URTW_SLOT, IEEE80211_DUR_DS_SLOT); if (IEEE80211_IS_CHAN_G(c)) { urtw_write8_m(sc, URTW_DIFS, 0x14); urtw_write8_m(sc, URTW_8187_EIFS, 0x5b - 0x14); urtw_write8_m(sc, URTW_CW_VAL, 0x73); } else { urtw_write8_m(sc, URTW_DIFS, 0x24); urtw_write8_m(sc, URTW_8187_EIFS, 0x5b - 0x24); urtw_write8_m(sc, URTW_CW_VAL, 0xa5); } fail: return (error); } void urtw_set_chan(struct urtw_softc *sc, struct ieee80211_channel *c) { struct urtw_rf *rf = &sc->sc_rf; struct ieee80211com *ic = &sc->sc_ic; usbd_status error = 0; uint32_t data; u_int chan; chan = ieee80211_chan2ieee(ic, c); if (chan == 0 || chan == IEEE80211_CHAN_ANY) return; /* * During changing the channel we need to temporary disable * TX. */ urtw_read32_m(sc, URTW_TX_CONF, &data); data &= ~URTW_TX_LOOPBACK_MASK; urtw_write32_m(sc, URTW_TX_CONF, data | URTW_TX_LOOPBACK_MAC); error = rf->set_chan(rf, chan); if (error != 0) { printf("%s could not change the channel\n", sc->sc_dev.dv_xname); return; } usbd_delay_ms(sc->sc_udev, 10); urtw_write32_m(sc, URTW_TX_CONF, data | URTW_TX_LOOPBACK_NONE); fail: return; } void urtw_next_scan(void *arg) { struct urtw_softc *sc = arg; struct ieee80211com *ic = &sc->sc_ic; struct ifnet *ifp = &ic->ic_if; if (usbd_is_dying(sc->sc_udev)) return; usbd_ref_incr(sc->sc_udev); if (ic->ic_state == IEEE80211_S_SCAN) ieee80211_next_scan(ifp); usbd_ref_decr(sc->sc_udev); } void urtw_task(void *arg) { struct urtw_softc *sc = arg; struct ieee80211com *ic = &sc->sc_ic; struct ieee80211_node *ni; enum ieee80211_state ostate; usbd_status error = 0; if (usbd_is_dying(sc->sc_udev)) return; ostate = ic->ic_state; switch (sc->sc_state) { case IEEE80211_S_INIT: if (ostate == IEEE80211_S_RUN) { /* turn link LED off */ (void)urtw_led_off(sc, URTW_LED_GPIO); } break; case IEEE80211_S_SCAN: urtw_set_chan(sc, ic->ic_bss->ni_chan); if (!usbd_is_dying(sc->sc_udev)) timeout_add_msec(&sc->scan_to, 200); break; case IEEE80211_S_AUTH: case IEEE80211_S_ASSOC: urtw_set_chan(sc, ic->ic_bss->ni_chan); break; case IEEE80211_S_RUN: ni = ic->ic_bss; /* setting bssid. */ error = urtw_set_bssid(sc, ni->ni_bssid); if (error != 0) goto fail; 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, 0x3ff); error = urtw_led_ctl(sc, URTW_LED_CTL_LINK); if (error != 0) printf("%s: could not control LED (%d)\n", sc->sc_dev.dv_xname, error); break; } sc->sc_newstate(ic, sc->sc_state, sc->sc_arg); fail: if (error != 0) DPRINTF(("%s: error duing processing RUN state.", sc->sc_dev.dv_xname)); } usbd_status urtw_8187b_update_wmm(struct urtw_softc *sc) { struct ieee80211com *ic = &sc->sc_ic; struct ieee80211_channel *c = ic->ic_ibss_chan; uint32_t data; uint8_t aifs, sifs, slot, ecwmin, ecwmax; usbd_status error; sifs = 0xa; if (IEEE80211_IS_CHAN_G(c)) slot = 0x9; else slot = 0x14; aifs = (2 * slot) + sifs; ecwmin = 3; ecwmax = 7; data = ((uint32_t)aifs << 0) | /* AIFS, offset 0 */ ((uint32_t)ecwmin << 8) | /* ECW minimum, offset 8 */ ((uint32_t)ecwmax << 12); /* ECW maximum, offset 16 */ urtw_write32_m(sc, URTW_AC_VO, data); urtw_write32_m(sc, URTW_AC_VI, data); urtw_write32_m(sc, URTW_AC_BE, data); urtw_write32_m(sc, URTW_AC_BK, data); fail: return (error); } usbd_status urtw_8187b_reset(struct urtw_softc *sc) { uint8_t data; usbd_status 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_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, &data); urtw_write8_m(sc, 0x62, data & ~(1 << 5)); urtw_write8_m(sc, 0x62, data | (1 << 5)); 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; urtw_read8_m(sc, URTW_CMD, &data); data = (data & 2) | URTW_CMD_RST; urtw_write8_m(sc, URTW_CMD, data); usbd_delay_ms(sc->sc_udev, 100); urtw_read8_m(sc, URTW_CMD, &data); if (data & URTW_CMD_RST) { printf("%s: reset timeout\n", sc->sc_dev.dv_xname); goto fail; } fail: return (error); } int urtw_8187b_init(struct ifnet *ifp) { struct urtw_softc *sc = ifp->if_softc; struct urtw_rf *rf = &sc->sc_rf; struct ieee80211com *ic = &sc->sc_ic; int ret; uint8_t data; usbd_status error; urtw_stop(ifp, 0); error = urtw_8187b_update_wmm(sc); if (error != 0) goto fail; error = urtw_8187b_reset(sc); if (error) goto fail; /* Applying MAC address again. */ error = urtw_set_mode(sc, URTW_EPROM_CMD_CONFIG); if (error) goto fail; IEEE80211_ADDR_COPY(ic->ic_myaddr, LLADDR(ifp->if_sadl)); error = urtw_set_macaddr(sc, ic->ic_myaddr); if (error) goto fail; error = urtw_set_mode(sc, URTW_EPROM_CMD_NORMAL); if (error) goto fail; error = urtw_update_msr(sc); if (error) goto fail; error = rf->init(rf); if (error != 0) goto fail; urtw_write8_m(sc, URTW_CMD, URTW_CMD_TX_ENABLE | URTW_CMD_RX_ENABLE); error = urtw_intr_enable(sc); if (error != 0) goto fail; error = urtw_write8e(sc, 0x41, 0xf4); if (error != 0) goto fail; error = urtw_write8e(sc, 0x40, 0x00); if (error != 0) goto fail; error = urtw_write8e(sc, 0x42, 0x00); if (error != 0) goto fail; error = urtw_write8e(sc, 0x42, 0x01); if (error != 0) goto fail; error = urtw_write8e(sc, 0x40, 0x0f); if (error != 0) goto fail; error = urtw_write8e(sc, 0x42, 0x00); if (error != 0) goto fail; error = urtw_write8e(sc, 0x42, 0x01); if (error != 0) goto fail; urtw_read8_m(sc, 0xdb, &data); urtw_write8_m(sc, 0xdb, data | (1 << 2)); urtw_write16_idx_m(sc, 0x72, 0x59fa, 3); urtw_write16_idx_m(sc, 0x74, 0x59d2, 3); urtw_write16_idx_m(sc, 0x76, 0x59d2, 3); urtw_write16_idx_m(sc, 0x78, 0x19fa, 3); urtw_write16_idx_m(sc, 0x7a, 0x19fa, 3); urtw_write16_idx_m(sc, 0x7c, 0x00d0, 3); urtw_write8_m(sc, 0x61, 0); urtw_write8_idx_m(sc, 0x80, 0x0f, 1); urtw_write8_idx_m(sc, 0x83, 0x03, 1); urtw_write8_m(sc, 0xda, 0x10); urtw_write8_idx_m(sc, 0x4d, 0x08, 2); urtw_write32_m(sc, URTW_HSSI_PARA, 0x0600321b); urtw_write16_idx_m(sc, 0xec, 0x0800, 1); urtw_write8_m(sc, URTW_ACM_CONTROL, 0); /* Reset softc variables. */ sc->sc_txidx = sc->sc_tx_low_queued = sc->sc_tx_normal_queued = 0; sc->sc_txtimer = 0; if (!(sc->sc_flags & URTW_INIT_ONCE)) { error = urtw_open_pipes(sc); if (error != 0) goto fail; ret = urtw_alloc_rx_data_list(sc); if (error != 0) goto fail; ret = urtw_alloc_tx_data_list(sc); if (error != 0) goto fail; sc->sc_flags |= URTW_INIT_ONCE; } error = urtw_rx_enable(sc); if (error != 0) goto fail; error = urtw_tx_enable(sc); if (error != 0) goto fail; ifp->if_flags |= IFF_RUNNING; ifq_clr_oactive(&ifp->if_snd); ifp->if_timer = 1; if (ic->ic_opmode == IEEE80211_M_MONITOR) ieee80211_new_state(ic, IEEE80211_S_RUN, -1); else ieee80211_new_state(ic, IEEE80211_S_SCAN, -1); fail: return (error); } usbd_status urtw_8225v2_b_config_mac(struct urtw_softc *sc) { int i; usbd_status error; for (i = 0; i < nitems(urtw_8187b_regtbl); i++) { urtw_write8_idx_m(sc, urtw_8187b_regtbl[i].reg, urtw_8187b_regtbl[i].val, urtw_8187b_regtbl[i].idx); } urtw_write16_m(sc, URTW_TID_AC_MAP, 0xfa50); urtw_write16_m(sc, URTW_INT_MIG, 0); urtw_write32_idx_m(sc, 0xf0, 0, 1); urtw_write32_idx_m(sc, 0xf4, 0, 1); urtw_write8_idx_m(sc, 0xf8, 0, 1); urtw_write32_m(sc, URTW_RF_TIMING, 0x00004001); fail: return (error); } usbd_status urtw_8225v2_b_init_rfe(struct urtw_softc *sc) { usbd_status error; 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); usbd_delay_ms(sc->sc_udev, 100); fail: return (error); } usbd_status urtw_8225v2_b_update_chan(struct urtw_softc *sc) { struct ieee80211com *ic = &sc->sc_ic; struct ieee80211_channel *c = ic->ic_ibss_chan; uint8_t aifs, difs, eifs, sifs, slot; usbd_status error; urtw_write8_m(sc, URTW_SIFS, 0x22); sifs = 0xa; if (IEEE80211_IS_CHAN_G(c)) { slot = 0x9; difs = 0x1c; eifs = 0x5b; } else { slot = 0x14; difs = 0x32; eifs = 0x5b; } aifs = (2 * slot) + sifs; urtw_write8_m(sc, URTW_SLOT, slot); urtw_write8_m(sc, URTW_AC_VO, aifs); urtw_write8_m(sc, URTW_AC_VI, aifs); urtw_write8_m(sc, URTW_AC_BE, aifs); urtw_write8_m(sc, URTW_AC_BK, aifs); urtw_write8_m(sc, URTW_DIFS, difs); urtw_write8_m(sc, URTW_8187B_EIFS, eifs); fail: return (error); } usbd_status urtw_8225v2_b_rf_init(struct urtw_rf *rf) { struct urtw_softc *sc = rf->rf_sc; int i; uint8_t data; usbd_status error; /* Set up ACK rate, retry limit, TX AGC, TX antenna. */ urtw_write16_m(sc, URTW_8187B_BRSR, 0x0fff); urtw_read8_m(sc, URTW_CW_CONF, &data); urtw_write8_m(sc, URTW_CW_CONF, data | URTW_CW_CONF_PERPACKET_RETRY); urtw_read8_m(sc, URTW_TX_AGC_CTL, &data); urtw_write8_m(sc, URTW_TX_AGC_CTL, data | URTW_TX_AGC_CTL_PERPACKET_GAIN | URTW_TX_AGC_CTL_PERPACKET_ANTSEL); /* Auto rate fallback control. */ urtw_write16_idx_m(sc, URTW_ARFR, 0x0fff, 1); /* 1M ~ 54M */ urtw_read8_m(sc, URTW_RATE_FALLBACK, &data); urtw_write8_m(sc, URTW_RATE_FALLBACK, data | URTW_RATE_FALLBACK_ENABLE); urtw_write16_m(sc, URTW_BEACON_INTERVAL, 100); urtw_write16_m(sc, URTW_ATIM_WND, 2); urtw_write16_idx_m(sc, URTW_FEMR, 0xffff, 1); error = urtw_set_mode(sc, URTW_EPROM_CMD_CONFIG); if (error) goto fail; urtw_read8_m(sc, URTW_CONFIG1, &data); urtw_write8_m(sc, URTW_CONFIG1, (data & 0x3f) | 0x80); error = urtw_set_mode(sc, URTW_EPROM_CMD_NORMAL); if (error) goto fail; urtw_write8_m(sc, URTW_WPA_CONFIG, 0); urtw_8225v2_b_config_mac(sc); urtw_write16_idx_m(sc, URTW_RFSW_CTRL, 0x569a, 2); 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); error = urtw_set_mode(sc, URTW_EPROM_CMD_NORMAL); if (error) goto fail; urtw_8225v2_b_init_rfe(sc); for (i = 0; i < nitems(urtw_8225v2_b_rf); i++) { urtw_8225_write(sc, urtw_8225v2_b_rf[i].reg, urtw_8225v2_b_rf[i].val); } for (i = 0; i < nitems(urtw_8225v2_rxgain); i++) { urtw_8225_write(sc, 0x1, (uint8_t)(i + 1)); urtw_8225_write(sc, 0x2, urtw_8225v2_rxgain[i]); } urtw_8225_write(sc, 0x03, 0x080); urtw_8225_write(sc, 0x05, 0x004); urtw_8225_write(sc, 0x00, 0x0b7); urtw_8225_write(sc, 0x02, 0xc4d); urtw_8225_write(sc, 0x02, 0x44d); urtw_8225_write(sc, 0x00, 0x2bf); urtw_write8_m(sc, URTW_TX_GAIN_CCK, 0x03); urtw_write8_m(sc, URTW_TX_GAIN_OFDM, 0x07); urtw_write8_m(sc, URTW_TX_ANTENNA, 0x03); urtw_8187_write_phy_ofdm(sc, 0x80, 0x12); for (i = 0; i < nitems(urtw_8225v2_agc); i++) { urtw_8187_write_phy_ofdm(sc, 0x0f, urtw_8225v2_agc[i]); urtw_8187_write_phy_ofdm(sc, 0x0e, (uint8_t)i + 0x80); urtw_8187_write_phy_ofdm(sc, 0x0e, 0); } urtw_8187_write_phy_ofdm(sc, 0x80, 0x10); for (i = 0; i < nitems(urtw_8225v2_ofdm); i++) urtw_8187_write_phy_ofdm(sc, i, urtw_8225v2_ofdm[i]); urtw_8225v2_b_update_chan(sc); 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); error = urtw_8225v2_b_rf_set_chan(rf, 1); fail: return (error); } usbd_status urtw_8225v2_b_rf_set_chan(struct urtw_rf *rf, int chan) { struct urtw_softc *sc = rf->rf_sc; usbd_status error; error = urtw_8225v2_b_set_txpwrlvl(sc, chan); if (error) goto fail; urtw_8225_write(sc, 0x7, urtw_8225_channel[chan]); /* * Delay removed from 8185 to 8187. * usbd_delay_ms(sc->sc_udev, 10); */ urtw_write16_m(sc, URTW_AC_VO, 0x5114); urtw_write16_m(sc, URTW_AC_VI, 0x5114); urtw_write16_m(sc, URTW_AC_BE, 0x5114); urtw_write16_m(sc, URTW_AC_BK, 0x5114); fail: return (error); } usbd_status urtw_8225v2_b_set_txpwrlvl(struct urtw_softc *sc, int chan) { int i; uint8_t *cck_pwrtable; uint8_t cck_pwrlvl_min, cck_pwrlvl_max, ofdm_pwrlvl_min, ofdm_pwrlvl_max; int8_t cck_pwrlvl = sc->sc_txpwr_cck[chan] & 0xff; int8_t ofdm_pwrlvl = sc->sc_txpwr_ofdm[chan] & 0xff; usbd_status error; if (sc->sc_hwrev & URTW_HWREV_8187B_B) { cck_pwrlvl_min = 0; cck_pwrlvl_max = 15; ofdm_pwrlvl_min = 2; ofdm_pwrlvl_max = 17; } else { cck_pwrlvl_min = 7; cck_pwrlvl_max = 22; ofdm_pwrlvl_min = 10; ofdm_pwrlvl_max = 25; } /* CCK power setting */ cck_pwrlvl = (cck_pwrlvl > (cck_pwrlvl_max - cck_pwrlvl_min)) ? cck_pwrlvl_max : (cck_pwrlvl + cck_pwrlvl_min); cck_pwrlvl += sc->sc_txpwr_cck_base; cck_pwrlvl = (cck_pwrlvl > 35) ? 35 : cck_pwrlvl; cck_pwrlvl = (cck_pwrlvl < 0) ? 0 : cck_pwrlvl; cck_pwrtable = (chan == 14) ? urtw_8225v2_txpwr_cck_ch14 : urtw_8225v2_txpwr_cck; if (sc->sc_hwrev & URTW_HWREV_8187B_B) { if (cck_pwrlvl <= 6) ; /* do nothing */ else if (cck_pwrlvl <= 11) cck_pwrtable += 8; else cck_pwrtable += 16; } else { if (cck_pwrlvl <= 5) ; /* do nothing */ else if (cck_pwrlvl <= 11) cck_pwrtable += 8; else if (cck_pwrlvl <= 17) cck_pwrtable += 16; else cck_pwrtable += 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); /* * Delay removed from 8185 to 8187. * usbd_delay_ms(sc->sc_udev, 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; ofdm_pwrlvl = (ofdm_pwrlvl < 0) ? 0 : ofdm_pwrlvl; urtw_write8_m(sc, URTW_TX_GAIN_OFDM, urtw_8225v2_tx_gain_cck_ofdm[ofdm_pwrlvl] << 1); if (sc->sc_hwrev & URTW_HWREV_8187B_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); } } /* * Delay removed from 8185 to 8187. * usbd_delay_ms(sc->sc_udev, 1); */ fail: return (error); } int urtw_set_bssid(struct urtw_softc *sc, const uint8_t *bssid) { int error; urtw_write32_m(sc, URTW_BSSID, bssid[0] | bssid[1] << 8 | bssid[2] << 16 | bssid[3] << 24); urtw_write16_m(sc, URTW_BSSID + 4, bssid[4] | bssid[5] << 8); return 0; fail: return error; } int urtw_set_macaddr(struct urtw_softc *sc, const uint8_t *addr) { int error; urtw_write32_m(sc, URTW_MAC0, addr[0] | addr[1] << 8 | addr[2] << 16 | addr[3] << 24); urtw_write16_m(sc, URTW_MAC4, addr[4] | addr[5] << 8); return 0; fail: return error; }