e50d35e6c6
queue length. The default value for this parameter is 50, which is quite low for many of today's uses and the only way to modify this parameter right now is to edit if_var.h file. Also add read-only sysctl with the same name, so that it's possible to retrieve the current value. MFC after: 1 month
4444 lines
119 KiB
C
4444 lines
119 KiB
C
/*-
|
|
* Copyright (c) 2008 Weongyo Jeong <weongyo@FreeBSD.org>
|
|
*
|
|
* 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 <sys/cdefs.h>
|
|
__FBSDID("$FreeBSD$");
|
|
#include <sys/param.h>
|
|
#include <sys/sockio.h>
|
|
#include <sys/sysctl.h>
|
|
#include <sys/lock.h>
|
|
#include <sys/mutex.h>
|
|
#include <sys/mbuf.h>
|
|
#include <sys/kernel.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/systm.h>
|
|
#include <sys/malloc.h>
|
|
#include <sys/module.h>
|
|
#include <sys/bus.h>
|
|
#include <sys/endian.h>
|
|
#include <sys/kdb.h>
|
|
|
|
#include <machine/bus.h>
|
|
#include <machine/resource.h>
|
|
#include <sys/rman.h>
|
|
|
|
#include <net/if.h>
|
|
#include <net/if_arp.h>
|
|
#include <net/ethernet.h>
|
|
#include <net/if_dl.h>
|
|
#include <net/if_media.h>
|
|
#include <net/if_types.h>
|
|
|
|
#ifdef INET
|
|
#include <netinet/in.h>
|
|
#include <netinet/in_systm.h>
|
|
#include <netinet/in_var.h>
|
|
#include <netinet/if_ether.h>
|
|
#include <netinet/ip.h>
|
|
#endif
|
|
|
|
#include <net80211/ieee80211_var.h>
|
|
#include <net80211/ieee80211_regdomain.h>
|
|
#include <net80211/ieee80211_radiotap.h>
|
|
|
|
#include <dev/usb/usb.h>
|
|
#include <dev/usb/usbdi.h>
|
|
#include "usbdevs.h"
|
|
|
|
#include <dev/usb/wlan/if_urtwreg.h>
|
|
#include <dev/usb/wlan/if_urtwvar.h>
|
|
|
|
SYSCTL_NODE(_hw_usb, OID_AUTO, urtw, CTLFLAG_RW, 0, "USB Realtek 8187L");
|
|
#ifdef URTW_DEBUG
|
|
int urtw_debug = 0;
|
|
SYSCTL_INT(_hw_usb_urtw, OID_AUTO, debug, CTLFLAG_RW, &urtw_debug, 0,
|
|
"control debugging printfs");
|
|
TUNABLE_INT("hw.usb.urtw.debug", &urtw_debug);
|
|
enum {
|
|
URTW_DEBUG_XMIT = 0x00000001, /* basic xmit operation */
|
|
URTW_DEBUG_RECV = 0x00000002, /* basic recv operation */
|
|
URTW_DEBUG_RESET = 0x00000004, /* reset processing */
|
|
URTW_DEBUG_TX_PROC = 0x00000008, /* tx ISR proc */
|
|
URTW_DEBUG_RX_PROC = 0x00000010, /* rx ISR proc */
|
|
URTW_DEBUG_STATE = 0x00000020, /* 802.11 state transitions */
|
|
URTW_DEBUG_STAT = 0x00000040, /* statistic */
|
|
URTW_DEBUG_INIT = 0x00000080, /* initialization of dev */
|
|
URTW_DEBUG_TXSTATUS = 0x00000100, /* tx status */
|
|
URTW_DEBUG_ANY = 0xffffffff
|
|
};
|
|
#define DPRINTF(sc, m, fmt, ...) do { \
|
|
if (sc->sc_debug & (m)) \
|
|
printf(fmt, __VA_ARGS__); \
|
|
} while (0)
|
|
#else
|
|
#define DPRINTF(sc, m, fmt, ...) do { \
|
|
(void) sc; \
|
|
} while (0)
|
|
#endif
|
|
static int urtw_preamble_mode = URTW_PREAMBLE_MODE_LONG;
|
|
SYSCTL_INT(_hw_usb_urtw, OID_AUTO, preamble_mode, CTLFLAG_RW,
|
|
&urtw_preamble_mode, 0, "set the preable mode (long or short)");
|
|
TUNABLE_INT("hw.usb.urtw.preamble_mode", &urtw_preamble_mode);
|
|
|
|
/* recognized device vendors/products */
|
|
#define urtw_lookup(v, p) \
|
|
((const struct urtw_type *)usb_lookup(urtw_devs, v, p))
|
|
#define URTW_DEV_B(v,p) \
|
|
{ USB_VPI(USB_VENDOR_##v, USB_PRODUCT_##v##_##p, URTW_REV_RTL8187B) }
|
|
#define URTW_DEV_L(v,p) \
|
|
{ USB_VPI(USB_VENDOR_##v, USB_PRODUCT_##v##_##p, URTW_REV_RTL8187L) }
|
|
#define URTW_REV_RTL8187B 0
|
|
#define URTW_REV_RTL8187L 1
|
|
static const struct usb_device_id urtw_devs[] = {
|
|
URTW_DEV_B(NETGEAR, WG111V3),
|
|
URTW_DEV_B(REALTEK, RTL8187B_0),
|
|
URTW_DEV_B(REALTEK, RTL8187B_1),
|
|
URTW_DEV_B(REALTEK, RTL8187B_2),
|
|
URTW_DEV_B(SITECOMEU, WL168V4),
|
|
URTW_DEV_L(ASUS, P5B_WIFI),
|
|
URTW_DEV_L(BELKIN, F5D7050E),
|
|
URTW_DEV_L(LINKSYS4, WUSB54GCV2),
|
|
URTW_DEV_L(NETGEAR, WG111V2),
|
|
URTW_DEV_L(REALTEK, RTL8187),
|
|
URTW_DEV_L(SITECOMEU, WL168V1),
|
|
URTW_DEV_L(SURECOM, EP9001G2A),
|
|
{ USB_VPI(0x1b75, 0x8187, URTW_REV_RTL8187L) },
|
|
{ USB_VPI(USB_VENDOR_DICKSMITH, 0x9401, URTW_REV_RTL8187L) },
|
|
{ USB_VPI(USB_VENDOR_HP, 0xca02, URTW_REV_RTL8187L) },
|
|
{ USB_VPI(USB_VENDOR_LOGITEC, 0x010c, URTW_REV_RTL8187L) },
|
|
{ USB_VPI(USB_VENDOR_NETGEAR, 0x6100, URTW_REV_RTL8187L) },
|
|
{ USB_VPI(USB_VENDOR_SPHAIRON, 0x0150, URTW_REV_RTL8187L) },
|
|
{ USB_VPI(USB_VENDOR_QCOM, 0x6232, URTW_REV_RTL8187L) },
|
|
#undef URTW_DEV_L
|
|
#undef URTW_DEV_B
|
|
};
|
|
|
|
#define urtw_read8_m(sc, val, data) do { \
|
|
error = urtw_read8_c(sc, val, data); \
|
|
if (error != 0) \
|
|
goto fail; \
|
|
} while (0)
|
|
#define urtw_write8_m(sc, val, data) do { \
|
|
error = urtw_write8_c(sc, val, data); \
|
|
if (error != 0) \
|
|
goto fail; \
|
|
} while (0)
|
|
#define urtw_read16_m(sc, val, data) do { \
|
|
error = urtw_read16_c(sc, val, data); \
|
|
if (error != 0) \
|
|
goto fail; \
|
|
} while (0)
|
|
#define urtw_write16_m(sc, val, data) do { \
|
|
error = urtw_write16_c(sc, val, data); \
|
|
if (error != 0) \
|
|
goto fail; \
|
|
} while (0)
|
|
#define urtw_read32_m(sc, val, data) do { \
|
|
error = urtw_read32_c(sc, val, data); \
|
|
if (error != 0) \
|
|
goto fail; \
|
|
} while (0)
|
|
#define urtw_write32_m(sc, val, data) do { \
|
|
error = urtw_write32_c(sc, val, data); \
|
|
if (error != 0) \
|
|
goto fail; \
|
|
} while (0)
|
|
#define urtw_8187_write_phy_ofdm(sc, val, data) do { \
|
|
error = urtw_8187_write_phy_ofdm_c(sc, val, data); \
|
|
if (error != 0) \
|
|
goto fail; \
|
|
} while (0)
|
|
#define urtw_8187_write_phy_cck(sc, val, data) do { \
|
|
error = urtw_8187_write_phy_cck_c(sc, val, data); \
|
|
if (error != 0) \
|
|
goto fail; \
|
|
} while (0)
|
|
#define urtw_8225_write(sc, val, data) do { \
|
|
error = urtw_8225_write_c(sc, val, data); \
|
|
if (error != 0) \
|
|
goto fail; \
|
|
} while (0)
|
|
|
|
struct urtw_pair {
|
|
uint32_t reg;
|
|
uint32_t val;
|
|
};
|
|
|
|
static uint8_t urtw_8225_agc[] = {
|
|
0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9e, 0x9d, 0x9c, 0x9b,
|
|
0x9a, 0x99, 0x98, 0x97, 0x96, 0x95, 0x94, 0x93, 0x92, 0x91, 0x90,
|
|
0x8f, 0x8e, 0x8d, 0x8c, 0x8b, 0x8a, 0x89, 0x88, 0x87, 0x86, 0x85,
|
|
0x84, 0x83, 0x82, 0x81, 0x80, 0x3f, 0x3e, 0x3d, 0x3c, 0x3b, 0x3a,
|
|
0x39, 0x38, 0x37, 0x36, 0x35, 0x34, 0x33, 0x32, 0x31, 0x30, 0x2f,
|
|
0x2e, 0x2d, 0x2c, 0x2b, 0x2a, 0x29, 0x28, 0x27, 0x26, 0x25, 0x24,
|
|
0x23, 0x22, 0x21, 0x20, 0x1f, 0x1e, 0x1d, 0x1c, 0x1b, 0x1a, 0x19,
|
|
0x18, 0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x0f, 0x0e,
|
|
0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03,
|
|
0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
|
|
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
|
|
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01
|
|
};
|
|
|
|
static uint8_t urtw_8225z2_agc[] = {
|
|
0x5e, 0x5e, 0x5e, 0x5e, 0x5d, 0x5b, 0x59, 0x57, 0x55, 0x53, 0x51,
|
|
0x4f, 0x4d, 0x4b, 0x49, 0x47, 0x45, 0x43, 0x41, 0x3f, 0x3d, 0x3b,
|
|
0x39, 0x37, 0x35, 0x33, 0x31, 0x2f, 0x2d, 0x2b, 0x29, 0x27, 0x25,
|
|
0x23, 0x21, 0x1f, 0x1d, 0x1b, 0x19, 0x17, 0x15, 0x13, 0x11, 0x0f,
|
|
0x0d, 0x0b, 0x09, 0x07, 0x05, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01,
|
|
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x19, 0x19,
|
|
0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x19, 0x20, 0x21, 0x22, 0x23,
|
|
0x24, 0x25, 0x26, 0x26, 0x27, 0x27, 0x28, 0x28, 0x29, 0x2a, 0x2a,
|
|
0x2a, 0x2b, 0x2b, 0x2b, 0x2c, 0x2c, 0x2c, 0x2d, 0x2d, 0x2d, 0x2d,
|
|
0x2e, 0x2e, 0x2e, 0x2e, 0x2f, 0x2f, 0x2f, 0x30, 0x30, 0x31, 0x31,
|
|
0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31,
|
|
0x31, 0x31, 0x31, 0x31, 0x31, 0x31, 0x31
|
|
};
|
|
|
|
static uint32_t urtw_8225_channel[] = {
|
|
0x0000, /* dummy channel 0 */
|
|
0x085c, /* 1 */
|
|
0x08dc, /* 2 */
|
|
0x095c, /* 3 */
|
|
0x09dc, /* 4 */
|
|
0x0a5c, /* 5 */
|
|
0x0adc, /* 6 */
|
|
0x0b5c, /* 7 */
|
|
0x0bdc, /* 8 */
|
|
0x0c5c, /* 9 */
|
|
0x0cdc, /* 10 */
|
|
0x0d5c, /* 11 */
|
|
0x0ddc, /* 12 */
|
|
0x0e5c, /* 13 */
|
|
0x0f72, /* 14 */
|
|
};
|
|
|
|
static uint8_t urtw_8225_gain[] = {
|
|
0x23, 0x88, 0x7c, 0xa5, /* -82dbm */
|
|
0x23, 0x88, 0x7c, 0xb5, /* -82dbm */
|
|
0x23, 0x88, 0x7c, 0xc5, /* -82dbm */
|
|
0x33, 0x80, 0x79, 0xc5, /* -78dbm */
|
|
0x43, 0x78, 0x76, 0xc5, /* -74dbm */
|
|
0x53, 0x60, 0x73, 0xc5, /* -70dbm */
|
|
0x63, 0x58, 0x70, 0xc5, /* -66dbm */
|
|
};
|
|
|
|
static struct urtw_pair urtw_8225_rf_part1[] = {
|
|
{ 0x00, 0x0067 }, { 0x01, 0x0fe0 }, { 0x02, 0x044d }, { 0x03, 0x0441 },
|
|
{ 0x04, 0x0486 }, { 0x05, 0x0bc0 }, { 0x06, 0x0ae6 }, { 0x07, 0x082a },
|
|
{ 0x08, 0x001f }, { 0x09, 0x0334 }, { 0x0a, 0x0fd4 }, { 0x0b, 0x0391 },
|
|
{ 0x0c, 0x0050 }, { 0x0d, 0x06db }, { 0x0e, 0x0029 }, { 0x0f, 0x0914 },
|
|
};
|
|
|
|
static struct urtw_pair urtw_8225_rf_part2[] = {
|
|
{ 0x00, 0x01 }, { 0x01, 0x02 }, { 0x02, 0x42 }, { 0x03, 0x00 },
|
|
{ 0x04, 0x00 }, { 0x05, 0x00 }, { 0x06, 0x40 }, { 0x07, 0x00 },
|
|
{ 0x08, 0x40 }, { 0x09, 0xfe }, { 0x0a, 0x09 }, { 0x0b, 0x80 },
|
|
{ 0x0c, 0x01 }, { 0x0e, 0xd3 }, { 0x0f, 0x38 }, { 0x10, 0x84 },
|
|
{ 0x11, 0x06 }, { 0x12, 0x20 }, { 0x13, 0x20 }, { 0x14, 0x00 },
|
|
{ 0x15, 0x40 }, { 0x16, 0x00 }, { 0x17, 0x40 }, { 0x18, 0xef },
|
|
{ 0x19, 0x19 }, { 0x1a, 0x20 }, { 0x1b, 0x76 }, { 0x1c, 0x04 },
|
|
{ 0x1e, 0x95 }, { 0x1f, 0x75 }, { 0x20, 0x1f }, { 0x21, 0x27 },
|
|
{ 0x22, 0x16 }, { 0x24, 0x46 }, { 0x25, 0x20 }, { 0x26, 0x90 },
|
|
{ 0x27, 0x88 }
|
|
};
|
|
|
|
static struct urtw_pair urtw_8225_rf_part3[] = {
|
|
{ 0x00, 0x98 }, { 0x03, 0x20 }, { 0x04, 0x7e }, { 0x05, 0x12 },
|
|
{ 0x06, 0xfc }, { 0x07, 0x78 }, { 0x08, 0x2e }, { 0x10, 0x9b },
|
|
{ 0x11, 0x88 }, { 0x12, 0x47 }, { 0x13, 0xd0 }, { 0x19, 0x00 },
|
|
{ 0x1a, 0xa0 }, { 0x1b, 0x08 }, { 0x40, 0x86 }, { 0x41, 0x8d },
|
|
{ 0x42, 0x15 }, { 0x43, 0x18 }, { 0x44, 0x1f }, { 0x45, 0x1e },
|
|
{ 0x46, 0x1a }, { 0x47, 0x15 }, { 0x48, 0x10 }, { 0x49, 0x0a },
|
|
{ 0x4a, 0x05 }, { 0x4b, 0x02 }, { 0x4c, 0x05 }
|
|
};
|
|
|
|
static uint16_t urtw_8225_rxgain[] = {
|
|
0x0400, 0x0401, 0x0402, 0x0403, 0x0404, 0x0405, 0x0408, 0x0409,
|
|
0x040a, 0x040b, 0x0502, 0x0503, 0x0504, 0x0505, 0x0540, 0x0541,
|
|
0x0542, 0x0543, 0x0544, 0x0545, 0x0580, 0x0581, 0x0582, 0x0583,
|
|
0x0584, 0x0585, 0x0588, 0x0589, 0x058a, 0x058b, 0x0643, 0x0644,
|
|
0x0645, 0x0680, 0x0681, 0x0682, 0x0683, 0x0684, 0x0685, 0x0688,
|
|
0x0689, 0x068a, 0x068b, 0x068c, 0x0742, 0x0743, 0x0744, 0x0745,
|
|
0x0780, 0x0781, 0x0782, 0x0783, 0x0784, 0x0785, 0x0788, 0x0789,
|
|
0x078a, 0x078b, 0x078c, 0x078d, 0x0790, 0x0791, 0x0792, 0x0793,
|
|
0x0794, 0x0795, 0x0798, 0x0799, 0x079a, 0x079b, 0x079c, 0x079d,
|
|
0x07a0, 0x07a1, 0x07a2, 0x07a3, 0x07a4, 0x07a5, 0x07a8, 0x07a9,
|
|
0x07aa, 0x07ab, 0x07ac, 0x07ad, 0x07b0, 0x07b1, 0x07b2, 0x07b3,
|
|
0x07b4, 0x07b5, 0x07b8, 0x07b9, 0x07ba, 0x07bb, 0x07bb
|
|
};
|
|
|
|
static uint8_t urtw_8225_threshold[] = {
|
|
0x8d, 0x8d, 0x8d, 0x8d, 0x9d, 0xad, 0xbd,
|
|
};
|
|
|
|
static uint8_t urtw_8225_tx_gain_cck_ofdm[] = {
|
|
0x02, 0x06, 0x0e, 0x1e, 0x3e, 0x7e
|
|
};
|
|
|
|
static uint8_t urtw_8225_txpwr_cck[] = {
|
|
0x18, 0x17, 0x15, 0x11, 0x0c, 0x08, 0x04, 0x02,
|
|
0x1b, 0x1a, 0x17, 0x13, 0x0e, 0x09, 0x04, 0x02,
|
|
0x1f, 0x1e, 0x1a, 0x15, 0x10, 0x0a, 0x05, 0x02,
|
|
0x22, 0x21, 0x1d, 0x18, 0x11, 0x0b, 0x06, 0x02,
|
|
0x26, 0x25, 0x21, 0x1b, 0x14, 0x0d, 0x06, 0x03,
|
|
0x2b, 0x2a, 0x25, 0x1e, 0x16, 0x0e, 0x07, 0x03
|
|
};
|
|
|
|
static uint8_t urtw_8225_txpwr_cck_ch14[] = {
|
|
0x18, 0x17, 0x15, 0x0c, 0x00, 0x00, 0x00, 0x00,
|
|
0x1b, 0x1a, 0x17, 0x0e, 0x00, 0x00, 0x00, 0x00,
|
|
0x1f, 0x1e, 0x1a, 0x0f, 0x00, 0x00, 0x00, 0x00,
|
|
0x22, 0x21, 0x1d, 0x11, 0x00, 0x00, 0x00, 0x00,
|
|
0x26, 0x25, 0x21, 0x13, 0x00, 0x00, 0x00, 0x00,
|
|
0x2b, 0x2a, 0x25, 0x15, 0x00, 0x00, 0x00, 0x00
|
|
};
|
|
|
|
static uint8_t urtw_8225_txpwr_ofdm[]={
|
|
0x80, 0x90, 0xa2, 0xb5, 0xcb, 0xe4
|
|
};
|
|
|
|
static uint8_t urtw_8225v2_gain_bg[]={
|
|
0x23, 0x15, 0xa5, /* -82-1dbm */
|
|
0x23, 0x15, 0xb5, /* -82-2dbm */
|
|
0x23, 0x15, 0xc5, /* -82-3dbm */
|
|
0x33, 0x15, 0xc5, /* -78dbm */
|
|
0x43, 0x15, 0xc5, /* -74dbm */
|
|
0x53, 0x15, 0xc5, /* -70dbm */
|
|
0x63, 0x15, 0xc5, /* -66dbm */
|
|
};
|
|
|
|
static struct urtw_pair urtw_8225v2_rf_part1[] = {
|
|
{ 0x00, 0x02bf }, { 0x01, 0x0ee0 }, { 0x02, 0x044d }, { 0x03, 0x0441 },
|
|
{ 0x04, 0x08c3 }, { 0x05, 0x0c72 }, { 0x06, 0x00e6 }, { 0x07, 0x082a },
|
|
{ 0x08, 0x003f }, { 0x09, 0x0335 }, { 0x0a, 0x09d4 }, { 0x0b, 0x07bb },
|
|
{ 0x0c, 0x0850 }, { 0x0d, 0x0cdf }, { 0x0e, 0x002b }, { 0x0f, 0x0114 }
|
|
};
|
|
|
|
static struct urtw_pair urtw_8225v2b_rf_part0[] = {
|
|
{ 0x00, 0x00b7 }, { 0x01, 0x0ee0 }, { 0x02, 0x044d }, { 0x03, 0x0441 },
|
|
{ 0x04, 0x08c3 }, { 0x05, 0x0c72 }, { 0x06, 0x00e6 }, { 0x07, 0x082a },
|
|
{ 0x08, 0x003f }, { 0x09, 0x0335 }, { 0x0a, 0x09d4 }, { 0x0b, 0x07bb },
|
|
{ 0x0c, 0x0850 }, { 0x0d, 0x0cdf }, { 0x0e, 0x002b }, { 0x0f, 0x0114 }
|
|
};
|
|
|
|
static struct urtw_pair urtw_8225v2b_rf_part1[] = {
|
|
{0x0f0, 0x32}, {0x0f1, 0x32}, {0x0f2, 0x00},
|
|
{0x0f3, 0x00}, {0x0f4, 0x32}, {0x0f5, 0x43},
|
|
{0x0f6, 0x00}, {0x0f7, 0x00}, {0x0f8, 0x46},
|
|
{0x0f9, 0xa4}, {0x0fa, 0x00}, {0x0fb, 0x00},
|
|
{0x0fc, 0x96}, {0x0fd, 0xa4}, {0x0fe, 0x00},
|
|
{0x0ff, 0x00}, {0x158, 0x4b}, {0x159, 0x00},
|
|
{0x15a, 0x4b}, {0x15b, 0x00}, {0x160, 0x4b},
|
|
{0x161, 0x09}, {0x162, 0x4b}, {0x163, 0x09},
|
|
{0x1ce, 0x0f}, {0x1cf, 0x00}, {0x1e0, 0xff},
|
|
{0x1e1, 0x0f}, {0x1e2, 0x00}, {0x1f0, 0x4e},
|
|
{0x1f1, 0x01}, {0x1f2, 0x02}, {0x1f3, 0x03},
|
|
{0x1f4, 0x04}, {0x1f5, 0x05}, {0x1f6, 0x06},
|
|
{0x1f7, 0x07}, {0x1f8, 0x08}, {0x24e, 0x00},
|
|
{0x20c, 0x04}, {0x221, 0x61}, {0x222, 0x68},
|
|
{0x223, 0x6f}, {0x224, 0x76}, {0x225, 0x7d},
|
|
{0x226, 0x84}, {0x227, 0x8d}, {0x24d, 0x08},
|
|
{0x250, 0x05}, {0x251, 0xf5}, {0x252, 0x04},
|
|
{0x253, 0xa0}, {0x254, 0x1f}, {0x255, 0x23},
|
|
{0x256, 0x45}, {0x257, 0x67}, {0x258, 0x08},
|
|
{0x259, 0x08}, {0x25a, 0x08}, {0x25b, 0x08},
|
|
{0x260, 0x08}, {0x261, 0x08}, {0x262, 0x08},
|
|
{0x263, 0x08}, {0x264, 0xcf}, {0x272, 0x56},
|
|
{0x273, 0x9a}, {0x034, 0xf0}, {0x035, 0x0f},
|
|
{0x05b, 0x40}, {0x084, 0x88}, {0x085, 0x24},
|
|
{0x088, 0x54}, {0x08b, 0xb8}, {0x08c, 0x07},
|
|
{0x08d, 0x00}, {0x094, 0x1b}, {0x095, 0x12},
|
|
{0x096, 0x00}, {0x097, 0x06}, {0x09d, 0x1a},
|
|
{0x09f, 0x10}, {0x0b4, 0x22}, {0x0be, 0x80},
|
|
{0x0db, 0x00}, {0x0ee, 0x00}, {0x091, 0x03},
|
|
{0x24c, 0x00}, {0x39f, 0x00}, {0x08c, 0x01},
|
|
{0x08d, 0x10}, {0x08e, 0x08}, {0x08f, 0x00}
|
|
};
|
|
|
|
static struct urtw_pair urtw_8225v2_rf_part2[] = {
|
|
{ 0x00, 0x01 }, { 0x01, 0x02 }, { 0x02, 0x42 }, { 0x03, 0x00 },
|
|
{ 0x04, 0x00 }, { 0x05, 0x00 }, { 0x06, 0x40 }, { 0x07, 0x00 },
|
|
{ 0x08, 0x40 }, { 0x09, 0xfe }, { 0x0a, 0x08 }, { 0x0b, 0x80 },
|
|
{ 0x0c, 0x01 }, { 0x0d, 0x43 }, { 0x0e, 0xd3 }, { 0x0f, 0x38 },
|
|
{ 0x10, 0x84 }, { 0x11, 0x07 }, { 0x12, 0x20 }, { 0x13, 0x20 },
|
|
{ 0x14, 0x00 }, { 0x15, 0x40 }, { 0x16, 0x00 }, { 0x17, 0x40 },
|
|
{ 0x18, 0xef }, { 0x19, 0x19 }, { 0x1a, 0x20 }, { 0x1b, 0x15 },
|
|
{ 0x1c, 0x04 }, { 0x1d, 0xc5 }, { 0x1e, 0x95 }, { 0x1f, 0x75 },
|
|
{ 0x20, 0x1f }, { 0x21, 0x17 }, { 0x22, 0x16 }, { 0x23, 0x80 },
|
|
{ 0x24, 0x46 }, { 0x25, 0x00 }, { 0x26, 0x90 }, { 0x27, 0x88 }
|
|
};
|
|
|
|
static struct urtw_pair urtw_8225v2b_rf_part2[] = {
|
|
{ 0x00, 0x10 }, { 0x01, 0x0d }, { 0x02, 0x01 }, { 0x03, 0x00 },
|
|
{ 0x04, 0x14 }, { 0x05, 0xfb }, { 0x06, 0xfb }, { 0x07, 0x60 },
|
|
{ 0x08, 0x00 }, { 0x09, 0x60 }, { 0x0a, 0x00 }, { 0x0b, 0x00 },
|
|
{ 0x0c, 0x00 }, { 0x0d, 0x5c }, { 0x0e, 0x00 }, { 0x0f, 0x00 },
|
|
{ 0x10, 0x40 }, { 0x11, 0x00 }, { 0x12, 0x40 }, { 0x13, 0x00 },
|
|
{ 0x14, 0x00 }, { 0x15, 0x00 }, { 0x16, 0xa8 }, { 0x17, 0x26 },
|
|
{ 0x18, 0x32 }, { 0x19, 0x33 }, { 0x1a, 0x07 }, { 0x1b, 0xa5 },
|
|
{ 0x1c, 0x6f }, { 0x1d, 0x55 }, { 0x1e, 0xc8 }, { 0x1f, 0xb3 },
|
|
{ 0x20, 0x0a }, { 0x21, 0xe1 }, { 0x22, 0x2C }, { 0x23, 0x8a },
|
|
{ 0x24, 0x86 }, { 0x25, 0x83 }, { 0x26, 0x34 }, { 0x27, 0x0f },
|
|
{ 0x28, 0x4f }, { 0x29, 0x24 }, { 0x2a, 0x6f }, { 0x2b, 0xc2 },
|
|
{ 0x2c, 0x6b }, { 0x2d, 0x40 }, { 0x2e, 0x80 }, { 0x2f, 0x00 },
|
|
{ 0x30, 0xc0 }, { 0x31, 0xc1 }, { 0x32, 0x58 }, { 0x33, 0xf1 },
|
|
{ 0x34, 0x00 }, { 0x35, 0xe4 }, { 0x36, 0x90 }, { 0x37, 0x3e },
|
|
{ 0x38, 0x6d }, { 0x39, 0x3c }, { 0x3a, 0xfb }, { 0x3b, 0x07 }
|
|
};
|
|
|
|
static struct urtw_pair urtw_8225v2_rf_part3[] = {
|
|
{ 0x00, 0x98 }, { 0x03, 0x20 }, { 0x04, 0x7e }, { 0x05, 0x12 },
|
|
{ 0x06, 0xfc }, { 0x07, 0x78 }, { 0x08, 0x2e }, { 0x09, 0x11 },
|
|
{ 0x0a, 0x17 }, { 0x0b, 0x11 }, { 0x10, 0x9b }, { 0x11, 0x88 },
|
|
{ 0x12, 0x47 }, { 0x13, 0xd0 }, { 0x19, 0x00 }, { 0x1a, 0xa0 },
|
|
{ 0x1b, 0x08 }, { 0x1d, 0x00 }, { 0x40, 0x86 }, { 0x41, 0x9d },
|
|
{ 0x42, 0x15 }, { 0x43, 0x18 }, { 0x44, 0x36 }, { 0x45, 0x35 },
|
|
{ 0x46, 0x2e }, { 0x47, 0x25 }, { 0x48, 0x1c }, { 0x49, 0x12 },
|
|
{ 0x4a, 0x09 }, { 0x4b, 0x04 }, { 0x4c, 0x05 }
|
|
};
|
|
|
|
static uint16_t urtw_8225v2_rxgain[] = {
|
|
0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0008, 0x0009,
|
|
0x000a, 0x000b, 0x0102, 0x0103, 0x0104, 0x0105, 0x0140, 0x0141,
|
|
0x0142, 0x0143, 0x0144, 0x0145, 0x0180, 0x0181, 0x0182, 0x0183,
|
|
0x0184, 0x0185, 0x0188, 0x0189, 0x018a, 0x018b, 0x0243, 0x0244,
|
|
0x0245, 0x0280, 0x0281, 0x0282, 0x0283, 0x0284, 0x0285, 0x0288,
|
|
0x0289, 0x028a, 0x028b, 0x028c, 0x0342, 0x0343, 0x0344, 0x0345,
|
|
0x0380, 0x0381, 0x0382, 0x0383, 0x0384, 0x0385, 0x0388, 0x0389,
|
|
0x038a, 0x038b, 0x038c, 0x038d, 0x0390, 0x0391, 0x0392, 0x0393,
|
|
0x0394, 0x0395, 0x0398, 0x0399, 0x039a, 0x039b, 0x039c, 0x039d,
|
|
0x03a0, 0x03a1, 0x03a2, 0x03a3, 0x03a4, 0x03a5, 0x03a8, 0x03a9,
|
|
0x03aa, 0x03ab, 0x03ac, 0x03ad, 0x03b0, 0x03b1, 0x03b2, 0x03b3,
|
|
0x03b4, 0x03b5, 0x03b8, 0x03b9, 0x03ba, 0x03bb, 0x03bb
|
|
};
|
|
|
|
static uint16_t urtw_8225v2b_rxgain[] = {
|
|
0x0400, 0x0401, 0x0402, 0x0403, 0x0404, 0x0405, 0x0408, 0x0409,
|
|
0x040a, 0x040b, 0x0502, 0x0503, 0x0504, 0x0505, 0x0540, 0x0541,
|
|
0x0542, 0x0543, 0x0544, 0x0545, 0x0580, 0x0581, 0x0582, 0x0583,
|
|
0x0584, 0x0585, 0x0588, 0x0589, 0x058a, 0x058b, 0x0643, 0x0644,
|
|
0x0645, 0x0680, 0x0681, 0x0682, 0x0683, 0x0684, 0x0685, 0x0688,
|
|
0x0689, 0x068a, 0x068b, 0x068c, 0x0742, 0x0743, 0x0744, 0x0745,
|
|
0x0780, 0x0781, 0x0782, 0x0783, 0x0784, 0x0785, 0x0788, 0x0789,
|
|
0x078a, 0x078b, 0x078c, 0x078d, 0x0790, 0x0791, 0x0792, 0x0793,
|
|
0x0794, 0x0795, 0x0798, 0x0799, 0x079a, 0x079b, 0x079c, 0x079d,
|
|
0x07a0, 0x07a1, 0x07a2, 0x07a3, 0x07a4, 0x07a5, 0x07a8, 0x07a9,
|
|
0x03aa, 0x03ab, 0x03ac, 0x03ad, 0x03b0, 0x03b1, 0x03b2, 0x03b3,
|
|
0x03b4, 0x03b5, 0x03b8, 0x03b9, 0x03ba, 0x03bb, 0x03bb
|
|
};
|
|
|
|
static uint8_t urtw_8225v2_tx_gain_cck_ofdm[] = {
|
|
0x00, 0x01, 0x02, 0x03, 0x04, 0x05,
|
|
0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b,
|
|
0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11,
|
|
0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
|
|
0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d,
|
|
0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23,
|
|
};
|
|
|
|
static uint8_t urtw_8225v2_txpwr_cck[] = {
|
|
0x36, 0x35, 0x2e, 0x25, 0x1c, 0x12, 0x09, 0x04
|
|
};
|
|
|
|
static uint8_t urtw_8225v2_txpwr_cck_ch14[] = {
|
|
0x36, 0x35, 0x2e, 0x1b, 0x00, 0x00, 0x00, 0x00
|
|
};
|
|
|
|
static uint8_t urtw_8225v2b_txpwr_cck[] = {
|
|
0x36, 0x35, 0x2e, 0x25, 0x1c, 0x12, 0x09, 0x04,
|
|
0x30, 0x2f, 0x29, 0x21, 0x19, 0x10, 0x08, 0x03,
|
|
0x2b, 0x2a, 0x25, 0x1e, 0x16, 0x0e, 0x07, 0x03,
|
|
0x26, 0x25, 0x21, 0x1b, 0x14, 0x0d, 0x06, 0x03
|
|
};
|
|
|
|
static uint8_t urtw_8225v2b_txpwr_cck_ch14[] = {
|
|
0x36, 0x35, 0x2e, 0x1b, 0x00, 0x00, 0x00, 0x00,
|
|
0x30, 0x2f, 0x29, 0x15, 0x00, 0x00, 0x00, 0x00,
|
|
0x30, 0x2f, 0x29, 0x15, 0x00, 0x00, 0x00, 0x00,
|
|
0x30, 0x2f, 0x29, 0x15, 0x00, 0x00, 0x00, 0x00
|
|
};
|
|
|
|
static struct urtw_pair urtw_ratetable[] = {
|
|
{ 2, 0 }, { 4, 1 }, { 11, 2 }, { 12, 4 }, { 18, 5 },
|
|
{ 22, 3 }, { 24, 6 }, { 36, 7 }, { 48, 8 }, { 72, 9 },
|
|
{ 96, 10 }, { 108, 11 }
|
|
};
|
|
|
|
static const uint8_t urtw_8187b_reg_table[][3] = {
|
|
{ 0xf0, 0x32, 0 }, { 0xf1, 0x32, 0 }, { 0xf2, 0x00, 0 },
|
|
{ 0xf3, 0x00, 0 }, { 0xf4, 0x32, 0 }, { 0xf5, 0x43, 0 },
|
|
{ 0xf6, 0x00, 0 }, { 0xf7, 0x00, 0 }, { 0xf8, 0x46, 0 },
|
|
{ 0xf9, 0xa4, 0 }, { 0xfa, 0x00, 0 }, { 0xfb, 0x00, 0 },
|
|
{ 0xfc, 0x96, 0 }, { 0xfd, 0xa4, 0 }, { 0xfe, 0x00, 0 },
|
|
{ 0xff, 0x00, 0 }, { 0x58, 0x4b, 1 }, { 0x59, 0x00, 1 },
|
|
{ 0x5a, 0x4b, 1 }, { 0x5b, 0x00, 1 }, { 0x60, 0x4b, 1 },
|
|
{ 0x61, 0x09, 1 }, { 0x62, 0x4b, 1 }, { 0x63, 0x09, 1 },
|
|
{ 0xce, 0x0f, 1 }, { 0xcf, 0x00, 1 }, { 0xe0, 0xff, 1 },
|
|
{ 0xe1, 0x0f, 1 }, { 0xe2, 0x00, 1 }, { 0xf0, 0x4e, 1 },
|
|
{ 0xf1, 0x01, 1 }, { 0xf2, 0x02, 1 }, { 0xf3, 0x03, 1 },
|
|
{ 0xf4, 0x04, 1 }, { 0xf5, 0x05, 1 }, { 0xf6, 0x06, 1 },
|
|
{ 0xf7, 0x07, 1 }, { 0xf8, 0x08, 1 }, { 0x4e, 0x00, 2 },
|
|
{ 0x0c, 0x04, 2 }, { 0x21, 0x61, 2 }, { 0x22, 0x68, 2 },
|
|
{ 0x23, 0x6f, 2 }, { 0x24, 0x76, 2 }, { 0x25, 0x7d, 2 },
|
|
{ 0x26, 0x84, 2 }, { 0x27, 0x8d, 2 }, { 0x4d, 0x08, 2 },
|
|
{ 0x50, 0x05, 2 }, { 0x51, 0xf5, 2 }, { 0x52, 0x04, 2 },
|
|
{ 0x53, 0xa0, 2 }, { 0x54, 0x1f, 2 }, { 0x55, 0x23, 2 },
|
|
{ 0x56, 0x45, 2 }, { 0x57, 0x67, 2 }, { 0x58, 0x08, 2 },
|
|
{ 0x59, 0x08, 2 }, { 0x5a, 0x08, 2 }, { 0x5b, 0x08, 2 },
|
|
{ 0x60, 0x08, 2 }, { 0x61, 0x08, 2 }, { 0x62, 0x08, 2 },
|
|
{ 0x63, 0x08, 2 }, { 0x64, 0xcf, 2 }, { 0x72, 0x56, 2 },
|
|
{ 0x73, 0x9a, 2 }, { 0x34, 0xf0, 0 }, { 0x35, 0x0f, 0 },
|
|
{ 0x5b, 0x40, 0 }, { 0x84, 0x88, 0 }, { 0x85, 0x24, 0 },
|
|
{ 0x88, 0x54, 0 }, { 0x8b, 0xb8, 0 }, { 0x8c, 0x07, 0 },
|
|
{ 0x8d, 0x00, 0 }, { 0x94, 0x1b, 0 }, { 0x95, 0x12, 0 },
|
|
{ 0x96, 0x00, 0 }, { 0x97, 0x06, 0 }, { 0x9d, 0x1a, 0 },
|
|
{ 0x9f, 0x10, 0 }, { 0xb4, 0x22, 0 }, { 0xbe, 0x80, 0 },
|
|
{ 0xdb, 0x00, 0 }, { 0xee, 0x00, 0 }, { 0x91, 0x03, 0 },
|
|
{ 0x4c, 0x00, 2 }, { 0x9f, 0x00, 3 }, { 0x8c, 0x01, 0 },
|
|
{ 0x8d, 0x10, 0 }, { 0x8e, 0x08, 0 }, { 0x8f, 0x00, 0 }
|
|
};
|
|
|
|
static usb_callback_t urtw_bulk_rx_callback;
|
|
static usb_callback_t urtw_bulk_tx_callback;
|
|
static usb_callback_t urtw_bulk_tx_status_callback;
|
|
|
|
static const struct usb_config urtw_8187b_usbconfig[URTW_8187B_N_XFERS] = {
|
|
[URTW_8187B_BULK_RX] = {
|
|
.type = UE_BULK,
|
|
.endpoint = 0x83,
|
|
.direction = UE_DIR_IN,
|
|
.bufsize = MCLBYTES,
|
|
.flags = {
|
|
.ext_buffer = 1,
|
|
.pipe_bof = 1,
|
|
.short_xfer_ok = 1
|
|
},
|
|
.callback = urtw_bulk_rx_callback
|
|
},
|
|
[URTW_8187B_BULK_TX_STATUS] = {
|
|
.type = UE_BULK,
|
|
.endpoint = 0x89,
|
|
.direction = UE_DIR_IN,
|
|
.bufsize = MCLBYTES,
|
|
.flags = {
|
|
.ext_buffer = 1,
|
|
.pipe_bof = 1,
|
|
.short_xfer_ok = 1
|
|
},
|
|
.callback = urtw_bulk_tx_status_callback
|
|
},
|
|
[URTW_8187B_BULK_TX_BE] = {
|
|
.type = UE_BULK,
|
|
.endpoint = URTW_8187B_TXPIPE_BE,
|
|
.direction = UE_DIR_OUT,
|
|
.bufsize = URTW_TX_MAXSIZE,
|
|
.flags = {
|
|
.ext_buffer = 1,
|
|
.force_short_xfer = 1,
|
|
.pipe_bof = 1,
|
|
},
|
|
.callback = urtw_bulk_tx_callback,
|
|
.timeout = URTW_DATA_TIMEOUT
|
|
},
|
|
[URTW_8187B_BULK_TX_BK] = {
|
|
.type = UE_BULK,
|
|
.endpoint = URTW_8187B_TXPIPE_BK,
|
|
.direction = UE_DIR_OUT,
|
|
.bufsize = URTW_TX_MAXSIZE,
|
|
.flags = {
|
|
.ext_buffer = 1,
|
|
.force_short_xfer = 1,
|
|
.pipe_bof = 1,
|
|
},
|
|
.callback = urtw_bulk_tx_callback,
|
|
.timeout = URTW_DATA_TIMEOUT
|
|
},
|
|
[URTW_8187B_BULK_TX_VI] = {
|
|
.type = UE_BULK,
|
|
.endpoint = URTW_8187B_TXPIPE_VI,
|
|
.direction = UE_DIR_OUT,
|
|
.bufsize = URTW_TX_MAXSIZE,
|
|
.flags = {
|
|
.ext_buffer = 1,
|
|
.force_short_xfer = 1,
|
|
.pipe_bof = 1,
|
|
},
|
|
.callback = urtw_bulk_tx_callback,
|
|
.timeout = URTW_DATA_TIMEOUT
|
|
},
|
|
[URTW_8187B_BULK_TX_VO] = {
|
|
.type = UE_BULK,
|
|
.endpoint = URTW_8187B_TXPIPE_VO,
|
|
.direction = UE_DIR_OUT,
|
|
.bufsize = URTW_TX_MAXSIZE,
|
|
.flags = {
|
|
.ext_buffer = 1,
|
|
.force_short_xfer = 1,
|
|
.pipe_bof = 1,
|
|
},
|
|
.callback = urtw_bulk_tx_callback,
|
|
.timeout = URTW_DATA_TIMEOUT
|
|
},
|
|
[URTW_8187B_BULK_TX_EP12] = {
|
|
.type = UE_BULK,
|
|
.endpoint = 0xc,
|
|
.direction = UE_DIR_OUT,
|
|
.bufsize = URTW_TX_MAXSIZE,
|
|
.flags = {
|
|
.ext_buffer = 1,
|
|
.force_short_xfer = 1,
|
|
.pipe_bof = 1,
|
|
},
|
|
.callback = urtw_bulk_tx_callback,
|
|
.timeout = URTW_DATA_TIMEOUT
|
|
}
|
|
};
|
|
|
|
static const struct usb_config urtw_8187l_usbconfig[URTW_8187L_N_XFERS] = {
|
|
[URTW_8187L_BULK_RX] = {
|
|
.type = UE_BULK,
|
|
.endpoint = 0x81,
|
|
.direction = UE_DIR_IN,
|
|
.bufsize = MCLBYTES,
|
|
.flags = {
|
|
.ext_buffer = 1,
|
|
.pipe_bof = 1,
|
|
.short_xfer_ok = 1
|
|
},
|
|
.callback = urtw_bulk_rx_callback
|
|
},
|
|
[URTW_8187L_BULK_TX_LOW] = {
|
|
.type = UE_BULK,
|
|
.endpoint = 0x2,
|
|
.direction = UE_DIR_OUT,
|
|
.bufsize = URTW_TX_MAXSIZE,
|
|
.flags = {
|
|
.ext_buffer = 1,
|
|
.force_short_xfer = 1,
|
|
.pipe_bof = 1,
|
|
},
|
|
.callback = urtw_bulk_tx_callback,
|
|
.timeout = URTW_DATA_TIMEOUT
|
|
},
|
|
[URTW_8187L_BULK_TX_NORMAL] = {
|
|
.type = UE_BULK,
|
|
.endpoint = 0x3,
|
|
.direction = UE_DIR_OUT,
|
|
.bufsize = URTW_TX_MAXSIZE,
|
|
.flags = {
|
|
.ext_buffer = 1,
|
|
.force_short_xfer = 1,
|
|
.pipe_bof = 1,
|
|
},
|
|
.callback = urtw_bulk_tx_callback,
|
|
.timeout = URTW_DATA_TIMEOUT
|
|
},
|
|
};
|
|
|
|
static struct ieee80211vap *urtw_vap_create(struct ieee80211com *,
|
|
const char name[IFNAMSIZ], int unit, int opmode,
|
|
int flags, const uint8_t bssid[IEEE80211_ADDR_LEN],
|
|
const uint8_t mac[IEEE80211_ADDR_LEN]);
|
|
static void urtw_vap_delete(struct ieee80211vap *);
|
|
static void urtw_init(void *);
|
|
static void urtw_stop(struct ifnet *, int);
|
|
static void urtw_stop_locked(struct ifnet *, int);
|
|
static int urtw_ioctl(struct ifnet *, u_long, caddr_t);
|
|
static void urtw_start(struct ifnet *);
|
|
static int urtw_alloc_rx_data_list(struct urtw_softc *);
|
|
static int urtw_alloc_tx_data_list(struct urtw_softc *);
|
|
static int urtw_raw_xmit(struct ieee80211_node *, struct mbuf *,
|
|
const struct ieee80211_bpf_params *);
|
|
static void urtw_scan_start(struct ieee80211com *);
|
|
static void urtw_scan_end(struct ieee80211com *);
|
|
static void urtw_set_channel(struct ieee80211com *);
|
|
static void urtw_update_mcast(struct ifnet *);
|
|
static int urtw_tx_start(struct urtw_softc *,
|
|
struct ieee80211_node *, struct mbuf *,
|
|
struct urtw_data *, int);
|
|
static int urtw_newstate(struct ieee80211vap *,
|
|
enum ieee80211_state, int);
|
|
static void urtw_led_ch(void *);
|
|
static void urtw_ledtask(void *, int);
|
|
static void urtw_watchdog(void *);
|
|
static void urtw_set_multi(void *);
|
|
static int urtw_isbmode(uint16_t);
|
|
static uint16_t urtw_rate2rtl(int);
|
|
static uint16_t urtw_rtl2rate(int);
|
|
static usb_error_t urtw_set_rate(struct urtw_softc *);
|
|
static usb_error_t urtw_update_msr(struct urtw_softc *);
|
|
static usb_error_t urtw_read8_c(struct urtw_softc *, int, uint8_t *);
|
|
static usb_error_t urtw_read16_c(struct urtw_softc *, int, uint16_t *);
|
|
static usb_error_t urtw_read32_c(struct urtw_softc *, int, uint32_t *);
|
|
static usb_error_t urtw_write8_c(struct urtw_softc *, int, uint8_t);
|
|
static usb_error_t urtw_write16_c(struct urtw_softc *, int, uint16_t);
|
|
static usb_error_t urtw_write32_c(struct urtw_softc *, int, uint32_t);
|
|
static usb_error_t urtw_eprom_cs(struct urtw_softc *, int);
|
|
static usb_error_t urtw_eprom_ck(struct urtw_softc *);
|
|
static usb_error_t urtw_eprom_sendbits(struct urtw_softc *, int16_t *,
|
|
int);
|
|
static usb_error_t urtw_eprom_read32(struct urtw_softc *, uint32_t,
|
|
uint32_t *);
|
|
static usb_error_t urtw_eprom_readbit(struct urtw_softc *, int16_t *);
|
|
static usb_error_t urtw_eprom_writebit(struct urtw_softc *, int16_t);
|
|
static usb_error_t urtw_get_macaddr(struct urtw_softc *);
|
|
static usb_error_t urtw_get_txpwr(struct urtw_softc *);
|
|
static usb_error_t urtw_get_rfchip(struct urtw_softc *);
|
|
static usb_error_t urtw_led_init(struct urtw_softc *);
|
|
static usb_error_t urtw_8185_rf_pins_enable(struct urtw_softc *);
|
|
static usb_error_t urtw_8185_tx_antenna(struct urtw_softc *, uint8_t);
|
|
static usb_error_t urtw_8187_write_phy(struct urtw_softc *, uint8_t,
|
|
uint32_t);
|
|
static usb_error_t urtw_8187_write_phy_ofdm_c(struct urtw_softc *,
|
|
uint8_t, uint32_t);
|
|
static usb_error_t urtw_8187_write_phy_cck_c(struct urtw_softc *, uint8_t,
|
|
uint32_t);
|
|
static usb_error_t urtw_8225_setgain(struct urtw_softc *, int16_t);
|
|
static usb_error_t urtw_8225_usb_init(struct urtw_softc *);
|
|
static usb_error_t urtw_8225_write_c(struct urtw_softc *, uint8_t,
|
|
uint16_t);
|
|
static usb_error_t urtw_8225_write_s16(struct urtw_softc *, uint8_t, int,
|
|
uint16_t *);
|
|
static usb_error_t urtw_8225_read(struct urtw_softc *, uint8_t,
|
|
uint32_t *);
|
|
static usb_error_t urtw_8225_rf_init(struct urtw_softc *);
|
|
static usb_error_t urtw_8225_rf_set_chan(struct urtw_softc *, int);
|
|
static usb_error_t urtw_8225_rf_set_sens(struct urtw_softc *, int);
|
|
static usb_error_t urtw_8225_set_txpwrlvl(struct urtw_softc *, int);
|
|
static usb_error_t urtw_8225_rf_stop(struct urtw_softc *);
|
|
static usb_error_t urtw_8225v2_rf_init(struct urtw_softc *);
|
|
static usb_error_t urtw_8225v2_rf_set_chan(struct urtw_softc *, int);
|
|
static usb_error_t urtw_8225v2_set_txpwrlvl(struct urtw_softc *, int);
|
|
static usb_error_t urtw_8225v2_setgain(struct urtw_softc *, int16_t);
|
|
static usb_error_t urtw_8225_isv2(struct urtw_softc *, int *);
|
|
static usb_error_t urtw_8225v2b_rf_init(struct urtw_softc *);
|
|
static usb_error_t urtw_8225v2b_rf_set_chan(struct urtw_softc *, int);
|
|
static usb_error_t urtw_read8e(struct urtw_softc *, int, uint8_t *);
|
|
static usb_error_t urtw_write8e(struct urtw_softc *, int, uint8_t);
|
|
static usb_error_t urtw_8180_set_anaparam(struct urtw_softc *, uint32_t);
|
|
static usb_error_t urtw_8185_set_anaparam2(struct urtw_softc *, uint32_t);
|
|
static usb_error_t urtw_intr_enable(struct urtw_softc *);
|
|
static usb_error_t urtw_intr_disable(struct urtw_softc *);
|
|
static usb_error_t urtw_reset(struct urtw_softc *);
|
|
static usb_error_t urtw_led_on(struct urtw_softc *, int);
|
|
static usb_error_t urtw_led_ctl(struct urtw_softc *, int);
|
|
static usb_error_t urtw_led_blink(struct urtw_softc *);
|
|
static usb_error_t urtw_led_mode0(struct urtw_softc *, int);
|
|
static usb_error_t urtw_led_mode1(struct urtw_softc *, int);
|
|
static usb_error_t urtw_led_mode2(struct urtw_softc *, int);
|
|
static usb_error_t urtw_led_mode3(struct urtw_softc *, int);
|
|
static usb_error_t urtw_rx_setconf(struct urtw_softc *);
|
|
static usb_error_t urtw_rx_enable(struct urtw_softc *);
|
|
static usb_error_t urtw_tx_enable(struct urtw_softc *sc);
|
|
static void urtw_free_tx_data_list(struct urtw_softc *);
|
|
static void urtw_free_rx_data_list(struct urtw_softc *);
|
|
static void urtw_free_data_list(struct urtw_softc *,
|
|
struct urtw_data data[], int, int);
|
|
static usb_error_t urtw_adapter_start(struct urtw_softc *);
|
|
static usb_error_t urtw_adapter_start_b(struct urtw_softc *);
|
|
static usb_error_t urtw_set_mode(struct urtw_softc *, uint32_t);
|
|
static usb_error_t urtw_8187b_cmd_reset(struct urtw_softc *);
|
|
static usb_error_t urtw_do_request(struct urtw_softc *,
|
|
struct usb_device_request *, void *);
|
|
static usb_error_t urtw_8225v2b_set_txpwrlvl(struct urtw_softc *, int);
|
|
static usb_error_t urtw_led_off(struct urtw_softc *, int);
|
|
static void urtw_abort_xfers(struct urtw_softc *);
|
|
static struct urtw_data *
|
|
urtw_getbuf(struct urtw_softc *sc);
|
|
static int urtw_compute_txtime(uint16_t, uint16_t, uint8_t,
|
|
uint8_t);
|
|
static void urtw_updateslot(struct ifnet *);
|
|
static void urtw_updateslottask(void *, int);
|
|
static void urtw_sysctl_node(struct urtw_softc *);
|
|
|
|
static int
|
|
urtw_match(device_t dev)
|
|
{
|
|
struct usb_attach_arg *uaa = device_get_ivars(dev);
|
|
|
|
if (uaa->usb_mode != USB_MODE_HOST)
|
|
return (ENXIO);
|
|
if (uaa->info.bConfigIndex != URTW_CONFIG_INDEX)
|
|
return (ENXIO);
|
|
if (uaa->info.bIfaceIndex != URTW_IFACE_INDEX)
|
|
return (ENXIO);
|
|
|
|
return (usbd_lookup_id_by_uaa(urtw_devs, sizeof(urtw_devs), uaa));
|
|
}
|
|
|
|
static int
|
|
urtw_attach(device_t dev)
|
|
{
|
|
const struct usb_config *setup_start;
|
|
int ret = ENXIO;
|
|
struct urtw_softc *sc = device_get_softc(dev);
|
|
struct usb_attach_arg *uaa = device_get_ivars(dev);
|
|
struct ieee80211com *ic;
|
|
struct ifnet *ifp;
|
|
uint8_t bands, iface_index = URTW_IFACE_INDEX; /* XXX */
|
|
uint16_t n_setup;
|
|
uint32_t data;
|
|
usb_error_t error;
|
|
|
|
device_set_usb_desc(dev);
|
|
|
|
sc->sc_dev = dev;
|
|
sc->sc_udev = uaa->device;
|
|
if (USB_GET_DRIVER_INFO(uaa) == URTW_REV_RTL8187B)
|
|
sc->sc_flags |= URTW_RTL8187B;
|
|
#ifdef URTW_DEBUG
|
|
sc->sc_debug = urtw_debug;
|
|
#endif
|
|
|
|
mtx_init(&sc->sc_mtx, device_get_nameunit(sc->sc_dev), MTX_NETWORK_LOCK,
|
|
MTX_DEF);
|
|
usb_callout_init_mtx(&sc->sc_led_ch, &sc->sc_mtx, 0);
|
|
TASK_INIT(&sc->sc_led_task, 0, urtw_ledtask, sc);
|
|
TASK_INIT(&sc->sc_updateslot_task, 0, urtw_updateslottask, sc);
|
|
callout_init(&sc->sc_watchdog_ch, 0);
|
|
|
|
if (sc->sc_flags & URTW_RTL8187B) {
|
|
setup_start = urtw_8187b_usbconfig;
|
|
n_setup = URTW_8187B_N_XFERS;
|
|
} else {
|
|
setup_start = urtw_8187l_usbconfig;
|
|
n_setup = URTW_8187L_N_XFERS;
|
|
}
|
|
|
|
error = usbd_transfer_setup(uaa->device, &iface_index, sc->sc_xfer,
|
|
setup_start, n_setup, sc, &sc->sc_mtx);
|
|
if (error) {
|
|
device_printf(dev, "could not allocate USB transfers, "
|
|
"err=%s\n", usbd_errstr(error));
|
|
ret = ENXIO;
|
|
goto fail0;
|
|
}
|
|
|
|
URTW_LOCK(sc);
|
|
|
|
urtw_read32_m(sc, URTW_RX, &data);
|
|
sc->sc_epromtype = (data & URTW_RX_9356SEL) ? URTW_EEPROM_93C56 :
|
|
URTW_EEPROM_93C46;
|
|
|
|
error = urtw_get_rfchip(sc);
|
|
if (error != 0)
|
|
goto fail;
|
|
error = urtw_get_macaddr(sc);
|
|
if (error != 0)
|
|
goto fail;
|
|
error = urtw_get_txpwr(sc);
|
|
if (error != 0)
|
|
goto fail;
|
|
error = urtw_led_init(sc);
|
|
if (error != 0)
|
|
goto fail;
|
|
|
|
URTW_UNLOCK(sc);
|
|
|
|
sc->sc_rts_retry = URTW_DEFAULT_RTS_RETRY;
|
|
sc->sc_tx_retry = URTW_DEFAULT_TX_RETRY;
|
|
sc->sc_currate = 3;
|
|
sc->sc_preamble_mode = urtw_preamble_mode;
|
|
|
|
ifp = sc->sc_ifp = if_alloc(IFT_IEEE80211);
|
|
if (ifp == NULL) {
|
|
device_printf(sc->sc_dev, "can not allocate ifnet\n");
|
|
ret = ENOMEM;
|
|
goto fail1;
|
|
}
|
|
|
|
ifp->if_softc = sc;
|
|
if_initname(ifp, "urtw", device_get_unit(sc->sc_dev));
|
|
ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
|
|
ifp->if_init = urtw_init;
|
|
ifp->if_ioctl = urtw_ioctl;
|
|
ifp->if_start = urtw_start;
|
|
/* XXX URTW_TX_DATA_LIST_COUNT */
|
|
IFQ_SET_MAXLEN(&ifp->if_snd, ifqmaxlen);
|
|
ifp->if_snd.ifq_drv_maxlen = ifqmaxlen;
|
|
IFQ_SET_READY(&ifp->if_snd);
|
|
|
|
ic = ifp->if_l2com;
|
|
ic->ic_ifp = ifp;
|
|
ic->ic_phytype = IEEE80211_T_OFDM; /* not only, but not used */
|
|
ic->ic_opmode = IEEE80211_M_STA; /* default to BSS mode */
|
|
|
|
/* set device capabilities */
|
|
ic->ic_caps =
|
|
IEEE80211_C_STA | /* station mode */
|
|
IEEE80211_C_MONITOR | /* monitor mode supported */
|
|
IEEE80211_C_TXPMGT | /* tx power management */
|
|
IEEE80211_C_SHPREAMBLE | /* short preamble supported */
|
|
IEEE80211_C_SHSLOT | /* short slot time supported */
|
|
IEEE80211_C_BGSCAN | /* capable of bg scanning */
|
|
IEEE80211_C_WPA; /* 802.11i */
|
|
|
|
bands = 0;
|
|
setbit(&bands, IEEE80211_MODE_11B);
|
|
setbit(&bands, IEEE80211_MODE_11G);
|
|
ieee80211_init_channels(ic, NULL, &bands);
|
|
|
|
ieee80211_ifattach(ic, sc->sc_bssid);
|
|
ic->ic_raw_xmit = urtw_raw_xmit;
|
|
ic->ic_scan_start = urtw_scan_start;
|
|
ic->ic_scan_end = urtw_scan_end;
|
|
ic->ic_set_channel = urtw_set_channel;
|
|
ic->ic_updateslot = urtw_updateslot;
|
|
ic->ic_vap_create = urtw_vap_create;
|
|
ic->ic_vap_delete = urtw_vap_delete;
|
|
ic->ic_update_mcast = urtw_update_mcast;
|
|
|
|
ieee80211_radiotap_attach(ic,
|
|
&sc->sc_txtap.wt_ihdr, sizeof(sc->sc_txtap),
|
|
URTW_TX_RADIOTAP_PRESENT,
|
|
&sc->sc_rxtap.wr_ihdr, sizeof(sc->sc_rxtap),
|
|
URTW_RX_RADIOTAP_PRESENT);
|
|
|
|
urtw_sysctl_node(sc);
|
|
|
|
if (bootverbose)
|
|
ieee80211_announce(ic);
|
|
return (0);
|
|
|
|
fail: URTW_UNLOCK(sc);
|
|
fail1: usbd_transfer_unsetup(sc->sc_xfer, (sc->sc_flags & URTW_RTL8187B) ?
|
|
URTW_8187B_N_XFERS : URTW_8187L_N_XFERS);
|
|
fail0:
|
|
return (ret);
|
|
}
|
|
|
|
static int
|
|
urtw_detach(device_t dev)
|
|
{
|
|
struct urtw_softc *sc = device_get_softc(dev);
|
|
struct ifnet *ifp = sc->sc_ifp;
|
|
struct ieee80211com *ic = ifp->if_l2com;
|
|
|
|
if (!device_is_attached(dev))
|
|
return (0);
|
|
|
|
urtw_stop(ifp, 1);
|
|
ieee80211_draintask(ic, &sc->sc_updateslot_task);
|
|
ieee80211_draintask(ic, &sc->sc_led_task);
|
|
|
|
usb_callout_drain(&sc->sc_led_ch);
|
|
callout_drain(&sc->sc_watchdog_ch);
|
|
|
|
usbd_transfer_unsetup(sc->sc_xfer, (sc->sc_flags & URTW_RTL8187B) ?
|
|
URTW_8187B_N_XFERS : URTW_8187L_N_XFERS);
|
|
ieee80211_ifdetach(ic);
|
|
|
|
urtw_free_tx_data_list(sc);
|
|
urtw_free_rx_data_list(sc);
|
|
|
|
if_free(ifp);
|
|
mtx_destroy(&sc->sc_mtx);
|
|
|
|
return (0);
|
|
}
|
|
|
|
static void
|
|
urtw_free_tx_data_list(struct urtw_softc *sc)
|
|
{
|
|
|
|
urtw_free_data_list(sc, sc->sc_tx, URTW_TX_DATA_LIST_COUNT, 0);
|
|
}
|
|
|
|
static void
|
|
urtw_free_rx_data_list(struct urtw_softc *sc)
|
|
{
|
|
|
|
urtw_free_data_list(sc, sc->sc_rx, URTW_RX_DATA_LIST_COUNT, 1);
|
|
}
|
|
|
|
static void
|
|
urtw_free_data_list(struct urtw_softc *sc, struct urtw_data data[], int ndata,
|
|
int fillmbuf)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < ndata; i++) {
|
|
struct urtw_data *dp = &data[i];
|
|
|
|
if (fillmbuf == 1) {
|
|
if (dp->m != NULL) {
|
|
m_freem(dp->m);
|
|
dp->m = NULL;
|
|
dp->buf = NULL;
|
|
}
|
|
} else {
|
|
if (dp->buf != NULL) {
|
|
free(dp->buf, M_USBDEV);
|
|
dp->buf = NULL;
|
|
}
|
|
}
|
|
if (dp->ni != NULL) {
|
|
ieee80211_free_node(dp->ni);
|
|
dp->ni = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
static struct ieee80211vap *
|
|
urtw_vap_create(struct ieee80211com *ic,
|
|
const char name[IFNAMSIZ], int unit, int opmode, int flags,
|
|
const uint8_t bssid[IEEE80211_ADDR_LEN],
|
|
const uint8_t mac[IEEE80211_ADDR_LEN])
|
|
{
|
|
struct urtw_vap *uvp;
|
|
struct ieee80211vap *vap;
|
|
|
|
if (!TAILQ_EMPTY(&ic->ic_vaps)) /* only one at a time */
|
|
return (NULL);
|
|
uvp = (struct urtw_vap *) malloc(sizeof(struct urtw_vap),
|
|
M_80211_VAP, M_NOWAIT | M_ZERO);
|
|
if (uvp == NULL)
|
|
return (NULL);
|
|
vap = &uvp->vap;
|
|
/* enable s/w bmiss handling for sta mode */
|
|
ieee80211_vap_setup(ic, vap, name, unit, opmode,
|
|
flags | IEEE80211_CLONE_NOBEACONS, bssid, mac);
|
|
|
|
/* override state transition machine */
|
|
uvp->newstate = vap->iv_newstate;
|
|
vap->iv_newstate = urtw_newstate;
|
|
|
|
/* complete setup */
|
|
ieee80211_vap_attach(vap, ieee80211_media_change,
|
|
ieee80211_media_status);
|
|
ic->ic_opmode = opmode;
|
|
return (vap);
|
|
}
|
|
|
|
static void
|
|
urtw_vap_delete(struct ieee80211vap *vap)
|
|
{
|
|
struct urtw_vap *uvp = URTW_VAP(vap);
|
|
|
|
ieee80211_vap_detach(vap);
|
|
free(uvp, M_80211_VAP);
|
|
}
|
|
|
|
static void
|
|
urtw_init_locked(void *arg)
|
|
{
|
|
int ret;
|
|
struct urtw_softc *sc = arg;
|
|
struct ifnet *ifp = sc->sc_ifp;
|
|
usb_error_t error;
|
|
|
|
if (ifp->if_drv_flags & IFF_DRV_RUNNING)
|
|
urtw_stop_locked(ifp, 0);
|
|
|
|
error = (sc->sc_flags & URTW_RTL8187B) ? urtw_adapter_start_b(sc) :
|
|
urtw_adapter_start(sc);
|
|
if (error != 0)
|
|
goto fail;
|
|
|
|
/* reset softc variables */
|
|
sc->sc_txtimer = 0;
|
|
|
|
if (!(sc->sc_flags & URTW_INIT_ONCE)) {
|
|
ret = urtw_alloc_rx_data_list(sc);
|
|
if (error != 0)
|
|
goto fail;
|
|
ret = urtw_alloc_tx_data_list(sc);
|
|
if (error != 0)
|
|
goto fail;
|
|
sc->sc_flags |= URTW_INIT_ONCE;
|
|
}
|
|
|
|
error = urtw_rx_enable(sc);
|
|
if (error != 0)
|
|
goto fail;
|
|
error = urtw_tx_enable(sc);
|
|
if (error != 0)
|
|
goto fail;
|
|
|
|
if (sc->sc_flags & URTW_RTL8187B)
|
|
usbd_transfer_start(sc->sc_xfer[URTW_8187B_BULK_TX_STATUS]);
|
|
|
|
ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
|
|
ifp->if_drv_flags |= IFF_DRV_RUNNING;
|
|
|
|
callout_reset(&sc->sc_watchdog_ch, hz, urtw_watchdog, sc);
|
|
fail:
|
|
return;
|
|
}
|
|
|
|
static void
|
|
urtw_init(void *arg)
|
|
{
|
|
struct urtw_softc *sc = arg;
|
|
|
|
URTW_LOCK(sc);
|
|
urtw_init_locked(arg);
|
|
URTW_UNLOCK(sc);
|
|
}
|
|
|
|
static usb_error_t
|
|
urtw_adapter_start_b(struct urtw_softc *sc)
|
|
{
|
|
#define N(a) (sizeof(a) / sizeof((a)[0]))
|
|
uint8_t data8;
|
|
usb_error_t error;
|
|
|
|
error = urtw_set_mode(sc, URTW_EPROM_CMD_CONFIG);
|
|
if (error)
|
|
goto fail;
|
|
|
|
urtw_read8_m(sc, URTW_CONFIG3, &data8);
|
|
urtw_write8_m(sc, URTW_CONFIG3,
|
|
data8 | URTW_CONFIG3_ANAPARAM_WRITE | URTW_CONFIG3_GNT_SELECT);
|
|
urtw_write32_m(sc, URTW_ANAPARAM2, URTW_8187B_8225_ANAPARAM2_ON);
|
|
urtw_write32_m(sc, URTW_ANAPARAM, URTW_8187B_8225_ANAPARAM_ON);
|
|
urtw_write8_m(sc, URTW_ANAPARAM3, URTW_8187B_8225_ANAPARAM3_ON);
|
|
|
|
urtw_write8_m(sc, 0x61, 0x10);
|
|
urtw_read8_m(sc, 0x62, &data8);
|
|
urtw_write8_m(sc, 0x62, data8 & ~(1 << 5));
|
|
urtw_write8_m(sc, 0x62, data8 | (1 << 5));
|
|
|
|
urtw_read8_m(sc, URTW_CONFIG3, &data8);
|
|
data8 &= ~URTW_CONFIG3_ANAPARAM_WRITE;
|
|
urtw_write8_m(sc, URTW_CONFIG3, data8);
|
|
|
|
error = urtw_set_mode(sc, URTW_EPROM_CMD_NORMAL);
|
|
if (error)
|
|
goto fail;
|
|
|
|
error = urtw_8187b_cmd_reset(sc);
|
|
if (error)
|
|
goto fail;
|
|
|
|
error = sc->sc_rf_init(sc);
|
|
if (error != 0)
|
|
goto fail;
|
|
urtw_write8_m(sc, URTW_CMD, URTW_CMD_RX_ENABLE | URTW_CMD_TX_ENABLE);
|
|
|
|
/* fix RTL8187B RX stall */
|
|
error = urtw_intr_enable(sc);
|
|
if (error)
|
|
goto fail;
|
|
|
|
error = urtw_write8e(sc, 0x41, 0xf4);
|
|
if (error)
|
|
goto fail;
|
|
error = urtw_write8e(sc, 0x40, 0x00);
|
|
if (error)
|
|
goto fail;
|
|
error = urtw_write8e(sc, 0x42, 0x00);
|
|
if (error)
|
|
goto fail;
|
|
error = urtw_write8e(sc, 0x42, 0x01);
|
|
if (error)
|
|
goto fail;
|
|
error = urtw_write8e(sc, 0x40, 0x0f);
|
|
if (error)
|
|
goto fail;
|
|
error = urtw_write8e(sc, 0x42, 0x00);
|
|
if (error)
|
|
goto fail;
|
|
error = urtw_write8e(sc, 0x42, 0x01);
|
|
if (error)
|
|
goto fail;
|
|
|
|
urtw_read8_m(sc, 0xdb, &data8);
|
|
urtw_write8_m(sc, 0xdb, data8 | (1 << 2));
|
|
urtw_write16_m(sc, 0x372, 0x59fa);
|
|
urtw_write16_m(sc, 0x374, 0x59d2);
|
|
urtw_write16_m(sc, 0x376, 0x59d2);
|
|
urtw_write16_m(sc, 0x378, 0x19fa);
|
|
urtw_write16_m(sc, 0x37a, 0x19fa);
|
|
urtw_write16_m(sc, 0x37c, 0x00d0);
|
|
urtw_write8_m(sc, 0x61, 0);
|
|
|
|
urtw_write8_m(sc, 0x180, 0x0f);
|
|
urtw_write8_m(sc, 0x183, 0x03);
|
|
urtw_write8_m(sc, 0xda, 0x10);
|
|
urtw_write8_m(sc, 0x24d, 0x08);
|
|
urtw_write32_m(sc, URTW_HSSI_PARA, 0x0600321b);
|
|
|
|
urtw_write16_m(sc, 0x1ec, 0x800); /* RX MAX SIZE */
|
|
fail:
|
|
return (error);
|
|
#undef N
|
|
}
|
|
|
|
static usb_error_t
|
|
urtw_adapter_start(struct urtw_softc *sc)
|
|
{
|
|
usb_error_t error;
|
|
|
|
error = urtw_reset(sc);
|
|
if (error)
|
|
goto fail;
|
|
|
|
urtw_write8_m(sc, URTW_ADDR_MAGIC1, 0);
|
|
urtw_write8_m(sc, URTW_GPIO, 0);
|
|
|
|
/* for led */
|
|
urtw_write8_m(sc, URTW_ADDR_MAGIC1, 4);
|
|
error = urtw_led_ctl(sc, URTW_LED_CTL_POWER_ON);
|
|
if (error != 0)
|
|
goto fail;
|
|
|
|
error = urtw_set_mode(sc, URTW_EPROM_CMD_CONFIG);
|
|
if (error)
|
|
goto fail;
|
|
/* applying MAC address again. */
|
|
urtw_write32_m(sc, URTW_MAC0, ((uint32_t *)sc->sc_bssid)[0]);
|
|
urtw_write16_m(sc, URTW_MAC4, ((uint32_t *)sc->sc_bssid)[1] & 0xffff);
|
|
error = urtw_set_mode(sc, URTW_EPROM_CMD_NORMAL);
|
|
if (error)
|
|
goto fail;
|
|
|
|
error = urtw_update_msr(sc);
|
|
if (error)
|
|
goto fail;
|
|
|
|
urtw_write32_m(sc, URTW_INT_TIMEOUT, 0);
|
|
urtw_write8_m(sc, URTW_WPA_CONFIG, 0);
|
|
urtw_write8_m(sc, URTW_RATE_FALLBACK, URTW_RATE_FALLBACK_ENABLE | 0x1);
|
|
error = urtw_set_rate(sc);
|
|
if (error != 0)
|
|
goto fail;
|
|
|
|
error = sc->sc_rf_init(sc);
|
|
if (error != 0)
|
|
goto fail;
|
|
if (sc->sc_rf_set_sens != NULL)
|
|
sc->sc_rf_set_sens(sc, sc->sc_sens);
|
|
|
|
/* XXX correct? to call write16 */
|
|
urtw_write16_m(sc, URTW_PSR, 1);
|
|
urtw_write16_m(sc, URTW_ADDR_MAGIC2, 0x10);
|
|
urtw_write8_m(sc, URTW_TALLY_SEL, 0x80);
|
|
urtw_write8_m(sc, URTW_ADDR_MAGIC3, 0x60);
|
|
/* XXX correct? to call write16 */
|
|
urtw_write16_m(sc, URTW_PSR, 0);
|
|
urtw_write8_m(sc, URTW_ADDR_MAGIC1, 4);
|
|
|
|
error = urtw_intr_enable(sc);
|
|
if (error != 0)
|
|
goto fail;
|
|
|
|
fail:
|
|
return (error);
|
|
}
|
|
|
|
static usb_error_t
|
|
urtw_set_mode(struct urtw_softc *sc, uint32_t mode)
|
|
{
|
|
uint8_t data;
|
|
usb_error_t error;
|
|
|
|
urtw_read8_m(sc, URTW_EPROM_CMD, &data);
|
|
data = (data & ~URTW_EPROM_CMD_MASK) | (mode << URTW_EPROM_CMD_SHIFT);
|
|
data = data & ~(URTW_EPROM_CS | URTW_EPROM_CK);
|
|
urtw_write8_m(sc, URTW_EPROM_CMD, data);
|
|
fail:
|
|
return (error);
|
|
}
|
|
|
|
static usb_error_t
|
|
urtw_8187b_cmd_reset(struct urtw_softc *sc)
|
|
{
|
|
int i;
|
|
uint8_t data8;
|
|
usb_error_t error;
|
|
|
|
/* XXX the code can be duplicate with urtw_reset(). */
|
|
urtw_read8_m(sc, URTW_CMD, &data8);
|
|
data8 = (data8 & 0x2) | URTW_CMD_RST;
|
|
urtw_write8_m(sc, URTW_CMD, data8);
|
|
|
|
for (i = 0; i < 20; i++) {
|
|
usb_pause_mtx(&sc->sc_mtx, 2);
|
|
urtw_read8_m(sc, URTW_CMD, &data8);
|
|
if (!(data8 & URTW_CMD_RST))
|
|
break;
|
|
}
|
|
if (i >= 20) {
|
|
device_printf(sc->sc_dev, "reset timeout\n");
|
|
goto fail;
|
|
}
|
|
fail:
|
|
return (error);
|
|
}
|
|
|
|
static usb_error_t
|
|
urtw_do_request(struct urtw_softc *sc,
|
|
struct usb_device_request *req, void *data)
|
|
{
|
|
usb_error_t err;
|
|
int ntries = 10;
|
|
|
|
URTW_ASSERT_LOCKED(sc);
|
|
|
|
while (ntries--) {
|
|
err = usbd_do_request_flags(sc->sc_udev, &sc->sc_mtx,
|
|
req, data, 0, NULL, 250 /* ms */);
|
|
if (err == 0)
|
|
break;
|
|
|
|
DPRINTF(sc, URTW_DEBUG_INIT,
|
|
"Control request failed, %s (retrying)\n",
|
|
usbd_errstr(err));
|
|
usb_pause_mtx(&sc->sc_mtx, hz / 100);
|
|
}
|
|
return (err);
|
|
}
|
|
|
|
static void
|
|
urtw_stop_locked(struct ifnet *ifp, int disable)
|
|
{
|
|
struct urtw_softc *sc = ifp->if_softc;
|
|
uint8_t data8;
|
|
usb_error_t error;
|
|
|
|
(void)disable;
|
|
ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE);
|
|
|
|
error = urtw_intr_disable(sc);
|
|
if (error)
|
|
goto fail;
|
|
urtw_read8_m(sc, URTW_CMD, &data8);
|
|
data8 &= ~(URTW_CMD_RX_ENABLE | URTW_CMD_TX_ENABLE);
|
|
urtw_write8_m(sc, URTW_CMD, data8);
|
|
|
|
error = sc->sc_rf_stop(sc);
|
|
if (error != 0)
|
|
goto fail;
|
|
|
|
error = urtw_set_mode(sc, URTW_EPROM_CMD_CONFIG);
|
|
if (error)
|
|
goto fail;
|
|
urtw_read8_m(sc, URTW_CONFIG4, &data8);
|
|
urtw_write8_m(sc, URTW_CONFIG4, data8 | URTW_CONFIG4_VCOOFF);
|
|
error = urtw_set_mode(sc, URTW_EPROM_CMD_NORMAL);
|
|
if (error)
|
|
goto fail;
|
|
fail:
|
|
if (error)
|
|
device_printf(sc->sc_dev, "failed to stop (%s)\n",
|
|
usbd_errstr(error));
|
|
|
|
usb_callout_stop(&sc->sc_led_ch);
|
|
callout_stop(&sc->sc_watchdog_ch);
|
|
|
|
urtw_abort_xfers(sc);
|
|
}
|
|
|
|
static void
|
|
urtw_stop(struct ifnet *ifp, int disable)
|
|
{
|
|
struct urtw_softc *sc = ifp->if_softc;
|
|
|
|
URTW_LOCK(sc);
|
|
urtw_stop_locked(ifp, disable);
|
|
URTW_UNLOCK(sc);
|
|
}
|
|
|
|
static void
|
|
urtw_abort_xfers(struct urtw_softc *sc)
|
|
{
|
|
int i, max;
|
|
|
|
URTW_ASSERT_LOCKED(sc);
|
|
|
|
max = (sc->sc_flags & URTW_RTL8187B) ? URTW_8187B_N_XFERS :
|
|
URTW_8187L_N_XFERS;
|
|
|
|
/* abort any pending transfers */
|
|
for (i = 0; i < max; i++)
|
|
usbd_transfer_stop(sc->sc_xfer[i]);
|
|
}
|
|
|
|
static int
|
|
urtw_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
|
|
{
|
|
struct urtw_softc *sc = ifp->if_softc;
|
|
struct ieee80211com *ic = ifp->if_l2com;
|
|
struct ifreq *ifr = (struct ifreq *) data;
|
|
int error = 0, startall = 0;
|
|
|
|
switch (cmd) {
|
|
case SIOCSIFFLAGS:
|
|
if (ifp->if_flags & IFF_UP) {
|
|
if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
|
|
if ((ifp->if_flags ^ sc->sc_if_flags) &
|
|
(IFF_ALLMULTI | IFF_PROMISC))
|
|
urtw_set_multi(sc);
|
|
} else {
|
|
urtw_init(ifp->if_softc);
|
|
startall = 1;
|
|
}
|
|
} else {
|
|
if (ifp->if_drv_flags & IFF_DRV_RUNNING)
|
|
urtw_stop(ifp, 1);
|
|
}
|
|
sc->sc_if_flags = ifp->if_flags;
|
|
if (startall)
|
|
ieee80211_start_all(ic);
|
|
break;
|
|
case SIOCGIFMEDIA:
|
|
error = ifmedia_ioctl(ifp, ifr, &ic->ic_media, cmd);
|
|
break;
|
|
case SIOCGIFADDR:
|
|
error = ether_ioctl(ifp, cmd, data);
|
|
break;
|
|
default:
|
|
error = EINVAL;
|
|
break;
|
|
}
|
|
|
|
return (error);
|
|
}
|
|
|
|
static void
|
|
urtw_start(struct ifnet *ifp)
|
|
{
|
|
struct urtw_data *bf;
|
|
struct urtw_softc *sc = ifp->if_softc;
|
|
struct ieee80211_node *ni;
|
|
struct mbuf *m;
|
|
|
|
if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0)
|
|
return;
|
|
|
|
URTW_LOCK(sc);
|
|
for (;;) {
|
|
IFQ_DRV_DEQUEUE(&ifp->if_snd, m);
|
|
if (m == NULL)
|
|
break;
|
|
bf = urtw_getbuf(sc);
|
|
if (bf == NULL) {
|
|
IFQ_DRV_PREPEND(&ifp->if_snd, m);
|
|
break;
|
|
}
|
|
|
|
ni = (struct ieee80211_node *)m->m_pkthdr.rcvif;
|
|
m->m_pkthdr.rcvif = NULL;
|
|
|
|
if (urtw_tx_start(sc, ni, m, bf, URTW_PRIORITY_NORMAL) != 0) {
|
|
ifp->if_oerrors++;
|
|
STAILQ_INSERT_HEAD(&sc->sc_tx_inactive, bf, next);
|
|
ieee80211_free_node(ni);
|
|
break;
|
|
}
|
|
|
|
sc->sc_txtimer = 5;
|
|
callout_reset(&sc->sc_watchdog_ch, hz, urtw_watchdog, sc);
|
|
}
|
|
URTW_UNLOCK(sc);
|
|
}
|
|
|
|
static int
|
|
urtw_alloc_data_list(struct urtw_softc *sc, struct urtw_data data[],
|
|
int ndata, int maxsz, int fillmbuf)
|
|
{
|
|
int i, error;
|
|
|
|
for (i = 0; i < ndata; i++) {
|
|
struct urtw_data *dp = &data[i];
|
|
|
|
dp->sc = sc;
|
|
if (fillmbuf) {
|
|
dp->m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR);
|
|
if (dp->m == NULL) {
|
|
device_printf(sc->sc_dev,
|
|
"could not allocate rx mbuf\n");
|
|
error = ENOMEM;
|
|
goto fail;
|
|
}
|
|
dp->buf = mtod(dp->m, uint8_t *);
|
|
} else {
|
|
dp->m = NULL;
|
|
dp->buf = malloc(maxsz, M_USBDEV, M_NOWAIT);
|
|
if (dp->buf == NULL) {
|
|
device_printf(sc->sc_dev,
|
|
"could not allocate buffer\n");
|
|
error = ENOMEM;
|
|
goto fail;
|
|
}
|
|
if (((unsigned long)dp->buf) % 4)
|
|
device_printf(sc->sc_dev,
|
|
"warn: unaligned buffer %p\n", dp->buf);
|
|
}
|
|
dp->ni = NULL;
|
|
}
|
|
|
|
return 0;
|
|
|
|
fail: urtw_free_data_list(sc, data, ndata, fillmbuf);
|
|
return error;
|
|
}
|
|
|
|
static int
|
|
urtw_alloc_rx_data_list(struct urtw_softc *sc)
|
|
{
|
|
int error, i;
|
|
|
|
error = urtw_alloc_data_list(sc,
|
|
sc->sc_rx, URTW_RX_DATA_LIST_COUNT, MCLBYTES, 1 /* mbufs */);
|
|
if (error != 0)
|
|
return (error);
|
|
|
|
STAILQ_INIT(&sc->sc_rx_active);
|
|
STAILQ_INIT(&sc->sc_rx_inactive);
|
|
|
|
for (i = 0; i < URTW_RX_DATA_LIST_COUNT; i++)
|
|
STAILQ_INSERT_HEAD(&sc->sc_rx_inactive, &sc->sc_rx[i], next);
|
|
|
|
return (0);
|
|
}
|
|
|
|
static int
|
|
urtw_alloc_tx_data_list(struct urtw_softc *sc)
|
|
{
|
|
int error, i;
|
|
|
|
error = urtw_alloc_data_list(sc,
|
|
sc->sc_tx, URTW_TX_DATA_LIST_COUNT, URTW_TX_MAXSIZE,
|
|
0 /* no mbufs */);
|
|
if (error != 0)
|
|
return (error);
|
|
|
|
STAILQ_INIT(&sc->sc_tx_active);
|
|
STAILQ_INIT(&sc->sc_tx_inactive);
|
|
STAILQ_INIT(&sc->sc_tx_pending);
|
|
|
|
for (i = 0; i < URTW_TX_DATA_LIST_COUNT; i++)
|
|
STAILQ_INSERT_HEAD(&sc->sc_tx_inactive, &sc->sc_tx[i],
|
|
next);
|
|
|
|
return (0);
|
|
}
|
|
|
|
static int
|
|
urtw_raw_xmit(struct ieee80211_node *ni, struct mbuf *m,
|
|
const struct ieee80211_bpf_params *params)
|
|
{
|
|
struct ieee80211com *ic = ni->ni_ic;
|
|
struct ifnet *ifp = ic->ic_ifp;
|
|
struct urtw_data *bf;
|
|
struct urtw_softc *sc = ifp->if_softc;
|
|
|
|
/* prevent management frames from being sent if we're not ready */
|
|
if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) {
|
|
m_freem(m);
|
|
ieee80211_free_node(ni);
|
|
return ENETDOWN;
|
|
}
|
|
URTW_LOCK(sc);
|
|
bf = urtw_getbuf(sc);
|
|
if (bf == NULL) {
|
|
ieee80211_free_node(ni);
|
|
m_freem(m);
|
|
URTW_UNLOCK(sc);
|
|
return (ENOBUFS); /* XXX */
|
|
}
|
|
|
|
ifp->if_opackets++;
|
|
if (urtw_tx_start(sc, ni, m, bf, URTW_PRIORITY_LOW) != 0) {
|
|
ieee80211_free_node(ni);
|
|
ifp->if_oerrors++;
|
|
STAILQ_INSERT_HEAD(&sc->sc_tx_inactive, bf, next);
|
|
URTW_UNLOCK(sc);
|
|
return (EIO);
|
|
}
|
|
URTW_UNLOCK(sc);
|
|
|
|
sc->sc_txtimer = 5;
|
|
return (0);
|
|
}
|
|
|
|
static void
|
|
urtw_scan_start(struct ieee80211com *ic)
|
|
{
|
|
|
|
/* XXX do nothing? */
|
|
}
|
|
|
|
static void
|
|
urtw_scan_end(struct ieee80211com *ic)
|
|
{
|
|
|
|
/* XXX do nothing? */
|
|
}
|
|
|
|
static void
|
|
urtw_set_channel(struct ieee80211com *ic)
|
|
{
|
|
struct urtw_softc *sc = ic->ic_ifp->if_softc;
|
|
struct ifnet *ifp = sc->sc_ifp;
|
|
uint32_t data, orig;
|
|
usb_error_t error;
|
|
|
|
/*
|
|
* if the user set a channel explicitly using ifconfig(8) this function
|
|
* can be called earlier than we're expected that in some cases the
|
|
* initialization would be failed if setting a channel is called before
|
|
* the init have done.
|
|
*/
|
|
if (!(ifp->if_drv_flags & IFF_DRV_RUNNING))
|
|
return;
|
|
|
|
if (sc->sc_curchan != NULL && sc->sc_curchan == ic->ic_curchan)
|
|
return;
|
|
|
|
URTW_LOCK(sc);
|
|
|
|
/*
|
|
* during changing th channel we need to temporarily be disable
|
|
* TX.
|
|
*/
|
|
urtw_read32_m(sc, URTW_TX_CONF, &orig);
|
|
data = orig & ~URTW_TX_LOOPBACK_MASK;
|
|
urtw_write32_m(sc, URTW_TX_CONF, data | URTW_TX_LOOPBACK_MAC);
|
|
|
|
error = sc->sc_rf_set_chan(sc, ieee80211_chan2ieee(ic, ic->ic_curchan));
|
|
if (error != 0)
|
|
goto fail;
|
|
usb_pause_mtx(&sc->sc_mtx, 10);
|
|
urtw_write32_m(sc, URTW_TX_CONF, orig);
|
|
|
|
urtw_write16_m(sc, URTW_ATIM_WND, 2);
|
|
urtw_write16_m(sc, URTW_ATIM_TR_ITV, 100);
|
|
urtw_write16_m(sc, URTW_BEACON_INTERVAL, 100);
|
|
urtw_write16_m(sc, URTW_BEACON_INTERVAL_TIME, 100);
|
|
|
|
fail:
|
|
URTW_UNLOCK(sc);
|
|
|
|
sc->sc_curchan = ic->ic_curchan;
|
|
|
|
if (error != 0)
|
|
device_printf(sc->sc_dev, "could not change the channel\n");
|
|
}
|
|
|
|
static void
|
|
urtw_update_mcast(struct ifnet *ifp)
|
|
{
|
|
|
|
/* XXX do nothing? */
|
|
}
|
|
|
|
static int
|
|
urtw_tx_start(struct urtw_softc *sc, struct ieee80211_node *ni, struct mbuf *m0,
|
|
struct urtw_data *data, int prior)
|
|
{
|
|
struct ifnet *ifp = sc->sc_ifp;
|
|
struct ieee80211_frame *wh = mtod(m0, struct ieee80211_frame *);
|
|
struct ieee80211_key *k;
|
|
const struct ieee80211_txparam *tp;
|
|
struct ieee80211com *ic = ifp->if_l2com;
|
|
struct ieee80211vap *vap = ni->ni_vap;
|
|
struct usb_xfer *rtl8187b_pipes[URTW_8187B_TXPIPE_MAX] = {
|
|
sc->sc_xfer[URTW_8187B_BULK_TX_BE],
|
|
sc->sc_xfer[URTW_8187B_BULK_TX_BK],
|
|
sc->sc_xfer[URTW_8187B_BULK_TX_VI],
|
|
sc->sc_xfer[URTW_8187B_BULK_TX_VO]
|
|
};
|
|
struct usb_xfer *xfer;
|
|
int dur = 0, rtsdur = 0, rtsenable = 0, ctsenable = 0, rate,
|
|
pkttime = 0, txdur = 0, isshort = 0, xferlen;
|
|
uint16_t acktime, rtstime, ctstime;
|
|
uint32_t flags;
|
|
usb_error_t error;
|
|
|
|
URTW_ASSERT_LOCKED(sc);
|
|
|
|
/*
|
|
* Software crypto.
|
|
*/
|
|
if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
|
|
k = ieee80211_crypto_encap(ni, m0);
|
|
if (k == NULL) {
|
|
device_printf(sc->sc_dev,
|
|
"ieee80211_crypto_encap returns NULL.\n");
|
|
/* XXX we don't expect the fragmented frames */
|
|
m_freem(m0);
|
|
return (ENOBUFS);
|
|
}
|
|
|
|
/* in case packet header moved, reset pointer */
|
|
wh = mtod(m0, struct ieee80211_frame *);
|
|
}
|
|
|
|
if (ieee80211_radiotap_active_vap(vap)) {
|
|
struct urtw_tx_radiotap_header *tap = &sc->sc_txtap;
|
|
|
|
/* XXX Are variables correct? */
|
|
tap->wt_flags = 0;
|
|
tap->wt_chan_freq = htole16(ic->ic_curchan->ic_freq);
|
|
tap->wt_chan_flags = htole16(ic->ic_curchan->ic_flags);
|
|
|
|
ieee80211_radiotap_tx(vap, m0);
|
|
}
|
|
|
|
if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) == IEEE80211_FC0_TYPE_MGT ||
|
|
(wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) == IEEE80211_FC0_TYPE_CTL) {
|
|
tp = &vap->iv_txparms[ieee80211_chan2mode(ic->ic_curchan)];
|
|
rate = tp->mgmtrate;
|
|
} else {
|
|
tp = &vap->iv_txparms[ieee80211_chan2mode(ni->ni_chan)];
|
|
/* for data frames */
|
|
if (IEEE80211_IS_MULTICAST(wh->i_addr1))
|
|
rate = tp->mcastrate;
|
|
else if (tp->ucastrate != IEEE80211_FIXED_RATE_NONE)
|
|
rate = tp->ucastrate;
|
|
else
|
|
rate = urtw_rtl2rate(sc->sc_currate);
|
|
}
|
|
|
|
sc->sc_stats.txrates[sc->sc_currate]++;
|
|
|
|
if (IEEE80211_IS_MULTICAST(wh->i_addr1))
|
|
txdur = pkttime = urtw_compute_txtime(m0->m_pkthdr.len +
|
|
IEEE80211_CRC_LEN, rate, 0, 0);
|
|
else {
|
|
acktime = urtw_compute_txtime(14, 2,0, 0);
|
|
if ((m0->m_pkthdr.len + 4) > vap->iv_rtsthreshold) {
|
|
rtsenable = 1;
|
|
ctsenable = 0;
|
|
rtstime = urtw_compute_txtime(URTW_ACKCTS_LEN, 2, 0, 0);
|
|
ctstime = urtw_compute_txtime(14, 2, 0, 0);
|
|
pkttime = urtw_compute_txtime(m0->m_pkthdr.len +
|
|
IEEE80211_CRC_LEN, rate, 0, isshort);
|
|
rtsdur = ctstime + pkttime + acktime +
|
|
3 * URTW_ASIFS_TIME;
|
|
txdur = rtstime + rtsdur;
|
|
} else {
|
|
rtsenable = ctsenable = rtsdur = 0;
|
|
pkttime = urtw_compute_txtime(m0->m_pkthdr.len +
|
|
IEEE80211_CRC_LEN, rate, 0, isshort);
|
|
txdur = pkttime + URTW_ASIFS_TIME + acktime;
|
|
}
|
|
|
|
if (wh->i_fc[1] & IEEE80211_FC1_MORE_FRAG)
|
|
dur = urtw_compute_txtime(m0->m_pkthdr.len +
|
|
IEEE80211_CRC_LEN, rate, 0, isshort) +
|
|
3 * URTW_ASIFS_TIME +
|
|
2 * acktime;
|
|
else
|
|
dur = URTW_ASIFS_TIME + acktime;
|
|
}
|
|
*(uint16_t *)wh->i_dur = htole16(dur);
|
|
|
|
xferlen = m0->m_pkthdr.len;
|
|
xferlen += (sc->sc_flags & URTW_RTL8187B) ? (4 * 8) : (4 * 3);
|
|
if ((0 == xferlen % 64) || (0 == xferlen % 512))
|
|
xferlen += 1;
|
|
|
|
bzero(data->buf, URTW_TX_MAXSIZE);
|
|
flags = m0->m_pkthdr.len & 0xfff;
|
|
flags |= URTW_TX_FLAG_NO_ENC;
|
|
if ((ic->ic_flags & IEEE80211_F_SHPREAMBLE) &&
|
|
(ni->ni_capinfo & IEEE80211_CAPINFO_SHORT_PREAMBLE) &&
|
|
(sc->sc_preamble_mode == URTW_PREAMBLE_MODE_SHORT) &&
|
|
(sc->sc_currate != 0))
|
|
flags |= URTW_TX_FLAG_SPLCP;
|
|
if (wh->i_fc[1] & IEEE80211_FC1_MORE_FRAG)
|
|
flags |= URTW_TX_FLAG_MOREFRAG;
|
|
|
|
flags |= (sc->sc_currate & 0xf) << URTW_TX_FLAG_TXRATE_SHIFT;
|
|
|
|
if (sc->sc_flags & URTW_RTL8187B) {
|
|
struct urtw_8187b_txhdr *tx;
|
|
|
|
tx = (struct urtw_8187b_txhdr *)data->buf;
|
|
if (ctsenable)
|
|
flags |= URTW_TX_FLAG_CTS;
|
|
if (rtsenable) {
|
|
flags |= URTW_TX_FLAG_RTS;
|
|
flags |= (urtw_rate2rtl(11) & 0xf) <<
|
|
URTW_TX_FLAG_RTSRATE_SHIFT;
|
|
tx->rtsdur = rtsdur;
|
|
}
|
|
tx->flag = htole32(flags);
|
|
tx->txdur = txdur;
|
|
if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) ==
|
|
IEEE80211_FC0_TYPE_MGT &&
|
|
(wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) ==
|
|
IEEE80211_FC0_SUBTYPE_PROBE_RESP)
|
|
tx->retry = 1;
|
|
else
|
|
tx->retry = URTW_TX_MAXRETRY;
|
|
m_copydata(m0, 0, m0->m_pkthdr.len, (uint8_t *)(tx + 1));
|
|
} else {
|
|
struct urtw_8187l_txhdr *tx;
|
|
|
|
tx = (struct urtw_8187l_txhdr *)data->buf;
|
|
if (rtsenable) {
|
|
flags |= URTW_TX_FLAG_RTS;
|
|
tx->rtsdur = rtsdur;
|
|
}
|
|
flags |= (urtw_rate2rtl(11) & 0xf) << URTW_TX_FLAG_RTSRATE_SHIFT;
|
|
tx->flag = htole32(flags);
|
|
tx->retry = 3; /* CW minimum */
|
|
tx->retry = 7 << 4; /* CW maximum */
|
|
tx->retry = URTW_TX_MAXRETRY << 8; /* retry limitation */
|
|
m_copydata(m0, 0, m0->m_pkthdr.len, (uint8_t *)(tx + 1));
|
|
}
|
|
|
|
data->buflen = xferlen;
|
|
data->ni = ni;
|
|
data->m = m0;
|
|
|
|
if (sc->sc_flags & URTW_RTL8187B) {
|
|
switch (wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) {
|
|
case IEEE80211_FC0_TYPE_CTL:
|
|
case IEEE80211_FC0_TYPE_MGT:
|
|
xfer = sc->sc_xfer[URTW_8187B_BULK_TX_EP12];
|
|
break;
|
|
default:
|
|
KASSERT(M_WME_GETAC(m0) < URTW_8187B_TXPIPE_MAX,
|
|
("unsupported WME pipe %d", M_WME_GETAC(m0)));
|
|
xfer = rtl8187b_pipes[M_WME_GETAC(m0)];
|
|
break;
|
|
}
|
|
} else
|
|
xfer = (prior == URTW_PRIORITY_LOW) ?
|
|
sc->sc_xfer[URTW_8187L_BULK_TX_LOW] :
|
|
sc->sc_xfer[URTW_8187L_BULK_TX_NORMAL];
|
|
|
|
STAILQ_INSERT_TAIL(&sc->sc_tx_pending, data, next);
|
|
usbd_transfer_start(xfer);
|
|
|
|
error = urtw_led_ctl(sc, URTW_LED_CTL_TX);
|
|
if (error != 0)
|
|
device_printf(sc->sc_dev, "could not control LED (%d)\n",
|
|
error);
|
|
return (0);
|
|
}
|
|
|
|
static int
|
|
urtw_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
|
|
{
|
|
struct ieee80211_node *ni = vap->iv_bss;
|
|
struct ieee80211com *ic = vap->iv_ic;
|
|
struct urtw_softc *sc = ic->ic_ifp->if_softc;
|
|
struct urtw_vap *uvp = URTW_VAP(vap);
|
|
usb_error_t error = 0;
|
|
|
|
DPRINTF(sc, URTW_DEBUG_STATE, "%s: %s -> %s\n", __func__,
|
|
ieee80211_state_name[vap->iv_state],
|
|
ieee80211_state_name[nstate]);
|
|
|
|
sc->sc_state = nstate;
|
|
|
|
IEEE80211_UNLOCK(ic);
|
|
URTW_LOCK(sc);
|
|
usb_callout_stop(&sc->sc_led_ch);
|
|
callout_stop(&sc->sc_watchdog_ch);
|
|
|
|
switch (nstate) {
|
|
case IEEE80211_S_INIT:
|
|
case IEEE80211_S_SCAN:
|
|
case IEEE80211_S_AUTH:
|
|
case IEEE80211_S_ASSOC:
|
|
break;
|
|
case IEEE80211_S_RUN:
|
|
/* setting bssid. */
|
|
urtw_write32_m(sc, URTW_BSSID, ((uint32_t *)ni->ni_bssid)[0]);
|
|
urtw_write16_m(sc, URTW_BSSID + 4,
|
|
((uint16_t *)ni->ni_bssid)[2]);
|
|
urtw_update_msr(sc);
|
|
/* XXX maybe the below would be incorrect. */
|
|
urtw_write16_m(sc, URTW_ATIM_WND, 2);
|
|
urtw_write16_m(sc, URTW_ATIM_TR_ITV, 100);
|
|
urtw_write16_m(sc, URTW_BEACON_INTERVAL, 0x64);
|
|
urtw_write16_m(sc, URTW_BEACON_INTERVAL_TIME, 100);
|
|
error = urtw_led_ctl(sc, URTW_LED_CTL_LINK);
|
|
if (error != 0)
|
|
device_printf(sc->sc_dev,
|
|
"could not control LED (%d)\n", error);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
fail:
|
|
URTW_UNLOCK(sc);
|
|
IEEE80211_LOCK(ic);
|
|
return (uvp->newstate(vap, nstate, arg));
|
|
}
|
|
|
|
static void
|
|
urtw_watchdog(void *arg)
|
|
{
|
|
struct urtw_softc *sc = arg;
|
|
struct ifnet *ifp = sc->sc_ifp;
|
|
|
|
if (sc->sc_txtimer > 0) {
|
|
if (--sc->sc_txtimer == 0) {
|
|
device_printf(sc->sc_dev, "device timeout\n");
|
|
ifp->if_oerrors++;
|
|
return;
|
|
}
|
|
callout_reset(&sc->sc_watchdog_ch, hz, urtw_watchdog, sc);
|
|
}
|
|
}
|
|
|
|
static void
|
|
urtw_set_multi(void *arg)
|
|
{
|
|
struct urtw_softc *sc = arg;
|
|
struct ifnet *ifp = sc->sc_ifp;
|
|
|
|
if (!(ifp->if_flags & IFF_UP))
|
|
return;
|
|
|
|
/*
|
|
* XXX don't know how to set a device. Lack of docs. Just try to set
|
|
* IFF_ALLMULTI flag here.
|
|
*/
|
|
ifp->if_flags |= IFF_ALLMULTI;
|
|
}
|
|
|
|
static usb_error_t
|
|
urtw_set_rate(struct urtw_softc *sc)
|
|
{
|
|
int i, basic_rate, min_rr_rate, max_rr_rate;
|
|
uint16_t data;
|
|
usb_error_t error;
|
|
|
|
basic_rate = urtw_rate2rtl(48);
|
|
min_rr_rate = urtw_rate2rtl(12);
|
|
max_rr_rate = urtw_rate2rtl(48);
|
|
|
|
urtw_write8_m(sc, URTW_RESP_RATE,
|
|
max_rr_rate << URTW_RESP_MAX_RATE_SHIFT |
|
|
min_rr_rate << URTW_RESP_MIN_RATE_SHIFT);
|
|
|
|
urtw_read16_m(sc, URTW_BRSR, &data);
|
|
data &= ~URTW_BRSR_MBR_8185;
|
|
|
|
for (i = 0; i <= basic_rate; i++)
|
|
data |= (1 << i);
|
|
|
|
urtw_write16_m(sc, URTW_BRSR, data);
|
|
fail:
|
|
return (error);
|
|
}
|
|
|
|
static uint16_t
|
|
urtw_rate2rtl(int rate)
|
|
{
|
|
#define N(a) (sizeof(a) / sizeof((a)[0]))
|
|
int i;
|
|
|
|
for (i = 0; i < N(urtw_ratetable); i++) {
|
|
if (rate == urtw_ratetable[i].reg)
|
|
return urtw_ratetable[i].val;
|
|
}
|
|
|
|
return (3);
|
|
#undef N
|
|
}
|
|
|
|
static uint16_t
|
|
urtw_rtl2rate(int rate)
|
|
{
|
|
#define N(a) (sizeof(a) / sizeof((a)[0]))
|
|
int i;
|
|
|
|
for (i = 0; i < N(urtw_ratetable); i++) {
|
|
if (rate == urtw_ratetable[i].val)
|
|
return urtw_ratetable[i].reg;
|
|
}
|
|
|
|
return (0);
|
|
#undef N
|
|
}
|
|
|
|
static usb_error_t
|
|
urtw_update_msr(struct urtw_softc *sc)
|
|
{
|
|
struct ifnet *ifp = sc->sc_ifp;
|
|
struct ieee80211com *ic = ifp->if_l2com;
|
|
uint8_t data;
|
|
usb_error_t error;
|
|
|
|
urtw_read8_m(sc, URTW_MSR, &data);
|
|
data &= ~URTW_MSR_LINK_MASK;
|
|
|
|
if (sc->sc_state == IEEE80211_S_RUN) {
|
|
switch (ic->ic_opmode) {
|
|
case IEEE80211_M_STA:
|
|
case IEEE80211_M_MONITOR:
|
|
data |= URTW_MSR_LINK_STA;
|
|
if (sc->sc_flags & URTW_RTL8187B)
|
|
data |= URTW_MSR_LINK_ENEDCA;
|
|
break;
|
|
case IEEE80211_M_IBSS:
|
|
data |= URTW_MSR_LINK_ADHOC;
|
|
break;
|
|
case IEEE80211_M_HOSTAP:
|
|
data |= URTW_MSR_LINK_HOSTAP;
|
|
break;
|
|
default:
|
|
panic("unsupported operation mode 0x%x\n",
|
|
ic->ic_opmode);
|
|
/* never reach */
|
|
}
|
|
} else
|
|
data |= URTW_MSR_LINK_NONE;
|
|
|
|
urtw_write8_m(sc, URTW_MSR, data);
|
|
fail:
|
|
return (error);
|
|
}
|
|
|
|
static usb_error_t
|
|
urtw_read8_c(struct urtw_softc *sc, int val, uint8_t *data)
|
|
{
|
|
struct usb_device_request req;
|
|
usb_error_t error;
|
|
|
|
URTW_ASSERT_LOCKED(sc);
|
|
|
|
req.bmRequestType = UT_READ_VENDOR_DEVICE;
|
|
req.bRequest = URTW_8187_GETREGS_REQ;
|
|
USETW(req.wValue, (val & 0xff) | 0xff00);
|
|
USETW(req.wIndex, (val >> 8) & 0x3);
|
|
USETW(req.wLength, sizeof(uint8_t));
|
|
|
|
error = urtw_do_request(sc, &req, data);
|
|
return (error);
|
|
}
|
|
|
|
static usb_error_t
|
|
urtw_read16_c(struct urtw_softc *sc, int val, uint16_t *data)
|
|
{
|
|
struct usb_device_request req;
|
|
usb_error_t error;
|
|
|
|
URTW_ASSERT_LOCKED(sc);
|
|
|
|
req.bmRequestType = UT_READ_VENDOR_DEVICE;
|
|
req.bRequest = URTW_8187_GETREGS_REQ;
|
|
USETW(req.wValue, (val & 0xff) | 0xff00);
|
|
USETW(req.wIndex, (val >> 8) & 0x3);
|
|
USETW(req.wLength, sizeof(uint16_t));
|
|
|
|
error = urtw_do_request(sc, &req, data);
|
|
return (error);
|
|
}
|
|
|
|
static usb_error_t
|
|
urtw_read32_c(struct urtw_softc *sc, int val, uint32_t *data)
|
|
{
|
|
struct usb_device_request req;
|
|
usb_error_t error;
|
|
|
|
URTW_ASSERT_LOCKED(sc);
|
|
|
|
req.bmRequestType = UT_READ_VENDOR_DEVICE;
|
|
req.bRequest = URTW_8187_GETREGS_REQ;
|
|
USETW(req.wValue, (val & 0xff) | 0xff00);
|
|
USETW(req.wIndex, (val >> 8) & 0x3);
|
|
USETW(req.wLength, sizeof(uint32_t));
|
|
|
|
error = urtw_do_request(sc, &req, data);
|
|
return (error);
|
|
}
|
|
|
|
static usb_error_t
|
|
urtw_write8_c(struct urtw_softc *sc, int val, uint8_t data)
|
|
{
|
|
struct usb_device_request req;
|
|
|
|
URTW_ASSERT_LOCKED(sc);
|
|
|
|
req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
|
|
req.bRequest = URTW_8187_SETREGS_REQ;
|
|
USETW(req.wValue, (val & 0xff) | 0xff00);
|
|
USETW(req.wIndex, (val >> 8) & 0x3);
|
|
USETW(req.wLength, sizeof(uint8_t));
|
|
|
|
return (urtw_do_request(sc, &req, &data));
|
|
}
|
|
|
|
static usb_error_t
|
|
urtw_write16_c(struct urtw_softc *sc, int val, uint16_t data)
|
|
{
|
|
struct usb_device_request req;
|
|
|
|
URTW_ASSERT_LOCKED(sc);
|
|
|
|
req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
|
|
req.bRequest = URTW_8187_SETREGS_REQ;
|
|
USETW(req.wValue, (val & 0xff) | 0xff00);
|
|
USETW(req.wIndex, (val >> 8) & 0x3);
|
|
USETW(req.wLength, sizeof(uint16_t));
|
|
|
|
return (urtw_do_request(sc, &req, &data));
|
|
}
|
|
|
|
static usb_error_t
|
|
urtw_write32_c(struct urtw_softc *sc, int val, uint32_t data)
|
|
{
|
|
struct usb_device_request req;
|
|
|
|
URTW_ASSERT_LOCKED(sc);
|
|
|
|
req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
|
|
req.bRequest = URTW_8187_SETREGS_REQ;
|
|
USETW(req.wValue, (val & 0xff) | 0xff00);
|
|
USETW(req.wIndex, (val >> 8) & 0x3);
|
|
USETW(req.wLength, sizeof(uint32_t));
|
|
|
|
return (urtw_do_request(sc, &req, &data));
|
|
}
|
|
|
|
static usb_error_t
|
|
urtw_get_macaddr(struct urtw_softc *sc)
|
|
{
|
|
uint32_t data;
|
|
usb_error_t error;
|
|
|
|
error = urtw_eprom_read32(sc, URTW_EPROM_MACADDR, &data);
|
|
if (error != 0)
|
|
goto fail;
|
|
sc->sc_bssid[0] = data & 0xff;
|
|
sc->sc_bssid[1] = (data & 0xff00) >> 8;
|
|
error = urtw_eprom_read32(sc, URTW_EPROM_MACADDR + 1, &data);
|
|
if (error != 0)
|
|
goto fail;
|
|
sc->sc_bssid[2] = data & 0xff;
|
|
sc->sc_bssid[3] = (data & 0xff00) >> 8;
|
|
error = urtw_eprom_read32(sc, URTW_EPROM_MACADDR + 2, &data);
|
|
if (error != 0)
|
|
goto fail;
|
|
sc->sc_bssid[4] = data & 0xff;
|
|
sc->sc_bssid[5] = (data & 0xff00) >> 8;
|
|
fail:
|
|
return (error);
|
|
}
|
|
|
|
static usb_error_t
|
|
urtw_eprom_read32(struct urtw_softc *sc, uint32_t addr, uint32_t *data)
|
|
{
|
|
#define URTW_READCMD_LEN 3
|
|
int addrlen, i;
|
|
int16_t addrstr[8], data16, readcmd[] = { 1, 1, 0 };
|
|
usb_error_t error;
|
|
|
|
/* NB: make sure the buffer is initialized */
|
|
*data = 0;
|
|
|
|
/* enable EPROM programming */
|
|
urtw_write8_m(sc, URTW_EPROM_CMD, URTW_EPROM_CMD_PROGRAM_MODE);
|
|
DELAY(URTW_EPROM_DELAY);
|
|
|
|
error = urtw_eprom_cs(sc, URTW_EPROM_ENABLE);
|
|
if (error != 0)
|
|
goto fail;
|
|
error = urtw_eprom_ck(sc);
|
|
if (error != 0)
|
|
goto fail;
|
|
error = urtw_eprom_sendbits(sc, readcmd, URTW_READCMD_LEN);
|
|
if (error != 0)
|
|
goto fail;
|
|
if (sc->sc_epromtype == URTW_EEPROM_93C56) {
|
|
addrlen = 8;
|
|
addrstr[0] = addr & (1 << 7);
|
|
addrstr[1] = addr & (1 << 6);
|
|
addrstr[2] = addr & (1 << 5);
|
|
addrstr[3] = addr & (1 << 4);
|
|
addrstr[4] = addr & (1 << 3);
|
|
addrstr[5] = addr & (1 << 2);
|
|
addrstr[6] = addr & (1 << 1);
|
|
addrstr[7] = addr & (1 << 0);
|
|
} else {
|
|
addrlen=6;
|
|
addrstr[0] = addr & (1 << 5);
|
|
addrstr[1] = addr & (1 << 4);
|
|
addrstr[2] = addr & (1 << 3);
|
|
addrstr[3] = addr & (1 << 2);
|
|
addrstr[4] = addr & (1 << 1);
|
|
addrstr[5] = addr & (1 << 0);
|
|
}
|
|
error = urtw_eprom_sendbits(sc, addrstr, addrlen);
|
|
if (error != 0)
|
|
goto fail;
|
|
|
|
error = urtw_eprom_writebit(sc, 0);
|
|
if (error != 0)
|
|
goto fail;
|
|
|
|
for (i = 0; i < 16; i++) {
|
|
error = urtw_eprom_ck(sc);
|
|
if (error != 0)
|
|
goto fail;
|
|
error = urtw_eprom_readbit(sc, &data16);
|
|
if (error != 0)
|
|
goto fail;
|
|
|
|
(*data) |= (data16 << (15 - i));
|
|
}
|
|
|
|
error = urtw_eprom_cs(sc, URTW_EPROM_DISABLE);
|
|
if (error != 0)
|
|
goto fail;
|
|
error = urtw_eprom_ck(sc);
|
|
if (error != 0)
|
|
goto fail;
|
|
|
|
/* now disable EPROM programming */
|
|
urtw_write8_m(sc, URTW_EPROM_CMD, URTW_EPROM_CMD_NORMAL_MODE);
|
|
fail:
|
|
return (error);
|
|
#undef URTW_READCMD_LEN
|
|
}
|
|
|
|
static usb_error_t
|
|
urtw_eprom_cs(struct urtw_softc *sc, int able)
|
|
{
|
|
uint8_t data;
|
|
usb_error_t error;
|
|
|
|
urtw_read8_m(sc, URTW_EPROM_CMD, &data);
|
|
if (able == URTW_EPROM_ENABLE)
|
|
urtw_write8_m(sc, URTW_EPROM_CMD, data | URTW_EPROM_CS);
|
|
else
|
|
urtw_write8_m(sc, URTW_EPROM_CMD, data & ~URTW_EPROM_CS);
|
|
DELAY(URTW_EPROM_DELAY);
|
|
fail:
|
|
return (error);
|
|
}
|
|
|
|
static usb_error_t
|
|
urtw_eprom_ck(struct urtw_softc *sc)
|
|
{
|
|
uint8_t data;
|
|
usb_error_t error;
|
|
|
|
/* masking */
|
|
urtw_read8_m(sc, URTW_EPROM_CMD, &data);
|
|
urtw_write8_m(sc, URTW_EPROM_CMD, data | URTW_EPROM_CK);
|
|
DELAY(URTW_EPROM_DELAY);
|
|
/* unmasking */
|
|
urtw_read8_m(sc, URTW_EPROM_CMD, &data);
|
|
urtw_write8_m(sc, URTW_EPROM_CMD, data & ~URTW_EPROM_CK);
|
|
DELAY(URTW_EPROM_DELAY);
|
|
fail:
|
|
return (error);
|
|
}
|
|
|
|
static usb_error_t
|
|
urtw_eprom_readbit(struct urtw_softc *sc, int16_t *data)
|
|
{
|
|
uint8_t data8;
|
|
usb_error_t error;
|
|
|
|
urtw_read8_m(sc, URTW_EPROM_CMD, &data8);
|
|
*data = (data8 & URTW_EPROM_READBIT) ? 1 : 0;
|
|
DELAY(URTW_EPROM_DELAY);
|
|
|
|
fail:
|
|
return (error);
|
|
}
|
|
|
|
static usb_error_t
|
|
urtw_eprom_writebit(struct urtw_softc *sc, int16_t bit)
|
|
{
|
|
uint8_t data;
|
|
usb_error_t error;
|
|
|
|
urtw_read8_m(sc, URTW_EPROM_CMD, &data);
|
|
if (bit != 0)
|
|
urtw_write8_m(sc, URTW_EPROM_CMD, data | URTW_EPROM_WRITEBIT);
|
|
else
|
|
urtw_write8_m(sc, URTW_EPROM_CMD, data & ~URTW_EPROM_WRITEBIT);
|
|
DELAY(URTW_EPROM_DELAY);
|
|
fail:
|
|
return (error);
|
|
}
|
|
|
|
static usb_error_t
|
|
urtw_eprom_sendbits(struct urtw_softc *sc, int16_t *buf, int buflen)
|
|
{
|
|
int i = 0;
|
|
usb_error_t error = 0;
|
|
|
|
for (i = 0; i < buflen; i++) {
|
|
error = urtw_eprom_writebit(sc, buf[i]);
|
|
if (error != 0)
|
|
goto fail;
|
|
error = urtw_eprom_ck(sc);
|
|
if (error != 0)
|
|
goto fail;
|
|
}
|
|
fail:
|
|
return (error);
|
|
}
|
|
|
|
|
|
static usb_error_t
|
|
urtw_get_txpwr(struct urtw_softc *sc)
|
|
{
|
|
int i, j;
|
|
uint32_t data;
|
|
usb_error_t error;
|
|
|
|
error = urtw_eprom_read32(sc, URTW_EPROM_TXPW_BASE, &data);
|
|
if (error != 0)
|
|
goto fail;
|
|
sc->sc_txpwr_cck_base = data & 0xf;
|
|
sc->sc_txpwr_ofdm_base = (data >> 4) & 0xf;
|
|
|
|
for (i = 1, j = 0; i < 6; i += 2, j++) {
|
|
error = urtw_eprom_read32(sc, URTW_EPROM_TXPW0 + j, &data);
|
|
if (error != 0)
|
|
goto fail;
|
|
sc->sc_txpwr_cck[i] = data & 0xf;
|
|
sc->sc_txpwr_cck[i + 1] = (data & 0xf00) >> 8;
|
|
sc->sc_txpwr_ofdm[i] = (data & 0xf0) >> 4;
|
|
sc->sc_txpwr_ofdm[i + 1] = (data & 0xf000) >> 12;
|
|
}
|
|
for (i = 1, j = 0; i < 4; i += 2, j++) {
|
|
error = urtw_eprom_read32(sc, URTW_EPROM_TXPW1 + j, &data);
|
|
if (error != 0)
|
|
goto fail;
|
|
sc->sc_txpwr_cck[i + 6] = data & 0xf;
|
|
sc->sc_txpwr_cck[i + 6 + 1] = (data & 0xf00) >> 8;
|
|
sc->sc_txpwr_ofdm[i + 6] = (data & 0xf0) >> 4;
|
|
sc->sc_txpwr_ofdm[i + 6 + 1] = (data & 0xf000) >> 12;
|
|
}
|
|
if (sc->sc_flags & URTW_RTL8187B) {
|
|
error = urtw_eprom_read32(sc, URTW_EPROM_TXPW2, &data);
|
|
if (error != 0)
|
|
goto fail;
|
|
sc->sc_txpwr_cck[1 + 6 + 4] = data & 0xf;
|
|
sc->sc_txpwr_ofdm[1 + 6 + 4] = (data & 0xf0) >> 4;
|
|
error = urtw_eprom_read32(sc, 0x0a, &data);
|
|
if (error != 0)
|
|
goto fail;
|
|
sc->sc_txpwr_cck[2 + 6 + 4] = data & 0xf;
|
|
sc->sc_txpwr_ofdm[2 + 6 + 4] = (data & 0xf0) >> 4;
|
|
error = urtw_eprom_read32(sc, 0x1c, &data);
|
|
if (error != 0)
|
|
goto fail;
|
|
sc->sc_txpwr_cck[3 + 6 + 4] = data & 0xf;
|
|
sc->sc_txpwr_cck[3 + 6 + 4 + 1] = (data & 0xf00) >> 8;
|
|
sc->sc_txpwr_ofdm[3 + 6 + 4] = (data & 0xf0) >> 4;
|
|
sc->sc_txpwr_ofdm[3 + 6 + 4 + 1] = (data & 0xf000) >> 12;
|
|
} else {
|
|
for (i = 1, j = 0; i < 4; i += 2, j++) {
|
|
error = urtw_eprom_read32(sc, URTW_EPROM_TXPW2 + j,
|
|
&data);
|
|
if (error != 0)
|
|
goto fail;
|
|
sc->sc_txpwr_cck[i + 6 + 4] = data & 0xf;
|
|
sc->sc_txpwr_cck[i + 6 + 4 + 1] = (data & 0xf00) >> 8;
|
|
sc->sc_txpwr_ofdm[i + 6 + 4] = (data & 0xf0) >> 4;
|
|
sc->sc_txpwr_ofdm[i + 6 + 4 + 1] = (data & 0xf000) >> 12;
|
|
}
|
|
}
|
|
fail:
|
|
return (error);
|
|
}
|
|
|
|
|
|
static usb_error_t
|
|
urtw_get_rfchip(struct urtw_softc *sc)
|
|
{
|
|
int ret;
|
|
uint8_t data8;
|
|
uint32_t data;
|
|
usb_error_t error;
|
|
|
|
if (sc->sc_flags & URTW_RTL8187B) {
|
|
urtw_read8_m(sc, 0xe1, &data8);
|
|
switch (data8) {
|
|
case 0:
|
|
sc->sc_flags |= URTW_RTL8187B_REV_B;
|
|
break;
|
|
case 1:
|
|
sc->sc_flags |= URTW_RTL8187B_REV_D;
|
|
break;
|
|
case 2:
|
|
sc->sc_flags |= URTW_RTL8187B_REV_E;
|
|
break;
|
|
default:
|
|
device_printf(sc->sc_dev, "unknown type: %#x\n", data8);
|
|
sc->sc_flags |= URTW_RTL8187B_REV_B;
|
|
break;
|
|
}
|
|
} else {
|
|
urtw_read32_m(sc, URTW_TX_CONF, &data);
|
|
switch (data & URTW_TX_HWMASK) {
|
|
case URTW_TX_R8187vD_B:
|
|
sc->sc_flags |= URTW_RTL8187B;
|
|
break;
|
|
case URTW_TX_R8187vD:
|
|
break;
|
|
default:
|
|
device_printf(sc->sc_dev, "unknown RTL8187L type: %#x\n",
|
|
data & URTW_TX_HWMASK);
|
|
break;
|
|
}
|
|
}
|
|
|
|
error = urtw_eprom_read32(sc, URTW_EPROM_RFCHIPID, &data);
|
|
if (error != 0)
|
|
goto fail;
|
|
switch (data & 0xff) {
|
|
case URTW_EPROM_RFCHIPID_RTL8225U:
|
|
error = urtw_8225_isv2(sc, &ret);
|
|
if (error != 0)
|
|
goto fail;
|
|
if (ret == 0) {
|
|
sc->sc_rf_init = urtw_8225_rf_init;
|
|
sc->sc_rf_set_sens = urtw_8225_rf_set_sens;
|
|
sc->sc_rf_set_chan = urtw_8225_rf_set_chan;
|
|
sc->sc_rf_stop = urtw_8225_rf_stop;
|
|
} else {
|
|
sc->sc_rf_init = urtw_8225v2_rf_init;
|
|
sc->sc_rf_set_chan = urtw_8225v2_rf_set_chan;
|
|
sc->sc_rf_stop = urtw_8225_rf_stop;
|
|
}
|
|
sc->sc_max_sens = URTW_8225_RF_MAX_SENS;
|
|
sc->sc_sens = URTW_8225_RF_DEF_SENS;
|
|
break;
|
|
case URTW_EPROM_RFCHIPID_RTL8225Z2:
|
|
sc->sc_rf_init = urtw_8225v2b_rf_init;
|
|
sc->sc_rf_set_chan = urtw_8225v2b_rf_set_chan;
|
|
sc->sc_max_sens = URTW_8225_RF_MAX_SENS;
|
|
sc->sc_sens = URTW_8225_RF_DEF_SENS;
|
|
sc->sc_rf_stop = urtw_8225_rf_stop;
|
|
break;
|
|
default:
|
|
panic("unsupported RF chip %d\n", data & 0xff);
|
|
/* never reach */
|
|
}
|
|
|
|
device_printf(sc->sc_dev, "%s rf %s hwrev %s\n",
|
|
(sc->sc_flags & URTW_RTL8187B) ? "rtl8187b" : "rtl8187l",
|
|
((data & 0xff) == URTW_EPROM_RFCHIPID_RTL8225U) ? "rtl8225u" :
|
|
"rtl8225z2",
|
|
(sc->sc_flags & URTW_RTL8187B) ? ((data8 == 0) ? "b" :
|
|
(data8 == 1) ? "d" : "e") : "none");
|
|
|
|
fail:
|
|
return (error);
|
|
}
|
|
|
|
|
|
static usb_error_t
|
|
urtw_led_init(struct urtw_softc *sc)
|
|
{
|
|
uint32_t rev;
|
|
usb_error_t error;
|
|
|
|
urtw_read8_m(sc, URTW_PSR, &sc->sc_psr);
|
|
error = urtw_eprom_read32(sc, URTW_EPROM_SWREV, &rev);
|
|
if (error != 0)
|
|
goto fail;
|
|
|
|
switch (rev & URTW_EPROM_CID_MASK) {
|
|
case URTW_EPROM_CID_ALPHA0:
|
|
sc->sc_strategy = URTW_SW_LED_MODE1;
|
|
break;
|
|
case URTW_EPROM_CID_SERCOMM_PS:
|
|
sc->sc_strategy = URTW_SW_LED_MODE3;
|
|
break;
|
|
case URTW_EPROM_CID_HW_LED:
|
|
sc->sc_strategy = URTW_HW_LED;
|
|
break;
|
|
case URTW_EPROM_CID_RSVD0:
|
|
case URTW_EPROM_CID_RSVD1:
|
|
default:
|
|
sc->sc_strategy = URTW_SW_LED_MODE0;
|
|
break;
|
|
}
|
|
|
|
sc->sc_gpio_ledpin = URTW_LED_PIN_GPIO0;
|
|
|
|
fail:
|
|
return (error);
|
|
}
|
|
|
|
|
|
static usb_error_t
|
|
urtw_8225_rf_init(struct urtw_softc *sc)
|
|
{
|
|
#define N(a) (sizeof(a) / sizeof((a)[0]))
|
|
int i;
|
|
uint16_t data;
|
|
usb_error_t error;
|
|
|
|
error = urtw_8180_set_anaparam(sc, URTW_8225_ANAPARAM_ON);
|
|
if (error)
|
|
goto fail;
|
|
|
|
error = urtw_8225_usb_init(sc);
|
|
if (error)
|
|
goto fail;
|
|
|
|
urtw_write32_m(sc, URTW_RF_TIMING, 0x000a8008);
|
|
urtw_read16_m(sc, URTW_BRSR, &data); /* XXX ??? */
|
|
urtw_write16_m(sc, URTW_BRSR, 0xffff);
|
|
urtw_write32_m(sc, URTW_RF_PARA, 0x100044);
|
|
|
|
error = urtw_set_mode(sc, URTW_EPROM_CMD_CONFIG);
|
|
if (error)
|
|
goto fail;
|
|
urtw_write8_m(sc, URTW_CONFIG3, 0x44);
|
|
error = urtw_set_mode(sc, URTW_EPROM_CMD_NORMAL);
|
|
if (error)
|
|
goto fail;
|
|
|
|
error = urtw_8185_rf_pins_enable(sc);
|
|
if (error)
|
|
goto fail;
|
|
usb_pause_mtx(&sc->sc_mtx, 1000);
|
|
|
|
for (i = 0; i < N(urtw_8225_rf_part1); i++) {
|
|
urtw_8225_write(sc, urtw_8225_rf_part1[i].reg,
|
|
urtw_8225_rf_part1[i].val);
|
|
usb_pause_mtx(&sc->sc_mtx, 1);
|
|
}
|
|
usb_pause_mtx(&sc->sc_mtx, 100);
|
|
urtw_8225_write(sc,
|
|
URTW_8225_ADDR_2_MAGIC, URTW_8225_ADDR_2_DATA_MAGIC1);
|
|
usb_pause_mtx(&sc->sc_mtx, 200);
|
|
urtw_8225_write(sc,
|
|
URTW_8225_ADDR_2_MAGIC, URTW_8225_ADDR_2_DATA_MAGIC2);
|
|
usb_pause_mtx(&sc->sc_mtx, 200);
|
|
urtw_8225_write(sc,
|
|
URTW_8225_ADDR_0_MAGIC, URTW_8225_ADDR_0_DATA_MAGIC3);
|
|
|
|
for (i = 0; i < 95; i++) {
|
|
urtw_8225_write(sc, URTW_8225_ADDR_1_MAGIC, (uint8_t)(i + 1));
|
|
urtw_8225_write(sc, URTW_8225_ADDR_2_MAGIC, urtw_8225_rxgain[i]);
|
|
}
|
|
|
|
urtw_8225_write(sc,
|
|
URTW_8225_ADDR_0_MAGIC, URTW_8225_ADDR_0_DATA_MAGIC4);
|
|
urtw_8225_write(sc,
|
|
URTW_8225_ADDR_0_MAGIC, URTW_8225_ADDR_0_DATA_MAGIC5);
|
|
|
|
for (i = 0; i < 128; i++) {
|
|
urtw_8187_write_phy_ofdm(sc, 0xb, urtw_8225_agc[i]);
|
|
usb_pause_mtx(&sc->sc_mtx, 1);
|
|
urtw_8187_write_phy_ofdm(sc, 0xa, (uint8_t)i + 0x80);
|
|
usb_pause_mtx(&sc->sc_mtx, 1);
|
|
}
|
|
|
|
for (i = 0; i < N(urtw_8225_rf_part2); i++) {
|
|
urtw_8187_write_phy_ofdm(sc, urtw_8225_rf_part2[i].reg,
|
|
urtw_8225_rf_part2[i].val);
|
|
usb_pause_mtx(&sc->sc_mtx, 1);
|
|
}
|
|
|
|
error = urtw_8225_setgain(sc, 4);
|
|
if (error)
|
|
goto fail;
|
|
|
|
for (i = 0; i < N(urtw_8225_rf_part3); i++) {
|
|
urtw_8187_write_phy_cck(sc, urtw_8225_rf_part3[i].reg,
|
|
urtw_8225_rf_part3[i].val);
|
|
usb_pause_mtx(&sc->sc_mtx, 1);
|
|
}
|
|
|
|
urtw_write8_m(sc, URTW_TESTR, 0x0d);
|
|
|
|
error = urtw_8225_set_txpwrlvl(sc, 1);
|
|
if (error)
|
|
goto fail;
|
|
|
|
urtw_8187_write_phy_cck(sc, 0x10, 0x9b);
|
|
usb_pause_mtx(&sc->sc_mtx, 1);
|
|
urtw_8187_write_phy_ofdm(sc, 0x26, 0x90);
|
|
usb_pause_mtx(&sc->sc_mtx, 1);
|
|
|
|
/* TX ant A, 0x0 for B */
|
|
error = urtw_8185_tx_antenna(sc, 0x3);
|
|
if (error)
|
|
goto fail;
|
|
urtw_write32_m(sc, URTW_HSSI_PARA, 0x3dc00002);
|
|
|
|
error = urtw_8225_rf_set_chan(sc, 1);
|
|
fail:
|
|
return (error);
|
|
#undef N
|
|
}
|
|
|
|
static usb_error_t
|
|
urtw_8185_rf_pins_enable(struct urtw_softc *sc)
|
|
{
|
|
usb_error_t error = 0;
|
|
|
|
urtw_write16_m(sc, URTW_RF_PINS_ENABLE, 0x1ff7);
|
|
fail:
|
|
return (error);
|
|
}
|
|
|
|
static usb_error_t
|
|
urtw_8185_tx_antenna(struct urtw_softc *sc, uint8_t ant)
|
|
{
|
|
usb_error_t error;
|
|
|
|
urtw_write8_m(sc, URTW_TX_ANTENNA, ant);
|
|
usb_pause_mtx(&sc->sc_mtx, 1);
|
|
fail:
|
|
return (error);
|
|
}
|
|
|
|
static usb_error_t
|
|
urtw_8187_write_phy_ofdm_c(struct urtw_softc *sc, uint8_t addr, uint32_t data)
|
|
{
|
|
|
|
data = data & 0xff;
|
|
return urtw_8187_write_phy(sc, addr, data);
|
|
}
|
|
|
|
static usb_error_t
|
|
urtw_8187_write_phy_cck_c(struct urtw_softc *sc, uint8_t addr, uint32_t data)
|
|
{
|
|
|
|
data = data & 0xff;
|
|
return urtw_8187_write_phy(sc, addr, data | 0x10000);
|
|
}
|
|
|
|
static usb_error_t
|
|
urtw_8187_write_phy(struct urtw_softc *sc, uint8_t addr, uint32_t data)
|
|
{
|
|
uint32_t phyw;
|
|
usb_error_t error;
|
|
|
|
phyw = ((data << 8) | (addr | 0x80));
|
|
urtw_write8_m(sc, URTW_PHY_MAGIC4, ((phyw & 0xff000000) >> 24));
|
|
urtw_write8_m(sc, URTW_PHY_MAGIC3, ((phyw & 0x00ff0000) >> 16));
|
|
urtw_write8_m(sc, URTW_PHY_MAGIC2, ((phyw & 0x0000ff00) >> 8));
|
|
urtw_write8_m(sc, URTW_PHY_MAGIC1, ((phyw & 0x000000ff)));
|
|
usb_pause_mtx(&sc->sc_mtx, 1);
|
|
fail:
|
|
return (error);
|
|
}
|
|
|
|
static usb_error_t
|
|
urtw_8225_setgain(struct urtw_softc *sc, int16_t gain)
|
|
{
|
|
usb_error_t error;
|
|
|
|
urtw_8187_write_phy_ofdm(sc, 0x0d, urtw_8225_gain[gain * 4]);
|
|
urtw_8187_write_phy_ofdm(sc, 0x1b, urtw_8225_gain[gain * 4 + 2]);
|
|
urtw_8187_write_phy_ofdm(sc, 0x1d, urtw_8225_gain[gain * 4 + 3]);
|
|
urtw_8187_write_phy_ofdm(sc, 0x23, urtw_8225_gain[gain * 4 + 1]);
|
|
fail:
|
|
return (error);
|
|
}
|
|
|
|
static usb_error_t
|
|
urtw_8225_usb_init(struct urtw_softc *sc)
|
|
{
|
|
uint8_t data;
|
|
usb_error_t error;
|
|
|
|
urtw_write8_m(sc, URTW_RF_PINS_SELECT + 1, 0);
|
|
urtw_write8_m(sc, URTW_GPIO, 0);
|
|
error = urtw_read8e(sc, 0x53, &data);
|
|
if (error)
|
|
goto fail;
|
|
error = urtw_write8e(sc, 0x53, data | (1 << 7));
|
|
if (error)
|
|
goto fail;
|
|
urtw_write8_m(sc, URTW_RF_PINS_SELECT + 1, 4);
|
|
urtw_write8_m(sc, URTW_GPIO, 0x20);
|
|
urtw_write8_m(sc, URTW_GP_ENABLE, 0);
|
|
|
|
urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, 0x80);
|
|
urtw_write16_m(sc, URTW_RF_PINS_SELECT, 0x80);
|
|
urtw_write16_m(sc, URTW_RF_PINS_ENABLE, 0x80);
|
|
|
|
usb_pause_mtx(&sc->sc_mtx, 500);
|
|
fail:
|
|
return (error);
|
|
}
|
|
|
|
static usb_error_t
|
|
urtw_8225_write_c(struct urtw_softc *sc, uint8_t addr, uint16_t data)
|
|
{
|
|
uint16_t d80, d82, d84;
|
|
usb_error_t error;
|
|
|
|
urtw_read16_m(sc, URTW_RF_PINS_OUTPUT, &d80);
|
|
d80 &= URTW_RF_PINS_MAGIC1;
|
|
urtw_read16_m(sc, URTW_RF_PINS_ENABLE, &d82);
|
|
urtw_read16_m(sc, URTW_RF_PINS_SELECT, &d84);
|
|
d84 &= URTW_RF_PINS_MAGIC2;
|
|
urtw_write16_m(sc, URTW_RF_PINS_ENABLE, d82 | URTW_RF_PINS_MAGIC3);
|
|
urtw_write16_m(sc, URTW_RF_PINS_SELECT, d84 | URTW_RF_PINS_MAGIC3);
|
|
DELAY(10);
|
|
|
|
urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, d80 | URTW_BB_HOST_BANG_EN);
|
|
DELAY(2);
|
|
urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, d80);
|
|
DELAY(10);
|
|
|
|
error = urtw_8225_write_s16(sc, addr, 0x8225, &data);
|
|
if (error != 0)
|
|
goto fail;
|
|
|
|
urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, d80 | URTW_BB_HOST_BANG_EN);
|
|
DELAY(10);
|
|
urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, d80 | URTW_BB_HOST_BANG_EN);
|
|
urtw_write16_m(sc, URTW_RF_PINS_SELECT, d84);
|
|
usb_pause_mtx(&sc->sc_mtx, 2);
|
|
fail:
|
|
return (error);
|
|
}
|
|
|
|
/* XXX why we should allocalte memory buffer instead of using memory stack? */
|
|
static usb_error_t
|
|
urtw_8225_write_s16(struct urtw_softc *sc, uint8_t addr, int index,
|
|
uint16_t *data)
|
|
{
|
|
uint8_t *buf;
|
|
uint16_t data16;
|
|
struct usb_device_request *req;
|
|
usb_error_t error = 0;
|
|
|
|
data16 = *data;
|
|
req = (usb_device_request_t *)malloc(sizeof(usb_device_request_t),
|
|
M_80211_VAP, M_NOWAIT | M_ZERO);
|
|
if (req == NULL) {
|
|
device_printf(sc->sc_dev, "could not allocate a memory\n");
|
|
goto fail0;
|
|
}
|
|
buf = (uint8_t *)malloc(2, M_80211_VAP, M_NOWAIT | M_ZERO);
|
|
if (req == NULL) {
|
|
device_printf(sc->sc_dev, "could not allocate a memory\n");
|
|
goto fail1;
|
|
}
|
|
|
|
req->bmRequestType = UT_WRITE_VENDOR_DEVICE;
|
|
req->bRequest = URTW_8187_SETREGS_REQ;
|
|
USETW(req->wValue, addr);
|
|
USETW(req->wIndex, index);
|
|
USETW(req->wLength, sizeof(uint16_t));
|
|
buf[0] = (data16 & 0x00ff);
|
|
buf[1] = (data16 & 0xff00) >> 8;
|
|
|
|
error = urtw_do_request(sc, req, buf);
|
|
|
|
free(buf, M_80211_VAP);
|
|
fail1: free(req, M_80211_VAP);
|
|
fail0: return (error);
|
|
}
|
|
|
|
static usb_error_t
|
|
urtw_8225_rf_set_chan(struct urtw_softc *sc, int chan)
|
|
{
|
|
usb_error_t error;
|
|
|
|
error = urtw_8225_set_txpwrlvl(sc, chan);
|
|
if (error)
|
|
goto fail;
|
|
urtw_8225_write(sc, URTW_8225_ADDR_7_MAGIC, urtw_8225_channel[chan]);
|
|
usb_pause_mtx(&sc->sc_mtx, 10);
|
|
fail:
|
|
return (error);
|
|
}
|
|
|
|
static usb_error_t
|
|
urtw_8225_rf_set_sens(struct urtw_softc *sc, int sens)
|
|
{
|
|
usb_error_t error;
|
|
|
|
if (sens < 0 || sens > 6)
|
|
return -1;
|
|
|
|
if (sens > 4)
|
|
urtw_8225_write(sc,
|
|
URTW_8225_ADDR_C_MAGIC, URTW_8225_ADDR_C_DATA_MAGIC1);
|
|
else
|
|
urtw_8225_write(sc,
|
|
URTW_8225_ADDR_C_MAGIC, URTW_8225_ADDR_C_DATA_MAGIC2);
|
|
|
|
sens = 6 - sens;
|
|
error = urtw_8225_setgain(sc, sens);
|
|
if (error)
|
|
goto fail;
|
|
|
|
urtw_8187_write_phy_cck(sc, 0x41, urtw_8225_threshold[sens]);
|
|
|
|
fail:
|
|
return (error);
|
|
}
|
|
|
|
static usb_error_t
|
|
urtw_8225_set_txpwrlvl(struct urtw_softc *sc, int chan)
|
|
{
|
|
int i, idx, set;
|
|
uint8_t *cck_pwltable;
|
|
uint8_t cck_pwrlvl_max, ofdm_pwrlvl_min, ofdm_pwrlvl_max;
|
|
uint8_t cck_pwrlvl = sc->sc_txpwr_cck[chan] & 0xff;
|
|
uint8_t ofdm_pwrlvl = sc->sc_txpwr_ofdm[chan] & 0xff;
|
|
usb_error_t error;
|
|
|
|
cck_pwrlvl_max = 11;
|
|
ofdm_pwrlvl_max = 25; /* 12 -> 25 */
|
|
ofdm_pwrlvl_min = 10;
|
|
|
|
/* CCK power setting */
|
|
cck_pwrlvl = (cck_pwrlvl > cck_pwrlvl_max) ? cck_pwrlvl_max : cck_pwrlvl;
|
|
idx = cck_pwrlvl % 6;
|
|
set = cck_pwrlvl / 6;
|
|
cck_pwltable = (chan == 14) ? urtw_8225_txpwr_cck_ch14 :
|
|
urtw_8225_txpwr_cck;
|
|
|
|
urtw_write8_m(sc, URTW_TX_GAIN_CCK,
|
|
urtw_8225_tx_gain_cck_ofdm[set] >> 1);
|
|
for (i = 0; i < 8; i++) {
|
|
urtw_8187_write_phy_cck(sc, 0x44 + i,
|
|
cck_pwltable[idx * 8 + i]);
|
|
}
|
|
usb_pause_mtx(&sc->sc_mtx, 1);
|
|
|
|
/* OFDM power setting */
|
|
ofdm_pwrlvl = (ofdm_pwrlvl > (ofdm_pwrlvl_max - ofdm_pwrlvl_min)) ?
|
|
ofdm_pwrlvl_max : ofdm_pwrlvl + ofdm_pwrlvl_min;
|
|
ofdm_pwrlvl = (ofdm_pwrlvl > 35) ? 35 : ofdm_pwrlvl;
|
|
|
|
idx = ofdm_pwrlvl % 6;
|
|
set = ofdm_pwrlvl / 6;
|
|
|
|
error = urtw_8185_set_anaparam2(sc, URTW_8225_ANAPARAM2_ON);
|
|
if (error)
|
|
goto fail;
|
|
urtw_8187_write_phy_ofdm(sc, 2, 0x42);
|
|
urtw_8187_write_phy_ofdm(sc, 6, 0);
|
|
urtw_8187_write_phy_ofdm(sc, 8, 0);
|
|
|
|
urtw_write8_m(sc, URTW_TX_GAIN_OFDM,
|
|
urtw_8225_tx_gain_cck_ofdm[set] >> 1);
|
|
urtw_8187_write_phy_ofdm(sc, 0x5, urtw_8225_txpwr_ofdm[idx]);
|
|
urtw_8187_write_phy_ofdm(sc, 0x7, urtw_8225_txpwr_ofdm[idx]);
|
|
usb_pause_mtx(&sc->sc_mtx, 1);
|
|
fail:
|
|
return (error);
|
|
}
|
|
|
|
|
|
static usb_error_t
|
|
urtw_8225_rf_stop(struct urtw_softc *sc)
|
|
{
|
|
uint8_t data;
|
|
usb_error_t error;
|
|
|
|
urtw_8225_write(sc, 0x4, 0x1f);
|
|
|
|
error = urtw_set_mode(sc, URTW_EPROM_CMD_CONFIG);
|
|
if (error)
|
|
goto fail;
|
|
|
|
urtw_read8_m(sc, URTW_CONFIG3, &data);
|
|
urtw_write8_m(sc, URTW_CONFIG3, data | URTW_CONFIG3_ANAPARAM_WRITE);
|
|
if (sc->sc_flags & URTW_RTL8187B) {
|
|
urtw_write32_m(sc, URTW_ANAPARAM2,
|
|
URTW_8187B_8225_ANAPARAM2_OFF);
|
|
urtw_write32_m(sc, URTW_ANAPARAM, URTW_8187B_8225_ANAPARAM_OFF);
|
|
urtw_write32_m(sc, URTW_ANAPARAM3,
|
|
URTW_8187B_8225_ANAPARAM3_OFF);
|
|
} else {
|
|
urtw_write32_m(sc, URTW_ANAPARAM2, URTW_8225_ANAPARAM2_OFF);
|
|
urtw_write32_m(sc, URTW_ANAPARAM, URTW_8225_ANAPARAM_OFF);
|
|
}
|
|
|
|
urtw_write8_m(sc, URTW_CONFIG3, data & ~URTW_CONFIG3_ANAPARAM_WRITE);
|
|
error = urtw_set_mode(sc, URTW_EPROM_CMD_NORMAL);
|
|
if (error)
|
|
goto fail;
|
|
|
|
fail:
|
|
return (error);
|
|
}
|
|
|
|
static usb_error_t
|
|
urtw_8225v2_rf_init(struct urtw_softc *sc)
|
|
{
|
|
#define N(a) (sizeof(a) / sizeof((a)[0]))
|
|
int i;
|
|
uint16_t data;
|
|
uint32_t data32;
|
|
usb_error_t error;
|
|
|
|
error = urtw_8180_set_anaparam(sc, URTW_8225_ANAPARAM_ON);
|
|
if (error)
|
|
goto fail;
|
|
|
|
error = urtw_8225_usb_init(sc);
|
|
if (error)
|
|
goto fail;
|
|
|
|
urtw_write32_m(sc, URTW_RF_TIMING, 0x000a8008);
|
|
urtw_read16_m(sc, URTW_BRSR, &data); /* XXX ??? */
|
|
urtw_write16_m(sc, URTW_BRSR, 0xffff);
|
|
urtw_write32_m(sc, URTW_RF_PARA, 0x100044);
|
|
|
|
error = urtw_set_mode(sc, URTW_EPROM_CMD_CONFIG);
|
|
if (error)
|
|
goto fail;
|
|
urtw_write8_m(sc, URTW_CONFIG3, 0x44);
|
|
error = urtw_set_mode(sc, URTW_EPROM_CMD_NORMAL);
|
|
if (error)
|
|
goto fail;
|
|
|
|
error = urtw_8185_rf_pins_enable(sc);
|
|
if (error)
|
|
goto fail;
|
|
|
|
usb_pause_mtx(&sc->sc_mtx, 500);
|
|
|
|
for (i = 0; i < N(urtw_8225v2_rf_part1); i++) {
|
|
urtw_8225_write(sc, urtw_8225v2_rf_part1[i].reg,
|
|
urtw_8225v2_rf_part1[i].val);
|
|
}
|
|
usb_pause_mtx(&sc->sc_mtx, 50);
|
|
|
|
urtw_8225_write(sc,
|
|
URTW_8225_ADDR_0_MAGIC, URTW_8225_ADDR_0_DATA_MAGIC1);
|
|
|
|
for (i = 0; i < 95; i++) {
|
|
urtw_8225_write(sc, URTW_8225_ADDR_1_MAGIC, (uint8_t)(i + 1));
|
|
urtw_8225_write(sc, URTW_8225_ADDR_2_MAGIC,
|
|
urtw_8225v2_rxgain[i]);
|
|
}
|
|
|
|
urtw_8225_write(sc,
|
|
URTW_8225_ADDR_3_MAGIC, URTW_8225_ADDR_3_DATA_MAGIC1);
|
|
urtw_8225_write(sc,
|
|
URTW_8225_ADDR_5_MAGIC, URTW_8225_ADDR_5_DATA_MAGIC1);
|
|
urtw_8225_write(sc,
|
|
URTW_8225_ADDR_0_MAGIC, URTW_8225_ADDR_0_DATA_MAGIC2);
|
|
urtw_8225_write(sc,
|
|
URTW_8225_ADDR_2_MAGIC, URTW_8225_ADDR_2_DATA_MAGIC1);
|
|
usb_pause_mtx(&sc->sc_mtx, 100);
|
|
urtw_8225_write(sc,
|
|
URTW_8225_ADDR_2_MAGIC, URTW_8225_ADDR_2_DATA_MAGIC2);
|
|
usb_pause_mtx(&sc->sc_mtx, 100);
|
|
|
|
error = urtw_8225_read(sc, URTW_8225_ADDR_6_MAGIC, &data32);
|
|
if (error != 0)
|
|
goto fail;
|
|
if (data32 != URTW_8225_ADDR_6_DATA_MAGIC1)
|
|
device_printf(sc->sc_dev, "expect 0xe6!! (0x%x)\n", data32);
|
|
if (!(data32 & URTW_8225_ADDR_6_DATA_MAGIC2)) {
|
|
urtw_8225_write(sc,
|
|
URTW_8225_ADDR_2_MAGIC, URTW_8225_ADDR_2_DATA_MAGIC1);
|
|
usb_pause_mtx(&sc->sc_mtx, 100);
|
|
urtw_8225_write(sc,
|
|
URTW_8225_ADDR_2_MAGIC, URTW_8225_ADDR_2_DATA_MAGIC2);
|
|
usb_pause_mtx(&sc->sc_mtx, 50);
|
|
error = urtw_8225_read(sc, URTW_8225_ADDR_6_MAGIC, &data32);
|
|
if (error != 0)
|
|
goto fail;
|
|
if (!(data32 & URTW_8225_ADDR_6_DATA_MAGIC2))
|
|
device_printf(sc->sc_dev, "RF calibration failed\n");
|
|
}
|
|
usb_pause_mtx(&sc->sc_mtx, 100);
|
|
|
|
urtw_8225_write(sc,
|
|
URTW_8225_ADDR_0_MAGIC, URTW_8225_ADDR_0_DATA_MAGIC6);
|
|
for (i = 0; i < 128; i++) {
|
|
urtw_8187_write_phy_ofdm(sc, 0xb, urtw_8225_agc[i]);
|
|
urtw_8187_write_phy_ofdm(sc, 0xa, (uint8_t)i + 0x80);
|
|
}
|
|
|
|
for (i = 0; i < N(urtw_8225v2_rf_part2); i++) {
|
|
urtw_8187_write_phy_ofdm(sc, urtw_8225v2_rf_part2[i].reg,
|
|
urtw_8225v2_rf_part2[i].val);
|
|
}
|
|
|
|
error = urtw_8225v2_setgain(sc, 4);
|
|
if (error)
|
|
goto fail;
|
|
|
|
for (i = 0; i < N(urtw_8225v2_rf_part3); i++) {
|
|
urtw_8187_write_phy_cck(sc, urtw_8225v2_rf_part3[i].reg,
|
|
urtw_8225v2_rf_part3[i].val);
|
|
}
|
|
|
|
urtw_write8_m(sc, URTW_TESTR, 0x0d);
|
|
|
|
error = urtw_8225v2_set_txpwrlvl(sc, 1);
|
|
if (error)
|
|
goto fail;
|
|
|
|
urtw_8187_write_phy_cck(sc, 0x10, 0x9b);
|
|
urtw_8187_write_phy_ofdm(sc, 0x26, 0x90);
|
|
|
|
/* TX ant A, 0x0 for B */
|
|
error = urtw_8185_tx_antenna(sc, 0x3);
|
|
if (error)
|
|
goto fail;
|
|
urtw_write32_m(sc, URTW_HSSI_PARA, 0x3dc00002);
|
|
|
|
error = urtw_8225_rf_set_chan(sc, 1);
|
|
fail:
|
|
return (error);
|
|
#undef N
|
|
}
|
|
|
|
static usb_error_t
|
|
urtw_8225v2_rf_set_chan(struct urtw_softc *sc, int chan)
|
|
{
|
|
usb_error_t error;
|
|
|
|
error = urtw_8225v2_set_txpwrlvl(sc, chan);
|
|
if (error)
|
|
goto fail;
|
|
|
|
urtw_8225_write(sc, URTW_8225_ADDR_7_MAGIC, urtw_8225_channel[chan]);
|
|
usb_pause_mtx(&sc->sc_mtx, 10);
|
|
fail:
|
|
return (error);
|
|
}
|
|
|
|
static usb_error_t
|
|
urtw_8225_read(struct urtw_softc *sc, uint8_t addr, uint32_t *data)
|
|
{
|
|
int i;
|
|
int16_t bit;
|
|
uint8_t rlen = 12, wlen = 6;
|
|
uint16_t o1, o2, o3, tmp;
|
|
uint32_t d2w = ((uint32_t)(addr & 0x1f)) << 27;
|
|
uint32_t mask = 0x80000000, value = 0;
|
|
usb_error_t error;
|
|
|
|
urtw_read16_m(sc, URTW_RF_PINS_OUTPUT, &o1);
|
|
urtw_read16_m(sc, URTW_RF_PINS_ENABLE, &o2);
|
|
urtw_read16_m(sc, URTW_RF_PINS_SELECT, &o3);
|
|
urtw_write16_m(sc, URTW_RF_PINS_ENABLE, o2 | URTW_RF_PINS_MAGIC4);
|
|
urtw_write16_m(sc, URTW_RF_PINS_SELECT, o3 | URTW_RF_PINS_MAGIC4);
|
|
o1 &= ~URTW_RF_PINS_MAGIC4;
|
|
urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, o1 | URTW_BB_HOST_BANG_EN);
|
|
DELAY(5);
|
|
urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, o1);
|
|
DELAY(5);
|
|
|
|
for (i = 0; i < (wlen / 2); i++, mask = mask >> 1) {
|
|
bit = ((d2w & mask) != 0) ? 1 : 0;
|
|
|
|
urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, bit | o1);
|
|
DELAY(2);
|
|
urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, bit | o1 |
|
|
URTW_BB_HOST_BANG_CLK);
|
|
DELAY(2);
|
|
urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, bit | o1 |
|
|
URTW_BB_HOST_BANG_CLK);
|
|
DELAY(2);
|
|
mask = mask >> 1;
|
|
if (i == 2)
|
|
break;
|
|
bit = ((d2w & mask) != 0) ? 1 : 0;
|
|
urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, bit | o1 |
|
|
URTW_BB_HOST_BANG_CLK);
|
|
DELAY(2);
|
|
urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, bit | o1 |
|
|
URTW_BB_HOST_BANG_CLK);
|
|
DELAY(2);
|
|
urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, bit | o1);
|
|
DELAY(1);
|
|
}
|
|
urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, bit | o1 | URTW_BB_HOST_BANG_RW |
|
|
URTW_BB_HOST_BANG_CLK);
|
|
DELAY(2);
|
|
urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, bit | o1 | URTW_BB_HOST_BANG_RW);
|
|
DELAY(2);
|
|
urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, o1 | URTW_BB_HOST_BANG_RW);
|
|
DELAY(2);
|
|
|
|
mask = 0x800;
|
|
for (i = 0; i < rlen; i++, mask = mask >> 1) {
|
|
urtw_write16_m(sc, URTW_RF_PINS_OUTPUT,
|
|
o1 | URTW_BB_HOST_BANG_RW);
|
|
DELAY(2);
|
|
urtw_write16_m(sc, URTW_RF_PINS_OUTPUT,
|
|
o1 | URTW_BB_HOST_BANG_RW | URTW_BB_HOST_BANG_CLK);
|
|
DELAY(2);
|
|
urtw_write16_m(sc, URTW_RF_PINS_OUTPUT,
|
|
o1 | URTW_BB_HOST_BANG_RW | URTW_BB_HOST_BANG_CLK);
|
|
DELAY(2);
|
|
urtw_write16_m(sc, URTW_RF_PINS_OUTPUT,
|
|
o1 | URTW_BB_HOST_BANG_RW | URTW_BB_HOST_BANG_CLK);
|
|
DELAY(2);
|
|
|
|
urtw_read16_m(sc, URTW_RF_PINS_INPUT, &tmp);
|
|
value |= ((tmp & URTW_BB_HOST_BANG_CLK) ? mask : 0);
|
|
urtw_write16_m(sc, URTW_RF_PINS_OUTPUT,
|
|
o1 | URTW_BB_HOST_BANG_RW);
|
|
DELAY(2);
|
|
}
|
|
|
|
urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, o1 | URTW_BB_HOST_BANG_EN |
|
|
URTW_BB_HOST_BANG_RW);
|
|
DELAY(2);
|
|
|
|
urtw_write16_m(sc, URTW_RF_PINS_ENABLE, o2);
|
|
urtw_write16_m(sc, URTW_RF_PINS_SELECT, o3);
|
|
urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, URTW_RF_PINS_OUTPUT_MAGIC1);
|
|
|
|
if (data != NULL)
|
|
*data = value;
|
|
fail:
|
|
return (error);
|
|
}
|
|
|
|
|
|
static usb_error_t
|
|
urtw_8225v2_set_txpwrlvl(struct urtw_softc *sc, int chan)
|
|
{
|
|
int i;
|
|
uint8_t *cck_pwrtable;
|
|
uint8_t cck_pwrlvl_max = 15, ofdm_pwrlvl_max = 25, ofdm_pwrlvl_min = 10;
|
|
uint8_t cck_pwrlvl = sc->sc_txpwr_cck[chan] & 0xff;
|
|
uint8_t ofdm_pwrlvl = sc->sc_txpwr_ofdm[chan] & 0xff;
|
|
usb_error_t error;
|
|
|
|
/* CCK power setting */
|
|
cck_pwrlvl = (cck_pwrlvl > cck_pwrlvl_max) ? cck_pwrlvl_max : cck_pwrlvl;
|
|
cck_pwrlvl += sc->sc_txpwr_cck_base;
|
|
cck_pwrlvl = (cck_pwrlvl > 35) ? 35 : cck_pwrlvl;
|
|
cck_pwrtable = (chan == 14) ? urtw_8225v2_txpwr_cck_ch14 :
|
|
urtw_8225v2_txpwr_cck;
|
|
|
|
for (i = 0; i < 8; i++)
|
|
urtw_8187_write_phy_cck(sc, 0x44 + i, cck_pwrtable[i]);
|
|
|
|
urtw_write8_m(sc, URTW_TX_GAIN_CCK,
|
|
urtw_8225v2_tx_gain_cck_ofdm[cck_pwrlvl]);
|
|
usb_pause_mtx(&sc->sc_mtx, 1);
|
|
|
|
/* OFDM power setting */
|
|
ofdm_pwrlvl = (ofdm_pwrlvl > (ofdm_pwrlvl_max - ofdm_pwrlvl_min)) ?
|
|
ofdm_pwrlvl_max : ofdm_pwrlvl + ofdm_pwrlvl_min;
|
|
ofdm_pwrlvl += sc->sc_txpwr_ofdm_base;
|
|
ofdm_pwrlvl = (ofdm_pwrlvl > 35) ? 35 : ofdm_pwrlvl;
|
|
|
|
error = urtw_8185_set_anaparam2(sc, URTW_8225_ANAPARAM2_ON);
|
|
if (error)
|
|
goto fail;
|
|
|
|
urtw_8187_write_phy_ofdm(sc, 2, 0x42);
|
|
urtw_8187_write_phy_ofdm(sc, 5, 0x0);
|
|
urtw_8187_write_phy_ofdm(sc, 6, 0x40);
|
|
urtw_8187_write_phy_ofdm(sc, 7, 0x0);
|
|
urtw_8187_write_phy_ofdm(sc, 8, 0x40);
|
|
|
|
urtw_write8_m(sc, URTW_TX_GAIN_OFDM,
|
|
urtw_8225v2_tx_gain_cck_ofdm[ofdm_pwrlvl]);
|
|
usb_pause_mtx(&sc->sc_mtx, 1);
|
|
fail:
|
|
return (error);
|
|
}
|
|
|
|
static usb_error_t
|
|
urtw_8225v2_setgain(struct urtw_softc *sc, int16_t gain)
|
|
{
|
|
uint8_t *gainp;
|
|
usb_error_t error;
|
|
|
|
/* XXX for A? */
|
|
gainp = urtw_8225v2_gain_bg;
|
|
urtw_8187_write_phy_ofdm(sc, 0x0d, gainp[gain * 3]);
|
|
usb_pause_mtx(&sc->sc_mtx, 1);
|
|
urtw_8187_write_phy_ofdm(sc, 0x1b, gainp[gain * 3 + 1]);
|
|
usb_pause_mtx(&sc->sc_mtx, 1);
|
|
urtw_8187_write_phy_ofdm(sc, 0x1d, gainp[gain * 3 + 2]);
|
|
usb_pause_mtx(&sc->sc_mtx, 1);
|
|
urtw_8187_write_phy_ofdm(sc, 0x21, 0x17);
|
|
usb_pause_mtx(&sc->sc_mtx, 1);
|
|
fail:
|
|
return (error);
|
|
}
|
|
|
|
static usb_error_t
|
|
urtw_8225_isv2(struct urtw_softc *sc, int *ret)
|
|
{
|
|
uint32_t data;
|
|
usb_error_t error;
|
|
|
|
*ret = 1;
|
|
|
|
urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, URTW_RF_PINS_MAGIC5);
|
|
urtw_write16_m(sc, URTW_RF_PINS_SELECT, URTW_RF_PINS_MAGIC5);
|
|
urtw_write16_m(sc, URTW_RF_PINS_ENABLE, URTW_RF_PINS_MAGIC5);
|
|
usb_pause_mtx(&sc->sc_mtx, 500);
|
|
|
|
urtw_8225_write(sc, URTW_8225_ADDR_0_MAGIC,
|
|
URTW_8225_ADDR_0_DATA_MAGIC1);
|
|
|
|
error = urtw_8225_read(sc, URTW_8225_ADDR_8_MAGIC, &data);
|
|
if (error != 0)
|
|
goto fail;
|
|
if (data != URTW_8225_ADDR_8_DATA_MAGIC1)
|
|
*ret = 0;
|
|
else {
|
|
error = urtw_8225_read(sc, URTW_8225_ADDR_9_MAGIC, &data);
|
|
if (error != 0)
|
|
goto fail;
|
|
if (data != URTW_8225_ADDR_9_DATA_MAGIC1)
|
|
*ret = 0;
|
|
}
|
|
|
|
urtw_8225_write(sc, URTW_8225_ADDR_0_MAGIC,
|
|
URTW_8225_ADDR_0_DATA_MAGIC2);
|
|
fail:
|
|
return (error);
|
|
}
|
|
|
|
static usb_error_t
|
|
urtw_8225v2b_rf_init(struct urtw_softc *sc)
|
|
{
|
|
#define N(a) (sizeof(a) / sizeof((a)[0]))
|
|
int i;
|
|
uint8_t data8;
|
|
usb_error_t error;
|
|
|
|
error = urtw_set_mode(sc, URTW_EPROM_CMD_CONFIG);
|
|
if (error)
|
|
goto fail;
|
|
|
|
/*
|
|
* initialize extra registers on 8187
|
|
*/
|
|
urtw_write16_m(sc, URTW_BRSR_8187B, 0xfff);
|
|
|
|
/* retry limit */
|
|
urtw_read8_m(sc, URTW_CW_CONF, &data8);
|
|
data8 |= URTW_CW_CONF_PERPACKET_RETRY;
|
|
urtw_write8_m(sc, URTW_CW_CONF, data8);
|
|
|
|
/* TX AGC */
|
|
urtw_read8_m(sc, URTW_TX_AGC_CTL, &data8);
|
|
data8 |= URTW_TX_AGC_CTL_PERPACKET_GAIN;
|
|
urtw_write8_m(sc, URTW_TX_AGC_CTL, data8);
|
|
|
|
/* Auto Rate Fallback Control */
|
|
#define URTW_ARFR 0x1e0
|
|
urtw_write16_m(sc, URTW_ARFR, 0xfff);
|
|
urtw_read8_m(sc, URTW_RATE_FALLBACK, &data8);
|
|
urtw_write8_m(sc, URTW_RATE_FALLBACK,
|
|
data8 | URTW_RATE_FALLBACK_ENABLE);
|
|
|
|
urtw_read8_m(sc, URTW_MSR, &data8);
|
|
urtw_write8_m(sc, URTW_MSR, data8 & 0xf3);
|
|
urtw_read8_m(sc, URTW_MSR, &data8);
|
|
urtw_write8_m(sc, URTW_MSR, data8 | URTW_MSR_LINK_ENEDCA);
|
|
urtw_write8_m(sc, URTW_ACM_CONTROL, sc->sc_acmctl);
|
|
|
|
urtw_write16_m(sc, URTW_ATIM_WND, 2);
|
|
urtw_write16_m(sc, URTW_BEACON_INTERVAL, 100);
|
|
#define URTW_FEMR_FOR_8187B 0x1d4
|
|
urtw_write16_m(sc, URTW_FEMR_FOR_8187B, 0xffff);
|
|
|
|
/* led type */
|
|
urtw_read8_m(sc, URTW_CONFIG1, &data8);
|
|
data8 = (data8 & 0x3f) | 0x80;
|
|
urtw_write8_m(sc, URTW_CONFIG1, data8);
|
|
|
|
/* applying MAC address again. */
|
|
urtw_write32_m(sc, URTW_MAC0, ((uint32_t *)sc->sc_bssid)[0]);
|
|
urtw_write16_m(sc, URTW_MAC4, ((uint32_t *)sc->sc_bssid)[1] & 0xffff);
|
|
|
|
error = urtw_set_mode(sc, URTW_EPROM_CMD_NORMAL);
|
|
if (error)
|
|
goto fail;
|
|
|
|
urtw_write8_m(sc, URTW_WPA_CONFIG, 0);
|
|
|
|
/*
|
|
* MAC configuration
|
|
*/
|
|
for (i = 0; i < N(urtw_8225v2b_rf_part1); i++)
|
|
urtw_write8_m(sc, urtw_8225v2b_rf_part1[i].reg,
|
|
urtw_8225v2b_rf_part1[i].val);
|
|
urtw_write16_m(sc, URTW_TID_AC_MAP, 0xfa50);
|
|
urtw_write16_m(sc, URTW_INT_MIG, 0x0000);
|
|
urtw_write32_m(sc, 0x1f0, 0);
|
|
urtw_write32_m(sc, 0x1f4, 0);
|
|
urtw_write8_m(sc, 0x1f8, 0);
|
|
urtw_write32_m(sc, URTW_RF_TIMING, 0x4001);
|
|
|
|
#define URTW_RFSW_CTRL 0x272
|
|
urtw_write16_m(sc, URTW_RFSW_CTRL, 0x569a);
|
|
|
|
/*
|
|
* initialize PHY
|
|
*/
|
|
error = urtw_set_mode(sc, URTW_EPROM_CMD_CONFIG);
|
|
if (error)
|
|
goto fail;
|
|
urtw_read8_m(sc, URTW_CONFIG3, &data8);
|
|
urtw_write8_m(sc, URTW_CONFIG3,
|
|
data8 | URTW_CONFIG3_ANAPARAM_WRITE);
|
|
|
|
error = urtw_set_mode(sc, URTW_EPROM_CMD_NORMAL);
|
|
if (error)
|
|
goto fail;
|
|
|
|
/* setup RFE initial timing */
|
|
urtw_write16_m(sc, URTW_RF_PINS_OUTPUT, 0x0480);
|
|
urtw_write16_m(sc, URTW_RF_PINS_SELECT, 0x2488);
|
|
urtw_write16_m(sc, URTW_RF_PINS_ENABLE, 0x1fff);
|
|
usb_pause_mtx(&sc->sc_mtx, 1100);
|
|
|
|
for (i = 0; i < N(urtw_8225v2b_rf_part0); i++) {
|
|
urtw_8225_write(sc, urtw_8225v2b_rf_part0[i].reg,
|
|
urtw_8225v2b_rf_part0[i].val);
|
|
usb_pause_mtx(&sc->sc_mtx, 1);
|
|
}
|
|
urtw_8225_write(sc, 0x00, 0x01b7);
|
|
|
|
for (i = 0; i < 95; i++) {
|
|
urtw_8225_write(sc, URTW_8225_ADDR_1_MAGIC, (uint8_t)(i + 1));
|
|
usb_pause_mtx(&sc->sc_mtx, 1);
|
|
urtw_8225_write(sc, URTW_8225_ADDR_2_MAGIC,
|
|
urtw_8225v2b_rxgain[i]);
|
|
usb_pause_mtx(&sc->sc_mtx, 1);
|
|
}
|
|
|
|
urtw_8225_write(sc, URTW_8225_ADDR_3_MAGIC, 0x080);
|
|
usb_pause_mtx(&sc->sc_mtx, 1);
|
|
urtw_8225_write(sc, URTW_8225_ADDR_5_MAGIC, 0x004);
|
|
usb_pause_mtx(&sc->sc_mtx, 1);
|
|
urtw_8225_write(sc, URTW_8225_ADDR_0_MAGIC, 0x0b7);
|
|
usb_pause_mtx(&sc->sc_mtx, 1);
|
|
usb_pause_mtx(&sc->sc_mtx, 3000);
|
|
urtw_8225_write(sc, URTW_8225_ADDR_2_MAGIC, 0xc4d);
|
|
usb_pause_mtx(&sc->sc_mtx, 2000);
|
|
urtw_8225_write(sc, URTW_8225_ADDR_2_MAGIC, 0x44d);
|
|
usb_pause_mtx(&sc->sc_mtx, 1);
|
|
urtw_8225_write(sc, URTW_8225_ADDR_0_MAGIC, 0x2bf);
|
|
usb_pause_mtx(&sc->sc_mtx, 1);
|
|
|
|
urtw_write8_m(sc, URTW_TX_GAIN_CCK, 0x03);
|
|
urtw_write8_m(sc, URTW_TX_GAIN_OFDM, 0x07);
|
|
urtw_write8_m(sc, URTW_TX_ANTENNA, 0x03);
|
|
|
|
urtw_8187_write_phy_ofdm(sc, 0x80, 0x12);
|
|
for (i = 0; i < 128; i++) {
|
|
uint32_t addr, data;
|
|
|
|
data = (urtw_8225z2_agc[i] << 8) | 0x0000008f;
|
|
addr = ((i + 0x80) << 8) | 0x0000008e;
|
|
|
|
urtw_8187_write_phy_ofdm(sc, data & 0x7f, (data >> 8) & 0xff);
|
|
urtw_8187_write_phy_ofdm(sc, addr & 0x7f, (addr >> 8) & 0xff);
|
|
urtw_8187_write_phy_ofdm(sc, 0x0e, 0x00);
|
|
}
|
|
urtw_8187_write_phy_ofdm(sc, 0x80, 0x10);
|
|
|
|
for (i = 0; i < N(urtw_8225v2b_rf_part2); i++)
|
|
urtw_8187_write_phy_ofdm(sc, i, urtw_8225v2b_rf_part2[i].val);
|
|
|
|
urtw_write32_m(sc, URTW_8187B_AC_VO, (7 << 12) | (3 << 8) | 0x1c);
|
|
urtw_write32_m(sc, URTW_8187B_AC_VI, (7 << 12) | (3 << 8) | 0x1c);
|
|
urtw_write32_m(sc, URTW_8187B_AC_BE, (7 << 12) | (3 << 8) | 0x1c);
|
|
urtw_write32_m(sc, URTW_8187B_AC_BK, (7 << 12) | (3 << 8) | 0x1c);
|
|
|
|
urtw_8187_write_phy_ofdm(sc, 0x97, 0x46);
|
|
urtw_8187_write_phy_ofdm(sc, 0xa4, 0xb6);
|
|
urtw_8187_write_phy_ofdm(sc, 0x85, 0xfc);
|
|
urtw_8187_write_phy_cck(sc, 0xc1, 0x88);
|
|
|
|
fail:
|
|
return (error);
|
|
#undef N
|
|
}
|
|
|
|
static usb_error_t
|
|
urtw_8225v2b_rf_set_chan(struct urtw_softc *sc, int chan)
|
|
{
|
|
usb_error_t error;
|
|
|
|
error = urtw_8225v2b_set_txpwrlvl(sc, chan);
|
|
if (error)
|
|
goto fail;
|
|
|
|
urtw_8225_write(sc, URTW_8225_ADDR_7_MAGIC, urtw_8225_channel[chan]);
|
|
usb_pause_mtx(&sc->sc_mtx, 10);
|
|
fail:
|
|
return (error);
|
|
}
|
|
|
|
static usb_error_t
|
|
urtw_8225v2b_set_txpwrlvl(struct urtw_softc *sc, int chan)
|
|
{
|
|
int i;
|
|
uint8_t *cck_pwrtable;
|
|
uint8_t cck_pwrlvl_max = 15;
|
|
uint8_t cck_pwrlvl = sc->sc_txpwr_cck[chan] & 0xff;
|
|
uint8_t ofdm_pwrlvl = sc->sc_txpwr_ofdm[chan] & 0xff;
|
|
usb_error_t error;
|
|
|
|
/* CCK power setting */
|
|
cck_pwrlvl = (cck_pwrlvl > cck_pwrlvl_max) ?
|
|
((sc->sc_flags & URTW_RTL8187B_REV_B) ? cck_pwrlvl_max : 22) :
|
|
(cck_pwrlvl + ((sc->sc_flags & URTW_RTL8187B_REV_B) ? 0 : 7));
|
|
cck_pwrlvl += sc->sc_txpwr_cck_base;
|
|
cck_pwrlvl = (cck_pwrlvl > 35) ? 35 : cck_pwrlvl;
|
|
cck_pwrtable = (chan == 14) ? urtw_8225v2b_txpwr_cck_ch14 :
|
|
urtw_8225v2b_txpwr_cck;
|
|
|
|
if (sc->sc_flags & URTW_RTL8187B_REV_B)
|
|
cck_pwrtable += (cck_pwrlvl <= 6) ? 0 :
|
|
((cck_pwrlvl <= 11) ? 8 : 16);
|
|
else
|
|
cck_pwrtable += (cck_pwrlvl <= 5) ? 0 :
|
|
((cck_pwrlvl <= 11) ? 8 : ((cck_pwrlvl <= 17) ? 16 : 24));
|
|
|
|
for (i = 0; i < 8; i++)
|
|
urtw_8187_write_phy_cck(sc, 0x44 + i, cck_pwrtable[i]);
|
|
|
|
urtw_write8_m(sc, URTW_TX_GAIN_CCK,
|
|
urtw_8225v2_tx_gain_cck_ofdm[cck_pwrlvl] << 1);
|
|
usb_pause_mtx(&sc->sc_mtx, 1);
|
|
|
|
/* OFDM power setting */
|
|
ofdm_pwrlvl = (ofdm_pwrlvl > 15) ?
|
|
((sc->sc_flags & URTW_RTL8187B_REV_B) ? 17 : 25) :
|
|
(ofdm_pwrlvl + ((sc->sc_flags & URTW_RTL8187B_REV_B) ? 2 : 10));
|
|
ofdm_pwrlvl += sc->sc_txpwr_ofdm_base;
|
|
ofdm_pwrlvl = (ofdm_pwrlvl > 35) ? 35 : ofdm_pwrlvl;
|
|
|
|
urtw_write8_m(sc, URTW_TX_GAIN_OFDM,
|
|
urtw_8225v2_tx_gain_cck_ofdm[ofdm_pwrlvl] << 1);
|
|
|
|
if (sc->sc_flags & URTW_RTL8187B_REV_B) {
|
|
if (ofdm_pwrlvl <= 11) {
|
|
urtw_8187_write_phy_ofdm(sc, 0x87, 0x60);
|
|
urtw_8187_write_phy_ofdm(sc, 0x89, 0x60);
|
|
} else {
|
|
urtw_8187_write_phy_ofdm(sc, 0x87, 0x5c);
|
|
urtw_8187_write_phy_ofdm(sc, 0x89, 0x5c);
|
|
}
|
|
} else {
|
|
if (ofdm_pwrlvl <= 11) {
|
|
urtw_8187_write_phy_ofdm(sc, 0x87, 0x5c);
|
|
urtw_8187_write_phy_ofdm(sc, 0x89, 0x5c);
|
|
} else if (ofdm_pwrlvl <= 17) {
|
|
urtw_8187_write_phy_ofdm(sc, 0x87, 0x54);
|
|
urtw_8187_write_phy_ofdm(sc, 0x89, 0x54);
|
|
} else {
|
|
urtw_8187_write_phy_ofdm(sc, 0x87, 0x50);
|
|
urtw_8187_write_phy_ofdm(sc, 0x89, 0x50);
|
|
}
|
|
}
|
|
usb_pause_mtx(&sc->sc_mtx, 1);
|
|
fail:
|
|
return (error);
|
|
}
|
|
|
|
static usb_error_t
|
|
urtw_read8e(struct urtw_softc *sc, int val, uint8_t *data)
|
|
{
|
|
struct usb_device_request req;
|
|
usb_error_t error;
|
|
|
|
req.bmRequestType = UT_READ_VENDOR_DEVICE;
|
|
req.bRequest = URTW_8187_GETREGS_REQ;
|
|
USETW(req.wValue, val | 0xfe00);
|
|
USETW(req.wIndex, 0);
|
|
USETW(req.wLength, sizeof(uint8_t));
|
|
|
|
error = urtw_do_request(sc, &req, data);
|
|
return (error);
|
|
}
|
|
|
|
static usb_error_t
|
|
urtw_write8e(struct urtw_softc *sc, int val, uint8_t data)
|
|
{
|
|
struct usb_device_request req;
|
|
|
|
req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
|
|
req.bRequest = URTW_8187_SETREGS_REQ;
|
|
USETW(req.wValue, val | 0xfe00);
|
|
USETW(req.wIndex, 0);
|
|
USETW(req.wLength, sizeof(uint8_t));
|
|
|
|
return (urtw_do_request(sc, &req, &data));
|
|
}
|
|
|
|
static usb_error_t
|
|
urtw_8180_set_anaparam(struct urtw_softc *sc, uint32_t val)
|
|
{
|
|
uint8_t data;
|
|
usb_error_t error;
|
|
|
|
error = urtw_set_mode(sc, URTW_EPROM_CMD_CONFIG);
|
|
if (error)
|
|
goto fail;
|
|
|
|
urtw_read8_m(sc, URTW_CONFIG3, &data);
|
|
urtw_write8_m(sc, URTW_CONFIG3, data | URTW_CONFIG3_ANAPARAM_WRITE);
|
|
urtw_write32_m(sc, URTW_ANAPARAM, val);
|
|
urtw_read8_m(sc, URTW_CONFIG3, &data);
|
|
urtw_write8_m(sc, URTW_CONFIG3, data & ~URTW_CONFIG3_ANAPARAM_WRITE);
|
|
|
|
error = urtw_set_mode(sc, URTW_EPROM_CMD_NORMAL);
|
|
if (error)
|
|
goto fail;
|
|
fail:
|
|
return (error);
|
|
}
|
|
|
|
static usb_error_t
|
|
urtw_8185_set_anaparam2(struct urtw_softc *sc, uint32_t val)
|
|
{
|
|
uint8_t data;
|
|
usb_error_t error;
|
|
|
|
error = urtw_set_mode(sc, URTW_EPROM_CMD_CONFIG);
|
|
if (error)
|
|
goto fail;
|
|
|
|
urtw_read8_m(sc, URTW_CONFIG3, &data);
|
|
urtw_write8_m(sc, URTW_CONFIG3, data | URTW_CONFIG3_ANAPARAM_WRITE);
|
|
urtw_write32_m(sc, URTW_ANAPARAM2, val);
|
|
urtw_read8_m(sc, URTW_CONFIG3, &data);
|
|
urtw_write8_m(sc, URTW_CONFIG3, data & ~URTW_CONFIG3_ANAPARAM_WRITE);
|
|
|
|
error = urtw_set_mode(sc, URTW_EPROM_CMD_NORMAL);
|
|
if (error)
|
|
goto fail;
|
|
fail:
|
|
return (error);
|
|
}
|
|
|
|
static usb_error_t
|
|
urtw_intr_enable(struct urtw_softc *sc)
|
|
{
|
|
usb_error_t error;
|
|
|
|
urtw_write16_m(sc, URTW_INTR_MASK, 0xffff);
|
|
fail:
|
|
return (error);
|
|
}
|
|
|
|
static usb_error_t
|
|
urtw_intr_disable(struct urtw_softc *sc)
|
|
{
|
|
usb_error_t error;
|
|
|
|
urtw_write16_m(sc, URTW_INTR_MASK, 0);
|
|
fail:
|
|
return (error);
|
|
}
|
|
|
|
static usb_error_t
|
|
urtw_reset(struct urtw_softc *sc)
|
|
{
|
|
uint8_t data;
|
|
usb_error_t error;
|
|
|
|
error = urtw_8180_set_anaparam(sc, URTW_8225_ANAPARAM_ON);
|
|
if (error)
|
|
goto fail;
|
|
error = urtw_8185_set_anaparam2(sc, URTW_8225_ANAPARAM2_ON);
|
|
if (error)
|
|
goto fail;
|
|
|
|
error = urtw_intr_disable(sc);
|
|
if (error)
|
|
goto fail;
|
|
usb_pause_mtx(&sc->sc_mtx, 100);
|
|
|
|
error = urtw_write8e(sc, 0x18, 0x10);
|
|
if (error != 0)
|
|
goto fail;
|
|
error = urtw_write8e(sc, 0x18, 0x11);
|
|
if (error != 0)
|
|
goto fail;
|
|
error = urtw_write8e(sc, 0x18, 0x00);
|
|
if (error != 0)
|
|
goto fail;
|
|
usb_pause_mtx(&sc->sc_mtx, 100);
|
|
|
|
urtw_read8_m(sc, URTW_CMD, &data);
|
|
data = (data & 0x2) | URTW_CMD_RST;
|
|
urtw_write8_m(sc, URTW_CMD, data);
|
|
usb_pause_mtx(&sc->sc_mtx, 100);
|
|
|
|
urtw_read8_m(sc, URTW_CMD, &data);
|
|
if (data & URTW_CMD_RST) {
|
|
device_printf(sc->sc_dev, "reset timeout\n");
|
|
goto fail;
|
|
}
|
|
|
|
error = urtw_set_mode(sc, URTW_EPROM_CMD_LOAD);
|
|
if (error)
|
|
goto fail;
|
|
usb_pause_mtx(&sc->sc_mtx, 100);
|
|
|
|
error = urtw_8180_set_anaparam(sc, URTW_8225_ANAPARAM_ON);
|
|
if (error)
|
|
goto fail;
|
|
error = urtw_8185_set_anaparam2(sc, URTW_8225_ANAPARAM2_ON);
|
|
if (error)
|
|
goto fail;
|
|
fail:
|
|
return (error);
|
|
}
|
|
|
|
static usb_error_t
|
|
urtw_led_ctl(struct urtw_softc *sc, int mode)
|
|
{
|
|
usb_error_t error = 0;
|
|
|
|
switch (sc->sc_strategy) {
|
|
case URTW_SW_LED_MODE0:
|
|
error = urtw_led_mode0(sc, mode);
|
|
break;
|
|
case URTW_SW_LED_MODE1:
|
|
error = urtw_led_mode1(sc, mode);
|
|
break;
|
|
case URTW_SW_LED_MODE2:
|
|
error = urtw_led_mode2(sc, mode);
|
|
break;
|
|
case URTW_SW_LED_MODE3:
|
|
error = urtw_led_mode3(sc, mode);
|
|
break;
|
|
default:
|
|
panic("unsupported LED mode %d\n", sc->sc_strategy);
|
|
/* never reach */
|
|
}
|
|
|
|
return (error);
|
|
}
|
|
|
|
static usb_error_t
|
|
urtw_led_mode0(struct urtw_softc *sc, int mode)
|
|
{
|
|
|
|
switch (mode) {
|
|
case URTW_LED_CTL_POWER_ON:
|
|
sc->sc_gpio_ledstate = URTW_LED_POWER_ON_BLINK;
|
|
break;
|
|
case URTW_LED_CTL_TX:
|
|
if (sc->sc_gpio_ledinprogress == 1)
|
|
return (0);
|
|
|
|
sc->sc_gpio_ledstate = URTW_LED_BLINK_NORMAL;
|
|
sc->sc_gpio_blinktime = 2;
|
|
break;
|
|
case URTW_LED_CTL_LINK:
|
|
sc->sc_gpio_ledstate = URTW_LED_ON;
|
|
break;
|
|
default:
|
|
panic("unsupported LED mode 0x%x", mode);
|
|
/* never reach */
|
|
}
|
|
|
|
switch (sc->sc_gpio_ledstate) {
|
|
case URTW_LED_ON:
|
|
if (sc->sc_gpio_ledinprogress != 0)
|
|
break;
|
|
urtw_led_on(sc, URTW_LED_GPIO);
|
|
break;
|
|
case URTW_LED_BLINK_NORMAL:
|
|
if (sc->sc_gpio_ledinprogress != 0)
|
|
break;
|
|
sc->sc_gpio_ledinprogress = 1;
|
|
sc->sc_gpio_blinkstate = (sc->sc_gpio_ledon != 0) ?
|
|
URTW_LED_OFF : URTW_LED_ON;
|
|
usb_callout_reset(&sc->sc_led_ch, hz, urtw_led_ch, sc);
|
|
break;
|
|
case URTW_LED_POWER_ON_BLINK:
|
|
urtw_led_on(sc, URTW_LED_GPIO);
|
|
usb_pause_mtx(&sc->sc_mtx, 100);
|
|
urtw_led_off(sc, URTW_LED_GPIO);
|
|
break;
|
|
default:
|
|
panic("unknown LED status 0x%x", sc->sc_gpio_ledstate);
|
|
/* never reach */
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
static usb_error_t
|
|
urtw_led_mode1(struct urtw_softc *sc, int mode)
|
|
{
|
|
|
|
return (USB_ERR_INVAL);
|
|
}
|
|
|
|
static usb_error_t
|
|
urtw_led_mode2(struct urtw_softc *sc, int mode)
|
|
{
|
|
|
|
return (USB_ERR_INVAL);
|
|
}
|
|
|
|
static usb_error_t
|
|
urtw_led_mode3(struct urtw_softc *sc, int mode)
|
|
{
|
|
|
|
return (USB_ERR_INVAL);
|
|
}
|
|
|
|
static usb_error_t
|
|
urtw_led_on(struct urtw_softc *sc, int type)
|
|
{
|
|
usb_error_t error;
|
|
|
|
if (type == URTW_LED_GPIO) {
|
|
switch (sc->sc_gpio_ledpin) {
|
|
case URTW_LED_PIN_GPIO0:
|
|
urtw_write8_m(sc, URTW_GPIO, 0x01);
|
|
urtw_write8_m(sc, URTW_GP_ENABLE, 0x00);
|
|
break;
|
|
default:
|
|
panic("unsupported LED PIN type 0x%x",
|
|
sc->sc_gpio_ledpin);
|
|
/* never reach */
|
|
}
|
|
} else {
|
|
panic("unsupported LED type 0x%x", type);
|
|
/* never reach */
|
|
}
|
|
|
|
sc->sc_gpio_ledon = 1;
|
|
fail:
|
|
return (error);
|
|
}
|
|
|
|
static usb_error_t
|
|
urtw_led_off(struct urtw_softc *sc, int type)
|
|
{
|
|
usb_error_t error;
|
|
|
|
if (type == URTW_LED_GPIO) {
|
|
switch (sc->sc_gpio_ledpin) {
|
|
case URTW_LED_PIN_GPIO0:
|
|
urtw_write8_m(sc, URTW_GPIO, URTW_GPIO_DATA_MAGIC1);
|
|
urtw_write8_m(sc,
|
|
URTW_GP_ENABLE, URTW_GP_ENABLE_DATA_MAGIC1);
|
|
break;
|
|
default:
|
|
panic("unsupported LED PIN type 0x%x",
|
|
sc->sc_gpio_ledpin);
|
|
/* never reach */
|
|
}
|
|
} else {
|
|
panic("unsupported LED type 0x%x", type);
|
|
/* never reach */
|
|
}
|
|
|
|
sc->sc_gpio_ledon = 0;
|
|
|
|
fail:
|
|
return (error);
|
|
}
|
|
|
|
static void
|
|
urtw_led_ch(void *arg)
|
|
{
|
|
struct urtw_softc *sc = arg;
|
|
struct ifnet *ifp = sc->sc_ifp;
|
|
struct ieee80211com *ic = ifp->if_l2com;
|
|
|
|
ieee80211_runtask(ic, &sc->sc_led_task);
|
|
}
|
|
|
|
static void
|
|
urtw_ledtask(void *arg, int pending)
|
|
{
|
|
struct urtw_softc *sc = arg;
|
|
|
|
if (sc->sc_strategy != URTW_SW_LED_MODE0)
|
|
panic("could not process a LED strategy 0x%x", sc->sc_strategy);
|
|
|
|
URTW_LOCK(sc);
|
|
urtw_led_blink(sc);
|
|
URTW_UNLOCK(sc);
|
|
}
|
|
|
|
static usb_error_t
|
|
urtw_led_blink(struct urtw_softc *sc)
|
|
{
|
|
uint8_t ing = 0;
|
|
usb_error_t error;
|
|
|
|
if (sc->sc_gpio_blinkstate == URTW_LED_ON)
|
|
error = urtw_led_on(sc, URTW_LED_GPIO);
|
|
else
|
|
error = urtw_led_off(sc, URTW_LED_GPIO);
|
|
sc->sc_gpio_blinktime--;
|
|
if (sc->sc_gpio_blinktime == 0)
|
|
ing = 1;
|
|
else {
|
|
if (sc->sc_gpio_ledstate != URTW_LED_BLINK_NORMAL &&
|
|
sc->sc_gpio_ledstate != URTW_LED_BLINK_SLOWLY &&
|
|
sc->sc_gpio_ledstate != URTW_LED_BLINK_CM3)
|
|
ing = 1;
|
|
}
|
|
if (ing == 1) {
|
|
if (sc->sc_gpio_ledstate == URTW_LED_ON &&
|
|
sc->sc_gpio_ledon == 0)
|
|
error = urtw_led_on(sc, URTW_LED_GPIO);
|
|
else if (sc->sc_gpio_ledstate == URTW_LED_OFF &&
|
|
sc->sc_gpio_ledon == 1)
|
|
error = urtw_led_off(sc, URTW_LED_GPIO);
|
|
|
|
sc->sc_gpio_blinktime = 0;
|
|
sc->sc_gpio_ledinprogress = 0;
|
|
return (0);
|
|
}
|
|
|
|
sc->sc_gpio_blinkstate = (sc->sc_gpio_blinkstate != URTW_LED_ON) ?
|
|
URTW_LED_ON : URTW_LED_OFF;
|
|
|
|
switch (sc->sc_gpio_ledstate) {
|
|
case URTW_LED_BLINK_NORMAL:
|
|
usb_callout_reset(&sc->sc_led_ch, hz, urtw_led_ch, sc);
|
|
break;
|
|
default:
|
|
panic("unknown LED status 0x%x", sc->sc_gpio_ledstate);
|
|
/* never reach */
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
static usb_error_t
|
|
urtw_rx_enable(struct urtw_softc *sc)
|
|
{
|
|
uint8_t data;
|
|
usb_error_t error;
|
|
|
|
usbd_transfer_start((sc->sc_flags & URTW_RTL8187B) ?
|
|
sc->sc_xfer[URTW_8187B_BULK_RX] : sc->sc_xfer[URTW_8187L_BULK_RX]);
|
|
|
|
error = urtw_rx_setconf(sc);
|
|
if (error != 0)
|
|
goto fail;
|
|
|
|
if ((sc->sc_flags & URTW_RTL8187B) == 0) {
|
|
urtw_read8_m(sc, URTW_CMD, &data);
|
|
urtw_write8_m(sc, URTW_CMD, data | URTW_CMD_RX_ENABLE);
|
|
}
|
|
fail:
|
|
return (error);
|
|
}
|
|
|
|
static usb_error_t
|
|
urtw_tx_enable(struct urtw_softc *sc)
|
|
{
|
|
uint8_t data8;
|
|
uint32_t data;
|
|
usb_error_t error;
|
|
|
|
if (sc->sc_flags & URTW_RTL8187B) {
|
|
urtw_read32_m(sc, URTW_TX_CONF, &data);
|
|
data &= ~URTW_TX_LOOPBACK_MASK;
|
|
data &= ~(URTW_TX_DPRETRY_MASK | URTW_TX_RTSRETRY_MASK);
|
|
data &= ~(URTW_TX_NOCRC | URTW_TX_MXDMA_MASK);
|
|
data &= ~URTW_TX_SWPLCPLEN;
|
|
data |= URTW_TX_HW_SEQNUM | URTW_TX_DISREQQSIZE |
|
|
(7 << 8) | /* short retry limit */
|
|
(7 << 0) | /* long retry limit */
|
|
(7 << 21); /* MAX TX DMA */
|
|
urtw_write32_m(sc, URTW_TX_CONF, data);
|
|
|
|
urtw_read8_m(sc, URTW_MSR, &data8);
|
|
data8 |= URTW_MSR_LINK_ENEDCA;
|
|
urtw_write8_m(sc, URTW_MSR, data8);
|
|
return (error);
|
|
}
|
|
|
|
urtw_read8_m(sc, URTW_CW_CONF, &data8);
|
|
data8 &= ~(URTW_CW_CONF_PERPACKET_CW | URTW_CW_CONF_PERPACKET_RETRY);
|
|
urtw_write8_m(sc, URTW_CW_CONF, data8);
|
|
|
|
urtw_read8_m(sc, URTW_TX_AGC_CTL, &data8);
|
|
data8 &= ~URTW_TX_AGC_CTL_PERPACKET_GAIN;
|
|
data8 &= ~URTW_TX_AGC_CTL_PERPACKET_ANTSEL;
|
|
data8 &= ~URTW_TX_AGC_CTL_FEEDBACK_ANT;
|
|
urtw_write8_m(sc, URTW_TX_AGC_CTL, data8);
|
|
|
|
urtw_read32_m(sc, URTW_TX_CONF, &data);
|
|
data &= ~URTW_TX_LOOPBACK_MASK;
|
|
data |= URTW_TX_LOOPBACK_NONE;
|
|
data &= ~(URTW_TX_DPRETRY_MASK | URTW_TX_RTSRETRY_MASK);
|
|
data |= sc->sc_tx_retry << URTW_TX_DPRETRY_SHIFT;
|
|
data |= sc->sc_rts_retry << URTW_TX_RTSRETRY_SHIFT;
|
|
data &= ~(URTW_TX_NOCRC | URTW_TX_MXDMA_MASK);
|
|
data |= URTW_TX_MXDMA_2048 | URTW_TX_CWMIN | URTW_TX_DISCW;
|
|
data &= ~URTW_TX_SWPLCPLEN;
|
|
data |= URTW_TX_NOICV;
|
|
urtw_write32_m(sc, URTW_TX_CONF, data);
|
|
|
|
urtw_read8_m(sc, URTW_CMD, &data8);
|
|
urtw_write8_m(sc, URTW_CMD, data8 | URTW_CMD_TX_ENABLE);
|
|
fail:
|
|
return (error);
|
|
}
|
|
|
|
static usb_error_t
|
|
urtw_rx_setconf(struct urtw_softc *sc)
|
|
{
|
|
struct ifnet *ifp = sc->sc_ifp;
|
|
struct ieee80211com *ic = ifp->if_l2com;
|
|
uint32_t data;
|
|
usb_error_t error;
|
|
|
|
urtw_read32_m(sc, URTW_RX, &data);
|
|
data = data &~ URTW_RX_FILTER_MASK;
|
|
if (sc->sc_flags & URTW_RTL8187B) {
|
|
data = data | URTW_RX_FILTER_MNG | URTW_RX_FILTER_DATA |
|
|
URTW_RX_FILTER_MCAST | URTW_RX_FILTER_BCAST |
|
|
URTW_RX_FILTER_NICMAC | URTW_RX_CHECK_BSSID |
|
|
URTW_RX_FIFO_THRESHOLD_NONE |
|
|
URTW_MAX_RX_DMA_2048 |
|
|
URTW_RX_AUTORESETPHY | URTW_RCR_ONLYERLPKT;
|
|
} else {
|
|
data = data | URTW_RX_FILTER_MNG | URTW_RX_FILTER_DATA;
|
|
data = data | URTW_RX_FILTER_BCAST | URTW_RX_FILTER_MCAST;
|
|
|
|
if (ic->ic_opmode == IEEE80211_M_MONITOR) {
|
|
data = data | URTW_RX_FILTER_ICVERR;
|
|
data = data | URTW_RX_FILTER_PWR;
|
|
}
|
|
if (sc->sc_crcmon == 1 && ic->ic_opmode == IEEE80211_M_MONITOR)
|
|
data = data | URTW_RX_FILTER_CRCERR;
|
|
|
|
if (ic->ic_opmode == IEEE80211_M_MONITOR ||
|
|
(ifp->if_flags & (IFF_ALLMULTI | IFF_PROMISC))) {
|
|
data = data | URTW_RX_FILTER_ALLMAC;
|
|
} else {
|
|
data = data | URTW_RX_FILTER_NICMAC;
|
|
data = data | URTW_RX_CHECK_BSSID;
|
|
}
|
|
|
|
data = data &~ URTW_RX_FIFO_THRESHOLD_MASK;
|
|
data = data | URTW_RX_FIFO_THRESHOLD_NONE |
|
|
URTW_RX_AUTORESETPHY;
|
|
data = data &~ URTW_MAX_RX_DMA_MASK;
|
|
data = data | URTW_MAX_RX_DMA_2048 | URTW_RCR_ONLYERLPKT;
|
|
}
|
|
|
|
urtw_write32_m(sc, URTW_RX, data);
|
|
fail:
|
|
return (error);
|
|
}
|
|
|
|
static struct mbuf *
|
|
urtw_rxeof(struct usb_xfer *xfer, struct urtw_data *data, int *rssi_p,
|
|
int8_t *nf_p)
|
|
{
|
|
int actlen, flen, rssi;
|
|
struct ieee80211_frame *wh;
|
|
struct mbuf *m, *mnew;
|
|
struct urtw_softc *sc = data->sc;
|
|
struct ifnet *ifp = sc->sc_ifp;
|
|
struct ieee80211com *ic = ifp->if_l2com;
|
|
uint8_t noise = 0, rate;
|
|
|
|
usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
|
|
|
|
if (actlen < URTW_MIN_RXBUFSZ) {
|
|
ifp->if_ierrors++;
|
|
return (NULL);
|
|
}
|
|
|
|
if (sc->sc_flags & URTW_RTL8187B) {
|
|
struct urtw_8187b_rxhdr *rx;
|
|
|
|
rx = (struct urtw_8187b_rxhdr *)(data->buf +
|
|
(actlen - (sizeof(struct urtw_8187b_rxhdr))));
|
|
flen = le32toh(rx->flag) & 0xfff;
|
|
if (flen > actlen) {
|
|
ifp->if_ierrors++;
|
|
return (NULL);
|
|
}
|
|
rate = (le32toh(rx->flag) >> URTW_RX_FLAG_RXRATE_SHIFT) & 0xf;
|
|
/* XXX correct? */
|
|
rssi = rx->rssi & URTW_RX_RSSI_MASK;
|
|
noise = rx->noise;
|
|
} else {
|
|
struct urtw_8187l_rxhdr *rx;
|
|
|
|
rx = (struct urtw_8187l_rxhdr *)(data->buf +
|
|
(actlen - (sizeof(struct urtw_8187l_rxhdr))));
|
|
flen = le32toh(rx->flag) & 0xfff;
|
|
if (flen > actlen) {
|
|
ifp->if_ierrors++;
|
|
return (NULL);
|
|
}
|
|
|
|
rate = (le32toh(rx->flag) >> URTW_RX_FLAG_RXRATE_SHIFT) & 0xf;
|
|
/* XXX correct? */
|
|
rssi = rx->rssi & URTW_RX_8187L_RSSI_MASK;
|
|
noise = rx->noise;
|
|
}
|
|
|
|
mnew = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR);
|
|
if (mnew == NULL) {
|
|
ifp->if_ierrors++;
|
|
return (NULL);
|
|
}
|
|
|
|
m = data->m;
|
|
data->m = mnew;
|
|
data->buf = mtod(mnew, uint8_t *);
|
|
|
|
/* finalize mbuf */
|
|
m->m_pkthdr.rcvif = ifp;
|
|
m->m_pkthdr.len = m->m_len = flen - IEEE80211_CRC_LEN;
|
|
|
|
if (ieee80211_radiotap_active(ic)) {
|
|
struct urtw_rx_radiotap_header *tap = &sc->sc_rxtap;
|
|
|
|
/* XXX Are variables correct? */
|
|
tap->wr_chan_freq = htole16(ic->ic_curchan->ic_freq);
|
|
tap->wr_chan_flags = htole16(ic->ic_curchan->ic_flags);
|
|
tap->wr_dbm_antsignal = (int8_t)rssi;
|
|
}
|
|
|
|
wh = mtod(m, struct ieee80211_frame *);
|
|
if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) == IEEE80211_FC0_TYPE_DATA)
|
|
sc->sc_currate = (rate > 0) ? rate : sc->sc_currate;
|
|
|
|
*rssi_p = rssi;
|
|
*nf_p = noise; /* XXX correct? */
|
|
|
|
return (m);
|
|
}
|
|
|
|
static void
|
|
urtw_bulk_rx_callback(struct usb_xfer *xfer, usb_error_t error)
|
|
{
|
|
struct urtw_softc *sc = usbd_xfer_softc(xfer);
|
|
struct ifnet *ifp = sc->sc_ifp;
|
|
struct ieee80211com *ic = ifp->if_l2com;
|
|
struct ieee80211_frame *wh;
|
|
struct ieee80211_node *ni;
|
|
struct mbuf *m = NULL;
|
|
struct urtw_data *data;
|
|
int8_t nf = -95;
|
|
int rssi = 1;
|
|
|
|
URTW_ASSERT_LOCKED(sc);
|
|
|
|
switch (USB_GET_STATE(xfer)) {
|
|
case USB_ST_TRANSFERRED:
|
|
data = STAILQ_FIRST(&sc->sc_rx_active);
|
|
if (data == NULL)
|
|
goto setup;
|
|
STAILQ_REMOVE_HEAD(&sc->sc_rx_active, next);
|
|
m = urtw_rxeof(xfer, data, &rssi, &nf);
|
|
STAILQ_INSERT_TAIL(&sc->sc_rx_inactive, data, next);
|
|
/* FALLTHROUGH */
|
|
case USB_ST_SETUP:
|
|
setup:
|
|
data = STAILQ_FIRST(&sc->sc_rx_inactive);
|
|
if (data == NULL) {
|
|
KASSERT(m == NULL, ("mbuf isn't NULL"));
|
|
return;
|
|
}
|
|
STAILQ_REMOVE_HEAD(&sc->sc_rx_inactive, next);
|
|
STAILQ_INSERT_TAIL(&sc->sc_rx_active, data, next);
|
|
usbd_xfer_set_frame_data(xfer, 0, data->buf,
|
|
usbd_xfer_max_len(xfer));
|
|
usbd_transfer_submit(xfer);
|
|
|
|
/*
|
|
* To avoid LOR we should unlock our private mutex here to call
|
|
* ieee80211_input() because here is at the end of a USB
|
|
* callback and safe to unlock.
|
|
*/
|
|
URTW_UNLOCK(sc);
|
|
if (m != NULL) {
|
|
wh = mtod(m, struct ieee80211_frame *);
|
|
ni = ieee80211_find_rxnode(ic,
|
|
(struct ieee80211_frame_min *)wh);
|
|
if (ni != NULL) {
|
|
(void) ieee80211_input(ni, m, rssi, nf);
|
|
/* node is no longer needed */
|
|
ieee80211_free_node(ni);
|
|
} else
|
|
(void) ieee80211_input_all(ic, m, rssi, nf);
|
|
m = NULL;
|
|
}
|
|
URTW_LOCK(sc);
|
|
break;
|
|
default:
|
|
/* needs it to the inactive queue due to a error. */
|
|
data = STAILQ_FIRST(&sc->sc_rx_active);
|
|
if (data != NULL) {
|
|
STAILQ_REMOVE_HEAD(&sc->sc_rx_active, next);
|
|
STAILQ_INSERT_TAIL(&sc->sc_rx_inactive, data, next);
|
|
}
|
|
if (error != USB_ERR_CANCELLED) {
|
|
usbd_xfer_set_stall(xfer);
|
|
ifp->if_ierrors++;
|
|
goto setup;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
#define URTW_STATUS_TYPE_TXCLOSE 1
|
|
#define URTW_STATUS_TYPE_BEACON_INTR 0
|
|
|
|
static void
|
|
urtw_txstatus_eof(struct usb_xfer *xfer)
|
|
{
|
|
struct urtw_softc *sc = usbd_xfer_softc(xfer);
|
|
struct ifnet *ifp = sc->sc_ifp;
|
|
int actlen, type, pktretry, seq;
|
|
uint64_t val;
|
|
|
|
usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL);
|
|
|
|
if (actlen != sizeof(uint64_t))
|
|
return;
|
|
|
|
val = le64toh(sc->sc_txstatus);
|
|
type = (val >> 30) & 0x3;
|
|
if (type == URTW_STATUS_TYPE_TXCLOSE) {
|
|
pktretry = val & 0xff;
|
|
seq = (val >> 16) & 0xff;
|
|
if (pktretry == URTW_TX_MAXRETRY)
|
|
ifp->if_oerrors++;
|
|
DPRINTF(sc, URTW_DEBUG_TXSTATUS, "pktretry %d seq %#x\n",
|
|
pktretry, seq);
|
|
}
|
|
}
|
|
|
|
static void
|
|
urtw_bulk_tx_status_callback(struct usb_xfer *xfer, usb_error_t error)
|
|
{
|
|
struct urtw_softc *sc = usbd_xfer_softc(xfer);
|
|
struct ifnet *ifp = sc->sc_ifp;
|
|
|
|
URTW_ASSERT_LOCKED(sc);
|
|
|
|
switch (USB_GET_STATE(xfer)) {
|
|
case USB_ST_TRANSFERRED:
|
|
urtw_txstatus_eof(xfer);
|
|
/* FALLTHROUGH */
|
|
case USB_ST_SETUP:
|
|
setup:
|
|
usbd_xfer_set_frame_data(xfer, 0, &sc->sc_txstatus,
|
|
sizeof(int64_t));
|
|
usbd_transfer_submit(xfer);
|
|
break;
|
|
default:
|
|
if (error != USB_ERR_CANCELLED) {
|
|
usbd_xfer_set_stall(xfer);
|
|
ifp->if_ierrors++;
|
|
goto setup;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
urtw_txeof(struct usb_xfer *xfer, struct urtw_data *data)
|
|
{
|
|
struct urtw_softc *sc = usbd_xfer_softc(xfer);
|
|
struct ifnet *ifp = sc->sc_ifp;
|
|
struct mbuf *m;
|
|
|
|
URTW_ASSERT_LOCKED(sc);
|
|
|
|
/*
|
|
* Do any tx complete callback. Note this must be done before releasing
|
|
* the node reference.
|
|
*/
|
|
if (data->m) {
|
|
m = data->m;
|
|
if (m->m_flags & M_TXCB) {
|
|
/* XXX status? */
|
|
ieee80211_process_callback(data->ni, m, 0);
|
|
}
|
|
m_freem(m);
|
|
data->m = NULL;
|
|
}
|
|
if (data->ni) {
|
|
ieee80211_free_node(data->ni);
|
|
data->ni = NULL;
|
|
}
|
|
sc->sc_txtimer = 0;
|
|
ifp->if_opackets++;
|
|
ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
|
|
}
|
|
|
|
static void
|
|
urtw_bulk_tx_callback(struct usb_xfer *xfer, usb_error_t error)
|
|
{
|
|
struct urtw_softc *sc = usbd_xfer_softc(xfer);
|
|
struct ifnet *ifp = sc->sc_ifp;
|
|
struct urtw_data *data;
|
|
|
|
URTW_ASSERT_LOCKED(sc);
|
|
|
|
switch (USB_GET_STATE(xfer)) {
|
|
case USB_ST_TRANSFERRED:
|
|
data = STAILQ_FIRST(&sc->sc_tx_active);
|
|
if (data == NULL)
|
|
goto setup;
|
|
STAILQ_REMOVE_HEAD(&sc->sc_tx_active, next);
|
|
urtw_txeof(xfer, data);
|
|
STAILQ_INSERT_TAIL(&sc->sc_tx_inactive, data, next);
|
|
/* FALLTHROUGH */
|
|
case USB_ST_SETUP:
|
|
setup:
|
|
data = STAILQ_FIRST(&sc->sc_tx_pending);
|
|
if (data == NULL) {
|
|
DPRINTF(sc, URTW_DEBUG_XMIT,
|
|
"%s: empty pending queue\n", __func__);
|
|
return;
|
|
}
|
|
STAILQ_REMOVE_HEAD(&sc->sc_tx_pending, next);
|
|
STAILQ_INSERT_TAIL(&sc->sc_tx_active, data, next);
|
|
|
|
usbd_xfer_set_frame_data(xfer, 0, data->buf, data->buflen);
|
|
usbd_transfer_submit(xfer);
|
|
|
|
URTW_UNLOCK(sc);
|
|
urtw_start(ifp);
|
|
URTW_LOCK(sc);
|
|
break;
|
|
default:
|
|
data = STAILQ_FIRST(&sc->sc_tx_active);
|
|
if (data == NULL)
|
|
goto setup;
|
|
if (data->ni != NULL) {
|
|
ieee80211_free_node(data->ni);
|
|
data->ni = NULL;
|
|
ifp->if_oerrors++;
|
|
}
|
|
if (error != USB_ERR_CANCELLED) {
|
|
usbd_xfer_set_stall(xfer);
|
|
goto setup;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
static struct urtw_data *
|
|
_urtw_getbuf(struct urtw_softc *sc)
|
|
{
|
|
struct urtw_data *bf;
|
|
|
|
bf = STAILQ_FIRST(&sc->sc_tx_inactive);
|
|
if (bf != NULL)
|
|
STAILQ_REMOVE_HEAD(&sc->sc_tx_inactive, next);
|
|
else
|
|
bf = NULL;
|
|
if (bf == NULL)
|
|
DPRINTF(sc, URTW_DEBUG_XMIT, "%s: %s\n", __func__,
|
|
"out of xmit buffers");
|
|
return (bf);
|
|
}
|
|
|
|
static struct urtw_data *
|
|
urtw_getbuf(struct urtw_softc *sc)
|
|
{
|
|
struct urtw_data *bf;
|
|
|
|
URTW_ASSERT_LOCKED(sc);
|
|
|
|
bf = _urtw_getbuf(sc);
|
|
if (bf == NULL) {
|
|
struct ifnet *ifp = sc->sc_ifp;
|
|
|
|
DPRINTF(sc, URTW_DEBUG_XMIT, "%s: stop queue\n", __func__);
|
|
ifp->if_drv_flags |= IFF_DRV_OACTIVE;
|
|
}
|
|
return (bf);
|
|
}
|
|
|
|
static int
|
|
urtw_isbmode(uint16_t rate)
|
|
{
|
|
|
|
return ((rate <= 22 && rate != 12 && rate != 18) ||
|
|
rate == 44) ? (1) : (0);
|
|
}
|
|
|
|
static uint16_t
|
|
urtw_rate2dbps(uint16_t rate)
|
|
{
|
|
|
|
switch(rate) {
|
|
case 12:
|
|
case 18:
|
|
case 24:
|
|
case 36:
|
|
case 48:
|
|
case 72:
|
|
case 96:
|
|
case 108:
|
|
return (rate * 2);
|
|
default:
|
|
break;
|
|
}
|
|
return (24);
|
|
}
|
|
|
|
static int
|
|
urtw_compute_txtime(uint16_t framelen, uint16_t rate,
|
|
uint8_t ismgt, uint8_t isshort)
|
|
{
|
|
uint16_t ceiling, frametime, n_dbps;
|
|
|
|
if (urtw_isbmode(rate)) {
|
|
if (ismgt || !isshort || rate == 2)
|
|
frametime = (uint16_t)(144 + 48 +
|
|
(framelen * 8 / (rate / 2)));
|
|
else
|
|
frametime = (uint16_t)(72 + 24 +
|
|
(framelen * 8 / (rate / 2)));
|
|
if ((framelen * 8 % (rate / 2)) != 0)
|
|
frametime++;
|
|
} else {
|
|
n_dbps = urtw_rate2dbps(rate);
|
|
ceiling = (16 + 8 * framelen + 6) / n_dbps
|
|
+ (((16 + 8 * framelen + 6) % n_dbps) ? 1 : 0);
|
|
frametime = (uint16_t)(16 + 4 + 4 * ceiling + 6);
|
|
}
|
|
return (frametime);
|
|
}
|
|
|
|
/*
|
|
* Callback from the 802.11 layer to update the
|
|
* slot time based on the current setting.
|
|
*/
|
|
static void
|
|
urtw_updateslot(struct ifnet *ifp)
|
|
{
|
|
struct urtw_softc *sc = ifp->if_softc;
|
|
struct ieee80211com *ic = ifp->if_l2com;
|
|
|
|
ieee80211_runtask(ic, &sc->sc_updateslot_task);
|
|
}
|
|
|
|
static void
|
|
urtw_updateslottask(void *arg, int pending)
|
|
{
|
|
struct urtw_softc *sc = arg;
|
|
struct ifnet *ifp = sc->sc_ifp;
|
|
struct ieee80211com *ic = ifp->if_l2com;
|
|
int error;
|
|
|
|
if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0)
|
|
return;
|
|
|
|
URTW_LOCK(sc);
|
|
if (sc->sc_flags & URTW_RTL8187B) {
|
|
urtw_write8_m(sc, URTW_SIFS, 0x22);
|
|
if (IEEE80211_IS_CHAN_ANYG(ic->ic_curchan))
|
|
urtw_write8_m(sc, URTW_SLOT, 0x9);
|
|
else
|
|
urtw_write8_m(sc, URTW_SLOT, 0x14);
|
|
urtw_write8_m(sc, URTW_8187B_EIFS, 0x5b);
|
|
urtw_write8_m(sc, URTW_CARRIER_SCOUNT, 0x5b);
|
|
} else {
|
|
urtw_write8_m(sc, URTW_SIFS, 0x22);
|
|
if (sc->sc_state == IEEE80211_S_ASSOC &&
|
|
ic->ic_flags & IEEE80211_F_SHSLOT)
|
|
urtw_write8_m(sc, URTW_SLOT, 0x9);
|
|
else
|
|
urtw_write8_m(sc, URTW_SLOT, 0x14);
|
|
if (IEEE80211_IS_CHAN_ANYG(ic->ic_curchan)) {
|
|
urtw_write8_m(sc, URTW_DIFS, 0x14);
|
|
urtw_write8_m(sc, URTW_EIFS, 0x5b - 0x14);
|
|
urtw_write8_m(sc, URTW_CW_VAL, 0x73);
|
|
} else {
|
|
urtw_write8_m(sc, URTW_DIFS, 0x24);
|
|
urtw_write8_m(sc, URTW_EIFS, 0x5b - 0x24);
|
|
urtw_write8_m(sc, URTW_CW_VAL, 0xa5);
|
|
}
|
|
}
|
|
fail:
|
|
URTW_UNLOCK(sc);
|
|
}
|
|
|
|
static void
|
|
urtw_sysctl_node(struct urtw_softc *sc)
|
|
{
|
|
#define URTW_SYSCTL_STAT_ADD32(c, h, n, p, d) \
|
|
SYSCTL_ADD_UINT(c, h, OID_AUTO, n, CTLFLAG_RD, p, 0, d)
|
|
struct sysctl_ctx_list *ctx;
|
|
struct sysctl_oid_list *child, *parent;
|
|
struct sysctl_oid *tree;
|
|
struct urtw_stats *stats = &sc->sc_stats;
|
|
|
|
ctx = device_get_sysctl_ctx(sc->sc_dev);
|
|
child = SYSCTL_CHILDREN(device_get_sysctl_tree(sc->sc_dev));
|
|
|
|
tree = SYSCTL_ADD_NODE(ctx, child, OID_AUTO, "stats", CTLFLAG_RD,
|
|
NULL, "URTW statistics");
|
|
parent = SYSCTL_CHILDREN(tree);
|
|
|
|
/* Tx statistics. */
|
|
tree = SYSCTL_ADD_NODE(ctx, parent, OID_AUTO, "tx", CTLFLAG_RD,
|
|
NULL, "Tx MAC statistics");
|
|
child = SYSCTL_CHILDREN(tree);
|
|
URTW_SYSCTL_STAT_ADD32(ctx, child, "1m", &stats->txrates[0],
|
|
"1 Mbit/s");
|
|
URTW_SYSCTL_STAT_ADD32(ctx, child, "2m", &stats->txrates[1],
|
|
"2 Mbit/s");
|
|
URTW_SYSCTL_STAT_ADD32(ctx, child, "5.5m", &stats->txrates[2],
|
|
"5.5 Mbit/s");
|
|
URTW_SYSCTL_STAT_ADD32(ctx, child, "6m", &stats->txrates[4],
|
|
"6 Mbit/s");
|
|
URTW_SYSCTL_STAT_ADD32(ctx, child, "9m", &stats->txrates[5],
|
|
"9 Mbit/s");
|
|
URTW_SYSCTL_STAT_ADD32(ctx, child, "11m", &stats->txrates[3],
|
|
"11 Mbit/s");
|
|
URTW_SYSCTL_STAT_ADD32(ctx, child, "12m", &stats->txrates[6],
|
|
"12 Mbit/s");
|
|
URTW_SYSCTL_STAT_ADD32(ctx, child, "18m", &stats->txrates[7],
|
|
"18 Mbit/s");
|
|
URTW_SYSCTL_STAT_ADD32(ctx, child, "24m", &stats->txrates[8],
|
|
"24 Mbit/s");
|
|
URTW_SYSCTL_STAT_ADD32(ctx, child, "36m", &stats->txrates[9],
|
|
"36 Mbit/s");
|
|
URTW_SYSCTL_STAT_ADD32(ctx, child, "48m", &stats->txrates[10],
|
|
"48 Mbit/s");
|
|
URTW_SYSCTL_STAT_ADD32(ctx, child, "54m", &stats->txrates[11],
|
|
"54 Mbit/s");
|
|
#undef URTW_SYSCTL_STAT_ADD32
|
|
}
|
|
|
|
static device_method_t urtw_methods[] = {
|
|
DEVMETHOD(device_probe, urtw_match),
|
|
DEVMETHOD(device_attach, urtw_attach),
|
|
DEVMETHOD(device_detach, urtw_detach),
|
|
{ 0, 0 }
|
|
};
|
|
static driver_t urtw_driver = {
|
|
"urtw",
|
|
urtw_methods,
|
|
sizeof(struct urtw_softc)
|
|
};
|
|
static devclass_t urtw_devclass;
|
|
|
|
DRIVER_MODULE(urtw, uhub, urtw_driver, urtw_devclass, NULL, 0);
|
|
MODULE_DEPEND(urtw, wlan, 1, 1, 1);
|
|
MODULE_DEPEND(urtw, usb, 1, 1, 1);
|