From: Sepherosa Ziehau Date: Sun, 3 Sep 2006 07:37:58 +0000 (+0000) Subject: - Port rtw(4) from NetBSD, which supports various RealTek 8180 chip based X-Git-Tag: v2.0.1~4474 X-Git-Url: https://gitweb.dragonflybsd.org/dragonfly.git/commitdiff_plain/44db266b1a27242b4fb0210b92825d4ae69545f9 - Port rtw(4) from NetBSD, which supports various RealTek 8180 chip based wireless NIC. - Put NetBSD 802.11 duration related structures and functions in rtw.c and rtwvar.h, and rename them to rtw_xxxx. - Fix various ieee80211_node leakages in TX path. - Use spare RX DMA map to recover from bus_dmamap_load_mbuf() failure. - Utilize TX rate control algorithm framework in our 802.11 layer, support Onoe TX rate control algorithm. - Hook rtw(4) into module building. - Hook rtw(4) into GENERIC and LINT. Thank David Young and many other people for their work on this driver. Tested with a Linksys WPC11 ver.4 --- diff --git a/sys/conf/files b/sys/conf/files index 466a615ddc..beff709439 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -1,5 +1,5 @@ # $FreeBSD: src/sys/conf/files,v 1.340.2.137 2003/06/04 17:10:30 sam Exp $ -# $DragonFly: src/sys/conf/files,v 1.138 2006/09/01 15:12:11 sephe Exp $ +# $DragonFly: src/sys/conf/files,v 1.139 2006/09/03 07:37:58 sephe Exp $ # # The long compile-with and dependency lines are required because of # limitations in config: backslash-newline doesn't work in strings, and @@ -384,6 +384,11 @@ dev/netif/vx/if_vx_pci.c optional vx pci dev/netif/wi/if_wi.c optional wi dev/netif/wi/if_wi_pccard.c optional wi pccard dev/netif/wi/if_wi_pci.c optional wi pci +dev/netif/rtw/rtw.c optional rtw +dev/netif/rtw/rtwphy.c optional rtw +dev/netif/rtw/rtwphyio.c optional rtw +dev/netif/rtw/smc93cx6.c optional rtw +dev/netif/rtw/if_rtw_pci.c optional rtw pci dev/netif/xe/if_xe.c optional xe dev/netif/xe/if_xe_pccard.c optional xe pccard vfs/gnu/ext2fs/ext2_alloc.c optional ext2fs diff --git a/sys/config/GENERIC b/sys/config/GENERIC index 79baf10546..bdfa8f8875 100644 --- a/sys/config/GENERIC +++ b/sys/config/GENERIC @@ -4,7 +4,7 @@ # Check the LINT configuration file in sys/i386/conf, for an # exhaustive list of options. # -# $DragonFly: src/sys/config/GENERIC,v 1.36 2006/05/20 09:13:09 sephe Exp $ +# $DragonFly: src/sys/config/GENERIC,v 1.37 2006/09/03 07:37:58 sephe Exp $ machine i386 cpu I386_CPU @@ -229,6 +229,7 @@ device wlan_wep # 802.11 WEP support device an #device awi # PRISM I IEEE 802.11b wireless NIC device ral # Ralink Technology 802.11 wireless NIC +device rtw # RealTek 802.11 wireless NIC # WaveLAN/IEEE 802.11 wireless NICs. Note: the WaveLAN/IEEE really # exists only as a PCMCIA device, so there is no ISA attachment needed # and resources will always be dynamically assigned by the pccard code. diff --git a/sys/config/LINT b/sys/config/LINT index f4739864a2..1cdd7007f0 100644 --- a/sys/config/LINT +++ b/sys/config/LINT @@ -3,7 +3,7 @@ # as much of the source tree as it can. # # $FreeBSD: src/sys/i386/conf/LINT,v 1.749.2.144 2003/06/04 17:56:59 sam Exp $ -# $DragonFly: src/sys/config/LINT,v 1.89 2006/09/01 15:12:11 sephe Exp $ +# $DragonFly: src/sys/config/LINT,v 1.90 2006/09/03 07:37:58 sephe Exp $ # # NB: You probably don't want to try running a kernel built from this # file. Instead, you should start from GENERIC, and add options from @@ -1421,6 +1421,7 @@ device an # Aironet Communications 4500/4800 device ipw # Intel PRO/Wireless 2100 device iwi # Intel PRO/Wireless 2200BG/2915ABG device wi # WaveLAN/IEEE, PRISM-II, Spectrum24 802.11DS +device rtw # RealTek 8180 device acx # TI ACX100/ACX111 device wl0 at isa? port 0x300 # T1 speed ISA/radio lan device xe # Xircom PCMCIA diff --git a/sys/dev/netif/Makefile b/sys/dev/netif/Makefile index 8085d23abb..8cfaaac706 100644 --- a/sys/dev/netif/Makefile +++ b/sys/dev/netif/Makefile @@ -1,8 +1,8 @@ -# $DragonFly: src/sys/dev/netif/Makefile,v 1.21 2006/08/27 03:36:08 sephe Exp $ +# $DragonFly: src/sys/dev/netif/Makefile,v 1.22 2006/09/03 07:37:58 sephe Exp $ # SUBDIR= an acx ar ath aue axe bfe bge cue dc ed em ep fwe fxp gx ipw iwi kue \ - lge lnc mii_layer my nfe nge nv pcn ral ray re rl rue sbni sbsh sf \ - sis sk sr ste ti tl tx txp vge vr vx wb wi xe xl + lge lnc mii_layer my nfe nge nv pcn ral ray re rl rtw rue sbni sbsh \ + sf sis sk sr ste ti tl tx txp vge vr vx wb wi xe xl .include diff --git a/sys/dev/netif/rtw/Makefile b/sys/dev/netif/rtw/Makefile new file mode 100644 index 0000000000..0d9957a503 --- /dev/null +++ b/sys/dev/netif/rtw/Makefile @@ -0,0 +1,14 @@ +# $DragonFly: src/sys/dev/netif/rtw/Makefile,v 1.1 2006/09/03 07:37:58 sephe Exp $ +KMOD = if_rtw + +SRCS = if_rtw_pci.c smc93cx6.c rtwphyio.c rtwphy.c rtw.c +SRCS += device_if.h bus_if.h pci_if.h opt_aic7xxx.h + +.if !defined(BUILDING_WITH_KERNEL) +opt_aic7xxx.h: + echo "" > ${.TARGET} +.endif + +KMODDEPS= wlan wlan_ratectl_onoe + +.include diff --git a/sys/dev/netif/rtw/if_rtw_pci.c b/sys/dev/netif/rtw/if_rtw_pci.c new file mode 100644 index 0000000000..5eafccdf79 --- /dev/null +++ b/sys/dev/netif/rtw/if_rtw_pci.c @@ -0,0 +1,261 @@ +/* + * Copyright (c) 2006 The DragonFly Project. All rights reserved. + * + * This code is derived from software contributed to The DragonFly Project + * by Sepherosa Ziehau + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of The DragonFly Project nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific, prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $NetBSD: if_rtw_pci.c,v 1.4 2005/12/04 17:44:02 christos Exp $ + * $DragonFly: src/sys/dev/netif/rtw/if_rtw_pci.c,v 1.1 2006/09/03 07:37:58 sephe Exp $ + */ + +/* + * Copyright (c) 1998, 1999, 2000, 2002 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility, + * NASA Ames Research Center; Charles M. Hannum; and David Young. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * PCI bus front-end for the Realtek RTL8180 802.11 MAC/BBP chip. + */ + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include "rtwreg.h" +#include "sa2400reg.h" +#include "rtwvar.h" + +/* + * PCI configuration space registers + */ +#define RTW_PCI_IOBA 0x10 /* i/o mapped base */ +#define RTW_PCI_MMBA 0x14 /* memory mapped base */ + +static const struct rtw_pci_reg { + int reg_type; + int reg_rid; +} rtw_pci_regs[] = { + /* Prefer IO memory over IO port */ + { SYS_RES_MEMORY, RTW_PCI_MMBA }, + { SYS_RES_IOPORT, RTW_PCI_IOBA } +}; + +static const struct rtw_pci_product { + uint16_t app_vendor; /* PCI vendor ID */ + uint16_t app_product; /* PCI product ID */ + const char *app_product_name; +} rtw_pci_products[] = { + { PCI_VENDOR_REALTEK, PCI_PRODUCT_REALTEK_RT8180, + "Realtek RTL8180 802.11 MAC/BBP" }, + { PCI_VENDOR_BELKIN, PCI_PRODUCT_BELKIN_F5D6001, + "Belkin F5D6001" }, + + { 0, 0, NULL } +}; + +static int rtw_pci_probe(device_t); +static int rtw_pci_attach(device_t); +static int rtw_pci_detach(device_t); +static int rtw_pci_shutdown(device_t); + +static device_method_t rtw_pci_methods[] = { + DEVMETHOD(device_probe, rtw_pci_probe), + DEVMETHOD(device_attach, rtw_pci_attach), + DEVMETHOD(device_detach, rtw_pci_detach), + DEVMETHOD(device_shutdown, rtw_pci_shutdown), +#if 0 + DEVMETHOD(device_suspend, rtw_pci_suspend), + DEVMETHOD(device_resume, rtw_pci_resume), +#endif + { 0, 0 } +}; + +static driver_t rtw_pci_driver = { + "rtw", + rtw_pci_methods, + sizeof(struct rtw_softc) +}; + +DRIVER_MODULE(rtw, pci, rtw_pci_driver, rtw_devclass, 0, 0); +DRIVER_MODULE(rtw, cardbus, rtw_pci_driver, rtw_devclass, 0, 0); + +MODULE_DEPEND(rtw, wlan, 1, 1, 1); +MODULE_DEPEND(rtw, wlan_ratectl_onoe, 1, 1, 1); +MODULE_DEPEND(rtw, pci, 1, 1, 1); +MODULE_DEPEND(rtw, cardbus, 1, 1, 1); + +static int +rtw_pci_probe(device_t dev) +{ + const struct rtw_pci_product *app; + uint16_t vid, did; + + vid = pci_get_vendor(dev); + did = pci_get_device(dev); + for (app = rtw_pci_products; app->app_product_name != NULL; app++) { + if (vid == app->app_vendor && did == app->app_product) { + device_set_desc(dev, app->app_product_name); + return 0; + } + } + return ENXIO; +} + +static int +rtw_pci_attach(device_t dev) +{ + struct rtw_softc *sc = device_get_softc(dev); + struct rtw_regs *regs = &sc->sc_regs; + int i, error; + + /* + * No power management hooks. + * XXX Maybe we should add some! + */ + sc->sc_flags |= RTW_F_ENABLED; + + sc->sc_rev = pci_get_revid(dev); + +#ifndef BURN_BRIDGES + if (pci_get_powerstate(dev) != PCI_POWERSTATE_D0) { + uint32_t mem, port, irq; + + mem = pci_read_config(dev, RTW_PCI_MMBA, 4); + port = pci_read_config(dev, RTW_PCI_IOBA, 4); + irq = pci_read_config(dev, PCIR_INTLINE, 4); + + device_printf(dev, "chip is in D%d power mode " + "-- setting to D0\n", pci_get_powerstate(dev)); + + pci_set_powerstate(dev, PCI_POWERSTATE_D0); + + pci_write_config(dev, RTW_PCI_MMBA, mem, 4); + pci_write_config(dev, RTW_PCI_IOBA, port, 4); + pci_write_config(dev, PCIR_INTLINE, irq, 4); + } +#endif /* !BURN_BRIDGES */ + + /* Enable PCI bus master */ + pci_enable_busmaster(dev); + + /* Allocate IO memory/port */ +#define N(arr) sizeof(arr) / sizeof(arr[0]) + for (i = 0; i < N(rtw_pci_regs); ++i) { + regs->r_rid = rtw_pci_regs[i].reg_rid; + regs->r_type = rtw_pci_regs[i].reg_type; + regs->r_res = bus_alloc_resource_any(dev, regs->r_type, + ®s->r_rid, RF_ACTIVE); + if (regs->r_res != NULL) + break; + } +#undef N + if (regs->r_res == NULL) { + device_printf(dev, "can't allocate IO mem/port\n"); + return ENXIO; + } + regs->r_bh = rman_get_bushandle(regs->r_res); + regs->r_bt = rman_get_bustag(regs->r_res); + + error = rtw_attach(dev); + if (error) + rtw_pci_detach(dev); + return error; +} + +static int +rtw_pci_detach(device_t dev) +{ + struct rtw_softc *sc = device_get_softc(dev); + struct rtw_regs *regs = &sc->sc_regs; + + if (device_is_attached(dev)) + rtw_detach(dev); + + if (regs->r_res != NULL) { + bus_release_resource(dev, regs->r_type, regs->r_rid, + regs->r_res); + } + return 0; +} + +static int +rtw_pci_shutdown(device_t dev) +{ + rtw_stop(device_get_softc(dev), 1); + return 0; +} diff --git a/sys/dev/netif/rtw/max2820reg.h b/sys/dev/netif/rtw/max2820reg.h new file mode 100644 index 0000000000..2b477bf7d9 --- /dev/null +++ b/sys/dev/netif/rtw/max2820reg.h @@ -0,0 +1,190 @@ +/* + * Copyright (c) 2004 David Young. All rights reserved. + * + * This code was written by David Young. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY David Young ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL David + * Young BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * $NetBSD: max2820reg.h,v 1.5 2006/03/08 08:26:50 dyoung Exp $ + * $DragonFly: src/sys/dev/netif/rtw/max2820reg.h,v 1.1 2006/09/03 07:37:58 sephe Exp $ + */ + +#ifndef _DEV_IC_MAX2820REG_H_ +#define _DEV_IC_MAX2820REG_H_ + +/* + * Serial bus format for Maxim MAX2820/MAX2820A/MAX2821/MAX2821A + * 2.4GHz 802.11b Zero-IF Transceivers + */ +#define MAX2820_TWI_ADDR_MASK __BITS(15,12) +#define MAX2820_TWI_DATA_MASK __BITS(11,0) + +/* + * Registers for Maxim MAX2820/MAX2820A/MAX2821/MAX2821A 2.4GHz + * 802.11b Zero-IF Transceivers + */ +#define MAX2820_TEST 0 /* Test Register */ +#define MAX2820_TEST_DEFAULT __BITS(2,0) /* Always set to this value. */ + +#define MAX2820_ENABLE 1 /* Block-Enable Register */ +#define MAX2820_ENABLE_RSVD1 __BIT(11) /* reserved */ +#define MAX2820_ENABLE_PAB __BIT(10) /* Transmit Baseband Filters + * Enable + * PAB_EN = SHDNB && + * (MAX2820_ENABLE_PAB || + * TX_ON) + */ +#define MAX2820_ENABLE_TXFLT __BIT(9) /* Transmit Baseband Filters + * Enable + * TXFLT_EN = SHDNB && + * (MAX2820_ENABLE_TXFLT || + * TX_ON) + */ +#define MAX2820_ENABLE_TXUVD __BIT(8) /* Tx Upconverter, VGA, and + * Driver Amp Enable + * TXUVD_EN = SHDNB && + * (MAX2820_ENABLE_TXUVD || + * TX_ON) + */ +#define MAX2820_ENABLE_DET __BIT(7) /* Receive Detector Enable + * DET_EN = SHDNB && + * (MAX2820_ENABLE_DET || + * RX_ON) + */ +#define MAX2820_ENABLE_RXDFA __BIT(6) /* Rx Downconverter, Filters, + * and AGC Amps Enable + * RXDFA_EN = SHDNB && + * (MAX2820_ENABLE_RXDFA || + * RX_ON) + */ +#define MAX2820_ENABLE_RXLNA __BIT(5) /* Receive LNA Enable + * AT_EN = SHDNB && + * (MAX2820_ENABLE_RXLNA || + * RX_ON) + */ +#define MAX2820_ENABLE_AT __BIT(4) /* Auto-tuner Enable + * AT_EN = SHDNB && + * (MAX2820_ENABLE_AT || + * RX_ON || TX_ON) + */ +#define MAX2820_ENABLE_CP __BIT(3) /* PLL Charge-Pump Enable + * CP_EN = SHDNB + * && MAX2820_ENABLE_CP + */ +#define MAX2820_ENABLE_PLL __BIT(2) /* PLL Enable + * PLL_EN = SHDNB + * && MAX2820_ENABLE_PLL + */ +#define MAX2820_ENABLE_VCO __BIT(1) /* VCO Enable + * VCO_EN = SHDNB + * && MAX2820_ENABLE_VCO + */ +#define MAX2820_ENABLE_RSVD0 __BIT(0) /* reserved */ +#define MAX2820_ENABLE_DEFAULT (MAX2820_ENABLE_AT|MAX2820_ENABLE_CP|\ + MAX2820_ENABLE_PLL|MAX2820_ENABLE_VCO) + +#define MAX2820_SYNTH 2 /* Synthesizer Register */ +#define MAX2820_SYNTH_RSVD0 __BITS(11,7) /* reserved */ +#define MAX2820_SYNTH_ICP __BIT(6) /* Charge-Pump Current Select + * 0 = +/-1mA + * 1 = +/-2mA + */ +#define MAX2820_SYNTH_R_MASK __BITS(5,0) /* Reference Frequency Divider + * 0 = 22MHz + * 1 = 44MHz + */ +#define MAX2820_SYNTH_R_22MHZ SHIFTIN(0, MAX2820_SYNTH_R_MASK) +#define MAX2820_SYNTH_R_44MHZ SHIFTIN(1, MAX2820_SYNTH_R_MASK) +#define MAX2820_SYNTH_ICP_DEFAULT MAX2820_SYNTH_ICP +#define MAX2820_SYNTH_R_DEFAULT SHIFTIN(0, MAX2820_SYNTH_R_MASK) + +#define MAX2820_CHANNEL 3 /* Channel Frequency Register */ +#define MAX2820_CHANNEL_RSVD __BITS(11,7) /* reserved */ +#define MAX2820_CHANNEL_CF_MASK __BITS(6,0) /* Channel Frequency Select + * fLO = 2400MHz + CF * 1MHz + */ +#define MAX2820_CHANNEL_RSVD_DEFAULT SHIFTIN(0, MAX2820_CHANNEL_RSVD) +#define MAX2820_CHANNEL_CF_DEFAULT SHIFTIN(37, MAX2820_CHANNEL_CF_MASK) + +#define MAX2820_RECEIVE 4 /* Receiver Settings Register + * MAX2820/MAX2821 + */ +#define MAX2820_RECEIVE_2C_MASK __BITS(11,9) /* VGA DC Offset Nulling + * Parameter 2 + */ +#define MAX2820_RECEIVE_1C_MASK __BITS(8,6) /* VGA DC Offset Nulling + * Parameter 1 + */ +#define MAX2820_RECEIVE_DL_MASK __BITS(5,4) /* Rx Level Detector Midpoint + * Select + * 11, 01 = 50.2mVp + * 10 = 70.9mVp + * 00 = 35.5mVp + */ +#define MAX2820_RECEIVE_SF __BIT(3) /* Special Function Select + * 0 = OFF + * 1 = ON + */ +#define MAX2820_RECEIVE_BW_MASK __BITS(2,0) /* Receive Filter -3dB Frequency + * Select (all frequencies are + * approximate) + */ +/* 8.5MHz */ +#define MAX2820_RECEIVE_BW_8_5MHZ SHIFTIN(0, MAX2820_RECEIVE_BW_MASK) +#define MAX2820_RECEIVE_BW_8MHZ SHIFTIN(1, MAX2820_RECEIVE_BW_MASK) +#define MAX2820_RECEIVE_BW_7_5MHZ SHIFTIN(2, MAX2820_RECEIVE_BW_MASK) +#define MAX2820_RECEIVE_BW_7MHZ SHIFTIN(3, MAX2820_RECEIVE_BW_MASK) +#define MAX2820_RECEIVE_BW_6_5MHZ SHIFTIN(4, MAX2820_RECEIVE_BW_MASK) +#define MAX2820_RECEIVE_BW_6MHZ SHIFTIN(5, MAX2820_RECEIVE_BW_MASK) +#define MAX2820_RECEIVE_2C_DEFAULT SHIFTIN(7, MAX2820_RECEIVE_2C_MASK) +#define MAX2820_RECEIVE_1C_DEFAULT SHIFTIN(7, MAX2820_RECEIVE_1C_MASK) +#define MAX2820_RECEIVE_DL_DEFAULT SHIFTIN(1, MAX2820_RECEIVE_DL_MASK) +#define MAX2820_RECEIVE_SF_DEFAULT SHIFTIN(0, MAX2820_RECEIVE_SF) +#define MAX2820_RECEIVE_BW_DEFAULT MAX2820_RECEIVE_BW_7_5MHZ + +#define MAX2820A_RECEIVE 4 /* Receiver Settings Register, + * MAX2820A/MAX2821A + */ +/* VGA DC Offset Nulling Parameter 2 */ +#define MAX2820A_RECEIVE_2C_MASK __BITS(11,9) +#define MAX2820A_RECEIVE_2C_DEFAULT SHIFTIN(7, MAX2820A_RECEIVE_2C_MASK) +/* VGA DC Offset Nulling Parameter 1 */ +#define MAX2820A_RECEIVE_1C_MASK __BITS(8,6) +#define MAX2820A_RECEIVE_1C_DEFAULT SHIFTIN(7, MAX2820A_RECEIVE_1C_MASK) +#define MAX2820A_RECEIVE_RSVD0_MASK __BITS(5,3) +#define MAX2820A_RECEIVE_RSVD0_DEFAULT SHIFTIN(2, MAX2820A_RECEIVE_RSVD0_MASK) +#define MAX2820A_RECEIVE_RSVD1_MASK __BITS(2,0) +#define MAX2820A_RECEIVE_RSVD1_DEFAULT SHIFTIN(2,MAX2820_RECEIVE_RSVD1_MASK) + +#define MAX2820_TRANSMIT 5 /* Transmitter Settings Reg. */ +#define MAX2820_TRANSMIT_RSVD_MASK __BITS(11,4) /* reserved */ +#define MAX2820_TRANSMIT_PA_MASK __BITS(3,0) /* PA Bias Select + * 15 = Highest + * 0 = Lowest + */ +#define MAX2820_TRANSMIT_PA_DEFAULT SHIFTIN(0, MAX2820_TRANSMIT_PA_MASK) + +#endif /* _DEV_IC_MAX2820REG_H_ */ diff --git a/sys/dev/netif/rtw/rtw.c b/sys/dev/netif/rtw/rtw.c new file mode 100644 index 0000000000..05441d776e --- /dev/null +++ b/sys/dev/netif/rtw/rtw.c @@ -0,0 +1,4450 @@ +/* + * Copyright (c) 2006 The DragonFly Project. All rights reserved. + * + * This code is derived from software contributed to The DragonFly Project + * by Sepherosa Ziehau + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of The DragonFly Project nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific, prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $NetBSD: rtw.c,v 1.72 2006/03/28 00:48:10 dyoung Exp $ + * $DragonFly: src/sys/dev/netif/rtw/rtw.c,v 1.1 2006/09/03 07:37:58 sephe Exp $ + */ + +/* + * Copyright (c) 2004, 2005 David Young. All rights reserved. + * + * Programmed for NetBSD by David Young. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of David Young may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY David Young ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL David + * Young BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + */ + +/* + * Device driver for the Realtek RTL8180 802.11 MAC/BBP. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "rtwbitop.h" +#include "rtwreg.h" +#include "rtwvar.h" +#include "rtwphyio.h" +#include "rtwphy.h" +#include "smc93cx6var.h" + +/* XXX */ +#define IEEE80211_DUR_DS_LONG_PREAMBLE 144 +#define IEEE80211_DUR_DS_SHORT_PREAMBLE 72 +#define IEEE80211_DUR_DS_SLOW_PLCPHDR 48 +#define IEEE80211_DUR_DS_FAST_PLCPHDR 24 +#define IEEE80211_DUR_DS_SLOW_ACK 112 +#define IEEE80211_DUR_DS_SLOW_CTS 112 +#define IEEE80211_DUR_DS_SIFS 10 + +struct rtw_txsegs { + int nseg; + bus_dma_segment_t segs[RTW_MAXPKTSEGS]; +}; + +devclass_t rtw_devclass; + +static const struct ieee80211_rateset rtw_rates_11b = { 4, { 2, 4, 11, 22 } }; + +SYSCTL_NODE(_hw, OID_AUTO, rtw, CTLFLAG_RD, 0, + "Realtek RTL818x 802.11 controls"); + +/* [0, SHIFTOUT(RTW_CONFIG4_RFTYPE_MASK, RTW_CONFIG4_RFTYPE_MASK)] */ +static int rtw_rfprog_fallback = 0; +SYSCTL_INT(_hw_rtw, OID_AUTO, rfprog_fallback, CTLFLAG_RW, + &rtw_rfprog_fallback, 0, "fallback RF programming method"); + +static int rtw_host_rfio = 0; /* 0/1 */ +SYSCTL_INT(_hw_rtw, OID_AUTO, host_rfio, CTLFLAG_RW, + &rtw_host_rfio, 0, "enable host control of RF I/O"); + +#ifdef RTW_DEBUG +int rtw_debug = 0; /* [0, RTW_DEBUG_MAX] */ +SYSCTL_INT(_hw_rtw, OID_AUTO, debug, CTLFLAG_RW, &rtw_debug, 0, "debug level"); + +static int rtw_rxbufs_limit = RTW_RXQLEN; /* [0, RTW_RXQLEN] */ +SYSCTL_INT(_hw_rtw, OID_AUTO, rxbufs_limit, CTLFLAG_RW, &rtw_rxbufs_limit, 0, + "rx buffers limit"); +#endif /* RTW_DEBUG */ + +#if 0 +static int rtw_xmtr_restart = 0; +SYSCTL_INT(_hw_rtw, OID_AUTO, xmtr_restart, CTLFLAG_RW, &rtw_xmtr_restart, 0, + "gratuitously reset xmtr on rcvr error"); + +static int rtw_ring_reset = 0; +SYSCTL_INT(_hw_rtw, OID_AUTO, ring_reset, CTLFLAG_RW, &rtw_ring_reset, 0, + "reset ring pointers on rcvr error"); +#endif + +static int rtw_do_chip_reset = 0; +SYSCTL_INT(_hw_rtw, OID_AUTO, chip_reset, CTLFLAG_RW, &rtw_do_chip_reset, 0, + "gratuitously reset chip on rcvr error"); + +int rtw_dwelltime = 200; /* milliseconds */ + +/* XXX */ +static struct ieee80211_cipher rtw_cipher_wep; + +static void rtw_led_init(struct rtw_softc *); +static void rtw_led_newstate(struct rtw_softc *, enum ieee80211_state); +static void rtw_led_slowblink(void *); +static void rtw_led_fastblink(void *); +static void rtw_led_set(struct rtw_softc *); + +static void rtw_init(void *); +static void rtw_start(struct ifnet *); +static int rtw_ioctl(struct ifnet *, u_long, caddr_t, struct ucred *); +static void rtw_watchdog(struct ifnet *); +static void rtw_intr(void *); + +static void rtw_intr_rx(struct rtw_softc *, uint16_t); +static void rtw_intr_tx(struct rtw_softc *, uint16_t); +static void rtw_intr_beacon(struct rtw_softc *, uint16_t); +static void rtw_intr_atim(struct rtw_softc *); +static void rtw_intr_ioerror(struct rtw_softc *, uint16_t); +static void rtw_intr_timeout(struct rtw_softc *); + +static int rtw_dequeue(struct ifnet *, struct rtw_txsoft_blk **, + struct rtw_txdesc_blk **, struct mbuf **, + struct ieee80211_node **); +static struct mbuf *rtw_load_txbuf(struct rtw_softc *, struct rtw_txsoft *, + struct rtw_txsegs *, int, struct mbuf *); + +static void rtw_idle(struct rtw_softc *); +static void rtw_txring_fixup(struct rtw_softc *); +static void rtw_rxring_fixup(struct rtw_softc *); +static int rtw_txring_next(struct rtw_regs *, struct rtw_txdesc_blk *); +static void rtw_reset_oactive(struct rtw_softc *); + +static int rtw_enable(struct rtw_softc *); +static void rtw_disable(struct rtw_softc *); +static void rtw_io_enable(struct rtw_softc *, uint8_t, int); +static int rtw_pwrstate(struct rtw_softc *, enum rtw_pwrstate); +static void rtw_set_access(struct rtw_softc *, enum rtw_access); + +static void rtw_continuous_tx_enable(struct rtw_softc *, int); +static void rtw_txdac_enable(struct rtw_softc *, int); +static void rtw_anaparm_enable(struct rtw_regs *, int); +static void rtw_config0123_enable(struct rtw_regs *, int); + +static void rtw_transmit_config(struct rtw_regs *); +static void rtw_set_rfprog(struct rtw_softc *); +static void rtw_enable_interrupts(struct rtw_softc *); +static void rtw_pktfilt_load(struct rtw_softc *); +static void rtw_wep_setkeys(struct rtw_softc *); +static void rtw_resume_ticks(struct rtw_softc *); +static void rtw_set_nettype(struct rtw_softc *, enum ieee80211_opmode); + +static int rtw_reset(struct rtw_softc *); +static int rtw_chip_reset(struct rtw_softc *); +static int rtw_recall_eeprom(struct rtw_softc *); +static int rtw_srom_read(struct rtw_softc *); +static int rtw_srom_parse(struct rtw_softc *); +static struct rtw_rf *rtw_rf_attach(struct rtw_softc *, enum rtw_rfchipid, int); + +static uint8_t rtw_check_phydelay(struct rtw_regs *, uint32_t); +static void rtw_identify_country(struct rtw_softc *); +static int rtw_identify_sta(struct rtw_softc *); + +static int rtw_swring_setup(struct rtw_softc *); +static void rtw_hwring_setup(struct rtw_softc *); + +static int rtw_desc_blk_alloc(struct rtw_softc *); +static void rtw_desc_blk_free(struct rtw_softc *); +static int rtw_soft_blk_alloc(struct rtw_softc *); +static void rtw_soft_blk_free(struct rtw_softc *); + +static void rtw_txdesc_blk_init_all(struct rtw_softc *); +static void rtw_txsoft_blk_init_all(struct rtw_softc *); +static void rtw_rxdesc_blk_init_all(struct rtw_softc *); +static int rtw_rxsoft_blk_init_all(struct rtw_softc *); + +static void rtw_txdesc_blk_reset_all(struct rtw_softc *); + +static int rtw_rxsoft_alloc(struct rtw_softc *, struct rtw_rxsoft *, int); +static void rtw_rxdesc_init(struct rtw_softc *, int idx, int); + +#ifdef RTW_DEBUG +static void rtw_print_txdesc(struct rtw_softc *, const char *, + struct rtw_txsoft *, struct rtw_txdesc_blk *, + int); +#endif /* RTW_DEBUG */ + +static int rtw_newstate(struct ieee80211com *, enum ieee80211_state, int); +static void rtw_next_scan(void *); + +static int rtw_key_delete(struct ieee80211com *, + const struct ieee80211_key *); +static int rtw_key_set(struct ieee80211com *, + const struct ieee80211_key *, + const u_int8_t[IEEE80211_ADDR_LEN]); +static void rtw_key_update_end(struct ieee80211com *); +static void rtw_key_update_begin(struct ieee80211com *); +static int rtw_wep_decap(struct ieee80211_key *, struct mbuf *, int); + +static int rtw_compute_duration1(int, int, uint32_t, int, + struct rtw_duration *); +static int rtw_compute_duration(const struct ieee80211_frame_min *, + const struct ieee80211_key *, int, + uint32_t, int, int, + struct rtw_duration *, + struct rtw_duration *, int *, int); + +#ifdef RTW_DEBUG +static void +rtw_print_regs(struct rtw_regs *regs, const char *dvname, const char *where) +{ +#define PRINTREG32(sc, reg) \ + RTW_DPRINTF(RTW_DEBUG_REGDUMP, \ + ("%s: reg[ " #reg " / %03x ] = %08x\n", \ + dvname, reg, RTW_READ(regs, reg))) + +#define PRINTREG16(sc, reg) \ + RTW_DPRINTF(RTW_DEBUG_REGDUMP, \ + ("%s: reg[ " #reg " / %03x ] = %04x\n", \ + dvname, reg, RTW_READ16(regs, reg))) + +#define PRINTREG8(sc, reg) \ + RTW_DPRINTF(RTW_DEBUG_REGDUMP, \ + ("%s: reg[ " #reg " / %03x ] = %02x\n", \ + dvname, reg, RTW_READ8(regs, reg))) + + RTW_DPRINTF(RTW_DEBUG_REGDUMP, ("%s: %s\n", dvname, where)); + + PRINTREG32(regs, RTW_IDR0); + PRINTREG32(regs, RTW_IDR1); + PRINTREG32(regs, RTW_MAR0); + PRINTREG32(regs, RTW_MAR1); + PRINTREG32(regs, RTW_TSFTRL); + PRINTREG32(regs, RTW_TSFTRH); + PRINTREG32(regs, RTW_TLPDA); + PRINTREG32(regs, RTW_TNPDA); + PRINTREG32(regs, RTW_THPDA); + PRINTREG32(regs, RTW_TCR); + PRINTREG32(regs, RTW_RCR); + PRINTREG32(regs, RTW_TINT); + PRINTREG32(regs, RTW_TBDA); + PRINTREG32(regs, RTW_ANAPARM); + PRINTREG32(regs, RTW_BB); + PRINTREG32(regs, RTW_PHYCFG); + PRINTREG32(regs, RTW_WAKEUP0L); + PRINTREG32(regs, RTW_WAKEUP0H); + PRINTREG32(regs, RTW_WAKEUP1L); + PRINTREG32(regs, RTW_WAKEUP1H); + PRINTREG32(regs, RTW_WAKEUP2LL); + PRINTREG32(regs, RTW_WAKEUP2LH); + PRINTREG32(regs, RTW_WAKEUP2HL); + PRINTREG32(regs, RTW_WAKEUP2HH); + PRINTREG32(regs, RTW_WAKEUP3LL); + PRINTREG32(regs, RTW_WAKEUP3LH); + PRINTREG32(regs, RTW_WAKEUP3HL); + PRINTREG32(regs, RTW_WAKEUP3HH); + PRINTREG32(regs, RTW_WAKEUP4LL); + PRINTREG32(regs, RTW_WAKEUP4LH); + PRINTREG32(regs, RTW_WAKEUP4HL); + PRINTREG32(regs, RTW_WAKEUP4HH); + PRINTREG32(regs, RTW_DK0); + PRINTREG32(regs, RTW_DK1); + PRINTREG32(regs, RTW_DK2); + PRINTREG32(regs, RTW_DK3); + PRINTREG32(regs, RTW_RETRYCTR); + PRINTREG32(regs, RTW_RDSAR); + PRINTREG32(regs, RTW_FER); + PRINTREG32(regs, RTW_FEMR); + PRINTREG32(regs, RTW_FPSR); + PRINTREG32(regs, RTW_FFER); + + /* 16-bit registers */ + PRINTREG16(regs, RTW_BRSR); + PRINTREG16(regs, RTW_IMR); + PRINTREG16(regs, RTW_ISR); + PRINTREG16(regs, RTW_BCNITV); + PRINTREG16(regs, RTW_ATIMWND); + PRINTREG16(regs, RTW_BINTRITV); + PRINTREG16(regs, RTW_ATIMTRITV); + PRINTREG16(regs, RTW_CRC16ERR); + PRINTREG16(regs, RTW_CRC0); + PRINTREG16(regs, RTW_CRC1); + PRINTREG16(regs, RTW_CRC2); + PRINTREG16(regs, RTW_CRC3); + PRINTREG16(regs, RTW_CRC4); + PRINTREG16(regs, RTW_CWR); + + /* 8-bit registers */ + PRINTREG8(regs, RTW_CR); + PRINTREG8(regs, RTW_9346CR); + PRINTREG8(regs, RTW_CONFIG0); + PRINTREG8(regs, RTW_CONFIG1); + PRINTREG8(regs, RTW_CONFIG2); + PRINTREG8(regs, RTW_MSR); + PRINTREG8(regs, RTW_CONFIG3); + PRINTREG8(regs, RTW_CONFIG4); + PRINTREG8(regs, RTW_TESTR); + PRINTREG8(regs, RTW_PSR); + PRINTREG8(regs, RTW_SCR); + PRINTREG8(regs, RTW_PHYDELAY); + PRINTREG8(regs, RTW_CRCOUNT); + PRINTREG8(regs, RTW_PHYADDR); + PRINTREG8(regs, RTW_PHYDATAW); + PRINTREG8(regs, RTW_PHYDATAR); + PRINTREG8(regs, RTW_CONFIG5); + PRINTREG8(regs, RTW_TPPOLL); + + PRINTREG16(regs, RTW_BSSID16); + PRINTREG32(regs, RTW_BSSID32); +#undef PRINTREG32 +#undef PRINTREG16 +#undef PRINTREG8 +} +#endif /* RTW_DEBUG */ + +static void +rtw_continuous_tx_enable(struct rtw_softc *sc, int enable) +{ + struct rtw_regs *regs = &sc->sc_regs; + uint32_t tcr; + + tcr = RTW_READ(regs, RTW_TCR); + tcr &= ~RTW_TCR_LBK_MASK; + if (enable) + tcr |= RTW_TCR_LBK_CONT; + else + tcr |= RTW_TCR_LBK_NORMAL; + RTW_WRITE(regs, RTW_TCR, tcr); + RTW_SYNC(regs, RTW_TCR, RTW_TCR); + rtw_set_access(sc, RTW_ACCESS_ANAPARM); + rtw_txdac_enable(sc, !enable); + rtw_set_access(sc, RTW_ACCESS_ANAPARM);/* XXX Voodoo from Linux. */ + rtw_set_access(sc, RTW_ACCESS_NONE); +} + +#ifdef RTW_DEBUG +static const char * +rtw_access_string(enum rtw_access access) +{ + switch (access) { + case RTW_ACCESS_NONE: + return "none"; + case RTW_ACCESS_CONFIG: + return "config"; + case RTW_ACCESS_ANAPARM: + return "anaparm"; + default: + return "unknown"; + } +} +#endif /* RTW_DEBUG */ + +static void +rtw_set_access1(struct rtw_regs *regs, enum rtw_access naccess) +{ + KKASSERT(naccess >= RTW_ACCESS_NONE && naccess <= RTW_ACCESS_ANAPARM); + KKASSERT(regs->r_access >= RTW_ACCESS_NONE && + regs->r_access <= RTW_ACCESS_ANAPARM); + + if (naccess == regs->r_access) + return; + + switch (naccess) { + case RTW_ACCESS_NONE: + switch (regs->r_access) { + case RTW_ACCESS_ANAPARM: + rtw_anaparm_enable(regs, 0); + /*FALLTHROUGH*/ + case RTW_ACCESS_CONFIG: + rtw_config0123_enable(regs, 0); + /*FALLTHROUGH*/ + case RTW_ACCESS_NONE: + break; + } + break; + case RTW_ACCESS_CONFIG: + switch (regs->r_access) { + case RTW_ACCESS_NONE: + rtw_config0123_enable(regs, 1); + /*FALLTHROUGH*/ + case RTW_ACCESS_CONFIG: + break; + case RTW_ACCESS_ANAPARM: + rtw_anaparm_enable(regs, 0); + break; + } + break; + case RTW_ACCESS_ANAPARM: + switch (regs->r_access) { + case RTW_ACCESS_NONE: + rtw_config0123_enable(regs, 1); + /*FALLTHROUGH*/ + case RTW_ACCESS_CONFIG: + rtw_anaparm_enable(regs, 1); + /*FALLTHROUGH*/ + case RTW_ACCESS_ANAPARM: + break; + } + break; + } +} + +static void +rtw_set_access(struct rtw_softc *sc, enum rtw_access access) +{ + struct rtw_regs *regs = &sc->sc_regs; + + rtw_set_access1(regs, access); + RTW_DPRINTF(RTW_DEBUG_ACCESS, + ("%s: access %s -> %s\n", sc->sc_ic.ic_if.if_xname, + rtw_access_string(regs->r_access), + rtw_access_string(access))); + regs->r_access = access; +} + +/* + * Enable registers, switch register banks. + */ +static void +rtw_config0123_enable(struct rtw_regs *regs, int enable) +{ + uint8_t ecr; + + ecr = RTW_READ8(regs, RTW_9346CR); + ecr &= ~(RTW_9346CR_EEM_MASK | RTW_9346CR_EECS | RTW_9346CR_EESK); + if (enable) { + ecr |= RTW_9346CR_EEM_CONFIG; + } else { + RTW_WBW(regs, RTW_9346CR, MAX(RTW_CONFIG0, RTW_CONFIG3)); + ecr |= RTW_9346CR_EEM_NORMAL; + } + RTW_WRITE8(regs, RTW_9346CR, ecr); + RTW_SYNC(regs, RTW_9346CR, RTW_9346CR); +} + +/* requires rtw_config0123_enable(, 1) */ +static void +rtw_anaparm_enable(struct rtw_regs *regs, int enable) +{ + uint8_t cfg3; + + cfg3 = RTW_READ8(regs, RTW_CONFIG3); + cfg3 |= RTW_CONFIG3_CLKRUNEN; + if (enable) + cfg3 |= RTW_CONFIG3_PARMEN; + else + cfg3 &= ~RTW_CONFIG3_PARMEN; + RTW_WRITE8(regs, RTW_CONFIG3, cfg3); + RTW_SYNC(regs, RTW_CONFIG3, RTW_CONFIG3); +} + +/* requires rtw_anaparm_enable(, 1) */ +static void +rtw_txdac_enable(struct rtw_softc *sc, int enable) +{ + uint32_t anaparm; + struct rtw_regs *regs = &sc->sc_regs; + + anaparm = RTW_READ(regs, RTW_ANAPARM); + if (enable) + anaparm &= ~RTW_ANAPARM_TXDACOFF; + else + anaparm |= RTW_ANAPARM_TXDACOFF; + RTW_WRITE(regs, RTW_ANAPARM, anaparm); + RTW_SYNC(regs, RTW_ANAPARM, RTW_ANAPARM); +} + +static int +rtw_chip_reset1(struct rtw_softc *sc) +{ + struct rtw_regs *regs = &sc->sc_regs; + uint8_t cr; + int i; + + RTW_WRITE8(regs, RTW_CR, RTW_CR_RST); + + RTW_WBR(regs, RTW_CR, RTW_CR); + + for (i = 0; i < 1000; i++) { + if ((cr = RTW_READ8(regs, RTW_CR) & RTW_CR_RST) == 0) { + RTW_DPRINTF(RTW_DEBUG_RESET, + ("%s: reset in %dus\n", + sc->sc_ic.ic_if.if_xname, i)); + return 0; + } + RTW_RBR(regs, RTW_CR, RTW_CR); + DELAY(10); /* 10us */ + } + + if_printf(&sc->sc_ic.ic_if, "reset failed\n"); + return ETIMEDOUT; +} + +static int +rtw_chip_reset(struct rtw_softc *sc) +{ + struct rtw_regs *regs = &sc->sc_regs; + uint32_t tcr; + + /* from Linux driver */ + tcr = RTW_TCR_CWMIN | RTW_TCR_MXDMA_2048 | + SHIFTIN(7, RTW_TCR_SRL_MASK) | SHIFTIN(7, RTW_TCR_LRL_MASK); + + RTW_WRITE(regs, RTW_TCR, tcr); + + RTW_WBW(regs, RTW_CR, RTW_TCR); + + return rtw_chip_reset1(sc); +} + +static int +rtw_wep_decap(struct ieee80211_key *k, struct mbuf *m, int hdrlen) +{ + struct ieee80211_key keycopy; + const struct ieee80211_cipher *wep_cipher; + + RTW_DPRINTF(RTW_DEBUG_KEY, ("%s:\n", __func__)); + + keycopy = *k; + keycopy.wk_flags &= ~IEEE80211_KEY_SWCRYPT; + + wep_cipher = ieee80211_crypto_cipher(IEEE80211_CIPHER_WEP); + KKASSERT(wep_cipher != NULL); + + return wep_cipher->ic_decap(&keycopy, m, hdrlen); +} + +static int +rtw_key_delete(struct ieee80211com *ic, const struct ieee80211_key *k) +{ + struct rtw_softc *sc = ic->ic_ifp->if_softc; + u_int keyix = k->wk_keyix; + + DPRINTF(sc, RTW_DEBUG_KEY, ("%s: delete key %u\n", __func__, keyix)); + + if (keyix >= IEEE80211_WEP_NKID) + return 0; + if (k->wk_keylen != 0) + sc->sc_flags &= ~RTW_F_DK_VALID; + return 1; +} + +static int +rtw_key_set(struct ieee80211com *ic, const struct ieee80211_key *k, + const u_int8_t mac[IEEE80211_ADDR_LEN]) +{ + struct rtw_softc *sc = ic->ic_ifp->if_softc; + + DPRINTF(sc, RTW_DEBUG_KEY, ("%s: set key %u\n", __func__, k->wk_keyix)); + + if (k->wk_keyix >= IEEE80211_WEP_NKID) + return 0; + + sc->sc_flags &= ~RTW_F_DK_VALID; + return 1; +} + +static void +rtw_key_update_begin(struct ieee80211com *ic) +{ +#ifdef RTW_DEBUG + struct ifnet *ifp = ic->ic_ifp; + struct rtw_softc *sc = ifp->if_softc; +#endif + + DPRINTF(sc, RTW_DEBUG_KEY, ("%s:\n", __func__)); +} + +static void +rtw_key_update_end(struct ieee80211com *ic) +{ + struct ifnet *ifp = ic->ic_ifp; + struct rtw_softc *sc = ifp->if_softc; + + DPRINTF(sc, RTW_DEBUG_KEY, ("%s:\n", __func__)); + + if ((sc->sc_flags & RTW_F_DK_VALID) != 0 || + (sc->sc_flags & RTW_F_ENABLED) == 0 || + (sc->sc_flags & RTW_F_INVALID) != 0) + return; + + rtw_io_enable(sc, RTW_CR_RE | RTW_CR_TE, 0); + rtw_wep_setkeys(sc); + rtw_io_enable(sc, RTW_CR_RE | RTW_CR_TE, + (ifp->if_flags & IFF_RUNNING) != 0); +} + +static __inline int +rtw_key_hwsupp(uint32_t flags, const struct ieee80211_key *k) +{ + if (k->wk_cipher->ic_cipher != IEEE80211_CIPHER_WEP) + return 0; + + return ((flags & RTW_C_RXWEP_40) != 0 && k->wk_keylen == 5) || + ((flags & RTW_C_RXWEP_104) != 0 && k->wk_keylen == 13); +} + +static void +rtw_wep_setkeys(struct rtw_softc *sc) +{ + struct ieee80211com *ic = &sc->sc_ic; + struct ieee80211_key *wk = ic->ic_nw_keys; + const struct ieee80211_cipher *wep_cipher; + struct rtw_regs *regs = &sc->sc_regs; + union rtw_keys *rk = &sc->sc_keys; + uint8_t psr, scr; + int i, keylen; + + memset(rk->rk_keys, 0, sizeof(rk->rk_keys)); + + wep_cipher = ieee80211_crypto_cipher(IEEE80211_CIPHER_WEP); + KKASSERT(wep_cipher != NULL); + + /* Temporarily use software crypto for all keys. */ + for (i = 0; i < IEEE80211_WEP_NKID; i++) { + if (wk[i].wk_cipher == &rtw_cipher_wep) + wk[i].wk_cipher = wep_cipher; + } + + rtw_set_access(sc, RTW_ACCESS_CONFIG); + + psr = RTW_READ8(regs, RTW_PSR); + scr = RTW_READ8(regs, RTW_SCR); + scr &= ~(RTW_SCR_KM_MASK | RTW_SCR_TXSECON | RTW_SCR_RXSECON); + + if ((sc->sc_ic.ic_flags & IEEE80211_F_PRIVACY) == 0) + goto out; + + for (keylen = i = 0; i < IEEE80211_WEP_NKID; i++) { + if (!rtw_key_hwsupp(sc->sc_flags, &wk[i])) + continue; + if (i == ic->ic_def_txkey) { + keylen = wk[i].wk_keylen; + break; + } + keylen = MAX(keylen, wk[i].wk_keylen); + } + + if (keylen == 5) + scr |= RTW_SCR_KM_WEP40 | RTW_SCR_RXSECON; + else if (keylen == 13) + scr |= RTW_SCR_KM_WEP104 | RTW_SCR_RXSECON; + + for (i = 0; i < IEEE80211_WEP_NKID; i++) { + if (wk[i].wk_keylen != keylen || + wk[i].wk_cipher->ic_cipher != IEEE80211_CIPHER_WEP) + continue; + /* h/w will decrypt, s/w still strips headers */ + wk[i].wk_cipher = &rtw_cipher_wep; + memcpy(rk->rk_keys[i], wk[i].wk_key, wk[i].wk_keylen); + } +out: + RTW_WRITE8(regs, RTW_PSR, psr & ~RTW_PSR_PSEN); + + bus_space_write_region_4(regs->r_bt, regs->r_bh, RTW_DK0, rk->rk_words, + sizeof(rk->rk_words) / sizeof(rk->rk_words[0])); + + RTW_WBW(regs, RTW_DK0, RTW_PSR); + RTW_WRITE8(regs, RTW_PSR, psr); + RTW_WBW(regs, RTW_PSR, RTW_SCR); + RTW_WRITE8(regs, RTW_SCR, scr); + RTW_SYNC(regs, RTW_SCR, RTW_SCR); + rtw_set_access(sc, RTW_ACCESS_NONE); + sc->sc_flags |= RTW_F_DK_VALID; +} + +static int +rtw_recall_eeprom(struct rtw_softc *sc) +{ + struct rtw_regs *regs = &sc->sc_regs; + int i; + uint8_t ecr; + + ecr = RTW_READ8(regs, RTW_9346CR); + ecr = (ecr & ~RTW_9346CR_EEM_MASK) | RTW_9346CR_EEM_AUTOLOAD; + RTW_WRITE8(regs, RTW_9346CR, ecr); + + RTW_WBR(regs, RTW_9346CR, RTW_9346CR); + + /* wait 25ms for completion */ + for (i = 0; i < 250; i++) { + ecr = RTW_READ8(regs, RTW_9346CR); + if ((ecr & RTW_9346CR_EEM_MASK) == RTW_9346CR_EEM_NORMAL) { + RTW_DPRINTF(RTW_DEBUG_RESET, + ("%s: recall EEPROM in %dus\n", + sc->sc_ic.ic_if.if_xname, i * 100)); + return 0; + } + RTW_RBR(regs, RTW_9346CR, RTW_9346CR); + DELAY(100); + } + if_printf(&sc->sc_ic.ic_if, "recall EEPROM failed\n"); + return ETIMEDOUT; +} + +static int +rtw_reset(struct rtw_softc *sc) +{ + struct rtw_regs *regs = &sc->sc_regs; + uint8_t config1; + int rc; + + sc->sc_flags &= ~RTW_F_DK_VALID; + + rc = rtw_chip_reset(sc); + if (rc) + return rc; + + rtw_recall_eeprom(sc); /* ignore err */ + + config1 = RTW_READ8(regs, RTW_CONFIG1); + RTW_WRITE8(regs, RTW_CONFIG1, config1 & ~RTW_CONFIG1_PMEN); + /* TBD turn off maximum power saving? */ + return 0; +} + +static int +rtw_srom_parse(struct rtw_softc *sc) +{ + struct rtw_srom *sr = &sc->sc_srom; + char scratch[sizeof("unknown 0xXX")]; + uint8_t mac[IEEE80211_ADDR_LEN]; + const char *rfname, *paname; + uint16_t srom_version; + int i; + + sc->sc_flags &= ~(RTW_F_DIGPHY | RTW_F_DFLANTB | RTW_F_ANTDIV); + sc->sc_rcr &= ~(RTW_RCR_ENCS1 | RTW_RCR_ENCS2); + + srom_version = RTW_SR_GET16(sr, RTW_SR_VERSION); + if_printf(&sc->sc_ic.ic_if, "SROM version %d.%d", + srom_version >> 8, srom_version & 0xff); + + if (srom_version <= 0x0101) { + printf(" is not understood, limping along with defaults\n"); + + /* Default values */ + sc->sc_flags |= (RTW_F_DIGPHY | RTW_F_ANTDIV); + sc->sc_csthr = RTW_SR_ENERGYDETTHR_DEFAULT; + sc->sc_rcr |= RTW_RCR_ENCS1; + sc->sc_rfchipid = RTW_RFCHIPID_PHILIPS; + return 0; + } + printf("\n"); + + for (i = 0; i < IEEE80211_ADDR_LEN; i++) + mac[i] = RTW_SR_GET(sr, RTW_SR_MAC + i); + + RTW_DPRINTF(RTW_DEBUG_ATTACH, + ("%s: EEPROM MAC %6D\n", sc->sc_ic.ic_if.if_xname, mac, ":")); + + sc->sc_csthr = RTW_SR_GET(sr, RTW_SR_ENERGYDETTHR); + + if ((RTW_SR_GET(sr, RTW_SR_CONFIG2) & RTW_CONFIG2_ANT) != 0) + sc->sc_flags |= RTW_F_ANTDIV; + + /* + * Note well: the sense of the RTW_SR_RFPARM_DIGPHY bit seems + * to be reversed. + */ + if ((RTW_SR_GET(sr, RTW_SR_RFPARM) & RTW_SR_RFPARM_DIGPHY) == 0) + sc->sc_flags |= RTW_F_DIGPHY; + if ((RTW_SR_GET(sr, RTW_SR_RFPARM) & RTW_SR_RFPARM_DFLANTB) != 0) + sc->sc_flags |= RTW_F_DFLANTB; + + sc->sc_rcr |= SHIFTIN(SHIFTOUT(RTW_SR_GET(sr, RTW_SR_RFPARM), + RTW_SR_RFPARM_CS_MASK), RTW_RCR_ENCS1); + + if ((RTW_SR_GET(sr, RTW_SR_CONFIG0) & RTW_CONFIG0_WEP104) != 0) + sc->sc_flags |= RTW_C_RXWEP_104; + + sc->sc_flags |= RTW_C_RXWEP_40; /* XXX */ + + sc->sc_rfchipid = RTW_SR_GET(sr, RTW_SR_RFCHIPID); + switch (sc->sc_rfchipid) { + case RTW_RFCHIPID_GCT: /* this combo seen in the wild */ + rfname = "GCT GRF5101"; + paname = "Winspring WS9901"; + break; + case RTW_RFCHIPID_MAXIM: + rfname = "Maxim MAX2820"; /* guess */ + paname = "Maxim MAX2422"; /* guess */ + break; + case RTW_RFCHIPID_INTERSIL: + rfname = "Intersil HFA3873"; /* guess */ + paname = "Intersil "; + break; + case RTW_RFCHIPID_PHILIPS: /* this combo seen in the wild */ + rfname = "Philips SA2400A"; + paname = "Philips SA2411"; + break; + case RTW_RFCHIPID_RFMD: + /* this is the same front-end as an atw(4)! */ + rfname = "RFMD RF2948B, " /* mentioned in Realtek docs */ + "LNA: RFMD RF2494, " /* mentioned in Realtek docs */ + "SYN: Silicon Labs Si4126"; /* inferred from + * reference driver + */ + paname = "RFMD RF2189"; /* mentioned in Realtek docs */ + break; + case RTW_RFCHIPID_RESERVED: + rfname = paname = "reserved"; + break; + default: + snprintf(scratch, sizeof(scratch), "unknown 0x%02x", + sc->sc_rfchipid); + rfname = paname = scratch; + } + if_printf(&sc->sc_ic.ic_if, "RF: %s, PA: %s\n", rfname, paname); + + switch (RTW_SR_GET(sr, RTW_SR_CONFIG0) & RTW_CONFIG0_GL_MASK) { + case RTW_CONFIG0_GL_USA: + case _RTW_CONFIG0_GL_USA: + sc->sc_locale = RTW_LOCALE_USA; + break; + case RTW_CONFIG0_GL_EUROPE: + sc->sc_locale = RTW_LOCALE_EUROPE; + break; + case RTW_CONFIG0_GL_JAPAN: + sc->sc_locale = RTW_LOCALE_JAPAN; + break; + default: + sc->sc_locale = RTW_LOCALE_UNKNOWN; + break; + } + return 0; +} + +static int +rtw_srom_read(struct rtw_softc *sc) +{ + struct rtw_regs *regs = &sc->sc_regs; + struct rtw_srom *sr = &sc->sc_srom; + struct seeprom_descriptor sd; + uint8_t ecr; + int rc; + + memset(&sd, 0, sizeof(sd)); + + ecr = RTW_READ8(regs, RTW_9346CR); + + if ((sc->sc_flags & RTW_F_9356SROM) != 0) { + RTW_DPRINTF(RTW_DEBUG_ATTACH, + ("%s: 93c56 SROM\n", sc->sc_ic.ic_if.if_xname)); + sr->sr_size = 256; + sd.sd_chip = C56_66; + } else { + RTW_DPRINTF(RTW_DEBUG_ATTACH, + ("%s: 93c46 SROM\n", sc->sc_ic.ic_if.if_xname)); + sr->sr_size = 128; + sd.sd_chip = C46; + } + + ecr &= ~(RTW_9346CR_EEDI | RTW_9346CR_EEDO | RTW_9346CR_EESK | + RTW_9346CR_EEM_MASK | RTW_9346CR_EECS); + ecr |= RTW_9346CR_EEM_PROGRAM; + + RTW_WRITE8(regs, RTW_9346CR, ecr); + + sr->sr_content = malloc(sr->sr_size, M_DEVBUF, M_WAITOK | M_ZERO); + + /* + * RTL8180 has a single 8-bit register for controlling the + * 93cx6 SROM. There is no "ready" bit. The RTL8180 + * input/output sense is the reverse of read_seeprom's. + */ + sd.sd_tag = regs->r_bt; + sd.sd_bsh = regs->r_bh; + sd.sd_regsize = 1; + sd.sd_control_offset = RTW_9346CR; + sd.sd_status_offset = RTW_9346CR; + sd.sd_dataout_offset = RTW_9346CR; + sd.sd_CK = RTW_9346CR_EESK; + sd.sd_CS = RTW_9346CR_EECS; + sd.sd_DI = RTW_9346CR_EEDO; + sd.sd_DO = RTW_9346CR_EEDI; + /* make read_seeprom enter EEPROM read/write mode */ + sd.sd_MS = ecr; + sd.sd_RDY = 0; + + /* TBD bus barriers */ + if (!read_seeprom(&sd, sr->sr_content, 0, sr->sr_size / 2)) { + if_printf(&sc->sc_ic.ic_if, "could not read SROM\n"); + free(sr->sr_content, M_DEVBUF); + sr->sr_content = NULL; + return EIO; /* XXX */ + } + + /* end EEPROM read/write mode */ + RTW_WRITE8(regs, RTW_9346CR, + (ecr & ~RTW_9346CR_EEM_MASK) | RTW_9346CR_EEM_NORMAL); + RTW_WBRW(regs, RTW_9346CR, RTW_9346CR); + + rc = rtw_recall_eeprom(sc); + if (rc) + return rc; + +#ifdef RTW_DEBUG + { + int i; + RTW_DPRINTF(RTW_DEBUG_ATTACH, + ("\n%s: serial ROM:\n\t", sc->sc_ic.ic_if.if_xname)); + for (i = 0; i < sr->sr_size/2; i++) { + if (((i % 8) == 0) && (i != 0)) + RTW_DPRINTF(RTW_DEBUG_ATTACH, ("\n\t")); + RTW_DPRINTF(RTW_DEBUG_ATTACH, + (" %04x", sr->sr_content[i])); + } + RTW_DPRINTF(RTW_DEBUG_ATTACH, ("\n")); + } +#endif /* RTW_DEBUG */ + return 0; +} + +static void +rtw_set_rfprog(struct rtw_softc *sc) +{ + struct rtw_regs *regs = &sc->sc_regs; + const char *method; + uint8_t cfg4; + + cfg4 = RTW_READ8(regs, RTW_CONFIG4) & ~RTW_CONFIG4_RFTYPE_MASK; + + switch (sc->sc_rfchipid) { + default: + cfg4 |= SHIFTIN(rtw_rfprog_fallback, RTW_CONFIG4_RFTYPE_MASK); + method = "fallback"; + break; + case RTW_RFCHIPID_INTERSIL: + cfg4 |= RTW_CONFIG4_RFTYPE_INTERSIL; + method = "Intersil"; + break; + case RTW_RFCHIPID_PHILIPS: + cfg4 |= RTW_CONFIG4_RFTYPE_PHILIPS; + method = "Philips"; + break; + case RTW_RFCHIPID_GCT: /* XXX a guess */ + case RTW_RFCHIPID_RFMD: + cfg4 |= RTW_CONFIG4_RFTYPE_RFMD; + method = "RFMD"; + break; + } + + RTW_WRITE8(regs, RTW_CONFIG4, cfg4); + + RTW_WBR(regs, RTW_CONFIG4, RTW_CONFIG4); + + RTW_DPRINTF(RTW_DEBUG_INIT, + ("%s: %s RF programming method, %#02x\n", + sc->sc_ic.ic_if.if_xname, method, + RTW_READ8(regs, RTW_CONFIG4))); +} + +static __inline void +rtw_init_channels(struct rtw_softc *sc) +{ + const char *name = NULL; + struct ieee80211_channel *chans = sc->sc_ic.ic_channels; + int i; +#define ADD_CHANNEL(_chans, _chan) do { \ + _chans[_chan].ic_flags = IEEE80211_CHAN_B; \ + _chans[_chan].ic_freq = \ + ieee80211_ieee2mhz(_chan, _chans[_chan].ic_flags); \ +} while (0) + + switch (sc->sc_locale) { + case RTW_LOCALE_USA: /* 1-11 */ + name = "USA"; + for (i = 1; i <= 11; i++) + ADD_CHANNEL(chans, i); + break; + case RTW_LOCALE_JAPAN: /* 1-14 */ + name = "Japan"; + ADD_CHANNEL(chans, 14); + for (i = 1; i <= 14; i++) + ADD_CHANNEL(chans, i); + break; + case RTW_LOCALE_EUROPE: /* 1-13 */ + name = "Europe"; + for (i = 1; i <= 13; i++) + ADD_CHANNEL(chans, i); + break; + default: /* 10-11 allowed by most countries */ + name = ""; + for (i = 10; i <= 11; i++) + ADD_CHANNEL(chans, i); + break; + } + if_printf(&sc->sc_ic.ic_if, "Geographic Location %s\n", name); +#undef ADD_CHANNEL +} + + +static void +rtw_identify_country(struct rtw_softc *sc) +{ + uint8_t cfg0; + + cfg0 = RTW_READ8(&sc->sc_regs, RTW_CONFIG0); + switch (cfg0 & RTW_CONFIG0_GL_MASK) { + case RTW_CONFIG0_GL_USA: + case _RTW_CONFIG0_GL_USA: + sc->sc_locale = RTW_LOCALE_USA; + break; + case RTW_CONFIG0_GL_JAPAN: + sc->sc_locale = RTW_LOCALE_JAPAN; + break; + case RTW_CONFIG0_GL_EUROPE: + sc->sc_locale = RTW_LOCALE_EUROPE; + break; + default: + sc->sc_locale = RTW_LOCALE_UNKNOWN; + break; + } +} + +static int +rtw_identify_sta(struct rtw_softc *sc) +{ + static const uint8_t empty_macaddr[IEEE80211_ADDR_LEN] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + struct rtw_regs *regs = &sc->sc_regs; + uint8_t *addr = sc->sc_ic.ic_myaddr; + uint32_t idr0, idr1; + + idr0 = RTW_READ(regs, RTW_IDR0); + idr1 = RTW_READ(regs, RTW_IDR1); + + addr[0] = SHIFTOUT(idr0, __BITS(0, 7)); + addr[1] = SHIFTOUT(idr0, __BITS(8, 15)); + addr[2] = SHIFTOUT(idr0, __BITS(16, 23)); + addr[3] = SHIFTOUT(idr0, __BITS(24 ,31)); + + addr[4] = SHIFTOUT(idr1, __BITS(0, 7)); + addr[5] = SHIFTOUT(idr1, __BITS(8, 15)); + + if (IEEE80211_ADDR_EQ(addr, empty_macaddr)) { + if_printf(&sc->sc_ic.ic_if, "could not get mac address\n"); + return ENXIO; + } + return 0; +} + +static uint8_t +rtw_chan2txpower(struct rtw_srom *sr, struct ieee80211com *ic, + struct ieee80211_channel *chan) +{ + u_int idx = RTW_SR_TXPOWER1 + ieee80211_chan2ieee(ic, chan) - 1; + + KASSERT(idx >= RTW_SR_TXPOWER1 && idx <= RTW_SR_TXPOWER14, + ("%s: channel %d out of range", __func__, + idx - RTW_SR_TXPOWER1 + 1)); + return RTW_SR_GET(sr, idx); +} + +static void +rtw_txdesc_blk_init_all(struct rtw_softc *sc) +{ + /* nfree: the number of free descriptors in each ring. + * The beacon ring is a special case: I do not let the + * driver use all of the descriptors on the beacon ring. + * The reasons are two-fold: + * + * (1) A BEACON descriptor's OWN bit is (apparently) not + * updated, so the driver cannot easily know if the descriptor + * belongs to it, or if it is racing the NIC. If the NIC + * does not OWN every descriptor, then the driver can safely + * update the descriptors when RTW_TBDA points at tdb_next. + * + * (2) I hope that the NIC will process more than one BEACON + * descriptor in a single beacon interval, since that will + * enable multiple-BSS support. Since the NIC does not + * clear the OWN bit, there is no natural place for it to + * stop processing BEACON desciptors. Maybe it will *not* + * stop processing them! I do not want to chance the NIC + * looping around and around a saturated beacon ring, so + * I will leave one descriptor unOWNed at all times. + */ + int nfree[RTW_NTXPRI] = { + RTW_NTXDESCLO, + RTW_NTXDESCMD, + RTW_NTXDESCHI, + RTW_NTXDESCBCN - 1 + }; + struct rtw_txdesc_blk *tdb; + int pri; + + for (tdb = sc->sc_txdesc_blk, pri = 0; pri < RTW_NTXPRI; tdb++, pri++) { + tdb->tdb_nfree = nfree[pri]; + tdb->tdb_next = 0; + + bus_dmamap_sync(tdb->tdb_dmat, tdb->tdb_dmamap, + BUS_DMASYNC_PREWRITE); + } +} + +static void +rtw_txsoft_blk_init_all(struct rtw_softc *sc) +{ + struct rtw_txsoft_blk *tsb; + int pri; + + for (tsb = sc->sc_txsoft_blk, pri = 0; pri < RTW_NTXPRI; tsb++, pri++) { + int i; + + STAILQ_INIT(&tsb->tsb_dirtyq); + STAILQ_INIT(&tsb->tsb_freeq); + for (i = 0; i < tsb->tsb_ndesc; i++) { + struct rtw_txsoft *ts; + + ts = &tsb->tsb_desc[i]; + ts->ts_mbuf = NULL; + STAILQ_INSERT_TAIL(&tsb->tsb_freeq, ts, ts_q); + } + tsb->tsb_tx_timer = 0; + } +} + +static void +rtw_rxbuf_dma_map(void *arg, bus_dma_segment_t *seg, int nseg, + bus_size_t mapsize, int error) +{ + if (error) + return; + + KASSERT(nseg == 1, ("too many rx mbuf seg\n")); + + *((bus_addr_t *)arg) = seg->ds_addr; +} + +static int +rtw_rxsoft_alloc(struct rtw_softc *sc, struct rtw_rxsoft *rs, int waitok) +{ + bus_addr_t paddr; + bus_dmamap_t map; + struct mbuf *m; + int rc; + + m = m_getcl(waitok ? MB_WAIT : MB_DONTWAIT, MT_DATA, M_PKTHDR); + if (m == NULL) + return ENOBUFS; + + m->m_pkthdr.len = m->m_len = MCLBYTES; + + rc = bus_dmamap_load_mbuf(sc->sc_rxsoft_dmat, sc->sc_rxsoft_dmamap, m, + rtw_rxbuf_dma_map, &paddr, + waitok ? BUS_DMA_NOWAIT : BUS_DMA_WAITOK); + if (rc) { + if_printf(&sc->sc_ic.ic_if, "can't load rx mbuf\n"); + m_freem(m); + return rc; + } + + if (rs->rs_mbuf != NULL) + bus_dmamap_unload(sc->sc_rxsoft_dmat, rs->rs_dmamap); + + /* Swap DMA map */ + map = rs->rs_dmamap; + rs->rs_dmamap = sc->sc_rxsoft_dmamap; + sc->sc_rxsoft_dmamap = map; + + rs->rs_mbuf = m; + rs->rs_phyaddr = paddr; + + bus_dmamap_sync(sc->sc_rxsoft_dmat, rs->rs_dmamap, BUS_DMASYNC_PREREAD); + return 0; +} + +static int +rtw_rxsoft_blk_init_all(struct rtw_softc *sc) +{ + int i, rc = 0; + + for (i = 0; i < RTW_RXQLEN; i++) { + struct rtw_rxsoft *rs; + + rs = &sc->sc_rxsoft[i]; + /* we're in rtw_init, so there should be no mbufs allocated */ + KKASSERT(rs->rs_mbuf == NULL); +#ifdef RTW_DEBUG + if (i == rtw_rxbufs_limit) { + if_printf(&sc->sc_ic.ic_if, + "TEST hit %d-buffer limit\n", i); + rc = ENOBUFS; + break; + } +#endif /* RTW_DEBUG */ + rc = rtw_rxsoft_alloc(sc, rs, 1); + if (rc) + break; + } + return rc; +} + +static void +rtw_rxdesc_init(struct rtw_softc *sc, int idx, int kick) +{ + struct rtw_rxdesc_blk *rdb = &sc->sc_rxdesc_blk; + struct rtw_rxdesc *rd = &rdb->rdb_desc[idx]; + struct rtw_rxsoft *rs = &sc->sc_rxsoft[idx]; + uint32_t ctl; + +#ifdef RTW_DEBUG + uint32_t octl, obuf; + + obuf = rd->rd_buf; + octl = rd->rd_ctl; +#endif /* RTW_DEBUG */ + + rd->rd_buf = htole32(rs->rs_phyaddr); + + ctl = SHIFTIN(rs->rs_mbuf->m_len, RTW_RXCTL_LENGTH_MASK) | + RTW_RXCTL_OWN | RTW_RXCTL_FS | RTW_RXCTL_LS; + + if (idx == rdb->rdb_ndesc - 1) + ctl |= RTW_RXCTL_EOR; + + rd->rd_ctl = htole32(ctl); + + RTW_DPRINTF(kick ? (RTW_DEBUG_RECV_DESC | RTW_DEBUG_IO_KICK) + : RTW_DEBUG_RECV_DESC, + ("%s: rd %p buf %08x -> %08x ctl %08x -> %08x\n", + sc->sc_ic.ic_if.if_xname, rd, le32toh(obuf), + le32toh(rd->rd_buf), le32toh(octl), le32toh(rd->rd_ctl))); +} + +static void +rtw_rxdesc_blk_init_all(struct rtw_softc *sc) +{ + struct rtw_rxdesc_blk *rdb = &sc->sc_rxdesc_blk; + int i; + + for (i = 0; i < rdb->rdb_ndesc; i++) + rtw_rxdesc_init(sc, i, 1); + + bus_dmamap_sync(rdb->rdb_dmat, rdb->rdb_dmamap, BUS_DMASYNC_PREWRITE); +} + +static void +rtw_io_enable(struct rtw_softc *sc, uint8_t flags, int enable) +{ + struct rtw_regs *regs = &sc->sc_regs; + uint8_t cr; + + RTW_DPRINTF(RTW_DEBUG_IOSTATE, + ("%s: %s 0x%02x\n", sc->sc_ic.ic_if.if_xname, + enable ? "enable" : "disable", flags)); + + cr = RTW_READ8(regs, RTW_CR); + + /* XXX reference source does not enable MULRW */ +#if 0 + /* enable PCI Read/Write Multiple */ + cr |= RTW_CR_MULRW; +#endif + + RTW_RBW(regs, RTW_CR, RTW_CR); /* XXX paranoia? */ + if (enable) + cr |= flags; + else + cr &= ~flags; + RTW_WRITE8(regs, RTW_CR, cr); + RTW_SYNC(regs, RTW_CR, RTW_CR); +} + +static void +rtw_intr_rx(struct rtw_softc *sc, uint16_t isr) +{ +#define IS_BEACON(__fc0) \ + ((__fc0 & (IEEE80211_FC0_TYPE_MASK | IEEE80211_FC0_SUBTYPE_MASK)) ==\ + (IEEE80211_FC0_TYPE_MGT | IEEE80211_FC0_SUBTYPE_BEACON)) + + /* + * convert rates: + * hardware -> net80211 + */ + static const int ratetbl[4] = { 2, 4, 11, 22 }; + struct ifnet *ifp = &sc->sc_if; + struct rtw_rxdesc_blk *rdb = &sc->sc_rxdesc_blk; + int next, nproc = 0, sync = 0; + + KKASSERT(rdb->rdb_next < rdb->rdb_ndesc); + + bus_dmamap_sync(rdb->rdb_dmat, rdb->rdb_dmamap, BUS_DMASYNC_POSTREAD); + + for (next = rdb->rdb_next; ; next = (next + 1) % rdb->rdb_ndesc) { + struct ieee80211_node *ni; + struct ieee80211_frame_min *wh; + struct rtw_rxdesc *rd; + struct rtw_rxsoft *rs; + struct mbuf *m; + int hwrate, len, rate, rssi, sq, error; + uint32_t hrssi, hstat, htsfth, htsftl; + + rd = &rdb->rdb_desc[next]; + rs = &sc->sc_rxsoft[next]; + + hstat = le32toh(rd->rd_stat); + hrssi = le32toh(rd->rd_rssi); + htsfth = le32toh(rd->rd_tsfth); + htsftl = le32toh(rd->rd_tsftl); + + RTW_DPRINTF(RTW_DEBUG_RECV_DESC, + ("%s: rxdesc[%d] hstat %08x hrssi %08x " + "htsft %08x%08x\n", ifp->if_xname, + next, hstat, hrssi, htsfth, htsftl)); + + ++nproc; + + /* still belongs to NIC */ + if (hstat & RTW_RXSTAT_OWN) { + if (nproc > 1) + break; + + /* sometimes the NIC skips to the 0th descriptor */ + rd = &rdb->rdb_desc[0]; + if (rd->rd_stat & htole32(RTW_RXSTAT_OWN)) + break; + RTW_DPRINTF(RTW_DEBUG_BUGS, + ("%s: NIC skipped from rxdesc[%u] " + "to rxdesc[0]\n", ifp->if_xname, next)); + next = rdb->rdb_ndesc - 1; + continue; + } + +#ifdef RTW_DEBUG +#define PRINTSTAT(flag) do { \ + if ((hstat & flag) != 0) { \ + printf("%s" #flag, delim); \ + delim = ","; \ + } \ +} while (0) + if (rtw_debug & RTW_DEBUG_RECV_DESC) { + const char *delim = "<"; + + if_printf(ifp, "%s", ""); + if ((hstat & RTW_RXSTAT_DEBUG) != 0) { + printf("status %08x", hstat); + PRINTSTAT(RTW_RXSTAT_SPLCP); + PRINTSTAT(RTW_RXSTAT_MAR); + PRINTSTAT(RTW_RXSTAT_PAR); + PRINTSTAT(RTW_RXSTAT_BAR); + PRINTSTAT(RTW_RXSTAT_PWRMGT); + PRINTSTAT(RTW_RXSTAT_CRC32); + PRINTSTAT(RTW_RXSTAT_ICV); + printf(">, "); + } + } +#endif /* RTW_DEBUG */ + + if (hstat & RTW_RXSTAT_IOERROR) { + if_printf(ifp, "DMA error/FIFO overflow %08x, " + "rx descriptor %d\n", + hstat & RTW_RXSTAT_IOERROR, next); + ifp->if_ierrors++; + goto next; + } + + len = SHIFTOUT(hstat, RTW_RXSTAT_LENGTH_MASK); + if (len < IEEE80211_MIN_LEN) { + sc->sc_ic.ic_stats.is_rx_tooshort++; + goto next; + } + + /* CRC is included with the packet; trim it off. */ + len -= IEEE80211_CRC_LEN; + + hwrate = SHIFTOUT(hstat, RTW_RXSTAT_RATE_MASK); + if (hwrate >= sizeof(ratetbl) / sizeof(ratetbl[0])) { + if_printf(ifp, "unknown rate #%d\n", + SHIFTOUT(hstat, RTW_RXSTAT_RATE_MASK)); + ifp->if_ierrors++; + goto next; + } + rate = ratetbl[hwrate]; + +#ifdef RTW_DEBUG + RTW_DPRINTF(RTW_DEBUG_RECV_DESC, + ("%s rate %d.%d Mb/s, time %08x%08x\n", + ifp->if_xname, (rate * 5) / 10, + (rate * 5) % 10, htsfth, htsftl)); +#endif /* RTW_DEBUG */ + + if ((hstat & RTW_RXSTAT_RES) && + sc->sc_ic.ic_opmode != IEEE80211_M_MONITOR) + goto next; + + /* if bad flags, skip descriptor */ + if ((hstat & RTW_RXSTAT_ONESEG) != RTW_RXSTAT_ONESEG) { + if_printf(ifp, "too many rx segments\n"); + goto next; + } + + bus_dmamap_sync(sc->sc_rxsoft_dmat, rs->rs_dmamap, + BUS_DMASYNC_POSTREAD); + + m = rs->rs_mbuf; + + /* if temporarily out of memory, re-use mbuf */ + error = rtw_rxsoft_alloc(sc, rs, 0); + if (error) { + if_printf(ifp, "%s: rtw_rxsoft_alloc(, %d) failed, " + "dropping packet\n", ifp->if_xname, next); + goto next; + } + + if (sc->sc_rfchipid == RTW_RFCHIPID_PHILIPS) { + rssi = SHIFTOUT(hrssi, RTW_RXRSSI_RSSI); + } else { + rssi = SHIFTOUT(hrssi, RTW_RXRSSI_IMR_RSSI); + /* TBD find out each front-end's LNA gain in the + * front-end's units + */ + if ((hrssi & RTW_RXRSSI_IMR_LNA) == 0) + rssi |= 0x80; + } + sq = SHIFTOUT(hrssi, RTW_RXRSSI_SQ); + + /* + * Note well: now we cannot recycle the rs_mbuf unless + * we restore its original length. + */ + m->m_pkthdr.rcvif = ifp; + m->m_pkthdr.len = m->m_len = len; + + wh = mtod(m, struct ieee80211_frame_min *); + + if (!IS_BEACON(wh->i_fc[0])) + sc->sc_led_state.ls_event |= RTW_LED_S_RX; + + /* TBD use _MAR, _BAR, _PAR flags as hints to _find_rxnode? */ + ni = ieee80211_find_rxnode(&sc->sc_ic, wh); + + sc->sc_tsfth = htsfth; + +#ifdef RTW_DEBUG + if ((ifp->if_flags & (IFF_DEBUG | IFF_LINK2)) == + (IFF_DEBUG | IFF_LINK2)) { + ieee80211_dump_pkt(mtod(m, uint8_t *), m->m_pkthdr.len, + rate, rssi); + } +#endif /* RTW_DEBUG */ + + if (sc->sc_radiobpf != NULL) { + struct rtw_rx_radiotap_header *rr = &sc->sc_rxtap; + + rr->rr_tsft = + htole64(((uint64_t)htsfth << 32) | htsftl); + + if ((hstat & RTW_RXSTAT_SPLCP) != 0) + rr->rr_flags = IEEE80211_RADIOTAP_F_SHORTPRE; + + rr->rr_flags = 0; + rr->rr_rate = rate; + rr->rr_antsignal = rssi; + rr->rr_barker_lock = htole16(sq); + + bpf_ptap(sc->sc_radiobpf, m, rr, sizeof(sc->sc_rxtapu)); + } + + ieee80211_input(&sc->sc_ic, m, ni, rssi, htsftl); + ieee80211_free_node(ni); +next: + rtw_rxdesc_init(sc, next, 0); + sync = 1; + } + + if (sync) { + bus_dmamap_sync(rdb->rdb_dmat, rdb->rdb_dmamap, + BUS_DMASYNC_PREWRITE); + } + + rdb->rdb_next = next; + KKASSERT(rdb->rdb_next < rdb->rdb_ndesc); +#undef IS_BEACON +} + +static __inline void +rtw_txsoft_release(bus_dma_tag_t dmat, struct rtw_txsoft *ts, + int data_retry, int rts_retry, int error, int ratectl) +{ + struct mbuf *m; + struct ieee80211_node *ni; + + if (!ts->ts_ratectl) + ratectl = 0; + + m = ts->ts_mbuf; + ni = ts->ts_ni; + KKASSERT(m != NULL); + KKASSERT(ni != NULL); + ts->ts_mbuf = NULL; + ts->ts_ni = NULL; + + if (ratectl) { + struct ieee80211_ratectl_res rc_res; + + rc_res.rc_res_rateidx = ts->ts_rateidx; + rc_res.rc_res_tries = data_retry + rts_retry + 1; + + ieee80211_ratectl_tx_complete(ni, m->m_pkthdr.len, + &rc_res, 1, + data_retry, rts_retry, + error); + } + + bus_dmamap_sync(dmat, ts->ts_dmamap, BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(dmat, ts->ts_dmamap); + m_freem(m); + ieee80211_free_node(ni); +} + +static __inline void +rtw_collect_txpkt(struct rtw_softc *sc, struct rtw_txdesc_blk *tdb, + struct rtw_txsoft *ts, int ndesc) +{ + uint32_t hstat; + int data_retry, rts_retry, error; + struct rtw_txdesc *tdn; + const char *condstring; + struct ifnet *ifp = &sc->sc_if; + + tdb->tdb_nfree += ndesc; + + tdn = &tdb->tdb_desc[ts->ts_last]; + + hstat = le32toh(tdn->td_stat); + rts_retry = SHIFTOUT(hstat, RTW_TXSTAT_RTSRETRY_MASK); + data_retry = SHIFTOUT(hstat, RTW_TXSTAT_DRC_MASK); + + ifp->if_collisions += rts_retry + data_retry; + + if ((hstat & RTW_TXSTAT_TOK) != 0) { + condstring = "ok"; + error = 0; + } else { + ifp->if_oerrors++; + condstring = "error"; + error = 1; + } + + rtw_txsoft_release(sc->sc_txsoft_dmat, ts, data_retry, rts_retry, + error, 1); + + DPRINTF(sc, RTW_DEBUG_XMIT_DESC, + ("%s: ts %p txdesc[%d, %d] %s tries rts %u data %u\n", + ifp->if_xname, ts, ts->ts_first, ts->ts_last, + condstring, rts_retry, data_retry)); +} + +static void +rtw_reset_oactive(struct rtw_softc *sc) +{ + int pri; +#ifdef RTW_DEBUG + short oflags = sc->sc_if.if_flags; +#endif + + for (pri = 0; pri < RTW_NTXPRI; pri++) { + struct rtw_txsoft_blk *tsb = &sc->sc_txsoft_blk[pri]; + struct rtw_txdesc_blk *tdb = &sc->sc_txdesc_blk[pri]; + + if (!STAILQ_EMPTY(&tsb->tsb_freeq) && tdb->tdb_nfree > 0) + sc->sc_if.if_flags &= ~IFF_OACTIVE; + } + +#ifdef RTW_DEBUG + if (oflags != sc->sc_if.if_flags) { + DPRINTF(sc, RTW_DEBUG_OACTIVE, + ("%s: reset OACTIVE\n", sc->sc_ic.ic_if.if_xname)); + } +#endif +} + +/* Collect transmitted packets. */ +static __inline void +rtw_collect_txring(struct rtw_softc *sc, struct rtw_txsoft_blk *tsb, + struct rtw_txdesc_blk *tdb, int force) +{ + struct rtw_txsoft *ts; + int ndesc; + + while ((ts = STAILQ_FIRST(&tsb->tsb_dirtyq)) != NULL) { + ndesc = 1 + ts->ts_last - ts->ts_first; + if (ts->ts_last < ts->ts_first) + ndesc += tdb->tdb_ndesc; + + KKASSERT(ndesc > 0); + + bus_dmamap_sync(tdb->tdb_dmat, tdb->tdb_dmamap, + BUS_DMASYNC_POSTREAD); + + if (force) { + int i; + + for (i = ts->ts_first; ; i = RTW_NEXT_IDX(tdb, i)) { + tdb->tdb_desc[i].td_stat &= + ~htole32(RTW_TXSTAT_OWN); + if (i == ts->ts_last) + break; + } + bus_dmamap_sync(tdb->tdb_dmat, tdb->tdb_dmamap, + BUS_DMASYNC_PREWRITE); + } else if ((tdb->tdb_desc[ts->ts_last].td_stat & + htole32(RTW_TXSTAT_OWN)) != 0) { + break; + } + + rtw_collect_txpkt(sc, tdb, ts, ndesc); + STAILQ_REMOVE_HEAD(&tsb->tsb_dirtyq, ts_q); + STAILQ_INSERT_TAIL(&tsb->tsb_freeq, ts, ts_q); + } + /* no more pending transmissions, cancel watchdog */ + if (ts == NULL) + tsb->tsb_tx_timer = 0; + rtw_reset_oactive(sc); +} + +static void +rtw_intr_tx(struct rtw_softc *sc, uint16_t isr) +{ + int pri; + + for (pri = 0; pri < RTW_NTXPRI; pri++) { + rtw_collect_txring(sc, &sc->sc_txsoft_blk[pri], + &sc->sc_txdesc_blk[pri], 0); + } + if (isr) + rtw_start(&sc->sc_ic.ic_if); +} + +static __inline struct mbuf * +rtw_beacon_alloc(struct rtw_softc *sc, struct ieee80211_node *ni) +{ + struct ieee80211com *ic = &sc->sc_ic; + struct ieee80211_beacon_offsets boff; + struct mbuf *m; + + m = ieee80211_beacon_alloc(ic, ni, &boff); + if (m != NULL) { + RTW_DPRINTF(RTW_DEBUG_BEACON, + ("%s: m %p len %u\n", ic->ic_if.if_xname, m, + m->m_len)); + } + return m; +} + +static void +rtw_intr_beacon(struct rtw_softc *sc, uint16_t isr) +{ + struct ieee80211com *ic = &sc->sc_ic; + struct rtw_regs *regs = &sc->sc_regs; + struct rtw_txdesc_blk *tdb = &sc->sc_txdesc_blk[RTW_TXPRIBCN]; + struct rtw_txsoft_blk *tsb = &sc->sc_txsoft_blk[RTW_TXPRIBCN]; + +#ifdef RTW_DEBUG + uint32_t tsfth, tsftl; + + tsfth = RTW_READ(regs, RTW_TSFTRH); + tsftl = RTW_READ(regs, RTW_TSFTRL); +#endif + + if (isr & (RTW_INTR_TBDOK | RTW_INTR_TBDER)) { +#ifdef RTW_DEBUG + int next = rtw_txring_next(regs, tdb); +#endif + + RTW_DPRINTF(RTW_DEBUG_BEACON, + ("%s: beacon ring %sprocessed, " + "isr = %#04x, next %d expected %d, %llu\n", + ic->ic_if.if_xname, + (next == tdb->tdb_next) ? "" : "un", + isr, next, tdb->tdb_next, + (uint64_t)tsfth << 32 | tsftl)); + + if ((RTW_READ8(regs, RTW_TPPOLL) & RTW_TPPOLL_BQ) == 0){ + rtw_collect_txring(sc, tsb, tdb, 1); + tdb->tdb_next = 0; + } + } + /* Start beacon transmission. */ + + if ((isr & RTW_INTR_BCNINT) && ic->ic_state == IEEE80211_S_RUN && + STAILQ_EMPTY(&tsb->tsb_dirtyq)) { + struct mbuf *m; + + RTW_DPRINTF(RTW_DEBUG_BEACON, + ("%s: beacon prep. time, isr = %#04x, %llu\n", + ic->ic_if.if_xname, isr, + (uint64_t)tsfth << 32 | tsftl)); + + m = rtw_beacon_alloc(sc, ic->ic_bss); + if (m == NULL) { + if_printf(&ic->ic_if, "could not allocate beacon\n"); + return; + } + + m->m_pkthdr.rcvif = (void *)ieee80211_ref_node(ic->ic_bss); + + IF_ENQUEUE(&sc->sc_beaconq, m); + + rtw_start(&ic->ic_if); + } +} + +static void +rtw_intr_atim(struct rtw_softc *sc) +{ + /* TBD */ + return; +} + +#ifdef RTW_DEBUG +static void +rtw_dump_rings(struct rtw_softc *sc) +{ + struct rtw_rxdesc_blk *rdb; + int desc, pri; + + if ((rtw_debug & RTW_DEBUG_IO_KICK) == 0) + return; + + for (pri = 0; pri < RTW_NTXPRI; pri++) { + struct rtw_txdesc_blk *tdb = &sc->sc_txdesc_blk[pri]; + + if_printf(&sc->sc_ic.ic_if, "txpri %d ndesc %d nfree %d\n", + pri, tdb->tdb_ndesc, tdb->tdb_nfree); + for (desc = 0; desc < tdb->tdb_ndesc; desc++) + rtw_print_txdesc(sc, ".", NULL, tdb, desc); + } + + rdb = &sc->sc_rxdesc_blk; + + for (desc = 0; desc < RTW_RXQLEN; desc++) { + struct rtw_rxdesc *rd = &rdb->rdb_desc[desc]; + + if_printf(&sc->sc_ic.ic_if, + "%sctl %08x rsvd0/rssi %08x buf/tsftl %08x " + "rsvd1/tsfth %08x\n", + (desc >= rdb->rdb_ndesc) ? "UNUSED " : "", + le32toh(rd->rd_ctl), le32toh(rd->rd_rssi), + le32toh(rd->rd_buf), le32toh(rd->rd_tsfth)); + } +} +#endif /* RTW_DEBUG */ + +static void +rtw_hwring_setup(struct rtw_softc *sc) +{ + struct rtw_regs *regs = &sc->sc_regs; + struct rtw_rxdesc_blk *rdb = &sc->sc_rxdesc_blk; + int pri; + + for (pri = 0; pri < RTW_NTXPRI; pri++) { + struct rtw_txdesc_blk *tdb = &sc->sc_txdesc_blk[pri]; + + RTW_WRITE(regs, tdb->tdb_basereg, tdb->tdb_base); + RTW_DPRINTF(RTW_DEBUG_XMIT_DESC, + ("%s: reg[tdb->tdb_basereg] <- %u\n", + sc->sc_ic.ic_if.if_xname, tdb->tdb_base)); + } + + RTW_WRITE(regs, RTW_RDSAR, rdb->rdb_base); + RTW_DPRINTF(RTW_DEBUG_RECV_DESC, + ("%s: reg[RDSAR] <- %u\n", sc->sc_ic.ic_if.if_xname, + rdb->rdb_base)); + + RTW_SYNC(regs, RTW_TLPDA, RTW_RDSAR); +} + +static int +rtw_swring_setup(struct rtw_softc *sc) +{ + int rc; + + rtw_txdesc_blk_init_all(sc); + rtw_txsoft_blk_init_all(sc); + + rc = rtw_rxsoft_blk_init_all(sc); + if (rc) { + if_printf(&sc->sc_ic.ic_if, "could not allocate rx buffers\n"); + return rc; + } + + rtw_rxdesc_blk_init_all(sc); + sc->sc_rxdesc_blk.rdb_next = 0; + return 0; +} + +static int +rtw_txring_next(struct rtw_regs *regs, struct rtw_txdesc_blk *tdb) +{ + return (le32toh(RTW_READ(regs, tdb->tdb_basereg)) - tdb->tdb_base) / + sizeof(struct rtw_txdesc); +} + +static void +rtw_txring_fixup(struct rtw_softc *sc) +{ + struct rtw_regs *regs = &sc->sc_regs; + int pri; + + for (pri = 0; pri < RTW_NTXPRI; pri++) { + struct rtw_txdesc_blk *tdb = &sc->sc_txdesc_blk[pri]; + int next; + + next = rtw_txring_next(regs, tdb); + if (tdb->tdb_next == next) + continue; + if_printf(&sc->sc_ic.ic_if, + "tx-ring %d expected next %d, read %d\n", + pri, tdb->tdb_next, next); + tdb->tdb_next = MIN(next, tdb->tdb_ndesc - 1); + } +} + +static void +rtw_rxring_fixup(struct rtw_softc *sc) +{ + struct rtw_rxdesc_blk *rdb = &sc->sc_rxdesc_blk; + uint32_t rdsar; + int next; + + rdsar = le32toh(RTW_READ(&sc->sc_regs, RTW_RDSAR)); + next = (rdsar - rdb->rdb_base) / sizeof(struct rtw_rxdesc); + + if (rdb->rdb_next != next) { + if_printf(&sc->sc_ic.ic_if, + "rx-ring expected next %d, read %d\n", + rdb->rdb_next, next); + rdb->rdb_next = MIN(next, rdb->rdb_ndesc - 1); + } +} + +static void +rtw_txdesc_blk_reset_all(struct rtw_softc *sc) +{ + int pri; + + for (pri = 0; pri < RTW_NTXPRI; pri++) { + rtw_collect_txring(sc, &sc->sc_txsoft_blk[pri], + &sc->sc_txdesc_blk[pri], 1); + } +} + +static void +rtw_intr_ioerror(struct rtw_softc *sc, uint16_t isr) +{ + struct rtw_regs *regs = &sc->sc_regs; + int xmtr = 0, rcvr = 0; + uint8_t cr = 0; + + if (isr & RTW_INTR_TXFOVW) { + if_printf(&sc->sc_ic.ic_if, "tx fifo underflow\n"); + rcvr = xmtr = 1; + cr |= RTW_CR_TE | RTW_CR_RE; + } + + if (isr & (RTW_INTR_RDU | RTW_INTR_RXFOVW)) { + cr |= RTW_CR_RE; + rcvr = 1; + } + + RTW_DPRINTF(RTW_DEBUG_BUGS, + ("%s: restarting xmit/recv, isr %04x\n", + sc->sc_ic.ic_if.if_xname, isr)); + +#ifdef RTW_DEBUG + rtw_dump_rings(sc); +#endif /* RTW_DEBUG */ + + rtw_io_enable(sc, cr, 0); + + /* Collect rx'd packets. Refresh rx buffers. */ + if (rcvr) + rtw_intr_rx(sc, 0); + + /* + * Collect tx'd packets. + * XXX let's hope this stops the transmit timeouts. + */ + if (xmtr) + rtw_txdesc_blk_reset_all(sc); + + RTW_WRITE16(regs, RTW_IMR, 0); + RTW_SYNC(regs, RTW_IMR, RTW_IMR); + + if (rtw_do_chip_reset) { + rtw_chip_reset1(sc); + rtw_wep_setkeys(sc); + } + + rtw_rxdesc_blk_init_all(sc); + +#ifdef RTW_DEBUG + rtw_dump_rings(sc); +#endif /* RTW_DEBUG */ + + RTW_WRITE16(regs, RTW_IMR, sc->sc_inten); + RTW_SYNC(regs, RTW_IMR, RTW_IMR); + + if (rcvr) + rtw_rxring_fixup(sc); + + rtw_io_enable(sc, cr, 1); + + if (xmtr) + rtw_txring_fixup(sc); +} + +static __inline void +rtw_suspend_ticks(struct rtw_softc *sc) +{ + RTW_DPRINTF(RTW_DEBUG_TIMEOUT, + ("%s: suspending ticks\n", sc->sc_ic.ic_if.if_xname)); + sc->sc_do_tick = 0; +} + +static void +rtw_resume_ticks(struct rtw_softc *sc) +{ + uint32_t tsftrl0, tsftrl1, next_tick; + struct rtw_regs *regs = &sc->sc_regs; + + tsftrl0 = RTW_READ(regs, RTW_TSFTRL); + + tsftrl1 = RTW_READ(regs, RTW_TSFTRL); + next_tick = tsftrl1 + 1000000; + RTW_WRITE(regs, RTW_TINT, next_tick); + + sc->sc_do_tick = 1; + + RTW_DPRINTF(RTW_DEBUG_TIMEOUT, + ("%s: resume ticks delta %#08x now %#08x next %#08x\n", + sc->sc_ic.ic_if.if_xname, tsftrl1 - tsftrl0, tsftrl1, + next_tick)); +} + +static void +rtw_intr_timeout(struct rtw_softc *sc) +{ + RTW_DPRINTF(RTW_DEBUG_TIMEOUT, + ("%s: timeout\n", sc->sc_ic.ic_if.if_xname)); + if (sc->sc_do_tick) + rtw_resume_ticks(sc); +} + +static void +rtw_intr(void *arg) +{ + struct rtw_softc *sc = arg; + struct rtw_regs *regs = &sc->sc_regs; + struct ifnet *ifp = &sc->sc_if; + int i; + + /* + * If the interface isn't running, the interrupt couldn't + * possibly have come from us. + */ + if ((sc->sc_flags & RTW_F_ENABLED) == 0 || + (ifp->if_flags & IFF_RUNNING) == 0) { + RTW_DPRINTF(RTW_DEBUG_INTR, + ("%s: stray interrupt\n", ifp->if_xname)); + return; + } + + for (i = 0; i < 10; i++) { + uint16_t isr; + + isr = RTW_READ16(regs, RTW_ISR); + + RTW_WRITE16(regs, RTW_ISR, isr); + RTW_WBR(regs, RTW_ISR, RTW_ISR); + + if (sc->sc_intr_ack != NULL) + sc->sc_intr_ack(regs); + + if (isr == 0) + break; + +#ifdef RTW_DEBUG +#define PRINTINTR(flag) do { \ + if ((isr & flag) != 0) { \ + printf("%s" #flag, delim); \ + delim = ","; \ + } \ +} while (0) + + if ((rtw_debug & RTW_DEBUG_INTR) != 0 && isr != 0) { + const char *delim = "<"; + + if_printf(ifp, "reg[ISR] = %x", isr); + + PRINTINTR(RTW_INTR_TXFOVW); + PRINTINTR(RTW_INTR_TIMEOUT); + PRINTINTR(RTW_INTR_BCNINT); + PRINTINTR(RTW_INTR_ATIMINT); + PRINTINTR(RTW_INTR_TBDER); + PRINTINTR(RTW_INTR_TBDOK); + PRINTINTR(RTW_INTR_THPDER); + PRINTINTR(RTW_INTR_THPDOK); + PRINTINTR(RTW_INTR_TNPDER); + PRINTINTR(RTW_INTR_TNPDOK); + PRINTINTR(RTW_INTR_RXFOVW); + PRINTINTR(RTW_INTR_RDU); + PRINTINTR(RTW_INTR_TLPDER); + PRINTINTR(RTW_INTR_TLPDOK); + PRINTINTR(RTW_INTR_RER); + PRINTINTR(RTW_INTR_ROK); + + printf(">\n"); + } +#undef PRINTINTR +#endif /* RTW_DEBUG */ + + if (isr & RTW_INTR_RX) + rtw_intr_rx(sc, isr & RTW_INTR_RX); + if (isr & RTW_INTR_TX) + rtw_intr_tx(sc, isr & RTW_INTR_TX); + if (isr & RTW_INTR_BEACON) + rtw_intr_beacon(sc, isr & RTW_INTR_BEACON); + if (isr & RTW_INTR_ATIMINT) + rtw_intr_atim(sc); + if (isr & RTW_INTR_IOERROR) + rtw_intr_ioerror(sc, isr & RTW_INTR_IOERROR); + if (isr & RTW_INTR_TIMEOUT) + rtw_intr_timeout(sc); + } +} + +/* Must be called at splnet. */ +void +rtw_stop(struct rtw_softc *sc, int disable) +{ + struct ieee80211com *ic = &sc->sc_ic; + struct ifnet *ifp = &ic->ic_if; + struct rtw_regs *regs = &sc->sc_regs; + int i; + + if ((sc->sc_flags & RTW_F_ENABLED) == 0) + return; + + rtw_suspend_ticks(sc); + + ieee80211_new_state(ic, IEEE80211_S_INIT, -1); + + if ((sc->sc_flags & RTW_F_INVALID) == 0) { + /* Disable interrupts. */ + RTW_WRITE16(regs, RTW_IMR, 0); + + RTW_WBW(regs, RTW_TPPOLL, RTW_IMR); + + /* + * Stop the transmit and receive processes. First stop DMA, + * then disable receiver and transmitter. + */ + RTW_WRITE8(regs, RTW_TPPOLL, RTW_TPPOLL_SALL); + + RTW_SYNC(regs, RTW_TPPOLL, RTW_IMR); + + rtw_io_enable(sc, RTW_CR_RE | RTW_CR_TE, 0); + } + + /* Free pending TX mbufs */ + for (i = 0; i < RTW_NTXPRI; ++i) { + struct rtw_txsoft_blk *tsb = &sc->sc_txsoft_blk[i]; + struct rtw_txsoft *ts; + + while ((ts = STAILQ_FIRST(&tsb->tsb_dirtyq)) != NULL) { + rtw_txsoft_release(sc->sc_txsoft_dmat, ts, 0, 0, 0, 0); + STAILQ_REMOVE_HEAD(&tsb->tsb_dirtyq, ts_q); + STAILQ_INSERT_TAIL(&tsb->tsb_freeq, ts, ts_q); + } + tsb->tsb_tx_timer = 0; + } + + /* Free pending RX mbufs */ + for (i = 0; i < RTW_RXQLEN; i++) { + struct rtw_rxsoft *rs = &sc->sc_rxsoft[i]; + + if (rs->rs_mbuf != NULL) { + bus_dmamap_sync(sc->sc_rxsoft_dmat, rs->rs_dmamap, + BUS_DMASYNC_POSTREAD); + bus_dmamap_unload(sc->sc_rxsoft_dmat, rs->rs_dmamap); + m_freem(rs->rs_mbuf); + rs->rs_mbuf = NULL; + } + } + + if (disable) + rtw_disable(sc); + + /* Mark the interface as not running. Cancel the watchdog timer. */ + ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE); + ifp->if_timer = 0; +} + +#ifdef RTW_DEBUG +const char * +rtw_pwrstate_string(enum rtw_pwrstate power) +{ + switch (power) { + case RTW_ON: + return "on"; + case RTW_SLEEP: + return "sleep"; + case RTW_OFF: + return "off"; + default: + return "unknown"; + } +} +#endif /* RTW_DEBUG */ + +/* + * XXX For Maxim, I am using the RFMD settings gleaned from the + * reference driver, plus a magic Maxim "ON" value that comes from + * the Realtek document "Windows PG for Rtl8180." + */ +static void +rtw_maxim_pwrstate(struct rtw_regs *regs, enum rtw_pwrstate power, + int before_rf, int digphy) +{ + uint32_t anaparm; + + anaparm = RTW_READ(regs, RTW_ANAPARM); + anaparm &= ~(RTW_ANAPARM_RFPOW_MASK | RTW_ANAPARM_TXDACOFF); + + switch (power) { + case RTW_OFF: + if (before_rf) + return; + anaparm |= RTW_ANAPARM_RFPOW_MAXIM_OFF; + anaparm |= RTW_ANAPARM_TXDACOFF; + break; + case RTW_SLEEP: + if (!before_rf) + return; + anaparm |= RTW_ANAPARM_RFPOW_MAXIM_SLEEP; + anaparm |= RTW_ANAPARM_TXDACOFF; + break; + case RTW_ON: + if (!before_rf) + return; + anaparm |= RTW_ANAPARM_RFPOW_MAXIM_ON; + break; + } + RTW_DPRINTF(RTW_DEBUG_PWR, + ("%s: power state %s, %s RF, reg[ANAPARM] <- %08x\n", + __func__, rtw_pwrstate_string(power), + (before_rf) ? "before" : "after", anaparm)); + + RTW_WRITE(regs, RTW_ANAPARM, anaparm); + RTW_SYNC(regs, RTW_ANAPARM, RTW_ANAPARM); +} + +/* XXX I am using the RFMD settings gleaned from the reference + * driver. They agree + */ +static void +rtw_rfmd_pwrstate(struct rtw_regs *regs, enum rtw_pwrstate power, + int before_rf, int digphy) +{ + uint32_t anaparm; + + anaparm = RTW_READ(regs, RTW_ANAPARM); + anaparm &= ~(RTW_ANAPARM_RFPOW_MASK | RTW_ANAPARM_TXDACOFF); + + switch (power) { + case RTW_OFF: + if (before_rf) + return; + anaparm |= RTW_ANAPARM_RFPOW_RFMD_OFF; + anaparm |= RTW_ANAPARM_TXDACOFF; + break; + case RTW_SLEEP: + if (!before_rf) + return; + anaparm |= RTW_ANAPARM_RFPOW_RFMD_SLEEP; + anaparm |= RTW_ANAPARM_TXDACOFF; + break; + case RTW_ON: + if (!before_rf) + return; + anaparm |= RTW_ANAPARM_RFPOW_RFMD_ON; + break; + } + RTW_DPRINTF(RTW_DEBUG_PWR, + ("%s: power state %s, %s RF, reg[ANAPARM] <- %08x\n", + __func__, rtw_pwrstate_string(power), + (before_rf) ? "before" : "after", anaparm)); + + RTW_WRITE(regs, RTW_ANAPARM, anaparm); + RTW_SYNC(regs, RTW_ANAPARM, RTW_ANAPARM); +} + +static void +rtw_philips_pwrstate(struct rtw_regs *regs, enum rtw_pwrstate power, + int before_rf, int digphy) +{ + uint32_t anaparm; + + anaparm = RTW_READ(regs, RTW_ANAPARM); + anaparm &= ~(RTW_ANAPARM_RFPOW_MASK | RTW_ANAPARM_TXDACOFF); + + switch (power) { + case RTW_OFF: + if (before_rf) + return; + anaparm |= RTW_ANAPARM_RFPOW_PHILIPS_OFF; + anaparm |= RTW_ANAPARM_TXDACOFF; + break; + case RTW_SLEEP: + if (!before_rf) + return; + anaparm |= RTW_ANAPARM_RFPOW_PHILIPS_SLEEP; + anaparm |= RTW_ANAPARM_TXDACOFF; + break; + case RTW_ON: + if (!before_rf) + return; + if (digphy) { + anaparm |= RTW_ANAPARM_RFPOW_DIG_PHILIPS_ON; + /* XXX guess */ + anaparm |= RTW_ANAPARM_TXDACOFF; + } else + anaparm |= RTW_ANAPARM_RFPOW_ANA_PHILIPS_ON; + break; + } + RTW_DPRINTF(RTW_DEBUG_PWR, + ("%s: power state %s, %s RF, reg[ANAPARM] <- %08x\n", + __func__, rtw_pwrstate_string(power), + (before_rf) ? "before" : "after", anaparm)); + + RTW_WRITE(regs, RTW_ANAPARM, anaparm); + RTW_SYNC(regs, RTW_ANAPARM, RTW_ANAPARM); +} + +static __inline void +rtw_pwrstate0(struct rtw_softc *sc, enum rtw_pwrstate power, int before_rf, + int digphy) +{ + rtw_set_access(sc, RTW_ACCESS_ANAPARM); + sc->sc_pwrstate_cb(&sc->sc_regs, power, before_rf, digphy); + rtw_set_access(sc, RTW_ACCESS_NONE); +} + +static int +rtw_pwrstate(struct rtw_softc *sc, enum rtw_pwrstate power) +{ + int rc; + + RTW_DPRINTF(RTW_DEBUG_PWR, + ("%s: %s->%s\n", sc->sc_ic.ic_if.if_xname, + rtw_pwrstate_string(sc->sc_pwrstate), + rtw_pwrstate_string(power))); + + if (sc->sc_pwrstate == power) + return 0; + + rtw_pwrstate0(sc, power, 1, sc->sc_flags & RTW_F_DIGPHY); + rc = rtw_rf_pwrstate(sc->sc_rf, power); + rtw_pwrstate0(sc, power, 0, sc->sc_flags & RTW_F_DIGPHY); + + switch (power) { + case RTW_ON: + /* TBD set LEDs */ + break; + case RTW_SLEEP: + /* TBD */ + break; + case RTW_OFF: + /* TBD */ + break; + } + if (rc == 0) + sc->sc_pwrstate = power; + else + sc->sc_pwrstate = RTW_OFF; + return rc; +} + +static int +rtw_tune(struct rtw_softc *sc) +{ + struct ieee80211com *ic = &sc->sc_ic; + struct rtw_tx_radiotap_header *rt = &sc->sc_txtap; + struct rtw_rx_radiotap_header *rr = &sc->sc_rxtap; + u_int chan; + int rc, antdiv, dflantb; + + antdiv = sc->sc_flags & RTW_F_ANTDIV; + dflantb = sc->sc_flags & RTW_F_DFLANTB; + + chan = ieee80211_chan2ieee(ic, ic->ic_curchan); + if (chan == IEEE80211_CHAN_ANY) + panic("%s: chan == IEEE80211_CHAN_ANY\n", ic->ic_if.if_xname); + + rt->rt_chan_freq = htole16(ic->ic_curchan->ic_freq); + rt->rt_chan_flags = htole16(ic->ic_curchan->ic_flags); + + rr->rr_chan_freq = htole16(ic->ic_curchan->ic_freq); + rr->rr_chan_flags = htole16(ic->ic_curchan->ic_flags); + + if (chan == sc->sc_cur_chan) { + RTW_DPRINTF(RTW_DEBUG_TUNE, + ("%s: already tuned chan #%d\n", + ic->ic_if.if_xname, chan)); + return 0; + } + + rtw_suspend_ticks(sc); + + rtw_io_enable(sc, RTW_CR_RE | RTW_CR_TE, 0); + + /* TBD wait for Tx to complete */ + + KKASSERT((sc->sc_flags & RTW_F_ENABLED) != 0); + + rc = rtw_phy_init(&sc->sc_regs, sc->sc_rf, + rtw_chan2txpower(&sc->sc_srom, ic, ic->ic_curchan), + sc->sc_csthr, ic->ic_curchan->ic_freq, antdiv, + dflantb, RTW_ON); + if (rc != 0) { + /* XXX condition on powersaving */ + printf("%s: phy init failed\n", ic->ic_if.if_xname); + } + + sc->sc_cur_chan = chan; + + rtw_io_enable(sc, RTW_CR_RE | RTW_CR_TE, 1); + + rtw_resume_ticks(sc); + + return rc; +} + +static void +rtw_disable(struct rtw_softc *sc) +{ + int rc; + + if ((sc->sc_flags & RTW_F_ENABLED) == 0) + return; + + /* turn off PHY */ + if ((sc->sc_flags & RTW_F_INVALID) == 0 && + (rc = rtw_pwrstate(sc, RTW_OFF)) != 0) + if_printf(&sc->sc_ic.ic_if, "failed to turn off PHY\n"); + + sc->sc_flags &= ~RTW_F_ENABLED; +} + +static int +rtw_enable(struct rtw_softc *sc) +{ + if ((sc->sc_flags & RTW_F_ENABLED) == 0) { + sc->sc_flags |= RTW_F_ENABLED; + /* + * Power may have been removed, and WEP keys thus reset. + */ + sc->sc_flags &= ~RTW_F_DK_VALID; + } + return (0); +} + +static void +rtw_transmit_config(struct rtw_regs *regs) +{ + uint32_t tcr; + + tcr = RTW_READ(regs, RTW_TCR); + + tcr |= RTW_TCR_CWMIN; + tcr &= ~RTW_TCR_MXDMA_MASK; + tcr |= RTW_TCR_MXDMA_256; + tcr |= RTW_TCR_SAT; /* send ACK as fast as possible */ + tcr &= ~RTW_TCR_LBK_MASK; + tcr |= RTW_TCR_LBK_NORMAL; /* normal operating mode */ + + /* set short/long retry limits */ + tcr &= ~(RTW_TCR_SRL_MASK|RTW_TCR_LRL_MASK); + tcr |= SHIFTIN(4, RTW_TCR_SRL_MASK) | SHIFTIN(4, RTW_TCR_LRL_MASK); + + tcr &= ~RTW_TCR_CRC; /* NIC appends CRC32 */ + + RTW_WRITE(regs, RTW_TCR, tcr); + RTW_SYNC(regs, RTW_TCR, RTW_TCR); +} + +static void +rtw_enable_interrupts(struct rtw_softc *sc) +{ + struct rtw_regs *regs = &sc->sc_regs; + + sc->sc_inten = RTW_INTR_RX|RTW_INTR_TX|RTW_INTR_BEACON|RTW_INTR_ATIMINT; + sc->sc_inten |= RTW_INTR_IOERROR|RTW_INTR_TIMEOUT; + + RTW_WRITE16(regs, RTW_IMR, sc->sc_inten); + RTW_WBW(regs, RTW_IMR, RTW_ISR); + RTW_WRITE16(regs, RTW_ISR, 0xffff); + RTW_SYNC(regs, RTW_IMR, RTW_ISR); + + /* XXX necessary? */ + if (sc->sc_intr_ack != NULL) + sc->sc_intr_ack(regs); +} + +static void +rtw_set_nettype(struct rtw_softc *sc, enum ieee80211_opmode opmode) +{ + struct rtw_regs *regs = &sc->sc_regs; + uint8_t msr; + + /* I'm guessing that MSR is protected as CONFIG[0123] are. */ + rtw_set_access(sc, RTW_ACCESS_CONFIG); + + msr = RTW_READ8(regs, RTW_MSR) & ~RTW_MSR_NETYPE_MASK; + + switch (opmode) { + case IEEE80211_M_AHDEMO: + case IEEE80211_M_IBSS: + msr |= RTW_MSR_NETYPE_ADHOC_OK; + break; + case IEEE80211_M_HOSTAP: + msr |= RTW_MSR_NETYPE_AP_OK; + break; + case IEEE80211_M_MONITOR: + /* XXX */ + msr |= RTW_MSR_NETYPE_NOLINK; + break; + case IEEE80211_M_STA: + msr |= RTW_MSR_NETYPE_INFRA_OK; + break; + } + RTW_WRITE8(regs, RTW_MSR, msr); + + rtw_set_access(sc, RTW_ACCESS_NONE); +} + +#define rtw_calchash(addr) \ + (ether_crc32_be((addr), IEEE80211_ADDR_LEN) >> 26) + +static void +rtw_pktfilt_load(struct rtw_softc *sc) +{ + struct rtw_regs *regs = &sc->sc_regs; + struct ieee80211com *ic = &sc->sc_ic; + struct ifnet *ifp = &ic->ic_if; + struct ifmultiaddr *ifma; + uint32_t hashes[2] = { 0, 0 }; + int hash; + + /* XXX might be necessary to stop Rx/Tx engines while setting filters */ + + sc->sc_rcr &= ~RTW_RCR_PKTFILTER_MASK; + sc->sc_rcr &= ~(RTW_RCR_MXDMA_MASK | RTW_RCR_RXFTH_MASK); + + sc->sc_rcr |= RTW_RCR_PKTFILTER_DEFAULT; + /* MAC auto-reset PHY (huh?) */ + sc->sc_rcr |= RTW_RCR_ENMARP; + /* DMA whole Rx packets, only. Set Tx DMA burst size to 1024 bytes. */ + sc->sc_rcr |= RTW_RCR_MXDMA_1024 | RTW_RCR_RXFTH_WHOLE; + + switch (ic->ic_opmode) { + case IEEE80211_M_MONITOR: + sc->sc_rcr |= RTW_RCR_MONITOR; + break; + case IEEE80211_M_AHDEMO: + case IEEE80211_M_IBSS: + /* receive broadcasts in our BSS */ + sc->sc_rcr |= RTW_RCR_ADD3; + break; + default: + break; + } + + ifp->if_flags &= ~IFF_ALLMULTI; + + /* XXX accept all broadcast if scanning */ + if ((ifp->if_flags & IFF_BROADCAST) != 0) + sc->sc_rcr |= RTW_RCR_AB; /* accept all broadcast */ + + if (ifp->if_flags & IFF_PROMISC) { + sc->sc_rcr |= RTW_RCR_AB; /* accept all broadcast */ +allmulti: + ifp->if_flags |= IFF_ALLMULTI; + goto setit; + } + + /* + * Program the 64-bit multicast hash filter. + */ + LIST_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { + if (ifma->ifma_addr->sa_family != AF_LINK) + continue; + + hash = rtw_calchash( + LLADDR((struct sockaddr_dl *)ifma->ifma_addr)); + hashes[hash >> 5] |= (1 << (hash & 0x1f)); + sc->sc_rcr |= RTW_RCR_AM; + } + + /* all bits set => hash is useless */ + if (~(hashes[0] & hashes[1]) == 0) + goto allmulti; + +setit: + if (ifp->if_flags & IFF_ALLMULTI) { + sc->sc_rcr |= RTW_RCR_AM; /* accept all multicast */ + hashes[0] = hashes[1] = 0xffffffff; + } + + RTW_WRITE(regs, RTW_MAR0, hashes[0]); + RTW_WRITE(regs, RTW_MAR1, hashes[1]); + RTW_WRITE(regs, RTW_RCR, sc->sc_rcr); + RTW_SYNC(regs, RTW_MAR0, RTW_RCR); /* RTW_MAR0 < RTW_MAR1 < RTW_RCR */ + + DPRINTF(sc, RTW_DEBUG_PKTFILT, + ("%s: RTW_MAR0 %08x RTW_MAR1 %08x RTW_RCR %08x\n", + ifp->if_xname, RTW_READ(regs, RTW_MAR0), + RTW_READ(regs, RTW_MAR1), RTW_READ(regs, RTW_RCR))); +} + +/* Must be called at splnet. */ +static void +rtw_init(void *xsc) +{ + struct rtw_softc *sc = xsc; + struct ieee80211com *ic = &sc->sc_ic; + struct ifnet *ifp = &ic->ic_if; + struct rtw_regs *regs = &sc->sc_regs; + int rc = 0; + + rc = rtw_enable(sc); + if (rc) + goto out; + + /* Cancel pending I/O and reset. */ + rtw_stop(sc, 0); + + DPRINTF(sc, RTW_DEBUG_TUNE, + ("%s: channel %d freq %d flags 0x%04x\n", ifp->if_xname, + ieee80211_chan2ieee(ic, ic->ic_curchan), + ic->ic_curchan->ic_freq, ic->ic_curchan->ic_flags)); + + rc = rtw_pwrstate(sc, RTW_OFF); + if (rc) + goto out; + + rc = rtw_swring_setup(sc); + if (rc) + goto out; + + rtw_transmit_config(regs); + + rtw_set_access(sc, RTW_ACCESS_CONFIG); + + RTW_WRITE8(regs, RTW_MSR, 0x0); /* no link */ + RTW_WBW(regs, RTW_MSR, RTW_BRSR); + + /* long PLCP header, 1Mb/2Mb basic rate */ + RTW_WRITE16(regs, RTW_BRSR, RTW_BRSR_MBR8180_2MBPS); + RTW_SYNC(regs, RTW_BRSR, RTW_BRSR); + + rtw_set_access(sc, RTW_ACCESS_ANAPARM); + rtw_set_access(sc, RTW_ACCESS_NONE); + + /* XXX from reference sources */ + RTW_WRITE(regs, RTW_FEMR, 0xffff); + RTW_SYNC(regs, RTW_FEMR, RTW_FEMR); + + rtw_set_rfprog(sc); + + RTW_WRITE8(regs, RTW_PHYDELAY, sc->sc_phydelay); + /* from Linux driver */ + RTW_WRITE8(regs, RTW_CRCOUNT, RTW_CRCOUNT_MAGIC); + + RTW_SYNC(regs, RTW_PHYDELAY, RTW_CRCOUNT); + + rtw_enable_interrupts(sc); + + rtw_pktfilt_load(sc); + + rtw_hwring_setup(sc); + + rtw_wep_setkeys(sc); + + rtw_io_enable(sc, RTW_CR_RE | RTW_CR_TE, 1); + + ifp->if_flags |= IFF_RUNNING; + ic->ic_state = IEEE80211_S_INIT; + + RTW_WRITE16(regs, RTW_BSSID16, 0x0); + RTW_WRITE(regs, RTW_BSSID32, 0x0); + + rtw_resume_ticks(sc); + + rtw_set_nettype(sc, IEEE80211_M_MONITOR); + + if (ic->ic_opmode == IEEE80211_M_MONITOR) + ieee80211_new_state(ic, IEEE80211_S_RUN, -1); + else + ieee80211_new_state(ic, IEEE80211_S_SCAN, -1); + +out: + if (rc) + if_printf(ifp, "interface not running\n"); +} + +static void +rtw_led_init(struct rtw_softc *sc) +{ + struct rtw_regs *regs = &sc->sc_regs; + uint8_t cfg0, cfg1; + + rtw_set_access(sc, RTW_ACCESS_CONFIG); + + cfg0 = RTW_READ8(regs, RTW_CONFIG0); + cfg0 |= RTW_CONFIG0_LEDGPOEN; + RTW_WRITE8(regs, RTW_CONFIG0, cfg0); + + cfg1 = RTW_READ8(regs, RTW_CONFIG1); + RTW_DPRINTF(RTW_DEBUG_LED, + ("%s: read %02x from reg[CONFIG1]\n", + sc->sc_ic.ic_if.if_xname, cfg1)); + + cfg1 &= ~RTW_CONFIG1_LEDS_MASK; + cfg1 |= RTW_CONFIG1_LEDS_TX_RX; + RTW_WRITE8(regs, RTW_CONFIG1, cfg1); + + rtw_set_access(sc, RTW_ACCESS_NONE); +} + +/* + * IEEE80211_S_INIT: LED1 off + * + * IEEE80211_S_AUTH, + * IEEE80211_S_ASSOC, + * IEEE80211_S_SCAN: LED1 blinks @ 1 Hz, blinks at 5Hz for tx/rx + * + * IEEE80211_S_RUN: LED1 on, blinks @ 5Hz for tx/rx + */ +static void +rtw_led_newstate(struct rtw_softc *sc, enum ieee80211_state nstate) +{ + struct rtw_led_state *ls = &sc->sc_led_state; + + switch (nstate) { + case IEEE80211_S_INIT: + rtw_led_init(sc); + callout_stop(&ls->ls_slow_ch); + callout_stop(&ls->ls_fast_ch); + ls->ls_slowblink = 0; + ls->ls_actblink = 0; + ls->ls_default = 0; + break; + case IEEE80211_S_SCAN: + callout_reset(&ls->ls_slow_ch, RTW_LED_SLOW_TICKS, + rtw_led_slowblink, sc); + callout_reset(&ls->ls_fast_ch, RTW_LED_FAST_TICKS, + rtw_led_fastblink, sc); + /*FALLTHROUGH*/ + case IEEE80211_S_AUTH: + case IEEE80211_S_ASSOC: + ls->ls_default = RTW_LED1; + ls->ls_actblink = RTW_LED1; + ls->ls_slowblink = RTW_LED1; + break; + case IEEE80211_S_RUN: + ls->ls_slowblink = 0; + break; + } + rtw_led_set(sc); +} + +static void +rtw_led_set(struct rtw_softc *sc) +{ + struct rtw_led_state *ls = &sc->sc_led_state; + struct rtw_regs *regs = &sc->sc_regs; + uint8_t led_condition, mask, newval, val; + bus_size_t ofs; + + led_condition = ls->ls_default; + + if (ls->ls_state & RTW_LED_S_SLOW) + led_condition ^= ls->ls_slowblink; + if (ls->ls_state & (RTW_LED_S_RX|RTW_LED_S_TX)) + led_condition ^= ls->ls_actblink; + + RTW_DPRINTF(RTW_DEBUG_LED, + ("%s: LED condition %02x\n", sc->sc_ic.ic_if.if_xname, + led_condition)); + + switch (sc->sc_hwverid) { + default: + case 'F': + ofs = RTW_PSR; + newval = mask = RTW_PSR_LEDGPO0 | RTW_PSR_LEDGPO1; + if (led_condition & RTW_LED0) + newval &= ~RTW_PSR_LEDGPO0; + if (led_condition & RTW_LED1) + newval &= ~RTW_PSR_LEDGPO1; + break; + case 'D': + ofs = RTW_9346CR; + mask = RTW_9346CR_EEM_MASK | RTW_9346CR_EEDI | RTW_9346CR_EECS; + newval = RTW_9346CR_EEM_PROGRAM; + if (led_condition & RTW_LED0) + newval |= RTW_9346CR_EEDI; + if (led_condition & RTW_LED1) + newval |= RTW_9346CR_EECS; + break; + } + val = RTW_READ8(regs, ofs); + RTW_DPRINTF(RTW_DEBUG_LED, + ("%s: read %02x from reg[%02x]\n", + sc->sc_ic.ic_if.if_xname, val, ofs)); + val &= ~mask; + val |= newval; + RTW_WRITE8(regs, ofs, val); + RTW_DPRINTF(RTW_DEBUG_LED, + ("%s: wrote %02x to reg[%02x]\n", + sc->sc_ic.ic_if.if_xname, val, ofs)); + RTW_SYNC(regs, ofs, ofs); +} + +static void +rtw_led_fastblink(void *arg) +{ + struct rtw_softc *sc = arg; + struct ifnet *ifp = &sc->sc_ic.ic_if; + struct rtw_led_state *ls = &sc->sc_led_state; + int ostate; + + lwkt_serialize_enter(ifp->if_serializer); + + ostate = ls->ls_state; + ls->ls_state ^= ls->ls_event; + + if ((ls->ls_event & RTW_LED_S_TX) == 0) + ls->ls_state &= ~RTW_LED_S_TX; + + if ((ls->ls_event & RTW_LED_S_RX) == 0) + ls->ls_state &= ~RTW_LED_S_RX; + + ls->ls_event = 0; + + if (ostate != ls->ls_state) + rtw_led_set(sc); + + callout_reset(&ls->ls_fast_ch, RTW_LED_FAST_TICKS, + rtw_led_fastblink, sc); + + lwkt_serialize_exit(ifp->if_serializer); +} + +static void +rtw_led_slowblink(void *arg) +{ + struct rtw_softc *sc = arg; + struct ifnet *ifp = &sc->sc_ic.ic_if; + struct rtw_led_state *ls = &sc->sc_led_state; + + lwkt_serialize_enter(ifp->if_serializer); + + ls->ls_state ^= RTW_LED_S_SLOW; + rtw_led_set(sc); + callout_reset(&ls->ls_slow_ch, RTW_LED_SLOW_TICKS, + rtw_led_slowblink, sc); + + lwkt_serialize_exit(ifp->if_serializer); +} + +static int +rtw_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data, struct ucred *cr) +{ + struct rtw_softc *sc = ifp->if_softc; + int rc = 0; + + switch (cmd) { + case SIOCSIFFLAGS: + if (ifp->if_flags & IFF_UP) { + if ((ifp->if_flags & IFF_RUNNING) == 0) + rtw_init(sc); + RTW_PRINT_REGS(&sc->sc_regs, ifp->if_xname, __func__); + } else if (sc->sc_flags & RTW_F_ENABLED) { + RTW_PRINT_REGS(&sc->sc_regs, ifp->if_xname, __func__); + rtw_stop(sc, 1); + } + break; + case SIOCADDMULTI: + case SIOCDELMULTI: + if (ifp->if_flags & IFF_RUNNING) + rtw_pktfilt_load(sc); + break; + default: + rc = ieee80211_ioctl(&sc->sc_ic, cmd, data, cr); + if (rc == ENETRESET) { + if (sc->sc_flags & RTW_F_ENABLED) + rtw_init(sc); + rc = 0; + } + break; + } + return rc; +} + +/* + * Select a transmit ring with at least one h/w and s/w descriptor free. + * Return 0 on success, -1 on failure. + */ +static __inline int +rtw_txring_choose(struct rtw_softc *sc, struct rtw_txsoft_blk **tsbp, + struct rtw_txdesc_blk **tdbp, int pri) +{ + struct rtw_txsoft_blk *tsb; + struct rtw_txdesc_blk *tdb; + + KKASSERT(pri >= 0 && pri < RTW_NTXPRI); + + tsb = &sc->sc_txsoft_blk[pri]; + tdb = &sc->sc_txdesc_blk[pri]; + + if (STAILQ_EMPTY(&tsb->tsb_freeq) || tdb->tdb_nfree == 0) { + if (tsb->tsb_tx_timer == 0) + tsb->tsb_tx_timer = 5; + *tsbp = NULL; + *tdbp = NULL; + return -1; + } + *tsbp = tsb; + *tdbp = tdb; + return 0; +} + +static __inline struct mbuf * +rtw_80211_dequeue(struct rtw_softc *sc, struct ifqueue *ifq, int pri, + struct rtw_txsoft_blk **tsbp, struct rtw_txdesc_blk **tdbp, + struct ieee80211_node **nip, int *if_flagsp) +{ + struct mbuf *m; + struct ifnet *ifp = &sc->sc_if; + + if (IF_QEMPTY(ifq)) + return NULL; + if (rtw_txring_choose(sc, tsbp, tdbp, pri) == -1) { + DPRINTF(sc, RTW_DEBUG_XMIT_RSRC, + ("%s: no ring %d descriptor\n", ifp->if_xname, pri)); + *if_flagsp |= IFF_OACTIVE; + ifp->if_timer = 1; + return NULL; + } + IF_DEQUEUE(ifq, m); + *nip = (struct ieee80211_node *)m->m_pkthdr.rcvif; + m->m_pkthdr.rcvif = NULL; + KKASSERT(*nip != NULL); + return m; +} + +/* + * Point *mp at the next 802.11 frame to transmit. Point *tsbp + * at the driver's selection of transmit control block for the packet. + */ +static int +rtw_dequeue(struct ifnet *ifp, struct rtw_txsoft_blk **tsbp, + struct rtw_txdesc_blk **tdbp, struct mbuf **mp, + struct ieee80211_node **nip) +{ + struct rtw_softc *sc = ifp->if_softc; + int *if_flagsp = &ifp->if_flags; + struct ether_header *eh; + struct mbuf *m0; + int pri; + + DPRINTF(sc, RTW_DEBUG_XMIT, + ("%s: enter %s\n", ifp->if_xname, __func__)); + + if (sc->sc_ic.ic_state == IEEE80211_S_RUN && + (*mp = rtw_80211_dequeue(sc, &sc->sc_beaconq, RTW_TXPRIBCN, tsbp, + tdbp, nip, if_flagsp)) != NULL) { + DPRINTF(sc, RTW_DEBUG_XMIT, + ("%s: dequeue beacon frame\n", ifp->if_xname)); + return 0; + } + + if ((*mp = rtw_80211_dequeue(sc, &sc->sc_ic.ic_mgtq, RTW_TXPRIMD, tsbp, + tdbp, nip, if_flagsp)) != NULL) { + DPRINTF(sc, RTW_DEBUG_XMIT, + ("%s: dequeue mgt frame\n", ifp->if_xname)); + return 0; + } + + *mp = NULL; + + if (sc->sc_ic.ic_state != IEEE80211_S_RUN) { + DPRINTF(sc, RTW_DEBUG_XMIT, + ("%s: not running\n", ifp->if_xname)); + return 0; + } + + m0 = ifq_poll(&ifp->if_snd); + if (m0 == NULL) { + DPRINTF(sc, RTW_DEBUG_XMIT, + ("%s: no frame ready\n", ifp->if_xname)); + return 0; + } + + pri = ((m0->m_flags & M_PWR_SAV) != 0) ? RTW_TXPRIHI : RTW_TXPRIMD; + + if (rtw_txring_choose(sc, tsbp, tdbp, pri) == -1) { + DPRINTF(sc, RTW_DEBUG_XMIT_RSRC, + ("%s: no ring %d descriptor\n", ifp->if_xname, pri)); + *if_flagsp |= IFF_OACTIVE; + sc->sc_if.if_timer = 1; + return 0; + } + + ifq_dequeue(&ifp->if_snd, m0); + DPRINTF(sc, RTW_DEBUG_XMIT, + ("%s: dequeue data frame\n", ifp->if_xname)); + + BPF_MTAP(ifp, m0); + + eh = mtod(m0, struct ether_header *); + *nip = ieee80211_find_txnode(&sc->sc_ic, eh->ether_dhost); + if (*nip == NULL) { + /* NB: ieee80211_find_txnode does stat+msg */ + m_freem(m0); + return -1; + } + + if ((m0 = ieee80211_encap(&sc->sc_ic, m0, *nip)) == NULL) { + DPRINTF(sc, RTW_DEBUG_XMIT, + ("%s: encap error\n", ifp->if_xname)); + ieee80211_free_node(*nip); + ifp->if_oerrors++; + return -1; + } + + ifp->if_opackets++; + DPRINTF(sc, RTW_DEBUG_XMIT, + ("%s: leave %s\n", ifp->if_xname, __func__)); + *mp = m0; + return 0; +} + +static __inline int +rtw_txsegs_too_short(struct rtw_txsegs *segs) +{ + int i; + + for (i = 0; i < segs->nseg; i++) { + if (segs->segs[i].ds_len < 4) + return 1; + } + return 0; +} + +static __inline int +rtw_txsegs_too_long(struct rtw_txsegs *segs) +{ + int i; + + for (i = 0; i < segs->nseg; i++) { + if (segs->segs[i].ds_len > RTW_TXLEN_LENGTH_MASK) + return 1; + } + return 0; +} + +static void +rtw_txbuf_dma_map(void *arg, bus_dma_segment_t *seg, int nseg, + bus_size_t mapsize, int error) +{ + struct rtw_txsegs *s = arg; + + if (error) + return; + + KASSERT(nseg <= RTW_MAXPKTSEGS, ("too many tx mbuf seg\n")); + + s->nseg = nseg; + bcopy(seg, s->segs, sizeof(*seg) * nseg); +} + +static struct mbuf * +rtw_load_txbuf(struct rtw_softc *sc, struct rtw_txsoft *ts, + struct rtw_txsegs *segs, int ndesc_free, struct mbuf *m) +{ + int unload = 0, error; + + error = bus_dmamap_load_mbuf(sc->sc_txsoft_dmat, ts->ts_dmamap, m, + rtw_txbuf_dma_map, segs, BUS_DMA_NOWAIT); + if (error && error != E2BIG) { + if_printf(&sc->sc_ic.ic_if, "can't load tx mbuf1\n"); + goto back; + } + + if (error || segs->nseg > ndesc_free || rtw_txsegs_too_short(segs)) { + struct mbuf *m_new; + + if (error == 0) + bus_dmamap_unload(sc->sc_txsoft_dmat, ts->ts_dmamap); + + m_new = m_defrag(m, MB_DONTWAIT); + if (m_new == NULL) { + if_printf(&sc->sc_ic.ic_if, "can't defrag tx mbuf\n"); + error = ENOBUFS; + goto back; + } + m = m_new; + + error = bus_dmamap_load_mbuf(sc->sc_txsoft_dmat, ts->ts_dmamap, + m, rtw_txbuf_dma_map, segs, + BUS_DMA_NOWAIT); + if (error) { + if_printf(&sc->sc_ic.ic_if, "can't load tx mbuf2\n"); + goto back; + } + unload = 1; + + error = E2BIG; + if (segs->nseg > ndesc_free) { + if_printf(&sc->sc_ic.ic_if, "not enough free txdesc\n"); + goto back; + } + if (rtw_txsegs_too_short(segs)) { + if_printf(&sc->sc_ic.ic_if, "segment too short\n"); + goto back; + } + error = 0; + } + + if (rtw_txsegs_too_long(segs)) { + if_printf(&sc->sc_ic.ic_if, "segment too long\n"); + unload = 1; + error = E2BIG; + } + +back: + if (error) { + if (unload) + bus_dmamap_unload(sc->sc_txsoft_dmat, ts->ts_dmamap); + m_freem(m); + m = NULL; + } else { + bus_dmamap_sync(sc->sc_txsoft_dmat, ts->ts_dmamap, + BUS_DMASYNC_PREWRITE); + } + return m; +} + +#ifdef RTW_DEBUG +static void +rtw_print_txdesc(struct rtw_softc *sc, const char *action, + struct rtw_txsoft *ts, struct rtw_txdesc_blk *tdb, int desc) +{ + struct rtw_txdesc *td = &tdb->tdb_desc[desc]; + + DPRINTF(sc, RTW_DEBUG_XMIT_DESC, + ("%s: %p %s txdesc[%d] " + "next %#08x buf %#08x " + "ctl0 %#08x ctl1 %#08x len %#08x\n", + sc->sc_ic.ic_if.if_xname, ts, action, + desc, le32toh(td->td_buf), le32toh(td->td_next), + le32toh(td->td_ctl0), le32toh(td->td_ctl1), + le32toh(td->td_len))); +} +#endif /* RTW_DEBUG */ + +static void +rtw_start(struct ifnet *ifp) +{ + struct rtw_softc *sc = ifp->if_softc; + struct ieee80211com *ic = &sc->sc_ic; + struct ieee80211_node *ni; + struct rtw_txsoft *ts; + struct mbuf *m0; + uint32_t proto_ctl0; + + DPRINTF(sc, RTW_DEBUG_XMIT, + ("%s: enter %s\n", ifp->if_xname, __func__)); + + if ((ifp->if_flags & (IFF_RUNNING | IFF_OACTIVE)) != IFF_RUNNING) + goto out; + + /* XXX do real rate control */ + proto_ctl0 = RTW_TXCTL0_RTSRATE_1MBPS; + + if (ic->ic_flags & IEEE80211_F_SHPREAMBLE) + proto_ctl0 |= RTW_TXCTL0_SPLCP; + + for (;;) { + struct rtw_txsegs segs; + struct rtw_duration *d0; + struct ieee80211_frame_min *wh; + struct rtw_txsoft_blk *tsb; + struct rtw_txdesc_blk *tdb; + struct rtw_txdesc *td; + struct ieee80211_key *k; + uint32_t ctl0, ctl1; + uint8_t tppoll; + int desc, i, lastdesc, npkt, rate, rateidx, ratectl; + + if (rtw_dequeue(ifp, &tsb, &tdb, &m0, &ni) == -1) + continue; + if (m0 == NULL) + break; + + wh = mtod(m0, struct ieee80211_frame_min *); + + if ((wh->i_fc[1] & IEEE80211_FC1_WEP) != 0 && + (k = ieee80211_crypto_encap(ic, ni, m0)) == NULL) { + ieee80211_free_node(ni); + m_freem(m0); + break; + } else { + k = NULL; + } + + ts = STAILQ_FIRST(&tsb->tsb_freeq); + + m0 = rtw_load_txbuf(sc, ts, &segs, tdb->tdb_nfree, m0); + if (m0 == NULL || segs.nseg == 0) { + DPRINTF(sc, RTW_DEBUG_XMIT, + ("%s: %s failed\n", ifp->if_xname, __func__)); + goto post_dequeue_err; + } + + /* + * Note well: rtw_load_txbuf may have created a new chain, + * so we must find the header once more. + */ + wh = mtod(m0, struct ieee80211_frame_min *); + + if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) == + IEEE80211_FC0_TYPE_MGT) { + rateidx = 0; + rate = 2; /* 1Mbit/s */ + ratectl = 0; + } else { + ieee80211_ratectl_findrate(ni, m0->m_pkthdr.len, + &rateidx, 1); + rate = IEEE80211_RS_RATE(&ni->ni_rates, rateidx); + ratectl =1; + + if (rate == 0) { + if_printf(ifp, "incorrect rate\n"); + rateidx = 0; + rate = 2; /* 1Mbit/s */ + ratectl = 0; + } + } + +#ifdef RTW_DEBUG + if ((ifp->if_flags & (IFF_DEBUG | IFF_LINK2)) == + (IFF_DEBUG | IFF_LINK2)) { + ieee80211_dump_pkt(mtod(m0, uint8_t *), + (segs.nseg == 1) ? m0->m_pkthdr.len + : sizeof(wh), + rate, 0); + } +#endif /* RTW_DEBUG */ + ctl0 = proto_ctl0 | + SHIFTIN(m0->m_pkthdr.len, RTW_TXCTL0_TPKTSIZE_MASK); + + switch (rate) { + default: + case 2: + ctl0 |= RTW_TXCTL0_RATE_1MBPS; + break; + case 4: + ctl0 |= RTW_TXCTL0_RATE_2MBPS; + break; + case 11: + ctl0 |= RTW_TXCTL0_RATE_5MBPS; + break; + case 22: + ctl0 |= RTW_TXCTL0_RATE_11MBPS; + break; + } + /* XXX >= ? Compare after fragmentation? */ + if (m0->m_pkthdr.len > ic->ic_rtsthreshold) + ctl0 |= RTW_TXCTL0_RTSEN; + + /* + * XXX Sometimes writes a bogus keyid; h/w doesn't + * seem to care, since we don't activate h/w Tx + * encryption. + */ + if (k != NULL) { + ctl0 |= SHIFTIN(k->wk_keyix, RTW_TXCTL0_KEYID_MASK) & + RTW_TXCTL0_KEYID_MASK; + } + + if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) == + IEEE80211_FC0_TYPE_MGT) { + ctl0 &= ~(RTW_TXCTL0_SPLCP | RTW_TXCTL0_RTSEN); + if ((wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) == + IEEE80211_FC0_SUBTYPE_BEACON) + ctl0 |= RTW_TXCTL0_BEACON; + } + + if (rtw_compute_duration(wh, k, m0->m_pkthdr.len, + ic->ic_flags, ic->ic_fragthreshold, + rate, &ts->ts_d0, &ts->ts_dn, &npkt, + (ifp->if_flags & (IFF_DEBUG|IFF_LINK2)) == + (IFF_DEBUG|IFF_LINK2)) == -1) { + DPRINTF(sc, RTW_DEBUG_XMIT, + ("%s: fail compute duration\n", __func__)); + goto post_load_err; + } + + d0 = &ts->ts_d0; + + *(uint16_t*)wh->i_dur = htole16(d0->d_data_dur); + + ctl1 = SHIFTIN(d0->d_plcp_len, RTW_TXCTL1_LENGTH_MASK) | + SHIFTIN(d0->d_rts_dur, RTW_TXCTL1_RTSDUR_MASK); + + if (d0->d_residue) + ctl1 |= RTW_TXCTL1_LENGEXT; + + /* TBD fragmentation */ + + ts->ts_first = tdb->tdb_next; + KKASSERT(ts->ts_first < tdb->tdb_ndesc); + + if (ic->ic_rawbpf != NULL) + bpf_mtap(ic->ic_rawbpf, m0); + + if (sc->sc_radiobpf != NULL) { + struct rtw_tx_radiotap_header *rt = &sc->sc_txtap; + + rt->rt_flags = 0; + rt->rt_rate = rate; + + bpf_ptap(sc->sc_radiobpf, m0, rt, + sizeof(sc->sc_txtapu)); + } + + for (i = 0, lastdesc = desc = ts->ts_first; i < segs.nseg; + i++, desc = RTW_NEXT_IDX(tdb, desc)) { + td = &tdb->tdb_desc[desc]; + td->td_ctl0 = htole32(ctl0); + if (i != 0) + td->td_ctl0 |= htole32(RTW_TXCTL0_OWN); + td->td_ctl1 = htole32(ctl1); + td->td_buf = htole32(segs.segs[i].ds_addr); + td->td_len = htole32(segs.segs[i].ds_len); + lastdesc = desc; +#ifdef RTW_DEBUG + rtw_print_txdesc(sc, "load", ts, tdb, desc); +#endif /* RTW_DEBUG */ + } + + KKASSERT(desc < tdb->tdb_ndesc); + + ts->ts_ni = ni; + KKASSERT(ni != NULL); + ts->ts_mbuf = m0; + ts->ts_rateidx = rateidx; + ts->ts_ratectl = ratectl; + ts->ts_last = lastdesc; + tdb->tdb_desc[ts->ts_last].td_ctl0 |= htole32(RTW_TXCTL0_LS); + tdb->tdb_desc[ts->ts_first].td_ctl0 |= htole32(RTW_TXCTL0_FS); + +#ifdef RTW_DEBUG + rtw_print_txdesc(sc, "FS on", ts, tdb, ts->ts_first); + rtw_print_txdesc(sc, "LS on", ts, tdb, ts->ts_last); +#endif /* RTW_DEBUG */ + + tdb->tdb_nfree -= segs.nseg; + tdb->tdb_next = desc; + + tdb->tdb_desc[ts->ts_first].td_ctl0 |= htole32(RTW_TXCTL0_OWN); + +#ifdef RTW_DEBUG + rtw_print_txdesc(sc, "OWN on", ts, tdb, ts->ts_first); +#endif /* RTW_DEBUG */ + + STAILQ_REMOVE_HEAD(&tsb->tsb_freeq, ts_q); + STAILQ_INSERT_TAIL(&tsb->tsb_dirtyq, ts, ts_q); + + if (tsb != &sc->sc_txsoft_blk[RTW_TXPRIBCN]) + sc->sc_led_state.ls_event |= RTW_LED_S_TX; + tsb->tsb_tx_timer = 5; + ifp->if_timer = 1; + tppoll = RTW_READ8(&sc->sc_regs, RTW_TPPOLL); + tppoll &= ~RTW_TPPOLL_SALL; + tppoll |= tsb->tsb_poll & RTW_TPPOLL_ALL; + RTW_WRITE8(&sc->sc_regs, RTW_TPPOLL, tppoll); + RTW_SYNC(&sc->sc_regs, RTW_TPPOLL, RTW_TPPOLL); + + bus_dmamap_sync(tdb->tdb_dmat, tdb->tdb_dmamap, + BUS_DMASYNC_PREWRITE); + } +out: + DPRINTF(sc, RTW_DEBUG_XMIT, + ("%s: leave %s\n", ifp->if_xname, __func__)); + return; + +post_load_err: + bus_dmamap_unload(sc->sc_txsoft_dmat, ts->ts_dmamap); + m_freem(m0); +post_dequeue_err: + ieee80211_free_node(ni); + + DPRINTF(sc, RTW_DEBUG_XMIT, + ("%s: leave %s\n", ifp->if_xname, __func__)); +} + +static void +rtw_idle(struct rtw_softc *sc) +{ + struct rtw_regs *regs = &sc->sc_regs; + int active; + + /* request stop DMA; wait for packets to stop transmitting. */ + + RTW_WRITE8(regs, RTW_TPPOLL, RTW_TPPOLL_SALL); + RTW_WBR(regs, RTW_TPPOLL, RTW_TPPOLL); + + for (active = 0; + active < 300 && + (RTW_READ8(regs, RTW_TPPOLL) & RTW_TPPOLL_ACTIVE) != 0; + active++) + DELAY(10); + if_printf(&sc->sc_ic.ic_if, "transmit DMA idle in %dus\n", active * 10); +} + +static void +rtw_watchdog(struct ifnet *ifp) +{ + int pri, tx_timeouts = 0; + struct rtw_softc *sc = ifp->if_softc; + + ifp->if_timer = 0; + + if ((sc->sc_flags & RTW_F_ENABLED) == 0) + return; + + for (pri = 0; pri < RTW_NTXPRI; pri++) { + struct rtw_txsoft_blk *tsb = &sc->sc_txsoft_blk[pri]; + + if (tsb->tsb_tx_timer == 0) + continue; + else if (--tsb->tsb_tx_timer == 0) { + if (STAILQ_EMPTY(&tsb->tsb_dirtyq)) + continue; + if_printf(ifp, "transmit timeout, priority %d\n", pri); + ifp->if_oerrors++; + tx_timeouts++; + } else { + ifp->if_timer = 1; + } + } + + if (tx_timeouts > 0) { + /* + * Stop Tx DMA, disable xmtr, flush Tx rings, enable xmtr, + * reset s/w tx-ring pointers, and start transmission. + * + * TBD Stop/restart just the broken rings? + */ + rtw_idle(sc); + rtw_io_enable(sc, RTW_CR_TE, 0); + rtw_txdesc_blk_reset_all(sc); + rtw_io_enable(sc, RTW_CR_TE, 1); + rtw_txring_fixup(sc); + rtw_start(ifp); + } + ieee80211_watchdog(&sc->sc_ic); +} + +static void +rtw_next_scan(void *arg) +{ + struct ieee80211com *ic = arg; + struct ifnet *ifp = &ic->ic_if; + + lwkt_serialize_enter(ifp->if_serializer); + + /* don't call rtw_start w/o network interrupts blocked */ + if (ic->ic_state == IEEE80211_S_SCAN) + ieee80211_next_scan(ic); + + lwkt_serialize_exit(ifp->if_serializer); +} + +static void +rtw_join_bss(struct rtw_softc *sc, uint8_t *bssid, uint16_t intval0) +{ + uint16_t bcnitv, bintritv, intval; + int i; + struct rtw_regs *regs = &sc->sc_regs; + + for (i = 0; i < IEEE80211_ADDR_LEN; i++) + RTW_WRITE8(regs, RTW_BSSID + i, bssid[i]); + + RTW_SYNC(regs, RTW_BSSID16, RTW_BSSID32); + + rtw_set_access(sc, RTW_ACCESS_CONFIG); + + intval = MIN(intval0, SHIFTOUT_MASK(RTW_BCNITV_BCNITV_MASK)); + + bcnitv = RTW_READ16(regs, RTW_BCNITV) & ~RTW_BCNITV_BCNITV_MASK; + bcnitv |= SHIFTIN(intval, RTW_BCNITV_BCNITV_MASK); + RTW_WRITE16(regs, RTW_BCNITV, bcnitv); + /* interrupt host 1ms before the TBTT */ + bintritv = RTW_READ16(regs, RTW_BINTRITV) & ~RTW_BINTRITV_BINTRITV; + bintritv |= SHIFTIN(1000, RTW_BINTRITV_BINTRITV); + RTW_WRITE16(regs, RTW_BINTRITV, bintritv); + /* magic from Linux */ + RTW_WRITE16(regs, RTW_ATIMWND, SHIFTIN(1, RTW_ATIMWND_ATIMWND)); + RTW_WRITE16(regs, RTW_ATIMTRITV, SHIFTIN(2, RTW_ATIMTRITV_ATIMTRITV)); + rtw_set_access(sc, RTW_ACCESS_NONE); + + rtw_io_enable(sc, RTW_CR_RE | RTW_CR_TE, 1); +} + +/* Synchronize the hardware state with the software state. */ +static int +rtw_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg) +{ + struct ifnet *ifp = ic->ic_ifp; + struct rtw_softc *sc = ifp->if_softc; + enum ieee80211_state ostate; + int error; + + ostate = ic->ic_state; + + ieee80211_ratectl_newstate(ic, nstate); + rtw_led_newstate(sc, nstate); + + if (nstate == IEEE80211_S_INIT) { + callout_stop(&sc->sc_scan_ch); + sc->sc_cur_chan = IEEE80211_CHAN_ANY; + return sc->sc_mtbl.mt_newstate(ic, nstate, arg); + } + + if (ostate == IEEE80211_S_INIT && nstate != IEEE80211_S_INIT) + rtw_pwrstate(sc, RTW_ON); + + error = rtw_tune(sc); + if (error != 0) + return error; + + switch (nstate) { + case IEEE80211_S_INIT: + panic("%s: unexpected state IEEE80211_S_INIT\n", __func__); + break; + case IEEE80211_S_SCAN: + if (ostate != IEEE80211_S_SCAN) { + memset(ic->ic_bss->ni_bssid, 0, IEEE80211_ADDR_LEN); + rtw_set_nettype(sc, IEEE80211_M_MONITOR); + } + + callout_reset(&sc->sc_scan_ch, rtw_dwelltime * hz / 1000, + rtw_next_scan, ic); + + break; + case IEEE80211_S_RUN: + switch (ic->ic_opmode) { + case IEEE80211_M_HOSTAP: + case IEEE80211_M_IBSS: + rtw_set_nettype(sc, IEEE80211_M_MONITOR); + /*FALLTHROUGH*/ + case IEEE80211_M_AHDEMO: + case IEEE80211_M_STA: + rtw_join_bss(sc, ic->ic_bss->ni_bssid, + ic->ic_bss->ni_intval); + break; + case IEEE80211_M_MONITOR: + break; + } + rtw_set_nettype(sc, ic->ic_opmode); + break; + case IEEE80211_S_ASSOC: + case IEEE80211_S_AUTH: + break; + } + + if (nstate != IEEE80211_S_SCAN) + callout_stop(&sc->sc_scan_ch); + + return sc->sc_mtbl.mt_newstate(ic, nstate, arg); +} + +/* Extend a 32-bit TSF timestamp to a 64-bit timestamp. */ +static uint64_t +rtw_tsf_extend(struct rtw_regs *regs, uint32_t rstamp) +{ + uint32_t tsftl, tsfth; + + tsfth = RTW_READ(regs, RTW_TSFTRH); + tsftl = RTW_READ(regs, RTW_TSFTRL); + if (tsftl < rstamp) /* Compensate for rollover. */ + tsfth--; + return ((uint64_t)tsfth << 32) | rstamp; +} + +static void +rtw_recv_mgmt(struct ieee80211com *ic, struct mbuf *m, + struct ieee80211_node *ni, int subtype, int rssi, uint32_t rstamp) +{ + struct ifnet *ifp = &ic->ic_if; + struct rtw_softc *sc = ifp->if_softc; + + sc->sc_mtbl.mt_recv_mgmt(ic, m, ni, subtype, rssi, rstamp); + + switch (subtype) { + case IEEE80211_FC0_SUBTYPE_PROBE_RESP: + case IEEE80211_FC0_SUBTYPE_BEACON: + if (ic->ic_opmode == IEEE80211_M_IBSS && + ic->ic_state == IEEE80211_S_RUN) { + uint64_t tsf = rtw_tsf_extend(&sc->sc_regs, rstamp); + + if (le64toh(ni->ni_tstamp.tsf) >= tsf) + ieee80211_ibss_merge(ni); + } + break; + default: + break; + } +} + +#ifdef foo +static struct ieee80211_node * +rtw_node_alloc(struct ieee80211_node_table *nt) +{ + struct ifnet *ifp = nt->nt_ic->ic_ifp; + struct rtw_softc *sc = (struct rtw_softc *)ifp->if_softc; + struct ieee80211_node *ni = (*sc->sc_mtbl.mt_node_alloc)(nt); + + DPRINTF(sc, RTW_DEBUG_NODE, + ("%s: alloc node %p\n", sc->sc_dev.dv_xname, ni)); + return ni; +} + +static void +rtw_node_free(struct ieee80211_node *ni) +{ + struct ieee80211com *ic = ni->ni_ic; + struct ifnet *ifp = ic->ic_ifp; + struct rtw_softc *sc = (struct rtw_softc *)ifp->if_softc; + + DPRINTF(sc, RTW_DEBUG_NODE, + ("%s: freeing node %p %s\n", sc->sc_dev.dv_xname, ni, + ether_sprintf(ni->ni_bssid))); + sc->sc_mtbl.mt_node_free(ni); +} +#endif + +static int +rtw_media_change(struct ifnet *ifp) +{ + int error; + + error = ieee80211_media_change(ifp); + if (error == ENETRESET) { + if ((ifp->if_flags & (IFF_RUNNING|IFF_UP)) == + (IFF_RUNNING|IFF_UP)) + rtw_init(ifp); /* XXX lose error */ + error = 0; + } + return error; +} + +static void +rtw_media_status(struct ifnet *ifp, struct ifmediareq *imr) +{ + struct rtw_softc *sc = ifp->if_softc; + + if ((sc->sc_flags & RTW_F_ENABLED) == 0) { + imr->ifm_active = IFM_IEEE80211 | IFM_NONE; + imr->ifm_status = 0; + return; + } + ieee80211_media_status(ifp, imr); +} + +static __inline void +rtw_set80211methods(struct rtw_mtbl *mtbl, struct ieee80211com *ic) +{ + mtbl->mt_newstate = ic->ic_newstate; + ic->ic_newstate = rtw_newstate; + + mtbl->mt_recv_mgmt = ic->ic_recv_mgmt; + ic->ic_recv_mgmt = rtw_recv_mgmt; + +#ifdef foo + mtbl->mt_node_free = ic->ic_node_free; + ic->ic_node_free = rtw_node_free; + + mtbl->mt_node_alloc = ic->ic_node_alloc; + ic->ic_node_alloc = rtw_node_alloc; +#endif + + ic->ic_crypto.cs_key_delete = rtw_key_delete; + ic->ic_crypto.cs_key_set = rtw_key_set; + ic->ic_crypto.cs_key_update_begin = rtw_key_update_begin; + ic->ic_crypto.cs_key_update_end = rtw_key_update_end; +} + +static __inline void +rtw_init_radiotap(struct rtw_softc *sc) +{ + sc->sc_rxtap.rr_ihdr.it_len = htole16(sizeof(sc->sc_rxtapu)); + sc->sc_rxtap.rr_ihdr.it_present = htole32(RTW_RX_RADIOTAP_PRESENT); + + sc->sc_txtap.rt_ihdr.it_len = htole16(sizeof(sc->sc_txtapu)); + sc->sc_txtap.rt_ihdr.it_present = htole32(RTW_TX_RADIOTAP_PRESENT); +} + +static struct rtw_rf * +rtw_rf_attach(struct rtw_softc *sc, enum rtw_rfchipid rfchipid, int digphy) +{ + rtw_rf_write_t rf_write; + struct rtw_rf *rf; + + switch (rfchipid) { + default: + rf_write = rtw_rf_hostwrite; + break; + case RTW_RFCHIPID_INTERSIL: + case RTW_RFCHIPID_PHILIPS: + case RTW_RFCHIPID_GCT: /* XXX a guess */ + case RTW_RFCHIPID_RFMD: + rf_write = (rtw_host_rfio) ? rtw_rf_hostwrite : rtw_rf_macwrite; + break; + } + + switch (rfchipid) { + case RTW_RFCHIPID_GCT: + rf = rtw_grf5101_create(&sc->sc_regs, rf_write, 0); + sc->sc_pwrstate_cb = rtw_maxim_pwrstate; + break; + case RTW_RFCHIPID_MAXIM: + rf = rtw_max2820_create(&sc->sc_regs, rf_write, 0); + sc->sc_pwrstate_cb = rtw_maxim_pwrstate; + break; + case RTW_RFCHIPID_PHILIPS: + rf = rtw_sa2400_create(&sc->sc_regs, rf_write, digphy); + sc->sc_pwrstate_cb = rtw_philips_pwrstate; + break; + case RTW_RFCHIPID_RFMD: + /* XXX RFMD has no RF constructor */ + sc->sc_pwrstate_cb = rtw_rfmd_pwrstate; + /*FALLTHROUGH*/ + default: + return NULL; + } + rf->rf_continuous_tx_cb = + (rtw_continuous_tx_cb_t)rtw_continuous_tx_enable; + rf->rf_continuous_tx_arg = sc; + return rf; +} + +/* Revision C and later use a different PHY delay setting than + * revisions A and B. + */ +static uint8_t +rtw_check_phydelay(struct rtw_regs *regs, uint32_t old_rcr) +{ +#define REVAB (RTW_RCR_MXDMA_UNLIMITED | RTW_RCR_AICV) +#define REVC (REVAB | RTW_RCR_RXFTH_WHOLE) + + uint8_t phydelay = SHIFTIN(0x6, RTW_PHYDELAY_PHYDELAY); + + RTW_WRITE(regs, RTW_RCR, REVAB); + RTW_WBW(regs, RTW_RCR, RTW_RCR); + RTW_WRITE(regs, RTW_RCR, REVC); + + RTW_WBR(regs, RTW_RCR, RTW_RCR); + if ((RTW_READ(regs, RTW_RCR) & REVC) == REVC) + phydelay |= RTW_PHYDELAY_REVC_MAGIC; + + RTW_WRITE(regs, RTW_RCR, old_rcr); /* restore RCR */ + RTW_SYNC(regs, RTW_RCR, RTW_RCR); + + return phydelay; +#undef REVC +#undef REVAB +} + +int +rtw_attach(device_t dev) +{ + struct rtw_softc *sc = device_get_softc(dev); + struct ieee80211com *ic = &sc->sc_ic; + const struct ieee80211_cipher *wep_cipher; + struct ifnet *ifp = &ic->ic_if; + int rc; + + wep_cipher = ieee80211_crypto_cipher(IEEE80211_CIPHER_WEP); + KKASSERT(wep_cipher != NULL); + + memcpy(&rtw_cipher_wep, wep_cipher, sizeof(rtw_cipher_wep)); + rtw_cipher_wep.ic_decap = rtw_wep_decap; + + if_initname(ifp, device_get_name(dev), device_get_unit(dev)); + + switch (RTW_READ(&sc->sc_regs, RTW_TCR) & RTW_TCR_HWVERID_MASK) { + case RTW_TCR_HWVERID_F: + sc->sc_hwverid = 'F'; + break; + case RTW_TCR_HWVERID_D: + sc->sc_hwverid = 'D'; + break; + default: + sc->sc_hwverid = '?'; + break; + } + + sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, + &sc->sc_irq_rid, + RF_ACTIVE | RF_SHAREABLE); + if (sc->sc_irq_res == NULL) { + device_printf(dev, "could not alloc irq res\n"); + return ENXIO; + } + + /* Allocate h/w desc blocks */ + rc = rtw_desc_blk_alloc(sc); + if (rc) + goto err; + + /* Allocate s/w desc blocks */ + rc = rtw_soft_blk_alloc(sc); + if (rc) + goto err; + + /* Reset the chip to a known state. */ + rc = rtw_reset(sc); + if (rc) { + device_printf(dev, "could not reset\n"); + goto err; + } + + sc->sc_rcr = RTW_READ(&sc->sc_regs, RTW_RCR); + + if ((sc->sc_rcr & RTW_RCR_9356SEL) != 0) + sc->sc_flags |= RTW_F_9356SROM; + + rc = rtw_srom_read(sc); + if (rc) + goto err; + + rc = rtw_srom_parse(sc); + if (rc) { + device_printf(dev, "malformed serial ROM\n"); + goto err; + } + + device_printf(dev, "%s PHY\n", + ((sc->sc_flags & RTW_F_DIGPHY) != 0) ? "digital" + : "analog"); + + device_printf(dev, "CS threshold %u\n", sc->sc_csthr); + + sc->sc_rf = rtw_rf_attach(sc, sc->sc_rfchipid, + sc->sc_flags & RTW_F_DIGPHY); + if (sc->sc_rf == NULL) { + device_printf(dev, "could not attach RF\n"); + rc = ENXIO; + goto err; + } + + sc->sc_phydelay = rtw_check_phydelay(&sc->sc_regs, sc->sc_rcr); + + RTW_DPRINTF(RTW_DEBUG_ATTACH, + ("%s: PHY delay %d\n", ifp->if_xname, sc->sc_phydelay)); + + if (sc->sc_locale == RTW_LOCALE_UNKNOWN) + rtw_identify_country(sc); + + rtw_init_channels(sc); + + rc = rtw_identify_sta(sc); + if (rc) + goto err; + + ifp->if_softc = sc; + ifp->if_flags = IFF_SIMPLEX | IFF_BROADCAST | IFF_MULTICAST; + ifp->if_init = rtw_init; + ifp->if_ioctl = rtw_ioctl; + ifp->if_start = rtw_start; + ifp->if_watchdog = rtw_watchdog; + ifq_set_maxlen(&ifp->if_snd, IFQ_MAXLEN); + ifq_set_ready(&ifp->if_snd); + + ic->ic_phytype = IEEE80211_T_DS; + ic->ic_opmode = IEEE80211_M_STA; + ic->ic_caps = IEEE80211_C_PMGT | + IEEE80211_C_IBSS | + IEEE80211_C_HOSTAP | + IEEE80211_C_MONITOR; + ic->ic_sup_rates[IEEE80211_MODE_11B] = rtw_rates_11b; + + /* initialize led callout */ + callout_init(&sc->sc_led_state.ls_fast_ch); + callout_init(&sc->sc_led_state.ls_slow_ch); + + ic->ic_ratectl.rc_st_ratectl_cap = IEEE80211_RATECTL_CAP_ONOE; + ic->ic_ratectl.rc_st_ratectl = IEEE80211_RATECTL_ONOE; + + /* + * Call MI attach routines. + */ + ieee80211_ifattach(&sc->sc_ic); + + /* Override some ieee80211 methods */ + rtw_set80211methods(&sc->sc_mtbl, &sc->sc_ic); + + /* + * possibly we should fill in our own sc_send_prresp, since + * the RTL8180 is probably sending probe responses in ad hoc + * mode. + */ + + /* complete initialization */ + ieee80211_media_init(&sc->sc_ic, rtw_media_change, rtw_media_status); + callout_init(&sc->sc_scan_ch); + + rtw_init_radiotap(sc); + + bpfattach_dlt(ifp, DLT_IEEE802_11_RADIO, + sizeof(struct ieee80211_frame) + 64, &sc->sc_radiobpf); + + rc = bus_setup_intr(dev, sc->sc_irq_res, INTR_MPSAFE, rtw_intr, sc, + &sc->sc_irq_handle, ifp->if_serializer); + if (rc) { + device_printf(dev, "can't set up interrupt\n"); + bpfdetach(ifp); + ieee80211_ifdetach(ic); + goto err; + } + + device_printf(dev, "hardware version %c\n", sc->sc_hwverid); + if (bootverbose) + ieee80211_announce(ic); + return 0; +err: + rtw_detach(dev); + return rc; +} + +int +rtw_detach(device_t dev) +{ + struct rtw_softc *sc = device_get_softc(dev); + struct ifnet *ifp = &sc->sc_ic.ic_if; + + if (device_is_attached(dev)) { + lwkt_serialize_enter(ifp->if_serializer); + + rtw_stop(sc, 1); + sc->sc_flags |= RTW_F_INVALID; + + callout_stop(&sc->sc_scan_ch); + bus_teardown_intr(dev, sc->sc_irq_res, sc->sc_irq_handle); + + lwkt_serialize_exit(ifp->if_serializer); + + ieee80211_ifdetach(&sc->sc_ic); + } + + if (sc->sc_rf != NULL) + rtw_rf_destroy(sc->sc_rf); + + if (sc->sc_srom.sr_content != NULL) + free(sc->sc_srom.sr_content, M_DEVBUF); + + if (sc->sc_irq_res != NULL) { + bus_release_resource(dev, SYS_RES_IRQ, sc->sc_irq_rid, + sc->sc_irq_res); + } + + rtw_soft_blk_free(sc); + rtw_desc_blk_free(sc); + return 0; +} + +static void +rtw_desc_dma_addr(void *arg, bus_dma_segment_t *seg, int nseg, int error) +{ + if (error) + return; + + KASSERT(nseg == 1, ("too many desc segments\n")); + *((uint32_t *)arg) = seg->ds_addr; /* XXX bus_addr_t */ +} + +static int +rtw_dma_alloc(struct rtw_softc *sc, bus_dma_tag_t *dmat, int len, + void **desc, uint32_t *phyaddr, bus_dmamap_t *dmamap) +{ + int error; + + error = bus_dma_tag_create(NULL, RTW_DESC_ALIGNMENT, 0, + BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, + NULL, NULL, len, 1, len, 0, dmat); + if (error) { + if_printf(&sc->sc_ic.ic_if, "could not alloc desc DMA tag"); + return error; + } + + error = bus_dmamem_alloc(*dmat, desc, BUS_DMA_WAITOK | BUS_DMA_ZERO, + dmamap); + if (error) { + if_printf(&sc->sc_ic.ic_if, "could not alloc desc DMA mem"); + return error; + } + + error = bus_dmamap_load(*dmat, *dmamap, *desc, len, + rtw_desc_dma_addr, phyaddr, BUS_DMA_WAITOK); + if (error) { + if_printf(&sc->sc_ic.ic_if, "could not load desc DMA mem"); + bus_dmamem_free(*dmat, *desc, *dmamap); + *desc = NULL; + return error; + } + return 0; +} + +static void +rtw_dma_free(struct rtw_softc *sc __unused, bus_dma_tag_t *dmat, void **desc, + bus_dmamap_t *dmamap) +{ + if (*desc != NULL) { + bus_dmamap_unload(*dmat, *dmamap); + bus_dmamem_free(*dmat, *desc, *dmamap); + *desc = NULL; + } + + if (*dmat != NULL) { + bus_dma_tag_destroy(*dmat); + *dmat = NULL; + } +} + +static void +rtw_txdesc_blk_free(struct rtw_softc *sc, int q_no) +{ + struct rtw_txdesc_blk *tdb = &sc->sc_txdesc_blk[q_no]; + + rtw_dma_free(sc, &tdb->tdb_dmat, (void **)&tdb->tdb_desc, + &tdb->tdb_dmamap); +} + +static int +rtw_txdesc_blk_alloc(struct rtw_softc *sc, int q_len, int q_no, + bus_size_t q_basereg) +{ + struct rtw_txdesc_blk *tdb = &sc->sc_txdesc_blk[q_no]; + int i, error; + + /* + * Setup TX h/w desc + */ + error = rtw_dma_alloc(sc, &tdb->tdb_dmat, + q_len * sizeof(*tdb->tdb_desc), + (void **)&tdb->tdb_desc, &tdb->tdb_base, + &tdb->tdb_dmamap); + if (error) { + printf("%dth tx\n", q_no); + return error; + } + tdb->tdb_basereg = q_basereg; + + tdb->tdb_ndesc = q_len; + for (i = 0; i < tdb->tdb_ndesc; ++i) + tdb->tdb_desc[i].td_next = htole32(RTW_NEXT_DESC(tdb, i)); + + return 0; +} + +static void +rtw_rxdesc_blk_free(struct rtw_softc *sc) +{ + struct rtw_rxdesc_blk *rdb = &sc->sc_rxdesc_blk; + + rtw_dma_free(sc, &rdb->rdb_dmat, (void **)&rdb->rdb_desc, + &rdb->rdb_dmamap); +} + +static int +rtw_rxdesc_blk_alloc(struct rtw_softc *sc, int q_len) +{ + struct rtw_rxdesc_blk *rdb = &sc->sc_rxdesc_blk; + int error; + + /* + * Setup RX h/w desc + */ + error = rtw_dma_alloc(sc, &rdb->rdb_dmat, + q_len * sizeof(*rdb->rdb_desc), + (void **)&rdb->rdb_desc, &rdb->rdb_base, + &rdb->rdb_dmamap); + if (error) { + printf("rx\n"); + } else { + rdb->rdb_ndesc = q_len; + } + + return error; +} + +static void +rtw_txsoft_blk_free(struct rtw_softc *sc, int n_sd, int q_no) +{ + struct rtw_txsoft_blk *tsb = &sc->sc_txsoft_blk[q_no]; + + if (tsb->tsb_desc != NULL) { + int i; + + for (i = 0; i < n_sd; ++i) { + bus_dmamap_destroy(sc->sc_txsoft_dmat, + tsb->tsb_desc[i].ts_dmamap); + } + free(tsb->tsb_desc, M_DEVBUF); + tsb->tsb_desc = NULL; + } +} + +static int +rtw_txsoft_blk_alloc(struct rtw_softc *sc, int q_len, int q_no, uint8_t q_poll) +{ + struct rtw_txsoft_blk *tsb = &sc->sc_txsoft_blk[q_no]; + int i, error; + + STAILQ_INIT(&tsb->tsb_dirtyq); + STAILQ_INIT(&tsb->tsb_freeq); + tsb->tsb_ndesc = q_len; + tsb->tsb_desc = malloc(q_len * sizeof(*tsb->tsb_desc), M_DEVBUF, + M_WAITOK | M_ZERO); + tsb->tsb_poll = q_poll; + + for (i = 0; i < tsb->tsb_ndesc; ++i) { + error = bus_dmamap_create(sc->sc_txsoft_dmat, 0, + &tsb->tsb_desc[i].ts_dmamap); + if (error) { + if_printf(&sc->sc_ic.ic_if, "could not create DMA map " + "for soft tx desc\n"); + rtw_txsoft_blk_free(sc, i, q_no); + return error; + } + } + return 0; +} + +static void +rtw_rxsoft_blk_free(struct rtw_softc *sc, int n_sd) +{ + if (sc->sc_rxsoft_free) { + int i; + + for (i = 0; i < n_sd; ++i) { + bus_dmamap_destroy(sc->sc_rxsoft_dmat, + sc->sc_rxsoft[i].rs_dmamap); + } + sc->sc_rxsoft_free = 0; + } +} + +static int +rtw_rxsoft_blk_alloc(struct rtw_softc *sc, int q_len) +{ + int i, error; + + sc->sc_rxsoft_free = 1; + + /* + * Setup RX s/w desc + */ + for (i = 0; i < q_len; ++i) { + error = bus_dmamap_create(sc->sc_rxsoft_dmat, 0, + &sc->sc_rxsoft[i].rs_dmamap); + if (error) { + if_printf(&sc->sc_ic.ic_if, "could not create DMA map " + "for soft rx desc\n"); + rtw_rxsoft_blk_free(sc, i); + return error; + } + } + return 0; +} + +#define TXQ_PARAM(q, poll, breg) \ + [RTW_TXPRI ## q] = { \ + .txq_len = RTW_TXQLEN ## q, \ + .txq_poll = poll, \ + .txq_basereg = breg \ + } +static const struct { + int txq_len; + uint8_t txq_poll; + bus_size_t txq_basereg; +} txq_params[RTW_NTXPRI] = { + TXQ_PARAM(LO, RTW_TPPOLL_LPQ | RTW_TPPOLL_SLPQ, RTW_TLPDA), + TXQ_PARAM(MD, RTW_TPPOLL_NPQ | RTW_TPPOLL_SNPQ, RTW_TNPDA), + TXQ_PARAM(HI, RTW_TPPOLL_HPQ | RTW_TPPOLL_SHPQ, RTW_THPDA), + TXQ_PARAM(BCN, RTW_TPPOLL_BQ | RTW_TPPOLL_SBQ, RTW_TBDA) +}; +#undef TXQ_PARAM + +static int +rtw_desc_blk_alloc(struct rtw_softc *sc) +{ + int i, error; + + /* Create h/w TX desc */ + for (i = 0; i < RTW_NTXPRI; ++i) { + error = rtw_txdesc_blk_alloc(sc, txq_params[i].txq_len, i, + txq_params[i].txq_basereg); + if (error) + return error; + } + + /* Create h/w RX desc */ + return rtw_rxdesc_blk_alloc(sc, RTW_RXQLEN); +} + +static void +rtw_desc_blk_free(struct rtw_softc *sc) +{ + int i; + + for (i = 0; i < RTW_NTXPRI; ++i) + rtw_txdesc_blk_free(sc, i); + rtw_rxdesc_blk_free(sc); +} + +static int +rtw_soft_blk_alloc(struct rtw_softc *sc) +{ + int i, error; + + /* Create DMA tag for TX mbuf */ + error = bus_dma_tag_create(NULL, 1, 0, + BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, + NULL, NULL, + MCLBYTES, RTW_MAXPKTSEGS, MCLBYTES, + 0, &sc->sc_txsoft_dmat); + if (error) { + if_printf(&sc->sc_ic.ic_if, "could not alloc txsoft DMA tag\n"); + return error; + } + + /* Create DMA tag for RX mbuf */ + error = bus_dma_tag_create(NULL, 1, 0, + BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, + NULL, NULL, + MCLBYTES, 1, MCLBYTES, + 0, &sc->sc_rxsoft_dmat); + if (error) { + if_printf(&sc->sc_ic.ic_if, "could not alloc rxsoft DMA tag\n"); + return error; + } + + /* Create a spare DMA map for RX mbuf */ + error = bus_dmamap_create(sc->sc_rxsoft_dmat, 0, &sc->sc_rxsoft_dmamap); + if (error) { + if_printf(&sc->sc_ic.ic_if, "could not alloc spare rxsoft " + "DMA map\n"); + bus_dma_tag_destroy(sc->sc_rxsoft_dmat); + sc->sc_rxsoft_dmat = NULL; + return error; + } + + /* Create s/w TX desc */ + for (i = 0; i < RTW_NTXPRI; ++i) { + error = rtw_txsoft_blk_alloc(sc, txq_params[i].txq_len, i, + txq_params[i].txq_poll); + if (error) + return error; + } + + /* Create s/w RX desc */ + return rtw_rxsoft_blk_alloc(sc, RTW_RXQLEN); +} + +static void +rtw_soft_blk_free(struct rtw_softc *sc) +{ + int i; + + for (i = 0; i < RTW_NTXPRI; ++i) + rtw_txsoft_blk_free(sc, txq_params[i].txq_len, i); + + rtw_rxsoft_blk_free(sc, RTW_RXQLEN); + + if (sc->sc_txsoft_dmat != NULL) { + bus_dma_tag_destroy(sc->sc_txsoft_dmat); + sc->sc_txsoft_dmat = NULL; + } + + if (sc->sc_rxsoft_dmat != NULL) { + bus_dmamap_destroy(sc->sc_rxsoft_dmat, sc->sc_rxsoft_dmamap); + bus_dma_tag_destroy(sc->sc_rxsoft_dmat); + sc->sc_rxsoft_dmat = NULL; + } +} + +/* + * Arguments in: + * + * paylen: payload length (no FCS, no WEP header) + * + * hdrlen: header length + * + * rate: MSDU speed, units 500kb/s + * + * flags: IEEE80211_F_SHPREAMBLE (use short preamble), + * IEEE80211_F_SHSLOT (use short slot length) + * + * Arguments out: + * + * d: 802.11 Duration field for RTS, + * 802.11 Duration field for data frame, + * PLCP Length for data frame, + * residual octets at end of data slot + */ +static int +rtw_compute_duration1(int len, int use_ack, uint32_t icflags, int rate, + struct rtw_duration *d) +{ + int pre, ctsrate; + int ack, bitlen, data_dur, remainder; + + /* + * RTS reserves medium for SIFS | CTS | SIFS | (DATA) | SIFS | ACK + * DATA reserves medium for SIFS | ACK + * + * XXXMYC: no ACK on multicast/broadcast or control packets + */ + + bitlen = len * 8; + + pre = IEEE80211_DUR_DS_SIFS; + if (icflags & IEEE80211_F_SHPREAMBLE) { + pre += IEEE80211_DUR_DS_SHORT_PREAMBLE + + IEEE80211_DUR_DS_FAST_PLCPHDR; + } else { + pre += IEEE80211_DUR_DS_LONG_PREAMBLE + + IEEE80211_DUR_DS_SLOW_PLCPHDR; + } + + d->d_residue = 0; + data_dur = (bitlen * 2) / rate; + remainder = (bitlen * 2) % rate; + if (remainder != 0) { + d->d_residue = (rate - remainder) / 16; + data_dur++; + } + + switch (rate) { + case 2: /* 1 Mb/s */ + case 4: /* 2 Mb/s */ + /* 1 - 2 Mb/s WLAN: send ACK/CTS at 1 Mb/s */ + ctsrate = 2; + break; + case 11: /* 5.5 Mb/s */ + case 22: /* 11 Mb/s */ + case 44: /* 22 Mb/s */ + /* 5.5 - 11 Mb/s WLAN: send ACK/CTS at 2 Mb/s */ + ctsrate = 4; + break; + default: + /* TBD */ + return -1; + } + + d->d_plcp_len = data_dur; + + ack = (use_ack) ? pre + (IEEE80211_DUR_DS_SLOW_ACK * 2) / ctsrate : 0; + + d->d_rts_dur = pre + (IEEE80211_DUR_DS_SLOW_CTS * 2) / ctsrate + + pre + data_dur + + ack; + + d->d_data_dur = ack; + return 0; +} + +/* + * Arguments in: + * + * wh: 802.11 header + * + * paylen: payload length (no FCS, no WEP header) + * + * rate: MSDU speed, units 500kb/s + * + * fraglen: fragment length, set to maximum (or higher) for no + * fragmentation + * + * flags: IEEE80211_F_PRIVACY (hardware adds WEP), + * IEEE80211_F_SHPREAMBLE (use short preamble), + * IEEE80211_F_SHSLOT (use short slot length) + * + * Arguments out: + * + * d0: 802.11 Duration fields (RTS/Data), PLCP Length, Service fields + * of first/only fragment + * + * dn: 802.11 Duration fields (RTS/Data), PLCP Length, Service fields + * of last fragment + * + * rtw_compute_duration assumes crypto-encapsulation, if any, + * has already taken place. + */ +static int +rtw_compute_duration(const struct ieee80211_frame_min *wh, + const struct ieee80211_key *wk, int len, + uint32_t icflags, int fraglen, int rate, + struct rtw_duration *d0, struct rtw_duration *dn, + int *npktp, int debug) +{ + int ack, rc; + int cryptolen, /* crypto overhead: header+trailer */ + firstlen, /* first fragment's payload + overhead length */ + hdrlen, /* header length w/o driver padding */ + lastlen, /* last fragment's payload length w/ overhead */ + lastlen0, /* last fragment's payload length w/o overhead */ + npkt, /* number of fragments */ + overlen, /* non-802.11 header overhead per fragment */ + paylen; /* payload length w/o overhead */ + + hdrlen = ieee80211_anyhdrsize((const void *)wh); + + /* Account for padding required by the driver. */ + if (icflags & IEEE80211_F_DATAPAD) + paylen = len - roundup(hdrlen, sizeof(u_int32_t)); + else + paylen = len - hdrlen; + + overlen = IEEE80211_CRC_LEN; + + if (wk != NULL) { + cryptolen = wk->wk_cipher->ic_header + + wk->wk_cipher->ic_trailer; + paylen -= cryptolen; + overlen += cryptolen; + } + + npkt = paylen / fraglen; + lastlen0 = paylen % fraglen; + + if (npkt == 0) { /* no fragments */ + lastlen = paylen + overlen; + } else if (lastlen0 != 0) { /* a short "tail" fragment */ + lastlen = lastlen0 + overlen; + npkt++; + } else { /* full-length "tail" fragment */ + lastlen = fraglen + overlen; + } + + if (npktp != NULL) + *npktp = npkt; + + if (npkt > 1) + firstlen = fraglen + overlen; + else + firstlen = paylen + overlen; + + if (debug) { + printf("%s: npkt %d firstlen %d lastlen0 %d lastlen %d " + "fraglen %d overlen %d len %d rate %d icflags %08x\n", + __func__, npkt, firstlen, lastlen0, lastlen, fraglen, + overlen, len, rate, icflags); + } + + ack = (!IEEE80211_IS_MULTICAST(wh->i_addr1) && + (wh->i_fc[1] & IEEE80211_FC0_TYPE_MASK) != + IEEE80211_FC0_TYPE_CTL); + + rc = rtw_compute_duration1(firstlen + hdrlen, ack, icflags, rate, d0); + if (rc == -1) + return rc; + + if (npkt <= 1) { + *dn = *d0; + return 0; + } + return rtw_compute_duration1(lastlen + hdrlen, ack, icflags, rate, dn); +} diff --git a/sys/dev/netif/rtw/rtwbitop.h b/sys/dev/netif/rtw/rtwbitop.h new file mode 100644 index 0000000000..2657285f22 --- /dev/null +++ b/sys/dev/netif/rtw/rtwbitop.h @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2004, 2005 David Young. All rights reserved. + * + * Programmed for NetBSD by David Young. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of David Young may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY David Young ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL David + * Young BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * $DragonFly: src/sys/dev/netif/rtw/Attic/rtwbitop.h,v 1.1 2006/09/03 07:37:58 sephe Exp $ + */ + +/* + * BIT(n): Return a bitmask with bit m set, where the least + * significant bit is bit 0. + * + * BITS(m, n): Return a bitmask with bits m through n, inclusive, + * set. It does not matter whether m>n or m<=n. + * The least significant bit is bit 0. + * + * A "bitfield" is a span of consecutive bits defined by a + * bitmask, where 1s select the bits in the bitfield. SHIFTIN, + * SHIFTOUT, and SHIFTOUT_MASK help read and write bitfields + * from device registers. + * + * SHIFTIN(v, mask): Left-shift bits `v' into the bitfield + * defined by `mask', and return them. No + * side-effects. + * + * SHIFTOUT(v, mask): Extract and return the bitfield selected + * by `mask' from `v', right-shifting the + * bits so that the rightmost selected bit + * is at bit 0. No side-effects. + * + * SHIFTOUT_MASK(mask): Right-shift the bits in `mask' so that + * the rightmost non-zero bit is at bit + * 0. This is useful for finding the + * greatest unsigned value that a bitfield + * can hold. No side-effects. Note that + * SHIFTOUT_MASK(m) = SHIFTOUT(m, m). + */ + +#ifndef _RTWBITOP_H +#define _RTWBITOP_H + +/* __BIT(n): nth bit, where __BIT(0) == 0x1. */ +#define __BIT(__n) (((__n) == 32) ? 0 : ((uint32_t)1 << (__n))) + +/* __BITS(m, n): bits m through n, m < n. */ +#define __BITS(__m, __n) \ + ((__BIT(MAX((__m), (__n)) + 1) - 1) ^ (__BIT(MIN((__m), (__n))) - 1)) + +/* find least significant bit that is set */ +#define __LOWEST_SET_BIT(__mask) ((((__mask) - 1) & (__mask)) ^ (__mask)) + +#define SHIFTOUT(__x, __mask) (((__x) & (__mask)) / __LOWEST_SET_BIT(__mask)) +#define SHIFTIN(__x, __mask) ((__x) * __LOWEST_SET_BIT(__mask)) +#define SHIFTOUT_MASK(__mask) SHIFTOUT((__mask), (__mask)) + +#endif /* !_RTWBITOP_H */ diff --git a/sys/dev/netif/rtw/rtwphy.c b/sys/dev/netif/rtw/rtwphy.c new file mode 100644 index 0000000000..2e69fba258 --- /dev/null +++ b/sys/dev/netif/rtw/rtwphy.c @@ -0,0 +1,812 @@ +/* + * Copyright (c) 2004, 2005 David Young. All rights reserved. + * + * Programmed for NetBSD by David Young. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of David Young may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY David Young ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL David + * Young BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * $NetBSD: rtwphy.c,v 1.9 2006/03/08 00:24:06 dyoung Exp $ + * $DragonFly: src/sys/dev/netif/rtw/rtwphy.c,v 1.1 2006/09/03 07:37:58 sephe Exp $ + */ + +/* + * Control the Philips SA2400 RF front-end and the baseband processor + * built into the Realtek RTL8180. + */ + +#include +#include +#include + +#include + +#include +#include +#include + +#include +#include + +#include "rtwbitop.h" +#include "rtwreg.h" +#include "max2820reg.h" +#include "sa2400reg.h" +#include "rtwvar.h" +#include "rtwphyio.h" +#include "rtwphy.h" + +static int rtw_max2820_pwrstate(struct rtw_rf *, enum rtw_pwrstate); +static int rtw_sa2400_pwrstate(struct rtw_rf *, enum rtw_pwrstate); + +#define GCT_WRITE(__gr, __addr, __val, __label) \ +do { \ + if (rtw_rfbus_write(&(__gr)->gr_bus, RTW_RFCHIPID_GCT, \ + (__addr), (__val)) == -1) \ + goto __label; \ +} while(0) + +static int +rtw_bbp_preinit(struct rtw_regs *regs, u_int antatten0, int dflantb, u_int freq) +{ + u_int antatten = antatten0; + + if (dflantb) + antatten |= RTW_BBP_ANTATTEN_DFLANTB; + if (freq == 2484) /* channel 14 */ + antatten |= RTW_BBP_ANTATTEN_CHAN14; + return rtw_bbp_write(regs, RTW_BBP_ANTATTEN, antatten); +} + +static int +rtw_bbp_init(struct rtw_regs *regs, struct rtw_bbpset *bb, int antdiv, + int dflantb, uint8_t cs_threshold, u_int freq) +{ + int rc; + uint32_t sys2, sys3; + + sys2 = bb->bb_sys2; + if (antdiv) + sys2 |= RTW_BBP_SYS2_ANTDIV; + sys3 = bb->bb_sys3 | SHIFTIN(cs_threshold, RTW_BBP_SYS3_CSTHRESH_MASK); + +#define RTW_BBP_WRITE_OR_RETURN(reg, val) \ + if ((rc = rtw_bbp_write(regs, reg, val)) != 0) \ + return rc; + + RTW_BBP_WRITE_OR_RETURN(RTW_BBP_SYS1, bb->bb_sys1); + RTW_BBP_WRITE_OR_RETURN(RTW_BBP_TXAGC, bb->bb_txagc); + RTW_BBP_WRITE_OR_RETURN(RTW_BBP_LNADET, bb->bb_lnadet); + RTW_BBP_WRITE_OR_RETURN(RTW_BBP_IFAGCINI, bb->bb_ifagcini); + RTW_BBP_WRITE_OR_RETURN(RTW_BBP_IFAGCLIMIT, bb->bb_ifagclimit); + RTW_BBP_WRITE_OR_RETURN(RTW_BBP_IFAGCDET, bb->bb_ifagcdet); + + if ((rc = rtw_bbp_preinit(regs, bb->bb_antatten, dflantb, freq)) != 0) + return rc; + + RTW_BBP_WRITE_OR_RETURN(RTW_BBP_TRL, bb->bb_trl); + RTW_BBP_WRITE_OR_RETURN(RTW_BBP_SYS2, sys2); + RTW_BBP_WRITE_OR_RETURN(RTW_BBP_SYS3, sys3); + RTW_BBP_WRITE_OR_RETURN(RTW_BBP_CHESTLIM, bb->bb_chestlim); + RTW_BBP_WRITE_OR_RETURN(RTW_BBP_CHSQLIM, bb->bb_chsqlim); + return 0; +} + +static int +rtw_sa2400_txpower(struct rtw_rf *rf, uint8_t opaque_txpower) +{ + struct rtw_sa2400 *sa = (struct rtw_sa2400 *)rf; + struct rtw_rfbus *bus = &sa->sa_bus; + + return rtw_rfbus_write(bus, RTW_RFCHIPID_PHILIPS, SA2400_TX, + opaque_txpower); +} + +/* make sure we're using the same settings as the reference driver */ +static void +verify_syna(u_int freq, uint32_t val) +{ + uint32_t expected_val = ~val; + + switch (freq) { + case 2412: + expected_val = 0x0000096c; /* ch 1 */ + break; + case 2417: + expected_val = 0x00080970; /* ch 2 */ + break; + case 2422: + expected_val = 0x00100974; /* ch 3 */ + break; + case 2427: + expected_val = 0x00180978; /* ch 4 */ + break; + case 2432: + expected_val = 0x00000980; /* ch 5 */ + break; + case 2437: + expected_val = 0x00080984; /* ch 6 */ + break; + case 2442: + expected_val = 0x00100988; /* ch 7 */ + break; + case 2447: + expected_val = 0x0018098c; /* ch 8 */ + break; + case 2452: + expected_val = 0x00000994; /* ch 9 */ + break; + case 2457: + expected_val = 0x00080998; /* ch 10 */ + break; + case 2462: + expected_val = 0x0010099c; /* ch 11 */ + break; + case 2467: + expected_val = 0x001809a0; /* ch 12 */ + break; + case 2472: + expected_val = 0x000009a8; /* ch 13 */ + break; + case 2484: + expected_val = 0x000009b4; /* ch 14 */ + break; + } + KKASSERT(val == expected_val); +} + +/* freq is in MHz */ +static int +rtw_sa2400_tune(struct rtw_rf *rf, u_int freq) +{ + struct rtw_sa2400 *sa = (struct rtw_sa2400 *)rf; + struct rtw_rfbus *bus = &sa->sa_bus; + int rc; + uint32_t syna, synb, sync; + int n, nf; + + /* + * XO = 44MHz, R = 11, hence N is in units of XO / R = 4MHz. + * + * The channel spacing (5MHz) is not divisible by 4MHz, so + * we set the fractional part of N to compensate. + */ + n = freq / 4; + nf = (freq % 4) * 2; + + syna = SHIFTIN(nf, SA2400_SYNA_NF_MASK) | + SHIFTIN(n, SA2400_SYNA_N_MASK); + verify_syna(freq, syna); + + /* + * Divide the 44MHz crystal down to 4MHz. Set the fractional + * compensation charge pump value to agree with the fractional + * modulus. + */ + synb = SHIFTIN(11, SA2400_SYNB_R_MASK) | SA2400_SYNB_L_NORMAL | + SA2400_SYNB_ON | SA2400_SYNB_ONE | + SHIFTIN(80, SA2400_SYNB_FC_MASK); /* agrees w/ SA2400_SYNA_FM = 0 */ + + sync = SA2400_SYNC_CP_NORMAL; + + rc = rtw_rfbus_write(bus, RTW_RFCHIPID_PHILIPS, SA2400_SYNA, syna); + if (rc != 0) + return rc; + + rc = rtw_rfbus_write(bus, RTW_RFCHIPID_PHILIPS, SA2400_SYNB, synb); + if (rc != 0) + return rc; + + rc = rtw_rfbus_write(bus, RTW_RFCHIPID_PHILIPS, SA2400_SYNC, sync); + if (rc != 0) + return rc; + + return rtw_rfbus_write(bus, RTW_RFCHIPID_PHILIPS, SA2400_SYND, 0x0); +} + +static int +rtw_sa2400_pwrstate(struct rtw_rf *rf, enum rtw_pwrstate power) +{ + struct rtw_sa2400 *sa = (struct rtw_sa2400 *)rf; + struct rtw_rfbus *bus = &sa->sa_bus; + uint32_t opmode; + + opmode = SA2400_OPMODE_DEFAULTS; + switch (power) { + case RTW_ON: + opmode |= SA2400_OPMODE_MODE_TXRX; + break; + case RTW_SLEEP: + opmode |= SA2400_OPMODE_MODE_WAIT; + break; + case RTW_OFF: + opmode |= SA2400_OPMODE_MODE_SLEEP; + break; + } + + if (sa->sa_digphy) + opmode |= SA2400_OPMODE_DIGIN; + + return rtw_rfbus_write(bus, RTW_RFCHIPID_PHILIPS, SA2400_OPMODE, + opmode); +} + +static int +rtw_sa2400_manrx_init(struct rtw_sa2400 *sa) +{ + uint32_t manrx; + + /* + * XXX we are not supposed to be in RXMGC mode when we do this? + */ + manrx = SA2400_MANRX_AHSN; + manrx |= SA2400_MANRX_TEN; + manrx |= SHIFTIN(1023, SA2400_MANRX_RXGAIN_MASK); + + return rtw_rfbus_write(&sa->sa_bus, RTW_RFCHIPID_PHILIPS, SA2400_MANRX, + manrx); +} + +static int +rtw_sa2400_vcocal_start(struct rtw_sa2400 *sa, int start) +{ + uint32_t opmode; + + opmode = SA2400_OPMODE_DEFAULTS; + if (start) + opmode |= SA2400_OPMODE_MODE_VCOCALIB; + else + opmode |= SA2400_OPMODE_MODE_SLEEP; + + if (sa->sa_digphy) + opmode |= SA2400_OPMODE_DIGIN; + + return rtw_rfbus_write(&sa->sa_bus, RTW_RFCHIPID_PHILIPS, SA2400_OPMODE, + opmode); +} + +static int +rtw_sa2400_vco_calibration(struct rtw_sa2400 *sa) +{ + int rc; + + /* calibrate VCO */ + if ((rc = rtw_sa2400_vcocal_start(sa, 1)) != 0) + return rc; + DELAY(2200); /* 2.2 milliseconds */ + /* XXX superfluous: SA2400 automatically entered SLEEP mode. */ + return rtw_sa2400_vcocal_start(sa, 0); +} + +static int +rtw_sa2400_filter_calibration(struct rtw_sa2400 *sa) +{ + uint32_t opmode; + + opmode = SA2400_OPMODE_DEFAULTS | SA2400_OPMODE_MODE_FCALIB; + if (sa->sa_digphy) + opmode |= SA2400_OPMODE_DIGIN; + + return rtw_rfbus_write(&sa->sa_bus, RTW_RFCHIPID_PHILIPS, SA2400_OPMODE, + opmode); +} + +static int +rtw_sa2400_dc_calibration(struct rtw_sa2400 *sa) +{ + struct rtw_rf *rf = &sa->sa_rf; + int rc; + uint32_t dccal; + + rf->rf_continuous_tx_cb(rf->rf_continuous_tx_arg, 1); + + dccal = SA2400_OPMODE_DEFAULTS | SA2400_OPMODE_MODE_TXRX; + + rc = rtw_rfbus_write(&sa->sa_bus, RTW_RFCHIPID_PHILIPS, SA2400_OPMODE, + dccal); + if (rc != 0) + return rc; + + /* + * DCALIB after being in Tx mode for 5 microseconds + */ + DELAY(5); + + dccal &= ~SA2400_OPMODE_MODE_MASK; + dccal |= SA2400_OPMODE_MODE_DCALIB; + + rc = rtw_rfbus_write(&sa->sa_bus, RTW_RFCHIPID_PHILIPS, SA2400_OPMODE, + dccal); + if (rc != 0) + return rc; + + DELAY(20); /* calibration takes at most 20 microseconds */ + + rf->rf_continuous_tx_cb(rf->rf_continuous_tx_arg, 0); + return 0; +} + +static int +rtw_sa2400_agc_init(struct rtw_sa2400 *sa) +{ + uint32_t agc; + + agc = SHIFTIN(25, SA2400_AGC_MAXGAIN_MASK); + agc |= SHIFTIN(7, SA2400_AGC_BBPDELAY_MASK); + agc |= SHIFTIN(15, SA2400_AGC_LNADELAY_MASK); + agc |= SHIFTIN(27, SA2400_AGC_RXONDELAY_MASK); + + return rtw_rfbus_write(&sa->sa_bus, RTW_RFCHIPID_PHILIPS, SA2400_AGC, + agc); +} + +static void +rtw_sa2400_destroy(struct rtw_rf *rf) +{ + struct rtw_sa2400 *sa = (struct rtw_sa2400 *)rf; + + memset(sa, 0, sizeof(*sa)); + free(sa, M_DEVBUF); +} + +static int +rtw_sa2400_calibrate(struct rtw_rf *rf, u_int freq) +{ + struct rtw_sa2400 *sa = (struct rtw_sa2400 *)rf; + int i, rc; + + /* XXX reference driver calibrates VCO twice. Is it a bug? */ + for (i = 0; i < 2; i++) { + if ((rc = rtw_sa2400_vco_calibration(sa)) != 0) + return rc; + } + /* VCO calibration erases synthesizer registers, so re-tune */ + if ((rc = rtw_sa2400_tune(rf, freq)) != 0) + return rc; + if ((rc = rtw_sa2400_filter_calibration(sa)) != 0) + return rc; + /* analog PHY needs DC calibration */ + if (!sa->sa_digphy) + return rtw_sa2400_dc_calibration(sa); + return 0; +} + +static int +rtw_sa2400_init(struct rtw_rf *rf, u_int freq, uint8_t opaque_txpower, + enum rtw_pwrstate power) +{ + struct rtw_sa2400 *sa = (struct rtw_sa2400 *)rf; + int rc; + + if ((rc = rtw_sa2400_txpower(rf, opaque_txpower)) != 0) + return rc; + + /* skip configuration if it's time to sleep or to power-down. */ + if (power == RTW_SLEEP || power == RTW_OFF) + return rtw_sa2400_pwrstate(rf, power); + + /* go to sleep for configuration */ + if ((rc = rtw_sa2400_pwrstate(rf, RTW_SLEEP)) != 0) + return rc; + + if ((rc = rtw_sa2400_tune(rf, freq)) != 0) + return rc; + if ((rc = rtw_sa2400_agc_init(sa)) != 0) + return rc; + if ((rc = rtw_sa2400_manrx_init(sa)) != 0) + return rc; + if ((rc = rtw_sa2400_calibrate(rf, freq)) != 0) + return rc; + + /* enter Tx/Rx mode */ + return rtw_sa2400_pwrstate(rf, power); +} + +struct rtw_rf * +rtw_sa2400_create(struct rtw_regs *regs, rtw_rf_write_t rf_write, int digphy) +{ + struct rtw_sa2400 *sa; + struct rtw_rfbus *bus; + struct rtw_rf *rf; + struct rtw_bbpset *bb; + + sa = malloc(sizeof(*sa), M_DEVBUF, M_WAITOK | M_ZERO); + + sa->sa_digphy = digphy; + + rf = &sa->sa_rf; + bus = &sa->sa_bus; + + rf->rf_init = rtw_sa2400_init; + rf->rf_destroy = rtw_sa2400_destroy; + rf->rf_txpower = rtw_sa2400_txpower; + rf->rf_tune = rtw_sa2400_tune; + rf->rf_pwrstate = rtw_sa2400_pwrstate; + bb = &rf->rf_bbpset; + + /* XXX magic */ + bb->bb_antatten = RTW_BBP_ANTATTEN_PHILIPS_MAGIC; + bb->bb_chestlim = 0x00; + bb->bb_chsqlim = 0xa0; + bb->bb_ifagcdet = 0x64; + bb->bb_ifagcini = 0x90; + bb->bb_ifagclimit = 0x1a; + bb->bb_lnadet = 0xe0; + bb->bb_sys1 = 0x98; + bb->bb_sys2 = 0x47; + bb->bb_sys3 = 0x90; + bb->bb_trl = 0x88; + bb->bb_txagc = 0x38; + + bus->b_regs = regs; + bus->b_write = rf_write; + + return &sa->sa_rf; +} + +static int +rtw_grf5101_txpower(struct rtw_rf *rf, uint8_t opaque_txpower) +{ + struct rtw_grf5101 *gr = (struct rtw_grf5101 *)rf; + + GCT_WRITE(gr, 0x15, 0, err); + GCT_WRITE(gr, 0x06, opaque_txpower, err); + GCT_WRITE(gr, 0x15, 0x10, err); + GCT_WRITE(gr, 0x15, 0x00, err); + return 0; +err: + return -1; +} + +static int +rtw_grf5101_pwrstate(struct rtw_rf *rf, enum rtw_pwrstate power) +{ + struct rtw_grf5101 *gr = (struct rtw_grf5101 *)rf; + + switch (power) { + case RTW_OFF: + case RTW_SLEEP: + GCT_WRITE(gr, 0x07, 0x0000, err); + GCT_WRITE(gr, 0x1f, 0x0045, err); + GCT_WRITE(gr, 0x1f, 0x0005, err); + GCT_WRITE(gr, 0x00, 0x08e4, err); + default: + break; + case RTW_ON: + GCT_WRITE(gr, 0x1f, 0x0001, err); + DELAY(10); + GCT_WRITE(gr, 0x1f, 0x0001, err); + DELAY(10); + GCT_WRITE(gr, 0x1f, 0x0041, err); + DELAY(10); + GCT_WRITE(gr, 0x1f, 0x0061, err); + DELAY(10); + GCT_WRITE(gr, 0x00, 0x0ae4, err); + DELAY(10); + GCT_WRITE(gr, 0x07, 0x1000, err); + DELAY(100); + break; + } + + return 0; +err: + return -1; +} + +static int +rtw_grf5101_tune(struct rtw_rf *rf, u_int freq) +{ + int channel; + struct rtw_grf5101 *gr = (struct rtw_grf5101 *)rf; + + if (freq == 2484) { + channel = 14; + } else if ((channel = (freq - 2412) / 5 + 1) < 1 || channel > 13) { + RTW_DPRINTF(RTW_DEBUG_PHY, + ("%s: invalid channel %d (freq %d)\n", __func__, channel, + freq)); + return -1; + } + + GCT_WRITE(gr, 0x07, 0, err); + GCT_WRITE(gr, 0x0b, channel - 1, err); + GCT_WRITE(gr, 0x07, 0x1000, err); + return 0; +err: + return -1; +} + +static int +rtw_grf5101_init(struct rtw_rf *rf, u_int freq, uint8_t opaque_txpower, + enum rtw_pwrstate power) +{ + int rc; + struct rtw_grf5101 *gr = (struct rtw_grf5101 *)rf; + + /* + * These values have been derived from the rtl8180-sa2400 + * Linux driver. It is unknown what they all do, GCT refuse + * to release any documentation so these are more than + * likely sub optimal settings + */ + + GCT_WRITE(gr, 0x01, 0x1a23, err); + GCT_WRITE(gr, 0x02, 0x4971, err); + GCT_WRITE(gr, 0x03, 0x41de, err); + GCT_WRITE(gr, 0x04, 0x2d80, err); + + GCT_WRITE(gr, 0x05, 0x61ff, err); + + GCT_WRITE(gr, 0x06, 0x0, err); + + GCT_WRITE(gr, 0x08, 0x7533, err); + GCT_WRITE(gr, 0x09, 0xc401, err); + GCT_WRITE(gr, 0x0a, 0x0, err); + GCT_WRITE(gr, 0x0c, 0x1c7, err); + GCT_WRITE(gr, 0x0d, 0x29d3, err); + GCT_WRITE(gr, 0x0e, 0x2e8, err); + GCT_WRITE(gr, 0x10, 0x192, err); + GCT_WRITE(gr, 0x11, 0x248, err); + GCT_WRITE(gr, 0x12, 0x0, err); + GCT_WRITE(gr, 0x13, 0x20c4, err); + GCT_WRITE(gr, 0x14, 0xf4fc, err); + GCT_WRITE(gr, 0x15, 0x0, err); + GCT_WRITE(gr, 0x16, 0x1500, err); + + if ((rc = rtw_grf5101_txpower(rf, opaque_txpower)) != 0) + return rc; + + if ((rc = rtw_grf5101_tune(rf, freq)) != 0) + return rc; + + return 0; +err: + return -1; +} + +static void +rtw_grf5101_destroy(struct rtw_rf *rf) +{ + struct rtw_grf5101 *gr = (struct rtw_grf5101 *)rf; + + memset(gr, 0, sizeof(*gr)); + free(gr, M_DEVBUF); +} + +struct rtw_rf * +rtw_grf5101_create(struct rtw_regs *regs, rtw_rf_write_t rf_write, int digphy) +{ + struct rtw_grf5101 *gr; + struct rtw_rfbus *bus; + struct rtw_rf *rf; + struct rtw_bbpset *bb; + + gr = malloc(sizeof(*gr), M_DEVBUF, M_WAITOK | M_ZERO); + + rf = &gr->gr_rf; + bus = &gr->gr_bus; + + rf->rf_init = rtw_grf5101_init; + rf->rf_destroy = rtw_grf5101_destroy; + rf->rf_txpower = rtw_grf5101_txpower; + rf->rf_tune = rtw_grf5101_tune; + rf->rf_pwrstate = rtw_grf5101_pwrstate; + bb = &rf->rf_bbpset; + + /* XXX magic */ + bb->bb_antatten = RTW_BBP_ANTATTEN_GCT_MAGIC; + bb->bb_chestlim = 0x00; + bb->bb_chsqlim = 0xa0; + bb->bb_ifagcdet = 0x64; + bb->bb_ifagcini = 0x90; + bb->bb_ifagclimit = 0x1e; + bb->bb_lnadet = 0xc0; + bb->bb_sys1 = 0xa8; + bb->bb_sys2 = 0x47; + bb->bb_sys3 = 0x9b; + bb->bb_trl = 0x88; + bb->bb_txagc = 0x08; + + bus->b_regs = regs; + bus->b_write = rf_write; + + return &gr->gr_rf; +} + +/* freq is in MHz */ +static int +rtw_max2820_tune(struct rtw_rf *rf, u_int freq) +{ + struct rtw_max2820 *mx = (struct rtw_max2820 *)rf; + struct rtw_rfbus *bus = &mx->mx_bus; + + if (freq < 2400 || freq > 2499) + return -1; + + return rtw_rfbus_write(bus, RTW_RFCHIPID_MAXIM, MAX2820_CHANNEL, + SHIFTIN(freq - 2400, MAX2820_CHANNEL_CF_MASK)); +} + +static void +rtw_max2820_destroy(struct rtw_rf *rf) +{ + struct rtw_max2820 *mx = (struct rtw_max2820 *)rf; + + memset(mx, 0, sizeof(*mx)); + free(mx, M_DEVBUF); +} + +static int +rtw_max2820_init(struct rtw_rf *rf, u_int freq, uint8_t opaque_txpower, + enum rtw_pwrstate power) +{ + struct rtw_max2820 *mx = (struct rtw_max2820 *)rf; + struct rtw_rfbus *bus = &mx->mx_bus; + int rc; + + rc = rtw_rfbus_write(bus, RTW_RFCHIPID_MAXIM, MAX2820_TEST, + MAX2820_TEST_DEFAULT); + if (rc != 0) + return rc; + + rc = rtw_rfbus_write(bus, RTW_RFCHIPID_MAXIM, MAX2820_ENABLE, + MAX2820_ENABLE_DEFAULT); + if (rc != 0) + return rc; + + /* skip configuration if it's time to sleep or to power-down. */ + if ((rc = rtw_max2820_pwrstate(rf, power)) != 0) + return rc; + else if (power == RTW_OFF || power == RTW_SLEEP) + return 0; + + rc = rtw_rfbus_write(bus, RTW_RFCHIPID_MAXIM, MAX2820_SYNTH, + MAX2820_SYNTH_R_44MHZ); + if (rc != 0) + return rc; + + if ((rc = rtw_max2820_tune(rf, freq)) != 0) + return rc; + + /* + * XXX The MAX2820 datasheet indicates that 1C and 2C should not + * be changed from 7, however, the reference driver sets them + * to 4 and 1, respectively. + */ + rc = rtw_rfbus_write(bus, RTW_RFCHIPID_MAXIM, MAX2820_RECEIVE, + MAX2820_RECEIVE_DL_DEFAULT | + SHIFTIN(4, MAX2820A_RECEIVE_1C_MASK) | + SHIFTIN(1, MAX2820A_RECEIVE_2C_MASK)); + if (rc != 0) + return rc; + + return rtw_rfbus_write(bus, RTW_RFCHIPID_MAXIM, MAX2820_TRANSMIT, + MAX2820_TRANSMIT_PA_DEFAULT); +} + +static int +rtw_max2820_txpower(struct rtw_rf *rf, uint8_t opaque_txpower) +{ + /* TBD */ + return 0; +} + +static int +rtw_max2820_pwrstate(struct rtw_rf *rf, enum rtw_pwrstate power) +{ + uint32_t enable; + struct rtw_max2820 *mx; + struct rtw_rfbus *bus; + + mx = (struct rtw_max2820 *)rf; + bus = &mx->mx_bus; + + switch (power) { + case RTW_OFF: + case RTW_SLEEP: + default: + enable = 0x0; + break; + case RTW_ON: + enable = MAX2820_ENABLE_DEFAULT; + break; + } + return rtw_rfbus_write(bus, RTW_RFCHIPID_MAXIM, MAX2820_ENABLE, enable); +} + +struct rtw_rf * +rtw_max2820_create(struct rtw_regs *regs, rtw_rf_write_t rf_write, int is_a) +{ + struct rtw_max2820 *mx; + struct rtw_rfbus *bus; + struct rtw_rf *rf; + struct rtw_bbpset *bb; + + mx = malloc(sizeof(*mx), M_DEVBUF, M_WAITOK | M_ZERO); + + mx->mx_is_a = is_a; + + rf = &mx->mx_rf; + bus = &mx->mx_bus; + + rf->rf_init = rtw_max2820_init; + rf->rf_destroy = rtw_max2820_destroy; + rf->rf_txpower = rtw_max2820_txpower; + rf->rf_tune = rtw_max2820_tune; + rf->rf_pwrstate = rtw_max2820_pwrstate; + bb = &rf->rf_bbpset; + + /* XXX magic */ + bb->bb_antatten = RTW_BBP_ANTATTEN_MAXIM_MAGIC; + bb->bb_chestlim = 0; + bb->bb_chsqlim = 159; + bb->bb_ifagcdet = 100; + bb->bb_ifagcini = 144; + bb->bb_ifagclimit = 26; + bb->bb_lnadet = 248; + bb->bb_sys1 = 136; + bb->bb_sys2 = 71; + bb->bb_sys3 = 155; + bb->bb_trl = 136; + bb->bb_txagc = 8; + + bus->b_regs = regs; + bus->b_write = rf_write; + + return &mx->mx_rf; +} + +/* freq is in MHz */ +int +rtw_phy_init(struct rtw_regs *regs, struct rtw_rf *rf, uint8_t opaque_txpower, + uint8_t cs_threshold, u_int freq, int antdiv, int dflantb, + enum rtw_pwrstate power) +{ + int rc; + RTW_DPRINTF(RTW_DEBUG_PHY, + ("%s: txpower %u csthresh %u freq %u antdiv %u dflantb %u " + "pwrstate %s\n", __func__, opaque_txpower, cs_threshold, freq, + antdiv, dflantb, rtw_pwrstate_string(power))); + + /* XXX is this really necessary? */ + if ((rc = rtw_rf_txpower(rf, opaque_txpower)) != 0) + return rc; + + rc = rtw_bbp_preinit(regs, rf->rf_bbpset.bb_antatten, dflantb, freq); + if (rc != 0) + return rc; + + if ((rc = rtw_rf_tune(rf, freq)) != 0) + return rc; + + /* initialize RF */ + if ((rc = rtw_rf_init(rf, freq, opaque_txpower, power)) != 0) + return rc; +#if 0 /* what is this redundant tx power setting here for? */ + if ((rc = rtw_rf_txpower(rf, opaque_txpower)) != 0) + return rc; +#endif + return rtw_bbp_init(regs, &rf->rf_bbpset, antdiv, dflantb, cs_threshold, freq); +} diff --git a/sys/dev/netif/rtw/rtwphy.h b/sys/dev/netif/rtw/rtwphy.h new file mode 100644 index 0000000000..08ac4f7102 --- /dev/null +++ b/sys/dev/netif/rtw/rtwphy.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2004, 2005 David Young. All rights reserved. + * + * Programmed for NetBSD by David Young. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of David Young may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY David Young ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL David + * Young BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * $NetBSD: rtwphy.h,v 1.5 2005/12/29 22:27:17 dyoung Exp $ + * $DragonFly: src/sys/dev/netif/rtw/rtwphy.h,v 1.1 2006/09/03 07:37:58 sephe Exp $ + */ +#ifndef _DEV_IC_RTWPHY_H +#define _DEV_IC_RTWPHY_H + +struct rtw_rf *rtw_grf5101_create(struct rtw_regs *, rtw_rf_write_t, int); +struct rtw_rf *rtw_sa2400_create(struct rtw_regs *, rtw_rf_write_t, int); +struct rtw_rf *rtw_max2820_create(struct rtw_regs *, rtw_rf_write_t, int); + +int rtw_phy_init(struct rtw_regs *, struct rtw_rf *, uint8_t, + uint8_t, u_int, int, int, enum rtw_pwrstate); + +#endif /* _DEV_IC_RTWPHY_H */ diff --git a/sys/dev/netif/rtw/rtwphyio.c b/sys/dev/netif/rtw/rtwphyio.c new file mode 100644 index 0000000000..b60cbe3616 --- /dev/null +++ b/sys/dev/netif/rtw/rtwphyio.c @@ -0,0 +1,361 @@ +/* + * Copyright (c) 2004, 2005 David Young. All rights reserved. + * + * Programmed for NetBSD by David Young. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of David Young may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY David Young ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL David + * Young BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * $NetBSD: rtwphyio.c,v 1.11 2006/03/08 00:24:06 dyoung Exp $ + * $DragonFly: src/sys/dev/netif/rtw/rtwphyio.c,v 1.1 2006/09/03 07:37:58 sephe Exp $ + */ + +/* + * Control input/output with the Philips SA2400 RF front-end and + * the baseband processor built into the Realtek RTL8180. + */ + +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include +#include + +#include "rtwbitop.h" +#include "rtwreg.h" +#include "max2820reg.h" +#include "sa2400reg.h" +#include "si4136reg.h" +#include "rtwvar.h" +#include "rtwphyio.h" +#include "rtwphy.h" + +static int rtw_macbangbits_timeout = 100; + +uint8_t +rtw_bbp_read(struct rtw_regs *regs, u_int addr) +{ + KKASSERT((addr & ~SHIFTOUT_MASK(RTW_BB_ADDR_MASK)) == 0); + RTW_WRITE(regs, RTW_BB, + SHIFTIN(addr, RTW_BB_ADDR_MASK) | RTW_BB_RD_MASK | RTW_BB_WR_MASK); + DELAY(10); /* XXX */ + RTW_WBR(regs, RTW_BB, RTW_BB); + return SHIFTOUT(RTW_READ(regs, RTW_BB), RTW_BB_RD_MASK); +} + +int +rtw_bbp_write(struct rtw_regs *regs, u_int addr, u_int val) +{ +#define BBP_WRITE_ITERS 50 +#define BBP_WRITE_DELAY 1 + int i; + uint32_t wrbbp, rdbbp; + + RTW_DPRINTF(RTW_DEBUG_PHYIO, + ("%s: bbp[%u] <- %u\n", __func__, addr, val)); + + KKASSERT((addr & ~SHIFTOUT_MASK(RTW_BB_ADDR_MASK)) == 0); + KKASSERT((val & ~SHIFTOUT_MASK(RTW_BB_WR_MASK)) == 0); + + wrbbp = SHIFTIN(addr, RTW_BB_ADDR_MASK) | RTW_BB_WREN | + SHIFTIN(val, RTW_BB_WR_MASK) | RTW_BB_RD_MASK, + + rdbbp = SHIFTIN(addr, RTW_BB_ADDR_MASK) | + RTW_BB_WR_MASK | RTW_BB_RD_MASK; + + RTW_DPRINTF(RTW_DEBUG_PHYIO, + ("%s: rdbbp = %#08x, wrbbp = %#08x\n", __func__, rdbbp, wrbbp)); + + for (i = BBP_WRITE_ITERS; --i >= 0; ) { + RTW_RBW(regs, RTW_BB, RTW_BB); + RTW_WRITE(regs, RTW_BB, wrbbp); + RTW_SYNC(regs, RTW_BB, RTW_BB); + RTW_WRITE(regs, RTW_BB, rdbbp); + RTW_SYNC(regs, RTW_BB, RTW_BB); + DELAY(BBP_WRITE_DELAY); /* 1 microsecond */ + if (SHIFTOUT(RTW_READ(regs, RTW_BB), + RTW_BB_RD_MASK) == val) { + RTW_DPRINTF(RTW_DEBUG_PHYIO, + ("%s: finished in %dus\n", __func__, + BBP_WRITE_DELAY * (BBP_WRITE_ITERS - i))); + return 0; + } + DELAY(BBP_WRITE_DELAY); /* again */ + } + printf("%s: timeout\n", __func__); + return -1; +} + +/* Help rtw_rf_hostwrite bang bits to RF over 3-wire interface. */ +static __inline void +rtw_rf_hostbangbits(struct rtw_regs *regs, uint32_t bits, int lo_to_hi, + u_int nbits) +{ + int i; + uint32_t mask, reg; + + KKASSERT(nbits <= 32); + + RTW_DPRINTF(RTW_DEBUG_PHYIO, + ("%s: %u bits, %#08x, %s\n", __func__, nbits, bits, + (lo_to_hi) ? "lo to hi" : "hi to lo")); + + reg = RTW_PHYCFG_HST; + RTW_WRITE(regs, RTW_PHYCFG, reg); + RTW_SYNC(regs, RTW_PHYCFG, RTW_PHYCFG); + + if (lo_to_hi) + mask = 0x1; + else + mask = 1 << (nbits - 1); + + for (i = 0; i < nbits; i++) { + RTW_DPRINTF(RTW_DEBUG_PHYBITIO, + ("%s: bits %#08x mask %#08x -> bit %#08x\n", + __func__, bits, mask, bits & mask)); + + if ((bits & mask) != 0) + reg |= RTW_PHYCFG_HST_DATA; + else + reg &= ~RTW_PHYCFG_HST_DATA; + + reg |= RTW_PHYCFG_HST_CLK; + RTW_WRITE(regs, RTW_PHYCFG, reg); + RTW_SYNC(regs, RTW_PHYCFG, RTW_PHYCFG); + + DELAY(2); /* arbitrary delay */ + + reg &= ~RTW_PHYCFG_HST_CLK; + RTW_WRITE(regs, RTW_PHYCFG, reg); + RTW_SYNC(regs, RTW_PHYCFG, RTW_PHYCFG); + + if (lo_to_hi) + mask <<= 1; + else + mask >>= 1; + } + + reg |= RTW_PHYCFG_HST_EN; + KKASSERT((reg & RTW_PHYCFG_HST_CLK) == 0); + RTW_WRITE(regs, RTW_PHYCFG, reg); + RTW_SYNC(regs, RTW_PHYCFG, RTW_PHYCFG); +} + +/* Help rtw_rf_macwrite: tell MAC to bang bits to RF over the 3-wire + * interface. + */ +static __inline int +rtw_rf_macbangbits(struct rtw_regs *regs, uint32_t reg) +{ + int i; + + RTW_DPRINTF(RTW_DEBUG_PHY, ("%s: %#08x\n", __func__, reg)); + + RTW_WRITE(regs, RTW_PHYCFG, RTW_PHYCFG_MAC_POLL | reg); + + RTW_WBR(regs, RTW_PHYCFG, RTW_PHYCFG); + + for (i = rtw_macbangbits_timeout; --i >= 0; DELAY(1)) { + if ((RTW_READ(regs, RTW_PHYCFG) & RTW_PHYCFG_MAC_POLL) == 0) { + RTW_DPRINTF(RTW_DEBUG_PHY, + ("%s: finished in %dus\n", __func__, + rtw_macbangbits_timeout - i)); + return 0; + } + RTW_RBR(regs, RTW_PHYCFG, RTW_PHYCFG); /* XXX paranoia? */ + } + + printf("%s: RTW_PHYCFG_MAC_POLL still set.\n", __func__); + return -1; +} + +static uint32_t +rtw_grf5101_host_crypt(u_int addr, uint32_t val) +{ + /* TBD */ + return 0; +} + +static uint32_t +rtw_grf5101_mac_crypt(u_int addr, uint32_t val) +{ + uint32_t data_and_addr; +#define EXTRACT_NIBBLE(d, which) (((d) >> (4 * (which))) & 0xf) + static uint8_t caesar[16] = {0x0, 0x8, 0x4, 0xc, + 0x2, 0xa, 0x6, 0xe, + 0x1, 0x9, 0x5, 0xd, + 0x3, 0xb, 0x7, 0xf}; + + data_and_addr = caesar[EXTRACT_NIBBLE(val, 2)] | + (caesar[EXTRACT_NIBBLE(val, 1)] << 4) | + (caesar[EXTRACT_NIBBLE(val, 0)] << 8) | + (caesar[(addr >> 1) & 0xf] << 12) | + ((addr & 0x1) << 16) | + (caesar[EXTRACT_NIBBLE(val, 3)] << 24); + return SHIFTIN(data_and_addr, + RTW_PHYCFG_MAC_PHILIPS_ADDR_MASK|RTW_PHYCFG_MAC_PHILIPS_DATA_MASK); +#undef EXTRACT_NIBBLE +} + +static __inline const char * +rtw_rfchipid_string(enum rtw_rfchipid rfchipid) +{ + switch (rfchipid) { + case RTW_RFCHIPID_MAXIM: + return "Maxim"; + case RTW_RFCHIPID_PHILIPS: + return "Philips"; + case RTW_RFCHIPID_GCT: + return "GCT"; + case RTW_RFCHIPID_RFMD: + return "RFMD"; + case RTW_RFCHIPID_INTERSIL: + return "Intersil"; + default: + return "unknown"; + } +} + +/* Bang bits over the 3-wire interface. */ +int +rtw_rf_hostwrite(struct rtw_regs *regs, enum rtw_rfchipid rfchipid, + u_int addr, uint32_t val) +{ + u_int nbits; + int lo_to_hi; + uint32_t bits; + + RTW_DPRINTF(RTW_DEBUG_PHYIO, ("%s: %s[%u] <- %#08x\n", __func__, + rtw_rfchipid_string(rfchipid), addr, val)); + + switch (rfchipid) { + case RTW_RFCHIPID_MAXIM: + nbits = 16; + lo_to_hi = 0; + bits = SHIFTIN(val, MAX2820_TWI_DATA_MASK) | + SHIFTIN(addr, MAX2820_TWI_ADDR_MASK); + break; + case RTW_RFCHIPID_PHILIPS: + KKASSERT((addr & ~SHIFTOUT_MASK(SA2400_TWI_ADDR_MASK)) == 0); + KKASSERT((val & ~SHIFTOUT_MASK(SA2400_TWI_DATA_MASK)) == 0); + bits = SHIFTIN(val, SA2400_TWI_DATA_MASK) | + SHIFTIN(addr, SA2400_TWI_ADDR_MASK) | SA2400_TWI_WREN; + nbits = 32; + lo_to_hi = 1; + break; + case RTW_RFCHIPID_GCT: + KKASSERT((addr & ~SHIFTOUT_MASK(SI4126_TWI_ADDR_MASK)) == 0); + KKASSERT((val & ~SHIFTOUT_MASK(SI4126_TWI_DATA_MASK)) == 0); + bits = rtw_grf5101_host_crypt(addr, val); + nbits = 21; + lo_to_hi = 1; + break; + case RTW_RFCHIPID_RFMD: + KKASSERT((addr & ~SHIFTOUT_MASK(SI4126_TWI_ADDR_MASK)) == 0); + KKASSERT((val & ~SHIFTOUT_MASK(SI4126_TWI_DATA_MASK)) == 0); + bits = SHIFTIN(val, SI4126_TWI_DATA_MASK) | + SHIFTIN(addr, SI4126_TWI_ADDR_MASK); + nbits = 22; + lo_to_hi = 0; + break; + case RTW_RFCHIPID_INTERSIL: + default: + printf("%s: unknown rfchipid %d\n", __func__, rfchipid); + return -1; + } + + rtw_rf_hostbangbits(regs, bits, lo_to_hi, nbits); + + return 0; +} + +static uint32_t +rtw_maxim_swizzle(u_int addr, uint32_t val) +{ + uint32_t hidata, lodata; + + KKASSERT((val & ~(RTW_MAXIM_LODATA_MASK|RTW_MAXIM_HIDATA_MASK)) == 0); + lodata = SHIFTOUT(val, RTW_MAXIM_LODATA_MASK); + hidata = SHIFTOUT(val, RTW_MAXIM_HIDATA_MASK); + return SHIFTIN(lodata, RTW_PHYCFG_MAC_MAXIM_LODATA_MASK) | + SHIFTIN(hidata, RTW_PHYCFG_MAC_MAXIM_HIDATA_MASK) | + SHIFTIN(addr, RTW_PHYCFG_MAC_MAXIM_ADDR_MASK); +} + +/* Tell the MAC what to bang over the 3-wire interface. */ +int +rtw_rf_macwrite(struct rtw_regs *regs, enum rtw_rfchipid rfchipid, + u_int addr, uint32_t val) +{ + uint32_t reg; + + RTW_DPRINTF(RTW_DEBUG_PHYIO, ("%s: %s[%u] <- %#08x\n", __func__, + rtw_rfchipid_string(rfchipid), addr, val)); + + switch (rfchipid) { + case RTW_RFCHIPID_GCT: + reg = rtw_grf5101_mac_crypt(addr, val); + break; + case RTW_RFCHIPID_MAXIM: + reg = rtw_maxim_swizzle(addr, val); + break; + default: /* XXX */ + case RTW_RFCHIPID_PHILIPS: + KKASSERT( + (addr & ~SHIFTOUT_MASK(RTW_PHYCFG_MAC_PHILIPS_ADDR_MASK)) == 0); + KKASSERT( + (val & ~SHIFTOUT_MASK(RTW_PHYCFG_MAC_PHILIPS_DATA_MASK)) == 0); + + reg = SHIFTIN(addr, RTW_PHYCFG_MAC_PHILIPS_ADDR_MASK) | + SHIFTIN(val, RTW_PHYCFG_MAC_PHILIPS_DATA_MASK); + } + + switch (rfchipid) { + case RTW_RFCHIPID_GCT: + case RTW_RFCHIPID_MAXIM: + case RTW_RFCHIPID_RFMD: + reg |= RTW_PHYCFG_MAC_RFTYPE_RFMD; + break; + case RTW_RFCHIPID_INTERSIL: + reg |= RTW_PHYCFG_MAC_RFTYPE_INTERSIL; + break; + case RTW_RFCHIPID_PHILIPS: + reg |= RTW_PHYCFG_MAC_RFTYPE_PHILIPS; + break; + default: + printf("%s: unknown rfchipid %d\n", __func__, rfchipid); + return -1; + } + + return rtw_rf_macbangbits(regs, reg); +} diff --git a/sys/dev/netif/rtw/rtwphyio.h b/sys/dev/netif/rtw/rtwphyio.h new file mode 100644 index 0000000000..af12c86fe9 --- /dev/null +++ b/sys/dev/netif/rtw/rtwphyio.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2004, 2005 David Young. All rights reserved. + * + * Programmed for NetBSD by David Young. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of David Young may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY David Young ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL David + * Young BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * $NetBSD: rtwphyio.h,v 1.3 2005/12/11 12:21:28 christos Exp $ + * $DragonFly: src/sys/dev/netif/rtw/rtwphyio.h,v 1.1 2006/09/03 07:37:58 sephe Exp $ + */ +#ifndef _DEV_IC_RTWPHYIO_H +#define _DEV_IC_RTWPHYIO_H + +int rtw_rf_hostwrite(struct rtw_regs *, enum rtw_rfchipid, u_int, uint32_t); +int rtw_rf_macwrite(struct rtw_regs *, enum rtw_rfchipid, u_int, uint32_t); +uint8_t rtw_bbp_read(struct rtw_regs *, u_int); +int rtw_bbp_write(struct rtw_regs *, u_int, u_int); + +#endif /* _DEV_IC_RTWPHYIO_H */ diff --git a/sys/dev/netif/rtw/rtwreg.h b/sys/dev/netif/rtw/rtwreg.h new file mode 100644 index 0000000000..eae59f405f --- /dev/null +++ b/sys/dev/netif/rtw/rtwreg.h @@ -0,0 +1,1100 @@ +/* + * Copyright (c) 2004, 2005 David Young. All rights reserved. + * + * Programmed for NetBSD by David Young. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of David Young may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY David Young ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL David + * Young BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * $NetBSD: rtwreg.h,v 1.19 2006/03/10 23:37:35 dyoung Exp $ + * $DragonFly: src/sys/dev/netif/rtw/rtwreg.h,v 1.1 2006/09/03 07:37:58 sephe Exp $ + */ + +/* RTL8180L Host Control and Status Registers */ + +#define RTW_IDR0 0x00 /* ID Register: MAC addr, 6 bytes. + * Auto-loaded from EEPROM. Read by byte, + * by word, or by double word, but write + * only by double word. + */ +#define RTW_IDR1 0x04 + +#define RTW_MAR0 0x08 /* Multicast filter, 64b. */ +#define RTW_MAR1 0x0c + +#define RTW_TSFTRL 0x18 /* Timing Synchronization Function Timer + * Register, low word, 32b, read-only. + */ +#define RTW_TSFTRH 0x1c /* High word, 32b, read-only. */ +#define RTW_TLPDA 0x20 /* Transmit Low Priority Descriptors Start + * Address, 32b, 256-byte alignment. + */ +#define RTW_TNPDA 0x24 /* Transmit Normal Priority Descriptors Start + * Address, 32b, 256-byte alignment. + */ +#define RTW_THPDA 0x28 /* Transmit High Priority Descriptors Start + * Address, 32b, 256-byte alignment. + */ + +#define RTW_BRSR 0x2c /* Basic Rate Set Register, 16b */ +#define RTW_BRSR_BPLCP __BIT(8)/* 1: use short PLCP header for CTS/ACK packet, + * 0: use long PLCP header + */ +#define RTW_BRSR_MBR8180_MASK __BITS(1,0) /* Maximum Basic Service Rate */ +#define RTW_BRSR_MBR8180_1MBPS SHIFTIN(0, RTW_BRSR_MBR8180_MASK) +#define RTW_BRSR_MBR8180_2MBPS SHIFTIN(1, RTW_BRSR_MBR8180_MASK) +#define RTW_BRSR_MBR8180_5MBPS SHIFTIN(2, RTW_BRSR_MBR8180_MASK) +#define RTW_BRSR_MBR8180_11MBPS SHIFTIN(3, RTW_BRSR_MBR8180_MASK) + +/* 8181 and 8180 docs conflict! */ +#define RTW_BRSR_MBR8181_1MBPS __BIT(0) +#define RTW_BRSR_MBR8181_2MBPS __BIT(1) +#define RTW_BRSR_MBR8181_5MBPS __BIT(2) +#define RTW_BRSR_MBR8181_11MBPS __BIT(3) + +#define RTW_BSSID 0x2e +/* BSSID, 6 bytes */ +#define RTW_BSSID16 0x2e /* first two bytes */ +#define RTW_BSSID32 (0x2e + 4) /* remaining four bytes */ +#define RTW_BSSID0 RTW_BSSID16 /* BSSID[0], 8b */ +#define RTW_BSSID1 (RTW_BSSID0 + 1) /* BSSID[1], 8b */ +#define RTW_BSSID2 (RTW_BSSID1 + 1) /* BSSID[2], 8b */ +#define RTW_BSSID3 (RTW_BSSID2 + 1) /* BSSID[3], 8b */ +#define RTW_BSSID4 (RTW_BSSID3 + 1) /* BSSID[4], 8b */ +#define RTW_BSSID5 (RTW_BSSID4 + 1) /* BSSID[5], 8b */ + +#define RTW_CR 0x37 /* Command Register, 8b */ +#define RTW_CR_RST __BIT(4)/* Reset: host sets to 1 to disable + * transmitter & receiver, reinitialize FIFO. + * RTL8180L sets to 0 to signal completion. + */ +#define RTW_CR_RE __BIT(3)/* Receiver Enable: host enables receiver + * by writing 1. RTL8180L indicates receiver + * is active with 1. After power-up, host + * must wait for reset before writing. + */ +#define RTW_CR_TE __BIT(2)/* Transmitter Enable: host enables transmitter + * by writing 1. RTL8180L indicates transmitter + * is active with 1. After power-up, host + * must wait for reset before writing. + */ +#define RTW_CR_MULRW __BIT(0)/* PCI Multiple Read/Write enable: 1 enables, + * 0 disables. XXX RTL8180, only? + */ + +#define RTW_IMR 0x3c /* Interrupt Mask Register, 16b */ +#define RTW_ISR 0x3e /* Interrupt status register, 16b */ + +#define RTW_INTR_TXFOVW __BIT(15) /* Tx FIFO underflow */ +#define RTW_INTR_TIMEOUT __BIT(14) /* Time Out: 1 indicates + * RTW_TSFTR[0:31] = RTW_TINT + */ +/* Beacon Time Out: time for host to prepare beacon: + * RTW_TSFTR % (RTW_BCNITV_BCNITV * TU) = + * (RTW_BCNITV_BCNITV * TU - RTW_BINTRITV) + */ +#define RTW_INTR_BCNINT __BIT(13) +/* ATIM Time Out: ATIM interval will pass, + * RTW_TSFTR % (RTW_BCNITV_BCNITV * TU) = + * (RTW_ATIMWND_ATIMWND * TU - RTW_ATIMTRITV) + */ +#define RTW_INTR_ATIMINT __BIT(12) +/* Tx Beacon Descriptor Error: beacon transmission aborted because frame Rx'd */ +#define RTW_INTR_TBDER __BIT(11) +#define RTW_INTR_TBDOK __BIT(10) /* Tx Beacon Descriptor OK */ +#define RTW_INTR_THPDER __BIT(9)/* Tx High Priority Descriptor Error: + * reached short/long retry limit + */ +#define RTW_INTR_THPDOK __BIT(8)/* Tx High Priority Descriptor OK */ +#define RTW_INTR_TNPDER __BIT(7)/* Tx Normal Priority Descriptor Error: + * reached short/long retry limit + */ +#define RTW_INTR_TNPDOK __BIT(6)/* Tx Normal Priority Descriptor OK */ +#define RTW_INTR_RXFOVW __BIT(5)/* Rx FIFO Overflow: either RDU (see below) + * or PCI bus too slow/busy + */ +#define RTW_INTR_RDU __BIT(4)/* Rx Descriptor Unavailable */ +#define RTW_INTR_TLPDER __BIT(3)/* Tx Normal Priority Descriptor Error + * reached short/long retry limit + */ +#define RTW_INTR_TLPDOK __BIT(2)/* Tx Normal Priority Descriptor OK */ +#define RTW_INTR_RER __BIT(1)/* Rx Error: CRC32 or ICV error */ +#define RTW_INTR_ROK __BIT(0)/* Rx OK */ + +/* Convenient interrupt conjunctions. */ +#define RTW_INTR_RX (RTW_INTR_RER|RTW_INTR_ROK) +#define RTW_INTR_TX (RTW_INTR_TLPDER|RTW_INTR_TLPDOK|RTW_INTR_THPDER|\ + RTW_INTR_THPDOK|RTW_INTR_TNPDER|RTW_INTR_TNPDOK|\ + RTW_INTR_TBDER|RTW_INTR_TBDOK) +#define RTW_INTR_BEACON (RTW_INTR_BCNINT|RTW_INTR_TBDER|RTW_INTR_TBDOK) +#define RTW_INTR_IOERROR (RTW_INTR_TXFOVW|RTW_INTR_RXFOVW|RTW_INTR_RDU) + +#define RTW_TCR 0x40 /* Transmit Configuration Register, 32b */ +#define RTW_TCR_CWMIN __BIT(31)/* 1: CWmin = 8, 0: CWmin = 32. */ +#define RTW_TCR_SWSEQ __BIT(30)/* 1: host assigns 802.11 sequence number, + * 0: hardware assigns sequence number + */ +/* Hardware version ID, read-only */ +#define RTW_TCR_HWVERID_MASK __BITS(29, 25) +#define RTW_TCR_HWVERID_D SHIFTIN(26, RTW_TCR_HWVERID_MASK) +#define RTW_TCR_HWVERID_F SHIFTIN(27, RTW_TCR_HWVERID_MASK) +#define RTW_TCR_HWVERID_RTL8180 RTW_TCR_HWVERID_F + +/* Set ACK/CTS Timeout (EIFS). + * 1: ACK rate = max(RTW_BRSR_MBR, Rx rate) (XXX not min? typo in datasheet?) + * 0: ACK rate = 1Mbps + */ +#define RTW_TCR_SAT __BIT(24) +/* Max DMA Burst Size per Tx DMA Burst */ +#define RTW_TCR_MXDMA_MASK __BITS(23,21) +#define RTW_TCR_MXDMA_16 SHIFTIN(0, RTW_TCR_MXDMA_MASK) +#define RTW_TCR_MXDMA_32 SHIFTIN(1, RTW_TCR_MXDMA_MASK) +#define RTW_TCR_MXDMA_64 SHIFTIN(2, RTW_TCR_MXDMA_MASK) +#define RTW_TCR_MXDMA_128 SHIFTIN(3, RTW_TCR_MXDMA_MASK) +#define RTW_TCR_MXDMA_256 SHIFTIN(4, RTW_TCR_MXDMA_MASK) +#define RTW_TCR_MXDMA_512 SHIFTIN(5, RTW_TCR_MXDMA_MASK) +#define RTW_TCR_MXDMA_1024 SHIFTIN(6, RTW_TCR_MXDMA_MASK) +#define RTW_TCR_MXDMA_2048 SHIFTIN(7, RTW_TCR_MXDMA_MASK) + +/* disable 802.11 random backoff */ +#define RTW_TCR_DISCW __BIT(20) + +/* host lets RTL8180 append ICV to WEP packets */ +#define RTW_TCR_ICV __BIT(19) + +/* Loopback Test: disables TXI/TXQ outputs. */ +#define RTW_TCR_LBK_MASK __BITS(18,17) +#define RTW_TCR_LBK_NORMAL SHIFTIN(0, RTW_TCR_LBK_MASK) /* normal ops */ +#define RTW_TCR_LBK_MAC SHIFTIN(1, RTW_TCR_LBK_MASK) /* MAC loopback */ +#define RTW_TCR_LBK_BBP SHIFTIN(2, RTW_TCR_LBK_MASK) /* baseband loop. */ +#define RTW_TCR_LBK_CONT SHIFTIN(3, RTW_TCR_LBK_MASK) /* continuous Tx */ + +#define RTW_TCR_CRC __BIT(16) /* 0: RTL8180 appends CRC32 + * 1: host appends CRC32 + * + * (I *think* this is right. + * The docs have a mysterious + * description in the + * passive voice.) + */ +#define RTW_TCR_SRL_MASK __BITS(15,8) /* Short Retry Limit */ +#define RTW_TCR_LRL_MASK __BITS(7,0) /* Long Retry Limit */ + +#define RTW_RCR 0x44 /* Receive Configuration Register, 32b */ +/* only do Early Rx on packets longer than 1536 bytes */ +#define RTW_RCR_ONLYERLPKT __BIT(31) +/* enable carrier sense method 2 */ +#define RTW_RCR_ENCS2 __BIT(30) +/* enable carrier sense method 1 */ +#define RTW_RCR_ENCS1 __BIT(29) +#define RTW_RCR_ENMARP __BIT(28) /* enable MAC auto-reset PHY */ +/* Check BSSID/ToDS/FromDS: set "Link On" when received BSSID + * matches RTW_BSSID and received ToDS/FromDS are appropriate + * according to RTW_MSR_NETYPE. + */ +#define RTW_RCR_CBSSID __BIT(23) + /* accept packets w/ PWRMGMT bit set */ +#define RTW_RCR_APWRMGT __BIT(22) +/* when RTW_MSR_NETYPE == RTW_MSR_NETYPE_INFRA_OK, accept + * broadcast/multicast packets whose 3rd address matches RTL8180's MAC. + */ +#define RTW_RCR_ADD3 __BIT(21) +#define RTW_RCR_AMF __BIT(20) /* accept management frames */ +#define RTW_RCR_ACF __BIT(19) /* accept control frames */ +#define RTW_RCR_ADF __BIT(18) /* accept data frames */ +/* Rx FIFO Threshold: RTL8180 begins PCI transfer when this many data + * bytes are received + */ +#define RTW_RCR_RXFTH_MASK __BITS(15,13) +#define RTW_RCR_RXFTH_64 SHIFTIN(2, RTW_RCR_RXFTH_MASK) +#define RTW_RCR_RXFTH_128 SHIFTIN(3, RTW_RCR_RXFTH_MASK) +#define RTW_RCR_RXFTH_256 SHIFTIN(4, RTW_RCR_RXFTH_MASK) +#define RTW_RCR_RXFTH_512 SHIFTIN(5, RTW_RCR_RXFTH_MASK) +#define RTW_RCR_RXFTH_1024 SHIFTIN(6, RTW_RCR_RXFTH_MASK) +#define RTW_RCR_RXFTH_WHOLE SHIFTIN(7, RTW_RCR_RXFTH_MASK) + +#define RTW_RCR_AICV __BIT(12)/* accept frames w/ ICV errors */ + +/* Max DMA Burst Size per Rx DMA Burst */ +#define RTW_RCR_MXDMA_MASK __BITS(10,8) +#define RTW_RCR_MXDMA_16 SHIFTIN(0, RTW_RCR_MXDMA_MASK) +#define RTW_RCR_MXDMA_32 SHIFTIN(1, RTW_RCR_MXDMA_MASK) +#define RTW_RCR_MXDMA_64 SHIFTIN(2, RTW_RCR_MXDMA_MASK) +#define RTW_RCR_MXDMA_128 SHIFTIN(3, RTW_RCR_MXDMA_MASK) +#define RTW_RCR_MXDMA_256 SHIFTIN(4, RTW_RCR_MXDMA_MASK) +#define RTW_RCR_MXDMA_512 SHIFTIN(5, RTW_RCR_MXDMA_MASK) +#define RTW_RCR_MXDMA_1024 SHIFTIN(6, RTW_RCR_MXDMA_MASK) +#define RTW_RCR_MXDMA_UNLIMITED SHIFTIN(7, RTW_RCR_MXDMA_MASK) + +/* EEPROM type, read-only. 1: EEPROM is 93c56, 0: 93c46 */ +#define RTW_RCR_9356SEL __BIT(6) + +#define RTW_RCR_ACRC32 __BIT(5)/* accept frames w/ CRC32 errors */ +#define RTW_RCR_AB __BIT(3)/* accept broadcast frames */ +#define RTW_RCR_AM __BIT(2)/* accept multicast frames */ +/* accept physical match frames. XXX means PLCP header ok? */ +#define RTW_RCR_APM __BIT(1) +#define RTW_RCR_AAP __BIT(0)/* accept frames w/ destination */ + +/* Additional bits to set in monitor mode. */ +#define RTW_RCR_MONITOR ( \ + RTW_RCR_AAP | \ + RTW_RCR_ACF | \ + RTW_RCR_ACRC32 | \ + RTW_RCR_AICV | \ + 0) + +/* The packet filter bits. */ +#define RTW_RCR_PKTFILTER_MASK (\ + RTW_RCR_AAP | \ + RTW_RCR_AB | \ + RTW_RCR_ACF | \ + RTW_RCR_ACRC32 | \ + RTW_RCR_ADD3 | \ + RTW_RCR_ADF | \ + RTW_RCR_AICV | \ + RTW_RCR_AM | \ + RTW_RCR_AMF | \ + RTW_RCR_APM | \ + RTW_RCR_APWRMGT | \ + 0) + +/* Receive power-management frames and mgmt/ctrl/data frames. */ +#define RTW_RCR_PKTFILTER_DEFAULT ( \ + RTW_RCR_ADF | \ + RTW_RCR_AMF | \ + RTW_RCR_APM | \ + RTW_RCR_APWRMGT | \ + 0) + +#define RTW_TINT 0x48 /* Timer Interrupt Register, 32b */ +#define RTW_TBDA 0x4c /* Transmit Beacon Descriptor Start Address, + * 32b, 256-byte alignment + */ +#define RTW_9346CR 0x50 /* 93c46/93c56 Command Register, 8b */ +#define RTW_9346CR_EEM_MASK __BITS(7,6) /* Operating Mode */ +#define RTW_9346CR_EEM_NORMAL SHIFTIN(0, RTW_9346CR_EEM_MASK) +/* Load the EEPROM. Reset registers to defaults. + * Takes ~2ms. RTL8180 indicates completion with RTW_9346CR_EEM_NORMAL. + * XXX RTL8180 only? + */ +#define RTW_9346CR_EEM_AUTOLOAD SHIFTIN(1, RTW_9346CR_EEM_MASK) +/* Disable network & bus-master operations and enable + * _EECS, _EESK, _EEDI, _EEDO. + * XXX RTL8180 only? + */ +#define RTW_9346CR_EEM_PROGRAM SHIFTIN(2, RTW_9346CR_EEM_MASK) +/* Enable RTW_CONFIG[0123] registers. */ +#define RTW_9346CR_EEM_CONFIG SHIFTIN(3, RTW_9346CR_EEM_MASK) +/* EEPROM pin status/control in _EEM_CONFIG, _EEM_AUTOLOAD modes. + * XXX RTL8180 only? + */ +#define RTW_9346CR_EECS __BIT(3) +#define RTW_9346CR_EESK __BIT(2) +#define RTW_9346CR_EEDI __BIT(1) +#define RTW_9346CR_EEDO __BIT(0) /* read-only */ + +#define RTW_CONFIG0 0x51 /* Configuration Register 0, 8b */ +#define RTW_CONFIG0_WEP40 __BIT(7)/* implements 40-bit WEP, + * XXX RTL8180 only? + */ +#define RTW_CONFIG0_WEP104 __BIT(6)/* implements 104-bit WEP, + * from EEPROM, read-only + * XXX RTL8180 only? + */ +#define RTW_CONFIG0_LEDGPOEN __BIT(4)/* 1: RTW_PSR_LEDGPO[01] control + * LED[01] pins. + * 0: LED behavior defined by + * RTW_CONFIG1_LEDS10_MASK + * XXX RTL8180 only? + */ +/* auxiliary power is present, read-only */ +#define RTW_CONFIG0_AUXPWR __BIT(3) +/* Geographic Location, read-only */ +#define RTW_CONFIG0_GL_MASK __BITS(1,0) +/* _RTW_CONFIG0_GL_* is what the datasheet says, but RTW_CONFIG0_GL_* + * work. + */ +#define _RTW_CONFIG0_GL_USA SHIFTIN(3, RTW_CONFIG0_GL_MASK) +#define RTW_CONFIG0_GL_EUROPE SHIFTIN(2, RTW_CONFIG0_GL_MASK) +#define RTW_CONFIG0_GL_JAPAN SHIFTIN(1, RTW_CONFIG0_GL_MASK) +#define RTW_CONFIG0_GL_USA SHIFTIN(0, RTW_CONFIG0_GL_MASK) +/* RTL8181 datasheet says RTW_CONFIG0_GL_JAPAN = 0. */ + +#define RTW_CONFIG1 0x52 /* Configuration Register 1, 8b */ + +/* LED configuration. From EEPROM. Read/write. + * + * Setting LED0 LED1 + * ------- ---- ---- + * RTW_CONFIG1_LEDS_ACT_INFRA Activity Infrastructure + * RTW_CONFIG1_LEDS_ACT_LINK Activity Link + * RTW_CONFIG1_LEDS_TX_RX Tx Rx + * RTW_CONFIG1_LEDS_LINKACT_INFRA Link/Activity Infrastructure + */ +#define RTW_CONFIG1_LEDS_MASK __BITS(7,6) +#define RTW_CONFIG1_LEDS_ACT_INFRA SHIFTIN(0, RTW_CONFIG1_LEDS_MASK) +#define RTW_CONFIG1_LEDS_ACT_LINK SHIFTIN(1, RTW_CONFIG1_LEDS_MASK) +#define RTW_CONFIG1_LEDS_TX_RX SHIFTIN(2, RTW_CONFIG1_LEDS_MASK) +#define RTW_CONFIG1_LEDS_LINKACT_INFRA SHIFTIN(3, RTW_CONFIG1_LEDS_MASK) + +/* LWAKE Output Signal. Only applicable to Cardbus. Pulse width is 150ms. + * + * RTW_CONFIG1_LWACT + * 0 1 + * RTW_CONFIG4_LWPTN 0 active high active low + * 1 positive pulse negative pulse + */ +#define RTW_CONFIG1_LWACT __BIT(4) + +#define RTW_CONFIG1_MEMMAP __BIT(3)/* using PCI memory space, read-only */ +#define RTW_CONFIG1_IOMAP __BIT(2)/* using PCI I/O space, read-only */ +#define RTW_CONFIG1_VPD __BIT(1)/* if set, VPD from offsets + * 0x40-0x7f in EEPROM are at + * registers 0x60-0x67 of PCI + * Configuration Space (XXX huh?) + */ +#define RTW_CONFIG1_PMEN __BIT(0)/* Power Management Enable: TBD */ + +#define RTW_CONFIG2 0x53 /* Configuration Register 2, 8b */ +#define RTW_CONFIG2_LCK __BIT(7)/* clocks are locked, read-only: + * Tx frequency & symbol clocks + * are derived from the same OSC + */ +#define RTW_CONFIG2_ANT __BIT(6) /* diversity enabled, read-only */ +#define RTW_CONFIG2_DPS __BIT(3) /* Descriptor Polling State: enable + * test mode. + */ +#define RTW_CONFIG2_PAPESIGN __BIT(2) /* TBD, from EEPROM */ +#define RTW_CONFIG2_PAPETIME_MASK __BITS(1,0) /* TBD, from EEPROM */ + +#define RTW_ANAPARM 0x54 /* Analog parameter, 32b */ +#define RTW_ANAPARM_RFPOW0_MASK __BITS(30,28) /* undocumented bits + * which appear to + * control the power + * state of the RF + * components + */ +#define RTW_ANAPARM_RFPOW_MASK \ + (RTW_ANAPARM_RFPOW0_MASK|RTW_ANAPARM_RFPOW1_MASK) + +#define RTW_ANAPARM_TXDACOFF __BIT(27) /* 1: disable Tx DAC, + * 0: enable + */ +#define RTW_ANAPARM_RFPOW1_MASK __BITS(26,20) /* undocumented bits + * which appear to + * control the power + * state of the RF + * components + */ + +/* + * Maxim On/Sleep/Off control + */ +#define RTW_ANAPARM_RFPOW_MAXIM_ON SHIFTIN(0x8, RTW_ANAPARM_RFPOW1_MASK) + +/* reg[RTW_ANAPARM] |= RTW_ANAPARM_TXDACOFF; */ +#define RTW_ANAPARM_RFPOW_MAXIM_SLEEP SHIFTIN(0x378, RTW_ANAPARM_RFPOW1_MASK) + +/* reg[RTW_ANAPARM] |= RTW_ANAPARM_TXDACOFF; */ +#define RTW_ANAPARM_RFPOW_MAXIM_OFF SHIFTIN(0x379, RTW_ANAPARM_RFPOW1_MASK) + +/* + * RFMD On/Sleep/Off control + */ +#define RTW_ANAPARM_RFPOW_RFMD_ON SHIFTIN(0x408, RTW_ANAPARM_RFPOW1_MASK) + +/* reg[RTW_ANAPARM] |= RTW_ANAPARM_TXDACOFF; */ +#define RTW_ANAPARM_RFPOW_RFMD_SLEEP SHIFTIN(0x378, RTW_ANAPARM_RFPOW1_MASK) + +/* reg[RTW_ANAPARM] |= RTW_ANAPARM_TXDACOFF; */ +#define RTW_ANAPARM_RFPOW_RFMD_OFF SHIFTIN(0x379, RTW_ANAPARM_RFPOW1_MASK) + +/* + * Philips On/Sleep/Off control + */ +#define RTW_ANAPARM_RFPOW_ANA_PHILIPS_ON \ + SHIFTIN(0x328, RTW_ANAPARM_RFPOW1_MASK) +#define RTW_ANAPARM_RFPOW_DIG_PHILIPS_ON \ + SHIFTIN(0x008, RTW_ANAPARM_RFPOW1_MASK) + +/* reg[RTW_ANAPARM] |= RTW_ANAPARM_TXDACOFF; */ +#define RTW_ANAPARM_RFPOW_PHILIPS_SLEEP\ + SHIFTIN(0x378, RTW_ANAPARM_RFPOW1_MASK) + +/* reg[RTW_ANAPARM] |= RTW_ANAPARM_TXDACOFF; */ +#define RTW_ANAPARM_RFPOW_PHILIPS_OFF\ + SHIFTIN(0x379, RTW_ANAPARM_RFPOW1_MASK) + +#define RTW_ANAPARM_RFPOW_PHILIPS_ON SHIFTIN(0x328, RTW_ANAPARM_RFPOW1_MASK) + +#define RTW_ANAPARM_CARDSP_MASK __BITS(19,0) /* undocumented + * card-specific + * bits from the + * EEPROM. + */ + +#define RTW_MSR 0x58 /* Media Status Register, 8b */ +/* Network Type and Link Status */ +#define RTW_MSR_NETYPE_MASK __BITS(3,2) +/* AP, XXX RTL8181 only? */ +#define RTW_MSR_NETYPE_AP_OK SHIFTIN(3, RTW_MSR_NETYPE_MASK) +/* infrastructure link ok */ +#define RTW_MSR_NETYPE_INFRA_OK SHIFTIN(2, RTW_MSR_NETYPE_MASK) +/* ad-hoc link ok */ +#define RTW_MSR_NETYPE_ADHOC_OK SHIFTIN(1, RTW_MSR_NETYPE_MASK) +/* no link */ +#define RTW_MSR_NETYPE_NOLINK SHIFTIN(0, RTW_MSR_NETYPE_MASK) + +#define RTW_CONFIG3 0x59 /* Configuration Register 3, 8b */ +#define RTW_CONFIG3_GNTSEL __BIT(7) /* Grant Select, read-only */ +#define RTW_CONFIG3_PARMEN __BIT(6) /* Set RTW_CONFIG3_PARMEN and + * RTW_9346CR_EEM_CONFIG to + * allow RTW_ANAPARM writes. + */ +#define RTW_CONFIG3_MAGIC __BIT(5)/* Valid when RTW_CONFIG1_PMEN is + * set. If set, RTL8180 wakes up + * OS when Magic Packet is Rx'd. + */ +#define RTW_CONFIG3_CARDBEN __BIT(3)/* Cardbus-related registers + * and functions are enabled, + * read-only. XXX RTL8180 only. + */ +#define RTW_CONFIG3_CLKRUNEN __BIT(2)/* CLKRUN enabled, read-only. + * XXX RTL8180 only. + */ +#define RTW_CONFIG3_FUNCREGEN __BIT(1)/* Function Registers Enabled, + * read-only. XXX RTL8180 only. + */ +#define RTW_CONFIG3_FBTBEN __BIT(0)/* Fast back-to-back enabled, + * read-only. + */ +#define RTW_CONFIG4 0x5A /* Configuration Register 4, 8b */ +#define RTW_CONFIG4_VCOPDN __BIT(7)/* VCO Power Down + * 0: normal operation + * (power-on default) + * 1: power-down VCO, RF front-end, + * and most RTL8180 components. + */ +#define RTW_CONFIG4_PWROFF __BIT(6)/* Power Off + * 0: normal operation + * (power-on default) + * 1: power-down RF front-end, + * and most RTL8180 components, + * but leave VCO on. + * + * XXX RFMD front-end only? + */ +#define RTW_CONFIG4_PWRMGT __BIT(5)/* Power Management + * 0: normal operation + * (power-on default) + * 1: set Tx packet's PWRMGMT bit. + */ +#define RTW_CONFIG4_LWPME __BIT(4)/* LANWAKE vs. PMEB: Cardbus-only + * 0: LWAKE & PMEB asserted + * simultaneously + * 1: LWAKE asserted only if + * both PMEB is asserted and + * ISOLATEB is low. + * XXX RTL8180 only. + */ +#define RTW_CONFIG4_LWPTN __BIT(2)/* see RTW_CONFIG1_LWACT + * XXX RTL8180 only. + */ +/* Radio Front-End Programming Method */ +#define RTW_CONFIG4_RFTYPE_MASK __BITS(1,0) +#define RTW_CONFIG4_RFTYPE_INTERSIL SHIFTIN(1, RTW_CONFIG4_RFTYPE_MASK) +#define RTW_CONFIG4_RFTYPE_RFMD SHIFTIN(2, RTW_CONFIG4_RFTYPE_MASK) +#define RTW_CONFIG4_RFTYPE_PHILIPS SHIFTIN(3, RTW_CONFIG4_RFTYPE_MASK) + +#define RTW_TESTR 0x5B /* TEST mode register, 8b */ + +#define RTW_PSR 0x5e /* Page Select Register, 8b */ +#define RTW_PSR_GPO __BIT(7)/* Control/status of pin 52. */ +#define RTW_PSR_GPI __BIT(6)/* Status of pin 64. */ +#define RTW_PSR_LEDGPO1 __BIT(5)/* Status/control of LED1 pin if + * RTW_CONFIG0_LEDGPOEN is set. + */ +#define RTW_PSR_LEDGPO0 __BIT(4)/* Status/control of LED0 pin if + * RTW_CONFIG0_LEDGPOEN is set. + */ +#define RTW_PSR_UWF __BIT(1)/* Enable Unicast Wakeup Frame */ +#define RTW_PSR_PSEN __BIT(0)/* 1: page 1, 0: page 0 */ + +#define RTW_SCR 0x5f /* Security Configuration Register, 8b */ +#define RTW_SCR_KM_MASK __BITS(5,4) /* Key Mode */ +#define RTW_SCR_KM_WEP104 SHIFTIN(1, RTW_SCR_KM_MASK) +#define RTW_SCR_KM_WEP40 SHIFTIN(0, RTW_SCR_KM_MASK) +#define RTW_SCR_TXSECON __BIT(1)/* Enable Tx WEP. Invalid if + * neither RTW_CONFIG0_WEP40 nor + * RTW_CONFIG0_WEP104 is set. + */ +#define RTW_SCR_RXSECON __BIT(0)/* Enable Rx WEP. Invalid if + * neither RTW_CONFIG0_WEP40 nor + * RTW_CONFIG0_WEP104 is set. + */ + +#define RTW_BCNITV 0x70 /* Beacon Interval Register, 16b */ +#define RTW_BCNITV_BCNITV_MASK __BITS(9,0) /* TU between TBTT, written + * by host. + */ +#define RTW_ATIMWND 0x72 /* ATIM Window Register, 16b */ +#define RTW_ATIMWND_ATIMWND __BITS(9,0) /* ATIM Window length in TU, + * written by host. + */ + +#define RTW_BINTRITV 0x74 /* Beacon Interrupt Interval Register, 16b */ +#define RTW_BINTRITV_BINTRITV __BITS(9,0) /* RTL8180 wakes host with + * RTW_INTR_BCNINT at BINTRITV + * microseconds before TBTT + */ +#define RTW_ATIMTRITV 0x76 /* ATIM Interrupt Interval Register, 16b */ +#define RTW_ATIMTRITV_ATIMTRITV __BITS(9,0) /* RTL8180 wakes host with + * RTW_INTR_ATIMINT at ATIMTRITV + * microseconds before end of + * ATIM Window + */ + +#define RTW_PHYDELAY 0x78 /* PHY Delay Register, 8b */ +#define RTW_PHYDELAY_REVC_MAGIC __BIT(3) /* Rev. C magic from reference + * driver + */ +#define RTW_PHYDELAY_PHYDELAY __BITS(2,0) /* microsecond Tx delay between + * MAC and RF front-end + */ +#define RTW_CRCOUNT 0x79 /* Carrier Sense Counter, 8b */ +#define RTW_CRCOUNT_MAGIC 0x4c + +#define RTW_CRC16ERR 0x7a /* CRC16 error count, 16b, XXX RTL8181 only? */ + +#define RTW_BB 0x7c /* Baseband interface, 32b */ +/* used for writing RTL8180's integrated baseband processor */ +#define RTW_BB_RD_MASK __BITS(23,16) /* data to read */ +#define RTW_BB_WR_MASK __BITS(15,8) /* data to write */ +#define RTW_BB_WREN __BIT(7) /* write enable */ +#define RTW_BB_ADDR_MASK __BITS(6,0) /* address */ + +#define RTW_PHYADDR 0x7c /* Address register for PHY interface, 8b */ +#define RTW_PHYDATAW 0x7d /* Write data to PHY, 8b, write-only */ +#define RTW_PHYDATAR 0x7e /* Read data from PHY, 8b (?), read-only */ + +#define RTW_PHYCFG 0x80 /* PHY Configuration Register, 32b */ +#define RTW_PHYCFG_MAC_POLL __BIT(31) /* if !RTW_PHYCFG_HST, + * host sets. MAC clears + * after banging bits. + */ +#define RTW_PHYCFG_HST __BIT(30) /* 1: host bangs bits + * 0: MAC bangs bits + */ +#define RTW_PHYCFG_MAC_RFTYPE_MASK __BITS(29,28) +#define RTW_PHYCFG_MAC_RFTYPE_INTERSIL SHIFTIN(0, RTW_PHYCFG_MAC_RFTYPE_MASK) +#define RTW_PHYCFG_MAC_RFTYPE_RFMD SHIFTIN(1, RTW_PHYCFG_MAC_RFTYPE_MASK) +#define RTW_PHYCFG_MAC_RFTYPE_GCT RTW_PHYCFG_MAC_RFTYPE_RFMD +#define RTW_PHYCFG_MAC_RFTYPE_PHILIPS SHIFTIN(3, RTW_PHYCFG_MAC_RFTYPE_MASK) +#define RTW_PHYCFG_MAC_PHILIPS_ADDR_MASK __BITS(27,24) +#define RTW_PHYCFG_MAC_PHILIPS_DATA_MASK __BITS(23,0) +#define RTW_PHYCFG_MAC_MAXIM_LODATA_MASK __BITS(27,24) +#define RTW_PHYCFG_MAC_MAXIM_ADDR_MASK __BITS(11,8) +#define RTW_PHYCFG_MAC_MAXIM_HIDATA_MASK __BITS(7,0) +#define RTW_PHYCFG_HST_EN __BIT(2) +#define RTW_PHYCFG_HST_CLK __BIT(1) +#define RTW_PHYCFG_HST_DATA __BIT(0) + +#define RTW_MAXIM_HIDATA_MASK __BITS(11,4) +#define RTW_MAXIM_LODATA_MASK __BITS(3,0) + +/** + ** 0x84 - 0xD3, page 1, selected when RTW_PSR[PSEN] == 1. + **/ + +#define RTW_WAKEUP0L 0x84 /* Power Management Wakeup Frame */ +#define RTW_WAKEUP0H 0x88 /* 32b */ + +#define RTW_WAKEUP1L 0x8c +#define RTW_WAKEUP1H 0x90 + +#define RTW_WAKEUP2LL 0x94 +#define RTW_WAKEUP2LH 0x98 + +#define RTW_WAKEUP2HL 0x9c +#define RTW_WAKEUP2HH 0xa0 + +#define RTW_WAKEUP3LL 0xa4 +#define RTW_WAKEUP3LH 0xa8 + +#define RTW_WAKEUP3HL 0xac +#define RTW_WAKEUP3HH 0xb0 + +#define RTW_WAKEUP4LL 0xb4 +#define RTW_WAKEUP4LH 0xb8 + +#define RTW_WAKEUP4HL 0xbc +#define RTW_WAKEUP4HH 0xc0 + +#define RTW_CRC0 0xc4 /* CRC of wakeup frame 0, 16b */ +#define RTW_CRC1 0xc6 /* CRC of wakeup frame 1, 16b */ +#define RTW_CRC2 0xc8 /* CRC of wakeup frame 2, 16b */ +#define RTW_CRC3 0xca /* CRC of wakeup frame 3, 16b */ +#define RTW_CRC4 0xcc /* CRC of wakeup frame 4, 16b */ + +/** + ** 0x84 - 0xD3, page 0, selected when RTW_PSR[PSEN] == 0. + **/ + +/* Default Key Registers, each 128b + * + * If RTW_SCR_KM_WEP104, 104 lsb are the key. + * If RTW_SCR_KM_WEP40, 40 lsb are the key. + */ +#define RTW_DK0 0x90 /* Default Key 0 Register, 128b */ +#define RTW_DK1 0xa0 /* Default Key 1 Register, 128b */ +#define RTW_DK2 0xb0 /* Default Key 2 Register, 128b */ +#define RTW_DK3 0xc0 /* Default Key 3 Register, 128b */ + +#define RTW_CONFIG5 0xd8 /* Configuration Register 5, 8b */ +#define RTW_CONFIG5_TXFIFOOK __BIT(7)/* Tx FIFO self-test pass, read-only */ +#define RTW_CONFIG5_RXFIFOOK __BIT(6)/* Rx FIFO self-test pass, read-only */ +#define RTW_CONFIG5_CALON __BIT(5)/* 1: start calibration cycle + * and raise AGCRESET pin. + * 0: lower AGCRESET pin + */ +#define RTW_CONFIG5_EACPI __BIT(2)/* Enable ACPI Wake up, default 0 */ +#define RTW_CONFIG5_LANWAKE __BIT(1)/* Enable LAN Wake signal, + * from EEPROM + */ +#define RTW_CONFIG5_PMESTS __BIT(0)/* 1: both software & PCI Reset + * reset PME_Status + * 0: only software resets PME_Status + * + * From EEPROM. + */ + +#define RTW_TPPOLL 0xd9 /* Transmit Priority Polling Register, 8b, + * write-only. + */ +#define RTW_TPPOLL_BQ __BIT(7)/* RTL8180 clears to notify host of a beacon + * Tx. Host writes have no effect. + */ +#define RTW_TPPOLL_HPQ __BIT(6)/* Host writes 1 to notify RTL8180 of + * high-priority Tx packets, RTL8180 clears + * to after high-priority Tx is complete. + */ +#define RTW_TPPOLL_NPQ __BIT(5)/* If RTW_CONFIG2_DPS is set, + * host writes 1 to notify RTL8180 of + * normal-priority Tx packets, RTL8180 clears + * after normal-priority Tx is complete. + * + * If RTW_CONFIG2_DPS is clear, host writes + * have no effect. RTL8180 clears after + * normal-priority Tx is complete. + */ +#define RTW_TPPOLL_LPQ __BIT(4)/* Host writes 1 to notify RTL8180 of + * low-priority Tx packets, RTL8180 clears + * after low-priority Tx is complete. + */ +#define RTW_TPPOLL_SBQ __BIT(3)/* Host writes 1 to tell RTL8180 to + * stop beacon DMA. This bit is invalid + * when RTW_CONFIG2_DPS is set. + */ +#define RTW_TPPOLL_SHPQ __BIT(2)/* Host writes 1 to tell RTL8180 to + * stop high-priority DMA. + */ +#define RTW_TPPOLL_SNPQ __BIT(1)/* Host writes 1 to tell RTL8180 to + * stop normal-priority DMA. This bit is invalid + * when RTW_CONFIG2_DPS is set. + */ +#define RTW_TPPOLL_SLPQ __BIT(0)/* Host writes 1 to tell RTL8180 to + * stop low-priority DMA. + */ + +/* Start all queues. */ +#define RTW_TPPOLL_ALL (RTW_TPPOLL_BQ | RTW_TPPOLL_HPQ | \ + RTW_TPPOLL_NPQ | RTW_TPPOLL_LPQ) +/* Check all queues' activity. */ +#define RTW_TPPOLL_ACTIVE RTW_TPPOLL_ALL +/* Stop all queues. */ +#define RTW_TPPOLL_SALL (RTW_TPPOLL_SBQ | RTW_TPPOLL_SHPQ | \ + RTW_TPPOLL_SNPQ | RTW_TPPOLL_SLPQ) + +#define RTW_CWR 0xdc /* Contention Window Register, 16b, read-only */ +/* Contention Window: indicates number of contention windows before Tx + */ +#define RTW_CWR_CW __BITS(9,0) + +/* Retry Count Register, 16b, read-only */ +#define RTW_RETRYCTR 0xde +/* Retry Count: indicates number of retries after Tx */ +#define RTW_RETRYCTR_RETRYCT __BITS(7,0) + +#define RTW_RDSAR 0xe4 /* Receive descriptor Start Address Register, + * 32b, 256-byte alignment. + */ +/* Function Event Register, 32b, Cardbus only. Only valid when + * both RTW_CONFIG3_CARDBEN and RTW_CONFIG3_FUNCREGEN are set. + */ +#define RTW_FER 0xf0 +#define RTW_FER_INTR __BIT(15) /* set when RTW_FFER_INTR is set */ +#define RTW_FER_GWAKE __BIT(4) /* General Wakeup */ +/* Function Event Mask Register, 32b, Cardbus only. Only valid when + * both RTW_CONFIG3_CARDBEN and RTW_CONFIG3_FUNCREGEN are set. + */ +#define RTW_FEMR 0xf4 +#define RTW_FEMR_INTR __BIT(15) /* set when RTW_FFER_INTR is set */ +#define RTW_FEMR_WKUP __BIT(14) /* Wakeup Mask */ +#define RTW_FEMR_GWAKE __BIT(4) /* General Wakeup */ +/* Function Present State Register, 32b, read-only, Cardbus only. + * Only valid when both RTW_CONFIG3_CARDBEN and RTW_CONFIG3_FUNCREGEN + * are set. + */ +#define RTW_FPSR 0xf8 +#define RTW_FPSR_INTR __BIT(15) /* TBD */ +#define RTW_FPSR_GWAKE __BIT(4) /* General Wakeup: TBD */ +/* Function Force Event Register, 32b, write-only, Cardbus only. + * Only valid when both RTW_CONFIG3_CARDBEN and RTW_CONFIG3_FUNCREGEN + * are set. + */ +#define RTW_FFER 0xfc +#define RTW_FFER_INTR __BIT(15) /* TBD */ +#define RTW_FFER_GWAKE __BIT(4) /* General Wakeup: TBD */ + +/* Serial EEPROM offsets */ +#define RTW_SR_ID 0x00 /* 16b */ +#define RTW_SR_VID 0x02 /* 16b */ +#define RTW_SR_DID 0x04 /* 16b */ +#define RTW_SR_SVID 0x06 /* 16b */ +#define RTW_SR_SMID 0x08 /* 16b */ +#define RTW_SR_MNGNT 0x0a +#define RTW_SR_MXLAT 0x0b +#define RTW_SR_RFCHIPID 0x0c +#define RTW_SR_CONFIG3 0x0d +#define RTW_SR_MAC 0x0e /* 6 bytes */ +#define RTW_SR_CONFIG0 0x14 +#define RTW_SR_CONFIG1 0x15 +#define RTW_SR_PMC 0x16 /* Power Management Capabilities, 16b */ +#define RTW_SR_CONFIG2 0x18 +#define RTW_SR_CONFIG4 0x19 +#define RTW_SR_ANAPARM 0x1a /* Analog Parameters, 32b */ +#define RTW_SR_TESTR 0x1e +#define RTW_SR_CONFIG5 0x1f +#define RTW_SR_TXPOWER1 0x20 +#define RTW_SR_TXPOWER2 0x21 +#define RTW_SR_TXPOWER3 0x22 +#define RTW_SR_TXPOWER4 0x23 +#define RTW_SR_TXPOWER5 0x24 +#define RTW_SR_TXPOWER6 0x25 +#define RTW_SR_TXPOWER7 0x26 +#define RTW_SR_TXPOWER8 0x27 +#define RTW_SR_TXPOWER9 0x28 +#define RTW_SR_TXPOWER10 0x29 +#define RTW_SR_TXPOWER11 0x2a +#define RTW_SR_TXPOWER12 0x2b +#define RTW_SR_TXPOWER13 0x2c +#define RTW_SR_TXPOWER14 0x2d +#define RTW_SR_CHANNELPLAN 0x2e /* bitmap of channels to scan */ +#define RTW_SR_ENERGYDETTHR 0x2f /* energy-detect threshold */ +#define RTW_SR_ENERGYDETTHR_DEFAULT 0x0c /* use this if old SROM */ +#define RTW_SR_CISPOINTER 0x30 /* 16b */ +#define RTW_SR_RFPARM 0x32 /* RF-specific parameter */ +#define RTW_SR_RFPARM_DIGPHY __BIT(0) /* 1: digital PHY */ +#define RTW_SR_RFPARM_DFLANTB __BIT(1) /* 1: antenna B is default */ +#define RTW_SR_RFPARM_CS_MASK __BITS(2,3) /* carrier-sense type */ +#define RTW_SR_VERSION 0x3c /* EEPROM content version, 16b */ +#define RTW_SR_CRC 0x3e /* EEPROM content CRC, 16b */ +#define RTW_SR_VPD 0x40 /* Vital Product Data, 64 bytes */ +#define RTW_SR_CIS 0x80 /* CIS Data, 93c56 only, 128 bytes*/ + +/* + * RTL8180 Transmit/Receive Descriptors + */ + +/* the first descriptor in each ring must be on a 256-byte boundary */ +#define RTW_DESC_ALIGNMENT 256 + +/* Tx descriptor */ +struct rtw_txdesc { + uint32_t td_ctl0; + uint32_t td_ctl1; + uint32_t td_buf; + uint32_t td_len; + uint32_t td_next; + uint32_t td_rsvd[3]; +}; + +#define td_stat td_ctl0 + +#define RTW_TXCTL0_OWN __BIT(31) /* 1: ready to Tx */ +#define RTW_TXCTL0_RSVD0 __BIT(30) /* reserved */ +#define RTW_TXCTL0_FS __BIT(29) /* first segment */ +#define RTW_TXCTL0_LS __BIT(28) /* last segment */ + +#define RTW_TXCTL0_RATE_MASK __BITS(27,24) /* Tx rate */ +#define RTW_TXCTL0_RATE_1MBPS SHIFTIN(0, RTW_TXCTL0_RATE_MASK) +#define RTW_TXCTL0_RATE_2MBPS SHIFTIN(1, RTW_TXCTL0_RATE_MASK) +#define RTW_TXCTL0_RATE_5MBPS SHIFTIN(2, RTW_TXCTL0_RATE_MASK) +#define RTW_TXCTL0_RATE_11MBPS SHIFTIN(3, RTW_TXCTL0_RATE_MASK) + +#define RTW_TXCTL0_RTSEN __BIT(23) /* RTS Enable */ + +#define RTW_TXCTL0_RTSRATE_MASK __BITS(22,19) /* Tx rate */ +#define RTW_TXCTL0_RTSRATE_1MBPS SHIFTIN(0, RTW_TXCTL0_RTSRATE_MASK) +#define RTW_TXCTL0_RTSRATE_2MBPS SHIFTIN(1, RTW_TXCTL0_RTSRATE_MASK) +#define RTW_TXCTL0_RTSRATE_5MBPS SHIFTIN(2, RTW_TXCTL0_RTSRATE_MASK) +#define RTW_TXCTL0_RTSRATE_11MBPS SHIFTIN(3, RTW_TXCTL0_RTSRATE_MASK) + +#define RTW_TXCTL0_BEACON __BIT(18) /* packet is a beacon */ +#define RTW_TXCTL0_MOREFRAG __BIT(17) /* another fragment + * follows + */ +/* add short PLCP preamble and header */ +#define RTW_TXCTL0_SPLCP __BIT(16) +#define RTW_TXCTL0_KEYID_MASK __BITS(15,14) /* default key id */ +#define RTW_TXCTL0_RSVD1_MASK __BITS(13,12) /* reserved */ +#define RTW_TXCTL0_TPKTSIZE_MASK __BITS(11,0) /* Tx packet size + * in bytes + */ + +#define RTW_TXSTAT_OWN RTW_TXCTL0_OWN +#define RTW_TXSTAT_RSVD0 RTW_TXCTL0_RSVD0 +#define RTW_TXSTAT_FS RTW_TXCTL0_FS +#define RTW_TXSTAT_LS RTW_TXCTL0_LS +#define RTW_TXSTAT_RSVD1_MASK __BITS(27,16) +#define RTW_TXSTAT_TOK __BIT(15) +#define RTW_TXSTAT_RTSRETRY_MASK __BITS(14,8) /* RTS retry count */ +#define RTW_TXSTAT_DRC_MASK __BITS(7,0) /* Data retry count */ + +#define RTW_TXCTL1_LENGEXT __BIT(31) /* supplements _LENGTH + * in packets sent 5.5Mb/s or + * faster + */ +#define RTW_TXCTL1_LENGTH_MASK __BITS(30,16) /* PLCP length (microseconds) */ +#define RTW_TXCTL1_RTSDUR_MASK __BITS(15,0) /* RTS Duration + * (microseconds) + */ + +#define RTW_TXLEN_LENGTH_MASK __BITS(11,0) /* Tx buffer length in bytes */ + +/* Rx descriptor */ +struct rtw_rxdesc { + uint32_t rd_ctl; + uint32_t rd_rsvd0; + uint32_t rd_buf; + uint32_t rd_rsvd1; +}; + +#define rd_stat rd_ctl +#define rd_rssi rd_rsvd0 +#define rd_tsftl rd_buf /* valid only when RTW_RXSTAT_LS is set */ +#define rd_tsfth rd_rsvd1 /* valid only when RTW_RXSTAT_LS is set */ + +#define RTW_RXCTL_OWN __BIT(31) /* 1: owned by NIC */ +#define RTW_RXCTL_EOR __BIT(30) /* end of ring */ +#define RTW_RXCTL_FS __BIT(29) /* first segment */ +#define RTW_RXCTL_LS __BIT(28) /* last segment */ +#define RTW_RXCTL_RSVD0_MASK __BITS(29,12) /* reserved */ +#define RTW_RXCTL_LENGTH_MASK __BITS(11,0) /* Rx buffer length */ + +#define RTW_RXSTAT_OWN RTW_RXCTL_OWN +#define RTW_RXSTAT_EOR RTW_RXCTL_EOR +#define RTW_RXSTAT_FS RTW_RXCTL_FS /* first segment */ +#define RTW_RXSTAT_LS RTW_RXCTL_LS /* last segment */ +#define RTW_RXSTAT_DMAFAIL __BIT(27) /* DMA failure on this pkt */ +#define RTW_RXSTAT_BOVF __BIT(26) /* buffer overflow XXX means + * FIFO exhausted? + */ +#define RTW_RXSTAT_SPLCP __BIT(25) /* Rx'd with short preamble + * and PLCP header + */ +#define RTW_RXSTAT_RSVD1 __BIT(24) /* reserved */ +#define RTW_RXSTAT_RATE_MASK __BITS(23,20) /* Rx rate */ +#define RTW_RXSTAT_RATE_1MBPS SHIFTIN(0, RTW_RXSTAT_RATE_MASK) +#define RTW_RXSTAT_RATE_2MBPS SHIFTIN(1, RTW_RXSTAT_RATE_MASK) +#define RTW_RXSTAT_RATE_5MBPS SHIFTIN(2, RTW_RXSTAT_RATE_MASK) +#define RTW_RXSTAT_RATE_11MBPS SHIFTIN(3, RTW_RXSTAT_RATE_MASK) +#define RTW_RXSTAT_MIC __BIT(19) /* XXX from reference driver */ +#define RTW_RXSTAT_MAR __BIT(18) /* is multicast */ +#define RTW_RXSTAT_PAR __BIT(17) /* matches RTL8180's MAC */ +#define RTW_RXSTAT_BAR __BIT(16) /* is broadcast */ +#define RTW_RXSTAT_RES __BIT(15) /* error summary. valid when + * RTW_RXSTAT_LS set. indicates + * that either RTW_RXSTAT_CRC32 + * or RTW_RXSTAT_ICV is set. + */ +#define RTW_RXSTAT_PWRMGT __BIT(14) /* 802.11 PWRMGMT bit is set */ +#define RTW_RXSTAT_CRC16 __BIT(14) /* XXX CRC16 error, from + * reference driver + */ +#define RTW_RXSTAT_CRC32 __BIT(13) /* CRC32 error */ +#define RTW_RXSTAT_ICV __BIT(12) /* ICV error */ +#define RTW_RXSTAT_LENGTH_MASK __BITS(11,0) /* frame length, including + * CRC32 + */ + +/* Convenient status conjunction. */ +#define RTW_RXSTAT_ONESEG (RTW_RXSTAT_FS|RTW_RXSTAT_LS) +/* Convenient status disjunctions. */ +#define RTW_RXSTAT_IOERROR (RTW_RXSTAT_DMAFAIL|RTW_RXSTAT_BOVF) +#define RTW_RXSTAT_DEBUG (RTW_RXSTAT_SPLCP|RTW_RXSTAT_MAR|\ + RTW_RXSTAT_PAR|RTW_RXSTAT_BAR|\ + RTW_RXSTAT_PWRMGT|RTW_RXSTAT_CRC32|\ + RTW_RXSTAT_ICV) + + +#define RTW_RXRSSI_VLAN __BITS(31,16) /* XXX from reference driver */ +/* for Philips RF front-ends */ +#define RTW_RXRSSI_RSSI __BITS(15,8) /* RF energy at the PHY */ +/* for RF front-ends by Intersil, Maxim, RFMD */ +#define RTW_RXRSSI_IMR_RSSI __BITS(15,9) /* RF energy at the PHY */ +#define RTW_RXRSSI_IMR_LNA __BIT(8) /* 1: LNA activated */ +#define RTW_RXRSSI_SQ __BITS(7,0) /* Barker code-lock quality */ + +#define RTW_READ8(regs, ofs) \ + bus_space_read_1((regs)->r_bt, (regs)->r_bh, (ofs)) + +#define RTW_READ16(regs, ofs) \ + bus_space_read_2((regs)->r_bt, (regs)->r_bh, (ofs)) + +#define RTW_READ(regs, ofs) \ + bus_space_read_4((regs)->r_bt, (regs)->r_bh, (ofs)) + +#define RTW_WRITE8(regs, ofs, val) \ + bus_space_write_1((regs)->r_bt, (regs)->r_bh, (ofs), (val)) + +#define RTW_WRITE16(regs, ofs, val) \ + bus_space_write_2((regs)->r_bt, (regs)->r_bh, (ofs), (val)) + +#define RTW_WRITE(regs, ofs, val) \ + bus_space_write_4((regs)->r_bt, (regs)->r_bh, (ofs), (val)) + +#define RTW_ISSET(regs, reg, mask) \ + (RTW_READ((regs), (reg)) & (mask)) + +#define RTW_CLR(regs, reg, mask) \ + RTW_WRITE((regs), (reg), RTW_READ((regs), (reg)) & ~(mask)) + +/* bus_space(9) lied? */ +#ifndef BUS_SPACE_BARRIER_SYNC +#define BUS_SPACE_BARRIER_SYNC (BUS_SPACE_BARRIER_READ|BUS_SPACE_BARRIER_WRITE) +#endif + +#ifndef BUS_SPACE_BARRIER_READ_BEFORE_READ +#define BUS_SPACE_BARRIER_READ_BEFORE_READ BUS_SPACE_BARRIER_READ +#endif + +#ifndef BUS_SPACE_BARRIER_READ_BEFORE_WRITE +#define BUS_SPACE_BARRIER_READ_BEFORE_WRITE BUS_SPACE_BARRIER_READ +#endif + +#ifndef BUS_SPACE_BARRIER_WRITE_BEFORE_READ +#define BUS_SPACE_BARRIER_WRITE_BEFORE_READ BUS_SPACE_BARRIER_WRITE +#endif + +#ifndef BUS_SPACE_BARRIER_WRITE_BEFORE_WRITE +#define BUS_SPACE_BARRIER_WRITE_BEFORE_WRITE BUS_SPACE_BARRIER_WRITE +#endif + +/* + * Bus barrier + * + * Complete outstanding read and/or write ops on [reg0, reg1] + * ([reg1, reg0]) before starting new ops on the same region. See + * acceptable bus_space_barrier(9) for the flag definitions. + */ +#define RTW_BARRIER(regs, reg0, reg1, flags) \ + bus_space_barrier((regs)->r_bh, (regs)->r_bt, \ + MIN(reg0, reg1), MAX(reg0, reg1) - MIN(reg0, reg1) + 4, flags) + +/* + * Barrier convenience macros. + */ +/* sync */ +#define RTW_SYNC(regs, reg0, reg1) \ + RTW_BARRIER(regs, reg0, reg1, BUS_SPACE_BARRIER_SYNC) + +/* write-before-write */ +#define RTW_WBW(regs, reg0, reg1) \ + RTW_BARRIER(regs, reg0, reg1, BUS_SPACE_BARRIER_WRITE_BEFORE_WRITE) + +/* write-before-read */ +#define RTW_WBR(regs, reg0, reg1) \ + RTW_BARRIER(regs, reg0, reg1, BUS_SPACE_BARRIER_WRITE_BEFORE_READ) + +/* read-before-read */ +#define RTW_RBR(regs, reg0, reg1) \ + RTW_BARRIER(regs, reg0, reg1, BUS_SPACE_BARRIER_READ_BEFORE_READ) + +/* read-before-read */ +#define RTW_RBW(regs, reg0, reg1) \ + RTW_BARRIER(regs, reg0, reg1, BUS_SPACE_BARRIER_READ_BEFORE_WRITE) + +#define RTW_WBRW(regs, reg0, reg1) \ + RTW_BARRIER(regs, reg0, reg1, \ + BUS_SPACE_BARRIER_WRITE_BEFORE_READ | \ + BUS_SPACE_BARRIER_WRITE_BEFORE_WRITE) + +/* + * Registers for RTL8180L's built-in baseband modem. + */ +#define RTW_BBP_SYS1 0x00 +#define RTW_BBP_TXAGC 0x03 /* guess: transmit auto gain control */ +#define RTW_BBP_LNADET 0x04 /* guess: low-noise amplifier activation + * threshold + */ +#define RTW_BBP_IFAGCINI 0x05 /* guess: intermediate frequency (IF) + * auto-gain control (AGC) initial value + */ +#define RTW_BBP_IFAGCLIMIT 0x06 /* guess: IF AGC maximum value */ +#define RTW_BBP_IFAGCDET 0x07 /* guess: activation threshold for + * IF AGC loop + */ + +#define RTW_BBP_ANTATTEN 0x10 /* guess: antenna & attenuation */ +#define RTW_BBP_ANTATTEN_GCT_MAGIC 0xa3 +#define RTW_BBP_ANTATTEN_PHILIPS_MAGIC 0x91 +#define RTW_BBP_ANTATTEN_INTERSIL_MAGIC 0x92 +#define RTW_BBP_ANTATTEN_RFMD_MAGIC 0x93 +#define RTW_BBP_ANTATTEN_MAXIM_MAGIC 0xb3 +#define RTW_BBP_ANTATTEN_DFLANTB 0x40 +#define RTW_BBP_ANTATTEN_CHAN14 0x0c + +#define RTW_BBP_TRL 0x11 /* guess: transmit/receive + * switch latency + */ +#define RTW_BBP_SYS2 0x12 +#define RTW_BBP_SYS2_ANTDIV 0x80 /* enable antenna diversity */ +#define RTW_BBP_SYS2_RATE_MASK __BITS(5,4) /* loopback rate? + * 0: 1Mbps + * 1: 2Mbps + * 2: 5.5Mbps + * 3: 11Mbps + */ +#define RTW_BBP_SYS3 0x13 +/* carrier-sense threshold */ +#define RTW_BBP_SYS3_CSTHRESH_MASK __BITS(0,3) +#define RTW_BBP_CHESTLIM 0x19 /* guess: channel energy-detect + * threshold + */ +#define RTW_BBP_CHSQLIM 0x1a /* guess: channel signal-quality + * threshold + */ diff --git a/sys/dev/netif/rtw/rtwvar.h b/sys/dev/netif/rtw/rtwvar.h new file mode 100644 index 0000000000..88f40a5af4 --- /dev/null +++ b/sys/dev/netif/rtw/rtwvar.h @@ -0,0 +1,532 @@ +/* + * Copyright (c) 2004, 2005 David Young. All rights reserved. + * + * Driver for the Realtek RTL8180 802.11 MAC/BBP by David Young. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of David Young may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY David Young ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL David + * Young BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * $NetBSD: rtwvar.h,v 1.28 2006/02/16 20:17:16 perry Exp $ + * $DragonFly: src/sys/dev/netif/rtw/rtwvar.h,v 1.1 2006/09/03 07:37:58 sephe Exp $ + */ + +#ifndef _DEV_IC_RTWVAR_H_ +#define _DEV_IC_RTWVAR_H_ + +/* + * 802.11 frame duration definitions. + */ + +struct rtw_duration { + uint16_t d_rts_dur; + uint16_t d_data_dur; + uint16_t d_plcp_len; + uint8_t d_residue; /* unused octets in time slot */ +}; + +#ifdef RTW_DEBUG +#define RTW_DEBUG_TUNE 0x0000001 +#define RTW_DEBUG_PKTFILT 0x0000002 +#define RTW_DEBUG_XMIT 0x0000004 +#define RTW_DEBUG_XMIT_DESC 0x0000008 +#define RTW_DEBUG_NODE 0x0000010 +#define RTW_DEBUG_PWR 0x0000020 +#define RTW_DEBUG_ATTACH 0x0000040 +#define RTW_DEBUG_REGDUMP 0x0000080 +#define RTW_DEBUG_ACCESS 0x0000100 +#define RTW_DEBUG_RESET 0x0000200 +#define RTW_DEBUG_INIT 0x0000400 +#define RTW_DEBUG_IOSTATE 0x0000800 +#define RTW_DEBUG_RECV 0x0001000 +#define RTW_DEBUG_RECV_DESC 0x0002000 +#define RTW_DEBUG_IO_KICK 0x0004000 +#define RTW_DEBUG_INTR 0x0008000 +#define RTW_DEBUG_PHY 0x0010000 +#define RTW_DEBUG_PHYIO 0x0020000 +#define RTW_DEBUG_PHYBITIO 0x0040000 +#define RTW_DEBUG_TIMEOUT 0x0080000 +#define RTW_DEBUG_BUGS 0x0100000 +#define RTW_DEBUG_BEACON 0x0200000 +#define RTW_DEBUG_LED 0x0400000 +#define RTW_DEBUG_KEY 0x0800000 +#define RTW_DEBUG_XMIT_RSRC 0x1000000 +#define RTW_DEBUG_OACTIVE 0x2000000 +#define RTW_DEBUG_MAX 0x3ffffff + +extern int rtw_debug; +#define RTW_DPRINTF(__flags, __x) \ + if ((rtw_debug & (__flags)) != 0) printf __x +#define DPRINTF(__sc, __flags, __x) \ + if (((__sc)->sc_if.if_flags & IFF_DEBUG) != 0) \ + RTW_DPRINTF(__flags, __x) +#define RTW_PRINT_REGS(__regs, __dvname, __where) \ + rtw_print_regs((__regs), (__dvname), (__where)) +#else /* RTW_DEBUG */ +#define RTW_DPRINTF(__flags, __x) +#define DPRINTF(__sc, __flags, __x) +#define RTW_PRINT_REGS(__regs, __dvname, __where) +#endif /* RTW_DEBUG */ + +enum rtw_locale { + RTW_LOCALE_USA = 0, + RTW_LOCALE_EUROPE, + RTW_LOCALE_JAPAN, + RTW_LOCALE_UNKNOWN +}; + +enum rtw_rfchipid { + RTW_RFCHIPID_RESERVED = 0, + RTW_RFCHIPID_INTERSIL = 1, + RTW_RFCHIPID_RFMD = 2, + RTW_RFCHIPID_PHILIPS = 3, + RTW_RFCHIPID_MAXIM = 4, + RTW_RFCHIPID_GCT = 5 +}; + +/* sc_flags */ +#define RTW_F_ENABLED 0x00000001 /* chip is enabled */ +#define RTW_F_DIGPHY 0x00000002 /* digital PHY */ +#define RTW_F_DFLANTB 0x00000004 /* B antenna is default */ +#define RTW_F_ANTDIV 0x00000010 /* h/w antenna diversity */ +#define RTW_F_9356SROM 0x00000020 /* 93c56 SROM */ +#define RTW_F_SLEEP 0x00000040 /* chip is asleep */ +#define RTW_F_INVALID 0x00000080 /* chip is absent */ +#define RTW_F_DK_VALID 0x00000100 /* keys in DK0-DK3 are valid */ +#define RTW_C_RXWEP_40 0x00000200 /* h/w decrypts 40-bit WEP */ +#define RTW_C_RXWEP_104 0x00000400 /* h/w decrypts 104-bit WEP */ + /* all PHY flags */ +#define RTW_F_ALLPHY (RTW_F_DIGPHY|RTW_F_DFLANTB|RTW_F_ANTDIV) + +enum rtw_access { + RTW_ACCESS_NONE = 0, + RTW_ACCESS_CONFIG = 1, + RTW_ACCESS_ANAPARM = 2 +}; + +struct rtw_regs { + bus_space_tag_t r_bt; + bus_space_handle_t r_bh; + enum rtw_access r_access; + int r_type; + int r_rid; + struct resource *r_res; +}; + +#define RTW_SR_GET(sr, ofs) \ + (((sr)->sr_content[(ofs)/2] >> (((ofs) % 2 == 0) ? 0 : 8)) & 0xff) + +#define RTW_SR_GET16(sr, ofs) \ + (RTW_SR_GET((sr), (ofs)) | (RTW_SR_GET((sr), (ofs) + 1) << 8)) + +struct rtw_srom { + uint16_t *sr_content; + uint16_t sr_size; +}; + +struct rtw_rxsoft { + struct mbuf *rs_mbuf; + bus_dmamap_t rs_dmamap; + bus_addr_t rs_phyaddr; +}; + +struct rtw_txsoft { + STAILQ_ENTRY(rtw_txsoft) ts_q; + struct mbuf *ts_mbuf; + bus_dmamap_t ts_dmamap; + struct ieee80211_node *ts_ni; /* destination node */ + int ts_ratectl; + int ts_rateidx; + u_int ts_first; /* 1st hw descriptor */ + u_int ts_last; /* last hw descriptor */ + struct rtw_duration ts_d0; + struct rtw_duration ts_dn; +}; + +#define RTW_NTXPRI 4 /* number of Tx priorities */ +#define RTW_TXPRILO 0 +#define RTW_TXPRIMD 1 +#define RTW_TXPRIHI 2 +#define RTW_TXPRIBCN 3 /* beacon priority */ + +#define RTW_MAXPKTSEGS 64 /* Max 64 segments per Tx packet */ + +#define CASSERT(cond, complaint) \ + complaint[(cond) ? 0 : -1] = complaint[(cond) ? 0 : -1] + +/* + * Note well: the descriptor rings must begin on RTW_DESC_ALIGNMENT + * boundaries. I allocate them consecutively from one buffer, so + * just round up. + */ +#define RTW_TXQLENLO 64 /* low-priority queue length */ +#define RTW_TXQLENMD 64 /* medium-priority */ +#define RTW_TXQLENHI 64 /* high-priority */ +#define RTW_TXQLENBCN 8 /* beacon */ + +#define RTW_NTXDESCLO RTW_TXQLENLO +#define RTW_NTXDESCMD RTW_TXQLENMD +#define RTW_NTXDESCHI RTW_TXQLENHI +#define RTW_NTXDESCBCN RTW_TXQLENBCN + +#define RTW_NTXDESCTOTAL (RTW_NTXDESCLO + RTW_NTXDESCMD + \ + RTW_NTXDESCHI + RTW_NTXDESCBCN) + +#define RTW_RXQLEN 64 + +struct rtw_rxdesc_blk { + int rdb_ndesc; + int rdb_next; + + bus_dma_tag_t rdb_dmat; + bus_dmamap_t rdb_dmamap; + struct rtw_rxdesc *rdb_desc; + + uint32_t rdb_base; /* XXX bus_addr_t */ +}; + +struct rtw_txdesc_blk { + int tdb_ndesc; + int tdb_next; + int tdb_nfree; + + bus_dma_tag_t tdb_dmat; + bus_dmamap_t tdb_dmamap; + struct rtw_txdesc *tdb_desc; + + bus_size_t tdb_basereg; + uint32_t tdb_base; /* XXX bus_addr_t */ +}; + +#define RTW_NEXT_IDX(__htc, __idx) (((__idx) + 1) % (__htc)->tdb_ndesc) + +#define RTW_NEXT_DESC(__htc, __idx) \ + ((__htc)->tdb_base + \ + sizeof(struct rtw_txdesc) * RTW_NEXT_IDX((__htc), (__idx))) + +STAILQ_HEAD(rtw_txq, rtw_txsoft); + +struct rtw_txsoft_blk { + /* dirty/free s/w descriptors */ + struct rtw_txq tsb_dirtyq; + struct rtw_txq tsb_freeq; + u_int tsb_ndesc; + int tsb_tx_timer; + struct rtw_txsoft *tsb_desc; + uint8_t tsb_poll; +}; + +struct rtw_descs { + struct rtw_txdesc hd_txlo[RTW_NTXDESCLO]; + struct rtw_txdesc hd_txmd[RTW_NTXDESCMD]; + struct rtw_txdesc hd_txhi[RTW_NTXDESCMD]; + struct rtw_rxdesc hd_rx[RTW_RXQLEN]; + struct rtw_txdesc hd_bcn[RTW_NTXDESCBCN]; +}; +#define RTW_DESC_OFFSET(ring, i) offsetof(struct rtw_descs, ring[i]) +#define RTW_RING_OFFSET(ring) RTW_DESC_OFFSET(ring, 0) +#define RTW_RING_BASE(sc, ring) ((sc)->sc_desc_physaddr + \ + RTW_RING_OFFSET(ring)) + +/* Radio capture format for RTL8180. */ + +#define RTW_RX_RADIOTAP_PRESENT \ + ((1 << IEEE80211_RADIOTAP_TSFT) | \ + (1 << IEEE80211_RADIOTAP_FLAGS) | \ + (1 << IEEE80211_RADIOTAP_RATE) | \ + (1 << IEEE80211_RADIOTAP_CHANNEL) | \ + (1 << IEEE80211_RADIOTAP_LOCK_QUALITY) | \ + (1 << IEEE80211_RADIOTAP_DB_ANTSIGNAL) | \ + 0) + +struct rtw_rx_radiotap_header { + struct ieee80211_radiotap_header rr_ihdr; + uint64_t rr_tsft; + uint8_t rr_flags; + uint8_t rr_rate; + uint16_t rr_chan_freq; + uint16_t rr_chan_flags; + uint16_t rr_barker_lock; + uint8_t rr_antsignal; +} __packed; + +#define RTW_TX_RADIOTAP_PRESENT \ + ((1 << IEEE80211_RADIOTAP_FLAGS) | \ + (1 << IEEE80211_RADIOTAP_RATE) | \ + (1 << IEEE80211_RADIOTAP_CHANNEL) | \ + 0) + +struct rtw_tx_radiotap_header { + struct ieee80211_radiotap_header rt_ihdr; + uint8_t rt_flags; + uint8_t rt_rate; + uint16_t rt_chan_freq; + uint16_t rt_chan_flags; +} __packed; + +enum rtw_attach_state {FINISHED, FINISH_DESCMAP_LOAD, FINISH_DESCMAP_CREATE, + FINISH_DESC_MAP, FINISH_DESC_ALLOC, FINISH_RXMAPS_CREATE, + FINISH_TXMAPS_CREATE, FINISH_RESET, FINISH_READ_SROM, FINISH_PARSE_SROM, + FINISH_RF_ATTACH, FINISH_ID_STA, FINISH_TXDESCBLK_SETUP, + FINISH_TXCTLBLK_SETUP, DETACHED}; + +struct rtw_hooks { + void *rh_shutdown; /* shutdown hook */ + void *rh_power; /* power management hook */ +}; + +struct rtw_mtbl { + int (*mt_newstate)(struct ieee80211com *, + enum ieee80211_state, int); + void (*mt_recv_mgmt)(struct ieee80211com *, + struct mbuf *, + struct ieee80211_node *, + int, int, uint32_t); + struct ieee80211_node *(*mt_node_alloc)(struct ieee80211_node_table*); + void (*mt_node_free)(struct ieee80211_node *); +}; + +enum rtw_pwrstate { RTW_OFF = 0, RTW_SLEEP, RTW_ON }; + +typedef void (*rtw_continuous_tx_cb_t)(void *arg, int); + +struct rtw_phy { + struct rtw_rf *p_rf; + struct rtw_regs *p_regs; +}; + +struct rtw_bbpset { + u_int bb_antatten; + u_int bb_chestlim; + u_int bb_chsqlim; + u_int bb_ifagcdet; + u_int bb_ifagcini; + u_int bb_ifagclimit; + u_int bb_lnadet; + u_int bb_sys1; + u_int bb_sys2; + u_int bb_sys3; + u_int bb_trl; + u_int bb_txagc; +}; + +struct rtw_rf { + void (*rf_destroy)(struct rtw_rf *); + /* args: frequency, txpower, power state */ + int (*rf_init)(struct rtw_rf *, u_int, uint8_t, + enum rtw_pwrstate); + /* arg: power state */ + int (*rf_pwrstate)(struct rtw_rf *, + enum rtw_pwrstate); + /* arg: frequency */ + int (*rf_tune)(struct rtw_rf *, u_int); + /* arg: txpower */ + int (*rf_txpower)(struct rtw_rf *, uint8_t); + rtw_continuous_tx_cb_t rf_continuous_tx_cb; + void *rf_continuous_tx_arg; + struct rtw_bbpset rf_bbpset; +}; + +static __inline void +rtw_rf_destroy(struct rtw_rf *rf) +{ + rf->rf_destroy(rf); +} + +static __inline int +rtw_rf_init(struct rtw_rf *rf, u_int freq, uint8_t opaque_txpower, + enum rtw_pwrstate power) +{ + return rf->rf_init(rf, freq, opaque_txpower, power); +} + +static __inline int +rtw_rf_pwrstate(struct rtw_rf *rf, enum rtw_pwrstate power) +{ + return rf->rf_pwrstate(rf, power); +} + +static __inline int +rtw_rf_tune(struct rtw_rf *rf, u_int freq) +{ + return rf->rf_tune(rf, freq); +} + +static __inline int +rtw_rf_txpower(struct rtw_rf *rf, uint8_t opaque_txpower) +{ + return rf->rf_txpower(rf, opaque_txpower); +} + +typedef int (*rtw_rf_write_t)(struct rtw_regs *, enum rtw_rfchipid, u_int, + uint32_t); + +struct rtw_rfbus { + struct rtw_regs *b_regs; + rtw_rf_write_t b_write; +}; + +static __inline int +rtw_rfbus_write(struct rtw_rfbus *bus, enum rtw_rfchipid rfchipid, u_int addr, + uint32_t val) +{ + return bus->b_write(bus->b_regs, rfchipid, addr, val); +} + +struct rtw_max2820 { + struct rtw_rf mx_rf; + struct rtw_rfbus mx_bus; + int mx_is_a; /* 1: MAX2820A/MAX2821A */ +}; + +struct rtw_grf5101 { + struct rtw_rf gr_rf; + struct rtw_rfbus gr_bus; +}; + +struct rtw_sa2400 { + struct rtw_rf sa_rf; + struct rtw_rfbus sa_bus; + int sa_digphy; /* 1: digital PHY */ +}; + +typedef void (*rtw_pwrstate_t)(struct rtw_regs *, enum rtw_pwrstate, int, int); + +union rtw_keys { + uint8_t rk_keys[4][16]; + uint32_t rk_words[16]; +}; + +#define RTW_LED_SLOW_TICKS MAX(1, hz/2) +#define RTW_LED_FAST_TICKS MAX(1, hz/10) + +struct rtw_led_state { +#define RTW_LED0 0x1 +#define RTW_LED1 0x2 + uint8_t ls_slowblink:2; + uint8_t ls_actblink:2; + uint8_t ls_default:2; + uint8_t ls_state; + uint8_t ls_event; +#define RTW_LED_S_RX 0x1 +#define RTW_LED_S_TX 0x2 +#define RTW_LED_S_SLOW 0x4 + struct callout ls_slow_ch; + struct callout ls_fast_ch; +}; + +struct rtw_softc { + struct ieee80211com sc_ic; + struct rtw_regs sc_regs; + uint32_t sc_flags; + + struct resource *sc_irq_res; + int sc_irq_rid; + void *sc_irq_handle; + + enum rtw_rfchipid sc_rfchipid; + enum rtw_locale sc_locale; + uint8_t sc_phydelay; + + /* s/w Tx/Rx descriptors */ + bus_dma_tag_t sc_txsoft_dmat; + struct rtw_txsoft_blk sc_txsoft_blk[RTW_NTXPRI]; + struct rtw_txdesc_blk sc_txdesc_blk[RTW_NTXPRI]; + + int sc_rxsoft_free; + bus_dmamap_t sc_rxsoft_dmamap; + bus_dma_tag_t sc_rxsoft_dmat; + struct rtw_rxsoft sc_rxsoft[RTW_RXQLEN]; + struct rtw_rxdesc_blk sc_rxdesc_blk; + + struct rtw_srom sc_srom; + + enum rtw_pwrstate sc_pwrstate; + + rtw_pwrstate_t sc_pwrstate_cb; + + struct rtw_rf *sc_rf; + + uint16_t sc_inten; + + /* interrupt acknowledge hook */ + void (*sc_intr_ack)(struct rtw_regs *); + + void (*sc_power)(struct rtw_softc *, int); + struct rtw_mtbl sc_mtbl; + struct rtw_hooks sc_hooks; + + struct bpf_if *sc_radiobpf; + + struct callout sc_scan_ch; + u_int sc_cur_chan; + + uint32_t sc_tsfth; /* most significant TSFT bits */ + uint32_t sc_rcr; /* RTW_RCR */ + uint8_t sc_csthr; /* carrier-sense threshold */ + + int sc_do_tick; /* indicate 1s ticks */ + struct timeval sc_tick0; /* first tick */ + + uint8_t sc_rev; /* PCI/Cardbus revision */ + + uint32_t sc_anaparm; /* register RTW_ANAPARM */ + + union { + struct rtw_rx_radiotap_header tap; + uint8_t pad[64]; + } sc_rxtapu; + union { + struct rtw_tx_radiotap_header tap; + uint8_t pad[64]; + } sc_txtapu; + union rtw_keys sc_keys; + struct ifqueue sc_beaconq; + struct rtw_led_state sc_led_state; + int sc_hwverid; +}; + +#define sc_if sc_ic.ic_if +#define sc_rxtap sc_rxtapu.tap +#define sc_txtap sc_txtapu.tap + +extern int rtw_host_rfio; +extern devclass_t rtw_devclass; + +#if 0 +void rtw_txdac_enable(struct rtw_softc *, int); +void rtw_anaparm_enable(struct rtw_regs *, int); +void rtw_config0123_enable(struct rtw_regs *, int); +void rtw_continuous_tx_enable(struct rtw_softc *, int); +void rtw_set_access(struct rtw_regs *, enum rtw_access); +#endif + +int rtw_attach(device_t); +int rtw_detach(device_t); +void rtw_stop(struct rtw_softc *, int); +#ifdef RTW_DEBUG +const char *rtw_pwrstate_string(enum rtw_pwrstate); +#endif + +#endif /* _DEV_IC_RTWVAR_H_ */ diff --git a/sys/dev/netif/rtw/sa2400reg.h b/sys/dev/netif/rtw/sa2400reg.h new file mode 100644 index 0000000000..27074db1f2 --- /dev/null +++ b/sys/dev/netif/rtw/sa2400reg.h @@ -0,0 +1,269 @@ +/* + * Copyright (c) 2005 David Young. All rights reserved. + * + * This code was written by David Young. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY David Young ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL David + * Young BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * $NetBSD: sa2400reg.h,v 1.6 2006/03/08 08:26:50 dyoung Exp $ + * $DragonFly: src/sys/dev/netif/rtw/sa2400reg.h,v 1.1 2006/09/03 07:37:58 sephe Exp $ + */ + +#ifndef _DEV_IC_SA2400REG_H_ +#define _DEV_IC_SA2400REG_H_ + +/* + * Serial bus format for Philips SA2400 Single-chip Transceiver. + */ +#define SA2400_TWI_DATA_MASK __BITS(31,8) +#define SA2400_TWI_WREN __BIT(7) /* enable write */ +#define SA2400_TWI_ADDR_MASK __BITS(6,0) + +/* + * Registers for Philips SA2400 Single-chip Transceiver. + */ +#define SA2400_SYNA 0 /* Synthesizer Register A */ +#define SA2400_SYNA_FM __BIT(21) /* fractional modulus select, + * 0: /8 (default) + * 1: /5 + */ +#define SA2400_SYNA_NF_MASK __BITS(20,18) /* fractional increment value, + * 0 to 7, default 4 + */ +#define SA2400_SYNA_N_MASK __BITS(17,2) /* main divider division ratio, + * 512 to 65535, default 615 + */ + +#define SA2400_SYNB 1 /* Synthesizer Register B */ +#define SA2400_SYNB_R_MASK __BITS(21,12) /* reference divider ratio, + * 4 to 1023, default 11 + */ +#define SA2400_SYNB_L_MASK __BITS(11,10) /* lock detect mode */ +#define SA2400_SYNB_L_INACTIVE0 SHIFTIN(0, SA2400_SYNB_L_MASK) +#define SA2400_SYNB_L_INACTIVE1 SHIFTIN(1, SA2400_SYNB_L_MASK) +#define SA2400_SYNB_L_NORMAL SHIFTIN(2, SA2400_SYNB_L_MASK) +#define SA2400_SYNB_L_INACTIVE2 SHIFTIN(3, SA2400_SYNB_L_MASK) + +#define SA2400_SYNB_ON __BIT(9) /* power on/off, + * 0: inverted chip mode control + * 1: as defined by chip mode + * (see SA2400_OPMODE) + */ +#define SA2400_SYNB_ONE __BIT(8) /* always 1 */ +#define SA2400_SYNB_FC_MASK __BITS(7,0) /* fractional compensation + * charge pump current DAC, + * 0 to 255, default 80. + */ + +#define SA2400_SYNC 2 /* Synthesizer Register C */ +#define SA2400_SYNC_CP_MASK __BITS(7,6) /* charge pump current + * setting + */ +#define SA2400_SYNC_CP_NORMAL_ SHIFTIN(0, SA2400_SYNC_CP_MASK) +#define SA2400_SYNC_CP_THIRD_ SHIFTIN(1, SA2400_SYNC_CP_MASK) +#define SA2400_SYNC_CP_NORMAL SHIFTIN(2, SA2400_SYNC_CP_MASK) /* recommended */ +#define SA2400_SYNC_CP_THIRD SHIFTIN(3, SA2400_SYNC_CP_MASK) + +#define SA2400_SYNC_SM_MASK __BITS(5,3) /* comparison divider select, + * 0 to 4, extra division + * ratio is 2**SM. + */ +#define SA2400_SYNC_ZERO __BIT(2) /* always 0 */ + +#define SA2400_SYND 3 /* Synthesizer Register D */ +#define SA2400_SYND_ZERO1_MASK __BITS(21,17) /* always 0 */ +#define SA2400_SYND_TPHPSU __BIT(16) /* T[phpsu], 1: disable + * PHP speedup pump, + * overrides SA2400_SYND_TSPU + */ +#define SA2400_SYND_TPSU __BIT(15) /* T[spu], 1: speedup on, + * 0: speedup off + */ +#define SA2400_SYND_ZERO2_MASK __BITS(14,3) /* always 0 */ + +#define SA2400_OPMODE 4 /* Operating mode, filter tuner, + * other controls + */ +/* 1: in Rx mode, RSSI-ADC always on 0: RSSI-ADC only on during AGC */ +#define SA2400_OPMODE_ADC __BIT(19) +/* read-only filter tuner error: 1 if tuner out of range */ +#define SA2400_OPMODE_FTERR __BIT(18) +/* Rx & Tx filter tuning, write tuning value (test mode only) or + * read tuner setting (in normal mode). + */ +#define SA2400_OPMODE_FILTTUNE_MASK __BITS(17,15) + +/* external reference voltage (pad v2p5) on */ +#define SA2400_OPMODE_V2P5 __BIT(14) +/* external reference current ... */ +#define SA2400_OPMODE_I1M __BIT(13) +/* external reference current ... */ +#define SA2400_OPMODE_I0P3 __BIT(12) +#define SA2400_OPMODE_IN22 __BIT(10) /* xtal input frequency, + * 0: 44 MHz + * 1: 22 MHz + */ +#define SA2400_OPMODE_CLK __BIT(9) /* reference clock output on */ +#define SA2400_OPMODE_XO __BIT(8) /* xtal oscillator on */ +#define SA2400_OPMODE_DIGIN __BIT(7) /* use digital Tx inputs + * (FIRDAC) + */ +#define SA2400_OPMODE_RXLV __BIT(6) /* Rx output common mode + * voltage, + * 0: V[DD]/2 + * 1: 1.25V + */ +#define SA2400_OPMODE_VEO __BIT(5) /* make internal vco + * available at vco pads + * (vcoextout) + */ +#define SA2400_OPMODE_VEI __BIT(4) /* use external vco input + * (vcoextin) + */ +/* main operating mode */ +#define SA2400_OPMODE_MODE_MASK __BITS(3,0) +#define SA2400_OPMODE_MODE_SLEEP SHIFTIN(0, SA2400_OPMODE_MODE_MASK) +#define SA2400_OPMODE_MODE_TXRX SHIFTIN(1, SA2400_OPMODE_MODE_MASK) +#define SA2400_OPMODE_MODE_WAIT SHIFTIN(2, SA2400_OPMODE_MODE_MASK) +#define SA2400_OPMODE_MODE_RXMGC SHIFTIN(3, SA2400_OPMODE_MODE_MASK) +#define SA2400_OPMODE_MODE_FCALIB SHIFTIN(4, SA2400_OPMODE_MODE_MASK) +#define SA2400_OPMODE_MODE_DCALIB SHIFTIN(5, SA2400_OPMODE_MODE_MASK) +#define SA2400_OPMODE_MODE_FASTTXRXMGC SHIFTIN(6, SA2400_OPMODE_MODE_MASK) +#define SA2400_OPMODE_MODE_RESET SHIFTIN(7, SA2400_OPMODE_MODE_MASK) +#define SA2400_OPMODE_MODE_VCOCALIB SHIFTIN(8, SA2400_OPMODE_MODE_MASK) + +#define SA2400_OPMODE_DEFAULTS \ + (SA2400_OPMODE_XO | SA2400_OPMODE_RXLV | SA2400_OPMODE_CLK | \ + SA2400_OPMODE_I0P3 | SHIFTIN(3, SA2400_OPMODE_FILTTUNE_MASK)) + +#define SA2400_AGC 5 /* AGC adjustment */ +#define SA2400_AGC_TARGETSIGN __BIT(23) /* fine-tune AGC target: + * -7dB to 7dB, sign bit ... */ +#define SA2400_AGC_TARGET_MASK __BITS(22,20) /* ... plus 0dB - 7dB */ +#define SA2400_AGC_MAXGAIN_MASK __BITS(19,15) /* maximum AGC gain, 0 to 31, + * (yields 54dB to 85dB) + */ +/* write: settling time after baseband gain switching, units of + * 182 nanoseconds. + * read: output of RSSI/Tx-peak detector's ADC in 5-bit Gray code. + */ +#define SA2400_AGC_BBPDELAY_MASK __BITS(14,10) +#define SA2400_AGC_ADCVAL_MASK SA2400_AGC_BBPDELAY_MASK + +/* write: settling time after LNA gain switching, units of + * 182 nanoseconds + * read: 2nd sample of RSSI in AGC cycle + */ +#define SA2400_AGC_LNADELAY_MASK __BITS(9,5) +#define SA2400_AGC_SAMPLE2_MASK SA2400_AGC_LNADELAY_MASK + +/* write: time between turning on Rx and AGCSET, units of + * 182 nanoseconds + * read: 1st sample of RSSI in AGC cycle + */ +#define SA2400_AGC_RXONDELAY_MASK __BITS(4,0) +#define SA2400_AGC_SAMPLE1_MASK SA2400_AGC_RXONDELAY_MASK + +#define SA2400_MANRX 6 /* Manual receiver control settings */ +#define SA2400_MANRX_AHSN __BIT(23) /* 1: AGC w/ high S/N---switch + * LNA at step 52 + * (recommended) + * 0: switch LNA at step 60 + */ + +/* If _RXOSQON, Q offset is + * (_RXOSQSIGN ? -1 : 1) * (1 + _RXOSQ_MASK) * 8 millivolts, + * otherwise, Q offset is 0. + * + * Ditto I offset. + */ +#define SA2400_MANRX_RXOSQON __BIT(22) /* Rx Q-channel correction. */ +#define SA2400_MANRX_RXOSQSIGN __BIT(21) +#define SA2400_MANRX_RXOSQ_MASK __BITS(20,18) + +#define SA2400_MANRX_RXOSION __BIT(17) /* Rx I-channel correction. */ +#define SA2400_MANRX_RXOSISIGN __BIT(16) +#define SA2400_MANRX_RXOSI_MASK __BITS(15,13) +#define SA2400_MANRX_TEN __BIT(12) /* use 10MHz offset cancellation + * cornerpoint for brief period + * after each gain change + */ + +/* DC offset cancellation cornerpoint select + * write: in RXMGC, set the cornerpoint + * read: in other modes, read AGC-controlled cornerpoint + */ +#define SA2400_MANRX_CORNERFREQ_MASK __BITS(11,10) + +/* write: in RXMGC mode, sets receiver gain + * read: in other modes, read AGC-controlled gain + */ +#define SA2400_MANRX_RXGAIN_MASK __BITS(9,0) + +#define SA2400_TX 7 /* Transmitter settings */ +/* Tx offsets + * + * write: in test mode, sets the offsets + * read: in normal mode, returns automatic settings + */ +#define SA2400_TX_TXOSQON __BIT(19) +#define SA2400_TX_TXOSQSIGN __BIT(18) +#define SA2400_TX_TXOSQ_MASK __BITS(17,15) +#define SA2400_TX_TXOSION __BIT(14) +#define SA2400_TX_TXOSISIGN __BIT(13) +#define SA2400_TX_TXOSI_MASK __BITS(12,10) + +#define SA2400_TX_RAMP_MASK __BITS(9,8) /* Ramp-up delay, + * 0: 1us + * 1: 2us + * 2: 3us + * 3: 4us + * datasheet says, "ramp-up + * time always 1us". huh? + */ +#define SA2400_TX_HIGAIN_MASK __BITS(7,4) /* Transmitter gain settings + * for TXHI output + */ +#define SA2400_TX_LOGAIN_MASK __BITS(3,0) /* Transmitter gain settings + * for TXLO output + */ + +#define SA2400_VCO 8 /* VCO settings */ +#define SA2400_VCO_ZERO __BITS(6,5) /* always zero */ +#define SA2400_VCO_VCERR __BIT(4)/* VCO calibration error flag---no + * band with low enough frequency + * could be found + */ +#define SA2400_VCO_VCOBAND_MASK __BITS(3,0) /* VCO band, + * write: in test mode, sets + * VCO band + * read: in normal mode, + * the result of + * calibration (VCOCAL). + * 0 = highest + * frequencies + */ +#endif /* _DEV_IC_SA2400REG_H_ */ diff --git a/sys/dev/netif/rtw/si4136reg.h b/sys/dev/netif/rtw/si4136reg.h new file mode 100644 index 0000000000..9b02b88a7f --- /dev/null +++ b/sys/dev/netif/rtw/si4136reg.h @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2005 David Young. All rights reserved. + * + * This code was written by David Young. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY David Young ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL David + * Young BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + * + * $NetBSD: si4136reg.h,v 1.4 2006/03/08 08:26:50 dyoung Exp $ + * $DragonFly: src/sys/dev/netif/rtw/si4136reg.h,v 1.1 2006/09/03 07:37:58 sephe Exp $ + */ + +#ifndef _DEV_IC_SI4136REG_H_ +#define _DEV_IC_SI4136REG_H_ + +/* + * Serial bus format for Silicon Laboratories Si4126/Si4136 RF synthesizer. + */ +#define SI4126_TWI_DATA_MASK __BITS(21, 4) +#define SI4126_TWI_ADDR_MASK __BITS(3, 0) + +/* + * Registers for Silicon Laboratories Si4126/Si4136 RF synthesizer. + */ +#define SI4126_MAIN 0 /* main configuration */ +#define SI4126_MAIN_AUXSEL_MASK __BITS(13, 12) /* aux. output pin function */ +/* reserved */ +#define SI4126_MAIN_AUXSEL_RSVD SHIFTIN(0x0, SI4126_MAIN_AUXSEL_MASK) +/* force low */ +#define SI4126_MAIN_AUXSEL_FRCLOW SHIFTIN(0x1, SI4126_MAIN_AUXSEL_MASK) +/* Lock Detect (LDETB) */ +#define SI4126_MAIN_AUXSEL_LDETB SHIFTIN(0x3, SI4126_MAIN_AUXSEL_MASK) + +#define SI4126_MAIN_IFDIV_MASK __BITS(11, 10) /* IFOUT = IFVCO + * frequency / 2**IFDIV. + */ + +/* 1: divide crystal input (XIN) by 2 */ +#define SI4126_MAIN_XINDIV2 __BIT(6) +#define SI4126_MAIN_LPWR __BIT(5) /* 1: low-power mode */ +#define SI4126_MAIN_AUTOPDB __BIT(3) /* 1: equivalent to + * reg[SI4126_POWER] <- + * SI4126_POWER_PDIB | + * SI4126_POWER_PDRB. + * + * 0: power-down under control + * of reg[SI4126_POWER]. + */ + +#define SI4126_GAIN 1 /* phase detector gain */ +#define SI4126_GAIN_KPI_MASK __BITS(5, 4) /* IF phase detector gain */ +#define SI4126_GAIN_KP2_MASK __BITS(3, 2) /* RF2 phase detector gain */ +#define SI4126_GAIN_KP1_MASK __BITS(1, 0) /* RF1 phase detector gain */ + +#define SI4126_POWER 2 /* powerdown */ +#define SI4126_POWER_PDIB __BIT(1) /* 1: IF synthesizer on */ +#define SI4126_POWER_PDRB __BIT(0) /* 1: RF synthesizer on */ + +#define SI4126_RF1N 3 /* RF1 N divider */ +#define SI4126_RF2N 4 /* RF2 N divider */ +#define SI4126_IFN 5 /* IF N divider */ +#define SI4126_RF1R 6 /* RF1 R divider */ +#define SI4126_RF2R 7 /* RF2 R divider */ +#define SI4126_IFR 8 /* IF R divider */ + +#endif /* _DEV_IC_SI4136REG_H_ */ diff --git a/sys/dev/netif/rtw/smc93cx6.c b/sys/dev/netif/rtw/smc93cx6.c new file mode 100644 index 0000000000..d8e3b171bc --- /dev/null +++ b/sys/dev/netif/rtw/smc93cx6.c @@ -0,0 +1,187 @@ +/* + * Interface for the 93C66/56/46/26/06 serial eeprom parts. + * + * Copyright (c) 1995, 1996 Daniel M. Eischen + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice immediately at the beginning of the file, without modification, + * this list of conditions, and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Absolutely no warranty of function or purpose is made by the author + * Daniel M. Eischen. + * 4. Modifications may be freely made to this file if the above conditions + * are met. + * + * $FreeBSD: src/sys/dev/aic7xxx/93cx6.c,v 1.5 2000/01/07 23:08:17 gibbs Exp $ + * $NetBSD: smc93cx6.c,v 1.12 2005/12/11 12:21:28 christos Exp $ + * $DragonFly: src/sys/dev/netif/rtw/smc93cx6.c,v 1.1 2006/09/03 07:37:58 sephe Exp $ + */ + +/* + * The instruction set of the 93C66/56/46/26/06 chips are as follows: + * + * Start OP * + * Function Bit Code Address** Data Description + * ------------------------------------------------------------------- + * READ 1 10 A5 - A0 Reads data stored in memory, + * starting at specified address + * EWEN 1 00 11XXXX Write enable must precede + * all programming modes + * ERASE 1 11 A5 - A0 Erase register A5A4A3A2A1A0 + * WRITE 1 01 A5 - A0 D15 - D0 Writes register + * ERAL 1 00 10XXXX Erase all registers + * WRAL 1 00 01XXXX D15 - D0 Writes to all registers + * EWDS 1 00 00XXXX Disables all programming + * instructions + * *Note: A value of X for address is a don't care condition. + * **Note: There are 8 address bits for the 93C56/66 chips unlike + * the 93C46/26/06 chips which have 6 address bits. + * + * The 93C46 has a four wire interface: clock, chip select, data in, and + * data out. In order to perform one of the above functions, you need + * to enable the chip select for a clock period (typically a minimum of + * 1 usec, with the clock high and low a minimum of 750 and 250 nsec + * respectively). While the chip select remains high, you can clock in + * the instructions (above) starting with the start bit, followed by the + * OP code, Address, and Data (if needed). For the READ instruction, the + * requested 16-bit register contents is read from the data out line but + * is preceded by an initial zero (leading 0, followed by 16-bits, MSB + * first). The clock cycling from low to high initiates the next data + * bit to be sent from the chip. + * + */ + +#include "opt_aic7xxx.h" + +#include +#include + +#include +#include +#include + +#include "smc93cx6var.h" + +/* + * Right now, we only have to read the SEEPROM. But we make it easier to + * add other 93Cx6 functions. + */ +static struct seeprom_cmd { + unsigned char len; + unsigned char bits[3]; +} seeprom_read = {3, {1, 1, 0}}; + +/* XXX bus barriers */ +#define CLOCK_PULSE(sd, rdy) do { \ + /* \ + * Wait for the SEERDY to go high; about 800 ns. \ + */ \ + int cpi = 1000; \ + if (rdy == 0) { \ + DELAY(4); /* more than long enough */ \ + break; \ + } \ + while ((SEEPROM_STATUS_INB(sd) & rdy) == 0 && cpi-- > 0) { \ + ; /* Do nothing */ \ + } \ + (void)SEEPROM_INB(sd); /* Clear clock */ \ +} while (0) + +/* + * Read the serial EEPROM and returns 1 if successful and 0 if + * not successful. + */ +int +read_seeprom(sd, buf, start_addr, count) + struct seeprom_descriptor *sd; + u_int16_t *buf; + bus_size_t start_addr; + bus_size_t count; +{ + int i = 0; + u_int k = 0; + u_int16_t v; + u_int32_t temp; + + /* + * Read the requested registers of the seeprom. The loop + * will range from 0 to count-1. + */ + for (k = start_addr; k < count + start_addr; k++) { + /* Send chip select for one clock cycle. */ + temp = sd->sd_MS ^ sd->sd_CS; + SEEPROM_OUTB(sd, temp ^ sd->sd_CK); + CLOCK_PULSE(sd, sd->sd_RDY); + + /* + * Now we're ready to send the read command followed by the + * address of the 16-bit register we want to read. + */ + for (i = 0; i < seeprom_read.len; i++) { + if (seeprom_read.bits[i] != 0) + temp ^= sd->sd_DO; + SEEPROM_OUTB(sd, temp); + CLOCK_PULSE(sd, sd->sd_RDY); + SEEPROM_OUTB(sd, temp ^ sd->sd_CK); + CLOCK_PULSE(sd, sd->sd_RDY); + if (seeprom_read.bits[i] != 0) + temp ^= sd->sd_DO; + } + /* Send the 6 or 8 bit address (MSB first, LSB last). */ + for (i = (sd->sd_chip - 1); i >= 0; i--) { + if ((k & (1 << i)) != 0) + temp ^= sd->sd_DO; + SEEPROM_OUTB(sd, temp); + CLOCK_PULSE(sd, sd->sd_RDY); + SEEPROM_OUTB(sd, temp ^ sd->sd_CK); + CLOCK_PULSE(sd, sd->sd_RDY); + if ((k & (1 << i)) != 0) + temp ^= sd->sd_DO; + } + + /* + * Now read the 16 bit register. An initial 0 precedes the + * register contents which begins with bit 15 (MSB) and ends + * with bit 0 (LSB). The initial 0 will be shifted off the + * top of our word as we let the loop run from 0 to 16. + */ + v = 0; + for (i = 16; i >= 0; i--) { + SEEPROM_OUTB(sd, temp); + CLOCK_PULSE(sd, sd->sd_RDY); + v <<= 1; + if (SEEPROM_DATA_INB(sd) & sd->sd_DI) + v |= 1; + SEEPROM_OUTB(sd, temp ^ sd->sd_CK); + CLOCK_PULSE(sd, sd->sd_RDY); + } + + buf[k - start_addr] = v; + + /* Reset the chip select for the next command cycle. */ + temp = sd->sd_MS; + SEEPROM_OUTB(sd, temp); + CLOCK_PULSE(sd, sd->sd_RDY); + SEEPROM_OUTB(sd, temp ^ sd->sd_CK); + CLOCK_PULSE(sd, sd->sd_RDY); + SEEPROM_OUTB(sd, temp); + CLOCK_PULSE(sd, sd->sd_RDY); + } +#ifdef AHC_DUMP_EEPROM + printf("\nSerial EEPROM:\n\t"); + for (k = 0; k < count; k = k + 1) { + if (((k % 8) == 0) && (k != 0)) { + printf ("\n\t"); + } + printf (" 0x%x", buf[k]); + } + printf ("\n"); +#endif + return (1); +} diff --git a/sys/dev/netif/rtw/smc93cx6var.h b/sys/dev/netif/rtw/smc93cx6var.h new file mode 100644 index 0000000000..b6a5d19d49 --- /dev/null +++ b/sys/dev/netif/rtw/smc93cx6var.h @@ -0,0 +1,107 @@ +/* + * Interface to the 93C46 serial EEPROM that is used to store BIOS + * settings for the aic7xxx based adaptec SCSI controllers. It can + * also be used for 93C26 and 93C06 serial EEPROMS. + * + * Copyright (c) 1994, 1995 Justin T. Gibbs. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * the GNU Public License ("GPL"). + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD: src/sys/dev/aic7xxx/aic7xxx.c,v 1.40 2000/01/07 23:08:17 gibbs Exp $ + * $NetBSD: smc93cx6var.h,v 1.9 2005/12/11 12:21:28 christos Exp $ + * $DragonFly: src/sys/dev/netif/rtw/smc93cx6var.h,v 1.1 2006/09/03 07:37:58 sephe Exp $ + */ + +typedef enum { + C46 = 6, + C56_66 = 8 +} seeprom_chip_t; + +struct seeprom_descriptor { + bus_space_tag_t sd_tag; + bus_space_handle_t sd_bsh; + bus_size_t sd_regsize; + bus_size_t sd_control_offset; + bus_size_t sd_status_offset; + bus_size_t sd_dataout_offset; + seeprom_chip_t sd_chip; + uint32_t sd_MS; + uint32_t sd_RDY; + uint32_t sd_CS; + uint32_t sd_CK; + uint32_t sd_DO; + uint32_t sd_DI; +}; + +/* + * This function will read count 16-bit words from the serial EEPROM and + * return their value in buf. The port address of the aic7xxx serial EEPROM + * control register is passed in as offset. The following parameters are + * also passed in: + * + * CS - Chip select + * CK - Clock + * DO - Data out + * DI - Data in + * RDY - SEEPROM ready + * MS - Memory port mode select + * + * A failed read attempt returns 0, and a successful read returns 1. + */ + +/* XXX bus barriers */ +#define SEEPROM_INB(sd) \ + (((sd)->sd_regsize == 4) \ + ? bus_space_read_4((sd)->sd_tag, (sd)->sd_bsh, \ + (sd)->sd_control_offset) \ + : bus_space_read_1((sd)->sd_tag, (sd)->sd_bsh, \ + (sd)->sd_control_offset)) + +#define SEEPROM_OUTB(sd, value) do { \ + if ((sd)->sd_regsize == 4) \ + bus_space_write_4((sd)->sd_tag, (sd)->sd_bsh, \ + (sd)->sd_control_offset, (value)); \ + else \ + bus_space_write_1((sd)->sd_tag, (sd)->sd_bsh, \ + (sd)->sd_control_offset, (u_int8_t) (value)); \ +} while (0) + +#define SEEPROM_STATUS_INB(sd) \ + (((sd)->sd_regsize == 4) \ + ? bus_space_read_4((sd)->sd_tag, (sd)->sd_bsh, \ + (sd)->sd_status_offset) \ + : bus_space_read_1((sd)->sd_tag, (sd)->sd_bsh, \ + (sd)->sd_status_offset)) + +#define SEEPROM_DATA_INB(sd) \ + (((sd)->sd_regsize == 4) \ + ? bus_space_read_4((sd)->sd_tag, (sd)->sd_bsh, \ + (sd)->sd_dataout_offset) \ + : bus_space_read_1((sd)->sd_tag, (sd)->sd_bsh, \ + (sd)->sd_dataout_offset)) + +int read_seeprom(struct seeprom_descriptor *, uint16_t *, bus_size_t, + bus_size_t); diff --git a/sys/i386/conf/GENERIC b/sys/i386/conf/GENERIC index 34269f2df0..9f55947972 100644 --- a/sys/i386/conf/GENERIC +++ b/sys/i386/conf/GENERIC @@ -4,7 +4,7 @@ # Check the LINT configuration file in sys/i386/conf, for an # exhaustive list of options. # -# $DragonFly: src/sys/i386/conf/Attic/GENERIC,v 1.36 2006/05/20 09:13:09 sephe Exp $ +# $DragonFly: src/sys/i386/conf/Attic/GENERIC,v 1.37 2006/09/03 07:37:58 sephe Exp $ machine i386 cpu I386_CPU @@ -229,6 +229,7 @@ device wlan_wep # 802.11 WEP support device an #device awi # PRISM I IEEE 802.11b wireless NIC device ral # Ralink Technology 802.11 wireless NIC +device rtw # RealTek 802.11 wireless NIC # WaveLAN/IEEE 802.11 wireless NICs. Note: the WaveLAN/IEEE really # exists only as a PCMCIA device, so there is no ISA attachment needed # and resources will always be dynamically assigned by the pccard code. diff --git a/sys/i386/conf/LINT b/sys/i386/conf/LINT index 2e831bd75e..ece0a446ae 100644 --- a/sys/i386/conf/LINT +++ b/sys/i386/conf/LINT @@ -3,7 +3,7 @@ # as much of the source tree as it can. # # $FreeBSD: src/sys/i386/conf/LINT,v 1.749.2.144 2003/06/04 17:56:59 sam Exp $ -# $DragonFly: src/sys/i386/conf/Attic/LINT,v 1.89 2006/09/01 15:12:11 sephe Exp $ +# $DragonFly: src/sys/i386/conf/Attic/LINT,v 1.90 2006/09/03 07:37:58 sephe Exp $ # # NB: You probably don't want to try running a kernel built from this # file. Instead, you should start from GENERIC, and add options from @@ -1421,6 +1421,7 @@ device an # Aironet Communications 4500/4800 device ipw # Intel PRO/Wireless 2100 device iwi # Intel PRO/Wireless 2200BG/2915ABG device wi # WaveLAN/IEEE, PRISM-II, Spectrum24 802.11DS +device rtw # RealTek 8180 device acx # TI ACX100/ACX111 device wl0 at isa? port 0x300 # T1 speed ISA/radio lan device xe # Xircom PCMCIA diff --git a/sys/netproto/802_11/ieee80211_crypto.h b/sys/netproto/802_11/ieee80211_crypto.h index 80b2c348eb..6807b4bb6b 100644 --- a/sys/netproto/802_11/ieee80211_crypto.h +++ b/sys/netproto/802_11/ieee80211_crypto.h @@ -30,7 +30,7 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD: src/sys/net80211/ieee80211_crypto.h,v 1.9.2.1 2005/09/03 22:40:02 sam Exp $ - * $DragonFly: src/sys/netproto/802_11/ieee80211_crypto.h,v 1.2 2006/05/18 13:51:46 sephe Exp $ + * $DragonFly: src/sys/netproto/802_11/ieee80211_crypto.h,v 1.3 2006/09/03 07:37:58 sephe Exp $ */ #ifndef _NET80211_IEEE80211_CRYPTO_H_ #define _NET80211_IEEE80211_CRYPTO_H_ @@ -173,6 +173,7 @@ extern const struct ieee80211_cipher ieee80211_cipher_none; void ieee80211_crypto_register(const struct ieee80211_cipher *); void ieee80211_crypto_unregister(const struct ieee80211_cipher *); int ieee80211_crypto_available(u_int cipher); +const struct ieee80211_cipher *ieee80211_crypto_cipher(u_int cipher); struct ieee80211_key *ieee80211_crypto_encap(struct ieee80211com *, struct ieee80211_node *, struct mbuf *); diff --git a/sys/netproto/802_11/wlan/ieee80211_crypto.c b/sys/netproto/802_11/wlan/ieee80211_crypto.c index 9b4bddbb8a..add954907c 100644 --- a/sys/netproto/802_11/wlan/ieee80211_crypto.c +++ b/sys/netproto/802_11/wlan/ieee80211_crypto.c @@ -30,7 +30,7 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD: src/sys/net80211/ieee80211_crypto.c,v 1.10.2.2 2005/09/03 22:40:02 sam Exp $ - * $DragonFly: src/sys/netproto/802_11/wlan/ieee80211_crypto.c,v 1.3 2006/05/18 13:51:46 sephe Exp $ + * $DragonFly: src/sys/netproto/802_11/wlan/ieee80211_crypto.c,v 1.4 2006/09/03 07:37:58 sephe Exp $ */ /* @@ -224,6 +224,12 @@ ieee80211_crypto_available(u_int cipher) return cipher < IEEE80211_CIPHER_MAX && ciphers[cipher] != NULL; } +const struct ieee80211_cipher * +ieee80211_crypto_cipher(u_int cipher) +{ + return cipher < IEEE80211_CIPHER_MAX ? ciphers[cipher] : NULL; +} + /* XXX well-known names! */ static const char *cipher_modnames[] = { "wlan_wep", /* IEEE80211_CIPHER_WEP */