summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJonathan Gray <jsg@cvs.openbsd.org>2004-12-29 01:02:32 +0000
committerJonathan Gray <jsg@cvs.openbsd.org>2004-12-29 01:02:32 +0000
commit30b88baa5b4a771a19cd3d99cb335b5b6f8cdd95 (patch)
treea9c549a66928f56139c310fb5d0a8821e543f77e
parent3c7a1deced17051290ff45a9547be413f9f43340 (diff)
Driver for Realtek 802.11 devices from NetBSD.
Not yet working.
-rw-r--r--share/man/man4/Makefile4
-rw-r--r--share/man/man4/cardbus.45
-rw-r--r--share/man/man4/pci.45
-rw-r--r--share/man/man4/rtw.460
-rw-r--r--sys/conf/files8
-rw-r--r--sys/dev/cardbus/files.cardbus7
-rw-r--r--sys/dev/cardbus/if_rtw_cardbus.c510
-rw-r--r--sys/dev/ic/max2820reg.h190
-rw-r--r--sys/dev/ic/rtw.c3611
-rw-r--r--sys/dev/ic/rtwphy.c651
-rw-r--r--sys/dev/ic/rtwphy.h10
-rw-r--r--sys/dev/ic/rtwphyio.c363
-rw-r--r--sys/dev/ic/rtwphyio.h41
-rw-r--r--sys/dev/ic/rtwreg.h1100
-rw-r--r--sys/dev/ic/rtwvar.h468
-rw-r--r--sys/dev/ic/sa2400reg.h263
-rw-r--r--sys/dev/pci/files.pci6
-rw-r--r--sys/dev/pci/if_rtw_pci.c306
18 files changed, 7601 insertions, 7 deletions
diff --git a/share/man/man4/Makefile b/share/man/man4/Makefile
index b9533e9e22b..563ee82327a 100644
--- a/share/man/man4/Makefile
+++ b/share/man/man4/Makefile
@@ -1,4 +1,4 @@
-# $OpenBSD: Makefile,v 1.285 2004/12/20 19:17:04 jaredy Exp $
+# $OpenBSD: Makefile,v 1.286 2004/12/29 01:02:30 jsg Exp $
MAN= aac.4 ac97.4 acphy.4 addcom.4 adv.4 aha.4 ahb.4 ahc.4 ahd.4 \
aic.4 amdpm.4 ami.4 amphy.4 an.4 aria.4 ast.4 atalk.4 \
@@ -24,7 +24,7 @@ MAN= aac.4 ac97.4 acphy.4 addcom.4 adv.4 aha.4 ahb.4 ahc.4 ahd.4 \
pckbd.4 pcmcia.4 pcppi.4 pcscp.4 pf.4 pflog.4 pfsync.4 piixpm.4 \
pms.4 ppb.4 ppp.4 pppoe.4 pty.4 puc.4 qsphy.4 radio.4 raid.4 \
random.4 ray.4 rd.4 re.4 rgephy.4 rl.4 rln.4 rlphy.4 route.4 rt.4 \
- rtfps.4 rtii.4 safe.4 san.4 sbus.4 scsi.4 sd.4 ses.4 sf.4 \
+ rtfps.4 rtii.4 rtw.4 safe.4 san.4 sbus.4 scsi.4 sd.4 ses.4 sf.4 \
sf2r.4 sfr.4 siop.4 sis.4 sk.4 sl.4 sm.4 spp.4 sppp.4 \
sqphy.4 ss.4 st.4 ste.4 stge.4 sti.4 stp.4 sv.4 \
systrace.4 tb.4 tcic.4 tcp.4 termios.4 ti.4 tl.4 \
diff --git a/share/man/man4/cardbus.4 b/share/man/man4/cardbus.4
index aa37b50c8ea..308b509c57f 100644
--- a/share/man/man4/cardbus.4
+++ b/share/man/man4/cardbus.4
@@ -1,4 +1,4 @@
-.\" $OpenBSD: cardbus.4,v 1.24 2004/12/28 23:53:14 deraadt Exp $
+.\" $OpenBSD: cardbus.4,v 1.25 2004/12/29 01:02:30 jsg Exp $
.\" $NetBSD: cardbus.4,v 1.4 2000/02/02 19:57:17 augustss Exp $
.\"
.\" Copyright (c) 1999 The NetBSD Foundation, Inc.
@@ -65,6 +65,8 @@ Intel 21143 and Xircom X3201-based Ethernet interfaces.
Intel PRO/100 CardBus II Ethernet interfaces.
.It Xr rl 4
Realtek 8129/8139-based Ethernet interfaces.
+.It Xr rtw 4
+Realtek 8180 802.11 wireless network adapter.
.It Xr xl 4
3Com 3c575 and 3c656-based Ethernet interfaces.
.El
@@ -96,6 +98,7 @@ USB OHCI host controller.
.Xr pcibios 4 ,
.Xr pcmcia 4 ,
.Xr rl 4 ,
+.Xr rtw 4 ,
.Xr xl 4
.Sh HISTORY
The
diff --git a/share/man/man4/pci.4 b/share/man/man4/pci.4
index b65bcfb5a28..7bb54d642e6 100644
--- a/share/man/man4/pci.4
+++ b/share/man/man4/pci.4
@@ -1,4 +1,4 @@
-.\" $OpenBSD: pci.4,v 1.107 2004/12/01 15:45:34 jmc Exp $
+.\" $OpenBSD: pci.4,v 1.108 2004/12/29 01:02:30 jsg Exp $
.\" $NetBSD: pci.4,v 1.29 2000/04/01 00:32:23 tsarna Exp $
.\"
.\" Copyright (c) 2000 Theo de Raadt. All rights reserved.
@@ -257,6 +257,8 @@ National Semiconductor DP83820 and DP83821 based Gigabit Ethernet.
RealTek 8169/8169S/8110S Ethernet interfaces.
.It Xr rl 4
Realtek 8129/8139 Ethernet interfaces.
+.It Xr rtw 4
+Realtek 8180 802.11 wireless network adapter.
.It Xr san 4
Sangoma Technologies AFT T1/E1 Network interfaces.
.It Xr sf 4
@@ -547,6 +549,7 @@ driver.
.Xr puc 4 ,
.Xr re 4 ,
.Xr rl 4 ,
+.Xr rtw 4,
.Xr safe 4 ,
.Xr san 4 ,
.Xr schizo 4 ,
diff --git a/share/man/man4/rtw.4 b/share/man/man4/rtw.4
new file mode 100644
index 00000000000..0a7316c01d5
--- /dev/null
+++ b/share/man/man4/rtw.4
@@ -0,0 +1,60 @@
+.\" $OpenBSD: rtw.4,v 1.1 2004/12/29 01:02:30 jsg Exp $
+.\"
+.\" Copyright (c) 2004 Jonathan Gray <jsg@openbsd.org>
+.\"
+.\" Permission to use, copy, modify, and distribute this software for any
+.\" purpose with or without fee is hereby granted, provided that the above
+.\" copyright notice and this permission notice appear in all copies.
+.\"
+.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+.\"
+.Dd December 29, 2004
+.Dt RTW 4
+.Os
+.Sh NAME
+.Nm rtw
+.Nd Realtek 8180 802.11 wireless network driver
+.Sh SYNOPSIS
+.Cd "rtw* at cadbus?"
+.Cd "rtw* at pci?"
+.Sh DESCRIPTION
+The
+.Nm
+driver supports PCI/Cardbus 802.11b wireless adapters based on the
+Realtek 8180.
+.Pp
+The operating mode is selected using the
+.Xr ifconfig 8
+utility.
+For more information on configuring this device, see
+.Xr ifconfig 8
+and
+.Xr ifmedia 4 .
+.El
+.Sh SEE ALSO
+.Xr arp 4 ,
+.Xr cardbus 4 ,
+.Xr ifmedia 4 ,
+.Xr netintro 4 ,
+.Xr pci 4 ,
+.Xr ifconfig 8
+.Rs
+.%T Realtek
+.%O http://www.realtek.com.tw
+.Sh HISTORY
+The
+.Nm
+device driver first appeared in
+.Ox 3.7 .
+.Sh AUTHORS
+The
+.Nm
+driver was written by
+.An David Young
+.Aq dyoung@NetBSD.org .
diff --git a/sys/conf/files b/sys/conf/files
index 4fb7584f2b3..ffdabde27bf 100644
--- a/sys/conf/files
+++ b/sys/conf/files
@@ -1,4 +1,4 @@
-# $OpenBSD: files,v 1.322 2004/12/22 12:02:47 grange Exp $
+# $OpenBSD: files,v 1.323 2004/12/29 01:02:30 jsg Exp $
# $NetBSD: files,v 1.87 1996/05/19 17:17:50 jonathan Exp $
# @(#)files.newconf 7.5 (Berkeley) 5/10/93
@@ -336,6 +336,12 @@ file dev/ic/ath.c ath
device atw: ether, ifnet, ifmedia, smc93cx6, wlan
file dev/ic/atw.c atw
+# Realtek RTL8180 802.11
+device rtw: ether, ifnet, ifmedia, smc93cx6, wlan
+file dev/ic/rtw.c rtw
+file dev/ic/rtwphy.c rtw
+file dev/ic/rtwphyio.c rtw
+
# Attributes which machine-independent bus support can be attached to.
# These should be defined here, because some of these busses can have
# devices which provide these attributes, and we'd like to avoid hairy
diff --git a/sys/dev/cardbus/files.cardbus b/sys/dev/cardbus/files.cardbus
index 0e81f33461c..4cd3678c26d 100644
--- a/sys/dev/cardbus/files.cardbus
+++ b/sys/dev/cardbus/files.cardbus
@@ -1,4 +1,4 @@
-# $OpenBSD: files.cardbus,v 1.11 2004/12/07 05:42:41 dlg Exp $
+# $OpenBSD: files.cardbus,v 1.12 2004/12/29 01:02:30 jsg Exp $
# $NetBSD: files.cardbus,v 1.8 2000/01/26 06:37:24 thorpej Exp $
#
# files.cardbus
@@ -61,6 +61,11 @@ file dev/cardbus/if_ath_cardbus.c ath_cardbus
attach atw at cardbus with atw_cardbus
file dev/cardbus/if_atw_cardbus.c atw_cardbus
+# Realtek RTL8180
+#
+attach rtw at cardbus with rtw_cardbus
+file dev/cardbus/if_rtw_cardbus.c rtw_cardbus
+
#
# EHCI USB controller
#
diff --git a/sys/dev/cardbus/if_rtw_cardbus.c b/sys/dev/cardbus/if_rtw_cardbus.c
new file mode 100644
index 00000000000..ed073de8823
--- /dev/null
+++ b/sys/dev/cardbus/if_rtw_cardbus.c
@@ -0,0 +1,510 @@
+/* $OpenBSD: if_rtw_cardbus.c,v 1.1 2004/12/29 01:02:30 jsg Exp $ */
+/* $NetBSD: if_rtw_cardbus.c,v 1.4 2004/12/20 21:05:34 dyoung Exp $ */
+
+/*-
+ * Copyright (c) 2004, 2005 David Young. All rights reserved.
+ *
+ * Adapted for the RTL8180 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.
+ */
+/*-
+ * Copyright (c) 1999, 2000 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.
+ *
+ * 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.
+ */
+
+/*
+ * Cardbus front-end for the Realtek RTL8180 802.11 MAC/BBP driver.
+ *
+ * TBD factor with atw, tlp Cardbus front-ends?
+ */
+
+#include <sys/cdefs.h>
+
+#include "bpfilter.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/mbuf.h>
+#include <sys/malloc.h>
+#include <sys/kernel.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/errno.h>
+#include <sys/device.h>
+
+#include <machine/endian.h>
+
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/if_media.h>
+#ifdef INET
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+#endif
+
+#include <net80211/ieee80211_compat.h>
+#include <net80211/ieee80211_radiotap.h>
+#include <net80211/ieee80211_var.h>
+
+#if NBPFILTER > 0
+#include <net/bpf.h>
+#endif
+
+#ifdef NS
+#include <netns/ns.h>
+#include <netns/ns_if.h>
+#endif
+
+#include <machine/bus.h>
+#include <machine/intr.h>
+
+#include <dev/ic/rtwreg.h>
+#include <dev/ic/rtwvar.h>
+
+#include <dev/pci/pcivar.h>
+#include <dev/pci/pcireg.h>
+#include <dev/pci/pcidevs.h>
+
+#include <dev/cardbus/cardbusvar.h>
+#include <dev/pci/pcidevs.h>
+
+/*
+ * PCI configuration space registers used by the RTL8180.
+ */
+#define RTW_PCI_IOBA 0x10 /* i/o mapped base */
+#define RTW_PCI_MMBA 0x14 /* memory mapped base */
+
+struct rtw_cardbus_softc {
+ struct rtw_softc sc_rtw; /* real RTL8180 softc */
+
+ /* CardBus-specific goo. */
+ void *sc_ih; /* interrupt handle */
+ cardbus_devfunc_t sc_ct; /* our CardBus devfuncs */
+ cardbustag_t sc_tag; /* our CardBus tag */
+ int sc_csr; /* CSR bits */
+ bus_size_t sc_mapsize; /* size of the mapped bus space
+ * region
+ */
+
+ int sc_cben; /* CardBus enables */
+ int sc_bar_reg; /* which BAR to use */
+ pcireg_t sc_bar_val; /* value of the BAR */
+
+ int sc_intrline; /* interrupt line */
+};
+
+int rtw_cardbus_match(struct device *, void *, void *);
+void rtw_cardbus_attach(struct device *, struct device *, void *);
+int rtw_cardbus_detach(struct device *, int);
+void rtw_cardbus_intr_ack(struct rtw_regs *);
+void rtw_cardbus_funcregen(struct rtw_regs *, int);
+
+struct cfattach rtw_cardbus_ca = {
+ sizeof(struct rtw_cardbus_softc), rtw_cardbus_match, rtw_cardbus_attach,
+ rtw_cardbus_detach
+};
+
+void rtw_cardbus_setup(struct rtw_cardbus_softc *);
+
+int rtw_cardbus_enable(struct rtw_softc *);
+void rtw_cardbus_disable(struct rtw_softc *);
+void rtw_cardbus_power(struct rtw_softc *, int);
+
+const struct rtw_cardbus_product *rtw_cardbus_lookup(
+ const struct cardbus_attach_args *);
+
+const struct rtw_cardbus_product {
+ u_int32_t rcp_vendor; /* PCI vendor ID */
+ u_int32_t rcp_product; /* PCI product ID */
+ const char *rcp_product_name;
+} rtw_cardbus_products[] = {
+ { PCI_VENDOR_REALTEK, PCI_PRODUCT_REALTEK_RT8180,
+ "Realtek RTL8180 802.11 MAC/BBP" },
+
+ { PCI_VENDOR_BELKIN2, PCI_PRODUCT_BELKIN2_F5D6020V3,
+ "Belkin F5D6020v3 802.11b (RTL8180 MAC/BBP)" },
+
+ { 0, 0, NULL },
+};
+
+const struct rtw_cardbus_product *
+rtw_cardbus_lookup(const struct cardbus_attach_args *ca)
+{
+ const struct rtw_cardbus_product *rcp;
+
+ for (rcp = rtw_cardbus_products;
+ rcp->rcp_product_name != NULL;
+ rcp++) {
+ if (PCI_VENDOR(ca->ca_id) == rcp->rcp_vendor &&
+ PCI_PRODUCT(ca->ca_id) == rcp->rcp_product)
+ return (rcp);
+ }
+ return (NULL);
+}
+
+int
+rtw_cardbus_match(struct device *parent, void *match, void *aux)
+{
+ struct cardbus_attach_args *ca = aux;
+
+ if (rtw_cardbus_lookup(ca) != NULL)
+ return (1);
+
+ return (0);
+}
+
+void
+rtw_cardbus_intr_ack(struct rtw_regs *regs)
+{
+ RTW_WRITE(regs, RTW_FER, RTW_FER_INTR);
+}
+
+void
+rtw_cardbus_funcregen(struct rtw_regs *regs, int enable)
+{
+ u_int32_t reg;
+ rtw_config0123_enable(regs, 1);
+ reg = RTW_READ(regs, RTW_CONFIG3);
+ if (enable) {
+ RTW_WRITE(regs, RTW_CONFIG3, reg | RTW_CONFIG3_FUNCREGEN);
+ } else {
+ RTW_WRITE(regs, RTW_CONFIG3, reg & ~RTW_CONFIG3_FUNCREGEN);
+ }
+ rtw_config0123_enable(regs, 0);
+}
+
+void
+rtw_cardbus_attach(struct device *parent, struct device *self, void *aux)
+{
+ struct rtw_cardbus_softc *csc = (void *)self;
+ struct rtw_softc *sc = &csc->sc_rtw;
+ struct rtw_regs *regs = &sc->sc_regs;
+ struct cardbus_attach_args *ca = aux;
+ cardbus_devfunc_t ct = ca->ca_ct;
+ const struct rtw_cardbus_product *rcp;
+ bus_addr_t adr;
+ int rev;
+
+ sc->sc_dmat = ca->ca_dmat;
+ csc->sc_ct = ct;
+ csc->sc_tag = ca->ca_tag;
+
+ rcp = rtw_cardbus_lookup(ca);
+ if (rcp == NULL) {
+ printf("\n");
+ panic("rtw_cardbus_attach: impossible");
+ }
+
+ /*
+ * Power management hooks.
+ */
+ sc->sc_enable = rtw_cardbus_enable;
+ sc->sc_disable = rtw_cardbus_disable;
+ sc->sc_power = rtw_cardbus_power;
+
+ sc->sc_intr_ack = rtw_cardbus_intr_ack;
+
+ /* Get revision info. */
+ rev = PCI_REVISION(ca->ca_class);
+
+ RTW_DPRINTF(RTW_DEBUG_ATTACH,
+ ("%s: pass %d.%d signature %08x\n", sc->sc_dev.dv_xname,
+ (rev >> 4) & 0xf, rev & 0xf,
+ cardbus_conf_read(ct->ct_cc, ct->ct_cf, csc->sc_tag, 0x80)));
+
+ /*
+ * Map the device.
+ */
+ csc->sc_csr = CARDBUS_COMMAND_MASTER_ENABLE;
+ if (Cardbus_mapreg_map(ct, RTW_PCI_MMBA,
+ CARDBUS_MAPREG_TYPE_MEM, 0, &regs->r_bt, &regs->r_bh, &adr,
+ &csc->sc_mapsize) == 0) {
+ RTW_DPRINTF(RTW_DEBUG_ATTACH,
+ ("%s: %s mapped %lu bytes mem space\n",
+ sc->sc_dev.dv_xname, __func__, (long)csc->sc_mapsize));
+#if rbus
+#else
+ (*ct->ct_cf->cardbus_mem_open)(cc, 0, adr, adr+csc->sc_mapsize);
+#endif
+ csc->sc_cben = CARDBUS_MEM_ENABLE;
+ csc->sc_csr |= CARDBUS_COMMAND_MEM_ENABLE;
+ csc->sc_bar_reg = RTW_PCI_MMBA;
+ csc->sc_bar_val = adr | CARDBUS_MAPREG_TYPE_MEM;
+ } else if (Cardbus_mapreg_map(ct, RTW_PCI_IOBA,
+ CARDBUS_MAPREG_TYPE_IO, 0, &regs->r_bt, &regs->r_bh, &adr,
+ &csc->sc_mapsize) == 0) {
+ RTW_DPRINTF(RTW_DEBUG_ATTACH,
+ ("%s: %s mapped %lu bytes I/O space\n",
+ sc->sc_dev.dv_xname, __func__, (long)csc->sc_mapsize));
+#if rbus
+#else
+ (*ct->ct_cf->cardbus_io_open)(cc, 0, adr, adr+csc->sc_mapsize);
+#endif
+ csc->sc_cben = CARDBUS_IO_ENABLE;
+ csc->sc_csr |= CARDBUS_COMMAND_IO_ENABLE;
+ csc->sc_bar_reg = RTW_PCI_IOBA;
+ csc->sc_bar_val = adr | CARDBUS_MAPREG_TYPE_IO;
+ } else {
+ printf("%s: unable to map device registers\n",
+ sc->sc_dev.dv_xname);
+ return;
+ }
+
+ /*
+ * Bring the chip out of powersave mode and initialize the
+ * configuration registers.
+ */
+ rtw_cardbus_setup(csc);
+
+ /* Remember which interrupt line. */
+ csc->sc_intrline = ca->ca_intrline;
+
+ printf("%s, irq %d\n", sc->sc_dev.dv_xname,
+ csc->sc_intrline);
+ /*
+ * Finish off the attach.
+ */
+ rtw_attach(sc);
+
+ rtw_cardbus_funcregen(regs, 1);
+
+ RTW_WRITE(regs, RTW_FEMR, RTW_FEMR_INTR);
+ RTW_WRITE(regs, RTW_FER, RTW_FER_INTR);
+
+ /*
+ * Power down the socket.
+ */
+ Cardbus_function_disable(csc->sc_ct);
+}
+
+int
+rtw_cardbus_detach(struct device *self, int flags)
+{
+ struct rtw_cardbus_softc *csc = (void *)self;
+ struct rtw_softc *sc = &csc->sc_rtw;
+ struct rtw_regs *regs = &sc->sc_regs;
+ struct cardbus_devfunc *ct = csc->sc_ct;
+ int rv;
+
+#if defined(DIAGNOSTIC)
+ if (ct == NULL)
+ panic("%s: data structure lacks", sc->sc_dev.dv_xname);
+#endif
+
+ rv = rtw_detach(sc);
+ if (rv)
+ return (rv);
+
+ rtw_cardbus_funcregen(regs, 0);
+
+ /*
+ * Unhook the interrupt handler.
+ */
+ if (csc->sc_ih != NULL)
+ cardbus_intr_disestablish(ct->ct_cc, ct->ct_cf, csc->sc_ih);
+
+ /*
+ * Release bus space and close window.
+ */
+ if (csc->sc_bar_reg != 0)
+ Cardbus_mapreg_unmap(ct, csc->sc_bar_reg,
+ regs->r_bt, regs->r_bh, csc->sc_mapsize);
+
+ return (0);
+}
+
+int
+rtw_cardbus_enable(struct rtw_softc *sc)
+{
+ struct rtw_cardbus_softc *csc = (void *) sc;
+ cardbus_devfunc_t ct = csc->sc_ct;
+ cardbus_chipset_tag_t cc = ct->ct_cc;
+ cardbus_function_tag_t cf = ct->ct_cf;
+
+ /*
+ * Power on the socket.
+ */
+ Cardbus_function_enable(ct);
+
+ /*
+ * Set up the PCI configuration registers.
+ */
+ rtw_cardbus_setup(csc);
+
+ /*
+ * Map and establish the interrupt.
+ */
+ csc->sc_ih = cardbus_intr_establish(cc, cf, csc->sc_intrline, IPL_NET,
+ rtw_intr, sc);
+ if (csc->sc_ih == NULL) {
+ printf("%s: unable to establish interrupt at %d\n",
+ sc->sc_dev.dv_xname, csc->sc_intrline);
+ Cardbus_function_disable(csc->sc_ct);
+ return (1);
+ }
+
+ rtw_cardbus_funcregen(&sc->sc_regs, 1);
+
+ RTW_WRITE(&sc->sc_regs, RTW_FEMR, RTW_FEMR_INTR);
+ RTW_WRITE(&sc->sc_regs, RTW_FER, RTW_FER_INTR);
+
+ return (0);
+}
+
+void
+rtw_cardbus_disable(struct rtw_softc *sc)
+{
+ struct rtw_cardbus_softc *csc = (void *) sc;
+ cardbus_devfunc_t ct = csc->sc_ct;
+ cardbus_chipset_tag_t cc = ct->ct_cc;
+ cardbus_function_tag_t cf = ct->ct_cf;
+
+ RTW_WRITE(&sc->sc_regs, RTW_FEMR,
+ RTW_READ(&sc->sc_regs, RTW_FEMR) & ~RTW_FEMR_INTR);
+
+ rtw_cardbus_funcregen(&sc->sc_regs, 0);
+
+ /* Unhook the interrupt handler. */
+ cardbus_intr_disestablish(cc, cf, csc->sc_ih);
+ csc->sc_ih = NULL;
+
+ /* Power down the socket. */
+ Cardbus_function_disable(ct);
+}
+
+void
+rtw_cardbus_power(struct rtw_softc *sc, int why)
+{
+ struct rtw_cardbus_softc *csc = (void *) sc;
+
+ RTW_DPRINTF(RTW_DEBUG_ATTACH,
+ ("%s: rtw_cardbus_power\n", sc->sc_dev.dv_xname));
+
+ if (why == PWR_RESUME) {
+ /*
+ * Give the PCI configuration registers a kick
+ * in the head.
+ */
+#ifdef DIAGNOSTIC
+ if ((sc->sc_flags & RTW_F_ENABLED) == 0)
+ panic("rtw_cardbus_power");
+#endif
+ rtw_cardbus_setup(csc);
+ }
+}
+
+void
+rtw_cardbus_setup(struct rtw_cardbus_softc *csc)
+{
+ struct rtw_softc *sc = &csc->sc_rtw;
+ cardbus_devfunc_t ct = csc->sc_ct;
+ cardbus_chipset_tag_t cc = ct->ct_cc;
+ cardbus_function_tag_t cf = ct->ct_cf;
+ pcireg_t reg;
+ int pmreg;
+
+ if (cardbus_get_capability(cc, cf, csc->sc_tag,
+ PCI_CAP_PWRMGMT, &pmreg, 0)) {
+ reg = cardbus_conf_read(cc, cf, csc->sc_tag, pmreg + 4) & 0x03;
+#if 1 /* XXX Probably not right for CardBus. */
+ if (reg == 3) {
+ /*
+ * The card has lost all configuration data in
+ * this state, so punt.
+ */
+ printf("%s: unable to wake up from power state D3\n",
+ sc->sc_dev.dv_xname);
+ return;
+ }
+#endif
+ if (reg != 0) {
+ printf("%s: waking up from power state D%d\n",
+ sc->sc_dev.dv_xname, reg);
+ cardbus_conf_write(cc, cf, csc->sc_tag,
+ pmreg + 4, 0);
+ }
+ }
+
+ /* Program the BAR. */
+ cardbus_conf_write(cc, cf, csc->sc_tag, csc->sc_bar_reg,
+ csc->sc_bar_val);
+
+ /* Make sure the right access type is on the CardBus bridge. */
+ (*ct->ct_cf->cardbus_ctrl)(cc, csc->sc_cben);
+ (*ct->ct_cf->cardbus_ctrl)(cc, CARDBUS_BM_ENABLE);
+
+ /* Enable the appropriate bits in the PCI CSR. */
+ reg = cardbus_conf_read(cc, cf, csc->sc_tag,
+ CARDBUS_COMMAND_STATUS_REG);
+ reg &= ~(CARDBUS_COMMAND_IO_ENABLE|CARDBUS_COMMAND_MEM_ENABLE);
+ reg |= csc->sc_csr;
+ cardbus_conf_write(cc, cf, csc->sc_tag, CARDBUS_COMMAND_STATUS_REG,
+ reg);
+
+ /*
+ * Make sure the latency timer is set to some reasonable
+ * value.
+ */
+ reg = cardbus_conf_read(cc, cf, csc->sc_tag, CARDBUS_BHLC_REG);
+ if (CARDBUS_LATTIMER(reg) < 0x20) {
+ reg &= ~(CARDBUS_LATTIMER_MASK << CARDBUS_LATTIMER_SHIFT);
+ reg |= (0x20 << CARDBUS_LATTIMER_SHIFT);
+ cardbus_conf_write(cc, cf, csc->sc_tag, CARDBUS_BHLC_REG, reg);
+ }
+}
diff --git a/sys/dev/ic/max2820reg.h b/sys/dev/ic/max2820reg.h
new file mode 100644
index 00000000000..9be5b749d8d
--- /dev/null
+++ b/sys/dev/ic/max2820reg.h
@@ -0,0 +1,190 @@
+/* $OpenBSD: max2820reg.h,v 1.1 2004/12/29 01:02:31 jsg Exp $ */
+/* $NetBSD: max2820reg.h,v 1.1 2004/09/26 02:29:15 dyoung Exp $ */
+
+/*
+ * 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.
+ */
+
+#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 LSHIFT(0, MAX2820_SYNTH_R_MASK)
+#define MAX2820_SYNTH_R_44MHZ LSHIFT(1, MAX2820_SYNTH_R_MASK)
+#define MAX2820_SYNTH_ICP_DEFAULT MAX2820_SYNTH_ICP
+#define MAX2820_SYNTH_R_DEFAULT LSHIFT(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 LSHIFT(0, MAX2820_CHANNEL_RSVD)
+#define MAX2820_CHANNEL_CF_DEFAULT LSHIFT(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 LSHIFT(0, MAX2820_RECEIVE_BW_MASK)
+#define MAX2820_RECEIVE_BW_8MHZ LSHIFT(1, MAX2820_RECEIVE_BW_MASK)
+#define MAX2820_RECEIVE_BW_7_5MHZ LSHIFT(2, MAX2820_RECEIVE_BW_MASK)
+#define MAX2820_RECEIVE_BW_7MHZ LSHIFT(3, MAX2820_RECEIVE_BW_MASK)
+#define MAX2820_RECEIVE_BW_6_5MHZ LSHIFT(4, MAX2820_RECEIVE_BW_MASK)
+#define MAX2820_RECEIVE_BW_6MHZ LSHIFT(5, MAX2820_RECEIVE_BW_MASK)
+#define MAX2820_RECEIVE_2C_DEFAULT LSHIFT(7, MAX2820_RECEIVE_2C_MASK)
+#define MAX2820_RECEIVE_1C_DEFAULT LSHIFT(7, MAX2820_RECEIVE_1C_MASK)
+#define MAX2820_RECEIVE_DL_DEFAULT LSHIFT(1, MAX2820_RECEIVE_DL_MASK)
+#define MAX2820_RECEIVE_SF_DEFAULT LSHIFT(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 LSHIFT(7, MAX2820A_RECEIVE_2C_MASK)
+/* VGA DC Offset Nulling Parameter 1 */
+#define MAX2820A_RECEIVE_1C_MASK BITS(8,6)
+#define MAX2820A_RECEIVE_1C_DEFAULT LSHIFT(7, MAX2820A_RECEIVE_1C_MASK)
+#define MAX2820A_RECEIVE_RSVD0_MASK BITS(5,3)
+#define MAX2820A_RECEIVE_RSVD0_DEFAULT LSHIFT(2, MAX2820A_RECEIVE_RSVD0_MASK)
+#define MAX2820A_RECEIVE_RSVD1_MASK BITS(2,0)
+#define MAX2820A_RECEIVE_RSVD1_DEFAULT LSHIFT(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 LSHIFT(0, MAX2820_TRANSMIT_PA_MASK)
+
+#endif /* _DEV_IC_MAX2820REG_H_ */
diff --git a/sys/dev/ic/rtw.c b/sys/dev/ic/rtw.c
new file mode 100644
index 00000000000..dcd99ae6d3c
--- /dev/null
+++ b/sys/dev/ic/rtw.c
@@ -0,0 +1,3611 @@
+/* $OpenBSD: rtw.c,v 1.1 2004/12/29 01:02:31 jsg Exp $ */
+/* $NetBSD: rtw.c,v 1.29 2004/12/27 19:49:16 dyoung 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 <sys/cdefs.h>
+#include "bpfilter.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/mbuf.h>
+#include <sys/malloc.h>
+#include <sys/kernel.h>
+#if 0
+#include <sys/errno.h>
+#include <sys/device.h>
+#endif
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/types.h>
+
+#include <machine/endian.h>
+#include <machine/bus.h>
+#include <machine/intr.h> /* splnet */
+
+#include <uvm/uvm_extern.h>
+
+#include <net/if.h>
+#include <net/if_media.h>
+
+#if NBPFILTER > 0
+#include <net/bpf.h>
+#endif
+
+#ifdef INET
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+#endif
+
+#include <net80211/ieee80211_var.h>
+#include <net80211/ieee80211_compat.h>
+#include <net80211/ieee80211_radiotap.h>
+
+#include <dev/ic/rtwreg.h>
+#include <dev/ic/rtwvar.h>
+#include <dev/ic/rtwphyio.h>
+#include <dev/ic/rtwphy.h>
+
+#include <dev/ic/smc93cx6var.h>
+
+#define KASSERT2(__cond, __msg) \
+ do { \
+ if (!(__cond)) \
+ panic __msg ; \
+ } while (0)
+
+int rtw_rfprog_fallback = 0;
+int rtw_host_rfio = 0;
+int rtw_flush_rfio = 1;
+int rtw_rfio_delay = 0;
+
+#ifdef RTW_DEBUG
+int rtw_debug = 0;
+#endif /* RTW_DEBUG */
+
+#define NEXT_ATTACH_STATE(sc, state) do { \
+ DPRINTF(sc, RTW_DEBUG_ATTACH, \
+ ("%s: attach state %s\n", __func__, #state)); \
+ sc->sc_attach_state = state; \
+} while (0)
+
+int rtw_dwelltime = 1000; /* milliseconds */
+
+void rtw_start(struct ifnet *);
+void rtw_srom_defaults(struct rtw_srom *, u_int32_t *, u_int8_t *,
+ enum rtw_rfchipid *, u_int32_t *);
+void rtw_txdesc_blk_init_all(struct rtw_txdesc_blk *);
+void rtw_txctl_blk_init_all(struct rtw_txctl_blk *);
+void rtw_txdescs_sync(bus_dma_tag_t, bus_dmamap_t , struct rtw_txdesc_blk *,
+ u_int, u_int, int);
+void rtw_txdescs_sync_all(bus_dma_tag_t, bus_dmamap_t,
+ struct rtw_txdesc_blk *);
+void rtw_rxbufs_release(bus_dma_tag_t, struct rtw_rxctl *);
+void rtw_rxdesc_init_all(bus_dma_tag_t, bus_dmamap_t,
+ struct rtw_rxdesc *, struct rtw_rxctl *, int);
+void rtw_io_enable(struct rtw_regs *, u_int8_t, int);
+void rtw_intr_rx(struct rtw_softc *, u_int16_t);
+void rtw_intr_beacon(struct rtw_softc *, u_int16_t);
+void rtw_intr_atim(struct rtw_softc *);
+void rtw_transmit_config(struct rtw_regs *);
+void rtw_pktfilt_load(struct rtw_softc *);
+void rtw_start(struct ifnet *);
+void rtw_watchdog(struct ifnet *);
+void rtw_start_beacon(struct rtw_softc *, int);
+void rtw_next_scan(void *);
+void rtw_recv_mgmt(struct ieee80211com *, struct mbuf *,
+ struct ieee80211_node *, int, int, u_int32_t);
+struct ieee80211_node * rtw_node_alloc(struct ieee80211com *);
+void rtw_node_free(struct ieee80211com *, struct ieee80211_node *);
+void rtw_media_status(struct ifnet *, struct ifmediareq *);
+void rtw_txctl_blk_cleanup_all(struct rtw_softc *);
+void rtw_txdesc_blk_setup(struct rtw_txdesc_blk *, struct rtw_txdesc *,
+ u_int, bus_addr_t, bus_addr_t);
+void rtw_txdesc_blk_setup_all(struct rtw_softc *);
+void rtw_intr_tx(struct rtw_softc *, u_int16_t);
+void rtw_intr_ioerror(struct rtw_softc *, u_int16_t);
+void rtw_intr_timeout(struct rtw_softc *);
+void rtw_stop(struct ifnet *, int);
+void rtw_maxim_pwrstate(struct rtw_regs *, enum rtw_pwrstate, int, int);
+void rtw_philips_pwrstate(struct rtw_regs *, enum rtw_pwrstate, int, int);
+void rtw_pwrstate0(struct rtw_softc *, enum rtw_pwrstate, int, int);
+void rtw_recv_beacon(struct rtw_softc *, struct mbuf *,
+ struct ieee80211_node *, int, int, u_int32_t);
+void rtw_join_bss(struct rtw_softc *, uint8_t *, enum ieee80211_opmode,
+ uint16_t);
+void rtw_set_access1(struct rtw_regs *, enum rtw_access, enum rtw_access);
+int rtw_srom_parse(struct rtw_srom *, u_int32_t *, u_int8_t *,
+ enum rtw_rfchipid *, u_int32_t *, enum rtw_locale *, const char *);
+int rtw_srom_read(struct rtw_regs *, u_int32_t, struct rtw_srom *,
+ const char *);
+void rtw_set_rfprog(struct rtw_regs *, enum rtw_rfchipid,
+ const char *);
+u_int8_t rtw_chan2txpower(struct rtw_srom *, struct ieee80211com *,
+ struct ieee80211_channel *);
+int rtw_txctl_blk_init(struct rtw_txctl_blk *);
+int rtw_rxctl_init_all(bus_dma_tag_t, struct rtw_rxctl *, u_int *,
+ const char *);
+void rtw_txbuf_release(bus_dma_tag_t, struct ieee80211com *,
+ struct rtw_txctl *);
+void rtw_txbufs_release(bus_dma_tag_t, bus_dmamap_t,
+ struct ieee80211com *, struct rtw_txctl_blk *);
+void rtw_hwring_setup(struct rtw_softc *);
+void rtw_swring_setup(struct rtw_softc *);
+void rtw_txdesc_blk_reset(struct rtw_txdesc_blk *);
+void rtw_txdescs_reset(struct rtw_softc *);
+void rtw_rxdescs_reset(struct rtw_softc *);
+void rtw_rfmd_pwrstate(struct rtw_regs *, enum rtw_pwrstate, int, int);
+int rtw_pwrstate(struct rtw_softc *, enum rtw_pwrstate);
+int rtw_tune(struct rtw_softc *);
+void rtw_set_nettype(struct rtw_softc *, enum ieee80211_opmode);
+int rtw_init(struct ifnet *);
+int rtw_ioctl(struct ifnet *, u_long, caddr_t);
+int rtw_seg_too_short(bus_dmamap_t);
+struct mbuf * rtw_dmamap_load_txbuf(bus_dma_tag_t, bus_dmamap_t, struct mbuf *,
+ u_int, short *, const char *);
+int rtw_newstate(struct ieee80211com *, enum ieee80211_state, int);
+int rtw_media_change(struct ifnet *);
+int rtw_txctl_blk_setup_all(struct rtw_softc *);
+struct rtw_rf * rtw_rf_attach(struct rtw_softc *, enum rtw_rfchipid,
+ rtw_rf_write_t, int);
+u_int8_t rtw_check_phydelay(struct rtw_regs *, u_int32_t);
+
+#ifdef RTW_DEBUG
+void rtw_print_txdesc(struct rtw_softc *, const char *,
+ struct rtw_txctl *, struct rtw_txdesc_blk *, int);
+const char * rtw_access_string(enum rtw_access);
+void rtw_dump_rings(struct rtw_softc *);
+void rtw_print_txdesc(struct rtw_softc *, const char *,
+ struct rtw_txctl *, struct rtw_txdesc_blk *, int);
+void rtw_print_regs(struct rtw_regs *, const char *, const char *);
+#endif
+
+#ifdef RTW_DEBUG
+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 */
+
+
+void
+rtw_continuous_tx_enable(struct rtw_softc *sc, int enable)
+{
+ struct rtw_regs *regs = &sc->sc_regs;
+
+ u_int32_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
+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
+
+void
+rtw_set_access1(struct rtw_regs *regs,
+ enum rtw_access oaccess, enum rtw_access naccess)
+{
+ KASSERT(naccess >= RTW_ACCESS_NONE && naccess <= RTW_ACCESS_ANAPARM);
+ KASSERT(oaccess >= RTW_ACCESS_NONE && oaccess <= RTW_ACCESS_ANAPARM);
+
+ if (naccess == oaccess)
+ return;
+
+ switch (naccess) {
+ case RTW_ACCESS_NONE:
+ switch (oaccess) {
+ 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 (oaccess) {
+ 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 (oaccess) {
+ 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;
+ }
+}
+
+void
+rtw_set_access(struct rtw_softc *sc, enum rtw_access access)
+{
+ rtw_set_access1(&sc->sc_regs, sc->sc_access, access);
+ RTW_DPRINTF(RTW_DEBUG_ACCESS,
+ ("%s: access %s -> %s\n", sc->sc_dev.dv_xname,
+ rtw_access_string(sc->sc_access),
+ rtw_access_string(access)));
+ sc->sc_access = access;
+}
+
+/*
+ * Enable registers, switch register banks.
+ */
+void
+rtw_config0123_enable(struct rtw_regs *regs, int enable)
+{
+ u_int8_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) */
+void
+rtw_anaparm_enable(struct rtw_regs *regs, int enable)
+{
+ u_int8_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) */
+void
+rtw_txdac_enable(struct rtw_softc *sc, int enable)
+{
+ u_int32_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 __inline int
+rtw_chip_reset1(struct rtw_regs *regs, const char *dvname)
+{
+ u_int8_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", dvname, i));
+ return 0;
+ }
+ RTW_RBR(regs, RTW_CR, RTW_CR);
+ DELAY(10); /* 10us */
+ }
+
+ printf("%s: reset failed\n", dvname);
+ return ETIMEDOUT;
+}
+
+static __inline int
+rtw_chip_reset(struct rtw_regs *regs, const char *dvname)
+{
+ uint32_t tcr;
+
+ /* from Linux driver */
+ tcr = RTW_TCR_CWMIN | RTW_TCR_MXDMA_2048 |
+ LSHIFT(7, RTW_TCR_SRL_MASK) | LSHIFT(7, RTW_TCR_LRL_MASK);
+
+ RTW_WRITE(regs, RTW_TCR, tcr);
+
+ RTW_WBW(regs, RTW_CR, RTW_TCR);
+
+ return rtw_chip_reset1(regs, dvname);
+}
+
+static __inline int
+rtw_recall_eeprom(struct rtw_regs *regs, const char *dvname)
+{
+ int i;
+ u_int8_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 2.5ms for completion */
+ for (i = 0; i < 25; 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", dvname, i * 100));
+ return 0;
+ }
+ RTW_RBR(regs, RTW_9346CR, RTW_9346CR);
+ DELAY(100);
+ }
+ printf("%s: recall EEPROM failed\n", dvname);
+ return ETIMEDOUT;
+}
+
+static __inline int
+rtw_reset(struct rtw_softc *sc)
+{
+ int rc;
+ uint8_t config1;
+
+ if ((rc = rtw_chip_reset(&sc->sc_regs, sc->sc_dev.dv_xname)) != 0)
+ return rc;
+
+ if ((rc = rtw_recall_eeprom(&sc->sc_regs, sc->sc_dev.dv_xname)) != 0)
+ ;
+
+ config1 = RTW_READ8(&sc->sc_regs, RTW_CONFIG1);
+ RTW_WRITE8(&sc->sc_regs, RTW_CONFIG1, config1 & ~RTW_CONFIG1_PMEN);
+ /* TBD turn off maximum power saving? */
+
+ return 0;
+}
+
+static __inline int
+rtw_txdesc_dmamaps_create(bus_dma_tag_t dmat, struct rtw_txctl *descs,
+ u_int ndescs)
+{
+ int i, rc = 0;
+ for (i = 0; i < ndescs; i++) {
+ rc = bus_dmamap_create(dmat, MCLBYTES, RTW_MAXPKTSEGS, MCLBYTES,
+ 0, 0, &descs[i].stx_dmamap);
+ if (rc != 0)
+ break;
+ }
+ return rc;
+}
+
+static __inline int
+rtw_rxdesc_dmamaps_create(bus_dma_tag_t dmat, struct rtw_rxctl *descs,
+ u_int ndescs)
+{
+ int i, rc = 0;
+ for (i = 0; i < ndescs; i++) {
+ rc = bus_dmamap_create(dmat, MCLBYTES, 1, MCLBYTES, 0, 0,
+ &descs[i].srx_dmamap);
+ if (rc != 0)
+ break;
+ }
+ return rc;
+}
+
+static __inline void
+rtw_rxctls_setup(struct rtw_rxctl *descs)
+{
+ int i;
+ for (i = 0; i < RTW_RXQLEN; i++)
+ descs[i].srx_mbuf = NULL;
+}
+
+static __inline void
+rtw_rxdesc_dmamaps_destroy(bus_dma_tag_t dmat, struct rtw_rxctl *descs,
+ u_int ndescs)
+{
+ int i;
+ for (i = 0; i < ndescs; i++) {
+ if (descs[i].srx_dmamap != NULL)
+ bus_dmamap_destroy(dmat, descs[i].srx_dmamap);
+ }
+}
+
+static __inline void
+rtw_txdesc_dmamaps_destroy(bus_dma_tag_t dmat, struct rtw_txctl *descs,
+ u_int ndescs)
+{
+ int i;
+ for (i = 0; i < ndescs; i++) {
+ if (descs[i].stx_dmamap != NULL)
+ bus_dmamap_destroy(dmat, descs[i].stx_dmamap);
+ }
+}
+
+static __inline void
+rtw_srom_free(struct rtw_srom *sr)
+{
+ sr->sr_size = 0;
+ if (sr->sr_content == NULL)
+ return;
+ free(sr->sr_content, M_DEVBUF);
+ sr->sr_content = NULL;
+}
+
+void
+rtw_srom_defaults(struct rtw_srom *sr, u_int32_t *flags, u_int8_t *cs_threshold,
+ enum rtw_rfchipid *rfchipid, u_int32_t *rcr)
+{
+ *flags |= (RTW_F_DIGPHY|RTW_F_ANTDIV);
+ *cs_threshold = RTW_SR_ENERGYDETTHR_DEFAULT;
+ *rcr |= RTW_RCR_ENCS1;
+ *rfchipid = RTW_RFCHIPID_PHILIPS;
+}
+
+int
+rtw_srom_parse(struct rtw_srom *sr, u_int32_t *flags, u_int8_t *cs_threshold,
+ enum rtw_rfchipid *rfchipid, u_int32_t *rcr, enum rtw_locale *locale,
+ const char *dvname)
+{
+ int i;
+ const char *rfname, *paname;
+ char scratch[sizeof("unknown 0xXX")];
+ u_int16_t version;
+ u_int8_t mac[IEEE80211_ADDR_LEN];
+
+ *flags &= ~(RTW_F_DIGPHY|RTW_F_DFLANTB|RTW_F_ANTDIV);
+ *rcr &= ~(RTW_RCR_ENCS1 | RTW_RCR_ENCS2);
+
+ version = RTW_SR_GET16(sr, RTW_SR_VERSION);
+ printf("%s: SROM version %d.%d", dvname, version >> 8, version & 0xff);
+
+ if (version <= 0x0101) {
+ printf(" is not understood, limping along with defaults\n");
+ rtw_srom_defaults(sr, flags, cs_threshold, rfchipid, rcr);
+ 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 %s\n", dvname, ether_sprintf(mac)));
+
+ *cs_threshold = RTW_SR_GET(sr, RTW_SR_ENERGYDETTHR);
+
+ if ((RTW_SR_GET(sr, RTW_SR_CONFIG2) & RTW_CONFIG2_ANT) != 0)
+ *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)
+ *flags |= RTW_F_DIGPHY;
+ if ((RTW_SR_GET(sr, RTW_SR_RFPARM) & RTW_SR_RFPARM_DFLANTB) != 0)
+ *flags |= RTW_F_DFLANTB;
+
+ *rcr |= LSHIFT(MASK_AND_RSHIFT(RTW_SR_GET(sr, RTW_SR_RFPARM),
+ RTW_SR_RFPARM_CS_MASK), RTW_RCR_ENCS1);
+
+ *rfchipid = RTW_SR_GET(sr, RTW_SR_RFCHIPID);
+ switch (*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 <unknown>";
+ 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", *rfchipid);
+ rfname = paname = scratch;
+ }
+ printf("%s: RF: %s, PA: %s\n", dvname, rfname, paname);
+
+ switch (RTW_SR_GET(sr, RTW_SR_CONFIG0) & RTW_CONFIG0_GL_MASK) {
+ case RTW_CONFIG0_GL_USA:
+ *locale = RTW_LOCALE_USA;
+ break;
+ case RTW_CONFIG0_GL_EUROPE:
+ *locale = RTW_LOCALE_EUROPE;
+ break;
+ case RTW_CONFIG0_GL_JAPAN:
+ *locale = RTW_LOCALE_JAPAN;
+ break;
+ default:
+ *locale = RTW_LOCALE_UNKNOWN;
+ break;
+ }
+ return 0;
+}
+
+/* Returns -1 on failure. */
+int
+rtw_srom_read(struct rtw_regs *regs, u_int32_t flags, struct rtw_srom *sr,
+ const char *dvname)
+{
+ int rc;
+ struct seeprom_descriptor sd;
+ u_int8_t ecr;
+
+ (void)memset(&sd, 0, sizeof(sd));
+
+ ecr = RTW_READ8(regs, RTW_9346CR);
+
+ if ((flags & RTW_F_9356SROM) != 0) {
+ RTW_DPRINTF(RTW_DEBUG_ATTACH, ("%s: 93c56 SROM\n", dvname));
+ sr->sr_size = 256;
+ sd.sd_chip = C56_66;
+ } else {
+ RTW_DPRINTF(RTW_DEBUG_ATTACH, ("%s: 93c46 SROM\n", dvname));
+ sr->sr_size = 128;
+ sd.sd_chip = C46;
+ }
+
+ ecr &= ~(RTW_9346CR_EEDI | RTW_9346CR_EEDO | RTW_9346CR_EESK |
+ RTW_9346CR_EEM_MASK);
+ ecr |= RTW_9346CR_EEM_PROGRAM;
+
+ RTW_WRITE8(regs, RTW_9346CR, ecr);
+
+ sr->sr_content = malloc(sr->sr_size, M_DEVBUF, M_NOWAIT);
+
+ if (sr->sr_content == NULL) {
+ printf("%s: unable to allocate SROM buffer\n", dvname);
+ return ENOMEM;
+ }
+
+ (void)memset(sr->sr_content, 0, sr->sr_size);
+
+ /* 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;
+#if 0
+ sd.sd_clkdelay = 50;
+#endif
+
+ /* TBD bus barriers */
+ if (!read_seeprom(&sd, sr->sr_content, 0, sr->sr_size/2)) {
+ printf("%s: could not read SROM\n", dvname);
+ free(sr->sr_content, M_DEVBUF);
+ sr->sr_content = NULL;
+ return -1; /* 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);
+
+ if ((rc = rtw_recall_eeprom(regs, dvname)) != 0)
+ return rc;
+
+#ifdef RTW_DEBUG
+ {
+ int i;
+ RTW_DPRINTF(RTW_DEBUG_ATTACH,
+ ("\n%s: serial ROM:\n\t", dvname));
+ 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;
+}
+
+void
+rtw_set_rfprog(struct rtw_regs *regs, enum rtw_rfchipid rfchipid,
+ const char *dvname)
+{
+ u_int8_t cfg4;
+ const char *method;
+
+ cfg4 = RTW_READ8(regs, RTW_CONFIG4) & ~RTW_CONFIG4_RFTYPE_MASK;
+
+ switch (rfchipid) {
+ default:
+ cfg4 |= LSHIFT(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_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", dvname, method,
+ RTW_READ8(regs, RTW_CONFIG4)));
+}
+
+#if 0
+static __inline int
+rtw_identify_rf(struct rtw_regs *regs, enum rtw_rftype *rftype,
+ const char *dvname)
+{
+ u_int8_t cfg4;
+ const char *name;
+
+ cfg4 = RTW_READ8(regs, RTW_CONFIG4);
+
+ switch (cfg4 & RTW_CONFIG4_RFTYPE_MASK) {
+ case RTW_CONFIG4_RFTYPE_PHILIPS:
+ *rftype = RTW_RFTYPE_PHILIPS;
+ name = "Philips";
+ break;
+ case RTW_CONFIG4_RFTYPE_INTERSIL:
+ *rftype = RTW_RFTYPE_INTERSIL;
+ name = "Intersil";
+ break;
+ case RTW_CONFIG4_RFTYPE_RFMD:
+ *rftype = RTW_RFTYPE_RFMD;
+ name = "RFMD";
+ break;
+ default:
+ name = "<unknown>";
+ return ENXIO;
+ }
+
+ printf("%s: RF prog type %s\n", dvname, name);
+ return 0;
+}
+#endif
+
+static __inline void
+rtw_init_channels(enum rtw_locale locale,
+ struct ieee80211_channel (*chans)[IEEE80211_CHAN_MAX+1],
+ const char *dvname)
+{
+ int i;
+ const char *name = NULL;
+#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 (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 = "<unknown>";
+ for (i = 10; i <= 11; i++)
+ ADD_CHANNEL(chans, i);
+ break;
+ }
+ printf("%s: Geographic Location %s\n", dvname, name);
+#undef ADD_CHANNEL
+}
+
+static __inline void
+rtw_identify_country(struct rtw_regs *regs, enum rtw_locale *locale,
+ const char *dvname)
+{
+ u_int8_t cfg0 = RTW_READ8(regs, RTW_CONFIG0);
+
+ switch (cfg0 & RTW_CONFIG0_GL_MASK) {
+ case RTW_CONFIG0_GL_USA:
+ *locale = RTW_LOCALE_USA;
+ break;
+ case RTW_CONFIG0_GL_JAPAN:
+ *locale = RTW_LOCALE_JAPAN;
+ break;
+ case RTW_CONFIG0_GL_EUROPE:
+ *locale = RTW_LOCALE_EUROPE;
+ break;
+ default:
+ *locale = RTW_LOCALE_UNKNOWN;
+ break;
+ }
+}
+
+static __inline int
+rtw_identify_sta(struct rtw_regs *regs, u_int8_t (*addr)[IEEE80211_ADDR_LEN],
+ const char *dvname)
+{
+ static const u_int8_t empty_macaddr[IEEE80211_ADDR_LEN] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+ };
+ u_int32_t idr0 = RTW_READ(regs, RTW_IDR0),
+ idr1 = RTW_READ(regs, RTW_IDR1);
+
+ (*addr)[0] = MASK_AND_RSHIFT(idr0, BITS(0, 7));
+ (*addr)[1] = MASK_AND_RSHIFT(idr0, BITS(8, 15));
+ (*addr)[2] = MASK_AND_RSHIFT(idr0, BITS(16, 23));
+ (*addr)[3] = MASK_AND_RSHIFT(idr0, BITS(24 ,31));
+
+ (*addr)[4] = MASK_AND_RSHIFT(idr1, BITS(0, 7));
+ (*addr)[5] = MASK_AND_RSHIFT(idr1, BITS(8, 15));
+
+ if (IEEE80211_ADDR_EQ(addr, empty_macaddr)) {
+ printf("%s: could not get mac address, attach failed\n",
+ dvname);
+ return ENXIO;
+ }
+
+ printf("%s: 802.11 address %s\n", dvname, ether_sprintf(*addr));
+
+ return 0;
+}
+
+u_int8_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;
+ KASSERT2(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);
+}
+
+void
+rtw_txdesc_blk_init_all(struct rtw_txdesc_blk *htcs)
+{
+ int pri;
+ u_int ndesc[RTW_NTXPRI] =
+ {RTW_NTXDESCLO, RTW_NTXDESCMD, RTW_NTXDESCHI, RTW_NTXDESCBCN};
+
+ for (pri = 0; pri < RTW_NTXPRI; pri++) {
+ htcs[pri].htc_nfree = ndesc[pri];
+ htcs[pri].htc_next = 0;
+ }
+}
+
+int
+rtw_txctl_blk_init(struct rtw_txctl_blk *stc)
+{
+ int i;
+ struct rtw_txctl *stx;
+
+ SIMPLEQ_INIT(&stc->stc_dirtyq);
+ SIMPLEQ_INIT(&stc->stc_freeq);
+ for (i = 0; i < stc->stc_ndesc; i++) {
+ stx = &stc->stc_desc[i];
+ stx->stx_mbuf = NULL;
+ SIMPLEQ_INSERT_TAIL(&stc->stc_freeq, stx, stx_q);
+ }
+ return 0;
+}
+
+void
+rtw_txctl_blk_init_all(struct rtw_txctl_blk *stcs)
+{
+ int pri;
+ for (pri = 0; pri < RTW_NTXPRI; pri++)
+ rtw_txctl_blk_init(&stcs[pri]);
+}
+
+static __inline void
+rtw_rxdescs_sync(bus_dma_tag_t dmat, bus_dmamap_t dmap, u_int desc0, u_int
+ nsync, int ops)
+{
+ KASSERT(nsync <= RTW_RXQLEN);
+ /* sync to end of ring */
+ if (desc0 + nsync > RTW_RXQLEN) {
+ bus_dmamap_sync(dmat, dmap,
+ offsetof(struct rtw_descs, hd_rx[desc0]),
+ sizeof(struct rtw_rxdesc) * (RTW_RXQLEN - desc0), ops);
+ nsync -= (RTW_RXQLEN - desc0);
+ desc0 = 0;
+ }
+
+ KASSERT(desc0 < RTW_RXQLEN);
+ KASSERT(nsync <= RTW_RXQLEN);
+ KASSERT(desc0 + nsync <= RTW_RXQLEN);
+
+ /* sync what remains */
+ bus_dmamap_sync(dmat, dmap,
+ offsetof(struct rtw_descs, hd_rx[desc0]),
+ sizeof(struct rtw_rxdesc) * nsync, ops);
+}
+
+void
+rtw_txdescs_sync(bus_dma_tag_t dmat, bus_dmamap_t dmap,
+ struct rtw_txdesc_blk *htc, u_int desc0, u_int nsync, int ops)
+{
+ /* sync to end of ring */
+ if (desc0 + nsync > htc->htc_ndesc) {
+ bus_dmamap_sync(dmat, dmap,
+ htc->htc_ofs + sizeof(struct rtw_txdesc) * desc0,
+ sizeof(struct rtw_txdesc) * (htc->htc_ndesc - desc0),
+ ops);
+ nsync -= (htc->htc_ndesc - desc0);
+ desc0 = 0;
+ }
+
+ /* sync what remains */
+ bus_dmamap_sync(dmat, dmap,
+ htc->htc_ofs + sizeof(struct rtw_txdesc) * desc0,
+ sizeof(struct rtw_txdesc) * nsync, ops);
+}
+
+void
+rtw_txdescs_sync_all(bus_dma_tag_t dmat, bus_dmamap_t dmap,
+ struct rtw_txdesc_blk *htcs)
+{
+ int pri;
+ for (pri = 0; pri < RTW_NTXPRI; pri++) {
+ rtw_txdescs_sync(dmat, dmap,
+ &htcs[pri], 0, htcs[pri].htc_ndesc,
+ BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE);
+ }
+}
+
+void
+rtw_rxbufs_release(bus_dma_tag_t dmat, struct rtw_rxctl *desc)
+{
+ int i;
+ struct rtw_rxctl *srx;
+
+ for (i = 0; i < RTW_RXQLEN; i++) {
+ srx = &desc[i];
+ bus_dmamap_sync(dmat, srx->srx_dmamap, 0,
+ srx->srx_dmamap->dm_mapsize, BUS_DMASYNC_POSTREAD);
+ bus_dmamap_unload(dmat, srx->srx_dmamap);
+ m_freem(srx->srx_mbuf);
+ srx->srx_mbuf = NULL;
+ }
+}
+
+static __inline int
+rtw_rxbuf_alloc(bus_dma_tag_t dmat, struct rtw_rxctl *srx)
+{
+ int rc;
+ struct mbuf *m;
+
+ MGETHDR(m, M_DONTWAIT, MT_DATA);
+ if (m == NULL)
+ return ENOBUFS;
+
+ MCLGET(m, M_DONTWAIT);
+ if (m == NULL)
+ return ENOBUFS;
+
+ m->m_pkthdr.len = m->m_len = m->m_ext.ext_size;
+
+ if (srx->srx_mbuf != NULL)
+ bus_dmamap_unload(dmat, srx->srx_dmamap);
+
+ srx->srx_mbuf = NULL;
+
+ rc = bus_dmamap_load_mbuf(dmat, srx->srx_dmamap, m, BUS_DMA_NOWAIT);
+ if (rc != 0) {
+ m_freem(m);
+ return -1;
+ }
+
+ srx->srx_mbuf = m;
+
+ return 0;
+}
+
+int
+rtw_rxctl_init_all(bus_dma_tag_t dmat, struct rtw_rxctl *desc,
+ u_int *next, const char *dvname)
+{
+ int i, rc;
+ struct rtw_rxctl *srx;
+
+ for (i = 0; i < RTW_RXQLEN; i++) {
+ srx = &desc[i];
+ if ((rc = rtw_rxbuf_alloc(dmat, srx)) == 0)
+ continue;
+ printf("%s: failed rtw_rxbuf_alloc after %d buffers, rc = %d\n",
+ dvname, i, rc);
+ if (i == 0) {
+ rtw_rxbufs_release(dmat, desc);
+ return rc;
+ }
+ }
+ *next = 0;
+ return 0;
+}
+
+static __inline void
+rtw_rxdesc_init(bus_dma_tag_t dmat, bus_dmamap_t dmam,
+ struct rtw_rxdesc *hrx, struct rtw_rxctl *srx, int idx, int kick)
+{
+ int is_last = (idx == RTW_RXQLEN - 1);
+ u_int32_t ctl, octl, obuf;
+
+ obuf = hrx->hrx_buf;
+ hrx->hrx_buf = htole32(srx->srx_dmamap->dm_segs[0].ds_addr);
+
+ ctl = LSHIFT(srx->srx_mbuf->m_len, RTW_RXCTL_LENGTH_MASK) |
+ RTW_RXCTL_OWN | RTW_RXCTL_FS | RTW_RXCTL_LS;
+
+ if (is_last)
+ ctl |= RTW_RXCTL_EOR;
+
+ octl = hrx->hrx_ctl;
+ hrx->hrx_ctl = htole32(ctl);
+
+ RTW_DPRINTF(
+ kick ? (RTW_DEBUG_RECV_DESC | RTW_DEBUG_IO_KICK)
+ : RTW_DEBUG_RECV_DESC,
+ ("%s: hrx %p buf %08x -> %08x ctl %08x -> %08x\n", __func__, hrx,
+ letoh32(obuf), letoh32(hrx->hrx_buf), letoh32(octl),
+ letoh32(hrx->hrx_ctl)));
+
+ /* sync the mbuf */
+ bus_dmamap_sync(dmat, srx->srx_dmamap, 0, srx->srx_dmamap->dm_mapsize,
+ BUS_DMASYNC_PREREAD);
+
+ /* sync the descriptor */
+ bus_dmamap_sync(dmat, dmam, RTW_DESC_OFFSET(hd_rx, idx),
+ sizeof(struct rtw_rxdesc),
+ BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE);
+}
+
+void
+rtw_rxdesc_init_all(bus_dma_tag_t dmat, bus_dmamap_t dmam,
+ struct rtw_rxdesc *desc, struct rtw_rxctl *ctl, int kick)
+{
+ int i;
+ struct rtw_rxdesc *hrx;
+ struct rtw_rxctl *srx;
+
+ for (i = 0; i < RTW_RXQLEN; i++) {
+ hrx = &desc[i];
+ srx = &ctl[i];
+ rtw_rxdesc_init(dmat, dmam, hrx, srx, i, kick);
+ }
+}
+
+void
+rtw_io_enable(struct rtw_regs *regs, u_int8_t flags, int enable)
+{
+ u_int8_t cr;
+
+ RTW_DPRINTF(RTW_DEBUG_IOSTATE, ("%s: %s 0x%02x\n", __func__,
+ 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);
+}
+
+void
+rtw_intr_rx(struct rtw_softc *sc, u_int16_t isr)
+{
+ static const int ratetbl[4] = {2, 4, 11, 22}; /* convert rates:
+ * hardware -> net80211
+ */
+
+ u_int next, nproc = 0;
+ int hwrate, len, rate, rssi;
+ u_int32_t hrssi, hstat, htsfth, htsftl;
+ struct rtw_rxdesc *hrx;
+ struct rtw_rxctl *srx;
+ struct mbuf *m;
+
+ struct ieee80211com *ic = &sc->sc_ic;
+ struct ieee80211_node *ni;
+ struct ieee80211_frame *wh;
+
+ KASSERT(sc->sc_rxnext < RTW_RXQLEN);
+
+ for (next = sc->sc_rxnext; ; next = (next + 1) % RTW_RXQLEN) {
+ rtw_rxdescs_sync(sc->sc_dmat, sc->sc_desc_dmamap,
+ next, 1, BUS_DMASYNC_POSTREAD|BUS_DMASYNC_POSTWRITE);
+ hrx = &sc->sc_rxdesc[next];
+ srx = &sc->sc_rxctl[next];
+
+ hstat = letoh32(hrx->hrx_stat);
+ hrssi = letoh32(hrx->hrx_rssi);
+ htsfth = letoh32(hrx->hrx_tsfth);
+ htsftl = letoh32(hrx->hrx_tsftl);
+
+ RTW_DPRINTF(RTW_DEBUG_RECV_DESC,
+ ("%s: rxdesc[%d] hstat %08x hrssi %08x htsft %08x%08x\n",
+ __func__, next, hstat, hrssi, htsfth, htsftl));
+
+ KASSERT((hstat & (RTW_RXSTAT_FS|RTW_RXSTAT_LS)) ==
+ (RTW_RXSTAT_FS|RTW_RXSTAT_LS));
+
+ ++nproc;
+
+ /* still belongs to NIC */
+ if ((hstat & RTW_RXSTAT_OWN) != 0) {
+ if (nproc > 1)
+ break;
+
+ /* sometimes the NIC skips to the 0th descriptor */
+ rtw_rxdescs_sync(sc->sc_dmat, sc->sc_desc_dmamap,
+ 0, 1, BUS_DMASYNC_POSTREAD|BUS_DMASYNC_POSTWRITE);
+ hrx = &sc->sc_rxdesc[0];
+ if ((hrx->hrx_stat & htole32(RTW_RXSTAT_OWN)) != 0)
+ break;
+ RTW_DPRINTF(RTW_DEBUG_BUGS,
+ ("%s: NIC skipped to rxdesc[0]\n",
+ sc->sc_dev.dv_xname));
+ next = 0;
+ continue;
+ }
+
+ if ((hstat & RTW_RXSTAT_IOERROR) != 0) {
+ printf("%s: DMA error/FIFO overflow %08x, "
+ "rx descriptor %d\n", sc->sc_dev.dv_xname,
+ hstat & RTW_RXSTAT_IOERROR, next);
+ sc->sc_if.if_ierrors++;
+ goto next;
+ }
+
+ len = MASK_AND_RSHIFT(hstat, RTW_RXSTAT_LENGTH_MASK);
+ if (len < IEEE80211_MIN_LEN) {
+ sc->sc_ic.ic_stats.is_rx_tooshort++;
+ goto next;
+ }
+
+ hwrate = MASK_AND_RSHIFT(hstat, RTW_RXSTAT_RATE_MASK);
+ if (hwrate >= sizeof(ratetbl) / sizeof(ratetbl[0])) {
+ printf("%s: unknown rate #%d\n", sc->sc_dev.dv_xname,
+ MASK_AND_RSHIFT(hstat, RTW_RXSTAT_RATE_MASK));
+ sc->sc_if.if_ierrors++;
+ goto next;
+ }
+ rate = ratetbl[hwrate];
+
+#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) != 0) {
+ const char *delim = "<";
+ printf("%s: ", sc->sc_dev.dv_xname);
+ 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(">, ");
+ }
+ printf("rate %d.%d Mb/s, time %08x%08x\n",
+ (rate * 5) / 10, (rate * 5) % 10, htsfth, htsftl);
+ }
+#endif /* RTW_DEBUG */
+
+ if ((hstat & RTW_RXSTAT_RES) != 0 &&
+ sc->sc_ic.ic_opmode != IEEE80211_M_MONITOR)
+ goto next;
+
+ /* if bad flags, skip descriptor */
+ if ((hstat & RTW_RXSTAT_ONESEG) != RTW_RXSTAT_ONESEG) {
+ printf("%s: too many rx segments\n",
+ sc->sc_dev.dv_xname);
+ goto next;
+ }
+
+ bus_dmamap_sync(sc->sc_dmat, srx->srx_dmamap, 0,
+ srx->srx_dmamap->dm_mapsize, BUS_DMASYNC_POSTREAD);
+
+ m = srx->srx_mbuf;
+
+ /* if temporarily out of memory, re-use mbuf */
+ switch (rtw_rxbuf_alloc(sc->sc_dmat, srx)) {
+ case 0:
+ break;
+ case ENOBUFS:
+ printf("%s: rtw_rxbuf_alloc(, %d) failed, "
+ "dropping this packet\n", sc->sc_dev.dv_xname,
+ next);
+ goto next;
+ default:
+ /* XXX shorten rx ring, instead? */
+ panic("%s: could not load DMA map\n",
+ sc->sc_dev.dv_xname);
+ }
+
+ if (sc->sc_rfchipid == RTW_RFCHIPID_PHILIPS)
+ rssi = MASK_AND_RSHIFT(hrssi, RTW_RXRSSI_RSSI);
+ else {
+ rssi = MASK_AND_RSHIFT(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;
+ }
+
+ m->m_pkthdr.rcvif = &sc->sc_if;
+ m->m_pkthdr.len = m->m_len = len;
+ m->m_flags |= M_HASFCS;
+
+ wh = mtod(m, struct ieee80211_frame *);
+ /* 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 ((sc->sc_if.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 */
+
+ ieee80211_input(&sc->sc_if, m, ni, rssi, htsftl);
+ if (ni == ic->ic_bss)
+ ieee80211_unref_node(&ni);
+ else
+ ieee80211_free_node(&sc->sc_ic, ni);
+next:
+ rtw_rxdesc_init(sc->sc_dmat, sc->sc_desc_dmamap,
+ hrx, srx, next, 0);
+ }
+ KASSERT(sc->sc_rxnext < RTW_RXQLEN);
+
+ sc->sc_rxnext = next;
+
+ return;
+}
+
+void
+rtw_txbuf_release(bus_dma_tag_t dmat, struct ieee80211com *ic,
+ struct rtw_txctl *stx)
+{
+ struct mbuf *m;
+ struct ieee80211_node *ni;
+
+ m = stx->stx_mbuf;
+ ni = stx->stx_ni;
+ KASSERT(m != NULL);
+ KASSERT(ni != NULL);
+ stx->stx_mbuf = NULL;
+ stx->stx_ni = NULL;
+
+ bus_dmamap_sync(dmat, stx->stx_dmamap, 0, stx->stx_dmamap->dm_mapsize,
+ BUS_DMASYNC_POSTWRITE);
+ bus_dmamap_unload(dmat, stx->stx_dmamap);
+ m_freem(m);
+ if (ni == ic->ic_bss)
+ ieee80211_unref_node(&ni);
+ else
+ ieee80211_free_node(ic, ni);
+}
+
+void
+rtw_txbufs_release(bus_dma_tag_t dmat, bus_dmamap_t desc_dmamap,
+ struct ieee80211com *ic, struct rtw_txctl_blk *stc)
+{
+ struct rtw_txctl *stx;
+
+ while ((stx = SIMPLEQ_FIRST(&stc->stc_dirtyq)) != NULL) {
+ rtw_txbuf_release(dmat, ic, stx);
+ SIMPLEQ_REMOVE_HEAD(&stc->stc_dirtyq, stx_q);
+ SIMPLEQ_INSERT_TAIL(&stc->stc_freeq, stx, stx_q);
+ }
+}
+
+static __inline void
+rtw_collect_txpkt(struct rtw_softc *sc, struct rtw_txdesc_blk *htc,
+ struct rtw_txctl *stx, int ndesc)
+{
+ uint32_t hstat;
+ int data_retry, rts_retry;
+ struct rtw_txdesc *htxn;
+ const char *condstring;
+
+ rtw_txbuf_release(sc->sc_dmat, &sc->sc_ic, stx);
+
+ htc->htc_nfree += ndesc;
+
+ htxn = &htc->htc_desc[stx->stx_last];
+
+ hstat = letoh32(htxn->htx_stat);
+ rts_retry = MASK_AND_RSHIFT(hstat, RTW_TXSTAT_RTSRETRY_MASK);
+ data_retry = MASK_AND_RSHIFT(hstat, RTW_TXSTAT_DRC_MASK);
+
+ sc->sc_if.if_collisions += rts_retry + data_retry;
+
+ if ((hstat & RTW_TXSTAT_TOK) != 0)
+ condstring = "ok";
+ else {
+ sc->sc_if.if_oerrors++;
+ condstring = "error";
+ }
+
+ DPRINTF(sc, RTW_DEBUG_XMIT_DESC,
+ ("%s: stx %p txdesc[%d, %d] %s tries rts %u data %u\n",
+ sc->sc_dev.dv_xname, stx, stx->stx_first, stx->stx_last,
+ condstring, rts_retry, data_retry));
+}
+
+/* Collect transmitted packets. */
+static __inline void
+rtw_collect_txring(struct rtw_softc *sc, struct rtw_txctl_blk *stc,
+ struct rtw_txdesc_blk *htc)
+{
+ int ndesc;
+ struct rtw_txctl *stx;
+
+ while ((stx = SIMPLEQ_FIRST(&stc->stc_dirtyq)) != NULL) {
+ ndesc = 1 + stx->stx_last - stx->stx_first;
+ if (stx->stx_last < stx->stx_first)
+ ndesc += htc->htc_ndesc;
+
+ KASSERT(ndesc > 0);
+
+ rtw_txdescs_sync(sc->sc_dmat, sc->sc_desc_dmamap, htc,
+ stx->stx_first, ndesc,
+ BUS_DMASYNC_POSTREAD|BUS_DMASYNC_POSTWRITE);
+
+ if ((htc->htc_desc[stx->stx_last].htx_stat &
+ htole32(RTW_TXSTAT_OWN)) != 0)
+ break;
+
+ rtw_collect_txpkt(sc, htc, stx, ndesc);
+ SIMPLEQ_REMOVE_HEAD(&stc->stc_dirtyq, stx_q);
+ SIMPLEQ_INSERT_TAIL(&stc->stc_freeq, stx, stx_q);
+ sc->sc_if.if_flags &= ~IFF_OACTIVE;
+ }
+ if (stx == NULL)
+ stc->stc_tx_timer = 0;
+}
+
+void
+rtw_intr_tx(struct rtw_softc *sc, u_int16_t isr)
+{
+ int pri;
+ struct rtw_txctl_blk *stc;
+ struct rtw_txdesc_blk *htc;
+
+ for (pri = 0; pri < RTW_NTXPRI; pri++) {
+ stc = &sc->sc_txctl_blk[pri];
+ htc = &sc->sc_txdesc_blk[pri];
+
+ rtw_collect_txring(sc, stc, htc);
+
+ if ((isr & RTW_INTR_TX) != 0)
+ rtw_start(&sc->sc_if);
+ }
+
+ /* TBD */
+ return;
+}
+
+void
+rtw_intr_beacon(struct rtw_softc *sc, u_int16_t isr)
+{
+ /* TBD */
+ return;
+}
+
+void
+rtw_intr_atim(struct rtw_softc *sc)
+{
+ /* TBD */
+ return;
+}
+
+#ifdef RTW_DEBUG
+void
+rtw_dump_rings(struct rtw_softc *sc)
+{
+ struct rtw_txdesc_blk *htc;
+ struct rtw_rxdesc *hrx;
+ int desc, pri;
+
+ if ((rtw_debug & RTW_DEBUG_IO_KICK) == 0)
+ return;
+
+ for (pri = 0; pri < RTW_NTXPRI; pri++) {
+ htc = &sc->sc_txdesc_blk[pri];
+ printf("%s: txpri %d ndesc %d nfree %d\n", __func__, pri,
+ htc->htc_ndesc, htc->htc_nfree);
+ for (desc = 0; desc < htc->htc_ndesc; desc++)
+ rtw_print_txdesc(sc, ".", NULL, htc, desc);
+ }
+
+ for (desc = 0; desc < RTW_RXQLEN; desc++) {
+ hrx = &sc->sc_rxdesc[desc];
+ printf("%s: ctl %08x rsvd0/rssi %08x buf/tsftl %08x "
+ "rsvd1/tsfth %08x\n", __func__,
+ letoh32(hrx->hrx_ctl), letoh32(hrx->hrx_rssi),
+ letoh32(hrx->hrx_buf), letoh32(hrx->hrx_tsfth));
+ }
+}
+#endif /* RTW_DEBUG */
+
+void
+rtw_hwring_setup(struct rtw_softc *sc)
+{
+ struct rtw_regs *regs = &sc->sc_regs;
+ RTW_WRITE(regs, RTW_RDSAR, RTW_RING_BASE(sc, hd_rx));
+ RTW_WRITE(regs, RTW_TLPDA, RTW_RING_BASE(sc, hd_txlo));
+ RTW_WRITE(regs, RTW_TNPDA, RTW_RING_BASE(sc, hd_txmd));
+ RTW_WRITE(regs, RTW_THPDA, RTW_RING_BASE(sc, hd_txhi));
+ RTW_WRITE(regs, RTW_TBDA, RTW_RING_BASE(sc, hd_bcn));
+ RTW_SYNC(regs, RTW_TLPDA, RTW_RDSAR);
+#if 0
+ RTW_DPRINTF(RTW_DEBUG_XMIT_DESC,
+ ("%s: reg[TLPDA] <- %" PRIxPTR "\n", __func__,
+ (uintptr_t)RTW_RING_BASE(sc, hd_txlo)));
+ RTW_DPRINTF(RTW_DEBUG_XMIT_DESC,
+ ("%s: reg[TNPDA] <- %" PRIxPTR "\n", __func__,
+ (uintptr_t)RTW_RING_BASE(sc, hd_txmd)));
+ RTW_DPRINTF(RTW_DEBUG_XMIT_DESC,
+ ("%s: reg[THPDA] <- %" PRIxPTR "\n", __func__,
+ (uintptr_t)RTW_RING_BASE(sc, hd_txhi)));
+ RTW_DPRINTF(RTW_DEBUG_XMIT_DESC,
+ ("%s: reg[TBDA] <- %" PRIxPTR "\n", __func__,
+ (uintptr_t)RTW_RING_BASE(sc, hd_bcn)));
+ RTW_DPRINTF(RTW_DEBUG_RECV_DESC,
+ ("%s: reg[RDSAR] <- %" PRIxPTR "\n", __func__,
+ (uintptr_t)RTW_RING_BASE(sc, hd_rx)));
+#endif
+}
+
+void
+rtw_swring_setup(struct rtw_softc *sc)
+{
+ rtw_txdesc_blk_init_all(&sc->sc_txdesc_blk[0]);
+
+ rtw_txctl_blk_init_all(&sc->sc_txctl_blk[0]);
+
+ rtw_rxdescs_sync(sc->sc_dmat, sc->sc_desc_dmamap,
+ 0, RTW_RXQLEN, BUS_DMASYNC_POSTREAD|BUS_DMASYNC_POSTWRITE);
+ rtw_rxctl_init_all(sc->sc_dmat, sc->sc_rxctl, &sc->sc_rxnext,
+ sc->sc_dev.dv_xname);
+ rtw_rxdesc_init_all(sc->sc_dmat, sc->sc_desc_dmamap,
+ sc->sc_rxdesc, sc->sc_rxctl, 1);
+
+ rtw_txdescs_sync_all(sc->sc_dmat, sc->sc_desc_dmamap,
+ &sc->sc_txdesc_blk[0]);
+#if 0 /* redundant with rtw_rxdesc_init_all */
+ rtw_rxdescs_sync(sc->sc_dmat, sc->sc_desc_dmamap,
+ 0, RTW_RXQLEN, BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE);
+#endif
+}
+
+void
+rtw_txdesc_blk_reset(struct rtw_txdesc_blk *htc)
+{
+ int i;
+
+ (void)memset(htc->htc_desc, 0,
+ sizeof(htc->htc_desc[0]) * htc->htc_ndesc);
+ for (i = 0; i < htc->htc_ndesc; i++)
+ htc->htc_desc[i].htx_next = htole32(RTW_NEXT_DESC(htc, i));
+ htc->htc_nfree = htc->htc_ndesc;
+ htc->htc_next = 0;
+}
+
+void
+rtw_txdescs_reset(struct rtw_softc *sc)
+{
+ int pri;
+ struct rtw_txdesc_blk *htc;
+
+ for (pri = 0; pri < RTW_NTXPRI; pri++) {
+ htc = &sc->sc_txdesc_blk[pri];
+ rtw_txbufs_release(sc->sc_dmat, sc->sc_desc_dmamap, &sc->sc_ic,
+ &sc->sc_txctl_blk[pri]);
+ rtw_txdesc_blk_reset(htc);
+ rtw_txdescs_sync(sc->sc_dmat, sc->sc_desc_dmamap, htc,
+ 0, htc->htc_ndesc,
+ BUS_DMASYNC_PREWRITE|BUS_DMASYNC_PREREAD);
+ }
+}
+
+void
+rtw_rxdescs_reset(struct rtw_softc *sc)
+{
+ /* Re-initialize descriptors, just in case. */
+ rtw_rxdesc_init_all(sc->sc_dmat, sc->sc_desc_dmamap, sc->sc_rxdesc,
+ &sc->sc_rxctl[0], 1);
+
+ /* Reset to start of ring. */
+ sc->sc_rxnext = 0;
+}
+
+void
+rtw_intr_ioerror(struct rtw_softc *sc, u_int16_t isr)
+{
+ struct rtw_regs *regs = &sc->sc_regs;
+
+ if ((isr & RTW_INTR_TXFOVW) != 0)
+ printf("%s: tx fifo overflow\n", sc->sc_dev.dv_xname);
+
+ if ((isr & (RTW_INTR_RDU|RTW_INTR_RXFOVW)) == 0)
+ return;
+
+ RTW_DPRINTF(RTW_DEBUG_BUGS, ("%s: restarting xmit/recv\n",
+ sc->sc_dev.dv_xname));
+
+#ifdef RTW_DEBUG
+ rtw_dump_rings(sc);
+#endif /* RTW_DEBUG */
+
+ rtw_io_enable(regs, RTW_CR_RE | RTW_CR_TE, 0);
+
+ /* Collect rx'd packets. Refresh rx buffers. */
+ rtw_intr_rx(sc, 0);
+ /* Collect tx'd packets. */
+ rtw_intr_tx(sc, 0);
+
+ RTW_WRITE16(regs, RTW_IMR, 0);
+ RTW_SYNC(regs, RTW_IMR, RTW_IMR);
+
+ rtw_chip_reset1(regs, sc->sc_dev.dv_xname);
+
+ rtw_rxdescs_reset(sc);
+ rtw_txdescs_reset(sc);
+
+ rtw_hwring_setup(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);
+ rtw_io_enable(regs, RTW_CR_RE | RTW_CR_TE, 1);
+}
+
+static __inline void
+rtw_suspend_ticks(struct rtw_softc *sc)
+{
+ RTW_DPRINTF(RTW_DEBUG_TIMEOUT,
+ ("%s: suspending ticks\n", sc->sc_dev.dv_xname));
+ sc->sc_do_tick = 0;
+}
+
+static __inline void
+rtw_resume_ticks(struct rtw_softc *sc)
+{
+ u_int32_t tsftrl0, tsftrl1, next_tick;
+
+ tsftrl0 = RTW_READ(&sc->sc_regs, RTW_TSFTRL);
+
+ tsftrl1 = RTW_READ(&sc->sc_regs, RTW_TSFTRL);
+ next_tick = tsftrl1 + 1000000;
+ RTW_WRITE(&sc->sc_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_dev.dv_xname, tsftrl1 - tsftrl0, tsftrl1, next_tick));
+}
+
+void
+rtw_intr_timeout(struct rtw_softc *sc)
+{
+ RTW_DPRINTF(RTW_DEBUG_TIMEOUT, ("%s: timeout\n", sc->sc_dev.dv_xname));
+ if (sc->sc_do_tick)
+ rtw_resume_ticks(sc);
+ return;
+}
+
+int
+rtw_intr(void *arg)
+{
+ int i;
+ struct rtw_softc *sc = arg;
+ struct rtw_regs *regs = &sc->sc_regs;
+ u_int16_t isr;
+
+ /*
+ * If the interface isn't running, the interrupt couldn't
+ * possibly have come from us.
+ */
+ if ((sc->sc_flags & RTW_F_ENABLED) == 0 ||
+ (sc->sc_if.if_flags & IFF_RUNNING) == 0 ||
+ (sc->sc_dev.dv_flags & DVF_ACTIVE) == 0) {
+ RTW_DPRINTF(RTW_DEBUG_INTR, ("%s: stray interrupt\n",
+ sc->sc_dev.dv_xname));
+ return (0);
+ }
+
+ for (i = 0; i < 10; i++) {
+ 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 = "<";
+
+ printf("%s: reg[ISR] = %x", sc->sc_dev.dv_xname, 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) != 0)
+ rtw_intr_rx(sc, isr & RTW_INTR_RX);
+ if ((isr & RTW_INTR_TX) != 0)
+ rtw_intr_tx(sc, isr & RTW_INTR_TX);
+ if ((isr & RTW_INTR_BEACON) != 0)
+ rtw_intr_beacon(sc, isr & RTW_INTR_BEACON);
+ if ((isr & RTW_INTR_ATIMINT) != 0)
+ rtw_intr_atim(sc);
+ if ((isr & RTW_INTR_IOERROR) != 0)
+ rtw_intr_ioerror(sc, isr & RTW_INTR_IOERROR);
+ if ((isr & RTW_INTR_TIMEOUT) != 0)
+ rtw_intr_timeout(sc);
+ }
+
+ return 1;
+}
+
+/* Must be called at splnet. */
+void
+rtw_stop(struct ifnet *ifp, int disable)
+{
+ int pri;
+ struct rtw_softc *sc = (struct rtw_softc *)ifp->if_softc;
+ struct ieee80211com *ic = &sc->sc_ic;
+ struct rtw_regs *regs = &sc->sc_regs;
+
+ 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_SBQ|RTW_TPPOLL_SHPQ|RTW_TPPOLL_SNPQ|
+ RTW_TPPOLL_SLPQ);
+
+ RTW_SYNC(regs, RTW_TPPOLL, RTW_IMR);
+
+ rtw_io_enable(&sc->sc_regs, RTW_CR_RE|RTW_CR_TE, 0);
+ }
+
+ for (pri = 0; pri < RTW_NTXPRI; pri++) {
+ rtw_txbufs_release(sc->sc_dmat, sc->sc_desc_dmamap, &sc->sc_ic,
+ &sc->sc_txctl_blk[pri]);
+ }
+
+ if (disable) {
+ rtw_disable(sc);
+ rtw_rxbufs_release(sc->sc_dmat, &sc->sc_rxctl[0]);
+ }
+
+ /* Mark the interface as not running. Cancel the watchdog timer. */
+ ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE);
+ ifp->if_timer = 0;
+
+ return;
+}
+
+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";
+ }
+}
+
+/* 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."
+ */
+void
+rtw_maxim_pwrstate(struct rtw_regs *regs, enum rtw_pwrstate power,
+ int before_rf, int digphy)
+{
+ u_int32_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
+ */
+void
+rtw_rfmd_pwrstate(struct rtw_regs *regs, enum rtw_pwrstate power,
+ int before_rf, int digphy)
+{
+ u_int32_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);
+}
+
+void
+rtw_philips_pwrstate(struct rtw_regs *regs, enum rtw_pwrstate power,
+ int before_rf, int digphy)
+{
+ u_int32_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);
+}
+
+void
+rtw_pwrstate0(struct rtw_softc *sc, enum rtw_pwrstate power, int before_rf,
+ int digphy)
+{
+ struct rtw_regs *regs = &sc->sc_regs;
+
+ rtw_set_access(sc, RTW_ACCESS_ANAPARM);
+
+ (*sc->sc_pwrstate_cb)(regs, power, before_rf, digphy);
+
+ rtw_set_access(sc, RTW_ACCESS_NONE);
+
+ return;
+}
+
+int
+rtw_pwrstate(struct rtw_softc *sc, enum rtw_pwrstate power)
+{
+ int rc;
+
+ RTW_DPRINTF(RTW_DEBUG_PWR,
+ ("%s: %s->%s\n", __func__,
+ 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;
+}
+
+int
+rtw_tune(struct rtw_softc *sc)
+{
+ struct ieee80211com *ic = &sc->sc_ic;
+ u_int chan;
+ int rc;
+ int antdiv = sc->sc_flags & RTW_F_ANTDIV,
+ dflantb = sc->sc_flags & RTW_F_DFLANTB;
+
+ KASSERT(ic->ic_bss->ni_chan != NULL);
+
+ chan = ieee80211_chan2ieee(ic, ic->ic_bss->ni_chan);
+ if (chan == IEEE80211_CHAN_ANY)
+ panic("%s: chan == IEEE80211_CHAN_ANY\n", __func__);
+
+ if (chan == sc->sc_cur_chan) {
+ RTW_DPRINTF(RTW_DEBUG_TUNE,
+ ("%s: already tuned chan #%d\n", __func__, chan));
+ return 0;
+ }
+
+ rtw_suspend_ticks(sc);
+
+ rtw_io_enable(&sc->sc_regs, RTW_CR_RE | RTW_CR_TE, 0);
+
+ /* TBD wait for Tx to complete */
+
+ KASSERT((sc->sc_flags & RTW_F_ENABLED) != 0);
+
+ if ((rc = rtw_phy_init(&sc->sc_regs, sc->sc_rf,
+ rtw_chan2txpower(&sc->sc_srom, ic, ic->ic_bss->ni_chan),
+ sc->sc_csthr, ic->ic_bss->ni_chan->ic_freq, antdiv,
+ dflantb, RTW_ON)) != 0) {
+ /* XXX condition on powersaving */
+ printf("%s: phy init failed\n", sc->sc_dev.dv_xname);
+ }
+
+ sc->sc_cur_chan = chan;
+
+ rtw_io_enable(&sc->sc_regs, RTW_CR_RE | RTW_CR_TE, 1);
+
+ rtw_resume_ticks(sc);
+
+ return rc;
+}
+
+void
+rtw_disable(struct rtw_softc *sc)
+{
+ int rc;
+
+ if ((sc->sc_flags & RTW_F_ENABLED) == 0)
+ return;
+
+ /* turn off PHY */
+ if ((rc = rtw_pwrstate(sc, RTW_OFF)) != 0)
+ printf("%s: failed to turn off PHY (%d)\n",
+ sc->sc_dev.dv_xname, rc);
+
+ if (sc->sc_disable != NULL)
+ (*sc->sc_disable)(sc);
+
+ sc->sc_flags &= ~RTW_F_ENABLED;
+}
+
+int
+rtw_enable(struct rtw_softc *sc)
+{
+ if ((sc->sc_flags & RTW_F_ENABLED) == 0) {
+ if (sc->sc_enable != NULL && (*sc->sc_enable)(sc) != 0) {
+ printf("%s: device enable failed\n",
+ sc->sc_dev.dv_xname);
+ return (EIO);
+ }
+ sc->sc_flags |= RTW_F_ENABLED;
+ }
+ return (0);
+}
+
+void
+rtw_transmit_config(struct rtw_regs *regs)
+{
+ u_int32_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 |= LSHIFT(4, RTW_TCR_SRL_MASK) | LSHIFT(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 __inline 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);
+}
+
+void
+rtw_set_nettype(struct rtw_softc *sc, enum ieee80211_opmode opmode)
+{
+ uint8_t msr;
+
+ /* I'm guessing that MSR is protected as CONFIG[0123] are. */
+ rtw_set_access(sc, RTW_ACCESS_CONFIG);
+
+ msr = RTW_READ8(&sc->sc_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(&sc->sc_regs, RTW_MSR, msr);
+
+ rtw_set_access(sc, RTW_ACCESS_NONE);
+}
+
+/* XXX is the endianness correct? test. */
+#define rtw_calchash(addr) \
+ (ether_crc32_le((addr), IEEE80211_ADDR_LEN) & BITS(5, 0))
+
+void
+rtw_pktfilt_load(struct rtw_softc *sc)
+{
+ struct rtw_regs *regs = &sc->sc_regs;
+ struct ieee80211com *ic = &sc->sc_ic;
+ struct arpcom *ec = &ic->ic_ac;
+ struct ifnet *ifp = &sc->sc_ic.ic_if;
+ int hash;
+ u_int32_t hashes[2] = { 0, 0 };
+ struct ether_multi *enm;
+ struct ether_multistep step;
+
+ /* XXX might be necessary to stop Rx/Tx engines while setting filters */
+
+#define RTW_RCR_MONITOR (RTW_RCR_ACRC32|RTW_RCR_APM|RTW_RCR_AAP|RTW_RCR_AB|RTW_RCR_ACF | RTW_RCR_AICV | RTW_RCR_ACRC32)
+
+ if (ic->ic_opmode == IEEE80211_M_MONITOR)
+ sc->sc_rcr |= RTW_RCR_MONITOR;
+ else
+ sc->sc_rcr &= ~RTW_RCR_MONITOR;
+
+ /* XXX reference sources BEGIN */
+ sc->sc_rcr |= RTW_RCR_ENMARP;
+ sc->sc_rcr |= RTW_RCR_AB | RTW_RCR_AM | RTW_RCR_APM;
+#if 0
+ /* receive broadcasts in our BSS */
+ sc->sc_rcr |= RTW_RCR_ADD3;
+#endif
+ /* XXX reference sources END */
+
+ /* receive pwrmgmt frames. */
+ sc->sc_rcr |= RTW_RCR_APWRMGT;
+ /* receive mgmt/ctrl/data frames. */
+ sc->sc_rcr |= RTW_RCR_ADF | RTW_RCR_AMF;
+ /* initialize Rx DMA threshold, Tx DMA burst size */
+ sc->sc_rcr |= RTW_RCR_RXFTH_WHOLE | RTW_RCR_MXDMA_1024;
+
+ ifp->if_flags &= ~IFF_ALLMULTI;
+
+ 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.
+ */
+ ETHER_FIRST_MULTI(step, ec, enm);
+ while (enm != NULL) {
+ /* XXX */
+ if (memcmp(enm->enm_addrlo, enm->enm_addrhi,
+ ETHER_ADDR_LEN) != 0)
+ goto allmulti;
+
+ hash = rtw_calchash(enm->enm_addrlo);
+ hashes[hash >> 5] |= 1 << (hash & 0x1f);
+ ETHER_NEXT_MULTI(step, enm);
+ }
+
+ if (ifp->if_flags & IFF_BROADCAST) {
+ hash = rtw_calchash(etherbroadcastaddr);
+ hashes[hash >> 5] |= 1 << (hash & 0x1f);
+ }
+
+ /* 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 */
+
+ if (ic->ic_state == IEEE80211_S_SCAN)
+ sc->sc_rcr |= RTW_RCR_AB; /* accept all broadcast */
+
+ 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",
+ sc->sc_dev.dv_xname, RTW_READ(regs, RTW_MAR0),
+ RTW_READ(regs, RTW_MAR1), RTW_READ(regs, RTW_RCR)));
+
+ return;
+}
+
+/* Must be called at splnet. */
+int
+rtw_init(struct ifnet *ifp)
+{
+ struct rtw_softc *sc = (struct rtw_softc *)ifp->if_softc;
+ struct ieee80211com *ic = &sc->sc_ic;
+ struct rtw_regs *regs = &sc->sc_regs;
+ int rc = 0;
+
+ if ((rc = rtw_enable(sc)) != 0)
+ goto out;
+
+ /* Cancel pending I/O and reset. */
+ rtw_stop(ifp, 0);
+
+ ic->ic_bss->ni_chan = ic->ic_ibss_chan;
+ DPRINTF(sc, RTW_DEBUG_TUNE, ("%s: channel %d freq %d flags 0x%04x\n",
+ __func__, ieee80211_chan2ieee(ic, ic->ic_bss->ni_chan),
+ ic->ic_bss->ni_chan->ic_freq, ic->ic_bss->ni_chan->ic_flags));
+
+ if ((rc = rtw_pwrstate(sc, RTW_OFF)) != 0)
+ goto out;
+
+ rtw_swring_setup(sc);
+
+ 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);
+
+#if 0
+ RTW_WRITE(regs, RTW_FEMR, RTW_FEMR_GWAKE|RTW_FEMR_WKUP|RTW_FEMR_INTR);
+#endif
+ /* XXX from reference sources */
+ RTW_WRITE(regs, RTW_FEMR, 0xffff);
+ RTW_SYNC(regs, RTW_FEMR, RTW_FEMR);
+
+ rtw_set_rfprog(regs, sc->sc_rfchipid, sc->sc_dev.dv_xname);
+
+ 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_io_enable(regs, 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)
+ return ieee80211_new_state(ic, IEEE80211_S_RUN, -1);
+ else
+ return ieee80211_new_state(ic, IEEE80211_S_SCAN, -1);
+
+out:
+ return rc;
+}
+
+int
+rtw_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
+{
+ int rc = 0, s;
+ struct rtw_softc *sc = ifp->if_softc;
+ struct ieee80211com *ic = &sc->sc_ic;
+ struct ifreq *ifr = (struct ifreq *)data;
+ struct ifaddr *ifa = (struct ifaddr *)data;
+
+ s = splnet();
+ switch (cmd) {
+ case SIOCSIFMTU:
+ if (ifr->ifr_mtu > ETHERMTU || ifr->ifr_mtu < ETHERMIN) {
+ rc = EINVAL;
+ } else if (ifp->if_mtu != ifr->ifr_mtu) {
+ ifp->if_mtu = ifr->ifr_mtu;
+ }
+ break;
+ case SIOCSIFADDR:
+ ifp->if_flags |= IFF_UP;
+#ifdef INET
+ if (ifa->ifa_addr->sa_family == AF_INET) {
+ arp_ifinit(&ic->ic_ac, ifa);
+ }
+#endif /* INET */
+ /* FALLTHROUGH */
+
+ case SIOCSIFFLAGS:
+ if ((ifp->if_flags & IFF_UP) != 0) {
+ if (0 && (sc->sc_flags & RTW_F_ENABLED) != 0) {
+ rtw_pktfilt_load(sc);
+ } else
+ rc = rtw_init(ifp);
+#ifdef RTW_DEBUG
+ rtw_print_regs(&sc->sc_regs, ifp->if_xname, __func__);
+#endif /* RTW_DEBUG */
+ } else if ((sc->sc_flags & RTW_F_ENABLED) != 0) {
+#ifdef RTW_DEBUG
+ rtw_print_regs(&sc->sc_regs, ifp->if_xname, __func__);
+#endif /* RTW_DEBUG */
+ rtw_stop(ifp, 1);
+ }
+ break;
+ case SIOCADDMULTI:
+ case SIOCDELMULTI:
+ if (cmd == SIOCADDMULTI)
+ rc = ether_addmulti(ifr, &sc->sc_ic.ic_ac);
+ else
+ rc = ether_delmulti(ifr, &sc->sc_ic.ic_ac);
+ if (rc == ENETRESET) {
+ if (sc->sc_flags & IFF_RUNNING)
+ rtw_pktfilt_load(sc);
+ rc = 0;
+ }
+ break;
+ default:
+ if ((rc = ieee80211_ioctl(ifp, cmd, data)) == ENETRESET) {
+ if ((sc->sc_flags & RTW_F_ENABLED) != 0)
+ rc = rtw_init(ifp);
+ else
+ rc = 0;
+ }
+ break;
+ }
+ splx(s);
+ return rc;
+}
+
+/* Point *mp at the next 802.11 frame to transmit. Point *stcp
+ * at the driver's selection of transmit control block for the packet.
+ */
+static __inline int
+rtw_dequeue(struct ifnet *ifp, struct rtw_txctl_blk **stcp,
+ struct rtw_txdesc_blk **htcp, struct mbuf **mp,
+ struct ieee80211_node **nip)
+{
+ struct rtw_txctl_blk *stc;
+ struct rtw_txdesc_blk *htc;
+ struct mbuf *m0;
+ struct rtw_softc *sc;
+ struct ieee80211com *ic;
+
+ sc = (struct rtw_softc *)ifp->if_softc;
+
+ DPRINTF(sc, RTW_DEBUG_XMIT,
+ ("%s: enter %s\n", sc->sc_dev.dv_xname, __func__));
+ *mp = NULL;
+
+ stc = &sc->sc_txctl_blk[RTW_TXPRIMD];
+ htc = &sc->sc_txdesc_blk[RTW_TXPRIMD];
+
+ if (SIMPLEQ_EMPTY(&stc->stc_freeq) || htc->htc_nfree == 0) {
+ DPRINTF(sc, RTW_DEBUG_XMIT,
+ ("%s: out of descriptors\n", __func__));
+ ifp->if_flags |= IFF_OACTIVE;
+ return 0;
+ }
+
+ ic = &sc->sc_ic;
+
+ if (!IF_IS_EMPTY(&ic->ic_mgtq)) {
+ IF_DEQUEUE(&ic->ic_mgtq, m0);
+ *nip = (struct ieee80211_node *)m0->m_pkthdr.rcvif;
+ m0->m_pkthdr.rcvif = NULL;
+ DPRINTF(sc, RTW_DEBUG_XMIT,
+ ("%s: dequeue mgt frame\n", __func__));
+ } else if (ic->ic_state != IEEE80211_S_RUN) {
+ DPRINTF(sc, RTW_DEBUG_XMIT, ("%s: not running\n", __func__));
+ return 0;
+ } else if (!IF_IS_EMPTY(&ic->ic_pwrsaveq)) {
+ IF_DEQUEUE(&ic->ic_pwrsaveq, m0);
+ *nip = (struct ieee80211_node *)m0->m_pkthdr.rcvif;
+ m0->m_pkthdr.rcvif = NULL;
+ DPRINTF(sc, RTW_DEBUG_XMIT,
+ ("%s: dequeue pwrsave frame\n", __func__));
+ } else {
+ IFQ_POLL(&ifp->if_snd, m0);
+ if (m0 == NULL) {
+ DPRINTF(sc, RTW_DEBUG_XMIT,
+ ("%s: no frame\n", __func__));
+ return 0;
+ }
+ DPRINTF(sc, RTW_DEBUG_XMIT,
+ ("%s: dequeue data frame\n", __func__));
+ ifp->if_opackets++;
+#if NBPFILTER > 0
+ if (ifp->if_bpf)
+ bpf_mtap(ifp->if_bpf, m0);
+#endif
+ if ((m0 = ieee80211_encap(ifp, m0, nip)) == NULL) {
+ DPRINTF(sc, RTW_DEBUG_XMIT,
+ ("%s: encap error\n", __func__));
+ ifp->if_oerrors++;
+ return -1;
+ }
+ }
+ DPRINTF(sc, RTW_DEBUG_XMIT, ("%s: leave\n", __func__));
+ *stcp = stc;
+ *htcp = htc;
+ *mp = m0;
+ return 0;
+}
+
+int
+rtw_seg_too_short(bus_dmamap_t dmamap)
+{
+ int i;
+ for (i = 0; i < dmamap->dm_nsegs; i++) {
+ if (dmamap->dm_segs[i].ds_len < 4) {
+ printf("%s: segment too short\n", __func__);
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/* TBD factor with atw_start */
+struct mbuf *
+rtw_dmamap_load_txbuf(bus_dma_tag_t dmat, bus_dmamap_t dmam, struct mbuf *chain,
+ u_int ndescfree, short *ifflagsp, const char *dvname)
+{
+ int first, rc;
+ struct mbuf *m, *m0;
+
+ m0 = chain;
+
+ /*
+ * Load the DMA map. Copy and try (once) again if the packet
+ * didn't fit in the alloted number of segments.
+ */
+ for (first = 1;
+ ((rc = bus_dmamap_load_mbuf(dmat, dmam, m0,
+ BUS_DMA_WRITE|BUS_DMA_NOWAIT)) != 0 ||
+ dmam->dm_nsegs > ndescfree || rtw_seg_too_short(dmam)) && first;
+ first = 0) {
+ if (rc == 0)
+ bus_dmamap_unload(dmat, dmam);
+ MGETHDR(m, M_DONTWAIT, MT_DATA);
+ if (m == NULL) {
+ printf("%s: unable to allocate Tx mbuf\n",
+ dvname);
+ break;
+ }
+ if (m0->m_pkthdr.len > MHLEN) {
+ MCLGET(m, M_DONTWAIT);
+ if ((m->m_flags & M_EXT) == 0) {
+ printf("%s: cannot allocate Tx cluster\n",
+ dvname);
+ m_freem(m);
+ break;
+ }
+ }
+ m_copydata(m0, 0, m0->m_pkthdr.len, mtod(m, caddr_t));
+ m->m_pkthdr.len = m->m_len = m0->m_pkthdr.len;
+ m_freem(m0);
+ m0 = m;
+ m = NULL;
+ }
+ if (rc != 0) {
+ printf("%s: cannot load Tx buffer, rc = %d\n", dvname, rc);
+ m_freem(m0);
+ return NULL;
+ } else if (rtw_seg_too_short(dmam)) {
+ printf("%s: cannot load Tx buffer, segment too short\n",
+ dvname);
+ bus_dmamap_unload(dmat, dmam);
+ m_freem(m0);
+ return NULL;
+ } else if (dmam->dm_nsegs > ndescfree) {
+ *ifflagsp |= IFF_OACTIVE;
+ bus_dmamap_unload(dmat, dmam);
+ m_freem(m0);
+ return NULL;
+ }
+ return m0;
+}
+
+#ifdef RTW_DEBUG
+void
+rtw_print_txdesc(struct rtw_softc *sc, const char *action,
+ struct rtw_txctl *stx, struct rtw_txdesc_blk *htc, int desc)
+{
+ struct rtw_txdesc *htx = &htc->htc_desc[desc];
+ DPRINTF(sc, RTW_DEBUG_XMIT_DESC, ("%s: %p %s txdesc[%d] ctl0 %#08x "
+ "ctl1 %#08x buf %#08x len %#08x\n",
+ sc->sc_dev.dv_xname, stx, action, desc,
+ letoh32(htx->htx_ctl0),
+ letoh32(htx->htx_ctl1), letoh32(htx->htx_buf),
+ letoh32(htx->htx_len)));
+}
+#endif /* RTW_DEBUG */
+
+void
+rtw_start(struct ifnet *ifp)
+{
+ uint8_t tppoll;
+ int desc, i, lastdesc, npkt, rate;
+ uint32_t proto_ctl0, ctl0, ctl1;
+ bus_dmamap_t dmamap;
+ struct ieee80211com *ic;
+ struct ieee80211_duration *d0;
+ struct ieee80211_frame *wh;
+ struct ieee80211_node *ni;
+ struct mbuf *m0;
+ struct rtw_softc *sc;
+ struct rtw_txctl_blk *stc;
+ struct rtw_txdesc_blk *htc;
+ struct rtw_txctl *stx;
+ struct rtw_txdesc *htx;
+
+ sc = (struct rtw_softc *)ifp->if_softc;
+ ic = &sc->sc_ic;
+
+ DPRINTF(sc, RTW_DEBUG_XMIT,
+ ("%s: enter %s\n", sc->sc_dev.dv_xname, __func__));
+
+ /* XXX do real rate control */
+ proto_ctl0 = RTW_TXCTL0_RTSRATE_1MBPS;
+
+ switch (rate = MAX(2, ieee80211_get_rate(ic))) {
+ case 2:
+ proto_ctl0 |= RTW_TXCTL0_RATE_1MBPS;
+ break;
+ case 4:
+ proto_ctl0 |= RTW_TXCTL0_RATE_2MBPS;
+ break;
+ case 11:
+ proto_ctl0 |= RTW_TXCTL0_RATE_5MBPS;
+ break;
+ case 22:
+ proto_ctl0 |= RTW_TXCTL0_RATE_11MBPS;
+ break;
+ }
+
+ if ((ic->ic_flags & IEEE80211_F_SHPREAMBLE) != 0)
+ proto_ctl0 |= RTW_TXCTL0_SPLCP;
+
+ for (;;) {
+ if (rtw_dequeue(ifp, &stc, &htc, &m0, &ni) == -1)
+ continue;
+ if (m0 == NULL)
+ break;
+ stx = SIMPLEQ_FIRST(&stc->stc_freeq);
+
+ dmamap = stx->stx_dmamap;
+
+ m0 = rtw_dmamap_load_txbuf(sc->sc_dmat, dmamap, m0,
+ htc->htc_nfree, &ifp->if_flags, sc->sc_dev.dv_xname);
+
+ if (m0 == NULL || dmamap->dm_nsegs == 0) {
+ DPRINTF(sc, RTW_DEBUG_XMIT,
+ ("%s: fail dmamap load\n", __func__));
+ goto post_dequeue_err;
+ }
+
+#ifdef RTW_DEBUG
+ if ((sc->sc_if.if_flags & (IFF_DEBUG|IFF_LINK2)) ==
+ (IFF_DEBUG|IFF_LINK2)) {
+ ieee80211_dump_pkt(mtod(m0, uint8_t *),
+ (dmamap->dm_nsegs == 1) ? m0->m_pkthdr.len
+ : sizeof(wh),
+ rate, 0);
+ }
+#endif /* RTW_DEBUG */
+ ctl0 = proto_ctl0 |
+ LSHIFT(m0->m_pkthdr.len, RTW_TXCTL0_TPKTSIZE_MASK);
+
+ wh = mtod(m0, struct ieee80211_frame *);
+
+ if (ieee80211_compute_duration(wh, m0->m_pkthdr.len,
+ ic->ic_flags, ic->ic_fragthreshold,
+ rate, &stx->stx_d0, &stx->stx_dn, &npkt,
+ (sc->sc_if.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;
+ }
+
+ /* XXX >= ? */
+ if (m0->m_pkthdr.len > ic->ic_rtsthreshold)
+ ctl0 |= RTW_TXCTL0_RTSEN;
+
+ d0 = &stx->stx_d0;
+
+ *(uint16_t*)wh->i_dur = htole16(d0->d_data_dur);
+
+ ctl1 = LSHIFT(d0->d_plcp_len, RTW_TXCTL1_LENGTH_MASK) |
+ LSHIFT(d0->d_rts_dur, RTW_TXCTL1_RTSDUR_MASK);
+
+ if (d0->d_residue)
+ ctl1 |= RTW_TXCTL1_LENGEXT;
+
+ /* TBD fragmentation */
+
+ stx->stx_first = htc->htc_next;
+
+ rtw_txdescs_sync(sc->sc_dmat, sc->sc_desc_dmamap,
+ htc, stx->stx_first, dmamap->dm_nsegs,
+ BUS_DMASYNC_PREWRITE);
+
+ KASSERT(stx->stx_first < htc->htc_ndesc);
+
+ for (i = 0, lastdesc = desc = stx->stx_first;
+ i < dmamap->dm_nsegs;
+ i++, desc = RTW_NEXT_IDX(htc, desc)) {
+ if (dmamap->dm_segs[i].ds_len > RTW_TXLEN_LENGTH_MASK) {
+ DPRINTF(sc, RTW_DEBUG_XMIT_DESC,
+ ("%s: seg too long\n", __func__));
+ goto post_load_err;
+ }
+ htx = &htc->htc_desc[desc];
+ htx->htx_ctl0 = htole32(ctl0);
+ if (i != 0)
+ htx->htx_ctl0 |= htole32(RTW_TXCTL0_OWN);
+ htx->htx_ctl1 = htole32(ctl1);
+ htx->htx_buf = htole32(dmamap->dm_segs[i].ds_addr);
+ htx->htx_len = htole32(dmamap->dm_segs[i].ds_len);
+ lastdesc = desc;
+#ifdef RTW_DEBUG
+ rtw_print_txdesc(sc, "load", stx, htc, desc);
+#endif /* RTW_DEBUG */
+ }
+
+ KASSERT(desc < htc->htc_ndesc);
+
+ stx->stx_ni = ni;
+ stx->stx_mbuf = m0;
+ stx->stx_last = lastdesc;
+ htc->htc_desc[stx->stx_last].htx_ctl0 |= htole32(RTW_TXCTL0_LS);
+ htc->htc_desc[stx->stx_first].htx_ctl0 |=
+ htole32(RTW_TXCTL0_FS);
+
+#ifdef RTW_DEBUG
+ rtw_print_txdesc(sc, "FS on", stx, htc, stx->stx_first);
+ rtw_print_txdesc(sc, "LS on", stx, htc, stx->stx_last);
+#endif /* RTW_DEBUG */
+
+ htc->htc_nfree -= dmamap->dm_nsegs;
+ htc->htc_next = desc;
+
+ rtw_txdescs_sync(sc->sc_dmat, sc->sc_desc_dmamap,
+ htc, stx->stx_first, dmamap->dm_nsegs,
+ BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE);
+
+ htc->htc_desc[stx->stx_first].htx_ctl0 |=
+ htole32(RTW_TXCTL0_OWN);
+
+#ifdef RTW_DEBUG
+ rtw_print_txdesc(sc, "OWN on", stx, htc, stx->stx_first);
+#endif /* RTW_DEBUG */
+
+ rtw_txdescs_sync(sc->sc_dmat, sc->sc_desc_dmamap,
+ htc, stx->stx_first, 1,
+ BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE);
+
+ SIMPLEQ_REMOVE_HEAD(&stc->stc_freeq, stx_q);
+ SIMPLEQ_INSERT_TAIL(&stc->stc_dirtyq, stx, stx_q);
+
+ stc->stc_tx_timer = 5;
+ ifp->if_timer = 1;
+
+ tppoll = RTW_READ8(&sc->sc_regs, RTW_TPPOLL);
+
+ /* TBD poke other queues. */
+ RTW_WRITE8(&sc->sc_regs, RTW_TPPOLL, tppoll | RTW_TPPOLL_NPQ);
+ RTW_SYNC(&sc->sc_regs, RTW_TPPOLL, RTW_TPPOLL);
+ }
+ DPRINTF(sc, RTW_DEBUG_XMIT, ("%s: leave\n", __func__));
+ return;
+post_load_err:
+ bus_dmamap_unload(sc->sc_dmat, dmamap);
+ m_freem(m0);
+post_dequeue_err:
+ if (ni != ic->ic_bss)
+ ieee80211_free_node(&sc->sc_ic, ni);
+ return;
+}
+
+void
+rtw_watchdog(struct ifnet *ifp)
+{
+ int pri;
+ struct rtw_softc *sc;
+ struct rtw_txctl_blk *stc;
+
+ sc = ifp->if_softc;
+
+ ifp->if_timer = 0;
+
+ if ((sc->sc_flags & RTW_F_ENABLED) == 0)
+ return;
+
+ for (pri = 0; pri < RTW_NTXPRI; pri++) {
+ stc = &sc->sc_txctl_blk[pri];
+
+ if (stc->stc_tx_timer == 0)
+ continue;
+
+ if (--stc->stc_tx_timer == 0) {
+ if (SIMPLEQ_EMPTY(&stc->stc_dirtyq))
+ continue;
+ printf("%s: transmit timeout, priority %d\n",
+ ifp->if_xname, pri);
+ ifp->if_oerrors++;
+ /* Stop Tx DMA, disable transmitter, clear
+ * Tx rings, and restart.
+ */
+ RTW_WRITE8(&sc->sc_regs, RTW_TPPOLL, RTW_TPPOLL_SNPQ);
+ RTW_SYNC(&sc->sc_regs, RTW_TPPOLL, RTW_TPPOLL);
+ rtw_io_enable(&sc->sc_regs, RTW_CR_TE, 0);
+ rtw_txdescs_reset(sc);
+ rtw_io_enable(&sc->sc_regs, RTW_CR_TE, 1);
+ rtw_start(ifp);
+ } else
+ ifp->if_timer = 1;
+ }
+ ieee80211_watchdog(ifp);
+ return;
+}
+
+void
+rtw_start_beacon(struct rtw_softc *sc, int enable)
+{
+ /* TBD */
+ return;
+}
+
+void
+rtw_next_scan(void *arg)
+{
+ struct rtw_softc *sc = arg;
+ struct ieee80211com *ic = &sc->sc_ic;
+ struct ifnet *ifp = &ic->ic_if;
+ int s;
+
+ /* don't call rtw_start w/o network interrupts blocked */
+ s = splnet();
+ if (ic->ic_state == IEEE80211_S_SCAN)
+ ieee80211_next_scan(ifp);
+ splx(s);
+}
+
+void
+rtw_join_bss(struct rtw_softc *sc, uint8_t *bssid, enum ieee80211_opmode opmode,
+ uint16_t intval0)
+{
+ uint16_t bcnitv, 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, PRESHIFT(RTW_BCNITV_BCNITV_MASK));
+
+ bcnitv = RTW_READ16(regs, RTW_BCNITV) & ~RTW_BCNITV_BCNITV_MASK;
+ bcnitv |= LSHIFT(intval, RTW_BCNITV_BCNITV_MASK);
+ RTW_WRITE16(regs, RTW_BCNITV, bcnitv);
+ /* magic from Linux */
+ RTW_WRITE16(regs, RTW_ATIMWND, LSHIFT(1, RTW_ATIMWND_ATIMWND));
+ RTW_WRITE16(regs, RTW_ATIMTRITV, LSHIFT(2, RTW_ATIMTRITV_ATIMTRITV));
+
+ rtw_set_nettype(sc, opmode);
+
+ rtw_set_access(sc, RTW_ACCESS_NONE);
+
+ /* TBD WEP */
+ RTW_WRITE8(regs, RTW_SCR, 0);
+
+ rtw_io_enable(regs, RTW_CR_RE | RTW_CR_TE, 1);
+}
+
+/* Synchronize the hardware state with the software state. */
+int
+rtw_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg)
+{
+ struct ifnet *ifp = &ic->ic_if;
+ struct rtw_softc *sc = ifp->if_softc;
+ enum ieee80211_state ostate;
+ int error;
+
+ ostate = ic->ic_state;
+
+ if (nstate == IEEE80211_S_INIT) {
+ timeout_del(&sc->sc_scan_to);
+ sc->sc_cur_chan = IEEE80211_CHAN_ANY;
+ rtw_start_beacon(sc, 0);
+ return (*sc->sc_mtbl.mt_newstate)(ic, nstate, arg);
+ }
+
+ if (ostate == IEEE80211_S_INIT && nstate != IEEE80211_S_INIT)
+ rtw_pwrstate(sc, RTW_ON);
+
+ if ((error = rtw_tune(sc)) != 0)
+ return error;
+
+ switch (nstate) {
+ case IEEE80211_S_ASSOC:
+ rtw_join_bss(sc, ic->ic_bss->ni_bssid, ic->ic_opmode,
+ ic->ic_bss->ni_intval);
+ break;
+ case IEEE80211_S_INIT:
+ panic("%s: unexpected state IEEE80211_S_INIT\n", __func__);
+ break;
+ case IEEE80211_S_SCAN:
+ if (ostate != IEEE80211_S_SCAN) {
+ (void)memset(ic->ic_bss->ni_bssid, 0,
+ IEEE80211_ADDR_LEN);
+ rtw_join_bss(sc, ic->ic_bss->ni_bssid, ic->ic_opmode,
+ ic->ic_bss->ni_intval);
+ }
+
+ timeout_add(&sc->sc_scan_to, rtw_dwelltime * hz / 1000);
+
+ break;
+ case IEEE80211_S_RUN:
+ if (ic->ic_opmode == IEEE80211_M_STA)
+ break;
+ /*FALLTHROUGH*/
+ case IEEE80211_S_AUTH:
+#if 0
+ rtw_write_bcn_thresh(sc);
+ rtw_write_ssid(sc);
+ rtw_write_sup_rates(sc);
+#endif
+ if (ic->ic_opmode == IEEE80211_M_AHDEMO ||
+ ic->ic_opmode == IEEE80211_M_MONITOR)
+ break;
+
+ /* TBD set listen interval */
+
+#if 0
+ rtw_tsf(sc);
+#endif
+ break;
+ }
+
+ if (nstate != IEEE80211_S_SCAN)
+ timeout_del(&sc->sc_scan_to);
+
+ if (nstate == IEEE80211_S_RUN &&
+ (ic->ic_opmode == IEEE80211_M_HOSTAP ||
+ ic->ic_opmode == IEEE80211_M_IBSS))
+ rtw_start_beacon(sc, 1);
+ else
+ rtw_start_beacon(sc, 0);
+
+ return (*sc->sc_mtbl.mt_newstate)(ic, nstate, arg);
+}
+
+void
+rtw_recv_beacon(struct rtw_softc *sc, struct mbuf *m,
+ struct ieee80211_node *ni, int subtype, int rssi, u_int32_t rstamp)
+{
+ (*sc->sc_mtbl.mt_recv_mgmt)(&sc->sc_ic, m, ni, subtype, rssi, rstamp);
+ return;
+}
+
+void
+rtw_recv_mgmt(struct ieee80211com *ic, struct mbuf *m,
+ struct ieee80211_node *ni, int subtype, int rssi, u_int32_t rstamp)
+{
+ struct rtw_softc *sc = (struct rtw_softc*)ic->ic_softc;
+
+ switch (subtype) {
+ case IEEE80211_FC0_SUBTYPE_PROBE_REQ:
+ /* do nothing: hardware answers probe request XXX */
+ break;
+ case IEEE80211_FC0_SUBTYPE_PROBE_RESP:
+ case IEEE80211_FC0_SUBTYPE_BEACON:
+ rtw_recv_beacon(sc, m, ni, subtype, rssi, rstamp);
+ break;
+ default:
+ (*sc->sc_mtbl.mt_recv_mgmt)(ic, m, ni, subtype, rssi, rstamp);
+ break;
+ }
+ return;
+}
+
+struct ieee80211_node *
+rtw_node_alloc(struct ieee80211com *ic)
+{
+ struct rtw_softc *sc = (struct rtw_softc *)ic->ic_if.if_softc;
+ struct ieee80211_node *ni = (*sc->sc_mtbl.mt_node_alloc)(ic);
+
+ DPRINTF(sc, RTW_DEBUG_NODE,
+ ("%s: alloc node %p\n", sc->sc_dev.dv_xname, ni));
+ return ni;
+}
+
+void
+rtw_node_free(struct ieee80211com *ic, struct ieee80211_node *ni)
+{
+ struct rtw_softc *sc = (struct rtw_softc *)ic->ic_if.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)(ic, ni);
+}
+
+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;
+}
+
+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);
+}
+
+void
+rtw_power(int why, void *arg)
+{
+ struct rtw_softc *sc = arg;
+ struct ifnet *ifp = &sc->sc_ic.ic_if;
+ int s;
+
+ DPRINTF(sc, RTW_DEBUG_PWR,
+ ("%s: rtw_power(%d,)\n", sc->sc_dev.dv_xname, why));
+
+ s = splnet();
+ switch (why) {
+ case PWR_STANDBY:
+ /* XXX do nothing. */
+ break;
+ case PWR_SUSPEND:
+ rtw_stop(ifp, 0);
+ if (sc->sc_power != NULL)
+ (*sc->sc_power)(sc, why);
+ break;
+ case PWR_RESUME:
+ if (ifp->if_flags & IFF_UP) {
+ if (sc->sc_power != NULL)
+ (*sc->sc_power)(sc, why);
+ rtw_init(ifp);
+ }
+ break;
+ }
+ splx(s);
+}
+
+/* rtw_shutdown: make sure the interface is stopped at reboot time. */
+void
+rtw_shutdown(void *arg)
+{
+ struct rtw_softc *sc = arg;
+
+ rtw_stop(&sc->sc_ic.ic_if, 1);
+}
+
+static __inline void
+rtw_setifprops(struct ifnet *ifp, const char *dvname, void *softc)
+{
+ (void)memcpy(ifp->if_xname, dvname, IFNAMSIZ);
+ ifp->if_softc = softc;
+ ifp->if_flags = IFF_SIMPLEX | IFF_BROADCAST | IFF_MULTICAST |
+ IFF_NOTRAILERS;
+ ifp->if_ioctl = rtw_ioctl;
+ ifp->if_start = rtw_start;
+ ifp->if_watchdog = rtw_watchdog;
+}
+
+static __inline void
+rtw_set80211props(struct ieee80211com *ic)
+{
+ int nrate;
+ 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 | IEEE80211_C_WEP;
+
+ nrate = 0;
+ ic->ic_sup_rates[IEEE80211_MODE_11B].rs_rates[nrate++] =
+ IEEE80211_RATE_BASIC | 2;
+ ic->ic_sup_rates[IEEE80211_MODE_11B].rs_rates[nrate++] =
+ IEEE80211_RATE_BASIC | 4;
+ ic->ic_sup_rates[IEEE80211_MODE_11B].rs_rates[nrate++] = 11;
+ ic->ic_sup_rates[IEEE80211_MODE_11B].rs_rates[nrate++] = 22;
+ ic->ic_sup_rates[IEEE80211_MODE_11B].rs_nrates = nrate;
+}
+
+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;
+
+ 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;
+}
+
+static __inline void
+rtw_establish_hooks(struct rtw_hooks *hooks, const char *dvname,
+ void *arg)
+{
+ /*
+ * Make sure the interface is shutdown during reboot.
+ */
+ hooks->rh_shutdown = shutdownhook_establish(rtw_shutdown, arg);
+ if (hooks->rh_shutdown == NULL)
+ printf("%s: WARNING: unable to establish shutdown hook\n",
+ dvname);
+
+ /*
+ * Add a suspend hook to make sure we come back up after a
+ * resume.
+ */
+ hooks->rh_power = powerhook_establish(rtw_power, arg);
+ if (hooks->rh_power == NULL)
+ printf("%s: WARNING: unable to establish power hook\n",
+ dvname);
+}
+
+static __inline void
+rtw_disestablish_hooks(struct rtw_hooks *hooks, const char *dvname,
+ void *arg)
+{
+ if (hooks->rh_shutdown != NULL)
+ shutdownhook_disestablish(hooks->rh_shutdown);
+
+ if (hooks->rh_power != NULL)
+ powerhook_disestablish(hooks->rh_power);
+}
+
+static __inline void
+rtw_init_radiotap(struct rtw_softc *sc)
+{
+ memset(&sc->sc_rxtapu, 0, sizeof(sc->sc_rxtapu));
+ sc->sc_rxtap.rr_ihdr.it_len = sizeof(sc->sc_rxtapu);
+ sc->sc_rxtap.rr_ihdr.it_present = RTW_RX_RADIOTAP_PRESENT;
+
+ memset(&sc->sc_txtapu, 0, sizeof(sc->sc_txtapu));
+ sc->sc_txtap.rt_ihdr.it_len = sizeof(sc->sc_txtapu);
+ sc->sc_txtap.rt_ihdr.it_present = RTW_TX_RADIOTAP_PRESENT;
+}
+
+static int
+rtw_txctl_blk_setup(struct rtw_txctl_blk *stc, u_int qlen)
+{
+ SIMPLEQ_INIT(&stc->stc_dirtyq);
+ SIMPLEQ_INIT(&stc->stc_freeq);
+ stc->stc_ndesc = qlen;
+ stc->stc_desc = malloc(qlen * sizeof(*stc->stc_desc), M_DEVBUF,
+ M_NOWAIT);
+ if (stc->stc_desc == NULL)
+ return ENOMEM;
+ return 0;
+}
+
+void
+rtw_txctl_blk_cleanup_all(struct rtw_softc *sc)
+{
+ int pri;
+ struct rtw_txctl_blk *stc;
+
+ for (pri = 0; pri < RTW_NTXPRI; pri++) {
+ stc = &sc->sc_txctl_blk[pri];
+ free(stc->stc_desc, M_DEVBUF);
+ stc->stc_desc = NULL;
+ }
+}
+
+int
+rtw_txctl_blk_setup_all(struct rtw_softc *sc)
+{
+ int pri, rc = 0;
+ int qlen[RTW_NTXPRI] =
+ {RTW_TXQLENLO, RTW_TXQLENMD, RTW_TXQLENHI, RTW_TXQLENBCN};
+
+ for (pri = 0; pri < RTW_NTXPRI; pri++) {
+ rc = rtw_txctl_blk_setup(&sc->sc_txctl_blk[pri], qlen[pri]);
+ if (rc != 0)
+ break;
+ }
+ return rc;
+}
+
+void
+rtw_txdesc_blk_setup(struct rtw_txdesc_blk *htc, struct rtw_txdesc *desc,
+ u_int ndesc, bus_addr_t ofs, bus_addr_t physbase)
+{
+ htc->htc_ndesc = ndesc;
+ htc->htc_desc = desc;
+ htc->htc_physbase = physbase;
+ htc->htc_ofs = ofs;
+
+ (void)memset(htc->htc_desc, 0,
+ sizeof(htc->htc_desc[0]) * htc->htc_ndesc);
+
+ rtw_txdesc_blk_reset(htc);
+}
+
+void
+rtw_txdesc_blk_setup_all(struct rtw_softc *sc)
+{
+ rtw_txdesc_blk_setup(&sc->sc_txdesc_blk[RTW_TXPRILO],
+ &sc->sc_descs->hd_txlo[0], RTW_NTXDESCLO,
+ RTW_RING_OFFSET(hd_txlo), RTW_RING_BASE(sc, hd_txlo));
+
+ rtw_txdesc_blk_setup(&sc->sc_txdesc_blk[RTW_TXPRIMD],
+ &sc->sc_descs->hd_txmd[0], RTW_NTXDESCMD,
+ RTW_RING_OFFSET(hd_txmd), RTW_RING_BASE(sc, hd_txmd));
+
+ rtw_txdesc_blk_setup(&sc->sc_txdesc_blk[RTW_TXPRIHI],
+ &sc->sc_descs->hd_txhi[0], RTW_NTXDESCHI,
+ RTW_RING_OFFSET(hd_txhi), RTW_RING_BASE(sc, hd_txhi));
+
+ rtw_txdesc_blk_setup(&sc->sc_txdesc_blk[RTW_TXPRIBCN],
+ &sc->sc_descs->hd_bcn[0], RTW_NTXDESCBCN,
+ RTW_RING_OFFSET(hd_bcn), RTW_RING_BASE(sc, hd_bcn));
+}
+
+struct rtw_rf *
+rtw_rf_attach(struct rtw_softc *sc, enum rtw_rfchipid rfchipid,
+ rtw_rf_write_t rf_write, int digphy)
+{
+ struct rtw_rf *rf;
+
+ switch (rfchipid) {
+ 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 = (void *)sc;
+ return rf;
+}
+
+/* Revision C and later use a different PHY delay setting than
+ * revisions A and B.
+ */
+u_int8_t
+rtw_check_phydelay(struct rtw_regs *regs, u_int32_t rcr0)
+{
+#define REVAB (RTW_RCR_MXDMA_UNLIMITED | RTW_RCR_AICV)
+#define REVC (REVAB | RTW_RCR_RXFTH_WHOLE)
+
+ u_int8_t phydelay = LSHIFT(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, rcr0); /* restore RCR */
+ RTW_SYNC(regs, RTW_RCR, RTW_RCR);
+
+ return phydelay;
+#undef REVC
+}
+
+void
+rtw_attach(struct rtw_softc *sc)
+{
+ rtw_rf_write_t rf_write;
+ struct rtw_txctl_blk *stc;
+ int pri, rc, vers;
+
+#if 0
+ CASSERT(RTW_DESC_ALIGNMENT % sizeof(struct rtw_txdesc) == 0,
+ "RTW_DESC_ALIGNMENT is not a multiple of "
+ "sizeof(struct rtw_txdesc)");
+
+ CASSERT(RTW_DESC_ALIGNMENT % sizeof(struct rtw_rxdesc) == 0,
+ "RTW_DESC_ALIGNMENT is not a multiple of "
+ "sizeof(struct rtw_rxdesc)");
+
+ CASSERT(RTW_DESC_ALIGNMENT % RTW_MAXPKTSEGS == 0,
+ "RTW_DESC_ALIGNMENT is not a multiple of RTW_MAXPKTSEGS");
+#endif
+
+ NEXT_ATTACH_STATE(sc, DETACHED);
+
+ switch (RTW_READ(&sc->sc_regs, RTW_TCR) & RTW_TCR_HWVERID_MASK) {
+ case RTW_TCR_HWVERID_F:
+ vers = 'F';
+ rf_write = rtw_rf_hostwrite;
+ break;
+ case RTW_TCR_HWVERID_D:
+ vers = 'D';
+ if (rtw_host_rfio)
+ rf_write = rtw_rf_hostwrite;
+ else
+ rf_write = rtw_rf_macwrite;
+ break;
+ default:
+ vers = '?';
+ rf_write = rtw_rf_macwrite;
+ break;
+ }
+ printf("%s: hardware version %c\n", sc->sc_dev.dv_xname, vers);
+
+ rc = bus_dmamem_alloc(sc->sc_dmat, sizeof(struct rtw_descs),
+ RTW_DESC_ALIGNMENT, 0, &sc->sc_desc_segs, 1, &sc->sc_desc_nsegs,
+ 0);
+
+ if (rc != 0) {
+ printf("%s: could not allocate hw descriptors, error %d\n",
+ sc->sc_dev.dv_xname, rc);
+ goto err;
+ }
+
+ NEXT_ATTACH_STATE(sc, FINISH_DESC_ALLOC);
+
+ rc = bus_dmamem_map(sc->sc_dmat, &sc->sc_desc_segs,
+ sc->sc_desc_nsegs, sizeof(struct rtw_descs),
+ (caddr_t*)&sc->sc_descs, BUS_DMA_COHERENT);
+
+ if (rc != 0) {
+ printf("%s: could not map hw descriptors, error %d\n",
+ sc->sc_dev.dv_xname, rc);
+ goto err;
+ }
+ NEXT_ATTACH_STATE(sc, FINISH_DESC_MAP);
+
+ rc = bus_dmamap_create(sc->sc_dmat, sizeof(struct rtw_descs), 1,
+ sizeof(struct rtw_descs), 0, 0, &sc->sc_desc_dmamap);
+
+ if (rc != 0) {
+ printf("%s: could not create DMA map for hw descriptors, "
+ "error %d\n", sc->sc_dev.dv_xname, rc);
+ goto err;
+ }
+ NEXT_ATTACH_STATE(sc, FINISH_DESCMAP_CREATE);
+
+ rc = bus_dmamap_load(sc->sc_dmat, sc->sc_desc_dmamap, sc->sc_descs,
+ sizeof(struct rtw_descs), NULL, 0);
+
+ if (rc != 0) {
+ printf("%s: could not load DMA map for hw descriptors, "
+ "error %d\n", sc->sc_dev.dv_xname, rc);
+ goto err;
+ }
+ NEXT_ATTACH_STATE(sc, FINISH_DESCMAP_LOAD);
+
+ if (rtw_txctl_blk_setup_all(sc) != 0)
+ goto err;
+ NEXT_ATTACH_STATE(sc, FINISH_TXCTLBLK_SETUP);
+
+ rtw_txdesc_blk_setup_all(sc);
+
+ NEXT_ATTACH_STATE(sc, FINISH_TXDESCBLK_SETUP);
+
+ sc->sc_rxdesc = &sc->sc_descs->hd_rx[0];
+
+ rtw_rxctls_setup(&sc->sc_rxctl[0]);
+
+ for (pri = 0; pri < RTW_NTXPRI; pri++) {
+ stc = &sc->sc_txctl_blk[pri];
+
+ if ((rc = rtw_txdesc_dmamaps_create(sc->sc_dmat,
+ &stc->stc_desc[0], stc->stc_ndesc)) != 0) {
+ printf("%s: could not load DMA map for "
+ "hw tx descriptors, error %d\n",
+ sc->sc_dev.dv_xname, rc);
+ goto err;
+ }
+ }
+
+ NEXT_ATTACH_STATE(sc, FINISH_TXMAPS_CREATE);
+ if ((rc = rtw_rxdesc_dmamaps_create(sc->sc_dmat, &sc->sc_rxctl[0],
+ RTW_RXQLEN)) != 0) {
+ printf("%s: could not load DMA map for hw rx descriptors, "
+ "error %d\n", sc->sc_dev.dv_xname, rc);
+ goto err;
+ }
+ NEXT_ATTACH_STATE(sc, FINISH_RXMAPS_CREATE);
+
+ /* Reset the chip to a known state. */
+ if (rtw_reset(sc) != 0)
+ goto err;
+ NEXT_ATTACH_STATE(sc, FINISH_RESET);
+
+ sc->sc_rcr = RTW_READ(&sc->sc_regs, RTW_RCR);
+
+ if ((sc->sc_rcr & RTW_RCR_9356SEL) != 0)
+ sc->sc_flags |= RTW_F_9356SROM;
+
+ if (rtw_srom_read(&sc->sc_regs, sc->sc_flags, &sc->sc_srom,
+ sc->sc_dev.dv_xname) != 0)
+ goto err;
+
+ NEXT_ATTACH_STATE(sc, FINISH_READ_SROM);
+
+ if (rtw_srom_parse(&sc->sc_srom, &sc->sc_flags, &sc->sc_csthr,
+ &sc->sc_rfchipid, &sc->sc_rcr, &sc->sc_locale,
+ sc->sc_dev.dv_xname) != 0) {
+ printf("%s: attach failed, malformed serial ROM\n",
+ sc->sc_dev.dv_xname);
+ goto err;
+ }
+
+ printf("%s: %s PHY\n", sc->sc_dev.dv_xname,
+ ((sc->sc_flags & RTW_F_DIGPHY) != 0) ? "digital" : "analog");
+
+ printf("%s: CS threshold %u\n", sc->sc_dev.dv_xname, sc->sc_csthr);
+
+ NEXT_ATTACH_STATE(sc, FINISH_PARSE_SROM);
+
+ sc->sc_rf = rtw_rf_attach(sc, sc->sc_rfchipid, rf_write,
+ sc->sc_flags & RTW_F_DIGPHY);
+
+ if (sc->sc_rf == NULL) {
+ printf("%s: attach failed, could not attach RF\n",
+ sc->sc_dev.dv_xname);
+ goto err;
+ }
+
+#if 0
+ if (rtw_identify_rf(&sc->sc_regs, &sc->sc_rftype,
+ sc->sc_dev.dv_xname) != 0) {
+ printf("%s: attach failed, unknown RF unidentified\n",
+ sc->sc_dev.dv_xname);
+ goto err;
+ }
+#endif
+
+ NEXT_ATTACH_STATE(sc, FINISH_RF_ATTACH);
+
+ sc->sc_phydelay = rtw_check_phydelay(&sc->sc_regs, sc->sc_rcr);
+
+ RTW_DPRINTF(RTW_DEBUG_ATTACH,
+ ("%s: PHY delay %d\n", sc->sc_dev.dv_xname, sc->sc_phydelay));
+
+ if (sc->sc_locale == RTW_LOCALE_UNKNOWN)
+ rtw_identify_country(&sc->sc_regs, &sc->sc_locale,
+ sc->sc_dev.dv_xname);
+
+ rtw_init_channels(sc->sc_locale, &sc->sc_ic.ic_channels,
+ sc->sc_dev.dv_xname);
+
+ if (rtw_identify_sta(&sc->sc_regs, &sc->sc_ic.ic_myaddr,
+ sc->sc_dev.dv_xname) != 0)
+ goto err;
+ NEXT_ATTACH_STATE(sc, FINISH_ID_STA);
+
+ rtw_setifprops(&sc->sc_if, sc->sc_dev.dv_xname, (void*)sc);
+
+ IFQ_SET_READY(&sc->sc_if.if_snd);
+
+ rtw_set80211props(&sc->sc_ic);
+
+ /*
+ * Call MI attach routines.
+ */
+ if_attach(&sc->sc_if);
+ ieee80211_ifattach(&sc->sc_if);
+
+ 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_if, rtw_media_change, rtw_media_status);
+ timeout_set(&sc->sc_scan_to, rtw_next_scan, sc);
+
+#if NBPFILTER > 0
+ bpfattach(&sc->sc_radiobpf, &sc->sc_ic.ic_if, DLT_IEEE802_11_RADIO,
+ sizeof(struct ieee80211_frame) + 64);
+#endif
+
+ rtw_establish_hooks(&sc->sc_hooks, sc->sc_dev.dv_xname, (void*)sc);
+
+ rtw_init_radiotap(sc);
+
+ NEXT_ATTACH_STATE(sc, FINISHED);
+
+ return;
+err:
+ rtw_detach(sc);
+ return;
+}
+
+int
+rtw_detach(struct rtw_softc *sc)
+{
+ int pri;
+
+ switch (sc->sc_attach_state) {
+ case FINISHED:
+ rtw_stop(&sc->sc_if, 1);
+
+ rtw_disestablish_hooks(&sc->sc_hooks, sc->sc_dev.dv_xname,
+ (void*)sc);
+ timeout_del(&sc->sc_scan_to);
+ ieee80211_ifdetach(&sc->sc_if);
+ if_detach(&sc->sc_if);
+ break;
+ case FINISH_ID_STA:
+ case FINISH_RF_ATTACH:
+ rtw_rf_destroy(sc->sc_rf);
+ sc->sc_rf = NULL;
+ /*FALLTHROUGH*/
+ case FINISH_PARSE_SROM:
+ case FINISH_READ_SROM:
+ rtw_srom_free(&sc->sc_srom);
+ /*FALLTHROUGH*/
+ case FINISH_RESET:
+ case FINISH_RXMAPS_CREATE:
+ rtw_rxdesc_dmamaps_destroy(sc->sc_dmat, &sc->sc_rxctl[0],
+ RTW_RXQLEN);
+ /*FALLTHROUGH*/
+ case FINISH_TXMAPS_CREATE:
+ for (pri = 0; pri < RTW_NTXPRI; pri++) {
+ rtw_txdesc_dmamaps_destroy(sc->sc_dmat,
+ sc->sc_txctl_blk[pri].stc_desc,
+ sc->sc_txctl_blk[pri].stc_ndesc);
+ }
+ /*FALLTHROUGH*/
+ case FINISH_TXDESCBLK_SETUP:
+ case FINISH_TXCTLBLK_SETUP:
+ rtw_txctl_blk_cleanup_all(sc);
+ /*FALLTHROUGH*/
+ case FINISH_DESCMAP_LOAD:
+ bus_dmamap_unload(sc->sc_dmat, sc->sc_desc_dmamap);
+ /*FALLTHROUGH*/
+ case FINISH_DESCMAP_CREATE:
+ bus_dmamap_destroy(sc->sc_dmat, sc->sc_desc_dmamap);
+ /*FALLTHROUGH*/
+ case FINISH_DESC_MAP:
+ bus_dmamem_unmap(sc->sc_dmat, (caddr_t)sc->sc_descs,
+ sizeof(struct rtw_descs));
+ /*FALLTHROUGH*/
+ case FINISH_DESC_ALLOC:
+ bus_dmamem_free(sc->sc_dmat, &sc->sc_desc_segs,
+ sc->sc_desc_nsegs);
+ /*FALLTHROUGH*/
+ case DETACHED:
+ NEXT_ATTACH_STATE(sc, DETACHED);
+ break;
+ }
+ return 0;
+}
diff --git a/sys/dev/ic/rtwphy.c b/sys/dev/ic/rtwphy.c
new file mode 100644
index 00000000000..d27fbbc23f1
--- /dev/null
+++ b/sys/dev/ic/rtwphy.c
@@ -0,0 +1,651 @@
+/* $OpenBSD: rtwphy.c,v 1.1 2004/12/29 01:02:31 jsg Exp $ */
+/* $NetBSD: rtwphy.c,v 1.2 2004/12/12 06:37:59 dyoung 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.
+ */
+/*
+ * Control the Philips SA2400 RF front-end and the baseband processor
+ * built into the Realtek RTL8180.
+ */
+
+#include <sys/cdefs.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/systm.h>
+#include <sys/types.h>
+#include <sys/malloc.h>
+
+#include <machine/bus.h>
+
+#include <net/if.h>
+#include <net/if_media.h>
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+
+#include <net80211/ieee80211_var.h>
+#include <net80211/ieee80211_compat.h>
+#include <net80211/ieee80211_radiotap.h>
+
+#include <dev/ic/rtwreg.h>
+#include <dev/ic/max2820reg.h>
+#include <dev/ic/sa2400reg.h>
+#include <dev/ic/rtwvar.h>
+#include <dev/ic/rtwphyio.h>
+#include <dev/ic/rtwphy.h>
+
+int rtw_bbp_preinit(struct rtw_regs *, u_int, int, u_int);
+int rtw_bbp_init(struct rtw_regs *, struct rtw_bbpset *, int,
+ int, u_int8_t, u_int);
+
+void verify_syna(u_int, u_int32_t);
+
+int rtw_sa2400_pwrstate(struct rtw_rf *, enum rtw_pwrstate);
+int rtw_sa2400_txpower(struct rtw_rf *, u_int8_t);
+int rtw_sa2400_tune(struct rtw_rf *, u_int);
+int rtw_sa2400_manrx_init(struct rtw_sa2400 *);
+int rtw_sa2400_vcocal_start(struct rtw_sa2400 *, int);
+int rtw_sa2400_vco_calibration(struct rtw_sa2400 *);
+int rtw_sa2400_filter_calibration(struct rtw_sa2400 *);
+int rtw_sa2400_dc_calibration(struct rtw_sa2400 *);
+int rtw_sa2400_agc_init(struct rtw_sa2400 *);
+void rtw_sa2400_destroy(struct rtw_rf *);
+int rtw_sa2400_calibrate(struct rtw_rf *, u_int);
+int rtw_sa2400_init(struct rtw_rf *, u_int, u_int8_t,
+ enum rtw_pwrstate);
+
+int rtw_max2820_pwrstate(struct rtw_rf *, enum rtw_pwrstate);
+void rtw_max2820_destroy(struct rtw_rf *);
+int rtw_max2820_init(struct rtw_rf *, u_int, u_int8_t,
+ enum rtw_pwrstate);
+int rtw_max2820_txpower(struct rtw_rf *, u_int8_t);
+int rtw_max2820_tune(struct rtw_rf *, u_int);
+
+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);
+}
+
+int
+rtw_bbp_init(struct rtw_regs *regs, struct rtw_bbpset *bb, int antdiv,
+ int dflantb, u_int8_t cs_threshold, u_int freq)
+{
+ int rc;
+ u_int32_t sys2, sys3;
+
+ sys2 = bb->bb_sys2;
+ if (antdiv)
+ sys2 |= RTW_BBP_SYS2_ANTDIV;
+ sys3 = bb->bb_sys3 |
+ LSHIFT(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;
+}
+
+int
+rtw_sa2400_txpower(struct rtw_rf *rf, u_int8_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 */
+void
+verify_syna(u_int freq, u_int32_t val)
+{
+ u_int32_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;
+ }
+ KASSERT(val == expected_val);
+}
+
+/* freq is in MHz */
+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;
+ u_int32_t syna, synb, sync;
+
+ /* 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.
+ */
+ int n = freq / 4, nf = (freq % 4) * 2;
+
+ syna = LSHIFT(nf, SA2400_SYNA_NF_MASK) | LSHIFT(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 = LSHIFT(11, SA2400_SYNB_R_MASK) | SA2400_SYNB_L_NORMAL |
+ SA2400_SYNB_ON | SA2400_SYNB_ONE |
+ LSHIFT(80, SA2400_SYNB_FC_MASK); /* agrees w/ SA2400_SYNA_FM = 0 */
+
+ sync = SA2400_SYNC_CP_NORMAL;
+
+ if ((rc = rtw_rfbus_write(bus, RTW_RFCHIPID_PHILIPS, SA2400_SYNA,
+ syna)) != 0)
+ return rc;
+ if ((rc = rtw_rfbus_write(bus, RTW_RFCHIPID_PHILIPS, SA2400_SYNB,
+ synb)) != 0)
+ return rc;
+ if ((rc = rtw_rfbus_write(bus, RTW_RFCHIPID_PHILIPS, SA2400_SYNC,
+ sync)) != 0)
+ return rc;
+ return rtw_rfbus_write(bus, RTW_RFCHIPID_PHILIPS, SA2400_SYND, 0x0);
+}
+
+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;
+ u_int32_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);
+}
+
+int
+rtw_sa2400_manrx_init(struct rtw_sa2400 *sa)
+{
+ u_int32_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 |= LSHIFT(1023, SA2400_MANRX_RXGAIN_MASK);
+
+ return rtw_rfbus_write(&sa->sa_bus, RTW_RFCHIPID_PHILIPS, SA2400_MANRX,
+ manrx);
+}
+
+int
+rtw_sa2400_vcocal_start(struct rtw_sa2400 *sa, int start)
+{
+ u_int32_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);
+}
+
+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);
+}
+
+int
+rtw_sa2400_filter_calibration(struct rtw_sa2400 *sa)
+{
+ u_int32_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);
+}
+
+int
+rtw_sa2400_dc_calibration(struct rtw_sa2400 *sa)
+{
+ struct rtw_rf *rf = &sa->sa_rf;
+ int rc;
+ u_int32_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;
+
+ DELAY(5); /* DCALIB after being in Tx mode for 5
+ * microseconds
+ */
+
+ 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;
+}
+
+int
+rtw_sa2400_agc_init(struct rtw_sa2400 *sa)
+{
+ u_int32_t agc;
+
+ agc = LSHIFT(25, SA2400_AGC_MAXGAIN_MASK);
+ agc |= LSHIFT(7, SA2400_AGC_BBPDELAY_MASK);
+ agc |= LSHIFT(15, SA2400_AGC_LNADELAY_MASK);
+ agc |= LSHIFT(27, SA2400_AGC_RXONDELAY_MASK);
+
+ return rtw_rfbus_write(&sa->sa_bus, RTW_RFCHIPID_PHILIPS, SA2400_AGC,
+ agc);
+}
+
+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);
+}
+
+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;
+}
+
+int
+rtw_sa2400_init(struct rtw_rf *rf, u_int freq, u_int8_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_NOWAIT);
+ if (sa == NULL)
+ return NULL;
+ bzero(sa, sizeof(struct rtw_sa2400));
+
+ 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;
+}
+
+/* freq is in MHz */
+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,
+ LSHIFT(freq - 2400, MAX2820_CHANNEL_CF_MASK));
+}
+
+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);
+}
+
+int
+rtw_max2820_init(struct rtw_rf *rf, u_int freq, u_int8_t opaque_txpower,
+ enum rtw_pwrstate power)
+{
+ struct rtw_max2820 *mx = (struct rtw_max2820 *)rf;
+ struct rtw_rfbus *bus = &mx->mx_bus;
+ int rc;
+
+ if ((rc = rtw_rfbus_write(bus, RTW_RFCHIPID_MAXIM, MAX2820_TEST,
+ MAX2820_TEST_DEFAULT)) != 0)
+ return rc;
+
+ if ((rc = rtw_rfbus_write(bus, RTW_RFCHIPID_MAXIM, MAX2820_ENABLE,
+ MAX2820_ENABLE_DEFAULT)) != 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;
+
+ if ((rc = rtw_rfbus_write(bus, RTW_RFCHIPID_MAXIM, MAX2820_SYNTH,
+ MAX2820_SYNTH_R_44MHZ)) != 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.
+ */
+ if ((rc = rtw_rfbus_write(bus, RTW_RFCHIPID_MAXIM, MAX2820_RECEIVE,
+ MAX2820_RECEIVE_DL_DEFAULT |
+ LSHIFT(4, MAX2820A_RECEIVE_1C_MASK) |
+ LSHIFT(1, MAX2820A_RECEIVE_2C_MASK))) != 0)
+ return rc;
+
+ return rtw_rfbus_write(bus, RTW_RFCHIPID_MAXIM, MAX2820_TRANSMIT,
+ MAX2820_TRANSMIT_PA_DEFAULT);
+}
+
+int
+rtw_max2820_txpower(struct rtw_rf *rf, u_int8_t opaque_txpower)
+{
+ /* TBD */
+ return 0;
+}
+
+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_NOWAIT);
+ if (mx == NULL)
+ return NULL;
+ bzero(mx, sizeof(struct rtw_max2820));
+
+ 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, u_int8_t opaque_txpower,
+ u_int8_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;
+ if ((rc = rtw_bbp_preinit(regs, rf->rf_bbpset.bb_antatten, dflantb,
+ freq)) != 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/ic/rtwphy.h b/sys/dev/ic/rtwphy.h
new file mode 100644
index 00000000000..01d759f7f7d
--- /dev/null
+++ b/sys/dev/ic/rtwphy.h
@@ -0,0 +1,10 @@
+#ifndef _DEV_IC_RTWPHY_H
+#define _DEV_IC_RTWPHY_H
+
+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 *, u_int8_t, u_int8_t, u_int,
+ int, int, enum rtw_pwrstate);
+
+#endif /* _DEV_IC_RTWPHY_H */
diff --git a/sys/dev/ic/rtwphyio.c b/sys/dev/ic/rtwphyio.c
new file mode 100644
index 00000000000..0b4cf15dbc4
--- /dev/null
+++ b/sys/dev/ic/rtwphyio.c
@@ -0,0 +1,363 @@
+/* $OpenBSD: rtwphyio.c,v 1.1 2004/12/29 01:02:31 jsg Exp $ */
+/* $NetBSD: rtwphyio.c,v 1.4 2004/12/25 06:58:37 dyoung 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.
+ */
+/*
+ * Control input/output with the Philips SA2400 RF front-end and
+ * the baseband processor built into the Realtek RTL8180.
+ */
+
+#include <sys/cdefs.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/systm.h>
+#include <sys/types.h>
+
+#include <machine/bus.h>
+
+#include <net/if.h>
+#include <net/if_media.h>
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+
+#include <net80211/ieee80211_var.h>
+#include <net80211/ieee80211_compat.h>
+#include <net80211/ieee80211_radiotap.h>
+
+#include <dev/ic/rtwreg.h>
+#include <dev/ic/max2820reg.h>
+#include <dev/ic/sa2400reg.h>
+#include <dev/ic/si4136reg.h>
+#include <dev/ic/rtwvar.h>
+#include <dev/ic/rtwphyio.h>
+#include <dev/ic/rtwphy.h>
+
+u_int32_t rtw_grf5101_host_crypt(u_int, u_int32_t);
+u_int32_t rtw_maxim_swizzle(u_int, uint32_t);
+u_int32_t rtw_grf5101_mac_crypt(u_int, u_int32_t);
+
+static int rtw_macbangbits_timeout = 100;
+
+u_int8_t
+rtw_bbp_read(struct rtw_regs *regs, u_int addr)
+{
+ KASSERT((addr & ~PRESHIFT(RTW_BB_ADDR_MASK)) == 0);
+ RTW_WRITE(regs, RTW_BB,
+ LSHIFT(addr, RTW_BB_ADDR_MASK) | RTW_BB_RD_MASK | RTW_BB_WR_MASK);
+ delay(10); /* XXX */
+ RTW_WBR(regs, RTW_BB, RTW_BB);
+ return MASK_AND_RSHIFT(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;
+ u_int32_t wrbbp, rdbbp;
+
+ RTW_DPRINTF(RTW_DEBUG_PHYIO,
+ ("%s: bbp[%u] <- %u\n", __func__, addr, val));
+
+ KASSERT((addr & ~PRESHIFT(RTW_BB_ADDR_MASK)) == 0);
+ KASSERT((val & ~PRESHIFT(RTW_BB_WR_MASK)) == 0);
+
+ wrbbp = LSHIFT(addr, RTW_BB_ADDR_MASK) | RTW_BB_WREN |
+ LSHIFT(val, RTW_BB_WR_MASK) | RTW_BB_RD_MASK,
+
+ rdbbp = LSHIFT(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 (MASK_AND_RSHIFT(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, u_int32_t bits, int lo_to_hi,
+ u_int nbits)
+{
+ int i;
+ u_int32_t mask, reg;
+
+ KASSERT(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;
+ KASSERT((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, u_int32_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;
+}
+
+u_int32_t
+rtw_grf5101_host_crypt(u_int addr, u_int32_t val)
+{
+ /* TBD */
+ return 0;
+}
+
+u_int32_t
+rtw_grf5101_mac_crypt(u_int addr, u_int32_t val)
+{
+ u_int32_t data_and_addr;
+#define EXTRACT_NIBBLE(d, which) (((d) >> (4 * (which))) & 0xf)
+ static u_int8_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 LSHIFT(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, u_int32_t val)
+{
+ u_int nbits;
+ int lo_to_hi;
+ u_int32_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 = LSHIFT(val, MAX2820_TWI_DATA_MASK) |
+ LSHIFT(addr, MAX2820_TWI_ADDR_MASK);
+ break;
+ case RTW_RFCHIPID_PHILIPS:
+ KASSERT((addr & ~PRESHIFT(SA2400_TWI_ADDR_MASK)) == 0);
+ KASSERT((val & ~PRESHIFT(SA2400_TWI_DATA_MASK)) == 0);
+ bits = LSHIFT(val, SA2400_TWI_DATA_MASK) |
+ LSHIFT(addr, SA2400_TWI_ADDR_MASK) | SA2400_TWI_WREN;
+ nbits = 32;
+ lo_to_hi = 1;
+ break;
+ case RTW_RFCHIPID_GCT:
+ case RTW_RFCHIPID_RFMD:
+ KASSERT((addr & ~PRESHIFT(SI4126_TWI_ADDR_MASK)) == 0);
+ KASSERT((val & ~PRESHIFT(SI4126_TWI_DATA_MASK)) == 0);
+ if (rfchipid == RTW_RFCHIPID_GCT)
+ bits = rtw_grf5101_host_crypt(addr, val);
+ else {
+ bits = LSHIFT(val, SI4126_TWI_DATA_MASK) |
+ LSHIFT(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;
+}
+
+u_int32_t
+rtw_maxim_swizzle(u_int addr, u_int32_t val)
+{
+ u_int32_t hidata, lodata;
+
+ KASSERT((val & ~(RTW_MAXIM_LODATA_MASK|RTW_MAXIM_HIDATA_MASK)) == 0);
+ lodata = MASK_AND_RSHIFT(val, RTW_MAXIM_LODATA_MASK);
+ hidata = MASK_AND_RSHIFT(val, RTW_MAXIM_HIDATA_MASK);
+ return LSHIFT(lodata, RTW_PHYCFG_MAC_MAXIM_LODATA_MASK) |
+ LSHIFT(hidata, RTW_PHYCFG_MAC_MAXIM_HIDATA_MASK) |
+ LSHIFT(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, u_int32_t val)
+{
+ u_int32_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:
+ KASSERT(
+ (addr & ~PRESHIFT(RTW_PHYCFG_MAC_PHILIPS_ADDR_MASK)) == 0);
+ KASSERT(
+ (val & ~PRESHIFT(RTW_PHYCFG_MAC_PHILIPS_DATA_MASK)) == 0);
+
+ reg = LSHIFT(addr, RTW_PHYCFG_MAC_PHILIPS_ADDR_MASK) |
+ LSHIFT(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/ic/rtwphyio.h b/sys/dev/ic/rtwphyio.h
new file mode 100644
index 00000000000..90c44c072ff
--- /dev/null
+++ b/sys/dev/ic/rtwphyio.h
@@ -0,0 +1,41 @@
+/* $OpenBSD: rtwphyio.h,v 1.1 2004/12/29 01:02:31 jsg Exp $ */
+/* $NetBSD: rtwphyio.h,v 1.1 2004/09/26 02:29:15 dyoung 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.
+ */
+#ifndef _DEV_IC_RTWPHYIO_H
+#define _DEV_IC_RTWPHYIO_H
+
+int rtw_rf_hostwrite(struct rtw_regs *, enum rtw_rfchipid, u_int, u_int32_t);
+int rtw_rf_macwrite(struct rtw_regs *, enum rtw_rfchipid, u_int, u_int32_t);
+u_int8_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/ic/rtwreg.h b/sys/dev/ic/rtwreg.h
new file mode 100644
index 00000000000..45144dc73ab
--- /dev/null
+++ b/sys/dev/ic/rtwreg.h
@@ -0,0 +1,1100 @@
+/* $OpenBSD: rtwreg.h,v 1.1 2004/12/29 01:02:31 jsg Exp $ */
+/* $NetBSD: rtwreg.h,v 1.4 2004/12/21 09:07:23 dyoung Exp $ */
+
+/*
+ * Copyright (c) 2003 The NetBSD Foundation, Inc. All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * 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 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 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.
+ */
+
+/* Macros for bit twiddling. */
+/* TBD factor w/ dev/ic/atwreg.h. */
+
+#ifndef _BIT_TWIDDLE
+#define _BIT_TWIDDLE
+/* nth bit, BIT(0) == 0x1. */
+#define BIT(n) (((n) == 32) ? 0 : ((u_int32_t)1 << (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(x) ((((x) - 1) & (x)) ^ (x))
+
+/* for x a power of two and p a non-negative integer, is x a greater
+ * power than 2**p?
+ */
+#define GTEQ_POWER(x, p) (((u_long)(x) >> (p)) != 0)
+
+#define MASK_TO_SHIFT2(m) (GTEQ_POWER(LOWEST_SET_BIT((m)), 1) ? 1 : 0)
+
+#define MASK_TO_SHIFT4(m) \
+ (GTEQ_POWER(LOWEST_SET_BIT((m)), 2) \
+ ? 2 + MASK_TO_SHIFT2((m) >> 2) \
+ : MASK_TO_SHIFT2((m)))
+
+#define MASK_TO_SHIFT8(m) \
+ (GTEQ_POWER(LOWEST_SET_BIT((m)), 4) \
+ ? 4 + MASK_TO_SHIFT4((m) >> 4) \
+ : MASK_TO_SHIFT4((m)))
+
+#define MASK_TO_SHIFT16(m) \
+ (GTEQ_POWER(LOWEST_SET_BIT((m)), 8) \
+ ? 8 + MASK_TO_SHIFT8((m) >> 8) \
+ : MASK_TO_SHIFT8((m)))
+
+#define MASK_TO_SHIFT(m) \
+ (GTEQ_POWER(LOWEST_SET_BIT((m)), 16) \
+ ? 16 + MASK_TO_SHIFT16((m) >> 16) \
+ : MASK_TO_SHIFT16((m)))
+
+#define MASK_AND_RSHIFT(x, mask) (((x) & (mask)) >> MASK_TO_SHIFT(mask))
+#define LSHIFT(x, mask) ((x) << MASK_TO_SHIFT(mask))
+#define MASK_AND_REPLACE(reg, val, mask) ((reg & ~mask) | LSHIFT(val, mask))
+#define PRESHIFT(m) MASK_AND_RSHIFT((m), (m))
+
+#endif /* _BIT_TWIDDLE */
+
+/* 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 LSHIFT(0, RTW_BRSR_MBR8180_MASK)
+#define RTW_BRSR_MBR8180_2MBPS LSHIFT(1, RTW_BRSR_MBR8180_MASK)
+#define RTW_BRSR_MBR8180_5MBPS LSHIFT(2, RTW_BRSR_MBR8180_MASK)
+#define RTW_BRSR_MBR8180_11MBPS LSHIFT(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 Overflow */
+#define RTW_INTR_TIMEOUT BIT(14) /* Time Out: 1 indicates
+ * RTW_TSFTR[0:31] = RTW_TINT
+ */
+#define RTW_INTR_BCNINT BIT(13) /* Beacon Time Out: time for host to
+ * prepare beacon:
+ * RTW_TSFTR % (RTW_BCNITV_BCNITV * TU) =
+ * (RTW_BCNITV_BCNITV * TU - RTW_BINTRITV)
+ */
+#define RTW_INTR_ATIMINT BIT(12)
+ /* ATIM Time Out: ATIM interval will pass,
+ * RTW_TSFTR % (RTW_BCNITV_BCNITV * TU) =
+ * (RTW_ATIMWND_ATIMWND * TU - RTW_ATIMTRITV)
+ */
+#define RTW_INTR_TBDER BIT(11) /* Tx Beacon Descriptor Error:
+ * beacon transmission aborted because
+ * frame Rx'd
+ */
+#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)
+#define RTW_INTR_BEACON (RTW_INTR_TBDER|RTW_INTR_TBDOK|RTW_INTR_BCNINT)
+#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 LSHIFT(26, RTW_TCR_HWVERID_MASK)
+#define RTW_TCR_HWVERID_F LSHIFT(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 LSHIFT(0, RTW_TCR_MXDMA_MASK)
+#define RTW_TCR_MXDMA_32 LSHIFT(1, RTW_TCR_MXDMA_MASK)
+#define RTW_TCR_MXDMA_64 LSHIFT(2, RTW_TCR_MXDMA_MASK)
+#define RTW_TCR_MXDMA_128 LSHIFT(3, RTW_TCR_MXDMA_MASK)
+#define RTW_TCR_MXDMA_256 LSHIFT(4, RTW_TCR_MXDMA_MASK)
+#define RTW_TCR_MXDMA_512 LSHIFT(5, RTW_TCR_MXDMA_MASK)
+#define RTW_TCR_MXDMA_1024 LSHIFT(6, RTW_TCR_MXDMA_MASK)
+#define RTW_TCR_MXDMA_2048 LSHIFT(7, RTW_TCR_MXDMA_MASK)
+
+#define RTW_TCR_DISCW BIT(20) /* disable 802.11 random backoff */
+
+#define RTW_TCR_ICV BIT(19) /* host lets RTL8180 append ICV to
+ * WEP packets
+ */
+
+/* Loopback Test: disables TXI/TXQ outputs. */
+#define RTW_TCR_LBK_MASK BITS(18,17)
+#define RTW_TCR_LBK_NORMAL LSHIFT(0, RTW_TCR_LBK_MASK) /* normal ops */
+#define RTW_TCR_LBK_MAC LSHIFT(1, RTW_TCR_LBK_MASK) /* MAC loopback */
+#define RTW_TCR_LBK_BBP LSHIFT(2, RTW_TCR_LBK_MASK) /* baseband loop. */
+#define RTW_TCR_LBK_CONT LSHIFT(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 */
+#define RTW_RCR_ONLYERLPKT BIT(31) /* only do Early Rx on packets
+ * longer than 1536 bytes
+ */
+#define RTW_RCR_ENCS2 BIT(30) /* enable carrier sense method 2 */
+#define RTW_RCR_ENCS1 BIT(29) /* enable carrier sense method 1 */
+#define RTW_RCR_ENMARP BIT(28) /* enable MAC auto-reset PHY */
+#define RTW_RCR_CBSSID BIT(23) /* 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_APWRMGT BIT(22) /* accept packets w/ PWRMGMT bit set */
+#define RTW_RCR_ADD3 BIT(21) /* when RTW_MSR_NETYPE ==
+ * RTW_MSR_NETYPE_INFRA_OK, accept
+ * broadcast/multicast packets whose
+ * 3rd address matches RTL8180's MAC.
+ */
+#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 LSHIFT(2, RTW_RCR_RXFTH_MASK)
+#define RTW_RCR_RXFTH_128 LSHIFT(3, RTW_RCR_RXFTH_MASK)
+#define RTW_RCR_RXFTH_256 LSHIFT(4, RTW_RCR_RXFTH_MASK)
+#define RTW_RCR_RXFTH_512 LSHIFT(5, RTW_RCR_RXFTH_MASK)
+#define RTW_RCR_RXFTH_1024 LSHIFT(6, RTW_RCR_RXFTH_MASK)
+#define RTW_RCR_RXFTH_WHOLE LSHIFT(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 LSHIFT(0, RTW_RCR_MXDMA_MASK)
+#define RTW_RCR_MXDMA_32 LSHIFT(1, RTW_RCR_MXDMA_MASK)
+#define RTW_RCR_MXDMA_64 LSHIFT(2, RTW_RCR_MXDMA_MASK)
+#define RTW_RCR_MXDMA_128 LSHIFT(3, RTW_RCR_MXDMA_MASK)
+#define RTW_RCR_MXDMA_256 LSHIFT(4, RTW_RCR_MXDMA_MASK)
+#define RTW_RCR_MXDMA_512 LSHIFT(5, RTW_RCR_MXDMA_MASK)
+#define RTW_RCR_MXDMA_1024 LSHIFT(6, RTW_RCR_MXDMA_MASK)
+#define RTW_RCR_MXDMA_UNLIMITED LSHIFT(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 */
+
+#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 LSHIFT(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 LSHIFT(1, RTW_9346CR_EEM_MASK)
+/* Disable network & bus-master operations and enable
+ * _EECS, _EESK, _EEDI, _EEDO.
+ * XXX RTL8180 only?
+ */
+#define RTW_9346CR_EEM_PROGRAM LSHIFT(2, RTW_9346CR_EEM_MASK)
+/* Enable RTW_CONFIG[0123] registers. */
+#define RTW_9346CR_EEM_CONFIG LSHIFT(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 LSHIFT(3, RTW_CONFIG0_GL_MASK)
+#define RTW_CONFIG0_GL_EUROPE LSHIFT(2, RTW_CONFIG0_GL_MASK)
+#define RTW_CONFIG0_GL_JAPAN LSHIFT(1, RTW_CONFIG0_GL_MASK)
+#define RTW_CONFIG0_GL_USA LSHIFT(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 LSHIFT(0, RTW_CONFIG1_LEDS_MASK)
+#define RTW_CONFIG1_LEDS_ACT_LINK LSHIFT(1, RTW_CONFIG1_LEDS_MASK)
+#define RTW_CONFIG1_LEDS_TX_RX LSHIFT(2, RTW_CONFIG1_LEDS_MASK)
+#define RTW_CONFIG1_LEDS_LINKACT_INFRA LSHIFT(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 LSHIFT(0x8, RTW_ANAPARM_RFPOW1_MASK)
+
+/* reg[RTW_ANAPARM] |= RTW_ANAPARM_TXDACOFF; */
+#define RTW_ANAPARM_RFPOW_MAXIM_SLEEP LSHIFT(0x378, RTW_ANAPARM_RFPOW1_MASK)
+
+/* reg[RTW_ANAPARM] |= RTW_ANAPARM_TXDACOFF; */
+#define RTW_ANAPARM_RFPOW_MAXIM_OFF LSHIFT(0x379, RTW_ANAPARM_RFPOW1_MASK)
+
+/*
+ * RFMD On/Sleep/Off control
+ */
+#define RTW_ANAPARM_RFPOW_RFMD_ON LSHIFT(0x408, RTW_ANAPARM_RFPOW1_MASK)
+
+/* reg[RTW_ANAPARM] |= RTW_ANAPARM_TXDACOFF; */
+#define RTW_ANAPARM_RFPOW_RFMD_SLEEP LSHIFT(0x378, RTW_ANAPARM_RFPOW1_MASK)
+
+/* reg[RTW_ANAPARM] |= RTW_ANAPARM_TXDACOFF; */
+#define RTW_ANAPARM_RFPOW_RFMD_OFF LSHIFT(0x379, RTW_ANAPARM_RFPOW1_MASK)
+
+/*
+ * Philips On/Sleep/Off control
+ */
+#define RTW_ANAPARM_RFPOW_ANA_PHILIPS_ON \
+ LSHIFT(0x328, RTW_ANAPARM_RFPOW1_MASK)
+#define RTW_ANAPARM_RFPOW_DIG_PHILIPS_ON \
+ LSHIFT(0x008, RTW_ANAPARM_RFPOW1_MASK)
+
+/* reg[RTW_ANAPARM] |= RTW_ANAPARM_TXDACOFF; */
+#define RTW_ANAPARM_RFPOW_PHILIPS_SLEEP\
+ LSHIFT(0x378, RTW_ANAPARM_RFPOW1_MASK)
+
+/* reg[RTW_ANAPARM] |= RTW_ANAPARM_TXDACOFF; */
+#define RTW_ANAPARM_RFPOW_PHILIPS_OFF\
+ LSHIFT(0x379, RTW_ANAPARM_RFPOW1_MASK)
+
+#define RTW_ANAPARM_RFPOW_PHILIPS_ON LSHIFT(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 LSHIFT(3, RTW_MSR_NETYPE_MASK)
+/* infrastructure link ok */
+#define RTW_MSR_NETYPE_INFRA_OK LSHIFT(2, RTW_MSR_NETYPE_MASK)
+/* ad-hoc link ok */
+#define RTW_MSR_NETYPE_ADHOC_OK LSHIFT(1, RTW_MSR_NETYPE_MASK)
+/* no link */
+#define RTW_MSR_NETYPE_NOLINK LSHIFT(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 LSHIFT(1, RTW_CONFIG4_RFTYPE_MASK)
+#define RTW_CONFIG4_RFTYPE_RFMD LSHIFT(2, RTW_CONFIG4_RFTYPE_MASK)
+#define RTW_CONFIG4_RFTYPE_PHILIPS LSHIFT(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 LSHIFT(1, RTW_SCR_KM_MASK)
+#define RTW_SCR_KM_WEP40 LSHIFT(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 LSHIFT(0, RTW_PHYCFG_MAC_RFTYPE_MASK)
+#define RTW_PHYCFG_MAC_RFTYPE_RFMD LSHIFT(1, RTW_PHYCFG_MAC_RFTYPE_MASK)
+#define RTW_PHYCFG_MAC_RFTYPE_GCT RTW_PHYCFG_MAC_RFTYPE_RFMD
+#define RTW_PHYCFG_MAC_RFTYPE_PHILIPS LSHIFT(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(2) /* 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(2) /* Host writes 1 to tell RTL8180 to
+ * stop low-priority DMA.
+ */
+#define RTW_TPPOLL_FSWINT BIT(0) /* Force software interrupt. From
+ * reference driver.
+ */
+
+
+#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 {
+ u_int32_t htx_ctl0;
+ u_int32_t htx_ctl1;
+ u_int32_t htx_buf;
+ u_int32_t htx_len;
+ u_int32_t htx_next;
+ u_int32_t htx_rsvd[3];
+};
+
+#define htx_stat htx_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 LSHIFT(0, RTW_TXCTL0_RATE_MASK)
+#define RTW_TXCTL0_RATE_2MBPS LSHIFT(1, RTW_TXCTL0_RATE_MASK)
+#define RTW_TXCTL0_RATE_5MBPS LSHIFT(2, RTW_TXCTL0_RATE_MASK)
+#define RTW_TXCTL0_RATE_11MBPS LSHIFT(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 LSHIFT(0, RTW_TXCTL0_RTSRATE_MASK)
+#define RTW_TXCTL0_RTSRATE_2MBPS LSHIFT(1, RTW_TXCTL0_RTSRATE_MASK)
+#define RTW_TXCTL0_RTSRATE_5MBPS LSHIFT(2, RTW_TXCTL0_RTSRATE_MASK)
+#define RTW_TXCTL0_RTSRATE_11MBPS LSHIFT(3, RTW_TXCTL0_RTSRATE_MASK)
+
+#define RTW_TXCTL0_BEACON BIT(18) /* packet is a beacon */
+#define RTW_TXCTL0_MOREFRAG BIT(17) /* another fragment follows */
+#define RTW_TXCTL0_SPLCP BIT(16) /* add short PLCP preamble
+ * and header
+ */
+#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 {
+ u_int32_t hrx_ctl;
+ u_int32_t hrx_rsvd0;
+ u_int32_t hrx_buf;
+ u_int32_t hrx_rsvd1;
+};
+
+#define hrx_stat hrx_ctl
+#define hrx_rssi hrx_rsvd0
+#define hrx_tsftl hrx_buf /* valid only when RTW_RXSTAT_LS is set */
+#define hrx_tsfth hrx_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 LSHIFT(0, RTW_RXSTAT_RATE_MASK)
+#define RTW_RXSTAT_RATE_2MBPS LSHIFT(1, RTW_RXSTAT_RATE_MASK)
+#define RTW_RXSTAT_RATE_5MBPS LSHIFT(2, RTW_RXSTAT_RATE_MASK)
+#define RTW_RXSTAT_RATE_11MBPS LSHIFT(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(32,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
+#define RTW_BBP_LNADET 0x04
+#define RTW_BBP_IFAGCINI 0x05
+#define RTW_BBP_IFAGCLIMIT 0x06
+#define RTW_BBP_IFAGCDET 0x07
+
+#define RTW_BBP_ANTATTEN 0x10
+#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
+#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
+#define RTW_BBP_CHSQLIM 0x1a
+
diff --git a/sys/dev/ic/rtwvar.h b/sys/dev/ic/rtwvar.h
new file mode 100644
index 00000000000..fb91518560a
--- /dev/null
+++ b/sys/dev/ic/rtwvar.h
@@ -0,0 +1,468 @@
+/* $OpenBSD: rtwvar.h,v 1.1 2004/12/29 01:02:31 jsg Exp $ */
+/* $NetBSD: rtwvar.h,v 1.10 2004/12/26 22:37:57 mycroft Exp $ */
+/*-
+ * 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.
+ */
+
+#ifndef _DEV_IC_RTWVAR_H_
+#define _DEV_IC_RTWVAR_H_
+
+#include <sys/queue.h>
+#include <sys/timeout.h>
+
+#ifdef RTW_DEBUG
+#define RTW_DEBUG_TUNE 0x000001
+#define RTW_DEBUG_PKTFILT 0x000002
+#define RTW_DEBUG_XMIT 0x000004
+#define RTW_DEBUG_XMIT_DESC 0x000008
+#define RTW_DEBUG_NODE 0x000010
+#define RTW_DEBUG_PWR 0x000020
+#define RTW_DEBUG_ATTACH 0x000040
+#define RTW_DEBUG_REGDUMP 0x000080
+#define RTW_DEBUG_ACCESS 0x000100
+#define RTW_DEBUG_RESET 0x000200
+#define RTW_DEBUG_INIT 0x000400
+#define RTW_DEBUG_IOSTATE 0x000800
+#define RTW_DEBUG_RECV 0x001000
+#define RTW_DEBUG_RECV_DESC 0x002000
+#define RTW_DEBUG_IO_KICK 0x004000
+#define RTW_DEBUG_INTR 0x008000
+#define RTW_DEBUG_PHY 0x010000
+#define RTW_DEBUG_PHYIO 0x020000
+#define RTW_DEBUG_PHYBITIO 0x040000
+#define RTW_DEBUG_TIMEOUT 0x080000
+#define RTW_DEBUG_BUGS 0x100000
+#define RTW_DEBUG_MAX 0x1fffff
+
+extern int rtw_debug;
+#define RTW_DPRINTF(__flags, __x) \
+ if ((rtw_debug & (__flags)) != 0) printf __x
+#define DPRINTF(__sc, __flags, __x) \
+ if (((__sc)->sc_ic.ic_if.if_flags & IFF_DEBUG) != 0) \
+ RTW_DPRINTF(__flags, __x)
+#else /* RTW_DEBUG */
+#define RTW_DPRINTF(__flags, __x)
+#define DPRINTF(__sc, __flags, __x)
+#endif /* RTW_DEBUG */
+
+#if 0
+enum rtw_rftype {
+ RTW_RFTYPE_INTERSIL = 0,
+ RTW_RFTYPE_RFMD,
+ RTW_RFTYPE_PHILIPS,
+ RTW_RFTYPE_MAXIM
+};
+#endif
+
+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 */
+ /* all PHY flags */
+#define RTW_F_ALLPHY (RTW_F_DIGPHY|RTW_F_DFLANTB|RTW_F_ANTDIV)
+
+struct rtw_regs {
+ bus_space_tag_t r_bt;
+ bus_space_handle_t r_bh;
+};
+
+#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 {
+ u_int16_t *sr_content;
+ u_int16_t sr_size;
+};
+
+struct rtw_rxctl {
+ struct mbuf *srx_mbuf;
+ bus_dmamap_t srx_dmamap;
+};
+
+struct rtw_txctl {
+ SIMPLEQ_ENTRY(rtw_txctl) stx_q;
+ struct mbuf *stx_mbuf;
+ bus_dmamap_t stx_dmamap;
+ struct ieee80211_node *stx_ni; /* destination node */
+ u_int stx_first; /* 1st hw descriptor */
+ u_int stx_last; /* last hw descriptor */
+ struct ieee80211_duration stx_d0;
+ struct ieee80211_duration stx_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 1 /* 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_txdesc_blk {
+ u_int htc_ndesc;
+ u_int htc_next;
+ u_int htc_nfree;
+ bus_addr_t htc_physbase;
+ bus_addr_t htc_ofs;
+ struct rtw_txdesc *htc_desc;
+};
+
+#define RTW_NEXT_IDX(__htc, __idx) (((__idx) + 1) % (__htc)->htc_ndesc)
+
+#define RTW_NEXT_DESC(__htc, __idx) \
+ ((__htc)->htc_physbase + \
+ sizeof(struct rtw_txdesc) * RTW_NEXT_IDX((__htc), (__idx)))
+
+SIMPLEQ_HEAD(rtw_txq, rtw_txctl);
+
+struct rtw_txctl_blk {
+ /* dirty/free s/w descriptors */
+ struct rtw_txq stc_dirtyq;
+ struct rtw_txq stc_freeq;
+ u_int stc_ndesc;
+ int stc_tx_timer;
+ struct rtw_txctl *stc_desc;
+};
+
+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_FLAGS) | (1 << IEEE80211_RADIOTAP_RATE) | \
+ (1 << IEEE80211_RADIOTAP_CHANNEL) | \
+ (1 << IEEE80211_RADIOTAP_DB_ANTSIGNAL))
+
+struct rtw_rx_radiotap_header {
+ struct ieee80211_radiotap_header rr_ihdr;
+ u_int8_t rr_flags;
+ u_int8_t rr_rate;
+ u_int16_t rr_chan_freq;
+ u_int16_t rr_chan_flags;
+ u_int8_t rr_antsignal;
+} __attribute__((__packed__));
+
+#define RTW_TX_RADIOTAP_PRESENT ((1 << IEEE80211_RADIOTAP_FLAGS) | \
+ (1 << IEEE80211_RADIOTAP_RATE) | \
+ (1 << IEEE80211_RADIOTAP_CHANNEL))
+
+struct rtw_tx_radiotap_header {
+ struct ieee80211_radiotap_header rt_ihdr;
+ u_int8_t rt_flags;
+ u_int8_t rt_rate;
+ u_int16_t rt_chan_freq;
+ u_int16_t rt_chan_flags;
+} __attribute__((__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, u_int32_t);
+ struct ieee80211_node *(*mt_node_alloc)(struct ieee80211com *);
+ void (*mt_node_free)(struct ieee80211com *,
+ 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, u_int8_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 *, u_int8_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, u_int8_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, u_int8_t opaque_txpower)
+{
+ return (*rf->rf_txpower)(rf, opaque_txpower);
+}
+
+typedef int (*rtw_rf_write_t)(struct rtw_regs *, enum rtw_rfchipid, u_int,
+ u_int32_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,
+ u_int32_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_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);
+
+enum rtw_access {RTW_ACCESS_NONE = 0,
+ RTW_ACCESS_CONFIG = 1,
+ RTW_ACCESS_ANAPARM = 2};
+
+struct rtw_softc {
+ struct device sc_dev;
+ struct ieee80211com sc_ic;
+ struct rtw_regs sc_regs;
+ bus_dma_tag_t sc_dmat;
+ u_int32_t sc_flags;
+
+#if 0
+ enum rtw_rftype sc_rftype;
+#endif
+ enum rtw_attach_state sc_attach_state;
+ enum rtw_rfchipid sc_rfchipid;
+ enum rtw_locale sc_locale;
+ u_int8_t sc_phydelay;
+
+ /* s/w Tx/Rx descriptors */
+ struct rtw_txctl_blk sc_txctl_blk[RTW_NTXPRI];
+ struct rtw_rxctl sc_rxctl[RTW_RXQLEN];
+ u_int sc_txq;
+ u_int sc_txnext;
+
+ struct rtw_txdesc_blk sc_txdesc_blk[RTW_NTXPRI];
+ struct rtw_rxdesc *sc_rxdesc;
+ u_int sc_rxnext;
+
+ struct rtw_descs *sc_descs;
+
+ bus_dma_segment_t sc_desc_segs;
+ int sc_desc_nsegs;
+ bus_dmamap_t sc_desc_dmamap;
+#define sc_desc_physaddr sc_desc_dmamap->dm_segs[0].ds_addr
+
+ struct rtw_srom sc_srom;
+
+ enum rtw_pwrstate sc_pwrstate;
+
+ rtw_pwrstate_t sc_pwrstate_cb;
+
+ struct rtw_rf *sc_rf;
+
+ u_int16_t sc_inten;
+
+ /* interrupt acknowledge hook */
+ void (*sc_intr_ack) __P((struct rtw_regs *));
+
+ int (*sc_enable)(struct rtw_softc *);
+ void (*sc_disable)(struct rtw_softc *);
+ void (*sc_power)(struct rtw_softc *, int);
+ struct rtw_mtbl sc_mtbl;
+ struct rtw_hooks sc_hooks;
+
+ caddr_t sc_radiobpf;
+
+ struct timeval sc_last_beacon;
+ struct timeout sc_scan_to;
+ u_int sc_cur_chan;
+
+ u_int32_t sc_tsfth; /* most significant TSFT bits */
+ u_int32_t sc_rcr; /* RTW_RCR */
+ u_int8_t sc_csthr; /* carrier-sense threshold */
+
+ int sc_do_tick; /* indicate 1s ticks */
+ struct timeval sc_tick0; /* first tick */
+
+ u_int8_t sc_rev; /* PCI/Cardbus revision */
+
+ u_int32_t sc_anaparm; /* register RTW_ANAPARM */
+
+ union {
+ struct rtw_rx_radiotap_header tap;
+ u_int8_t pad[64];
+ } sc_rxtapu;
+ union {
+ struct rtw_tx_radiotap_header tap;
+ u_int8_t pad[64];
+ } sc_txtapu;
+ enum rtw_access sc_access;
+};
+
+#define sc_if sc_ic.ic_if
+#define sc_rxtap sc_rxtapu.tap
+#define sc_txtap sc_txtapu.tap
+
+extern int rtw_host_rfio;
+
+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_softc *, enum rtw_access);
+
+void rtw_attach(struct rtw_softc *);
+int rtw_detach(struct rtw_softc *);
+int rtw_intr(void *);
+
+void rtw_disable(struct rtw_softc *);
+int rtw_enable(struct rtw_softc *);
+
+int rtw_activate(struct device *, enum devact);
+void rtw_power(int, void *);
+void rtw_shutdown(void *);
+
+const char *rtw_pwrstate_string(enum rtw_pwrstate);
+
+#endif /* _DEV_IC_RTWVAR_H_ */
diff --git a/sys/dev/ic/sa2400reg.h b/sys/dev/ic/sa2400reg.h
new file mode 100644
index 00000000000..5f382baceca
--- /dev/null
+++ b/sys/dev/ic/sa2400reg.h
@@ -0,0 +1,263 @@
+/* $OpenBSD: sa2400reg.h,v 1.1 2004/12/29 01:02:31 jsg Exp $ */
+/* $NetBSD: sa2400reg.h,v 1.2 2004/12/12 06:37:59 dyoung Exp $ */
+
+/*
+ * 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.
+ */
+
+#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 LSHIFT(0, SA2400_SYNB_L_MASK)
+#define SA2400_SYNB_L_INACTIVE1 LSHIFT(1, SA2400_SYNB_L_MASK)
+#define SA2400_SYNB_L_NORMAL LSHIFT(2, SA2400_SYNB_L_MASK)
+#define SA2400_SYNB_L_INACTIVE2 LSHIFT(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_ LSHIFT(0, SA2400_SYNC_CP_MASK)
+#define SA2400_SYNC_CP_THIRD_ LSHIFT(1, SA2400_SYNC_CP_MASK)
+#define SA2400_SYNC_CP_NORMAL LSHIFT(2, SA2400_SYNC_CP_MASK) /* recommended */
+#define SA2400_SYNC_CP_THIRD LSHIFT(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
+ */
+#define SA2400_OPMODE_ADC BIT(19) /* 1: in Rx mode, RSSI-ADC always on
+ * 0: RSSI-ADC only on during AGC
+ */
+#define SA2400_OPMODE_FTERR BIT(18) /* read-only filter tuner error:
+ * 1 if tuner out of range
+ */
+/* 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)
+
+#define SA2400_OPMODE_V2P5 BIT(14) /* external reference voltage
+ * (pad v2p5) on
+ */
+#define SA2400_OPMODE_I1M BIT(13) /* external reference current ... */
+#define SA2400_OPMODE_I0P3 BIT(12) /* external reference current ... */
+#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 LSHIFT(0, SA2400_OPMODE_MODE_MASK)
+#define SA2400_OPMODE_MODE_TXRX LSHIFT(1, SA2400_OPMODE_MODE_MASK)
+#define SA2400_OPMODE_MODE_WAIT LSHIFT(2, SA2400_OPMODE_MODE_MASK)
+#define SA2400_OPMODE_MODE_RXMGC LSHIFT(3, SA2400_OPMODE_MODE_MASK)
+#define SA2400_OPMODE_MODE_FCALIB LSHIFT(4, SA2400_OPMODE_MODE_MASK)
+#define SA2400_OPMODE_MODE_DCALIB LSHIFT(5, SA2400_OPMODE_MODE_MASK)
+#define SA2400_OPMODE_MODE_FASTTXRXMGC LSHIFT(6, SA2400_OPMODE_MODE_MASK)
+#define SA2400_OPMODE_MODE_RESET LSHIFT(7, SA2400_OPMODE_MODE_MASK)
+#define SA2400_OPMODE_MODE_VCOCALIB LSHIFT(8, SA2400_OPMODE_MODE_MASK)
+
+#define SA2400_OPMODE_DEFAULTS \
+ (SA2400_OPMODE_XO | SA2400_OPMODE_RXLV | SA2400_OPMODE_CLK | \
+ SA2400_OPMODE_I0P3 | LSHIFT(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/pci/files.pci b/sys/dev/pci/files.pci
index 8e9f2c0464d..38036a9377f 100644
--- a/sys/dev/pci/files.pci
+++ b/sys/dev/pci/files.pci
@@ -1,4 +1,4 @@
-# $OpenBSD: files.pci,v 1.168 2004/12/19 16:06:23 deraadt Exp $
+# $OpenBSD: files.pci,v 1.169 2004/12/29 01:02:30 jsg Exp $
# $NetBSD: files.pci,v 1.20 1996/09/24 17:47:15 christos Exp $
#
# Config file and device description for machine-independent PCI code.
@@ -519,6 +519,10 @@ file dev/pci/if_ath_pci.c ath_pci
attach atw at pci with atw_pci
file dev/pci/if_atw_pci.c atw_pci
+# Realtek RTL8180 PCI/Mini-PCI
+attach rtw at pci with rtw_pci
+file dev/pci/if_rtw_pci.c rtw_pci
+
# Sangoma PCI card
device san: ifnet, ifmedia
attach san at pci
diff --git a/sys/dev/pci/if_rtw_pci.c b/sys/dev/pci/if_rtw_pci.c
new file mode 100644
index 00000000000..b9b09bd9110
--- /dev/null
+++ b/sys/dev/pci/if_rtw_pci.c
@@ -0,0 +1,306 @@
+/* $OpenBSD: if_rtw_pci.c,v 1.1 2004/12/29 01:02:30 jsg Exp $ */
+/* $NetBSD: if_rtw_pci.c,v 1.1 2004/09/26 02:33:36 dyoung 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.
+ *
+ * Derived from the ADMtek ADM8211 PCI bus front-end.
+ *
+ * Derived from the ``Tulip'' PCI bus front-end.
+ */
+
+#include <sys/cdefs.h>
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/mbuf.h>
+#include <sys/malloc.h>
+#include <sys/kernel.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/errno.h>
+#include <sys/device.h>
+
+#include <machine/endian.h>
+
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/if_media.h>
+#ifdef INET
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+#endif
+
+#include <net80211/ieee80211_compat.h>
+#include <net80211/ieee80211_radiotap.h>
+#include <net80211/ieee80211_var.h>
+
+#include <machine/bus.h>
+#include <machine/intr.h>
+
+#include <dev/ic/rtwreg.h>
+#include <dev/ic/sa2400reg.h>
+#include <dev/ic/rtwvar.h>
+
+#include <dev/pci/pcivar.h>
+#include <dev/pci/pcireg.h>
+#include <dev/pci/pcidevs.h>
+
+const struct rtw_pci_product * rtw_pci_lookup(const struct pci_attach_args *);
+int rtw_pci_enable(struct rtw_softc *);
+void rtw_pci_disable(struct rtw_softc *);
+
+/*
+ * PCI configuration space registers used by the ADM8211.
+ */
+#define RTW_PCI_IOBA 0x10 /* i/o mapped base */
+#define RTW_PCI_MMBA 0x14 /* memory mapped base */
+
+struct rtw_pci_softc {
+ struct rtw_softc psc_rtw; /* real ADM8211 softc */
+
+ pci_intr_handle_t psc_ih; /* interrupt handle */
+ void *psc_intrcookie;
+
+ pci_chipset_tag_t psc_pc; /* our PCI chipset */
+ pcitag_t psc_pcitag; /* our PCI tag */
+};
+
+int rtw_pci_match(struct device *, void *, void *);
+void rtw_pci_attach(struct device *, struct device *, void *);
+
+struct cfattach rtw_pci_ca = {
+ sizeof (struct rtw_pci_softc), rtw_pci_match, rtw_pci_attach
+};
+struct cfdriver rtw_cd = {
+ 0, "rtw", DV_IFNET
+};
+
+const struct rtw_pci_product {
+ u_int32_t app_vendor; /* PCI vendor ID */
+ u_int32_t app_product; /* PCI product ID */
+} rtw_pci_products[] = {
+ { PCI_VENDOR_REALTEK, PCI_PRODUCT_REALTEK_RT8180 },
+ { PCI_VENDOR_BELKIN2, PCI_PRODUCT_BELKIN2_F5D6001 },
+
+ { 0, 0 },
+};
+
+const struct rtw_pci_product *
+rtw_pci_lookup(const struct pci_attach_args *pa)
+{
+ const struct rtw_pci_product *app;
+
+ for (app = rtw_pci_products;
+ app->app_vendor != 0 && app->app_product != 0;
+ app++) {
+ if (PCI_VENDOR(pa->pa_id) == app->app_vendor &&
+ PCI_PRODUCT(pa->pa_id) == app->app_product)
+ return (app);
+ }
+ return (NULL);
+}
+
+int
+rtw_pci_match(struct device *parent, void *match, void *aux)
+{
+ struct pci_attach_args *pa = aux;
+
+ if (rtw_pci_lookup(pa) != NULL)
+ return (1);
+
+ return (0);
+}
+
+int
+rtw_pci_enable(struct rtw_softc *sc)
+{
+ struct rtw_pci_softc *psc = (void *)sc;
+
+ /* Establish the interrupt. */
+ psc->psc_intrcookie = pci_intr_establish(psc->psc_pc, psc->psc_ih,
+ IPL_NET, rtw_intr, sc, sc->sc_dev.dv_xname);
+ if (psc->psc_intrcookie == NULL) {
+ printf("%s: unable to establish interrupt\n",
+ sc->sc_dev.dv_xname);
+ return (1);
+ }
+
+ return (0);
+}
+
+void
+rtw_pci_disable(struct rtw_softc *sc)
+{
+ struct rtw_pci_softc *psc = (void *)sc;
+
+ /* Unhook the interrupt handler. */
+ pci_intr_disestablish(psc->psc_pc, psc->psc_intrcookie);
+ psc->psc_intrcookie = NULL;
+}
+
+void
+rtw_pci_attach(struct device *parent, struct device *self, void *aux)
+{
+ struct rtw_pci_softc *psc = (void *) self;
+ struct rtw_softc *sc = &psc->psc_rtw;
+ struct rtw_regs *regs = &sc->sc_regs;
+ struct pci_attach_args *pa = aux;
+ pci_chipset_tag_t pc = pa->pa_pc;
+ const char *intrstr = NULL;
+ bus_space_tag_t iot, memt;
+ bus_space_handle_t ioh, memh;
+ int ioh_valid, memh_valid;
+ const struct rtw_pci_product *app;
+ pcireg_t reg;
+ int pmreg;
+
+ psc->psc_pc = pa->pa_pc;
+ psc->psc_pcitag = pa->pa_tag;
+
+ app = rtw_pci_lookup(pa);
+ if (app == NULL) {
+ printf("\n");
+ panic("rtw_pci_attach: impossible");
+ }
+
+ /*
+ * No power management hooks.
+ * XXX Maybe we should add some!
+ */
+ sc->sc_flags |= RTW_F_ENABLED;
+
+ /*
+ * Get revision info, and set some chip-specific variables.
+ */
+ sc->sc_rev = PCI_REVISION(pa->pa_class);
+
+ /*
+ * Check to see if the device is in power-save mode, and
+ * being it out if necessary.
+ *
+ * XXX This code comes almost verbatim from if_tlp_pci.c. I do
+ * not understand it. Tulip clears the "sleep mode" bit in the
+ * CFDA register, first. There is an equivalent (?) register at the
+ * same place in the ADM8211, but the docs do not assign its bits
+ * any meanings. -dcy
+ */
+ if (pci_get_capability(pc, pa->pa_tag, PCI_CAP_PWRMGMT, &pmreg, 0)) {
+ reg = pci_conf_read(pc, pa->pa_tag, pmreg + PCI_PMCSR);
+ switch (reg & PCI_PMCSR_STATE_MASK) {
+ case PCI_PMCSR_STATE_D1:
+ case PCI_PMCSR_STATE_D2:
+ printf(": waking up from power state D%d\n",
+ reg & PCI_PMCSR_STATE_MASK);
+ pci_conf_write(pc, pa->pa_tag, pmreg + PCI_PMCSR,
+ (reg & ~PCI_PMCSR_STATE_MASK) |
+ PCI_PMCSR_STATE_D0);
+ break;
+ case PCI_PMCSR_STATE_D3:
+ /*
+ * The card has lost all configuration data in
+ * this state, so punt.
+ */
+ printf(": unable to wake up from power state D3, "
+ "reboot required.\n");
+ pci_conf_write(pc, pa->pa_tag, pmreg + PCI_PMCSR,
+ (reg & ~PCI_PMCSR_STATE_MASK) |
+ PCI_PMCSR_STATE_D0);
+ return;
+ }
+ }
+
+ /*
+ * Map the device.
+ */
+ ioh_valid = (pci_mapreg_map(pa, RTW_PCI_IOBA,
+ PCI_MAPREG_TYPE_IO, 0,
+ &iot, &ioh, NULL, NULL, 0) == 0);
+ memh_valid = (pci_mapreg_map(pa, RTW_PCI_MMBA,
+ PCI_MAPREG_TYPE_MEM|PCI_MAPREG_MEM_TYPE_32BIT, 0,
+ &memt, &memh, NULL, NULL, 0) == 0);
+
+ if (memh_valid) {
+ regs->r_bt = memt;
+ regs->r_bh = memh;
+ } else if (ioh_valid) {
+ regs->r_bt = iot;
+ regs->r_bh = ioh;
+ } else {
+ printf(": unable to map device registers\n");
+ return;
+ }
+
+ sc->sc_dmat = pa->pa_dmat;
+
+ /*
+ * Make sure bus mastering is enabled.
+ */
+ pci_conf_write(pc, pa->pa_tag, PCI_COMMAND_STATUS_REG,
+ pci_conf_read(pc, pa->pa_tag, PCI_COMMAND_STATUS_REG) |
+ PCI_COMMAND_MASTER_ENABLE);
+
+ /*
+ * Map and establish our interrupt.
+ */
+ if (pci_intr_map(pa, &psc->psc_ih)) {
+ printf(": unable to map interrupt\n");
+ return;
+ }
+ intrstr = pci_intr_string(pc, psc->psc_ih);
+ psc->psc_intrcookie = pci_intr_establish(pc, psc->psc_ih, IPL_NET,
+ rtw_intr, sc, sc->sc_dev.dv_xname);
+ if (psc->psc_intrcookie == NULL) {
+ printf(": unable to establish interrupt");
+ if (intrstr != NULL)
+ printf(" at %s", intrstr);
+ printf("\n");
+ return;
+ }
+
+ printf(": %s\n", intrstr);
+
+ sc->sc_enable = rtw_pci_enable;
+ sc->sc_disable = rtw_pci_disable;
+
+ /*
+ * Finish off the attach.
+ */
+ rtw_attach(sc);
+}