eabe30fc9c
that includes significant features and SMP safety. This commit includes a more or less complete rewrite of the *BSD USB stack, including Host Controller and Device Controller drivers and updating all existing USB drivers to use the new USB API: 1) A brief feature list: - A new and mutex enabled USB API. - Many USB drivers are now running Giant free. - Linux USB kernel compatibility layer. - New UGEN backend and libusb library, finally solves the "driver unloading" problem. The new BSD licensed libusb20 library is fully compatible with libusb-0.1.12 from sourceforge. - New "usbconfig" utility, for easy configuration of USB. - Full support for Split transactions, which means you can use your full speed USB audio device on a high speed USB HUB. - Full support for HS ISOC transactions, which makes writing drivers for various HS webcams possible, for example. - Full support for USB on embedded platforms, mostly cache flushing and buffer invalidating stuff. - Safer parsing of USB descriptors. - Autodetect of annoying USB install disks. - Support for USB device side mode, also called USB gadget mode, using the same API like the USB host side. In other words the new USB stack is symmetric with regard to host and device side. - Support for USB transfers like I/O vectors, means more throughput and less interrupts. - ... see the FreeBSD quarterly status reports under "USB project" 2) To enable the driver in the default kernel build: 2.a) Remove all existing USB device options from your kernel config file. 2.b) Add the following USB device options to your kernel configuration file: # USB core support device usb2_core # USB controller support device usb2_controller device usb2_controller_ehci device usb2_controller_ohci device usb2_controller_uhci # USB mass storage support device usb2_storage device usb2_storage_mass # USB ethernet support, requires miibus device usb2_ethernet device usb2_ethernet_aue device usb2_ethernet_axe device usb2_ethernet_cdce device usb2_ethernet_cue device usb2_ethernet_kue device usb2_ethernet_rue device usb2_ethernet_dav # USB wireless LAN support device usb2_wlan device usb2_wlan_rum device usb2_wlan_ral device usb2_wlan_zyd # USB serial device support device usb2_serial device usb2_serial_ark device usb2_serial_bsa device usb2_serial_bser device usb2_serial_chcom device usb2_serial_cycom device usb2_serial_foma device usb2_serial_ftdi device usb2_serial_gensa device usb2_serial_ipaq device usb2_serial_lpt device usb2_serial_mct device usb2_serial_modem device usb2_serial_moscom device usb2_serial_plcom device usb2_serial_visor device usb2_serial_vscom # USB bluetooth support device usb2_bluetooth device usb2_bluetooth_ng # USB input device support device usb2_input device usb2_input_hid device usb2_input_kbd device usb2_input_ms # USB sound and MIDI device support device usb2_sound 2) To enable the driver at runtime: 2.a) Unload all existing USB modules. If USB is compiled into the kernel then you might have to build a new kernel. 2.b) Load the "usb2_xxx.ko" modules under /boot/kernel having the same base name like the kernel device option. Submitted by: Hans Petter Selasky hselasky at c2i dot net Reviewed by: imp, alfred
2962 lines
73 KiB
C
2962 lines
73 KiB
C
/*-
|
|
* Copyright (c) 2005-2007 Damien Bergamini <damien.bergamini@free.fr>
|
|
* Copyright (c) 2006 Niall O'Higgins <niallo@openbsd.org>
|
|
* Copyright (c) 2007-2008 Hans Petter Selasky <hselasky@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.
|
|
*/
|
|
|
|
/*
|
|
* NOTE: all function names beginning like "rum_cfg_" can only
|
|
* be called from within the config thread function !
|
|
*/
|
|
|
|
#include <sys/cdefs.h>
|
|
__FBSDID("$FreeBSD$");
|
|
|
|
/*-
|
|
* Ralink Technology RT2501USB/RT2601USB chipset driver
|
|
* http://www.ralinktech.com.tw/
|
|
*/
|
|
|
|
#include <dev/usb2/include/usb2_devid.h>
|
|
#include <dev/usb2/include/usb2_standard.h>
|
|
#include <dev/usb2/include/usb2_mfunc.h>
|
|
#include <dev/usb2/include/usb2_error.h>
|
|
|
|
#define usb2_config_td_cc rum_config_copy
|
|
#define usb2_config_td_softc rum_softc
|
|
|
|
#define USB_DEBUG_VAR rum_debug
|
|
|
|
#include <dev/usb2/core/usb2_core.h>
|
|
#include <dev/usb2/core/usb2_lookup.h>
|
|
#include <dev/usb2/core/usb2_process.h>
|
|
#include <dev/usb2/core/usb2_config_td.h>
|
|
#include <dev/usb2/core/usb2_debug.h>
|
|
#include <dev/usb2/core/usb2_request.h>
|
|
#include <dev/usb2/core/usb2_busdma.h>
|
|
#include <dev/usb2/core/usb2_util.h>
|
|
|
|
#include <dev/usb2/wlan/usb2_wlan.h>
|
|
#include <dev/usb2/wlan/if_rum2_reg.h>
|
|
#include <dev/usb2/wlan/if_rum2_var.h>
|
|
#include <dev/usb2/wlan/if_rum2_fw.h>
|
|
|
|
#if USB_DEBUG
|
|
static int rum_debug = 0;
|
|
|
|
SYSCTL_NODE(_hw_usb2, OID_AUTO, rum, CTLFLAG_RW, 0, "USB rum");
|
|
SYSCTL_INT(_hw_usb2_rum, OID_AUTO, debug, CTLFLAG_RW, &rum_debug, 0,
|
|
"Debug level");
|
|
#endif
|
|
|
|
/* prototypes */
|
|
|
|
static device_probe_t rum_probe;
|
|
static device_attach_t rum_attach;
|
|
static device_detach_t rum_detach;
|
|
|
|
static usb2_callback_t rum_bulk_read_callback;
|
|
static usb2_callback_t rum_bulk_read_clear_stall_callback;
|
|
static usb2_callback_t rum_bulk_write_callback;
|
|
static usb2_callback_t rum_bulk_write_clear_stall_callback;
|
|
|
|
static usb2_config_td_command_t rum_cfg_first_time_setup;
|
|
static usb2_config_td_command_t rum_config_copy;
|
|
static usb2_config_td_command_t rum_cfg_scan_start;
|
|
static usb2_config_td_command_t rum_cfg_scan_end;
|
|
static usb2_config_td_command_t rum_cfg_select_band;
|
|
static usb2_config_td_command_t rum_cfg_set_chan;
|
|
static usb2_config_td_command_t rum_cfg_enable_tsf_sync;
|
|
static usb2_config_td_command_t rum_cfg_enable_mrr;
|
|
static usb2_config_td_command_t rum_cfg_update_slot;
|
|
static usb2_config_td_command_t rum_cfg_select_antenna;
|
|
static usb2_config_td_command_t rum_cfg_set_txpreamble;
|
|
static usb2_config_td_command_t rum_cfg_update_promisc;
|
|
static usb2_config_td_command_t rum_cfg_pre_init;
|
|
static usb2_config_td_command_t rum_cfg_init;
|
|
static usb2_config_td_command_t rum_cfg_pre_stop;
|
|
static usb2_config_td_command_t rum_cfg_stop;
|
|
static usb2_config_td_command_t rum_cfg_amrr_timeout;
|
|
static usb2_config_td_command_t rum_cfg_prepare_beacon;
|
|
static usb2_config_td_command_t rum_cfg_newstate;
|
|
|
|
static const char *rum_get_rf(uint32_t rev);
|
|
static int rum_ioctl_cb(struct ifnet *ifp, u_long cmd, caddr_t data);
|
|
static void rum_std_command(struct ieee80211com *ic, usb2_config_td_command_t *func);
|
|
static void rum_scan_start_cb(struct ieee80211com *);
|
|
static void rum_scan_end_cb(struct ieee80211com *);
|
|
static void rum_set_channel_cb(struct ieee80211com *);
|
|
static uint16_t rum_cfg_eeprom_read_2(struct rum_softc *sc, uint16_t addr);
|
|
static uint32_t rum_cfg_bbp_disbusy(struct rum_softc *sc);
|
|
static uint32_t rum_cfg_read(struct rum_softc *sc, uint16_t reg);
|
|
static uint8_t rum_cfg_bbp_init(struct rum_softc *sc);
|
|
static uint8_t rum_cfg_bbp_read(struct rum_softc *sc, uint8_t reg);
|
|
static void rum_cfg_amrr_start(struct rum_softc *sc);
|
|
static void rum_cfg_bbp_write(struct rum_softc *sc, uint8_t reg, uint8_t val);
|
|
static void rum_cfg_do_request(struct rum_softc *sc, struct usb2_device_request *req, void *data);
|
|
static void rum_cfg_eeprom_read(struct rum_softc *sc, uint16_t addr, void *buf, uint16_t len);
|
|
static void rum_cfg_load_microcode(struct rum_softc *sc, const uint8_t *ucode, uint16_t size);
|
|
static void rum_cfg_read_eeprom(struct rum_softc *sc);
|
|
static void rum_cfg_read_multi(struct rum_softc *sc, uint16_t reg, void *buf, uint16_t len);
|
|
static void rum_cfg_rf_write(struct rum_softc *sc, uint8_t reg, uint32_t val);
|
|
static void rum_cfg_set_bssid(struct rum_softc *sc, uint8_t *bssid);
|
|
static void rum_cfg_set_macaddr(struct rum_softc *sc, uint8_t *addr);
|
|
static void rum_cfg_write(struct rum_softc *sc, uint16_t reg, uint32_t val);
|
|
static void rum_cfg_write_multi(struct rum_softc *sc, uint16_t reg, void *buf, uint16_t len);
|
|
static void rum_end_of_commands(struct rum_softc *sc);
|
|
static void rum_init_cb(void *arg);
|
|
static void rum_start_cb(struct ifnet *ifp);
|
|
static void rum_watchdog(void *arg);
|
|
static uint8_t rum_get_rssi(struct rum_softc *sc, uint8_t raw);
|
|
static struct ieee80211vap *rum_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]);
|
|
static void rum_vap_delete(struct ieee80211vap *);
|
|
static struct ieee80211_node *rum_node_alloc(struct ieee80211vap *vap, const uint8_t mac[IEEE80211_ADDR_LEN]);
|
|
static void rum_newassoc(struct ieee80211_node *, int);
|
|
static void rum_cfg_disable_tsf_sync(struct rum_softc *sc);
|
|
static void rum_cfg_set_run(struct rum_softc *sc, struct rum_config_copy *cc);
|
|
static void rum_fill_write_queue(struct rum_softc *sc);
|
|
static void rum_tx_clean_queue(struct rum_softc *sc);
|
|
static void rum_tx_freem(struct mbuf *m);
|
|
static void rum_tx_mgt(struct rum_softc *sc, struct mbuf *m, struct ieee80211_node *ni);
|
|
static struct ieee80211vap *rum_get_vap(struct rum_softc *sc);
|
|
static void rum_tx_data(struct rum_softc *sc, struct mbuf *m, struct ieee80211_node *ni);
|
|
static void rum_tx_prot(struct rum_softc *sc, const struct mbuf *m, struct ieee80211_node *ni, uint8_t prot, uint16_t rate);
|
|
static void rum_tx_raw(struct rum_softc *sc, struct mbuf *m, struct ieee80211_node *ni, const struct ieee80211_bpf_params *params);
|
|
static int rum_raw_xmit_cb(struct ieee80211_node *ni, struct mbuf *m, const struct ieee80211_bpf_params *params);
|
|
static void rum_setup_desc_and_tx(struct rum_softc *sc, struct mbuf *m, uint32_t flags, uint16_t xflags, uint16_t rate);
|
|
static int rum_newstate_cb(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg);
|
|
static void rum_update_mcast_cb(struct ifnet *ifp);
|
|
static void rum_update_promisc_cb(struct ifnet *ifp);
|
|
|
|
/* various supported device vendors/products */
|
|
static const struct usb2_device_id rum_devs[] = {
|
|
{USB_VPI(USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_HWU54DM, 0)},
|
|
{USB_VPI(USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_RT2573_2, 0)},
|
|
{USB_VPI(USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_RT2573_3, 0)},
|
|
{USB_VPI(USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_RT2573_4, 0)},
|
|
{USB_VPI(USB_VENDOR_ABOCOM, USB_PRODUCT_ABOCOM_WUG2700, 0)},
|
|
{USB_VPI(USB_VENDOR_AMIT, USB_PRODUCT_AMIT_CGWLUSB2GO, 0)},
|
|
{USB_VPI(USB_VENDOR_ASUS, USB_PRODUCT_ASUS_RT2573_1, 0)},
|
|
{USB_VPI(USB_VENDOR_ASUS, USB_PRODUCT_ASUS_RT2573_2, 0)},
|
|
{USB_VPI(USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_F5D7050A, 0)},
|
|
{USB_VPI(USB_VENDOR_BELKIN, USB_PRODUCT_BELKIN_F5D9050V3, 0)},
|
|
{USB_VPI(USB_VENDOR_CISCOLINKSYS, USB_PRODUCT_CISCOLINKSYS_WUSB54GC, 0)},
|
|
{USB_VPI(USB_VENDOR_CISCOLINKSYS, USB_PRODUCT_CISCOLINKSYS_WUSB54GR, 0)},
|
|
{USB_VPI(USB_VENDOR_CONCEPTRONIC2, USB_PRODUCT_CONCEPTRONIC2_C54RU2, 0)},
|
|
{USB_VPI(USB_VENDOR_COREGA, USB_PRODUCT_COREGA_CGWLUSB2GL, 0)},
|
|
{USB_VPI(USB_VENDOR_COREGA, USB_PRODUCT_COREGA_CGWLUSB2GPX, 0)},
|
|
{USB_VPI(USB_VENDOR_DICKSMITH, USB_PRODUCT_DICKSMITH_CWD854F, 0)},
|
|
{USB_VPI(USB_VENDOR_DICKSMITH, USB_PRODUCT_DICKSMITH_RT2573, 0)},
|
|
{USB_VPI(USB_VENDOR_DLINK2, USB_PRODUCT_DLINK2_DWLG122C1, 0)},
|
|
{USB_VPI(USB_VENDOR_DLINK2, USB_PRODUCT_DLINK2_WUA1340, 0)},
|
|
{USB_VPI(USB_VENDOR_DLINK2, USB_PRODUCT_DLINK2_DWA111, 0)},
|
|
{USB_VPI(USB_VENDOR_DLINK2, USB_PRODUCT_DLINK2_DWA110, 0)},
|
|
{USB_VPI(USB_VENDOR_GIGABYTE, USB_PRODUCT_GIGABYTE_GNWB01GS, 0)},
|
|
{USB_VPI(USB_VENDOR_GIGABYTE, USB_PRODUCT_GIGABYTE_GNWI05GS, 0)},
|
|
{USB_VPI(USB_VENDOR_GIGASET, USB_PRODUCT_GIGASET_RT2573, 0)},
|
|
{USB_VPI(USB_VENDOR_GOODWAY, USB_PRODUCT_GOODWAY_RT2573, 0)},
|
|
{USB_VPI(USB_VENDOR_GUILLEMOT, USB_PRODUCT_GUILLEMOT_HWGUSB254LB, 0)},
|
|
{USB_VPI(USB_VENDOR_GUILLEMOT, USB_PRODUCT_GUILLEMOT_HWGUSB254V2AP, 0)},
|
|
{USB_VPI(USB_VENDOR_HUAWEI3COM, USB_PRODUCT_HUAWEI3COM_WUB320G, 0)},
|
|
{USB_VPI(USB_VENDOR_MELCO, USB_PRODUCT_MELCO_G54HP, 0)},
|
|
{USB_VPI(USB_VENDOR_MELCO, USB_PRODUCT_MELCO_SG54HP, 0)},
|
|
{USB_VPI(USB_VENDOR_MSI, USB_PRODUCT_MSI_RT2573_1, 0)},
|
|
{USB_VPI(USB_VENDOR_MSI, USB_PRODUCT_MSI_RT2573_2, 0)},
|
|
{USB_VPI(USB_VENDOR_MSI, USB_PRODUCT_MSI_RT2573_3, 0)},
|
|
{USB_VPI(USB_VENDOR_MSI, USB_PRODUCT_MSI_RT2573_4, 0)},
|
|
{USB_VPI(USB_VENDOR_NOVATECH, USB_PRODUCT_NOVATECH_RT2573, 0)},
|
|
{USB_VPI(USB_VENDOR_PLANEX2, USB_PRODUCT_PLANEX2_GWUS54HP, 0)},
|
|
{USB_VPI(USB_VENDOR_PLANEX2, USB_PRODUCT_PLANEX2_GWUS54MINI2, 0)},
|
|
{USB_VPI(USB_VENDOR_PLANEX2, USB_PRODUCT_PLANEX2_GWUSMM, 0)},
|
|
{USB_VPI(USB_VENDOR_QCOM, USB_PRODUCT_QCOM_RT2573, 0)},
|
|
{USB_VPI(USB_VENDOR_QCOM, USB_PRODUCT_QCOM_RT2573_2, 0)},
|
|
{USB_VPI(USB_VENDOR_RALINK, USB_PRODUCT_RALINK_RT2573, 0)},
|
|
{USB_VPI(USB_VENDOR_RALINK, USB_PRODUCT_RALINK_RT2573_2, 0)},
|
|
{USB_VPI(USB_VENDOR_RALINK, USB_PRODUCT_RALINK_RT2671, 0)},
|
|
{USB_VPI(USB_VENDOR_SITECOMEU, USB_PRODUCT_SITECOMEU_WL113R2, 0)},
|
|
{USB_VPI(USB_VENDOR_SITECOMEU, USB_PRODUCT_SITECOMEU_WL172, 0)},
|
|
{USB_VPI(USB_VENDOR_SPARKLAN, USB_PRODUCT_SPARKLAN_RT2573, 0)},
|
|
{USB_VPI(USB_VENDOR_SURECOM, USB_PRODUCT_SURECOM_RT2573, 0)},
|
|
};
|
|
|
|
struct rum_def_mac {
|
|
uint32_t reg;
|
|
uint32_t val;
|
|
};
|
|
|
|
static const struct rum_def_mac rum_def_mac[] = {
|
|
{RT2573_TXRX_CSR0, 0x025fb032},
|
|
{RT2573_TXRX_CSR1, 0x9eaa9eaf},
|
|
{RT2573_TXRX_CSR2, 0x8a8b8c8d},
|
|
{RT2573_TXRX_CSR3, 0x00858687},
|
|
{RT2573_TXRX_CSR7, 0x2e31353b},
|
|
{RT2573_TXRX_CSR8, 0x2a2a2a2c},
|
|
{RT2573_TXRX_CSR15, 0x0000000f},
|
|
{RT2573_MAC_CSR6, 0x00000fff},
|
|
{RT2573_MAC_CSR8, 0x016c030a},
|
|
{RT2573_MAC_CSR10, 0x00000718},
|
|
{RT2573_MAC_CSR12, 0x00000004},
|
|
{RT2573_MAC_CSR13, 0x00007f00},
|
|
{RT2573_SEC_CSR0, 0x00000000},
|
|
{RT2573_SEC_CSR1, 0x00000000},
|
|
{RT2573_SEC_CSR5, 0x00000000},
|
|
{RT2573_PHY_CSR1, 0x000023b0},
|
|
{RT2573_PHY_CSR5, 0x00040a06},
|
|
{RT2573_PHY_CSR6, 0x00080606},
|
|
{RT2573_PHY_CSR7, 0x00000408},
|
|
{RT2573_AIFSN_CSR, 0x00002273},
|
|
{RT2573_CWMIN_CSR, 0x00002344},
|
|
{RT2573_CWMAX_CSR, 0x000034aa}
|
|
};
|
|
|
|
struct rum_def_bbp {
|
|
uint8_t reg;
|
|
uint8_t val;
|
|
};
|
|
|
|
static const struct rum_def_bbp rum_def_bbp[] = {
|
|
{3, 0x80},
|
|
{15, 0x30},
|
|
{17, 0x20},
|
|
{21, 0xc8},
|
|
{22, 0x38},
|
|
{23, 0x06},
|
|
{24, 0xfe},
|
|
{25, 0x0a},
|
|
{26, 0x0d},
|
|
{32, 0x0b},
|
|
{34, 0x12},
|
|
{37, 0x07},
|
|
{39, 0xf8},
|
|
{41, 0x60},
|
|
{53, 0x10},
|
|
{54, 0x18},
|
|
{60, 0x10},
|
|
{61, 0x04},
|
|
{62, 0x04},
|
|
{75, 0xfe},
|
|
{86, 0xfe},
|
|
{88, 0xfe},
|
|
{90, 0x0f},
|
|
{99, 0x00},
|
|
{102, 0x16},
|
|
{107, 0x04}
|
|
};
|
|
|
|
struct rfprog {
|
|
uint8_t chan;
|
|
uint32_t r1, r2, r3, r4;
|
|
};
|
|
|
|
static const struct rfprog rum_rf5226[] = {
|
|
{1, 0x00b03, 0x001e1, 0x1a014, 0x30282},
|
|
{2, 0x00b03, 0x001e1, 0x1a014, 0x30287},
|
|
{3, 0x00b03, 0x001e2, 0x1a014, 0x30282},
|
|
{4, 0x00b03, 0x001e2, 0x1a014, 0x30287},
|
|
{5, 0x00b03, 0x001e3, 0x1a014, 0x30282},
|
|
{6, 0x00b03, 0x001e3, 0x1a014, 0x30287},
|
|
{7, 0x00b03, 0x001e4, 0x1a014, 0x30282},
|
|
{8, 0x00b03, 0x001e4, 0x1a014, 0x30287},
|
|
{9, 0x00b03, 0x001e5, 0x1a014, 0x30282},
|
|
{10, 0x00b03, 0x001e5, 0x1a014, 0x30287},
|
|
{11, 0x00b03, 0x001e6, 0x1a014, 0x30282},
|
|
{12, 0x00b03, 0x001e6, 0x1a014, 0x30287},
|
|
{13, 0x00b03, 0x001e7, 0x1a014, 0x30282},
|
|
{14, 0x00b03, 0x001e8, 0x1a014, 0x30284},
|
|
|
|
{34, 0x00b03, 0x20266, 0x36014, 0x30282},
|
|
{38, 0x00b03, 0x20267, 0x36014, 0x30284},
|
|
{42, 0x00b03, 0x20268, 0x36014, 0x30286},
|
|
{46, 0x00b03, 0x20269, 0x36014, 0x30288},
|
|
|
|
{36, 0x00b03, 0x00266, 0x26014, 0x30288},
|
|
{40, 0x00b03, 0x00268, 0x26014, 0x30280},
|
|
{44, 0x00b03, 0x00269, 0x26014, 0x30282},
|
|
{48, 0x00b03, 0x0026a, 0x26014, 0x30284},
|
|
{52, 0x00b03, 0x0026b, 0x26014, 0x30286},
|
|
{56, 0x00b03, 0x0026c, 0x26014, 0x30288},
|
|
{60, 0x00b03, 0x0026e, 0x26014, 0x30280},
|
|
{64, 0x00b03, 0x0026f, 0x26014, 0x30282},
|
|
|
|
{100, 0x00b03, 0x0028a, 0x2e014, 0x30280},
|
|
{104, 0x00b03, 0x0028b, 0x2e014, 0x30282},
|
|
{108, 0x00b03, 0x0028c, 0x2e014, 0x30284},
|
|
{112, 0x00b03, 0x0028d, 0x2e014, 0x30286},
|
|
{116, 0x00b03, 0x0028e, 0x2e014, 0x30288},
|
|
{120, 0x00b03, 0x002a0, 0x2e014, 0x30280},
|
|
{124, 0x00b03, 0x002a1, 0x2e014, 0x30282},
|
|
{128, 0x00b03, 0x002a2, 0x2e014, 0x30284},
|
|
{132, 0x00b03, 0x002a3, 0x2e014, 0x30286},
|
|
{136, 0x00b03, 0x002a4, 0x2e014, 0x30288},
|
|
{140, 0x00b03, 0x002a6, 0x2e014, 0x30280},
|
|
|
|
{149, 0x00b03, 0x002a8, 0x2e014, 0x30287},
|
|
{153, 0x00b03, 0x002a9, 0x2e014, 0x30289},
|
|
{157, 0x00b03, 0x002ab, 0x2e014, 0x30281},
|
|
{161, 0x00b03, 0x002ac, 0x2e014, 0x30283},
|
|
{165, 0x00b03, 0x002ad, 0x2e014, 0x30285}
|
|
};
|
|
|
|
static const struct rfprog rum_rf5225[] = {
|
|
{1, 0x00b33, 0x011e1, 0x1a014, 0x30282},
|
|
{2, 0x00b33, 0x011e1, 0x1a014, 0x30287},
|
|
{3, 0x00b33, 0x011e2, 0x1a014, 0x30282},
|
|
{4, 0x00b33, 0x011e2, 0x1a014, 0x30287},
|
|
{5, 0x00b33, 0x011e3, 0x1a014, 0x30282},
|
|
{6, 0x00b33, 0x011e3, 0x1a014, 0x30287},
|
|
{7, 0x00b33, 0x011e4, 0x1a014, 0x30282},
|
|
{8, 0x00b33, 0x011e4, 0x1a014, 0x30287},
|
|
{9, 0x00b33, 0x011e5, 0x1a014, 0x30282},
|
|
{10, 0x00b33, 0x011e5, 0x1a014, 0x30287},
|
|
{11, 0x00b33, 0x011e6, 0x1a014, 0x30282},
|
|
{12, 0x00b33, 0x011e6, 0x1a014, 0x30287},
|
|
{13, 0x00b33, 0x011e7, 0x1a014, 0x30282},
|
|
{14, 0x00b33, 0x011e8, 0x1a014, 0x30284},
|
|
|
|
{34, 0x00b33, 0x01266, 0x26014, 0x30282},
|
|
{38, 0x00b33, 0x01267, 0x26014, 0x30284},
|
|
{42, 0x00b33, 0x01268, 0x26014, 0x30286},
|
|
{46, 0x00b33, 0x01269, 0x26014, 0x30288},
|
|
|
|
{36, 0x00b33, 0x01266, 0x26014, 0x30288},
|
|
{40, 0x00b33, 0x01268, 0x26014, 0x30280},
|
|
{44, 0x00b33, 0x01269, 0x26014, 0x30282},
|
|
{48, 0x00b33, 0x0126a, 0x26014, 0x30284},
|
|
{52, 0x00b33, 0x0126b, 0x26014, 0x30286},
|
|
{56, 0x00b33, 0x0126c, 0x26014, 0x30288},
|
|
{60, 0x00b33, 0x0126e, 0x26014, 0x30280},
|
|
{64, 0x00b33, 0x0126f, 0x26014, 0x30282},
|
|
|
|
{100, 0x00b33, 0x0128a, 0x2e014, 0x30280},
|
|
{104, 0x00b33, 0x0128b, 0x2e014, 0x30282},
|
|
{108, 0x00b33, 0x0128c, 0x2e014, 0x30284},
|
|
{112, 0x00b33, 0x0128d, 0x2e014, 0x30286},
|
|
{116, 0x00b33, 0x0128e, 0x2e014, 0x30288},
|
|
{120, 0x00b33, 0x012a0, 0x2e014, 0x30280},
|
|
{124, 0x00b33, 0x012a1, 0x2e014, 0x30282},
|
|
{128, 0x00b33, 0x012a2, 0x2e014, 0x30284},
|
|
{132, 0x00b33, 0x012a3, 0x2e014, 0x30286},
|
|
{136, 0x00b33, 0x012a4, 0x2e014, 0x30288},
|
|
{140, 0x00b33, 0x012a6, 0x2e014, 0x30280},
|
|
|
|
{149, 0x00b33, 0x012a8, 0x2e014, 0x30287},
|
|
{153, 0x00b33, 0x012a9, 0x2e014, 0x30289},
|
|
{157, 0x00b33, 0x012ab, 0x2e014, 0x30281},
|
|
{161, 0x00b33, 0x012ac, 0x2e014, 0x30283},
|
|
{165, 0x00b33, 0x012ad, 0x2e014, 0x30285}
|
|
};
|
|
|
|
static const struct usb2_config rum_config[RUM_N_TRANSFER] = {
|
|
[0] = {
|
|
.type = UE_BULK,
|
|
.endpoint = UE_ADDR_ANY,
|
|
.direction = UE_DIR_OUT,
|
|
.mh.bufsize = (MCLBYTES + RT2573_TX_DESC_SIZE + 8),
|
|
.mh.flags = {.pipe_bof = 1,.force_short_xfer = 1,},
|
|
.mh.callback = &rum_bulk_write_callback,
|
|
.mh.timeout = 5000, /* ms */
|
|
},
|
|
|
|
[1] = {
|
|
.type = UE_BULK,
|
|
.endpoint = UE_ADDR_ANY,
|
|
.direction = UE_DIR_IN,
|
|
.mh.bufsize = (MCLBYTES + RT2573_RX_DESC_SIZE),
|
|
.mh.flags = {.pipe_bof = 1,.short_xfer_ok = 1,},
|
|
.mh.callback = &rum_bulk_read_callback,
|
|
},
|
|
|
|
[2] = {
|
|
.type = UE_CONTROL,
|
|
.endpoint = 0x00, /* Control pipe */
|
|
.direction = UE_DIR_ANY,
|
|
.mh.bufsize = sizeof(struct usb2_device_request),
|
|
.mh.callback = &rum_bulk_write_clear_stall_callback,
|
|
.mh.timeout = 1000, /* 1 second */
|
|
.mh.interval = 50, /* 50ms */
|
|
},
|
|
|
|
[3] = {
|
|
.type = UE_CONTROL,
|
|
.endpoint = 0x00, /* Control pipe */
|
|
.direction = UE_DIR_ANY,
|
|
.mh.bufsize = sizeof(struct usb2_device_request),
|
|
.mh.callback = &rum_bulk_read_clear_stall_callback,
|
|
.mh.timeout = 1000, /* 1 second */
|
|
.mh.interval = 50, /* 50ms */
|
|
},
|
|
};
|
|
|
|
static devclass_t rum_devclass;
|
|
|
|
static device_method_t rum_methods[] = {
|
|
DEVMETHOD(device_probe, rum_probe),
|
|
DEVMETHOD(device_attach, rum_attach),
|
|
DEVMETHOD(device_detach, rum_detach),
|
|
{0, 0}
|
|
};
|
|
|
|
static driver_t rum_driver = {
|
|
.name = "rum",
|
|
.methods = rum_methods,
|
|
.size = sizeof(struct rum_softc),
|
|
};
|
|
|
|
DRIVER_MODULE(rum, ushub, rum_driver, rum_devclass, NULL, 0);
|
|
MODULE_DEPEND(rum, usb2_wlan, 1, 1, 1);
|
|
MODULE_DEPEND(rum, usb2_core, 1, 1, 1);
|
|
MODULE_DEPEND(rum, wlan, 1, 1, 1);
|
|
MODULE_DEPEND(rum, wlan_amrr, 1, 1, 1);
|
|
|
|
static int
|
|
rum_probe(device_t dev)
|
|
{
|
|
struct usb2_attach_arg *uaa = device_get_ivars(dev);
|
|
|
|
if (uaa->usb2_mode != USB_MODE_HOST) {
|
|
return (ENXIO);
|
|
}
|
|
if (uaa->info.bConfigIndex != 0) {
|
|
return (ENXIO);
|
|
}
|
|
if (uaa->info.bIfaceIndex != RT2573_IFACE_INDEX) {
|
|
return (ENXIO);
|
|
}
|
|
return (usb2_lookup_id_by_uaa(rum_devs, sizeof(rum_devs), uaa));
|
|
}
|
|
|
|
static int
|
|
rum_attach(device_t dev)
|
|
{
|
|
struct usb2_attach_arg *uaa = device_get_ivars(dev);
|
|
struct rum_softc *sc = device_get_softc(dev);
|
|
int error;
|
|
uint8_t iface_index;
|
|
|
|
if (sc == NULL) {
|
|
return (ENOMEM);
|
|
}
|
|
device_set_usb2_desc(dev);
|
|
|
|
mtx_init(&sc->sc_mtx, "rum lock", MTX_NETWORK_LOCK,
|
|
MTX_DEF | MTX_RECURSE);
|
|
|
|
snprintf(sc->sc_name, sizeof(sc->sc_name), "%s",
|
|
device_get_nameunit(dev));
|
|
|
|
sc->sc_udev = uaa->device;
|
|
sc->sc_unit = device_get_unit(dev);
|
|
|
|
usb2_callout_init_mtx(&sc->sc_watchdog,
|
|
&sc->sc_mtx, CALLOUT_RETURNUNLOCKED);
|
|
|
|
iface_index = RT2573_IFACE_INDEX;
|
|
error = usb2_transfer_setup(uaa->device, &iface_index,
|
|
sc->sc_xfer, rum_config, RUM_N_TRANSFER, sc, &sc->sc_mtx);
|
|
if (error) {
|
|
device_printf(dev, "could not allocate USB transfers, "
|
|
"err=%s\n", usb2_errstr(error));
|
|
goto detach;
|
|
}
|
|
error = usb2_config_td_setup(&sc->sc_config_td, sc, &sc->sc_mtx,
|
|
&rum_end_of_commands,
|
|
sizeof(struct usb2_config_td_cc), 24);
|
|
if (error) {
|
|
device_printf(dev, "could not setup config "
|
|
"thread!\n");
|
|
goto detach;
|
|
}
|
|
mtx_lock(&sc->sc_mtx);
|
|
|
|
/* start setup */
|
|
|
|
usb2_config_td_queue_command
|
|
(&sc->sc_config_td, NULL, &rum_cfg_first_time_setup, 0, 0);
|
|
|
|
/* start watchdog (will exit mutex) */
|
|
|
|
rum_watchdog(sc);
|
|
|
|
return (0); /* success */
|
|
|
|
detach:
|
|
rum_detach(dev);
|
|
return (ENXIO); /* failure */
|
|
}
|
|
|
|
static int
|
|
rum_detach(device_t dev)
|
|
{
|
|
struct rum_softc *sc = device_get_softc(dev);
|
|
struct ieee80211com *ic;
|
|
struct ifnet *ifp;
|
|
|
|
usb2_config_td_drain(&sc->sc_config_td);
|
|
|
|
mtx_lock(&sc->sc_mtx);
|
|
|
|
usb2_callout_stop(&sc->sc_watchdog);
|
|
|
|
rum_cfg_pre_stop(sc, NULL, 0);
|
|
|
|
ifp = sc->sc_ifp;
|
|
ic = ifp->if_l2com;
|
|
|
|
mtx_unlock(&sc->sc_mtx);
|
|
|
|
/* stop all USB transfers first */
|
|
usb2_transfer_unsetup(sc->sc_xfer, RUM_N_TRANSFER);
|
|
|
|
/* get rid of any late children */
|
|
bus_generic_detach(dev);
|
|
|
|
if (ifp) {
|
|
bpfdetach(ifp);
|
|
ieee80211_ifdetach(ic);
|
|
if_free(ifp);
|
|
}
|
|
usb2_config_td_unsetup(&sc->sc_config_td);
|
|
|
|
usb2_callout_drain(&sc->sc_watchdog);
|
|
|
|
mtx_destroy(&sc->sc_mtx);
|
|
|
|
return (0);
|
|
}
|
|
|
|
static void
|
|
rum_cfg_do_request(struct rum_softc *sc, struct usb2_device_request *req,
|
|
void *data)
|
|
{
|
|
uint16_t length;
|
|
usb2_error_t err;
|
|
|
|
repeat:
|
|
|
|
if (usb2_config_td_is_gone(&sc->sc_config_td)) {
|
|
goto error;
|
|
}
|
|
err = usb2_do_request_flags
|
|
(sc->sc_udev, &sc->sc_mtx, req, data, 0, NULL, 1000);
|
|
|
|
if (err) {
|
|
|
|
DPRINTF("device request failed, err=%s "
|
|
"(ignored)\n", usb2_errstr(err));
|
|
|
|
/* wait a little before next try */
|
|
if (usb2_config_td_sleep(&sc->sc_config_td, hz / 4)) {
|
|
goto error;
|
|
}
|
|
/* try until we are detached */
|
|
goto repeat;
|
|
|
|
error:
|
|
/* the device has been detached */
|
|
length = UGETW(req->wLength);
|
|
|
|
if ((req->bmRequestType & UT_READ) && length) {
|
|
bzero(data, length);
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
static void
|
|
rum_cfg_eeprom_read(struct rum_softc *sc, uint16_t addr, void *buf, uint16_t len)
|
|
{
|
|
struct usb2_device_request req;
|
|
|
|
req.bmRequestType = UT_READ_VENDOR_DEVICE;
|
|
req.bRequest = RT2573_READ_EEPROM;
|
|
USETW(req.wValue, 0);
|
|
USETW(req.wIndex, addr);
|
|
USETW(req.wLength, len);
|
|
|
|
rum_cfg_do_request(sc, &req, buf);
|
|
return;
|
|
}
|
|
|
|
static uint16_t
|
|
rum_cfg_eeprom_read_2(struct rum_softc *sc, uint16_t addr)
|
|
{
|
|
uint16_t tmp;
|
|
|
|
rum_cfg_eeprom_read(sc, addr, &tmp, sizeof(tmp));
|
|
return (le16toh(tmp));
|
|
}
|
|
|
|
static uint32_t
|
|
rum_cfg_read(struct rum_softc *sc, uint16_t reg)
|
|
{
|
|
uint32_t val;
|
|
|
|
rum_cfg_read_multi(sc, reg, &val, sizeof(val));
|
|
return (le32toh(val));
|
|
}
|
|
|
|
static void
|
|
rum_cfg_read_multi(struct rum_softc *sc, uint16_t reg, void *buf, uint16_t len)
|
|
{
|
|
struct usb2_device_request req;
|
|
|
|
req.bmRequestType = UT_READ_VENDOR_DEVICE;
|
|
req.bRequest = RT2573_READ_MULTI_MAC;
|
|
USETW(req.wValue, 0);
|
|
USETW(req.wIndex, reg);
|
|
USETW(req.wLength, len);
|
|
|
|
rum_cfg_do_request(sc, &req, buf);
|
|
return;
|
|
}
|
|
|
|
static void
|
|
rum_cfg_write(struct rum_softc *sc, uint16_t reg, uint32_t val)
|
|
{
|
|
uint32_t tmp = htole32(val);
|
|
|
|
rum_cfg_write_multi(sc, reg, &tmp, sizeof(tmp));
|
|
return;
|
|
}
|
|
|
|
static void
|
|
rum_cfg_write_multi(struct rum_softc *sc, uint16_t reg, void *buf, uint16_t len)
|
|
{
|
|
struct usb2_device_request req;
|
|
|
|
req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
|
|
req.bRequest = RT2573_WRITE_MULTI_MAC;
|
|
USETW(req.wValue, 0);
|
|
USETW(req.wIndex, reg);
|
|
USETW(req.wLength, len);
|
|
|
|
rum_cfg_do_request(sc, &req, buf);
|
|
return;
|
|
}
|
|
|
|
static uint32_t
|
|
rum_cfg_bbp_disbusy(struct rum_softc *sc)
|
|
{
|
|
uint32_t tmp;
|
|
uint8_t to;
|
|
|
|
for (to = 0;; to++) {
|
|
if (to < 100) {
|
|
tmp = rum_cfg_read(sc, RT2573_PHY_CSR3);
|
|
|
|
if ((tmp & RT2573_BBP_BUSY) == 0) {
|
|
return (tmp);
|
|
}
|
|
if (usb2_config_td_sleep(&sc->sc_config_td, hz / 100)) {
|
|
break;
|
|
}
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
DPRINTF("could not disbusy BBP\n");
|
|
return (RT2573_BBP_BUSY); /* failure */
|
|
}
|
|
|
|
static void
|
|
rum_cfg_bbp_write(struct rum_softc *sc, uint8_t reg, uint8_t val)
|
|
{
|
|
uint32_t tmp;
|
|
|
|
if (rum_cfg_bbp_disbusy(sc) & RT2573_BBP_BUSY) {
|
|
return;
|
|
}
|
|
tmp = RT2573_BBP_BUSY | ((reg & 0x7f) << 8) | val;
|
|
rum_cfg_write(sc, RT2573_PHY_CSR3, tmp);
|
|
return;
|
|
}
|
|
|
|
static uint8_t
|
|
rum_cfg_bbp_read(struct rum_softc *sc, uint8_t reg)
|
|
{
|
|
uint32_t val;
|
|
|
|
if (rum_cfg_bbp_disbusy(sc) & RT2573_BBP_BUSY) {
|
|
return (0);
|
|
}
|
|
val = RT2573_BBP_BUSY | RT2573_BBP_READ | (reg << 8);
|
|
rum_cfg_write(sc, RT2573_PHY_CSR3, val);
|
|
|
|
val = rum_cfg_bbp_disbusy(sc);
|
|
return (val & 0xff);
|
|
}
|
|
|
|
static void
|
|
rum_cfg_rf_write(struct rum_softc *sc, uint8_t reg, uint32_t val)
|
|
{
|
|
uint32_t tmp;
|
|
uint8_t to;
|
|
|
|
reg &= 3;
|
|
|
|
for (to = 0;; to++) {
|
|
if (to < 100) {
|
|
tmp = rum_cfg_read(sc, RT2573_PHY_CSR4);
|
|
if (!(tmp & RT2573_RF_BUSY)) {
|
|
break;
|
|
}
|
|
if (usb2_config_td_sleep(&sc->sc_config_td, hz / 100)) {
|
|
return;
|
|
}
|
|
} else {
|
|
DPRINTF("could not write to RF\n");
|
|
return;
|
|
}
|
|
}
|
|
|
|
tmp = RT2573_RF_BUSY | RT2573_RF_20BIT | ((val & 0xfffff) << 2) | reg;
|
|
rum_cfg_write(sc, RT2573_PHY_CSR4, tmp);
|
|
|
|
DPRINTFN(16, "RF R[%u] <- 0x%05x\n", reg, val & 0xfffff);
|
|
return;
|
|
}
|
|
|
|
static void
|
|
rum_cfg_first_time_setup(struct rum_softc *sc,
|
|
struct usb2_config_td_cc *cc, uint16_t refcount)
|
|
{
|
|
struct ieee80211com *ic;
|
|
struct ifnet *ifp;
|
|
uint32_t tmp;
|
|
uint16_t i;
|
|
uint8_t bands;
|
|
|
|
/* setup RX tap header */
|
|
sc->sc_rxtap_len = sizeof(sc->sc_rxtap);
|
|
sc->sc_rxtap.wr_ihdr.it_len = htole16(sc->sc_rxtap_len);
|
|
sc->sc_rxtap.wr_ihdr.it_present = htole32(RT2573_RX_RADIOTAP_PRESENT);
|
|
|
|
/* setup TX tap header */
|
|
sc->sc_txtap_len = sizeof(sc->sc_txtap);
|
|
sc->sc_txtap.wt_ihdr.it_len = htole16(sc->sc_txtap_len);
|
|
sc->sc_txtap.wt_ihdr.it_present = htole32(RT2573_TX_RADIOTAP_PRESENT);
|
|
|
|
/* retrieve RT2573 rev. no */
|
|
for (i = 0; i < 100; i++) {
|
|
|
|
tmp = rum_cfg_read(sc, RT2573_MAC_CSR0);
|
|
if (tmp != 0) {
|
|
break;
|
|
}
|
|
/* wait a little */
|
|
if (usb2_config_td_sleep(&sc->sc_config_td, hz / 100)) {
|
|
/* device detached */
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
if (tmp == 0) {
|
|
DPRINTF("chip is maybe not ready\n");
|
|
}
|
|
/* retrieve MAC address and various other things from EEPROM */
|
|
rum_cfg_read_eeprom(sc);
|
|
|
|
printf("%s: MAC/BBP RT2573 (rev 0x%05x), RF %s\n",
|
|
sc->sc_name, tmp, rum_get_rf(sc->sc_rf_rev));
|
|
|
|
rum_cfg_load_microcode(sc, rt2573_ucode, sizeof(rt2573_ucode));
|
|
|
|
mtx_unlock(&sc->sc_mtx);
|
|
|
|
ifp = if_alloc(IFT_IEEE80211);
|
|
|
|
mtx_lock(&sc->sc_mtx);
|
|
|
|
if (ifp == NULL) {
|
|
DPRINTFN(0, "could not if_alloc()!\n");
|
|
goto done;
|
|
}
|
|
sc->sc_evilhack = ifp;
|
|
sc->sc_ifp = ifp;
|
|
ic = ifp->if_l2com;
|
|
|
|
ifp->if_softc = sc;
|
|
if_initname(ifp, "rum", sc->sc_unit);
|
|
ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
|
|
ifp->if_init = &rum_init_cb;
|
|
ifp->if_ioctl = &rum_ioctl_cb;
|
|
ifp->if_start = &rum_start_cb;
|
|
ifp->if_watchdog = NULL;
|
|
IFQ_SET_MAXLEN(&ifp->if_snd, IFQ_MAXLEN);
|
|
ifp->if_snd.ifq_drv_maxlen = IFQ_MAXLEN;
|
|
IFQ_SET_READY(&ifp->if_snd);
|
|
|
|
bcopy(sc->sc_myaddr, ic->ic_myaddr, sizeof(ic->ic_myaddr));
|
|
|
|
ic->ic_ifp = ifp;
|
|
ic->ic_phytype = IEEE80211_T_OFDM; /* not only, but not used */
|
|
ic->ic_opmode = IEEE80211_M_STA;
|
|
|
|
/* set device capabilities */
|
|
ic->ic_caps =
|
|
IEEE80211_C_STA /* station mode supported */
|
|
| IEEE80211_C_IBSS /* IBSS mode supported */
|
|
| IEEE80211_C_MONITOR /* monitor mode supported */
|
|
| IEEE80211_C_HOSTAP /* HostAp mode supported */
|
|
| IEEE80211_C_TXPMGT /* tx power management */
|
|
| IEEE80211_C_SHPREAMBLE /* short preamble supported */
|
|
| IEEE80211_C_SHSLOT /* short slot time supported */
|
|
| IEEE80211_C_BGSCAN /* bg scanning supported */
|
|
| IEEE80211_C_WPA /* 802.11i */
|
|
;
|
|
|
|
bands = 0;
|
|
setbit(&bands, IEEE80211_MODE_11B);
|
|
setbit(&bands, IEEE80211_MODE_11G);
|
|
ieee80211_init_channels(ic, NULL, &bands);
|
|
|
|
if ((sc->sc_rf_rev == RT2573_RF_5225) ||
|
|
(sc->sc_rf_rev == RT2573_RF_5226)) {
|
|
|
|
struct ieee80211_channel *c;
|
|
|
|
/* set supported .11a channels */
|
|
for (i = 34; i <= 46; i += 4) {
|
|
c = ic->ic_channels + (ic->ic_nchans++);
|
|
c->ic_freq = ieee80211_ieee2mhz(i, IEEE80211_CHAN_5GHZ);
|
|
c->ic_flags = IEEE80211_CHAN_A;
|
|
c->ic_ieee = i;
|
|
}
|
|
for (i = 36; i <= 64; i += 4) {
|
|
c = ic->ic_channels + (ic->ic_nchans++);
|
|
c->ic_freq = ieee80211_ieee2mhz(i, IEEE80211_CHAN_5GHZ);
|
|
c->ic_flags = IEEE80211_CHAN_A;
|
|
c->ic_ieee = i;
|
|
}
|
|
for (i = 100; i <= 140; i += 4) {
|
|
c = ic->ic_channels + (ic->ic_nchans++);
|
|
c->ic_freq = ieee80211_ieee2mhz(i, IEEE80211_CHAN_5GHZ);
|
|
c->ic_flags = IEEE80211_CHAN_A;
|
|
c->ic_ieee = i;
|
|
}
|
|
for (i = 149; i <= 165; i += 4) {
|
|
c = ic->ic_channels + (ic->ic_nchans++);
|
|
c->ic_freq = ieee80211_ieee2mhz(i, IEEE80211_CHAN_5GHZ);
|
|
c->ic_flags = IEEE80211_CHAN_A;
|
|
c->ic_ieee = i;
|
|
}
|
|
}
|
|
mtx_unlock(&sc->sc_mtx);
|
|
|
|
ieee80211_ifattach(ic);
|
|
|
|
mtx_lock(&sc->sc_mtx);
|
|
|
|
ic->ic_newassoc = &rum_newassoc;
|
|
ic->ic_raw_xmit = &rum_raw_xmit_cb;
|
|
ic->ic_node_alloc = &rum_node_alloc;
|
|
ic->ic_update_mcast = &rum_update_mcast_cb;
|
|
ic->ic_update_promisc = &rum_update_promisc_cb;
|
|
ic->ic_scan_start = &rum_scan_start_cb;
|
|
ic->ic_scan_end = &rum_scan_end_cb;
|
|
ic->ic_set_channel = &rum_set_channel_cb;
|
|
ic->ic_vap_create = &rum_vap_create;
|
|
ic->ic_vap_delete = &rum_vap_delete;
|
|
|
|
sc->sc_rates = ieee80211_get_ratetable(ic->ic_curchan);
|
|
|
|
mtx_unlock(&sc->sc_mtx);
|
|
|
|
bpfattach(ifp, DLT_IEEE802_11_RADIO,
|
|
sizeof(struct ieee80211_frame) + sizeof(sc->sc_txtap));
|
|
|
|
if (bootverbose) {
|
|
ieee80211_announce(ic);
|
|
}
|
|
mtx_lock(&sc->sc_mtx);
|
|
done:
|
|
return;
|
|
}
|
|
|
|
static void
|
|
rum_end_of_commands(struct rum_softc *sc)
|
|
{
|
|
sc->sc_flags &= ~RUM_FLAG_WAIT_COMMAND;
|
|
|
|
/* start write transfer, if not started */
|
|
usb2_transfer_start(sc->sc_xfer[0]);
|
|
return;
|
|
}
|
|
|
|
static void
|
|
rum_config_copy_chan(struct rum_config_copy_chan *cc,
|
|
struct ieee80211com *ic, struct ieee80211_channel *c)
|
|
{
|
|
if (!c)
|
|
return;
|
|
cc->chan_to_ieee =
|
|
ieee80211_chan2ieee(ic, c);
|
|
if (c != IEEE80211_CHAN_ANYC) {
|
|
cc->chan_to_mode =
|
|
ieee80211_chan2mode(c);
|
|
if (IEEE80211_IS_CHAN_B(c))
|
|
cc->chan_is_b = 1;
|
|
if (IEEE80211_IS_CHAN_A(c))
|
|
cc->chan_is_a = 1;
|
|
if (IEEE80211_IS_CHAN_2GHZ(c))
|
|
cc->chan_is_2ghz = 1;
|
|
if (IEEE80211_IS_CHAN_5GHZ(c))
|
|
cc->chan_is_5ghz = 1;
|
|
if (IEEE80211_IS_CHAN_ANYG(c))
|
|
cc->chan_is_g = 1;
|
|
}
|
|
return;
|
|
}
|
|
|
|
static void
|
|
rum_config_copy(struct rum_softc *sc,
|
|
struct usb2_config_td_cc *cc, uint16_t refcount)
|
|
{
|
|
struct ifnet *ifp;
|
|
struct ieee80211com *ic;
|
|
struct ieee80211_node *ni;
|
|
struct ieee80211vap *vap;
|
|
const struct ieee80211_txparam *tp;
|
|
|
|
bzero(cc, sizeof(*cc));
|
|
|
|
ifp = sc->sc_ifp;
|
|
if (ifp) {
|
|
cc->if_flags = ifp->if_flags;
|
|
bcopy(ifp->if_broadcastaddr, cc->if_broadcastaddr,
|
|
sizeof(cc->if_broadcastaddr));
|
|
|
|
ic = ifp->if_l2com;
|
|
if (ic) {
|
|
rum_config_copy_chan(&cc->ic_curchan, ic, ic->ic_curchan);
|
|
rum_config_copy_chan(&cc->ic_bsschan, ic, ic->ic_bsschan);
|
|
vap = TAILQ_FIRST(&ic->ic_vaps);
|
|
if (vap) {
|
|
ni = vap->iv_bss;
|
|
if (ni) {
|
|
cc->iv_bss.ni_intval = ni->ni_intval;
|
|
bcopy(ni->ni_bssid, cc->iv_bss.ni_bssid,
|
|
sizeof(cc->iv_bss.ni_bssid));
|
|
}
|
|
tp = vap->iv_txparms + cc->ic_bsschan.chan_to_mode;
|
|
if (tp->ucastrate == IEEE80211_FIXED_RATE_NONE) {
|
|
cc->iv_bss.fixed_rate_none = 1;
|
|
}
|
|
}
|
|
cc->ic_opmode = ic->ic_opmode;
|
|
cc->ic_flags = ic->ic_flags;
|
|
cc->ic_txpowlimit = ic->ic_txpowlimit;
|
|
cc->ic_curmode = ic->ic_curmode;
|
|
|
|
bcopy(ic->ic_myaddr, cc->ic_myaddr,
|
|
sizeof(cc->ic_myaddr));
|
|
}
|
|
}
|
|
sc->sc_flags |= RUM_FLAG_WAIT_COMMAND;
|
|
return;
|
|
}
|
|
|
|
static const char *
|
|
rum_get_rf(uint32_t rev)
|
|
{
|
|
; /* indent fix */
|
|
switch (rev) {
|
|
case RT2573_RF_2527:
|
|
return "RT2527 (MIMO XR)";
|
|
case RT2573_RF_2528:
|
|
return "RT2528";
|
|
case RT2573_RF_5225:
|
|
return "RT5225 (MIMO XR)";
|
|
case RT2573_RF_5226:
|
|
return "RT5226";
|
|
default:
|
|
return "unknown";
|
|
}
|
|
}
|
|
|
|
static void
|
|
rum_bulk_read_callback(struct usb2_xfer *xfer)
|
|
{
|
|
struct rum_softc *sc = xfer->priv_sc;
|
|
struct ifnet *ifp = sc->sc_ifp;
|
|
struct ieee80211com *ic = ifp->if_l2com;
|
|
struct ieee80211_node *ni;
|
|
|
|
struct mbuf *m = NULL;
|
|
uint32_t flags;
|
|
uint32_t max_len;
|
|
uint8_t rssi = 0;
|
|
|
|
switch (USB_GET_STATE(xfer)) {
|
|
case USB_ST_TRANSFERRED:
|
|
|
|
DPRINTFN(15, "rx done, actlen=%d\n", xfer->actlen);
|
|
|
|
if (xfer->actlen < (RT2573_RX_DESC_SIZE + IEEE80211_MIN_LEN)) {
|
|
DPRINTF("too short transfer, "
|
|
"%d bytes\n", xfer->actlen);
|
|
ifp->if_ierrors++;
|
|
goto tr_setup;
|
|
}
|
|
usb2_copy_out(xfer->frbuffers, 0,
|
|
&sc->sc_rx_desc, RT2573_RX_DESC_SIZE);
|
|
|
|
flags = le32toh(sc->sc_rx_desc.flags);
|
|
|
|
if (flags & RT2573_RX_CRC_ERROR) {
|
|
/*
|
|
* This should not happen since we did not
|
|
* request to receive those frames when we
|
|
* filled RAL_TXRX_CSR2:
|
|
*/
|
|
DPRINTFN(6, "PHY or CRC error\n");
|
|
ifp->if_ierrors++;
|
|
goto tr_setup;
|
|
}
|
|
m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR);
|
|
|
|
if (m == NULL) {
|
|
DPRINTF("could not allocate mbuf\n");
|
|
ifp->if_ierrors++;
|
|
goto tr_setup;
|
|
}
|
|
max_len = (xfer->actlen - RT2573_RX_DESC_SIZE);
|
|
|
|
usb2_copy_out(xfer->frbuffers, RT2573_RX_DESC_SIZE,
|
|
m->m_data, max_len);
|
|
|
|
/* finalize mbuf */
|
|
m->m_pkthdr.rcvif = ifp;
|
|
m->m_pkthdr.len = m->m_len = (flags >> 16) & 0xfff;
|
|
|
|
if (m->m_len > max_len) {
|
|
DPRINTF("invalid length in RX "
|
|
"descriptor, %u bytes, received %u bytes\n",
|
|
m->m_len, max_len);
|
|
ifp->if_ierrors++;
|
|
m_freem(m);
|
|
m = NULL;
|
|
goto tr_setup;
|
|
}
|
|
rssi = rum_get_rssi(sc, sc->sc_rx_desc.rssi);
|
|
|
|
DPRINTF("real length=%d bytes, rssi=%d\n", m->m_len, rssi);
|
|
|
|
if (bpf_peers_present(ifp->if_bpf)) {
|
|
struct rum_rx_radiotap_header *tap = &sc->sc_rxtap;
|
|
|
|
tap->wr_flags = IEEE80211_RADIOTAP_F_FCS;
|
|
tap->wr_rate = ieee80211_plcp2rate(sc->sc_rx_desc.rate,
|
|
(sc->sc_rx_desc.flags & htole32(RT2573_RX_OFDM)) ?
|
|
IEEE80211_T_OFDM : IEEE80211_T_CCK);
|
|
tap->wr_chan_freq = htole16(ic->ic_curchan->ic_freq);
|
|
tap->wr_chan_flags = htole16(ic->ic_curchan->ic_flags);
|
|
tap->wr_antenna = sc->sc_rx_ant;
|
|
tap->wr_antsignal = rssi;
|
|
|
|
bpf_mtap2(ifp->if_bpf, tap, sc->sc_rxtap_len, m);
|
|
}
|
|
case USB_ST_SETUP:
|
|
tr_setup:
|
|
|
|
if (sc->sc_flags & RUM_FLAG_READ_STALL) {
|
|
usb2_transfer_start(sc->sc_xfer[3]);
|
|
} else {
|
|
xfer->frlengths[0] = xfer->max_data_length;
|
|
usb2_start_hardware(xfer);
|
|
}
|
|
|
|
/*
|
|
* At the end of a USB callback it is always safe to unlock
|
|
* the private mutex of a device! That is why we do the
|
|
* "ieee80211_input" here, and not some lines up!
|
|
*/
|
|
if (m) {
|
|
mtx_unlock(&sc->sc_mtx);
|
|
|
|
ni = ieee80211_find_rxnode(ic, mtod(m, struct ieee80211_frame_min *));
|
|
if (ni != NULL) {
|
|
if (ieee80211_input(ni, m, rssi, RT2573_NOISE_FLOOR, 0)) {
|
|
/* ignore */
|
|
}
|
|
/* node is no longer needed */
|
|
ieee80211_free_node(ni);
|
|
} else {
|
|
if (ieee80211_input_all(ic, m, rssi, RT2573_NOISE_FLOOR, 0)) {
|
|
/* ignore */
|
|
}
|
|
}
|
|
|
|
mtx_lock(&sc->sc_mtx);
|
|
}
|
|
return;
|
|
|
|
default: /* Error */
|
|
if (xfer->error != USB_ERR_CANCELLED) {
|
|
/* try to clear stall first */
|
|
sc->sc_flags |= RUM_FLAG_READ_STALL;
|
|
usb2_transfer_start(sc->sc_xfer[3]);
|
|
}
|
|
return;
|
|
|
|
}
|
|
}
|
|
|
|
static void
|
|
rum_bulk_read_clear_stall_callback(struct usb2_xfer *xfer)
|
|
{
|
|
struct rum_softc *sc = xfer->priv_sc;
|
|
struct usb2_xfer *xfer_other = sc->sc_xfer[1];
|
|
|
|
if (usb2_clear_stall_callback(xfer, xfer_other)) {
|
|
DPRINTF("stall cleared\n");
|
|
sc->sc_flags &= ~RUM_FLAG_READ_STALL;
|
|
usb2_transfer_start(xfer_other);
|
|
}
|
|
return;
|
|
}
|
|
|
|
static uint8_t
|
|
rum_plcp_signal(uint16_t rate)
|
|
{
|
|
; /* indent fix */
|
|
switch (rate) {
|
|
/* CCK rates (NB: not IEEE std, device-specific) */
|
|
case 2:
|
|
return (0x0);
|
|
case 4:
|
|
return (0x1);
|
|
case 11:
|
|
return (0x2);
|
|
case 22:
|
|
return (0x3);
|
|
|
|
/* OFDM rates (cf IEEE Std 802.11a-1999, pp. 14 Table 80) */
|
|
case 12:
|
|
return (0xb);
|
|
case 18:
|
|
return (0xf);
|
|
case 24:
|
|
return (0xa);
|
|
case 36:
|
|
return (0xe);
|
|
case 48:
|
|
return (0x9);
|
|
case 72:
|
|
return (0xd);
|
|
case 96:
|
|
return (0x8);
|
|
case 108:
|
|
return (0xc);
|
|
|
|
/* XXX unsupported/unknown rate */
|
|
default:
|
|
return (0xff);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* We assume that "m->m_pkthdr.rcvif" is pointing to the "ni" that
|
|
* should be freed, when "rum_setup_desc_and_tx" is called.
|
|
*/
|
|
|
|
static void
|
|
rum_setup_desc_and_tx(struct rum_softc *sc, struct mbuf *m, uint32_t flags,
|
|
uint16_t xflags, uint16_t rate)
|
|
{
|
|
struct ifnet *ifp = sc->sc_ifp;
|
|
struct ieee80211com *ic = ifp->if_l2com;
|
|
struct mbuf *mm;
|
|
enum ieee80211_phytype phytype;
|
|
uint16_t plcp_length;
|
|
uint16_t len;
|
|
uint8_t remainder;
|
|
uint8_t is_beacon;
|
|
|
|
if (xflags & RT2573_TX_BEACON) {
|
|
xflags &= ~RT2573_TX_BEACON;
|
|
is_beacon = 1;
|
|
} else {
|
|
is_beacon = 0;
|
|
}
|
|
|
|
if (sc->sc_tx_queue.ifq_len >= IFQ_MAXLEN) {
|
|
/* free packet */
|
|
rum_tx_freem(m);
|
|
ifp->if_oerrors++;
|
|
return;
|
|
}
|
|
if (!((sc->sc_flags & RUM_FLAG_LL_READY) &&
|
|
(sc->sc_flags & RUM_FLAG_HL_READY))) {
|
|
/* free packet */
|
|
rum_tx_freem(m);
|
|
ifp->if_oerrors++;
|
|
return;
|
|
}
|
|
if (rate < 2) {
|
|
DPRINTF("rate < 2!\n");
|
|
|
|
/* avoid division by zero */
|
|
rate = 2;
|
|
}
|
|
ic->ic_lastdata = ticks;
|
|
if (bpf_peers_present(ifp->if_bpf)) {
|
|
struct rum_tx_radiotap_header *tap = &sc->sc_txtap;
|
|
|
|
tap->wt_flags = 0;
|
|
tap->wt_rate = rate;
|
|
tap->wt_chan_freq = htole16(ic->ic_curchan->ic_freq);
|
|
tap->wt_chan_flags = htole16(ic->ic_curchan->ic_flags);
|
|
tap->wt_antenna = sc->sc_tx_ant;
|
|
|
|
bpf_mtap2(ifp->if_bpf, tap, sc->sc_txtap_len, m);
|
|
}
|
|
len = m->m_pkthdr.len;
|
|
|
|
flags |= RT2573_TX_VALID;
|
|
flags |= (len << 16);
|
|
|
|
sc->sc_tx_desc.flags = htole32(flags);
|
|
sc->sc_tx_desc.xflags = htole16(xflags);
|
|
|
|
sc->sc_tx_desc.wme = htole16(RT2573_QID(0) | RT2573_AIFSN(2) |
|
|
RT2573_LOGCWMIN(4) | RT2573_LOGCWMAX(10));
|
|
|
|
/* setup PLCP fields */
|
|
sc->sc_tx_desc.plcp_signal = rum_plcp_signal(rate);
|
|
sc->sc_tx_desc.plcp_service = 4;
|
|
|
|
len += IEEE80211_CRC_LEN;
|
|
|
|
phytype = ieee80211_rate2phytype(sc->sc_rates, rate);
|
|
|
|
if (phytype == IEEE80211_T_OFDM) {
|
|
sc->sc_tx_desc.flags |= htole32(RT2573_TX_OFDM);
|
|
|
|
plcp_length = (len & 0xfff);
|
|
sc->sc_tx_desc.plcp_length_hi = plcp_length >> 6;
|
|
sc->sc_tx_desc.plcp_length_lo = plcp_length & 0x3f;
|
|
} else {
|
|
plcp_length = ((16 * len) + rate - 1) / rate;
|
|
if (rate == 22) {
|
|
remainder = (16 * len) % 22;
|
|
if ((remainder != 0) && (remainder < 7)) {
|
|
sc->sc_tx_desc.plcp_service |=
|
|
RT2573_PLCP_LENGEXT;
|
|
}
|
|
}
|
|
sc->sc_tx_desc.plcp_length_hi = plcp_length >> 8;
|
|
sc->sc_tx_desc.plcp_length_lo = plcp_length & 0xff;
|
|
|
|
if ((rate != 2) && (ic->ic_flags & IEEE80211_F_SHPREAMBLE)) {
|
|
sc->sc_tx_desc.plcp_signal |= 0x08;
|
|
}
|
|
}
|
|
|
|
if (sizeof(sc->sc_tx_desc) > MHLEN) {
|
|
DPRINTF("No room for header structure!\n");
|
|
rum_tx_freem(m);
|
|
return;
|
|
}
|
|
mm = m_gethdr(M_NOWAIT, MT_DATA);
|
|
if (mm == NULL) {
|
|
DPRINTF("Could not allocate header mbuf!\n");
|
|
rum_tx_freem(m);
|
|
return;
|
|
}
|
|
bcopy(&sc->sc_tx_desc, mm->m_data, sizeof(sc->sc_tx_desc));
|
|
mm->m_len = sizeof(sc->sc_tx_desc);
|
|
mm->m_next = m;
|
|
mm->m_pkthdr.len = mm->m_len + m->m_pkthdr.len;
|
|
mm->m_pkthdr.rcvif = NULL;
|
|
|
|
if (is_beacon) {
|
|
|
|
if (mm->m_pkthdr.len > sizeof(sc->sc_beacon_buf)) {
|
|
DPRINTFN(0, "Truncating beacon"
|
|
", %u bytes!\n", mm->m_pkthdr.len);
|
|
mm->m_pkthdr.len = sizeof(sc->sc_beacon_buf);
|
|
}
|
|
m_copydata(mm, 0, mm->m_pkthdr.len, sc->sc_beacon_buf);
|
|
|
|
/* copy the first 24 bytes of Tx descriptor into NIC memory */
|
|
rum_cfg_write_multi(sc, RT2573_HW_BEACON_BASE0,
|
|
sc->sc_beacon_buf, mm->m_pkthdr.len);
|
|
rum_tx_freem(mm);
|
|
return;
|
|
}
|
|
/* start write transfer, if not started */
|
|
_IF_ENQUEUE(&sc->sc_tx_queue, mm);
|
|
|
|
usb2_transfer_start(sc->sc_xfer[0]);
|
|
return;
|
|
}
|
|
|
|
static void
|
|
rum_bulk_write_callback(struct usb2_xfer *xfer)
|
|
{
|
|
struct rum_softc *sc = xfer->priv_sc;
|
|
struct ifnet *ifp = sc->sc_ifp;
|
|
struct mbuf *m;
|
|
uint16_t temp_len;
|
|
uint8_t align;
|
|
|
|
switch (USB_GET_STATE(xfer)) {
|
|
case USB_ST_TRANSFERRED:
|
|
DPRINTFN(11, "transfer complete\n");
|
|
|
|
ifp->if_opackets++;
|
|
|
|
case USB_ST_SETUP:
|
|
if (sc->sc_flags & RUM_FLAG_WRITE_STALL) {
|
|
usb2_transfer_start(sc->sc_xfer[2]);
|
|
break;
|
|
}
|
|
if (sc->sc_flags & RUM_FLAG_WAIT_COMMAND) {
|
|
/*
|
|
* don't send anything while a command is pending !
|
|
*/
|
|
break;
|
|
}
|
|
rum_fill_write_queue(sc);
|
|
|
|
_IF_DEQUEUE(&sc->sc_tx_queue, m);
|
|
|
|
if (m) {
|
|
|
|
if (m->m_pkthdr.len > (MCLBYTES + RT2573_TX_DESC_SIZE)) {
|
|
DPRINTFN(0, "data overflow, %u bytes\n",
|
|
m->m_pkthdr.len);
|
|
m->m_pkthdr.len = (MCLBYTES + RT2573_TX_DESC_SIZE);
|
|
}
|
|
usb2_m_copy_in(xfer->frbuffers, 0,
|
|
m, 0, m->m_pkthdr.len);
|
|
|
|
/* compute transfer length */
|
|
temp_len = m->m_pkthdr.len;
|
|
|
|
/* make transfer length 32-bit aligned */
|
|
align = (-(temp_len)) & 3;
|
|
|
|
/* check if we need to add four extra bytes */
|
|
if (((temp_len + align) % 64) == 0) {
|
|
align += 4;
|
|
}
|
|
/* check if we need to align length */
|
|
if (align != 0) {
|
|
/* zero the extra bytes */
|
|
usb2_bzero(xfer->frbuffers, temp_len, align);
|
|
temp_len += align;
|
|
}
|
|
DPRINTFN(11, "sending frame len=%u ferlen=%u\n",
|
|
m->m_pkthdr.len, temp_len);
|
|
|
|
xfer->frlengths[0] = temp_len;
|
|
usb2_start_hardware(xfer);
|
|
|
|
/* free mbuf and node */
|
|
rum_tx_freem(m);
|
|
|
|
}
|
|
break;
|
|
|
|
default: /* Error */
|
|
DPRINTFN(11, "transfer error, %s\n",
|
|
usb2_errstr(xfer->error));
|
|
|
|
if (xfer->error != USB_ERR_CANCELLED) {
|
|
/* try to clear stall first */
|
|
sc->sc_flags |= RUM_FLAG_WRITE_STALL;
|
|
usb2_transfer_start(sc->sc_xfer[2]);
|
|
}
|
|
ifp->if_oerrors++;
|
|
break;
|
|
}
|
|
return;
|
|
}
|
|
|
|
static void
|
|
rum_bulk_write_clear_stall_callback(struct usb2_xfer *xfer)
|
|
{
|
|
struct rum_softc *sc = xfer->priv_sc;
|
|
struct usb2_xfer *xfer_other = sc->sc_xfer[0];
|
|
|
|
if (usb2_clear_stall_callback(xfer, xfer_other)) {
|
|
DPRINTF("stall cleared\n");
|
|
sc->sc_flags &= ~RUM_FLAG_WRITE_STALL;
|
|
usb2_transfer_start(xfer_other);
|
|
}
|
|
return;
|
|
}
|
|
|
|
static void
|
|
rum_watchdog(void *arg)
|
|
{
|
|
struct rum_softc *sc = arg;
|
|
|
|
mtx_assert(&sc->sc_mtx, MA_OWNED);
|
|
|
|
if (sc->sc_amrr_timer) {
|
|
usb2_config_td_queue_command
|
|
(&sc->sc_config_td, NULL,
|
|
&rum_cfg_amrr_timeout, 0, 0);
|
|
}
|
|
usb2_callout_reset(&sc->sc_watchdog,
|
|
hz, &rum_watchdog, sc);
|
|
|
|
mtx_unlock(&sc->sc_mtx);
|
|
|
|
return;
|
|
}
|
|
|
|
static void
|
|
rum_init_cb(void *arg)
|
|
{
|
|
struct rum_softc *sc = arg;
|
|
|
|
mtx_lock(&sc->sc_mtx);
|
|
usb2_config_td_queue_command
|
|
(&sc->sc_config_td, &rum_cfg_pre_init,
|
|
&rum_cfg_init, 0, 0);
|
|
mtx_unlock(&sc->sc_mtx);
|
|
|
|
return;
|
|
}
|
|
|
|
static int
|
|
rum_ioctl_cb(struct ifnet *ifp, u_long cmd, caddr_t data)
|
|
{
|
|
struct rum_softc *sc = ifp->if_softc;
|
|
struct ieee80211com *ic = ifp->if_l2com;
|
|
int error;
|
|
|
|
switch (cmd) {
|
|
case SIOCSIFFLAGS:
|
|
mtx_lock(&sc->sc_mtx);
|
|
if (ifp->if_flags & IFF_UP) {
|
|
if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) {
|
|
usb2_config_td_queue_command
|
|
(&sc->sc_config_td, &rum_cfg_pre_init,
|
|
&rum_cfg_init, 0, 0);
|
|
}
|
|
} else {
|
|
if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
|
|
usb2_config_td_queue_command
|
|
(&sc->sc_config_td, &rum_cfg_pre_stop,
|
|
&rum_cfg_stop, 0, 0);
|
|
}
|
|
}
|
|
mtx_unlock(&sc->sc_mtx);
|
|
error = 0;
|
|
break;
|
|
|
|
case SIOCGIFMEDIA:
|
|
case SIOCSIFMEDIA:
|
|
error = ifmedia_ioctl(ifp, (void *)data, &ic->ic_media, cmd);
|
|
break;
|
|
|
|
default:
|
|
error = ether_ioctl(ifp, cmd, data);
|
|
}
|
|
return (error);
|
|
}
|
|
|
|
static void
|
|
rum_start_cb(struct ifnet *ifp)
|
|
{
|
|
struct rum_softc *sc = ifp->if_softc;
|
|
|
|
mtx_lock(&sc->sc_mtx);
|
|
/* start write transfer, if not started */
|
|
usb2_transfer_start(sc->sc_xfer[0]);
|
|
mtx_unlock(&sc->sc_mtx);
|
|
|
|
return;
|
|
}
|
|
|
|
static void
|
|
rum_cfg_newstate(struct rum_softc *sc,
|
|
struct usb2_config_td_cc *cc, uint16_t refcount)
|
|
{
|
|
struct ifnet *ifp = sc->sc_ifp;
|
|
struct ieee80211com *ic = ifp->if_l2com;
|
|
struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps);
|
|
struct rum_vap *uvp = RUM_VAP(vap);
|
|
enum ieee80211_state ostate;
|
|
enum ieee80211_state nstate;
|
|
int arg;
|
|
|
|
ostate = vap->iv_state;
|
|
nstate = sc->sc_ns_state;
|
|
arg = sc->sc_ns_arg;
|
|
|
|
if (ostate == IEEE80211_S_INIT) {
|
|
/* We are leaving INIT. TSF sync should be off. */
|
|
rum_cfg_disable_tsf_sync(sc);
|
|
}
|
|
switch (nstate) {
|
|
case IEEE80211_S_INIT:
|
|
break;
|
|
|
|
case IEEE80211_S_RUN:
|
|
rum_cfg_set_run(sc, cc);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
mtx_unlock(&sc->sc_mtx);
|
|
IEEE80211_LOCK(ic);
|
|
uvp->newstate(vap, nstate, arg);
|
|
if (vap->iv_newstate_cb != NULL)
|
|
vap->iv_newstate_cb(vap, nstate, arg);
|
|
IEEE80211_UNLOCK(ic);
|
|
mtx_lock(&sc->sc_mtx);
|
|
return;
|
|
}
|
|
|
|
static int
|
|
rum_newstate_cb(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
|
|
{
|
|
struct rum_vap *uvp = RUM_VAP(vap);
|
|
struct ieee80211com *ic = vap->iv_ic;
|
|
struct rum_softc *sc = ic->ic_ifp->if_softc;
|
|
|
|
DPRINTF("setting new state: %d\n", nstate);
|
|
|
|
/* Special case - cannot defer this call and cannot block ! */
|
|
if (nstate == IEEE80211_S_INIT) {
|
|
/* stop timers */
|
|
mtx_lock(&sc->sc_mtx);
|
|
sc->sc_amrr_timer = 0;
|
|
mtx_unlock(&sc->sc_mtx);
|
|
return (uvp->newstate(vap, nstate, arg));
|
|
}
|
|
mtx_lock(&sc->sc_mtx);
|
|
if (usb2_config_td_is_gone(&sc->sc_config_td)) {
|
|
mtx_unlock(&sc->sc_mtx);
|
|
return (0); /* nothing to do */
|
|
}
|
|
/* store next state */
|
|
sc->sc_ns_state = nstate;
|
|
sc->sc_ns_arg = arg;
|
|
|
|
/* stop timers */
|
|
sc->sc_amrr_timer = 0;
|
|
|
|
/*
|
|
* USB configuration can only be done from the USB configuration
|
|
* thread:
|
|
*/
|
|
usb2_config_td_queue_command
|
|
(&sc->sc_config_td, &rum_config_copy,
|
|
&rum_cfg_newstate, 0, 0);
|
|
|
|
mtx_unlock(&sc->sc_mtx);
|
|
|
|
return (EINPROGRESS);
|
|
}
|
|
|
|
static void
|
|
rum_std_command(struct ieee80211com *ic, usb2_config_td_command_t *func)
|
|
{
|
|
struct rum_softc *sc = ic->ic_ifp->if_softc;
|
|
|
|
mtx_lock(&sc->sc_mtx);
|
|
|
|
sc->sc_rates = ieee80211_get_ratetable(ic->ic_curchan);
|
|
|
|
usb2_config_td_queue_command
|
|
(&sc->sc_config_td, &rum_config_copy, func, 0, 0);
|
|
|
|
mtx_unlock(&sc->sc_mtx);
|
|
|
|
return;
|
|
}
|
|
|
|
static void
|
|
rum_scan_start_cb(struct ieee80211com *ic)
|
|
{
|
|
rum_std_command(ic, &rum_cfg_scan_start);
|
|
return;
|
|
}
|
|
|
|
static void
|
|
rum_scan_end_cb(struct ieee80211com *ic)
|
|
{
|
|
rum_std_command(ic, &rum_cfg_scan_end);
|
|
return;
|
|
}
|
|
|
|
static void
|
|
rum_set_channel_cb(struct ieee80211com *ic)
|
|
{
|
|
rum_std_command(ic, &rum_cfg_set_chan);
|
|
return;
|
|
}
|
|
|
|
static void
|
|
rum_cfg_scan_start(struct rum_softc *sc,
|
|
struct usb2_config_td_cc *cc, uint16_t refcount)
|
|
{
|
|
/* abort TSF synchronization */
|
|
rum_cfg_disable_tsf_sync(sc);
|
|
rum_cfg_set_bssid(sc, cc->if_broadcastaddr);
|
|
return;
|
|
}
|
|
|
|
static void
|
|
rum_cfg_scan_end(struct rum_softc *sc,
|
|
struct usb2_config_td_cc *cc, uint16_t refcount)
|
|
{
|
|
/* enable TSF synchronization */
|
|
rum_cfg_enable_tsf_sync(sc, cc, 0);
|
|
rum_cfg_set_bssid(sc, cc->iv_bss.ni_bssid);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Reprogram MAC/BBP to switch to a new band. Values taken from the reference
|
|
* driver.
|
|
*/
|
|
static void
|
|
rum_cfg_select_band(struct rum_softc *sc,
|
|
struct usb2_config_td_cc *cc, uint16_t refcount)
|
|
{
|
|
uint32_t tmp;
|
|
uint8_t bbp17, bbp35, bbp96, bbp97, bbp98, bbp104;
|
|
|
|
/* update all BBP registers that depend on the band */
|
|
bbp17 = 0x20;
|
|
bbp96 = 0x48;
|
|
bbp104 = 0x2c;
|
|
bbp35 = 0x50;
|
|
bbp97 = 0x48;
|
|
bbp98 = 0x48;
|
|
|
|
if (cc->ic_curchan.chan_is_5ghz) {
|
|
bbp17 += 0x08;
|
|
bbp96 += 0x10;
|
|
bbp104 += 0x0c;
|
|
bbp35 += 0x10;
|
|
bbp97 += 0x10;
|
|
bbp98 += 0x10;
|
|
}
|
|
if ((cc->ic_curchan.chan_is_2ghz && sc->sc_ext_2ghz_lna) ||
|
|
(cc->ic_curchan.chan_is_5ghz && sc->sc_ext_5ghz_lna)) {
|
|
bbp17 += 0x10;
|
|
bbp96 += 0x10;
|
|
bbp104 += 0x10;
|
|
}
|
|
sc->sc_bbp17 = bbp17;
|
|
rum_cfg_bbp_write(sc, 17, bbp17);
|
|
rum_cfg_bbp_write(sc, 96, bbp96);
|
|
rum_cfg_bbp_write(sc, 104, bbp104);
|
|
|
|
if ((cc->ic_curchan.chan_is_2ghz && sc->sc_ext_2ghz_lna) ||
|
|
(cc->ic_curchan.chan_is_5ghz && sc->sc_ext_5ghz_lna)) {
|
|
rum_cfg_bbp_write(sc, 75, 0x80);
|
|
rum_cfg_bbp_write(sc, 86, 0x80);
|
|
rum_cfg_bbp_write(sc, 88, 0x80);
|
|
}
|
|
rum_cfg_bbp_write(sc, 35, bbp35);
|
|
rum_cfg_bbp_write(sc, 97, bbp97);
|
|
rum_cfg_bbp_write(sc, 98, bbp98);
|
|
|
|
tmp = rum_cfg_read(sc, RT2573_PHY_CSR0);
|
|
tmp &= ~(RT2573_PA_PE_2GHZ | RT2573_PA_PE_5GHZ);
|
|
if (cc->ic_curchan.chan_is_2ghz)
|
|
tmp |= RT2573_PA_PE_2GHZ;
|
|
else
|
|
tmp |= RT2573_PA_PE_5GHZ;
|
|
rum_cfg_write(sc, RT2573_PHY_CSR0, tmp);
|
|
|
|
/* 802.11a uses a 16 microseconds short interframe space */
|
|
sc->sc_sifs = cc->ic_curchan.chan_is_5ghz ? 16 : 10;
|
|
|
|
return;
|
|
}
|
|
|
|
static void
|
|
rum_cfg_set_chan(struct rum_softc *sc,
|
|
struct usb2_config_td_cc *cc, uint16_t refcount)
|
|
{
|
|
enum {
|
|
N_RF5225 = (sizeof(rum_rf5225) / sizeof(rum_rf5225[0]))};
|
|
const struct rfprog *rfprog;
|
|
uint32_t chan;
|
|
uint16_t i;
|
|
uint8_t bbp3;
|
|
uint8_t bbp94 = RT2573_BBPR94_DEFAULT;
|
|
int8_t power;
|
|
|
|
chan = cc->ic_curchan.chan_to_ieee;
|
|
|
|
if ((chan == 0) ||
|
|
(chan == IEEE80211_CHAN_ANY)) {
|
|
/* nothing to do */
|
|
return;
|
|
}
|
|
if (chan == sc->sc_last_chan) {
|
|
return;
|
|
}
|
|
sc->sc_last_chan = chan;
|
|
|
|
/* select the appropriate RF settings based on what EEPROM says */
|
|
rfprog = ((sc->sc_rf_rev == RT2573_RF_5225) ||
|
|
(sc->sc_rf_rev == RT2573_RF_2527)) ? rum_rf5225 : rum_rf5226;
|
|
|
|
/* find the settings for this channel */
|
|
for (i = 0;; i++) {
|
|
if (i == (N_RF5225 - 1))
|
|
break;
|
|
if (rfprog[i].chan == chan)
|
|
break;
|
|
}
|
|
|
|
DPRINTF("chan=%d, i=%d\n", chan, i);
|
|
|
|
power = sc->sc_txpow[i];
|
|
if (power < 0) {
|
|
bbp94 += power;
|
|
power = 0;
|
|
} else if (power > 31) {
|
|
bbp94 += power - 31;
|
|
power = 31;
|
|
}
|
|
/*
|
|
* If we are switching from the 2GHz band to the 5GHz band or
|
|
* vice-versa, BBP registers need to be reprogrammed.
|
|
*/
|
|
rum_cfg_select_band(sc, cc, 0);
|
|
rum_cfg_select_antenna(sc, cc, 0);
|
|
|
|
rum_cfg_rf_write(sc, RT2573_RF1, rfprog[i].r1);
|
|
rum_cfg_rf_write(sc, RT2573_RF2, rfprog[i].r2);
|
|
rum_cfg_rf_write(sc, RT2573_RF3, rfprog[i].r3 | (power << 7));
|
|
rum_cfg_rf_write(sc, RT2573_RF4, rfprog[i].r4 | (sc->sc_rffreq << 10));
|
|
|
|
rum_cfg_rf_write(sc, RT2573_RF1, rfprog[i].r1);
|
|
rum_cfg_rf_write(sc, RT2573_RF2, rfprog[i].r2);
|
|
rum_cfg_rf_write(sc, RT2573_RF3, rfprog[i].r3 | (power << 7) | 1);
|
|
rum_cfg_rf_write(sc, RT2573_RF4, rfprog[i].r4 | (sc->sc_rffreq << 10));
|
|
|
|
rum_cfg_rf_write(sc, RT2573_RF1, rfprog[i].r1);
|
|
rum_cfg_rf_write(sc, RT2573_RF2, rfprog[i].r2);
|
|
rum_cfg_rf_write(sc, RT2573_RF3, rfprog[i].r3 | (power << 7));
|
|
rum_cfg_rf_write(sc, RT2573_RF4, rfprog[i].r4 | (sc->sc_rffreq << 10));
|
|
|
|
if (usb2_config_td_sleep(&sc->sc_config_td, hz / 100)) {
|
|
return;
|
|
}
|
|
/* enable smart mode for MIMO-capable RFs */
|
|
bbp3 = rum_cfg_bbp_read(sc, 3);
|
|
|
|
if ((sc->sc_rf_rev == RT2573_RF_5225) ||
|
|
(sc->sc_rf_rev == RT2573_RF_2527))
|
|
bbp3 &= ~RT2573_SMART_MODE;
|
|
else
|
|
bbp3 |= RT2573_SMART_MODE;
|
|
|
|
rum_cfg_bbp_write(sc, 3, bbp3);
|
|
|
|
rum_cfg_bbp_write(sc, 94, bbp94);
|
|
|
|
/* update basic rate set */
|
|
|
|
if (cc->ic_curchan.chan_is_b) {
|
|
/* 11b basic rates: 1, 2Mbps */
|
|
rum_cfg_write(sc, RT2573_TXRX_CSR5, 0x3);
|
|
} else if (cc->ic_curchan.chan_is_a) {
|
|
/* 11a basic rates: 6, 12, 24Mbps */
|
|
rum_cfg_write(sc, RT2573_TXRX_CSR5, 0x150);
|
|
} else {
|
|
/* 11b/g basic rates: 1, 2, 5.5, 11Mbps */
|
|
rum_cfg_write(sc, RT2573_TXRX_CSR5, 0xf);
|
|
}
|
|
|
|
if (usb2_config_td_sleep(&sc->sc_config_td, hz / 100)) {
|
|
return;
|
|
}
|
|
return;
|
|
}
|
|
|
|
static void
|
|
rum_cfg_set_run(struct rum_softc *sc,
|
|
struct usb2_config_td_cc *cc)
|
|
{
|
|
|
|
if (cc->ic_opmode != IEEE80211_M_MONITOR) {
|
|
rum_cfg_update_slot(sc, cc, 0);
|
|
rum_cfg_enable_mrr(sc, cc, 0);
|
|
rum_cfg_set_txpreamble(sc, cc, 0);
|
|
|
|
/* update basic rate set */
|
|
|
|
if (cc->ic_bsschan.chan_is_5ghz) {
|
|
/* 11a basic rates: 6, 12, 24Mbps */
|
|
rum_cfg_write(sc, RT2573_TXRX_CSR5, 0x150);
|
|
} else if (cc->ic_bsschan.chan_is_g) {
|
|
/* 11b/g basic rates: 1, 2, 5.5, 11Mbps */
|
|
rum_cfg_write(sc, RT2573_TXRX_CSR5, 0xf);
|
|
} else {
|
|
/* 11b basic rates: 1, 2Mbps */
|
|
rum_cfg_write(sc, RT2573_TXRX_CSR5, 0x3);
|
|
}
|
|
rum_cfg_set_bssid(sc, cc->iv_bss.ni_bssid);
|
|
}
|
|
if ((cc->ic_opmode == IEEE80211_M_HOSTAP) ||
|
|
(cc->ic_opmode == IEEE80211_M_IBSS)) {
|
|
rum_cfg_prepare_beacon(sc, cc, 0);
|
|
}
|
|
if (cc->ic_opmode != IEEE80211_M_MONITOR) {
|
|
rum_cfg_enable_tsf_sync(sc, cc, 0);
|
|
}
|
|
if (cc->iv_bss.fixed_rate_none) {
|
|
/* enable automatic rate adaptation */
|
|
rum_cfg_amrr_start(sc);
|
|
}
|
|
return;
|
|
}
|
|
|
|
static void
|
|
rum_cfg_enable_tsf_sync(struct rum_softc *sc,
|
|
struct usb2_config_td_cc *cc, uint16_t refcount)
|
|
{
|
|
uint32_t tmp;
|
|
|
|
if (cc->ic_opmode != IEEE80211_M_STA) {
|
|
/*
|
|
* Change default 16ms TBTT adjustment to 8ms.
|
|
* Must be done before enabling beacon generation.
|
|
*/
|
|
rum_cfg_write(sc, RT2573_TXRX_CSR10, (1 << 12) | 8);
|
|
}
|
|
tmp = rum_cfg_read(sc, RT2573_TXRX_CSR9) & 0xff000000;
|
|
|
|
/* set beacon interval (in 1/16ms unit) */
|
|
tmp |= cc->iv_bss.ni_intval * 16;
|
|
|
|
tmp |= RT2573_TSF_TICKING | RT2573_ENABLE_TBTT;
|
|
if (cc->ic_opmode == IEEE80211_M_STA)
|
|
tmp |= RT2573_TSF_MODE(1);
|
|
else
|
|
tmp |= RT2573_TSF_MODE(2) | RT2573_GENERATE_BEACON;
|
|
|
|
rum_cfg_write(sc, RT2573_TXRX_CSR9, tmp);
|
|
|
|
return;
|
|
}
|
|
|
|
static void
|
|
rum_cfg_disable_tsf_sync(struct rum_softc *sc)
|
|
{
|
|
uint32_t tmp;
|
|
|
|
/* abort TSF synchronization */
|
|
tmp = rum_cfg_read(sc, RT2573_TXRX_CSR9);
|
|
rum_cfg_write(sc, RT2573_TXRX_CSR9, tmp & ~0x00ffffff);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Enable multi-rate retries for frames sent at OFDM rates.
|
|
* In 802.11b/g mode, allow fallback to CCK rates.
|
|
*/
|
|
static void
|
|
rum_cfg_enable_mrr(struct rum_softc *sc,
|
|
struct usb2_config_td_cc *cc, uint16_t refcount)
|
|
{
|
|
uint32_t tmp;
|
|
|
|
tmp = rum_cfg_read(sc, RT2573_TXRX_CSR4);
|
|
|
|
if (cc->ic_curchan.chan_is_5ghz)
|
|
tmp &= ~RT2573_MRR_CCK_FALLBACK;
|
|
else
|
|
tmp |= RT2573_MRR_CCK_FALLBACK;
|
|
|
|
tmp |= RT2573_MRR_ENABLED;
|
|
|
|
rum_cfg_write(sc, RT2573_TXRX_CSR4, tmp);
|
|
|
|
return;
|
|
}
|
|
|
|
static void
|
|
rum_cfg_update_slot(struct rum_softc *sc,
|
|
struct usb2_config_td_cc *cc, uint16_t refcount)
|
|
{
|
|
uint32_t tmp;
|
|
uint8_t slottime;
|
|
|
|
slottime = (cc->ic_flags & IEEE80211_F_SHSLOT) ? 9 : 20;
|
|
|
|
tmp = rum_cfg_read(sc, RT2573_MAC_CSR9);
|
|
tmp = (tmp & ~0xff) | slottime;
|
|
rum_cfg_write(sc, RT2573_MAC_CSR9, tmp);
|
|
|
|
DPRINTF("setting slot time to %u us\n", slottime);
|
|
|
|
return;
|
|
}
|
|
|
|
static void
|
|
rum_cfg_set_txpreamble(struct rum_softc *sc,
|
|
struct usb2_config_td_cc *cc, uint16_t refcount)
|
|
{
|
|
uint32_t tmp;
|
|
|
|
tmp = rum_cfg_read(sc, RT2573_TXRX_CSR4);
|
|
|
|
if (cc->ic_flags & IEEE80211_F_SHPREAMBLE)
|
|
tmp |= RT2573_SHORT_PREAMBLE;
|
|
else
|
|
tmp &= ~RT2573_SHORT_PREAMBLE;
|
|
|
|
rum_cfg_write(sc, RT2573_TXRX_CSR4, tmp);
|
|
|
|
return;
|
|
}
|
|
|
|
static void
|
|
rum_cfg_set_bssid(struct rum_softc *sc, uint8_t *bssid)
|
|
{
|
|
uint32_t tmp;
|
|
|
|
tmp = bssid[0] | (bssid[1] << 8) | (bssid[2] << 16) | (bssid[3] << 24);
|
|
rum_cfg_write(sc, RT2573_MAC_CSR4, tmp);
|
|
|
|
tmp = (bssid[4]) | (bssid[5] << 8) | (RT2573_ONE_BSSID << 16);
|
|
rum_cfg_write(sc, RT2573_MAC_CSR5, tmp);
|
|
|
|
return;
|
|
}
|
|
|
|
static void
|
|
rum_cfg_set_macaddr(struct rum_softc *sc, uint8_t *addr)
|
|
{
|
|
uint32_t tmp;
|
|
|
|
tmp = addr[0] | (addr[1] << 8) | (addr[2] << 16) | (addr[3] << 24);
|
|
rum_cfg_write(sc, RT2573_MAC_CSR2, tmp);
|
|
|
|
tmp = addr[4] | (addr[5] << 8) | (0xff << 16);
|
|
rum_cfg_write(sc, RT2573_MAC_CSR3, tmp);
|
|
|
|
return;
|
|
}
|
|
|
|
static void
|
|
rum_cfg_update_promisc(struct rum_softc *sc,
|
|
struct usb2_config_td_cc *cc, uint16_t refcount)
|
|
{
|
|
uint32_t tmp;
|
|
|
|
tmp = rum_cfg_read(sc, RT2573_TXRX_CSR0);
|
|
|
|
if (cc->if_flags & IFF_PROMISC)
|
|
tmp &= ~RT2573_DROP_NOT_TO_ME;
|
|
else
|
|
tmp |= RT2573_DROP_NOT_TO_ME;
|
|
|
|
rum_cfg_write(sc, RT2573_TXRX_CSR0, tmp);
|
|
|
|
DPRINTF("%s promiscuous mode\n",
|
|
(cc->if_flags & IFF_PROMISC) ?
|
|
"entering" : "leaving");
|
|
return;
|
|
}
|
|
|
|
static void
|
|
rum_cfg_select_antenna(struct rum_softc *sc,
|
|
struct usb2_config_td_cc *cc, uint16_t refcount)
|
|
{
|
|
uint32_t tmp;
|
|
uint8_t bbp3;
|
|
uint8_t bbp4;
|
|
uint8_t bbp77;
|
|
uint8_t rx_ant;
|
|
uint8_t is_5ghz;
|
|
|
|
bbp3 = rum_cfg_bbp_read(sc, 3);
|
|
bbp4 = rum_cfg_bbp_read(sc, 4);
|
|
bbp77 = rum_cfg_bbp_read(sc, 77);
|
|
|
|
bbp3 &= ~0x01;
|
|
bbp4 &= ~0x23;
|
|
|
|
rx_ant = sc->sc_rx_ant;
|
|
is_5ghz = cc->ic_curchan.chan_is_5ghz;
|
|
|
|
switch (sc->sc_rf_rev) {
|
|
case RT2573_RF_5226:
|
|
case RT2573_RF_5225:
|
|
if (rx_ant == 0) {
|
|
/* Diversity */
|
|
bbp4 |= 0x02;
|
|
if (is_5ghz == 0)
|
|
bbp4 |= 0x20;
|
|
} else if (rx_ant == 1) {
|
|
/* RX: Antenna A */
|
|
bbp4 |= 0x01;
|
|
if (is_5ghz)
|
|
bbp77 &= ~0x03;
|
|
else
|
|
bbp77 |= 0x03;
|
|
} else if (rx_ant == 2) {
|
|
/* RX: Antenna B */
|
|
bbp4 |= 0x01;
|
|
if (is_5ghz)
|
|
bbp77 |= 0x03;
|
|
else
|
|
bbp77 &= ~0x03;
|
|
}
|
|
break;
|
|
|
|
case RT2573_RF_2528:
|
|
case RT2573_RF_2527:
|
|
if (rx_ant == 0) {
|
|
/* Diversity */
|
|
bbp4 |= 0x22;
|
|
} else if (rx_ant == 1) {
|
|
/* RX: Antenna A */
|
|
bbp4 |= 0x21;
|
|
bbp77 |= 0x03;
|
|
} else if (rx_ant == 2) {
|
|
/* RX: Antenna B */
|
|
bbp4 |= 0x21;
|
|
bbp77 &= ~0x03;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
bbp4 &= ~(sc->sc_ftype << 5);
|
|
|
|
/* make sure Rx is disabled before switching antenna */
|
|
tmp = rum_cfg_read(sc, RT2573_TXRX_CSR0);
|
|
rum_cfg_write(sc, RT2573_TXRX_CSR0, tmp | RT2573_DISABLE_RX);
|
|
|
|
rum_cfg_bbp_write(sc, 3, bbp3);
|
|
rum_cfg_bbp_write(sc, 4, bbp4);
|
|
rum_cfg_bbp_write(sc, 77, bbp77);
|
|
|
|
rum_cfg_write(sc, RT2573_TXRX_CSR0, tmp);
|
|
|
|
return;
|
|
}
|
|
|
|
static void
|
|
rum_cfg_read_eeprom(struct rum_softc *sc)
|
|
{
|
|
uint16_t val;
|
|
|
|
/* read MAC address */
|
|
rum_cfg_eeprom_read(sc, RT2573_EEPROM_ADDRESS, sc->sc_myaddr, 6);
|
|
|
|
val = rum_cfg_eeprom_read_2(sc, RT2573_EEPROM_ANTENNA);
|
|
sc->sc_rf_rev = (val >> 11) & 0x1f;
|
|
sc->sc_hw_radio = (val >> 10) & 0x1;
|
|
sc->sc_ftype = (val >> 6) & 0x1;
|
|
sc->sc_rx_ant = (val >> 4) & 0x3;
|
|
sc->sc_tx_ant = (val >> 2) & 0x3;
|
|
sc->sc_nb_ant = (val & 0x3);
|
|
|
|
DPRINTF("RF revision=%d\n", sc->sc_rf_rev);
|
|
|
|
val = rum_cfg_eeprom_read_2(sc, RT2573_EEPROM_CONFIG2);
|
|
sc->sc_ext_5ghz_lna = (val >> 6) & 0x1;
|
|
sc->sc_ext_2ghz_lna = (val >> 4) & 0x1;
|
|
|
|
DPRINTF("External 2GHz LNA=%d, External 5GHz LNA=%d\n",
|
|
sc->sc_ext_2ghz_lna, sc->sc_ext_5ghz_lna);
|
|
|
|
val = rum_cfg_eeprom_read_2(sc, RT2573_EEPROM_RSSI_2GHZ_OFFSET);
|
|
if ((val & 0xff) != 0xff)
|
|
sc->sc_rssi_2ghz_corr = (int8_t)(val & 0xff); /* signed */
|
|
else
|
|
sc->sc_rssi_2ghz_corr = 0;
|
|
|
|
/* range check */
|
|
if ((sc->sc_rssi_2ghz_corr < -10) ||
|
|
(sc->sc_rssi_2ghz_corr > 10)) {
|
|
sc->sc_rssi_2ghz_corr = 0;
|
|
}
|
|
val = rum_cfg_eeprom_read_2(sc, RT2573_EEPROM_RSSI_5GHZ_OFFSET);
|
|
if ((val & 0xff) != 0xff)
|
|
sc->sc_rssi_5ghz_corr = (int8_t)(val & 0xff); /* signed */
|
|
else
|
|
sc->sc_rssi_5ghz_corr = 0;
|
|
|
|
/* range check */
|
|
if ((sc->sc_rssi_5ghz_corr < -10) ||
|
|
(sc->sc_rssi_5ghz_corr > 10)) {
|
|
sc->sc_rssi_5ghz_corr = 0;
|
|
}
|
|
if (sc->sc_ext_2ghz_lna) {
|
|
sc->sc_rssi_2ghz_corr -= 14;
|
|
}
|
|
if (sc->sc_ext_5ghz_lna) {
|
|
sc->sc_rssi_5ghz_corr -= 14;
|
|
}
|
|
DPRINTF("RSSI 2GHz corr=%d, RSSI 5GHz corr=%d\n",
|
|
sc->sc_rssi_2ghz_corr, sc->sc_rssi_5ghz_corr);
|
|
|
|
val = rum_cfg_eeprom_read_2(sc, RT2573_EEPROM_FREQ_OFFSET);
|
|
if ((val & 0xff) != 0xff)
|
|
sc->sc_rffreq = (val & 0xff);
|
|
else
|
|
sc->sc_rffreq = 0;
|
|
|
|
DPRINTF("RF freq=%d\n", sc->sc_rffreq);
|
|
|
|
/* read Tx power for all a/b/g channels */
|
|
rum_cfg_eeprom_read(sc, RT2573_EEPROM_TXPOWER, sc->sc_txpow, 14);
|
|
|
|
/* XXX default Tx power for 802.11a channels */
|
|
memset(sc->sc_txpow + 14, 24, sizeof(sc->sc_txpow) - 14);
|
|
|
|
/* read default values for BBP registers */
|
|
rum_cfg_eeprom_read(sc, RT2573_EEPROM_BBP_BASE, sc->sc_bbp_prom, 2 * 16);
|
|
|
|
return;
|
|
}
|
|
|
|
static uint8_t
|
|
rum_cfg_bbp_init(struct rum_softc *sc)
|
|
{
|
|
enum {
|
|
N_DEF_BBP = (sizeof(rum_def_bbp) / sizeof(rum_def_bbp[0])),
|
|
};
|
|
uint16_t i;
|
|
uint8_t to;
|
|
uint8_t tmp;
|
|
|
|
/* wait for BBP to become ready */
|
|
for (to = 0;; to++) {
|
|
if (to < 100) {
|
|
tmp = rum_cfg_bbp_read(sc, 0);
|
|
if ((tmp != 0x00) &&
|
|
(tmp != 0xff)) {
|
|
break;
|
|
}
|
|
if (usb2_config_td_sleep(&sc->sc_config_td, hz / 100)) {
|
|
return (1); /* failure */
|
|
}
|
|
} else {
|
|
DPRINTF("timeout waiting for BBP\n");
|
|
return (1); /* failure */
|
|
}
|
|
}
|
|
|
|
/* initialize BBP registers to default values */
|
|
for (i = 0; i < N_DEF_BBP; i++) {
|
|
rum_cfg_bbp_write(sc, rum_def_bbp[i].reg, rum_def_bbp[i].val);
|
|
}
|
|
|
|
/* write vendor-specific BBP values (from EEPROM) */
|
|
for (i = 0; i < 16; i++) {
|
|
if ((sc->sc_bbp_prom[i].reg == 0) ||
|
|
(sc->sc_bbp_prom[i].reg == 0xff)) {
|
|
continue;
|
|
}
|
|
rum_cfg_bbp_write(sc, sc->sc_bbp_prom[i].reg, sc->sc_bbp_prom[i].val);
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
static void
|
|
rum_cfg_pre_init(struct rum_softc *sc,
|
|
struct usb2_config_td_cc *cc, uint16_t refcount)
|
|
{
|
|
struct ifnet *ifp = sc->sc_ifp;
|
|
struct ieee80211com *ic = ifp->if_l2com;
|
|
|
|
/* immediate configuration */
|
|
|
|
rum_cfg_pre_stop(sc, cc, 0);
|
|
|
|
ifp->if_drv_flags |= IFF_DRV_RUNNING;
|
|
|
|
sc->sc_flags |= RUM_FLAG_HL_READY;
|
|
|
|
IEEE80211_ADDR_COPY(ic->ic_myaddr, IF_LLADDR(ifp));
|
|
return;
|
|
}
|
|
|
|
static void
|
|
rum_cfg_init(struct rum_softc *sc,
|
|
struct usb2_config_td_cc *cc, uint16_t refcount)
|
|
{
|
|
enum {
|
|
N_DEF_MAC = (sizeof(rum_def_mac) / sizeof(rum_def_mac[0])),
|
|
};
|
|
|
|
uint32_t tmp;
|
|
uint16_t i;
|
|
uint8_t to;
|
|
|
|
/* delayed configuration */
|
|
|
|
rum_cfg_stop(sc, cc, 0);
|
|
|
|
/* initialize MAC registers to default values */
|
|
for (i = 0; i < N_DEF_MAC; i++) {
|
|
rum_cfg_write(sc, rum_def_mac[i].reg, rum_def_mac[i].val);
|
|
}
|
|
|
|
/* set host ready */
|
|
rum_cfg_write(sc, RT2573_MAC_CSR1, 3);
|
|
rum_cfg_write(sc, RT2573_MAC_CSR1, 0);
|
|
|
|
/* wait for BBP/RF to wakeup */
|
|
for (to = 0;; to++) {
|
|
if (to < 100) {
|
|
if (rum_cfg_read(sc, RT2573_MAC_CSR12) & 8) {
|
|
break;
|
|
}
|
|
rum_cfg_write(sc, RT2573_MAC_CSR12, 4); /* force wakeup */
|
|
|
|
if (usb2_config_td_sleep(&sc->sc_config_td, hz / 100)) {
|
|
goto fail;
|
|
}
|
|
} else {
|
|
DPRINTF("timeout waiting for "
|
|
"BBP/RF to wakeup\n");
|
|
goto fail;
|
|
}
|
|
}
|
|
|
|
if (rum_cfg_bbp_init(sc)) {
|
|
goto fail;
|
|
}
|
|
/* select default channel */
|
|
|
|
sc->sc_last_chan = 0;
|
|
|
|
rum_cfg_set_chan(sc, cc, 0);
|
|
|
|
/* clear STA registers */
|
|
rum_cfg_read_multi(sc, RT2573_STA_CSR0, sc->sc_sta, sizeof(sc->sc_sta));
|
|
/* set MAC address */
|
|
rum_cfg_set_macaddr(sc, cc->ic_myaddr);
|
|
|
|
/* initialize ASIC */
|
|
rum_cfg_write(sc, RT2573_MAC_CSR1, 4);
|
|
|
|
/*
|
|
* make sure that the first transaction
|
|
* clears the stall:
|
|
*/
|
|
sc->sc_flags |= (RUM_FLAG_READ_STALL |
|
|
RUM_FLAG_WRITE_STALL |
|
|
RUM_FLAG_LL_READY);
|
|
|
|
if ((sc->sc_flags & RUM_FLAG_LL_READY) &&
|
|
(sc->sc_flags & RUM_FLAG_HL_READY)) {
|
|
struct ifnet *ifp = sc->sc_ifp;
|
|
struct ieee80211com *ic = ifp->if_l2com;
|
|
|
|
/*
|
|
* start the USB transfers, if not already started:
|
|
*/
|
|
usb2_transfer_start(sc->sc_xfer[1]);
|
|
usb2_transfer_start(sc->sc_xfer[0]);
|
|
|
|
/*
|
|
* start IEEE802.11 layer
|
|
*/
|
|
mtx_unlock(&sc->sc_mtx);
|
|
ieee80211_start_all(ic);
|
|
mtx_lock(&sc->sc_mtx);
|
|
}
|
|
/* update Rx filter */
|
|
tmp = rum_cfg_read(sc, RT2573_TXRX_CSR0) & 0xffff;
|
|
|
|
tmp |= RT2573_DROP_PHY_ERROR | RT2573_DROP_CRC_ERROR;
|
|
|
|
if (cc->ic_opmode != IEEE80211_M_MONITOR) {
|
|
tmp |= RT2573_DROP_CTL | RT2573_DROP_VER_ERROR |
|
|
RT2573_DROP_ACKCTS;
|
|
if (cc->ic_opmode != IEEE80211_M_HOSTAP) {
|
|
tmp |= RT2573_DROP_TODS;
|
|
}
|
|
if (!(cc->if_flags & IFF_PROMISC)) {
|
|
tmp |= RT2573_DROP_NOT_TO_ME;
|
|
}
|
|
}
|
|
rum_cfg_write(sc, RT2573_TXRX_CSR0, tmp);
|
|
|
|
return;
|
|
|
|
fail:
|
|
rum_cfg_pre_stop(sc, NULL, 0);
|
|
|
|
if (cc) {
|
|
rum_cfg_stop(sc, cc, 0);
|
|
}
|
|
return;
|
|
}
|
|
|
|
static void
|
|
rum_cfg_pre_stop(struct rum_softc *sc,
|
|
struct usb2_config_td_cc *cc, uint16_t refcount)
|
|
{
|
|
struct ifnet *ifp = sc->sc_ifp;
|
|
|
|
if (cc) {
|
|
/* copy the needed configuration */
|
|
rum_config_copy(sc, cc, refcount);
|
|
}
|
|
/* immediate configuration */
|
|
|
|
if (ifp) {
|
|
/* clear flags */
|
|
ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
|
|
}
|
|
sc->sc_flags &= ~(RUM_FLAG_HL_READY |
|
|
RUM_FLAG_LL_READY);
|
|
|
|
/*
|
|
* stop all the transfers, if not already stopped:
|
|
*/
|
|
usb2_transfer_stop(sc->sc_xfer[0]);
|
|
usb2_transfer_stop(sc->sc_xfer[1]);
|
|
usb2_transfer_stop(sc->sc_xfer[2]);
|
|
usb2_transfer_stop(sc->sc_xfer[3]);
|
|
|
|
/* clean up transmission */
|
|
rum_tx_clean_queue(sc);
|
|
return;
|
|
}
|
|
|
|
static void
|
|
rum_cfg_stop(struct rum_softc *sc,
|
|
struct usb2_config_td_cc *cc, uint16_t refcount)
|
|
{
|
|
uint32_t tmp;
|
|
|
|
/* disable Rx */
|
|
tmp = rum_cfg_read(sc, RT2573_TXRX_CSR0);
|
|
rum_cfg_write(sc, RT2573_TXRX_CSR0, tmp | RT2573_DISABLE_RX);
|
|
|
|
/* reset ASIC */
|
|
rum_cfg_write(sc, RT2573_MAC_CSR1, 3);
|
|
|
|
/* wait a little */
|
|
usb2_config_td_sleep(&sc->sc_config_td, hz / 10);
|
|
|
|
rum_cfg_write(sc, RT2573_MAC_CSR1, 0);
|
|
|
|
/* wait a little */
|
|
usb2_config_td_sleep(&sc->sc_config_td, hz / 10);
|
|
|
|
return;
|
|
}
|
|
|
|
static void
|
|
rum_cfg_amrr_start(struct rum_softc *sc)
|
|
{
|
|
struct ieee80211vap *vap;
|
|
struct ieee80211_node *ni;
|
|
|
|
vap = rum_get_vap(sc);
|
|
|
|
if (vap == NULL) {
|
|
return;
|
|
}
|
|
ni = vap->iv_bss;
|
|
if (ni == NULL) {
|
|
return;
|
|
}
|
|
/* init AMRR */
|
|
|
|
ieee80211_amrr_node_init(&RUM_VAP(vap)->amrr, &RUM_NODE(ni)->amn, ni);
|
|
|
|
/* enable AMRR timer */
|
|
|
|
sc->sc_amrr_timer = 1;
|
|
return;
|
|
}
|
|
|
|
static void
|
|
rum_cfg_amrr_timeout(struct rum_softc *sc,
|
|
struct usb2_config_td_cc *cc, uint16_t refcount)
|
|
{
|
|
struct ifnet *ifp = sc->sc_ifp;
|
|
struct ieee80211vap *vap;
|
|
struct ieee80211_node *ni;
|
|
uint32_t ok;
|
|
uint32_t fail;
|
|
|
|
/* clear statistic registers (STA_CSR0 to STA_CSR5) */
|
|
rum_cfg_read_multi(sc, RT2573_STA_CSR0, sc->sc_sta, sizeof(sc->sc_sta));
|
|
|
|
vap = rum_get_vap(sc);
|
|
if (vap == NULL) {
|
|
return;
|
|
}
|
|
ni = vap->iv_bss;
|
|
if (ni == NULL) {
|
|
return;
|
|
}
|
|
if ((sc->sc_flags & RUM_FLAG_LL_READY) &&
|
|
(sc->sc_flags & RUM_FLAG_HL_READY)) {
|
|
|
|
ok = (le32toh(sc->sc_sta[4]) >> 16) + /* TX ok w/o retry */
|
|
(le32toh(sc->sc_sta[5]) & 0xffff); /* TX ok w/ retry */
|
|
fail = (le32toh(sc->sc_sta[5]) >> 16); /* TX retry-fail count */
|
|
|
|
if (sc->sc_amrr_timer) {
|
|
ieee80211_amrr_tx_update(&RUM_NODE(vap->iv_bss)->amn,
|
|
ok + fail, ok, (le32toh(sc->sc_sta[5]) & 0xffff) + fail);
|
|
|
|
if (ieee80211_amrr_choose(ni, &RUM_NODE(ni)->amn)) {
|
|
/* ignore */
|
|
}
|
|
}
|
|
ifp->if_oerrors += fail;/* count TX retry-fail as Tx errors */
|
|
}
|
|
return;
|
|
}
|
|
|
|
static void
|
|
rum_cfg_load_microcode(struct rum_softc *sc, const uint8_t *ucode, uint16_t size)
|
|
{
|
|
struct usb2_device_request req;
|
|
uint16_t reg = RT2573_MCU_CODE_BASE;
|
|
|
|
/* copy firmware image into NIC */
|
|
while (size >= 4) {
|
|
rum_cfg_write(sc, reg, UGETDW(ucode));
|
|
reg += 4;
|
|
ucode += 4;
|
|
size -= 4;
|
|
}
|
|
|
|
if (size != 0) {
|
|
DPRINTF("possibly invalid firmware\n");
|
|
}
|
|
req.bmRequestType = UT_WRITE_VENDOR_DEVICE;
|
|
req.bRequest = RT2573_MCU_CNTL;
|
|
USETW(req.wValue, RT2573_MCU_RUN);
|
|
USETW(req.wIndex, 0);
|
|
USETW(req.wLength, 0);
|
|
|
|
rum_cfg_do_request(sc, &req, NULL);
|
|
|
|
return;
|
|
}
|
|
|
|
static void
|
|
rum_cfg_prepare_beacon(struct rum_softc *sc,
|
|
struct usb2_config_td_cc *cc, uint16_t refcount)
|
|
{
|
|
struct ieee80211_node *ni;
|
|
struct ieee80211vap *vap;
|
|
struct ieee80211com *ic;
|
|
const struct ieee80211_txparam *tp;
|
|
struct mbuf *m;
|
|
|
|
vap = rum_get_vap(sc);
|
|
if (vap == NULL) {
|
|
return;
|
|
}
|
|
ni = vap->iv_bss;
|
|
if (ni == NULL) {
|
|
return;
|
|
}
|
|
ic = vap->iv_ic;
|
|
if (ic == NULL) {
|
|
return;
|
|
}
|
|
DPRINTFN(11, "Sending beacon frame.\n");
|
|
|
|
m = ieee80211_beacon_alloc(ni, &RUM_VAP(vap)->bo);
|
|
if (m == NULL) {
|
|
DPRINTFN(0, "could not allocate beacon\n");
|
|
return;
|
|
}
|
|
tp = &vap->iv_txparms[ieee80211_chan2mode(ic->ic_bsschan)];
|
|
|
|
m->m_pkthdr.rcvif = (void *)ieee80211_ref_node(ni);
|
|
rum_setup_desc_and_tx(sc, m, RT2573_TX_TIMESTAMP, RT2573_TX_HWSEQ | RT2573_TX_BEACON, tp->mgmtrate);
|
|
return;
|
|
}
|
|
|
|
static uint8_t
|
|
rum_get_rssi(struct rum_softc *sc, uint8_t raw)
|
|
{
|
|
struct ifnet *ifp = sc->sc_ifp;
|
|
struct ieee80211com *ic = ifp->if_l2com;
|
|
int16_t rssi;
|
|
uint8_t lna;
|
|
uint8_t agc;
|
|
|
|
lna = (raw >> 5) & 0x3;
|
|
agc = raw & 0x1f;
|
|
|
|
if (lna == 0) {
|
|
/*
|
|
* No RSSI mapping
|
|
*
|
|
* NB: Since RSSI is relative to noise floor, -1 is
|
|
* adequate for caller to know error happened.
|
|
*/
|
|
return (0);
|
|
}
|
|
rssi = (2 * agc) - RT2573_NOISE_FLOOR;
|
|
|
|
if (IEEE80211_IS_CHAN_2GHZ(ic->ic_curchan)) {
|
|
|
|
rssi += sc->sc_rssi_2ghz_corr;
|
|
|
|
if (lna == 1)
|
|
rssi -= 64;
|
|
else if (lna == 2)
|
|
rssi -= 74;
|
|
else if (lna == 3)
|
|
rssi -= 90;
|
|
} else {
|
|
|
|
rssi += sc->sc_rssi_5ghz_corr;
|
|
|
|
if ((!sc->sc_ext_5ghz_lna) && (lna != 1))
|
|
rssi += 4;
|
|
|
|
if (lna == 1)
|
|
rssi -= 64;
|
|
else if (lna == 2)
|
|
rssi -= 86;
|
|
else if (lna == 3)
|
|
rssi -= 100;
|
|
}
|
|
|
|
/* range check */
|
|
|
|
if (rssi < 0)
|
|
rssi = 0;
|
|
else if (rssi > 255)
|
|
rssi = 255;
|
|
|
|
return (rssi);
|
|
}
|
|
|
|
static struct ieee80211vap *
|
|
rum_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 rum_vap *rvp;
|
|
struct ieee80211vap *vap;
|
|
struct rum_softc *sc = ic->ic_ifp->if_softc;
|
|
|
|
DPRINTF("\n");
|
|
|
|
/* Need to sync with config thread: */
|
|
mtx_lock(&sc->sc_mtx);
|
|
if (usb2_config_td_sync(&sc->sc_config_td)) {
|
|
mtx_unlock(&sc->sc_mtx);
|
|
/* config thread is gone */
|
|
return (NULL);
|
|
}
|
|
mtx_unlock(&sc->sc_mtx);
|
|
|
|
if (!TAILQ_EMPTY(&ic->ic_vaps)) /* only one at a time */
|
|
return NULL;
|
|
rvp = (struct rum_vap *)malloc(sizeof(struct rum_vap),
|
|
M_80211_VAP, M_NOWAIT | M_ZERO);
|
|
if (rvp == NULL)
|
|
return NULL;
|
|
vap = &rvp->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 */
|
|
rvp->newstate = vap->iv_newstate;
|
|
vap->iv_newstate = &rum_newstate_cb;
|
|
|
|
ieee80211_amrr_init(&rvp->amrr, vap,
|
|
IEEE80211_AMRR_MIN_SUCCESS_THRESHOLD,
|
|
IEEE80211_AMRR_MAX_SUCCESS_THRESHOLD,
|
|
1000 /* 1 sec */ );
|
|
|
|
/* complete setup */
|
|
ieee80211_vap_attach(vap, ieee80211_media_change, ieee80211_media_status);
|
|
|
|
/* store current operation mode */
|
|
ic->ic_opmode = opmode;
|
|
|
|
return (vap);
|
|
}
|
|
|
|
static void
|
|
rum_vap_delete(struct ieee80211vap *vap)
|
|
{
|
|
struct rum_vap *rvp = RUM_VAP(vap);
|
|
struct rum_softc *sc = vap->iv_ic->ic_ifp->if_softc;
|
|
|
|
DPRINTF("\n");
|
|
|
|
/* Need to sync with config thread: */
|
|
mtx_lock(&sc->sc_mtx);
|
|
if (usb2_config_td_sync(&sc->sc_config_td)) {
|
|
/* ignore */
|
|
}
|
|
mtx_unlock(&sc->sc_mtx);
|
|
|
|
ieee80211_amrr_cleanup(&rvp->amrr);
|
|
ieee80211_vap_detach(vap);
|
|
free(rvp, M_80211_VAP);
|
|
return;
|
|
}
|
|
|
|
/* ARGUSED */
|
|
static struct ieee80211_node *
|
|
rum_node_alloc(struct ieee80211vap *vap __unused,
|
|
const uint8_t mac[IEEE80211_ADDR_LEN] __unused)
|
|
{
|
|
struct rum_node *rn;
|
|
|
|
rn = malloc(sizeof(struct rum_node), M_80211_NODE, M_NOWAIT | M_ZERO);
|
|
return ((rn != NULL) ? &rn->ni : NULL);
|
|
}
|
|
|
|
static void
|
|
rum_newassoc(struct ieee80211_node *ni, int isnew)
|
|
{
|
|
struct ieee80211vap *vap = ni->ni_vap;
|
|
|
|
ieee80211_amrr_node_init(&RUM_VAP(vap)->amrr, &RUM_NODE(ni)->amn, ni);
|
|
return;
|
|
}
|
|
|
|
static void
|
|
rum_fill_write_queue(struct rum_softc *sc)
|
|
{
|
|
struct ifnet *ifp = sc->sc_ifp;
|
|
struct ieee80211_node *ni;
|
|
struct mbuf *m;
|
|
|
|
/*
|
|
* We only fill up half of the queue with data frames. The rest is
|
|
* reserved for other kinds of frames.
|
|
*/
|
|
|
|
while (sc->sc_tx_queue.ifq_len < (IFQ_MAXLEN / 2)) {
|
|
|
|
IFQ_DRV_DEQUEUE(&ifp->if_snd, m);
|
|
if (m == NULL)
|
|
break;
|
|
|
|
ni = (void *)(m->m_pkthdr.rcvif);
|
|
m = ieee80211_encap(ni, m);
|
|
if (m == NULL) {
|
|
ieee80211_free_node(ni);
|
|
continue;
|
|
}
|
|
rum_tx_data(sc, m, ni);
|
|
}
|
|
return;
|
|
}
|
|
|
|
static void
|
|
rum_tx_clean_queue(struct rum_softc *sc)
|
|
{
|
|
struct mbuf *m;
|
|
|
|
for (;;) {
|
|
_IF_DEQUEUE(&sc->sc_tx_queue, m);
|
|
|
|
if (!m) {
|
|
break;
|
|
}
|
|
rum_tx_freem(m);
|
|
}
|
|
return;
|
|
}
|
|
|
|
static void
|
|
rum_tx_freem(struct mbuf *m)
|
|
{
|
|
struct ieee80211_node *ni;
|
|
|
|
while (m) {
|
|
ni = (void *)(m->m_pkthdr.rcvif);
|
|
if (!ni) {
|
|
m = m_free(m);
|
|
continue;
|
|
}
|
|
if (m->m_flags & M_TXCB) {
|
|
ieee80211_process_callback(ni, m, 0);
|
|
}
|
|
m_freem(m);
|
|
ieee80211_free_node(ni);
|
|
|
|
break;
|
|
}
|
|
return;
|
|
}
|
|
|
|
static void
|
|
rum_tx_mgt(struct rum_softc *sc, struct mbuf *m, struct ieee80211_node *ni)
|
|
{
|
|
struct ieee80211vap *vap = ni->ni_vap;
|
|
struct ieee80211com *ic = ni->ni_ic;
|
|
const struct ieee80211_txparam *tp;
|
|
struct ieee80211_frame *wh;
|
|
struct ieee80211_key *k;
|
|
uint32_t flags;
|
|
uint16_t dur;
|
|
|
|
tp = &vap->iv_txparms[ieee80211_chan2mode(ic->ic_curchan)];
|
|
|
|
wh = mtod(m, struct ieee80211_frame *);
|
|
if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
|
|
k = ieee80211_crypto_encap(ni, m);
|
|
if (k == NULL) {
|
|
m_freem(m);
|
|
ieee80211_free_node(ni);
|
|
return;
|
|
}
|
|
wh = mtod(m, struct ieee80211_frame *);
|
|
}
|
|
flags = 0;
|
|
if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) {
|
|
flags |= RT2573_TX_NEED_ACK;
|
|
|
|
dur = ieee80211_ack_duration(sc->sc_rates, tp->mgmtrate,
|
|
ic->ic_flags & IEEE80211_F_SHPREAMBLE);
|
|
USETW(wh->i_dur, dur);
|
|
|
|
/* tell hardware to add timestamp for probe responses */
|
|
if ((wh->i_fc[0] &
|
|
(IEEE80211_FC0_TYPE_MASK | IEEE80211_FC0_SUBTYPE_MASK)) ==
|
|
(IEEE80211_FC0_TYPE_MGT | IEEE80211_FC0_SUBTYPE_PROBE_RESP))
|
|
flags |= RT2573_TX_TIMESTAMP;
|
|
}
|
|
m->m_pkthdr.rcvif = (void *)ni;
|
|
rum_setup_desc_and_tx(sc, m, flags, 0, tp->mgmtrate);
|
|
return;
|
|
}
|
|
|
|
static struct ieee80211vap *
|
|
rum_get_vap(struct rum_softc *sc)
|
|
{
|
|
struct ifnet *ifp;
|
|
struct ieee80211com *ic;
|
|
|
|
if (sc == NULL) {
|
|
return NULL;
|
|
}
|
|
ifp = sc->sc_ifp;
|
|
if (ifp == NULL) {
|
|
return NULL;
|
|
}
|
|
ic = ifp->if_l2com;
|
|
if (ic == NULL) {
|
|
return NULL;
|
|
}
|
|
return TAILQ_FIRST(&ic->ic_vaps);
|
|
}
|
|
|
|
static void
|
|
rum_tx_data(struct rum_softc *sc, struct mbuf *m,
|
|
struct ieee80211_node *ni)
|
|
{
|
|
struct ieee80211vap *vap = ni->ni_vap;
|
|
struct ieee80211com *ic = ni->ni_ic;
|
|
const struct ieee80211_txparam *tp;
|
|
struct ieee80211_frame *wh;
|
|
struct ieee80211_key *k;
|
|
uint32_t flags = 0;
|
|
uint16_t dur;
|
|
uint16_t rate;
|
|
|
|
DPRINTFN(11, "Sending data.\n");
|
|
|
|
wh = mtod(m, struct ieee80211_frame *);
|
|
|
|
tp = &vap->iv_txparms[ieee80211_chan2mode(ni->ni_chan)];
|
|
if (IEEE80211_IS_MULTICAST(wh->i_addr1))
|
|
rate = tp->mcastrate;
|
|
else if (tp->ucastrate != IEEE80211_FIXED_RATE_NONE)
|
|
rate = tp->ucastrate;
|
|
else
|
|
rate = ni->ni_txrate;
|
|
|
|
if (wh->i_fc[1] & IEEE80211_FC1_WEP) {
|
|
k = ieee80211_crypto_encap(ni, m);
|
|
if (k == NULL) {
|
|
m_freem(m);
|
|
ieee80211_free_node(ni);
|
|
return;
|
|
}
|
|
/* packet header may have moved, reset our local pointer */
|
|
wh = mtod(m, struct ieee80211_frame *);
|
|
}
|
|
if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) {
|
|
uint8_t prot = IEEE80211_PROT_NONE;
|
|
|
|
if (m->m_pkthdr.len + IEEE80211_CRC_LEN > vap->iv_rtsthreshold)
|
|
prot = IEEE80211_PROT_RTSCTS;
|
|
else if ((ic->ic_flags & IEEE80211_F_USEPROT) &&
|
|
ieee80211_rate2phytype(sc->sc_rates, rate) == IEEE80211_T_OFDM)
|
|
prot = ic->ic_protmode;
|
|
if (prot != IEEE80211_PROT_NONE) {
|
|
rum_tx_prot(sc, m, ni, prot, rate);
|
|
flags |= RT2573_TX_LONG_RETRY | RT2573_TX_IFS_SIFS;
|
|
}
|
|
flags |= RT2573_TX_NEED_ACK;
|
|
flags |= RT2573_TX_MORE_FRAG;
|
|
|
|
dur = ieee80211_ack_duration(sc->sc_rates, rate,
|
|
ic->ic_flags & IEEE80211_F_SHPREAMBLE);
|
|
USETW(wh->i_dur, dur);
|
|
}
|
|
m->m_pkthdr.rcvif = (void *)ni;
|
|
rum_setup_desc_and_tx(sc, m, flags, 0, rate);
|
|
return;
|
|
}
|
|
|
|
static void
|
|
rum_tx_prot(struct rum_softc *sc,
|
|
const struct mbuf *m, struct ieee80211_node *ni,
|
|
uint8_t prot, uint16_t rate)
|
|
{
|
|
struct ieee80211com *ic = ni->ni_ic;
|
|
const struct ieee80211_frame *wh;
|
|
struct mbuf *mprot;
|
|
uint32_t flags;
|
|
uint16_t protrate;
|
|
uint16_t ackrate;
|
|
uint16_t pktlen;
|
|
uint16_t dur;
|
|
uint8_t isshort;
|
|
|
|
KASSERT((prot == IEEE80211_PROT_RTSCTS) ||
|
|
(prot == IEEE80211_PROT_CTSONLY),
|
|
("protection %u", prot));
|
|
|
|
DPRINTFN(11, "Sending protection frame.\n");
|
|
|
|
wh = mtod(m, const struct ieee80211_frame *);
|
|
pktlen = m->m_pkthdr.len + IEEE80211_CRC_LEN;
|
|
|
|
protrate = ieee80211_ctl_rate(sc->sc_rates, rate);
|
|
ackrate = ieee80211_ack_rate(sc->sc_rates, rate);
|
|
|
|
isshort = (ic->ic_flags & IEEE80211_F_SHPREAMBLE) != 0;
|
|
dur = ieee80211_compute_duration(sc->sc_rates, pktlen, rate, isshort);
|
|
+ieee80211_ack_duration(sc->sc_rates, rate, isshort);
|
|
flags = RT2573_TX_MORE_FRAG;
|
|
if (prot == IEEE80211_PROT_RTSCTS) {
|
|
/* NB: CTS is the same size as an ACK */
|
|
dur += ieee80211_ack_duration(sc->sc_rates, rate, isshort);
|
|
flags |= RT2573_TX_NEED_ACK;
|
|
mprot = ieee80211_alloc_rts(ic, wh->i_addr1, wh->i_addr2, dur);
|
|
} else {
|
|
mprot = ieee80211_alloc_cts(ic, ni->ni_vap->iv_myaddr, dur);
|
|
}
|
|
if (mprot == NULL) {
|
|
return;
|
|
}
|
|
mprot->m_pkthdr.rcvif = (void *)ieee80211_ref_node(ni);
|
|
rum_setup_desc_and_tx(sc, mprot, flags, 0, protrate);
|
|
return;
|
|
}
|
|
|
|
static void
|
|
rum_tx_raw(struct rum_softc *sc, struct mbuf *m, struct ieee80211_node *ni,
|
|
const struct ieee80211_bpf_params *params)
|
|
{
|
|
uint32_t flags;
|
|
uint16_t rate;
|
|
|
|
DPRINTFN(11, "Sending raw frame.\n");
|
|
|
|
rate = params->ibp_rate0 & IEEE80211_RATE_VAL;
|
|
|
|
/* XXX validate */
|
|
if (rate == 0) {
|
|
m_freem(m);
|
|
ieee80211_free_node(ni);
|
|
return;
|
|
}
|
|
flags = 0;
|
|
if ((params->ibp_flags & IEEE80211_BPF_NOACK) == 0)
|
|
flags |= RT2573_TX_NEED_ACK;
|
|
if (params->ibp_flags & (IEEE80211_BPF_RTS | IEEE80211_BPF_CTS)) {
|
|
rum_tx_prot(sc, m, ni,
|
|
params->ibp_flags & IEEE80211_BPF_RTS ?
|
|
IEEE80211_PROT_RTSCTS : IEEE80211_PROT_CTSONLY,
|
|
rate);
|
|
flags |= RT2573_TX_LONG_RETRY | RT2573_TX_IFS_SIFS;
|
|
}
|
|
m->m_pkthdr.rcvif = (void *)ni;
|
|
rum_setup_desc_and_tx(sc, m, flags, 0, rate);
|
|
return;
|
|
}
|
|
|
|
static int
|
|
rum_raw_xmit_cb(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 rum_softc *sc = ifp->if_softc;
|
|
|
|
mtx_lock(&sc->sc_mtx);
|
|
if (params == NULL) {
|
|
/*
|
|
* Legacy path; interpret frame contents to decide
|
|
* precisely how to send the frame.
|
|
*/
|
|
rum_tx_mgt(sc, m, ni);
|
|
} else {
|
|
/*
|
|
* Caller supplied explicit parameters to use in
|
|
* sending the frame.
|
|
*/
|
|
rum_tx_raw(sc, m, ni, params);
|
|
}
|
|
mtx_unlock(&sc->sc_mtx);
|
|
return (0);
|
|
}
|
|
|
|
static void
|
|
rum_update_mcast_cb(struct ifnet *ifp)
|
|
{
|
|
/* not supported */
|
|
return;
|
|
}
|
|
|
|
static void
|
|
rum_update_promisc_cb(struct ifnet *ifp)
|
|
{
|
|
struct rum_softc *sc = ifp->if_softc;
|
|
|
|
mtx_lock(&sc->sc_mtx);
|
|
usb2_config_td_queue_command
|
|
(&sc->sc_config_td, &rum_config_copy,
|
|
&rum_cfg_update_promisc, 0, 0);
|
|
mtx_unlock(&sc->sc_mtx);
|
|
return;
|
|
}
|