diff options
author | Kevin Lo <kevlo@cvs.openbsd.org> | 2008-11-27 14:08:08 +0000 |
---|---|---|
committer | Kevin Lo <kevlo@cvs.openbsd.org> | 2008-11-27 14:08:08 +0000 |
commit | 0e8171ee3e236cedaa8c846eba17f42be94b897a (patch) | |
tree | 2cf979779ffd8934cd256d6a8a64cd95308e17d0 /sys/dev/usb/if_urtw.c | |
parent | 1edb860465fd642089c912055e5f145e85c2c1a2 (diff) |
initial import of Weongyo Jeon's FreeBSD driver for the RealTek RTL8187L
802.11 devices.
currently it has issues scanning.
ok jsg@
Diffstat (limited to 'sys/dev/usb/if_urtw.c')
-rw-r--r-- | sys/dev/usb/if_urtw.c | 3288 |
1 files changed, 3288 insertions, 0 deletions
diff --git a/sys/dev/usb/if_urtw.c b/sys/dev/usb/if_urtw.c new file mode 100644 index 00000000000..6bbecd74c52 --- /dev/null +++ b/sys/dev/usb/if_urtw.c @@ -0,0 +1,3288 @@ +/* $OpenBSD: if_urtw.c,v 1.1 2008/11/27 14:08:07 kevlo Exp $ */ +/*- + * Copyright (c) 2008 Weongyo Jeong + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any + * redistribution must be conditioned upon including a substantially + * similar Disclaimer requirement for further binary redistribution. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGES. + */ + +#include "bpfilter.h" + +#include <sys/param.h> +#include <sys/sockio.h> +#include <sys/proc.h> +#include <sys/mbuf.h> +#include <sys/kernel.h> +#include <sys/socket.h> +#include <sys/systm.h> +#include <sys/malloc.h> +#include <sys/timeout.h> +#include <sys/conf.h> +#include <sys/device.h> + +#include <machine/bus.h> +#include <machine/endian.h> +#if NBPFILTER > 0 +#include <net/bpf.h> +#endif +#include <net/if.h> +#include <net/if_arp.h> +#include <net/if_dl.h> +#include <net/if_media.h> +#include <net/if_types.h> + +#include <netinet/in.h> +#include <netinet/in_systm.h> +#include <netinet/in_var.h> +#include <netinet/if_ether.h> +#include <netinet/ip.h> + +#include <net80211/ieee80211_var.h> +#include <net80211/ieee80211_radiotap.h> + +#include <dev/usb/usb.h> +#include <dev/usb/usbdi.h> +#include <dev/usb/usbdi_util.h> +#include <dev/usb/usbdevs.h> + +#include <dev/usb/if_urtwreg.h> + +#ifdef USB_DEBUG +#define URTW_DEBUG +#endif + +#ifdef URTW_DEBUG +#define DPRINTF(x) do { if (rum_debug) printf x; } while (0) +#define DPRINTFN(n, x) do { if (rum_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 usb_devno urtw_devs[] = { +#define URTW_DEV(v,p) { USB_VENDOR_##v, USB_PRODUCT_##v##_##p } + URTW_DEV(REALTEK, RTL8187), + URTW_DEV(NETGEAR, WG111V2) +#undef URTW_DEV +}; + +#define urtw_read8_m(sc, val, data) do { \ + error = urtw_read8_c(sc, val, data); \ + if (error != 0) \ + goto fail; \ +} while (0) +#define urtw_write8_m(sc, val, data) do { \ + error = urtw_write8_c(sc, val, data); \ + if (error != 0) \ + goto fail; \ +} while (0) +#define urtw_read16_m(sc, val, data) do { \ + error = urtw_read16_c(sc, val, data); \ + if (error != 0) \ + goto fail; \ +} while (0) +#define urtw_write16_m(sc, val, data) do { \ + error = urtw_write16_c(sc, val, data); \ + if (error != 0) \ + goto fail; \ +} while (0) +#define urtw_read32_m(sc, val, data) do { \ + error = urtw_read32_c(sc, val, data); \ + if (error != 0) \ + goto fail; \ +} while (0) +#define urtw_write32_m(sc, val, data) do { \ + error = urtw_write32_c(sc, val, data); \ + if (error != 0) \ + goto fail; \ +} while (0) +#define urtw_8187_write_phy_ofdm(sc, val, data) do { \ + error = urtw_8187_write_phy_ofdm_c(sc, val, data); \ + if (error != 0) \ + goto fail; \ +} while (0) +#define urtw_8187_write_phy_cck(sc, val, data) do { \ + error = urtw_8187_write_phy_cck_c(sc, val, data); \ + if (error != 0) \ + goto fail; \ +} while (0) +#define urtw_8225_write(sc, val, data) do { \ + error = urtw_8225_write_c(sc, val, data); \ + if (error != 0) \ + goto fail; \ +} while (0) + +struct urtw_pair { + uint32_t reg; + uint32_t val; +}; + +static uint8_t urtw_8225_agc[] = { + 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9d, 0x9c, 0x9b, + 0x9a, 0x99, 0x98, 0x97, 0x96, 0x95, 0x94, 0x93, 0x92, 0x91, 0x90, + 0x8f, 0x8e, 0x8d, 0x8c, 0x8b, 0x8a, 0x89, 0x88, 0x87, 0x86, 0x85, + 0x84, 0x83, 0x82, 0x81, 0x80, 0x3f, 0x3e, 0x3d, 0x3c, 0x3b, 0x3a, + 0x39, 0x38, 0x37, 0x36, 0x35, 0x34, 0x33, 0x32, 0x31, 0x30, 0x2f, + 0x2e, 0x2d, 0x2c, 0x2b, 0x2a, 0x29, 0x28, 0x27, 0x26, 0x25, 0x24, + 0x23, 0x22, 0x21, 0x20, 0x1f, 0x1e, 0x1d, 0x1c, 0x1b, 0x1a, 0x19, + 0x18, 0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x0f, 0x0e, + 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, + 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 +}; + +static uint32_t urtw_8225_channel[] = { + 0x0000, /* dummy channel 0 */ + 0x085c, /* 1 */ + 0x08dc, /* 2 */ + 0x095c, /* 3 */ + 0x09dc, /* 4 */ + 0x0a5c, /* 5 */ + 0x0adc, /* 6 */ + 0x0b5c, /* 7 */ + 0x0bdc, /* 8 */ + 0x0c5c, /* 9 */ + 0x0cdc, /* 10 */ + 0x0d5c, /* 11 */ + 0x0ddc, /* 12 */ + 0x0e5c, /* 13 */ + 0x0f72, /* 14 */ +}; + +static uint8_t urtw_8225_gain[] = { + 0x23, 0x88, 0x7c, 0xa5, /* -82dbm */ + 0x23, 0x88, 0x7c, 0xb5, /* -82dbm */ + 0x23, 0x88, 0x7c, 0xc5, /* -82dbm */ + 0x33, 0x80, 0x79, 0xc5, /* -78dbm */ + 0x43, 0x78, 0x76, 0xc5, /* -74dbm */ + 0x53, 0x60, 0x73, 0xc5, /* -70dbm */ + 0x63, 0x58, 0x70, 0xc5, /* -66dbm */ +}; + +static struct urtw_pair urtw_8225_rf_part1[] = { + { 0x00, 0x0067 }, { 0x01, 0x0fe0 }, { 0x02, 0x044d }, { 0x03, 0x0441 }, + { 0x04, 0x0486 }, { 0x05, 0x0bc0 }, { 0x06, 0x0ae6 }, { 0x07, 0x082a }, + { 0x08, 0x001f }, { 0x09, 0x0334 }, { 0x0a, 0x0fd4 }, { 0x0b, 0x0391 }, + { 0x0c, 0x0050 }, { 0x0d, 0x06db }, { 0x0e, 0x0029 }, { 0x0f, 0x0914 }, +}; + +static struct urtw_pair urtw_8225_rf_part2[] = { + { 0x00, 0x01 }, { 0x01, 0x02 }, { 0x02, 0x42 }, { 0x03, 0x00 }, + { 0x04, 0x00 }, { 0x05, 0x00 }, { 0x06, 0x40 }, { 0x07, 0x00 }, + { 0x08, 0x40 }, { 0x09, 0xfe }, { 0x0a, 0x09 }, { 0x0b, 0x80 }, + { 0x0c, 0x01 }, { 0x0e, 0xd3 }, { 0x0f, 0x38 }, { 0x10, 0x84 }, + { 0x11, 0x06 }, { 0x12, 0x20 }, { 0x13, 0x20 }, { 0x14, 0x00 }, + { 0x15, 0x40 }, { 0x16, 0x00 }, { 0x17, 0x40 }, { 0x18, 0xef }, + { 0x19, 0x19 }, { 0x1a, 0x20 }, { 0x1b, 0x76 }, { 0x1c, 0x04 }, + { 0x1e, 0x95 }, { 0x1f, 0x75 }, { 0x20, 0x1f }, { 0x21, 0x27 }, + { 0x22, 0x16 }, { 0x24, 0x46 }, { 0x25, 0x20 }, { 0x26, 0x90 }, + { 0x27, 0x88 } +}; + +static struct urtw_pair urtw_8225_rf_part3[] = { + { 0x00, 0x98 }, { 0x03, 0x20 }, { 0x04, 0x7e }, { 0x05, 0x12 }, + { 0x06, 0xfc }, { 0x07, 0x78 }, { 0x08, 0x2e }, { 0x10, 0x9b }, + { 0x11, 0x88 }, { 0x12, 0x47 }, { 0x13, 0xd0 }, { 0x19, 0x00 }, + { 0x1a, 0xa0 }, { 0x1b, 0x08 }, { 0x40, 0x86 }, { 0x41, 0x8d }, + { 0x42, 0x15 }, { 0x43, 0x18 }, { 0x44, 0x1f }, { 0x45, 0x1e }, + { 0x46, 0x1a }, { 0x47, 0x15 }, { 0x48, 0x10 }, { 0x49, 0x0a }, + { 0x4a, 0x05 }, { 0x4b, 0x02 }, { 0x4c, 0x05 } +}; + +static uint16_t urtw_8225_rxgain[] = { + 0x0400, 0x0401, 0x0402, 0x0403, 0x0404, 0x0405, 0x0408, 0x0409, + 0x040a, 0x040b, 0x0502, 0x0503, 0x0504, 0x0505, 0x0540, 0x0541, + 0x0542, 0x0543, 0x0544, 0x0545, 0x0580, 0x0581, 0x0582, 0x0583, + 0x0584, 0x0585, 0x0588, 0x0589, 0x058a, 0x058b, 0x0643, 0x0644, + 0x0645, 0x0680, 0x0681, 0x0682, 0x0683, 0x0684, 0x0685, 0x0688, + 0x0689, 0x068a, 0x068b, 0x068c, 0x0742, 0x0743, 0x0744, 0x0745, + 0x0780, 0x0781, 0x0782, 0x0783, 0x0784, 0x0785, 0x0788, 0x0789, + 0x078a, 0x078b, 0x078c, 0x078d, 0x0790, 0x0791, 0x0792, 0x0793, + 0x0794, 0x0795, 0x0798, 0x0799, 0x079a, 0x079b, 0x079c, 0x079d, + 0x07a0, 0x07a1, 0x07a2, 0x07a3, 0x07a4, 0x07a5, 0x07a8, 0x07a9, + 0x07aa, 0x07ab, 0x07ac, 0x07ad, 0x07b0, 0x07b1, 0x07b2, 0x07b3, + 0x07b4, 0x07b5, 0x07b8, 0x07b9, 0x07ba, 0x07bb, 0x07bb +}; + +static uint8_t urtw_8225_threshold[] = { + 0x8d, 0x8d, 0x8d, 0x8d, 0x9d, 0xad, 0xbd, +}; + +static uint8_t urtw_8225_tx_gain_cck_ofdm[] = { + 0x02, 0x06, 0x0e, 0x1e, 0x3e, 0x7e +}; + +static uint8_t urtw_8225_txpwr_cck[] = { + 0x18, 0x17, 0x15, 0x11, 0x0c, 0x08, 0x04, 0x02, + 0x1b, 0x1a, 0x17, 0x13, 0x0e, 0x09, 0x04, 0x02, + 0x1f, 0x1e, 0x1a, 0x15, 0x10, 0x0a, 0x05, 0x02, + 0x22, 0x21, 0x1d, 0x18, 0x11, 0x0b, 0x06, 0x02, + 0x26, 0x25, 0x21, 0x1b, 0x14, 0x0d, 0x06, 0x03, + 0x2b, 0x2a, 0x25, 0x1e, 0x16, 0x0e, 0x07, 0x03 +}; + +static uint8_t urtw_8225_txpwr_cck_ch14[] = { + 0x18, 0x17, 0x15, 0x0c, 0x00, 0x00, 0x00, 0x00, + 0x1b, 0x1a, 0x17, 0x0e, 0x00, 0x00, 0x00, 0x00, + 0x1f, 0x1e, 0x1a, 0x0f, 0x00, 0x00, 0x00, 0x00, + 0x22, 0x21, 0x1d, 0x11, 0x00, 0x00, 0x00, 0x00, + 0x26, 0x25, 0x21, 0x13, 0x00, 0x00, 0x00, 0x00, + 0x2b, 0x2a, 0x25, 0x15, 0x00, 0x00, 0x00, 0x00 +}; + +static uint8_t urtw_8225_txpwr_ofdm[]={ + 0x80, 0x90, 0xa2, 0xb5, 0xcb, 0xe4 +}; + +static uint8_t urtw_8225v2_gain_bg[]={ + 0x23, 0x15, 0xa5, /* -82-1dbm */ + 0x23, 0x15, 0xb5, /* -82-2dbm */ + 0x23, 0x15, 0xc5, /* -82-3dbm */ + 0x33, 0x15, 0xc5, /* -78dbm */ + 0x43, 0x15, 0xc5, /* -74dbm */ + 0x53, 0x15, 0xc5, /* -70dbm */ + 0x63, 0x15, 0xc5, /* -66dbm */ +}; + +static struct urtw_pair urtw_8225v2_rf_part1[] = { + { 0x00, 0x02bf }, { 0x01, 0x0ee0 }, { 0x02, 0x044d }, { 0x03, 0x0441 }, + { 0x04, 0x08c3 }, { 0x05, 0x0c72 }, { 0x06, 0x00e6 }, { 0x07, 0x082a }, + { 0x08, 0x003f }, { 0x09, 0x0335 }, { 0x0a, 0x09d4 }, { 0x0b, 0x07bb }, + { 0x0c, 0x0850 }, { 0x0d, 0x0cdf }, { 0x0e, 0x002b }, { 0x0f, 0x0114 } +}; + +static struct urtw_pair urtw_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[] = { + 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0008, 0x0009, + 0x000a, 0x000b, 0x0102, 0x0103, 0x0104, 0x0105, 0x0140, 0x0141, + 0x0142, 0x0143, 0x0144, 0x0145, 0x0180, 0x0181, 0x0182, 0x0183, + 0x0184, 0x0185, 0x0188, 0x0189, 0x018a, 0x018b, 0x0243, 0x0244, + 0x0245, 0x0280, 0x0281, 0x0282, 0x0283, 0x0284, 0x0285, 0x0288, + 0x0289, 0x028a, 0x028b, 0x028c, 0x0342, 0x0343, 0x0344, 0x0345, + 0x0380, 0x0381, 0x0382, 0x0383, 0x0384, 0x0385, 0x0388, 0x0389, + 0x038a, 0x038b, 0x038c, 0x038d, 0x0390, 0x0391, 0x0392, 0x0393, + 0x0394, 0x0395, 0x0398, 0x0399, 0x039a, 0x039b, 0x039c, 0x039d, + 0x03a0, 0x03a1, 0x03a2, 0x03a3, 0x03a4, 0x03a5, 0x03a8, 0x03a9, + 0x03aa, 0x03ab, 0x03ac, 0x03ad, 0x03b0, 0x03b1, 0x03b2, 0x03b3, + 0x03b4, 0x03b5, 0x03b8, 0x03b9, 0x03ba, 0x03bb, 0x03bb +}; + +static uint8_t urtw_8225v2_tx_gain_cck_ofdm[] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, + 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, + 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, + 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, + 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, +}; + +static uint8_t urtw_8225v2_txpwr_cck[] = { + 0x36, 0x35, 0x2e, 0x25, 0x1c, 0x12, 0x09, 0x04 +}; + +static uint8_t urtw_8225v2_txpwr_cck_ch14[] = { + 0x36, 0x35, 0x2e, 0x1b, 0x00, 0x00, 0x00, 0x00 +}; + +static 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_data_list(struct urtw_softc *, struct urtw_data [], + int, int, int); +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_free_data_list(struct urtw_softc *, + usbd_pipe_handle, usbd_pipe_handle, + struct urtw_data data[], int); +void urtw_rxeof(usbd_xfer_handle, usbd_private_handle, + usbd_status); +int urtw_tx_start(struct urtw_softc *, + struct ieee80211_node *, struct mbuf *, int); +void urtw_txeof_low(usbd_xfer_handle, usbd_private_handle, + usbd_status); +void urtw_txeof_normal(usbd_xfer_handle, usbd_private_handle, + 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 *); +usbd_status urtw_read16_c(struct urtw_softc *, int, uint16_t *); +usbd_status urtw_read32_c(struct urtw_softc *, int, uint32_t *); +usbd_status urtw_write8_c(struct urtw_softc *, int, uint8_t); +usbd_status urtw_write16_c(struct urtw_softc *, int, uint16_t); +usbd_status urtw_write32_c(struct urtw_softc *, int, uint32_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_softc *); +usbd_status urtw_8225_rf_set_chan(struct urtw_softc *, int); +usbd_status urtw_8225_rf_set_sens(struct urtw_softc *, int); +usbd_status urtw_8225_set_txpwrlvl(struct urtw_softc *, int); +usbd_status urtw_8225v2_rf_init(struct urtw_softc *); +usbd_status urtw_8225v2_rf_set_chan(struct urtw_softc *, 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 *sc); + +int urtw_match(struct device *, void *, void *); +void urtw_attach(struct device *, struct device *, void *); +int urtw_detach(struct device *, int); +int urtw_activate(struct device *, enum devact); + +struct cfdriver urtw_cd = { + NULL, "urtw", DV_IFNET +}; + +const struct cfattach urtw_ca = { + sizeof(struct urtw_softc), + urtw_match, + urtw_attach, + urtw_detach, + urtw_activate, +}; + +int +urtw_match(struct device *parent, void *match, void *aux) +{ + struct usb_attach_arg *uaa = aux; + + if (uaa->iface != NULL) + return UMATCH_NONE; + + return (usb_lookup(urtw_devs, uaa->vendor, uaa->product) != NULL) ? + UMATCH_VENDOR_PRODUCT : 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; + uint32_t data; + int i; + + sc->sc_udev = uaa->device; + + 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_init_task(&sc->sc_ledtask, urtw_ledusbtask, sc); + 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; + ifp->if_init = urtw_init; + ifp->if_ioctl = urtw_ioctl; + ifp->if_start = urtw_start; + ifp->if_watchdog = urtw_watchdog; + IFQ_SET_READY(&ifp->if_snd); + 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 + + usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH, sc->sc_udev, + &sc->sc_dev); + + printf("%s: address %s\n", + sc->sc_dev.dv_xname, ether_sprintf(ic->ic_myaddr)); + + return; +fail: + printf("%s: %s failed!\n", sc->sc_dev.dv_xname, __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(); + + ieee80211_ifdetach(ifp); /* free all nodes */ + if_detach(ifp); + + usb_rem_task(sc->sc_udev, &sc->sc_task); + usb_rem_task(sc->sc_udev, &sc->sc_ledtask); + timeout_del(&sc->scan_to); + timeout_del(&sc->sc_led_ch); + + /* abort and free xfers */ + urtw_free_tx_data_list(sc); + urtw_free_rx_data_list(sc); + urtw_close_pipes(sc); + + splx(s); + + usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, sc->sc_udev, + &sc->sc_dev); + + return (0); +} + +int +urtw_activate(struct device *self, enum devact act) +{ + switch (act) { + case DVACT_ACTIVATE: + return (EOPNOTSUPP); + case (DVACT_DEACTIVATE): + break; + } + + 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 */ + error = usbd_open_pipe(sc->sc_iface, 0x2, 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 */ + error = usbd_open_pipe(sc->sc_iface, 0x3, 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 */ + error = usbd_open_pipe(sc->sc_iface, 0x81, 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_data_list(struct urtw_softc *sc, struct urtw_data data[], + int ndata, int maxsz, int fillmbuf) +{ + int i, error; + + for (i = 0; i < ndata; i++) { + struct urtw_data *dp = &data[i]; + + dp->sc = sc; + dp->xfer = usbd_alloc_xfer(sc->sc_udev); + if (dp->xfer == NULL) { + printf("%s: could not allocate xfer\n", + sc->sc_dev.dv_xname); + error = ENOMEM; + goto fail; + } + if (fillmbuf) { + /* XXX check maxsz */ + MGETHDR(dp->m, M_DONTWAIT, MT_DATA); + if (dp->m == NULL) { + printf("%s: could not allocate rx mbuf\n", + sc->sc_dev.dv_xname); + error = ENOMEM; + goto fail; + } + MCLGET(dp->m, M_DONTWAIT); + if (!(dp->m->m_flags & M_EXT)) { + printf("%s: could not allocate rx mbuf cluster" + "\n", sc->sc_dev.dv_xname); + error = ENOMEM; + goto fail; + } + dp->buf = mtod(dp->m, uint8_t *); + } else { + dp->m = NULL; + dp->buf = usbd_alloc_buffer(dp->xfer, maxsz); + if (dp->buf == NULL) { + printf("%s: could not allocate buffer\n", + sc->sc_dev.dv_xname); + error = ENOMEM; + goto fail; + } + if (((unsigned long)dp->buf) % 4) + printf("%s: warn: unaligned buffer %p\n", + sc->sc_dev.dv_xname, dp->buf); + } + dp->ni = NULL; + } + + return (0); + +fail: urtw_free_data_list(sc, NULL, NULL, data, ndata); + return (error); +} + +void +urtw_free_data_list(struct urtw_softc *sc, usbd_pipe_handle pipe1, + usbd_pipe_handle pipe2, struct urtw_data data[], int ndata) +{ + struct ieee80211com *ic = &sc->sc_ic; + int i; + + /* make sure no transfers are pending */ + if (pipe1 != NULL) + usbd_abort_pipe(pipe1); + if (pipe2 != NULL) + usbd_abort_pipe(pipe2); + + for (i = 0; i < ndata; i++) { + struct urtw_data *dp = &data[i]; + + if (dp->xfer != NULL) { + usbd_free_xfer(dp->xfer); + dp->xfer = NULL; + } + if (dp->m != NULL) { + m_freem(dp->m); + dp->m = NULL; + } + if (dp->ni != NULL) { + ieee80211_release_node(ic, dp->ni); + dp->ni = NULL; + } + } +} + +int +urtw_alloc_rx_data_list(struct urtw_softc *sc) +{ + + /* XXX todo */ + printf("%s: WARNING: rx buffer is smaller than %d\n", + sc->sc_dev.dv_xname, URTW_RX_MAXSIZE); + return urtw_alloc_data_list(sc, + sc->sc_rxdata, URTW_RX_DATA_LIST_COUNT, MCLBYTES, 1 /* mbufs */); +} + +void +urtw_free_rx_data_list(struct urtw_softc *sc) +{ + + urtw_free_data_list(sc, sc->sc_rxpipe, NULL, sc->sc_rxdata, + URTW_RX_DATA_LIST_COUNT); +} + +int +urtw_alloc_tx_data_list(struct urtw_softc *sc) +{ + + return urtw_alloc_data_list(sc, + sc->sc_txdata, URTW_TX_DATA_LIST_COUNT, URTW_TX_MAXSIZE, + 0 /* no mbufs */); +} + +void +urtw_free_tx_data_list(struct urtw_softc *sc) +{ + + urtw_free_data_list(sc, sc->sc_txpipe_low, sc->sc_txpipe_normal, + sc->sc_txdata, URTW_TX_DATA_LIST_COUNT); +} + +int +urtw_media_change(struct ifnet *ifp) +{ + int error; + + error = ieee80211_media_change(ifp); + if (error != ENETRESET) + return (error); + + if ((ifp->if_flags & (IFF_UP | IFF_RUNNING)) == (IFF_UP | IFF_RUNNING)) + urtw_init(ifp); + + return (0); +} + +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); +} + +/* XXX why we should allocalte memory buffer instead of using memory stack? */ +usbd_status +urtw_8225_write_s16(struct urtw_softc *sc, uint8_t addr, int index, + uint16_t *data) +{ + uint8_t *buf; + uint16_t data16; + usb_device_request_t *req; + usbd_status error = 0; + + data16 = *data; + req = (usb_device_request_t *)malloc(sizeof(usb_device_request_t), + M_DEVBUF, M_NOWAIT | M_ZERO); + if (req == NULL) { + printf("%s: could not allocate a memory\n", + sc->sc_dev.dv_xname); + goto fail0; + } + buf = (uint8_t *)malloc(2, M_DEVBUF, M_NOWAIT | M_ZERO); + if (req == NULL) { + printf("%s: could not allocate a memory\n", + sc->sc_dev.dv_xname); + goto fail1; + } + + req->bmRequestType = UT_WRITE_VENDOR_DEVICE; + req->bRequest = URTW_8187_SETREGS_REQ; + USETW(req->wValue, addr); + USETW(req->wIndex, index); + USETW(req->wLength, sizeof(uint16_t)); + buf[0] = (data16 & 0x00ff); + buf[1] = (data16 & 0xff00) >> 8; + + error = usbd_do_request(sc->sc_udev, req, buf); + + free(buf, M_DEVBUF); +fail1: free(req, M_DEVBUF); +fail0: return (error); +} + +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, 1100); + + 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) +{ + int ret; + uint32_t data; + usbd_status error; + + error = urtw_eprom_read32(sc, URTW_EPROM_RFCHIPID, &data); + if (error != 0) + goto fail; + switch (data & 0xff) { + case URTW_EPROM_RFCHIPID_RTL8225U: + error = urtw_8225_isv2(sc, &ret); + if (error != 0) + goto fail; + if (ret == 0) { + sc->sc_rf_init = urtw_8225_rf_init; + sc->sc_rf_set_sens = urtw_8225_rf_set_sens; + sc->sc_rf_set_chan = urtw_8225_rf_set_chan; + } else { + sc->sc_rf_init = urtw_8225v2_rf_init; + sc->sc_rf_set_chan = urtw_8225v2_rf_set_chan; + } + sc->sc_max_sens = URTW_8225_RF_MAX_SENS; + sc->sc_sens = URTW_8225_RF_DEF_SENS; + break; + default: + panic("unsupported RF chip %d\n", data & 0xff); + /* never reach */ + } + +fail: + 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; + } + for (i = 1, j = 0; i < 4; i += 2, j++) { + error = urtw_eprom_read32(sc, URTW_EPROM_TXPW2 + j, &data); + if (error != 0) + goto fail; + sc->sc_txpwr_cck[i + 6 + 4] = data & 0xf; + sc->sc_txpwr_cck[i + 6 + 4 + 1] = (data & 0xf00) >> 8; + sc->sc_txpwr_ofdm[i + 6 + 4] = (data & 0xf0) >> 4; + sc->sc_txpwr_ofdm[i + 6 + 4 + 1] = (data & 0xf000) >> 12; + } +fail: + return (error); +} + +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) +{ + 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, 0); + 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) +{ + 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, 0); + USETW(req.wLength, sizeof(uint16_t)); + + error = usbd_do_request(sc->sc_udev, &req, data); + return (error); +} + +usbd_status +urtw_read32_c(struct urtw_softc *sc, int val, uint32_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 | 0xff00); + USETW(req.wIndex, 0); + USETW(req.wLength, sizeof(uint32_t)); + + error = usbd_do_request(sc->sc_udev, &req, data); + return (error); +} + +usbd_status +urtw_write8_c(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 | 0xff00); + USETW(req.wIndex, 0); + 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) +{ + 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, 0); + USETW(req.wLength, sizeof(uint16_t)); + + return (usbd_do_request(sc->sc_udev, &req, &data)); +} + +usbd_status +urtw_write32_c(struct urtw_softc *sc, int val, uint32_t data) +{ + 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, 0); + USETW(req.wLength, sizeof(uint32_t)); + + 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_8225_ANAPARAM_ON); + if (error) + goto fail; + error = urtw_8185_set_anaparam2(sc, URTW_8225_ANAPARAM2_ON); + if (error) + goto fail; + + error = urtw_intr_disable(sc); + if (error) + goto fail; + usbd_delay_ms(sc->sc_udev, 200); + + 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, 200); + + 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, 200); + + 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, 200); + + error = urtw_8180_set_anaparam(sc, URTW_8225_ANAPARAM_ON); + if (error) + goto fail; + error = urtw_8185_set_anaparam2(sc, URTW_8225_ANAPARAM2_ON); + if (error) + goto fail; +fail: + return (error); +} + +usbd_status +urtw_led_on(struct urtw_softc *sc, int type) +{ + usbd_status error; + + if (type == URTW_LED_GPIO) { + switch (sc->sc_gpio_ledpin) { + case URTW_LED_PIN_GPIO0: + urtw_write8_m(sc, URTW_GPIO, 0x01); + urtw_write8_m(sc, URTW_GP_ENABLE, 0x00); + break; + default: + panic("unsupported LED PIN type 0x%x", + sc->sc_gpio_ledpin); + /* never reach */ + } + } else { + panic("unsupported LED type 0x%x", type); + /* never reach */ + } + + sc->sc_gpio_ledon = 1; +fail: + return (error); +} + +static usbd_status +urtw_led_off(struct urtw_softc *sc, int type) +{ + usbd_status error; + + if (type == URTW_LED_GPIO) { + switch (sc->sc_gpio_ledpin) { + case URTW_LED_PIN_GPIO0: + urtw_write8_m(sc, URTW_GPIO, 0x01); + urtw_write8_m(sc, URTW_GP_ENABLE, 0x01); + break; + default: + panic("unsupported LED PIN type 0x%x", + sc->sc_gpio_ledpin); + /* never reach */ + } + } else { + panic("unsupported LED type 0x%x", type); + /* never reach */ + } + + sc->sc_gpio_ledon = 0; + +fail: + return (error); +} + +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: + panic("unsupported LED mode 0x%x", mode); + /* never reach */ + } + + switch (sc->sc_gpio_ledstate) { + case URTW_LED_ON: + if (sc->sc_gpio_ledinprogress != 0) + break; + urtw_led_on(sc, URTW_LED_GPIO); + break; + case URTW_LED_BLINK_NORMAL: + if (sc->sc_gpio_ledinprogress != 0) + break; + sc->sc_gpio_ledinprogress = 1; + sc->sc_gpio_blinkstate = (sc->sc_gpio_ledon != 0) ? + URTW_LED_OFF : URTW_LED_ON; + t.tv_sec = 0; + t.tv_usec = 100 * 1000L; + 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: + panic("unknown LED status 0x%x", sc->sc_gpio_ledstate); + /* never reach */ + } + 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) + panic("could not process a LED strategy 0x%x", sc->sc_strategy); + + 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: + panic("unsupported LED mode %d\n", sc->sc_strategy); + /* never reach */ + } + + 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; + timeout_add(&sc->sc_led_ch, tvtohz(&t)); + break; + default: + panic("unknown LED status 0x%x", sc->sc_gpio_ledstate); + /* never reach */ + } + 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; + + 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; + case IEEE80211_M_IBSS: + data |= URTW_MSR_LINK_ADHOC; + case IEEE80211_M_HOSTAP: + data |= URTW_MSR_LINK_HOSTAP; + break; + default: + panic("unsupported operation mode 0x%x\n", + ic->ic_opmode); + /* never reach */ + } + } else + data |= URTW_MSR_LINK_NONE; + + urtw_write8_m(sc, URTW_MSR, data); +fail: + return (error); +} + +uint16_t +urtw_rate2rtl(int rate) +{ +#define N(a) (sizeof(a) / sizeof((a)[0])) + int i; + + for (i = 0; i < N(urtw_ratetable); i++) { + if (rate == urtw_ratetable[i].reg) + return urtw_ratetable[i].val; + } + + return (3); +#undef N +} + +uint16_t +urtw_rtl2rate(int rate) +{ +#define N(a) (sizeof(a) / sizeof((a)[0])) + int i; + + for (i = 0; i < N(urtw_ratetable); i++) { + if (rate == urtw_ratetable[i].val) + return urtw_ratetable[i].reg; + } + + return (0); +#undef N +} + +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_BRSR, &data); + data &= ~URTW_BRSR_MBR_8185; + + for (i = 0; i <= basic_rate; i++) + data |= (1 << i); + + urtw_write16_m(sc, URTW_BRSR, data); +fail: + return (error); +} + +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_data *rxdata; + uint8_t data; + usbd_status error; + + /* + * Start up the receive pipe. + */ + for (i = 0; i < URTW_RX_DATA_LIST_COUNT; i++) { + rxdata = &sc->sc_rxdata[i]; + + usbd_setup_xfer(rxdata->xfer, sc->sc_rxpipe, rxdata, + rxdata->buf, MCLBYTES, USBD_SHORT_XFER_OK, USBD_NO_TIMEOUT, + urtw_rxeof); + error = usbd_transfer(rxdata->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; + + urtw_read8_m(sc, URTW_CW_CONF, &data8); + data8 &= ~(URTW_CW_CONF_PERPACKET_CW | URTW_CW_CONF_PERPACKET_RETRY); + urtw_write8_m(sc, URTW_CW_CONF, data8); + + urtw_read8_m(sc, URTW_TX_AGC_CTL, &data8); + data8 &= ~URTW_TX_AGC_CTL_PERPACKET_GAIN; + data8 &= ~URTW_TX_AGC_CTL_PERPACKET_ANTSEL; + data8 &= ~URTW_TX_AGC_CTL_FEEDBACK_ANT; + urtw_write8_m(sc, URTW_TX_AGC_CTL, data8); + + urtw_read32_m(sc, URTW_TX_CONF, &data); + data &= ~URTW_TX_LOOPBACK_MASK; + data |= URTW_TX_LOOPBACK_NONE; + data &= ~(URTW_TX_DPRETRY_MASK | URTW_TX_RTSRETRY_MASK); + data |= sc->sc_tx_retry << URTW_TX_DPRETRY_SHIFT; + data |= sc->sc_rts_retry << URTW_TX_RTSRETRY_SHIFT; + data &= ~(URTW_TX_NOCRC | URTW_TX_MXDMA_MASK); + data |= URTW_TX_MXDMA_2048 | URTW_TX_CWMIN | URTW_TX_DISCW; + data &= ~URTW_TX_SWPLCPLEN; + data |= URTW_TX_NOICV; + urtw_write32_m(sc, URTW_TX_CONF, data); + + urtw_read8_m(sc, URTW_CMD, &data8); + urtw_write8_m(sc, URTW_CMD, data8 | URTW_CMD_TX_ENABLE); +fail: + return (error); +} + +int +urtw_init(struct ifnet *ifp) +{ + struct urtw_softc *sc = ifp->if_softc; + struct ieee80211com *ic = &sc->sc_ic; + usbd_status error; + int ret; + + 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)); + urtw_write32_m(sc, URTW_MAC0, ((uint32_t *)ic->ic_myaddr)[0]); + urtw_write16_m(sc, URTW_MAC4, ((uint32_t *)ic->ic_myaddr)[1] & 0xffff); + error = urtw_set_mode(sc, URTW_EPROM_CMD_NORMAL); + if (error) + goto fail; + + error = urtw_update_msr(sc); + if (error) + goto fail; + + urtw_write32_m(sc, URTW_INT_TIMEOUT, 0); + urtw_write8_m(sc, URTW_WPA_CONFIG, 0); + urtw_write8_m(sc, URTW_RATE_FALLBACK, 0x81); + error = urtw_set_rate(sc); + if (error != 0) + goto fail; + + error = sc->sc_rf_init(sc); + if (error != 0) + goto fail; + if (sc->sc_rf_set_sens != NULL) + sc->sc_rf_set_sens(sc, sc->sc_sens); + + 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 = usbd_set_config_no(sc->sc_udev, URTW_CONFIG_NO, 0); + if (error != 0) { + printf("%s: could not set configuration no\n", + sc->sc_dev.dv_xname); + goto fail; + } + /* get the first interface handle */ + error = usbd_device2interface_handle(sc->sc_udev, + URTW_IFACE_INDEX, &sc->sc_iface); + if (error != 0) { + printf("%s: could not get interface handle\n", + sc->sc_dev.dv_xname); + goto fail; + } + 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_OACTIVE; + 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 ifaddr *ifa; + struct ifreq *ifr; + int s, error = 0; + + s = splnet(); + + switch (cmd) { + case SIOCSIFADDR: + ifa = (struct ifaddr *)data; + ifp->if_flags |= IFF_UP; +#ifdef INET + if (ifa->ifa_addr->sa_family == AF_INET) + arp_ifinit(&ic->ic_ac, ifa); +#endif + /* FALLTHROUGH */ + case SIOCSIFFLAGS: + if (ifp->if_flags & IFF_UP) { + /* + * If 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)) + urtw_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)) + urtw_init(ifp); + error = 0; + } + + splx(s); + + 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 | IFF_OACTIVE)) != IFF_RUNNING) + return; + + for (;;) { + IF_POLL(&ic->ic_mgtq, m0); + if (m0 != NULL) { + if (sc->sc_tx_low_queued >= URTW_TX_DATA_LIST_COUNT || + sc->sc_tx_normal_queued >= + URTW_TX_DATA_LIST_COUNT) { + ifp->if_flags |= IFF_OACTIVE; + break; + } + IF_DEQUEUE(&ic->ic_mgtq, m0); + ni = (struct ieee80211_node *)m0->m_pkthdr.rcvif; + m0->m_pkthdr.rcvif = NULL; +#if NBPFILTER > 0 + if (ic->ic_rawbpf != NULL) + bpf_mtap(ic->ic_rawbpf, m0, 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_POLL(&ifp->if_snd, m0); + if (m0 == NULL) + break; + if (sc->sc_tx_low_queued >= URTW_TX_DATA_LIST_COUNT || + sc->sc_tx_normal_queued >= + URTW_TX_DATA_LIST_COUNT) { + ifp->if_flags |= IFF_OACTIVE; + break; + } + IFQ_DEQUEUE(&ifp->if_snd, m0); +#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(usbd_xfer_handle xfer, usbd_private_handle priv, + usbd_status status) +{ + struct urtw_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; + ifp->if_opackets++; + + sc->sc_tx_low_queued--; + ifp->if_flags &= ~IFF_OACTIVE; + urtw_start(ifp); + + splx(s); +} + +void +urtw_txeof_normal(usbd_xfer_handle xfer, usbd_private_handle priv, + usbd_status status) +{ + struct urtw_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; + ifp->if_opackets++; + + sc->sc_tx_normal_queued--; + ifp->if_flags &= ~IFF_OACTIVE; + 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_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 + + xferlen = m0->m_pkthdr.len + 4 * 3; + if ((0 == xferlen % 64) || (0 == xferlen % 512)) + xferlen += 1; + + data = &sc->sc_txdata[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) + panic("TODO tx."); + 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); + } + data->buf[8] = 3; /* CW minimum */ + data->buf[8] |= (7 << 4); /* CW maximum */ + data->buf[9] |= 11; /* retry limitation */ + + m_copydata(m0, 0, m0->m_pkthdr.len, (uint8_t *)&data->buf[12]); + data->ni = ni; + data->m = 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, 100); + usbd_delay_ms(sc->sc_udev, 1000); +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))); + 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_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_softc *sc) +{ +#define N(a) (sizeof(a) / sizeof((a)[0])) + int i; + uint16_t data; + usbd_status error; + + error = urtw_8180_set_anaparam(sc, URTW_8225_ANAPARAM_ON); + if (error) + goto fail; + + error = urtw_8225_usb_init(sc); + if (error) + goto fail; + + urtw_write32_m(sc, URTW_RF_TIMING, 0x000a8008); + urtw_read16_m(sc, URTW_BRSR, &data); /* XXX ??? */ + urtw_write16_m(sc, URTW_BRSR, 0xffff); + urtw_write32_m(sc, URTW_RF_PARA, 0x100044); + + error = urtw_set_mode(sc, URTW_EPROM_CMD_CONFIG); + if (error) + goto fail; + urtw_write8_m(sc, URTW_CONFIG3, 0x44); + error = urtw_set_mode(sc, URTW_EPROM_CMD_NORMAL); + if (error) + goto fail; + + error = urtw_8185_rf_pins_enable(sc); + if (error) + goto fail; + usbd_delay_ms(sc->sc_udev, 1000); + + for (i = 0; i < N(urtw_8225_rf_part1); i++) { + urtw_8225_write(sc, urtw_8225_rf_part1[i].reg, + urtw_8225_rf_part1[i].val); + usbd_delay_ms(sc->sc_udev, 1); + } + usbd_delay_ms(sc->sc_udev, 100); + 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 < 95; 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 < 128; i++) { + urtw_8187_write_phy_ofdm(sc, 0xb, urtw_8225_agc[i]); + usbd_delay_ms(sc->sc_udev, 1); + urtw_8187_write_phy_ofdm(sc, 0xa, (uint8_t)i + 0x80); + usbd_delay_ms(sc->sc_udev, 1); + } + + for (i = 0; i < N(urtw_8225_rf_part2); i++) { + urtw_8187_write_phy_ofdm(sc, urtw_8225_rf_part2[i].reg, + urtw_8225_rf_part2[i].val); + usbd_delay_ms(sc->sc_udev, 1); + } + + error = urtw_8225_setgain(sc, 4); + if (error) + goto fail; + + for (i = 0; i < N(urtw_8225_rf_part3); i++) { + urtw_8187_write_phy_cck(sc, urtw_8225_rf_part3[i].reg, + urtw_8225_rf_part3[i].val); + 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(sc, 1); +fail: + return (error); +#undef N +} + +usbd_status +urtw_8225_rf_set_chan(struct urtw_softc *sc, int chan) +{ + 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, 0x9); + else + urtw_write8_m(sc, URTW_SLOT, 0x14); + + if (IEEE80211_IS_CHAN_G(c)) { + urtw_write8_m(sc, URTW_DIFS, 0x14); + urtw_write8_m(sc, URTW_EIFS, 0x5b - 0x14); + urtw_write8_m(sc, URTW_CW_VAL, 0x73); + } else { + urtw_write8_m(sc, URTW_DIFS, 0x24); + urtw_write8_m(sc, URTW_EIFS, 0x5b - 0x24); + urtw_write8_m(sc, URTW_CW_VAL, 0xa5); + } + +fail: + return (error); +} + +usbd_status +urtw_8225_rf_set_sens(struct urtw_softc *sc, int sens) +{ + usbd_status error; + + if (sens < 0 || sens > 6) + return -1; + + if (sens > 4) + urtw_8225_write(sc, 0x0c, 0x850); + else + urtw_8225_write(sc, 0x0c, 0x50); + + sens = 6 - sens; + error = urtw_8225_setgain(sc, sens); + if (error) + goto fail; + + urtw_8187_write_phy_cck(sc, 0x41, urtw_8225_threshold[sens]); + +fail: + return (error); +} + +void +urtw_stop(struct ifnet *ifp, int disable) +{ + struct urtw_softc *sc = ifp->if_softc; + + ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE); + + timeout_del(&sc->scan_to); + timeout_del(&sc->sc_led_ch); + + 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); +} + +int +urtw_isbmode(uint16_t rate) +{ + + rate = urtw_rtl2rate(rate); + + return ((rate <= 22 && rate != 12 && rate != 18) || + rate == 44) ? (1) : (0); +} + +void +urtw_rxeof(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status) +{ + struct urtw_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; + } + + /* 4 dword and 4 byte CRC */ + len = actlen - (4 * 4); + desc = data->buf + len; + flen = ((desc[1] & 0x0f) << 8) + (desc[0] & 0xff); + if (flen > actlen) { + ifp->if_ierrors++; + goto skip; + } + + rate = (desc[2] & 0xf0) >> 4; + quality = desc[4] & 0xff; + /* XXX correct? */ + rssi = (desc[6] & 0xfe) >> 1; + if (!urtw_isbmode(rate)) { + rssi = (rssi > 90) ? 90 : ((rssi < 25) ? 25 : rssi); + rssi = ((90 - rssi) * 100) / 65; + } else { + rssi = (rssi > 90) ? 95 : ((rssi < 30) ? 30 : rssi); + rssi = ((95 - rssi) * 100) / 65; + } + + 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.rcvif = ifp; + 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_bss->ni_chan->ic_freq); + tap->wr_chan_flags = htole16(ic->ic_bss->ni_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? */ + nf = (quality > 64) ? 0 : ((64 - quality) * 100) / 64; + + /* 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_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_softc *sc) +{ +#define N(a) (sizeof(a) / sizeof((a)[0])) + int i; + uint16_t data; + uint32_t data32; + usbd_status error; + + error = urtw_8180_set_anaparam(sc, URTW_8225_ANAPARAM_ON); + if (error) + goto fail; + + error = urtw_8225_usb_init(sc); + if (error) + goto fail; + + urtw_write32_m(sc, URTW_RF_TIMING, 0x000a8008); + urtw_read16_m(sc, URTW_BRSR, &data); /* XXX ??? */ + urtw_write16_m(sc, URTW_BRSR, 0xffff); + urtw_write32_m(sc, URTW_RF_PARA, 0x100044); + + error = urtw_set_mode(sc, URTW_EPROM_CMD_CONFIG); + if (error) + goto fail; + urtw_write8_m(sc, URTW_CONFIG3, 0x44); + error = urtw_set_mode(sc, URTW_EPROM_CMD_NORMAL); + if (error) + goto fail; + + error = urtw_8185_rf_pins_enable(sc); + if (error) + goto fail; + + usbd_delay_ms(sc->sc_udev, 1000); + + for (i = 0; i < N(urtw_8225v2_rf_part1); i++) { + urtw_8225_write(sc, urtw_8225v2_rf_part1[i].reg, + urtw_8225v2_rf_part1[i].val); + usbd_delay_ms(sc->sc_udev, 1); + } + usbd_delay_ms(sc->sc_udev, 100); + + urtw_8225_write(sc, 0x0, 0x1b7); + + for (i = 0; i < 95; i++) { + urtw_8225_write(sc, 0x1, (uint8_t)(i + 1)); + usbd_delay_ms(sc->sc_udev, 1); + urtw_8225_write(sc, 0x2, urtw_8225v2_rxgain[i]); + usbd_delay_ms(sc->sc_udev, 1); + } + + urtw_8225_write(sc, 0x3, 0x2); + usbd_delay_ms(sc->sc_udev, 1); + urtw_8225_write(sc, 0x5, 0x4); + usbd_delay_ms(sc->sc_udev, 1); + urtw_8225_write(sc, 0x0, 0xb7); + usbd_delay_ms(sc->sc_udev, 1); + 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); + + 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, 200); + + urtw_8225_write(sc, 0x0, 0x2bf); + for (i = 0; i < 128; i++) { + urtw_8187_write_phy_ofdm(sc, 0xb, urtw_8225_agc[i]); + usbd_delay_ms(sc->sc_udev, 1); + urtw_8187_write_phy_ofdm(sc, 0xa, (uint8_t)i + 0x80); + usbd_delay_ms(sc->sc_udev, 1); + } + usbd_delay_ms(sc->sc_udev, 1); + + for (i = 0; i < N(urtw_8225v2_rf_part2); i++) { + urtw_8187_write_phy_ofdm(sc, urtw_8225v2_rf_part2[i].reg, + urtw_8225v2_rf_part2[i].val); + usbd_delay_ms(sc->sc_udev, 1); + } + + error = urtw_8225v2_setgain(sc, 4); + if (error) + goto fail; + + for (i = 0; i < N(urtw_8225v2_rf_part3); i++) { + urtw_8187_write_phy_cck(sc, urtw_8225v2_rf_part3[i].reg, + urtw_8225v2_rf_part3[i].val); + usbd_delay_ms(sc->sc_udev, 1); + } + + 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); + 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(sc, 1); +fail: + return (error); +#undef N +} + +usbd_status +urtw_8225v2_rf_set_chan(struct urtw_softc *sc, int chan) +{ + 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, 0x9); + else + urtw_write8_m(sc, URTW_SLOT, 0x14); + + if (IEEE80211_IS_CHAN_G(c)) { + urtw_write8_m(sc, URTW_DIFS, 0x14); + urtw_write8_m(sc, URTW_EIFS, 0x5b - 0x14); + urtw_write8_m(sc, URTW_CW_VAL, 0x73); + } else { + urtw_write8_m(sc, URTW_DIFS, 0x24); + urtw_write8_m(sc, URTW_EIFS, 0x5b - 0x24); + urtw_write8_m(sc, URTW_CW_VAL, 0xa5); + } + +fail: + return (error); +} + +void +urtw_set_chan(struct urtw_softc *sc, struct ieee80211_channel *c) +{ + 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 th channel we need to temporarily be 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 = sc->sc_rf_set_chan(sc, 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 (ic->ic_state == IEEE80211_S_SCAN) + ieee80211_next_scan(ifp); +} + +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; + + 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); + timeout_add(&sc->scan_to, hz / 5); + 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. */ + urtw_write32_m(sc, URTW_BSSID, ((uint32_t *)ni->ni_bssid)[0]); + urtw_write16_m(sc, URTW_BSSID + 4, + ((uint16_t *)ni->ni_bssid)[2]); + urtw_update_msr(sc); + /* XXX maybe the below would be incorrect. */ + urtw_write16_m(sc, URTW_ATIM_WND, 2); + urtw_write16_m(sc, URTW_ATIM_TR_ITV, 100); + urtw_write16_m(sc, URTW_BEACON_INTERVAL, 0x64); + urtw_write16_m(sc, URTW_BEACON_INTERVAL_TIME, 100); + error = urtw_led_ctl(sc, URTW_LED_CTL_LINK); + if (error != 0) + 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)); +} |