diff options
| author | Warner Losh <imp@FreeBSD.org> | 2000-04-06 02:48:48 +0000 |
|---|---|---|
| committer | Warner Losh <imp@FreeBSD.org> | 2000-04-06 02:48:48 +0000 |
| commit | 0d5c223da470470cba1386f352cea432fd20e552 (patch) | |
| tree | eeef66c8d3d7862c2d75a6da954e1a9358c218f8 /sys/dev/awi | |
| parent | f092a22bbde929be5e69338b84f35e1600eeee68 (diff) | |
Notes
Diffstat (limited to 'sys/dev/awi')
| -rw-r--r-- | sys/dev/awi/README | 60 | ||||
| -rw-r--r-- | sys/dev/awi/am79c930.c | 451 | ||||
| -rw-r--r-- | sys/dev/awi/am79c930reg.h | 126 | ||||
| -rw-r--r-- | sys/dev/awi/am79c930var.h | 79 | ||||
| -rw-r--r-- | sys/dev/awi/awi.4 | 154 | ||||
| -rw-r--r-- | sys/dev/awi/awi.c | 2769 | ||||
| -rw-r--r-- | sys/dev/awi/awictl.h | 72 | ||||
| -rw-r--r-- | sys/dev/awi/awireg.h | 460 | ||||
| -rw-r--r-- | sys/dev/awi/awivar.h | 188 | ||||
| -rw-r--r-- | sys/dev/awi/if_awi_pccard.c | 245 | ||||
| -rw-r--r-- | sys/dev/awi/if_awi_pcmcia.c | 445 |
11 files changed, 5049 insertions, 0 deletions
diff --git a/sys/dev/awi/README b/sys/dev/awi/README new file mode 100644 index 0000000000000..38aaec59de927 --- /dev/null +++ b/sys/dev/awi/README @@ -0,0 +1,60 @@ +# $Id: README,v 1.19 2000/03/24 08:13:24 onoe Exp $ +# $FreeBSD$ + +===== Configuration + ifconfig awi0 link0 + changes to encapsulation from 802.2 LLC/SNAP (default) to + ether encapsulation. MELCO access point and PAO's ux driver + use this. + +===== Supported Cards + Any IEEE 802.11 cards use AMD Am79C930 and Harris (Intersil) Chipset + with PCnetMobile firmware by AMD. + BayStack 650 1Mbps Frequency Hopping PCCARD adapter + BayStack 660 2Mbps Direct Sequence PCCARD adapter + Icom SL-200 2Mbps Direct Sequence PCCARD adapter + Melco WLI-PCM 2Mbps Direct Sequence PCCARD adapter + NEL SSMagic 2Mbps Direct Sequence PCCARD adapter + Netwave AirSurfer Plus + 1Mbps Frequency Hopping PCCARD adapter + Netwave AirSurfer Pro + 2Mbps Direct Sequence PCCARD adapter + +===== Known Problems + WEP is not supported. + Does not create IBSS itself. + + Cannot configure at all on FreeBSD: + selection of infrastructure/adhoc mode + ESSID + ... + +===== How to add + +*** NetBSD current + already merged into current source tree. + + pcmcia: update the awi driver, which now supports AMD 79c930-based + 802.11DS cards as well as 802.11FH cards, and can operate + in infrastructure mode, adhoc mode, and wi(4) compatible + adhoc mode. [onoe 20000322] + +*** FreeBSD-current + make directory /sys/dev/awi and put all files there. + move if_ieee80211.h to /sys/net + add following lines to /sys/conf/files + dev/awi/am79c930.c optional awi + dev/awi/awi.c optional awi + dev/awi/if_awi_pccard.c optional awi card + add following line to config file + device awi0 + config and make kernel + + add description of your card to /etc/pccard.conf + ex. + card "AMD" "Am79C930" + config 0x1 "awi0" ? + card "Icom" "SL-200" + config 0x1 "awi0" ? + card "Bay Networks" "BayStack 650 Wireless LAN" + config 0x1 "awi0" ? diff --git a/sys/dev/awi/am79c930.c b/sys/dev/awi/am79c930.c new file mode 100644 index 0000000000000..e51f155b592fe --- /dev/null +++ b/sys/dev/awi/am79c930.c @@ -0,0 +1,451 @@ +/* $NetBSD: am79c930.c,v 1.5 2000/03/23 13:57:58 onoe Exp $ */ +/* $FreeBSD$ */ + +/*- + * Copyright (c) 1999 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Bill Sommerfeld + * + * 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. + */ + +/* + * Am79c930 chip driver. + * + * This is used by the awi driver to use the shared + * memory attached to the 79c930 to communicate with the firmware running + * in the 930's on-board 80188 core. + * + * The 79c930 can be mapped into just I/O space, or also have a + * memory mapping; the mapping must be set up by the bus front-end + * before am79c930_init is called. + */ + +/* + * operations: + * + * read_8, read_16, read_32, read_64, read_bytes + * write_8, write_16, write_32, write_64, write_bytes + * (two versions, depending on whether memory-space or i/o space is in use). + * + * interrupt E.C. + * start isr + * end isr + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#ifndef __FreeBSD__ +#include <sys/device.h> +#endif + +#include <machine/cpu.h> +#ifdef __FreeBSD__ +#include <machine/bus_pio.h> +#include <machine/bus_memio.h> +#endif +#include <machine/bus.h> +#ifdef __NetBSD__ +#include <machine/intr.h> +#endif + +#ifdef __NetBSD__ +#include <dev/ic/am79c930reg.h> +#include <dev/ic/am79c930var.h> +#endif +#ifdef __FreeBSD__ +#include <dev/awi/am79c930reg.h> +#include <dev/awi/am79c930var.h> +#endif + +#define AM930_DELAY(x) /*nothing*/ + +void am79c930_regdump __P((struct am79c930_softc *sc)); + +static void io_write_1 __P((struct am79c930_softc *, u_int32_t, u_int8_t)); +static void io_write_2 __P((struct am79c930_softc *, u_int32_t, u_int16_t)); +static void io_write_4 __P((struct am79c930_softc *, u_int32_t, u_int32_t)); +static void io_write_bytes __P((struct am79c930_softc *, u_int32_t, u_int8_t *, size_t)); + +static u_int8_t io_read_1 __P((struct am79c930_softc *, u_int32_t)); +static u_int16_t io_read_2 __P((struct am79c930_softc *, u_int32_t)); +static u_int32_t io_read_4 __P((struct am79c930_softc *, u_int32_t)); +static void io_read_bytes __P((struct am79c930_softc *, u_int32_t, u_int8_t *, size_t)); + +static void mem_write_1 __P((struct am79c930_softc *, u_int32_t, u_int8_t)); +static void mem_write_2 __P((struct am79c930_softc *, u_int32_t, u_int16_t)); +static void mem_write_4 __P((struct am79c930_softc *, u_int32_t, u_int32_t)); +static void mem_write_bytes __P((struct am79c930_softc *, u_int32_t, u_int8_t *, size_t)); + +static u_int8_t mem_read_1 __P((struct am79c930_softc *, u_int32_t)); +static u_int16_t mem_read_2 __P((struct am79c930_softc *, u_int32_t)); +static u_int32_t mem_read_4 __P((struct am79c930_softc *, u_int32_t)); +static void mem_read_bytes __P((struct am79c930_softc *, u_int32_t, u_int8_t *, size_t)); + +static struct am79c930_ops iospace_ops = { + io_write_1, + io_write_2, + io_write_4, + io_write_bytes, + io_read_1, + io_read_2, + io_read_4, + io_read_bytes +}; + +struct am79c930_ops memspace_ops = { + mem_write_1, + mem_write_2, + mem_write_4, + mem_write_bytes, + mem_read_1, + mem_read_2, + mem_read_4, + mem_read_bytes +}; + +static void io_write_1 (sc, off, val) + struct am79c930_softc *sc; + u_int32_t off; + u_int8_t val; +{ + AM930_DELAY(1); + bus_space_write_1(sc->sc_iot, sc->sc_ioh, AM79C930_LMA_HI, + ((off>>8)& 0x7f)); + AM930_DELAY(1); + bus_space_write_1(sc->sc_iot, sc->sc_ioh, AM79C930_LMA_LO, (off&0xff)); + AM930_DELAY(1); + bus_space_write_1(sc->sc_iot, sc->sc_ioh, AM79C930_IODPA, val); + AM930_DELAY(1); +} + +static void io_write_2 (sc, off, val) + struct am79c930_softc *sc; + u_int32_t off; + u_int16_t val; +{ + AM930_DELAY(1); + bus_space_write_1(sc->sc_iot, sc->sc_ioh, AM79C930_LMA_HI, + ((off>>8)& 0x7f)); + AM930_DELAY(1); + bus_space_write_1(sc->sc_iot,sc->sc_ioh,AM79C930_LMA_LO, (off&0xff)); + AM930_DELAY(1); + bus_space_write_1(sc->sc_iot,sc->sc_ioh,AM79C930_IODPA, val & 0xff); + AM930_DELAY(1); + bus_space_write_1(sc->sc_iot,sc->sc_ioh,AM79C930_IODPA, (val>>8)&0xff); + AM930_DELAY(1); +} + +static void io_write_4 (sc, off, val) + struct am79c930_softc *sc; + u_int32_t off; + u_int32_t val; +{ + AM930_DELAY(1); + bus_space_write_1(sc->sc_iot, sc->sc_ioh, AM79C930_LMA_HI, + ((off>>8)& 0x7f)); + AM930_DELAY(1); + bus_space_write_1(sc->sc_iot,sc->sc_ioh,AM79C930_LMA_LO, (off&0xff)); + AM930_DELAY(1); + bus_space_write_1(sc->sc_iot,sc->sc_ioh,AM79C930_IODPA,val & 0xff); + AM930_DELAY(1); + bus_space_write_1(sc->sc_iot,sc->sc_ioh,AM79C930_IODPA,(val>>8)&0xff); + AM930_DELAY(1); + bus_space_write_1(sc->sc_iot,sc->sc_ioh,AM79C930_IODPA,(val>>16)&0xff); + AM930_DELAY(1); + bus_space_write_1(sc->sc_iot,sc->sc_ioh,AM79C930_IODPA,(val>>24)&0xff); + AM930_DELAY(1); +} + +static void io_write_bytes (sc, off, ptr, len) + struct am79c930_softc *sc; + u_int32_t off; + u_int8_t *ptr; + size_t len; +{ + int i; + + AM930_DELAY(1); + bus_space_write_1(sc->sc_iot, sc->sc_ioh, AM79C930_LMA_HI, + ((off>>8)& 0x7f)); + AM930_DELAY(1); + bus_space_write_1(sc->sc_iot,sc->sc_ioh,AM79C930_LMA_LO, (off&0xff)); + AM930_DELAY(1); + for (i=0; i<len; i++) + bus_space_write_1(sc->sc_iot,sc->sc_ioh,AM79C930_IODPA,ptr[i]); +} + +static u_int8_t io_read_1 (sc, off) + struct am79c930_softc *sc; + u_int32_t off; +{ + u_int8_t val; + + bus_space_write_1(sc->sc_iot, sc->sc_ioh, AM79C930_LMA_HI, + ((off>>8)& 0x7f)); + AM930_DELAY(1); + bus_space_write_1(sc->sc_iot, sc->sc_ioh, AM79C930_LMA_LO, (off&0xff)); + AM930_DELAY(1); + val = bus_space_read_1(sc->sc_iot, sc->sc_ioh, AM79C930_IODPA); + AM930_DELAY(1); + return val; +} + +static u_int16_t io_read_2 (sc, off) + struct am79c930_softc *sc; + u_int32_t off; +{ + u_int16_t val; + + bus_space_write_1(sc->sc_iot, sc->sc_ioh, AM79C930_LMA_HI, + ((off>>8)& 0x7f)); + AM930_DELAY(1); + bus_space_write_1(sc->sc_iot, sc->sc_ioh, AM79C930_LMA_LO, (off&0xff)); + AM930_DELAY(1); + val = bus_space_read_1(sc->sc_iot, sc->sc_ioh, AM79C930_IODPA); + AM930_DELAY(1); + val |= bus_space_read_1(sc->sc_iot, sc->sc_ioh, AM79C930_IODPA) << 8; + AM930_DELAY(1); + return val; +} + +static u_int32_t io_read_4 (sc, off) + struct am79c930_softc *sc; + u_int32_t off; +{ + u_int32_t val; + + bus_space_write_1(sc->sc_iot, sc->sc_ioh, AM79C930_LMA_HI, + ((off>>8)& 0x7f)); + AM930_DELAY(1); + bus_space_write_1(sc->sc_iot, sc->sc_ioh, AM79C930_LMA_LO, (off&0xff)); + AM930_DELAY(1); + val = bus_space_read_1(sc->sc_iot, sc->sc_ioh, AM79C930_IODPA); + AM930_DELAY(1); + val |= bus_space_read_1(sc->sc_iot, sc->sc_ioh, AM79C930_IODPA) << 8; + AM930_DELAY(1); + val |= bus_space_read_1(sc->sc_iot, sc->sc_ioh, AM79C930_IODPA) << 16; + AM930_DELAY(1); + val |= bus_space_read_1(sc->sc_iot, sc->sc_ioh, AM79C930_IODPA) << 24; + AM930_DELAY(1); + return val; +} + +static void io_read_bytes (sc, off, ptr, len) + struct am79c930_softc *sc; + u_int32_t off; + u_int8_t *ptr; + size_t len; +{ + int i; + + bus_space_write_1(sc->sc_iot, sc->sc_ioh, AM79C930_LMA_HI, + ((off>>8)& 0x7f)); + AM930_DELAY(1); + bus_space_write_1(sc->sc_iot, sc->sc_ioh, AM79C930_LMA_LO, (off&0xff)); + AM930_DELAY(1); + for (i=0; i<len; i++) + ptr[i] = bus_space_read_1(sc->sc_iot, sc->sc_ioh, + AM79C930_IODPA); +} + +static void mem_write_1 (sc, off, val) + struct am79c930_softc *sc; + u_int32_t off; + u_int8_t val; +{ + bus_space_write_1(sc->sc_memt, sc->sc_memh, off, val); +} + +static void mem_write_2 (sc, off, val) + struct am79c930_softc *sc; + u_int32_t off; + u_int16_t val; +{ + bus_space_tag_t t = sc->sc_memt; + bus_space_handle_t h = sc->sc_memh; + + /* could be unaligned */ + if ((off & 0x1) == 0) + bus_space_write_2(t, h, off, val); + else { + bus_space_write_1(t, h, off, val & 0xff); + bus_space_write_1(t, h, off+1, (val >> 8) & 0xff); + } +} + +static void mem_write_4 (sc, off, val) + struct am79c930_softc *sc; + u_int32_t off; + u_int32_t val; +{ + bus_space_tag_t t = sc->sc_memt; + bus_space_handle_t h = sc->sc_memh; + + /* could be unaligned */ + if ((off & 0x3) == 0) + bus_space_write_4(t, h, off, val); + else { + bus_space_write_1(t, h, off, val & 0xff); + bus_space_write_1(t, h, off+1, (val >> 8) & 0xff); + bus_space_write_1(t, h, off+2, (val >> 16) & 0xff); + bus_space_write_1(t, h, off+3, (val >> 24) & 0xff); + } +} + +static void mem_write_bytes (sc, off, ptr, len) + struct am79c930_softc *sc; + u_int32_t off; + u_int8_t *ptr; + size_t len; +{ + bus_space_write_region_1 (sc->sc_memt, sc->sc_memh, off, ptr, len); +} + + +static u_int8_t mem_read_1 (sc, off) + struct am79c930_softc *sc; + u_int32_t off; +{ + return bus_space_read_1(sc->sc_memt, sc->sc_memh, off); +} + +static u_int16_t mem_read_2 (sc, off) + struct am79c930_softc *sc; + u_int32_t off; +{ + /* could be unaligned */ + if ((off & 0x1) == 0) + return bus_space_read_2(sc->sc_memt, sc->sc_memh, off); + else + return + bus_space_read_1(sc->sc_memt, sc->sc_memh, off ) | + (bus_space_read_1(sc->sc_memt, sc->sc_memh, off+1) << 8); +} + +static u_int32_t mem_read_4 (sc, off) + struct am79c930_softc *sc; + u_int32_t off; +{ + /* could be unaligned */ + if ((off & 0x3) == 0) + return bus_space_read_4(sc->sc_memt, sc->sc_memh, off); + else + return + bus_space_read_1(sc->sc_memt, sc->sc_memh, off ) | + (bus_space_read_1(sc->sc_memt, sc->sc_memh, off+1) << 8) | + (bus_space_read_1(sc->sc_memt, sc->sc_memh, off+2) <<16) | + (bus_space_read_1(sc->sc_memt, sc->sc_memh, off+3) <<24); +} + + + +static void mem_read_bytes (sc, off, ptr, len) + struct am79c930_softc *sc; + u_int32_t off; + u_int8_t *ptr; + size_t len; +{ + bus_space_read_region_1 (sc->sc_memt, sc->sc_memh, off, ptr, len); +} + + + + +/* + * Set bits in GCR. + */ + +void am79c930_gcr_setbits (sc, bits) + struct am79c930_softc *sc; + u_int8_t bits; +{ + u_int8_t gcr = bus_space_read_1 (sc->sc_iot, sc->sc_ioh, AM79C930_GCR); + + gcr |= bits; + + bus_space_write_1(sc->sc_iot, sc->sc_ioh, AM79C930_GCR, gcr); +} + +/* + * Clear bits in GCR. + */ + +void am79c930_gcr_clearbits (sc, bits) + struct am79c930_softc *sc; + u_int8_t bits; +{ + u_int8_t gcr = bus_space_read_1 (sc->sc_iot, sc->sc_ioh, AM79C930_GCR); + + gcr &= ~bits; + + bus_space_write_1(sc->sc_iot, sc->sc_ioh, AM79C930_GCR, gcr); +} + +u_int8_t am79c930_gcr_read (sc) + struct am79c930_softc *sc; +{ + return bus_space_read_1 (sc->sc_iot, sc->sc_ioh, AM79C930_GCR); +} + +#if 0 +void am79c930_regdump (sc) + struct am79c930_softc *sc; +{ + u_int8_t buf[8]; + int i; + + AM930_DELAY(5); + for (i=0; i<8; i++) { + buf[i] = bus_space_read_1 (sc->sc_iot, sc->sc_ioh, i); + AM930_DELAY(5); + } + printf("am79c930: regdump:"); + for (i=0; i<8; i++) { + printf(" %02x", buf[i]); + } + printf("\n"); +} +#endif + +void am79c930_chip_init (sc, how) + struct am79c930_softc *sc; +{ + /* zero the bank select register, and leave it that way.. */ + bus_space_write_1(sc->sc_iot, sc->sc_ioh, AM79C930_BSS, 0); + if (how) + sc->sc_ops = &memspace_ops; + else + sc->sc_ops = &iospace_ops; +} + + diff --git a/sys/dev/awi/am79c930reg.h b/sys/dev/awi/am79c930reg.h new file mode 100644 index 0000000000000..64b3e239fab1d --- /dev/null +++ b/sys/dev/awi/am79c930reg.h @@ -0,0 +1,126 @@ +/* $NetBSD: am79c930reg.h,v 1.3 2000/03/22 11:22:22 onoe Exp $ */ +/* $FreeBSD$ */ + +/*- + * Copyright (c) 1999 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Bill Sommerfeld + * + * 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. + */ + +/* + * Device register definitions gleaned from from the AMD "Am79C930 + * PCnet(tm)-Mobile Single Chip Wireless LAN Media Access Controller" + * data sheet, AMD Pub #20183, Rev B, amendment/0, issue date August 1997. + * + * As of 1999/10/23, this was available from AMD's web site in PDF + * form. + */ + + +/* + * The 79c930 contains a bus interface unit, a media access + * controller, and a tranceiver attachment interface. + * The MAC contains an 80188 CPU core. + * typical devices built around this chip typically add 32k or 64k of + * memory for buffers. + * + * The 80188 runs firmware which handles most of the 802.11 gorp, and + * communicates with the host using shared data structures in this + * memory; the specifics of the shared memory layout are not covered + * in this source file; see <dev/ic/am80211fw.h> for details of that layer. + */ + +/* + * Device Registers + */ + +#define AM79C930_IO_BASE 0 +#define AM79C930_IO_SIZE 16 +#define AM79C930_IO_SIZE_BIG 40 +#define AM79C930_IO_ALIGN 0x40 /* am79c930 decodes lower 6bits */ + + +#define AM79C930_GCR 0 /* General Config Register */ + +#define AM79C930_GCR_SWRESET 0x80 /* software reset */ +#define AM79C930_GCR_CORESET 0x40 /* core reset */ +#define AM79C930_GCR_DISPWDN 0x20 /* disable powerdown */ +#define AM79C930_GCR_ECWAIT 0x10 /* embedded controller wait */ +#define AM79C930_GCR_ECINT 0x08 /* interrupt from embedded ctrlr */ +#define AM79C930_GCR_INT2EC 0x04 /* interrupt to embedded ctrlr */ +#define AM79C930_GCR_ENECINT 0x02 /* enable interrupts from e.c. */ +#define AM79C930_GCR_DAM 0x01 /* direct access mode (read only) */ + +#define AM79C930_GCR_BITS "\020\1DAM\2ENECINT\3INT2EC\4ECINT\5ECWAIT\6DISPWDN\7CORESET\010SWRESET" + +#define AM79C930_BSS 1 /* Bank Switching Select register */ + +#define AM79C930_BSS_ECATR 0x80 /* E.C. ALE test read */ +#define AM79C930_BSS_FS 0x20 /* Flash Select */ +#define AM79C930_BSS_MBS 0x18 /* Memory Bank Select */ +#define AM79C930_BSS_EIOW 0x04 /* Expand I/O Window */ +#define AM79C930_BSS_TBS 0x03 /* TAI Bank Select */ + +#define AM79C930_LMA_LO 2 /* Local Memory Address register (low byte) */ + +#define AM79C930_LMA_HI 3 /* Local Memory Address register (high byte) */ + + /* set this bit to turn off ISAPnP version */ +#define AM79C930_LMA_HI_ISAPWRDWN 0x80 + +/* + * mmm, inconsistancy in chip documentation: + * According to page 79--80, all four of the following are equivalent + * and address the single byte pointed at by BSS_{FS,MBS} | LMA_{HI,LO} + * According to tables on p63 and p67, they're the LSB through MSB + * of a 32-bit word. + */ + +#define AM79C930_IODPA 4 /* I/O Data port A */ +#define AM79C930_IODPB 5 /* I/O Data port B */ +#define AM79C930_IODPC 6 /* I/O Data port C */ +#define AM79C930_IODPD 7 /* I/O Data port D */ + + +/* + * Tranceiver Attachment Interface Registers (TIR space) + * (omitted for now, since host access to them is for diagnostic + * purposes only). + */ + +/* + * memory space goo. + */ + +#define AM79C930_MEM_SIZE 0x8000 /* 32k */ +#define AM79C930_MEM_BASE 0x0 /* starting at 0 */ diff --git a/sys/dev/awi/am79c930var.h b/sys/dev/awi/am79c930var.h new file mode 100644 index 0000000000000..1e45c6817a9dd --- /dev/null +++ b/sys/dev/awi/am79c930var.h @@ -0,0 +1,79 @@ +/* $NetBSD$ */ +/* $FreeBSD$ */ + +/*- + * Copyright (c) 1999 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Bill Sommerfeld + * + * 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. + */ + +#define AM79C930_BUS_PCMCIA 1 +#define AM79C930_BUS_ISAPNP 2 /* not implemented */ + +struct am79c930_softc +{ + bus_space_tag_t sc_iot; + bus_space_handle_t sc_ioh; + + bus_space_tag_t sc_memt; + bus_space_handle_t sc_memh; + + struct am79c930_ops *sc_ops; + + int sc_bustype; +}; + +struct am79c930_ops +{ + void (*write_1) __P((struct am79c930_softc *, u_int32_t, u_int8_t)); + void (*write_2) __P((struct am79c930_softc *, u_int32_t, u_int16_t)); + void (*write_4) __P((struct am79c930_softc *, u_int32_t, u_int32_t)); + void (*write_bytes) __P((struct am79c930_softc *, u_int32_t, u_int8_t *, size_t)); + + u_int8_t (*read_1) __P((struct am79c930_softc *, u_int32_t)); + u_int16_t (*read_2) __P((struct am79c930_softc *, u_int32_t)); + u_int32_t (*read_4) __P((struct am79c930_softc *, u_int32_t)); + void (*read_bytes) __P((struct am79c930_softc *, u_int32_t, u_int8_t *, size_t)); +}; + +void am79c930_chip_init __P((struct am79c930_softc *sc, int)); + +void am79c930_gcr_setbits __P((struct am79c930_softc *sc, u_int8_t bits)); +void am79c930_gcr_clearbits __P((struct am79c930_softc *sc, u_int8_t bits)); + +u_int8_t am79c930_gcr_read __P((struct am79c930_softc *sc)); + +#define am79c930_hard_reset(sc) am79c930_gcr_setbits(sc, AM79C930_GCR_CORESET) +#define am79c930_hard_reset_off(sc) am79c930_gcr_clearbits(sc, AM79C930_GCR_CORESET) + + diff --git a/sys/dev/awi/awi.4 b/sys/dev/awi/awi.4 new file mode 100644 index 0000000000000..5afefa48a4b44 --- /dev/null +++ b/sys/dev/awi/awi.4 @@ -0,0 +1,154 @@ +.\" $NetBSD: awi.4,v 1.6 2000/03/22 11:24:33 onoe Exp $ +.\" $FreeBSD$ +.\" +.Dd October 31, 1999 +.Dt AWI 4 +.Os +.Sh NAME +.Nm awi +.Nd +AMD PCnetMobile IEEE 802.11 PCMCIA wireless network driver +.Sh SYNOPSIS +.Cd "awi* at pcmcia? function ?" +.Sh DESCRIPTION +.Pp +The +.Nm +driver supports various IEEE 802.11 wireless cards +which run AMD PCnetMobile firmware based on AMD 79c930 controller. +It provides access to 32kb of memory shared between the controller +and the host. +All host/device interaction is via this shared memory, which can be +accessed either via PCMCIA memory space or I/O space. +The +.Nm +driver encapsulates all IP and ARP traffic as 802.11 frames. +.Pp +The driver works both in infrastructure mode and in adhoc (independent +BSS) mode. +.Pp +In infrastructure mode, it communicates with an Access Point +which serves as a link-layer bridge between an Ethernet and +the wireless network. An access point also provides roaming capability +which allows wireless node to move between access points. +.Pp +In adhoc mode, it communicates peer to peer. +Though it is more efficient to communicate between wireless nodes, +the coverage is limited spatially due to lack of roaming capability. +.Pp +In addition to these two mode in IEEE 802.11 specification, the +.Nm +driver also supports a variant of adhoc mode out of spec, which makes +possible to communicate with adhoc mode of +.Xr wi 8 +driver. The NWID doesn't affect in this mode. +.Pp +For more information on configuring this device, see +.Xr ifconfig 8 +and +.Xr ifmedia 4 . +.Sh HARDWARE +Cards supported by the +.Nm +driver include: +.Pp +.Bl -tag -width BayStack_650x -offset indent +.It BayStack 650 +1Mbps Frequency Hopping PCCARD adapter +.It BayStack 660 +2Mbps Direct Sequence PCCARD adapter +.It Icom SL-200 +2Mbps Direct Sequence PCCARD adapter +.It Melco WLI-PCM +2Mbps Direct Sequence PCCARD adapter +.It NEL SSMagic +2Mbps Direct Sequence PCCARD adapter +.It Netwave AirSurfer Plus +1Mbps Frequency Hopping PCCARD adapter +.It Netwave AirSurfer Pro +2Mbps Direct Sequence PCCARD adapter +.El +.Pp +The original Xircom Netwave AirSurfer is supported by the +.Xr cnw 4 +driver. +.Sh MEDIA SELECTION +The DS cards support +.Em DS1 +and +.Em DS2 +media types, while the FH cards support +.Em FH1 +media type. For each media type, +.Em adhoc +mediaopt can be used to indicate the driver to operate in adhoc mode. +Also, +.Em adhoc,flag0 +mediaopt can be used for +.Xr wi 4 +compatible adhoc mode. +.Sh DIAGNOSTICS +.Bl -diag +.It "awi0: unable to allocate memory space; using i/o only" +Indicates that the driver was not able to allocate 32kb of PCMCIA bus +address space into which to map the device; the driver will use the +(slightly slower) i/o-space only access methods to read and write to +the shared memory. Since by default, NetBSD, only allocates 16kb of +address space per PCMCIA controller, this message is in some sense to +be expected and should not be a cause for alarm. +See +.Xr pcmcia 4 +for information on how to increase the memory available to the PCMCIA +controller. +.It "awi0: failed to complete selftest (%s)" +The device failed to complete its self test. +.It "awi0: synced with %s ssid %s at chanset %d" +.It "awi0: associated with %s ssid %s channel %d signal %d" +The device has successfully synchronized with the identified Access +Point and is ready to relay traffic. +.It "awi0: authentication failed (reason %d)" +.It "awi0: association failed (reason %d)" +The access point refuses the association request from the device. +.It "awi0: no recent beacons from %s; rescanning" +The device has not heard a beacon from its currently associated Access +Point recently, and is looking for a new access point. +.It "awi0: transmit timeout" +The device failed to generate an interrupt to acknowledge a +transmitted packet. +.It "awi0: failed to lock interrupt" +The system was unable to obtain the lock to access shared memory. +.It "awi0: command %d failed %x" +The device failed to complete the request from the system. +.El +.Sh BUGS +.Pp +Doesn't support WEP. +Doesn't create IBSS itself. +.Pp +.Sh SEE ALSO +.Xr arp 4 , +.Xr netintro 4 , +.Xr pcmcia 4 , +.Xr wi 4 , +.Xr cnw 4 , +.Xr ifmedia 4 , +.Xr ifconfig 8 +.Rs +.%T Am79C930 PCnet Mobile Single-Chip Wireless LAN Media Access Controller +.%O http://www.amd.com +.Re +.Sh HISTORY +The +.Nm +device driver first appeared in +.Nx 1.5 . +.Sh AUTHOR +The initial version of the +.Nm +driver was written by +.An Bill Sommerfeld Aq sommerfeld@netbsd.org . +Then the +.Nm +driver module completely rewritten to support cards with DS phy and +to support adhoc mode by +.An Atsushi Onoe Aq onoe@netbsd.org . diff --git a/sys/dev/awi/awi.c b/sys/dev/awi/awi.c new file mode 100644 index 0000000000000..62367a2c010c7 --- /dev/null +++ b/sys/dev/awi/awi.c @@ -0,0 +1,2769 @@ +/* $NetBSD: awi.c,v 1.12 2000/03/23 05:26:00 mycroft Exp $ */ +/* $FreeBSD$ */ + +/* + * Copyright (c) 2000 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Atsushi Onoe. + * + * 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. + */ + +/* + * Driver for AMD 802.11 PCnetMobile firmware. + * Uses am79c930 chip driver to talk to firmware running on the am79c930. + * + * The awi device driver first appeared in NetBSD 1.5. + * + * The initial version of the driver was written by + * Bill Sommerfeld <sommerfeld@netbsd.org>. + * Then the driver module completely rewritten to support cards with DS phy + * and to support adhoc mode by Atsushi Onoe <onoe@netbsd.org> + */ + +#ifdef __NetBSD__ +#include "opt_inet.h" +#include "opt_ns.h" +#include "bpfilter.h" +#include "rnd.h" +#endif +#ifdef __FreeBSD__ +#if __FreeBSD__ >= 3 +#include "opt_inet.h" +#endif +#if __FreeBSD__ >= 4 +#include "bpf.h" +#define NBPFILTER NBPF +#else +#include "bpfilter.h" +#endif +#endif + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/mbuf.h> +#include <sys/malloc.h> +#include <sys/proc.h> +#include <sys/socket.h> +#ifdef __FreeBSD__ +#include <sys/sockio.h> +#else +#include <sys/ioctl.h> +#endif +#include <sys/errno.h> +#include <sys/syslog.h> +#include <sys/select.h> +#if defined(__FreeBSD__) && __FreeBSD__ >= 4 +#include <sys/bus.h> +#else +#include <sys/device.h> +#endif +#if NRND > 0 +#include <sys/rnd.h> +#endif + +#include <net/if.h> +#include <net/if_dl.h> +#ifdef __FreeBSD__ +#include <net/ethernet.h> +#else +#include <net/if_ether.h> +#endif +#include <net/if_media.h> +#include <net/if_llc.h> +#include <net/if_ieee80211.h> + +#ifdef INET +#include <netinet/in.h> +#include <netinet/in_systm.h> +#include <netinet/in_var.h> +#include <netinet/ip.h> +#ifdef __NetBSD__ +#include <netinet/if_inarp.h> +#else +#include <netinet/if_ether.h> +#endif +#endif + +#ifdef NS +#include <netns/ns.h> +#include <netns/ns_if.h> +#endif + +#if NBPFILTER > 0 +#include <net/bpf.h> +#include <net/bpfdesc.h> +#endif + +#include <machine/cpu.h> +#include <machine/bus.h> +#ifdef __NetBSD__ +#include <machine/intr.h> +#endif +#ifdef __FreeBSD__ +#include <machine/clock.h> +#endif + +#ifdef __NetBSD__ +#include <dev/ic/am79c930reg.h> +#include <dev/ic/am79c930var.h> +#include <dev/ic/awireg.h> +#include <dev/ic/awivar.h> +#include <dev/ic/awictl.h> +#endif +#ifdef __FreeBSD__ +#include <dev/awi/am79c930reg.h> +#include <dev/awi/am79c930var.h> +#include <dev/awi/awireg.h> +#include <dev/awi/awivar.h> +#include <dev/awi/awictl.h> +#endif + +static int awi_ioctl __P((struct ifnet *ifp, u_long cmd, caddr_t data)); +#ifdef IFM_IEEE80211 +static int awi_media_rate2opt __P((struct awi_softc *sc, int rate)); +static int awi_media_opt2rate __P((struct awi_softc *sc, int opt)); +static int awi_media_change __P((struct ifnet *ifp)); +static void awi_media_status __P((struct ifnet *ifp, struct ifmediareq *imr)); +#endif +static int awi_drvget __P((struct ifnet *ifp, u_long cmd, caddr_t data)); +static int awi_drvset __P((struct ifnet *ifp, u_long cmd, caddr_t data)); +static int awi_init __P((struct awi_softc *sc)); +static void awi_stop __P((struct awi_softc *sc)); +static void awi_watchdog __P((struct ifnet *ifp)); +static void awi_start __P((struct ifnet *ifp)); +static void awi_txint __P((struct awi_softc *sc)); +static struct mbuf * awi_fix_txhdr __P((struct awi_softc *sc, struct mbuf *m0)); +static struct mbuf * awi_fix_rxhdr __P((struct awi_softc *sc, struct mbuf *m0)); +static void awi_input __P((struct awi_softc *sc, struct mbuf *m, u_int32_t rxts, u_int8_t rssi)); +static void awi_rxint __P((struct awi_softc *sc)); +struct mbuf * awi_devget __P((struct awi_softc *sc, u_int32_t off, u_int16_t len)); +static int awi_init_hw __P((struct awi_softc *sc)); +static int awi_init_mibs __P((struct awi_softc *sc)); +static int awi_init_txrx __P((struct awi_softc *sc)); +static void awi_stop_txrx __P((struct awi_softc *sc)); +static int awi_init_region __P((struct awi_softc *sc)); +static int awi_start_scan __P((struct awi_softc *sc)); +static int awi_next_scan __P((struct awi_softc *sc)); +static void awi_stop_scan __P((struct awi_softc *sc)); +static void awi_recv_beacon __P((struct awi_softc *sc, struct mbuf *m0, u_int32_t rxts, u_int8_t rssi)); +static int awi_set_ss __P((struct awi_softc *sc)); +static void awi_try_sync __P((struct awi_softc *sc)); +static void awi_sync_done __P((struct awi_softc *sc)); +static void awi_send_deauth __P((struct awi_softc *sc)); +static void awi_send_auth __P((struct awi_softc *sc)); +static void awi_recv_auth __P((struct awi_softc *sc, struct mbuf *m0)); +static void awi_send_asreq __P((struct awi_softc *sc, int reassoc)); +static void awi_recv_asresp __P((struct awi_softc *sc, struct mbuf *m0)); +static int awi_mib __P((struct awi_softc *sc, u_int8_t cmd, u_int8_t mib)); +static int awi_cmd_scan __P((struct awi_softc *sc)); +static int awi_cmd __P((struct awi_softc *sc, u_int8_t cmd)); +static void awi_cmd_done __P((struct awi_softc *sc)); +static int awi_next_txd __P((struct awi_softc *sc, int len, u_int32_t *framep, u_int32_t*ntxdp)); +static int awi_lock __P((struct awi_softc *sc)); +static void awi_unlock __P((struct awi_softc *sc)); +static int awi_intr_lock __P((struct awi_softc *sc)); +static void awi_intr_unlock __P((struct awi_softc *sc)); +static int awi_cmd_wait __P((struct awi_softc *sc)); + +#ifdef AWI_DEBUG +static void awi_dump_pkt __P((struct awi_softc *sc, struct mbuf *m, u_int8_t rssi)); +int awi_verbose = 0; +int awi_dump = 0; +#define AWI_DUMP_MASK(fc0) (1 << (((fc0) & IEEE80211_FC0_SUBTYPE_MASK) >> 4)) +int awi_dump_mask = AWI_DUMP_MASK(IEEE80211_FC0_SUBTYPE_BEACON); +int awi_dump_hdr = 0; +int awi_dump_len = 28; +#endif + +#if NBPFILTER > 0 +#define AWI_BPF_NORM 0 +#define AWI_BPF_RAW 1 +#ifdef __FreeBSD__ +#define AWI_BPF_MTAP(sc, m, raw) do { \ + if ((sc)->sc_ifp->if_bpf && (sc)->sc_rawbpf == (raw)) \ + bpf_mtap((sc)->sc_ifp, (m)); \ +} while (0); +#else +#define AWI_BPF_MTAP(sc, m, raw) do { \ + if ((sc)->sc_ifp->if_bpf && (sc)->sc_rawbpf == (raw)) \ + bpf_mtap((sc)->sc_ifp->if_bpf, (m)); \ +} while (0); +#endif +#else +#define AWI_BPF_MTAP(sc, m, raw) +#endif + +#ifndef llc_snap +#define llc_snap llc_un.type_snap +#endif + +#ifdef __FreeBSD__ +#if __FreeBSD__ < 4 +#define memset(p, v, n) bzero(p, n) /*XXX*/ +#endif + +#if __FreeBSD__ >= 4 +devclass_t awi_devclass; +#endif + +/* NetBSD compatible functions */ +static char * ether_sprintf __P((u_int8_t *)); + +static char * +ether_sprintf(enaddr) + u_int8_t *enaddr; +{ + static char strbuf[18]; + + sprintf(strbuf, "%6D", enaddr, ":"); + return strbuf; +} +#endif + +int +awi_attach(sc) + struct awi_softc *sc; +{ + struct ifnet *ifp = sc->sc_ifp; +#ifdef IFM_IEEE80211 + int i; + u_int8_t *phy_rates; + int mword; + struct ifmediareq imr; +#endif + int s; + int error; + + s = splnet(); + + /* + * Even if we can sleep in initialization state, + * all other processes (e.g. ifconfig) have to wait for + * completion of attaching interface. + */ + sc->sc_busy = 1; + sc->sc_status = AWI_ST_INIT; + TAILQ_INIT(&sc->sc_scan); + error = awi_init_hw(sc); + if (error) { + sc->sc_invalid = 1; + splx(s); + return error; + } + error = awi_init_mibs(sc); + splx(s); + if (error) { + sc->sc_invalid = 1; + return error; + } + + ifp->if_softc = sc; + ifp->if_start = awi_start; + ifp->if_ioctl = awi_ioctl; + ifp->if_watchdog = awi_watchdog; + ifp->if_mtu = ETHERMTU; + ifp->if_hdrlen = sizeof(struct ieee80211_frame) + + sizeof(struct ether_header); + ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; +#ifdef IFF_NOTRAILERS + ifp->if_flags |= IFF_NOTRAILERS; +#endif +#ifdef __NetBSD__ + memcpy(ifp->if_xname, sc->sc_dev.dv_xname, IFNAMSIZ); +#endif +#ifdef __FreeBSD__ + ifp->if_output = ether_output; + ifp->if_snd.ifq_maxlen = ifqmaxlen; + memcpy(sc->sc_ec.ac_enaddr, sc->sc_mib_addr.aMAC_Address, + ETHER_ADDR_LEN); +#endif + + printf("%s: IEEE802.11 (%s %dMbps) address %s\n", + sc->sc_dev.dv_xname, + sc->sc_mib_phy.IEEE_PHY_Type == AWI_PHY_TYPE_FH ? "FH" : "DS", + sc->sc_tx_rate / 10, ether_sprintf(sc->sc_mib_addr.aMAC_Address)); + if_attach(ifp); +#ifdef __FreeBSD__ + ether_ifattach(ifp); +#if NBPFILTER > 0 + bpfattach(ifp, DLT_EN10MB, sizeof(struct ether_header)); +#endif +#else + ether_ifattach(ifp, sc->sc_mib_addr.aMAC_Address); +#if NBPFILTER > 0 + bpfattach(&ifp->if_bpf, ifp, DLT_EN10MB, sizeof(struct ether_header)); +#endif +#endif + +#ifdef IFM_IEEE80211 + ifmedia_init(&sc->sc_media, 0, awi_media_change, awi_media_status); + phy_rates = sc->sc_mib_phy.aSuprt_Data_Rates; + for (i = 0; i < phy_rates[1]; i++) { + mword = awi_media_rate2opt(sc, AWI_80211_RATE(phy_rates[2 + i])); + if (mword == 0) + continue; + mword |= IFM_IEEE80211; + ifmedia_add(&sc->sc_media, mword, 0, NULL); + ifmedia_add(&sc->sc_media, + mword | IFM_IEEE80211_ADHOC, 0, NULL); + ifmedia_add(&sc->sc_media, + mword | IFM_IEEE80211_ADHOC | IFM_FLAG0, 0, NULL); + } + awi_media_status(ifp, &imr); + ifmedia_set(&sc->sc_media, imr.ifm_active); +#endif + + /* ready to accept ioctl */ + awi_unlock(sc); + return 0; +} + +#ifdef __NetBSD__ +int +awi_detach(sc) + struct awi_softc *sc; +{ + struct ifnet *ifp = sc->sc_ifp; + int s; + + s = splnet(); + sc->sc_invalid = 1; + awi_stop(sc); + while (sc->sc_sleep_cnt > 0) { + wakeup(sc); + (void)tsleep(sc, PWAIT, "awidet", 1); + } +#if NBPFILTER > 0 + bpfdetach(ifp); +#endif +#ifdef IFM_IEEE80211 + ifmedia_delete_instance(&sc->sc_media, IFM_INST_ANY); +#endif + ether_ifdetach(ifp); + if_detach(ifp); + if (sc->sc_enabled) { + if (sc->sc_disable) + (*sc->sc_disable)(sc); + sc->sc_enabled = 0; + } + splx(s); + return 0; +} + +int +awi_activate(self, act) + struct device *self; + enum devact act; +{ + struct awi_softc *sc = (struct awi_softc *)self; + int s, error = 0; + + s = splnet(); + switch (act) { + case DVACT_ACTIVATE: + error = EOPNOTSUPP; + break; + + case DVACT_DEACTIVATE: + sc->sc_invalid = 1; + if_deactivate(sc->sc_ifp); + break; + } + splx(s); + + return error; +} +#endif /* __NetBSD__ */ + +void +awi_reset(sc) + struct awi_softc *sc; +{ + int s; + + if (!sc->sc_enabled) + return; + s = splnet(); + sc->sc_invalid = 1; + awi_stop(sc); + if (sc->sc_disable) + (*sc->sc_disable)(sc); + sc->sc_enabled = 0; + DELAY(1000); + sc->sc_invalid = 0; + (void)awi_init(sc); + splx(s); +} + +static int +awi_ioctl(ifp, cmd, data) + struct ifnet *ifp; + u_long cmd; + caddr_t data; +{ + struct awi_softc *sc = ifp->if_softc; + struct ifreq *ifr = (struct ifreq *)data; + struct ifaddr *ifa = (struct ifaddr *)data; + int s, error; + size_t nwidlen; + u_int8_t nwid[IEEE80211_NWID_LEN + 1]; + u_int8_t *p; + + s = splnet(); + + /* serialize ioctl */ + error = awi_lock(sc); + if (error) + goto cantlock; + switch (cmd) { + case SIOCSIFADDR: + ifp->if_flags |= IFF_UP; + switch (ifa->ifa_addr->sa_family) { +#ifdef INET + case AF_INET: + arp_ifinit((void *)ifp, ifa); + break; +#endif + } + /* FALLTHROUGH */ + case SIOCSIFFLAGS: + sc->sc_format_llc = !(ifp->if_flags & IFF_LINK0); + if (!(ifp->if_flags & IFF_UP)) { + if (sc->sc_enabled) { + awi_stop(sc); + if (sc->sc_disable) + (*sc->sc_disable)(sc); + sc->sc_enabled = 0; + } + break; + } + error = awi_init(sc); + break; + + case SIOCADDMULTI: + case SIOCDELMULTI: +#ifdef __FreeBSD__ + error = ENETRESET; /*XXX*/ +#else + error = (cmd == SIOCADDMULTI) ? + ether_addmulti(ifr, &sc->sc_ec) : + ether_delmulti(ifr, &sc->sc_ec); +#endif + if (error == ENETRESET) { + if (sc->sc_enabled) + error = awi_init(sc); + else + error = 0; + } + break; + case SIOCSIFMTU: + if (ifr->ifr_mtu > ETHERMTU) + error = EINVAL; + else + ifp->if_mtu = ifr->ifr_mtu; + break; + case SIOCS80211NWID: + error = copyinstr(ifr->ifr_data, nwid, sizeof(nwid), &nwidlen); + if (error) + break; + nwidlen--; /* eliminate trailing '\0' */ + if (nwidlen > IEEE80211_NWID_LEN) { + error = EINVAL; + break; + } + if (sc->sc_mib_mac.aDesired_ESS_ID[1] == nwidlen && + memcmp(&sc->sc_mib_mac.aDesired_ESS_ID[2], nwid, + nwidlen) == 0) + break; + memset(sc->sc_mib_mac.aDesired_ESS_ID, 0, AWI_ESS_ID_SIZE); + sc->sc_mib_mac.aDesired_ESS_ID[0] = IEEE80211_ELEMID_SSID; + sc->sc_mib_mac.aDesired_ESS_ID[1] = nwidlen; + memcpy(&sc->sc_mib_mac.aDesired_ESS_ID[2], nwid, nwidlen); + if (sc->sc_enabled) { + awi_stop(sc); + error = awi_init(sc); + } + break; + case SIOCG80211NWID: + if (ifp->if_flags & IFF_RUNNING) + p = sc->sc_bss.essid; + else + p = sc->sc_mib_mac.aDesired_ESS_ID; + error = copyout(p + 2, ifr->ifr_data, IEEE80211_NWID_LEN); + break; +#ifdef IFM_IEEE80211 + case SIOCSIFMEDIA: + case SIOCGIFMEDIA: + error = ifmedia_ioctl(ifp, ifr, &sc->sc_media, cmd); + break; +#endif + case SIOCGDRVSPEC: + error = awi_drvget(ifp, cmd, data); + break; + case SIOCSDRVSPEC: + error = awi_drvset(ifp, cmd, data); + break; + default: + error = EINVAL; + break; + } + awi_unlock(sc); + cantlock: + splx(s); + return error; +} + +#ifdef IFM_IEEE80211 +static int +awi_media_rate2opt(sc, rate) + struct awi_softc *sc; + int rate; +{ + int mword; + + mword = 0; + switch (rate) { + case 10: + if (sc->sc_mib_phy.IEEE_PHY_Type == AWI_PHY_TYPE_FH) + mword = IFM_IEEE80211_FH1; + else + mword = IFM_IEEE80211_DS1; + break; + case 20: + if (sc->sc_mib_phy.IEEE_PHY_Type == AWI_PHY_TYPE_FH) + mword = IFM_IEEE80211_FH2; + else + mword = IFM_IEEE80211_DS2; + break; + case 55: + if (sc->sc_mib_phy.IEEE_PHY_Type == AWI_PHY_TYPE_DS) + mword = IFM_IEEE80211_DS5; + break; + case 110: + if (sc->sc_mib_phy.IEEE_PHY_Type == AWI_PHY_TYPE_DS) + mword = IFM_IEEE80211_DS11; + break; + } + return mword; +} + +static int +awi_media_opt2rate(sc, opt) + struct awi_softc *sc; + int opt; +{ + int rate; + + rate = 0; + switch (IFM_SUBTYPE(opt)) { + case IFM_IEEE80211_FH1: + case IFM_IEEE80211_FH2: + if (sc->sc_mib_phy.IEEE_PHY_Type != AWI_PHY_TYPE_FH) + return 0; + break; + case IFM_IEEE80211_DS1: + case IFM_IEEE80211_DS2: + case IFM_IEEE80211_DS5: + case IFM_IEEE80211_DS11: + if (sc->sc_mib_phy.IEEE_PHY_Type != AWI_PHY_TYPE_DS) + return 0; + break; + } + + switch (IFM_SUBTYPE(opt)) { + case IFM_IEEE80211_FH1: + case IFM_IEEE80211_DS1: + rate = 10; + break; + case IFM_IEEE80211_FH2: + case IFM_IEEE80211_DS2: + rate = 20; + break; + case IFM_IEEE80211_DS5: + rate = 55; + break; + case IFM_IEEE80211_DS11: + rate = 110; + break; + } + return rate; +} + +/* + * Called from ifmedia_ioctl via awi_ioctl with lock obtained. + */ +static int +awi_media_change(ifp) + struct ifnet *ifp; +{ + struct awi_softc *sc = ifp->if_softc; + struct ifmedia_entry *ime; + u_int8_t *phy_rates; + int i, rate, error; + + error = 0; + ime = sc->sc_media.ifm_cur; + rate = awi_media_opt2rate(sc, ime->ifm_media); + if (rate == 0) + return EINVAL; + if (rate != sc->sc_tx_rate) { + phy_rates = sc->sc_mib_phy.aSuprt_Data_Rates; + for (i = 0; i < phy_rates[1]; i++) { + if (rate == AWI_80211_RATE(phy_rates[2 + i])) + break; + } + if (i == phy_rates[1]) + return EINVAL; + } + if (ime->ifm_media & IFM_IEEE80211_ADHOC) { + sc->sc_mib_local.Network_Mode = 0; + sc->sc_no_bssid = (ime->ifm_media & IFM_FLAG0) ? 1 : 0; + } else { + sc->sc_mib_local.Network_Mode = 1; + } + if (sc->sc_enabled) { + awi_stop(sc); + error = awi_init(sc); + } + return error; +} + +static void +awi_media_status(ifp, imr) + struct ifnet *ifp; + struct ifmediareq *imr; +{ + struct awi_softc *sc = ifp->if_softc; + + imr->ifm_status = IFM_AVALID; + if (ifp->if_flags & IFF_RUNNING) + imr->ifm_status |= IFM_ACTIVE; + imr->ifm_active = IFM_IEEE80211; + imr->ifm_active |= awi_media_rate2opt(sc, sc->sc_tx_rate); + if (sc->sc_mib_local.Network_Mode == 0) { + imr->ifm_active |= IFM_IEEE80211_ADHOC; + if (sc->sc_no_bssid) + imr->ifm_active |= IFM_FLAG0; + } +} +#endif /* IFM_IEEE80211 */ + +static int +awi_drvget(ifp, cmd, data) + struct ifnet *ifp; + u_long cmd; + caddr_t data; +{ + struct awi_softc *sc = ifp->if_softc; + struct ifdrv *ifd = (struct ifdrv *)data; + u_int8_t buf[AWICTL_BUFSIZE]; + u_int8_t *essid; + int error = 0; + + switch (ifd->ifd_cmd) { + case AWICTL_REGION: + if (ifd->ifd_len < 1) + return ENOSPC; + ifd->ifd_len = 1; + buf[0] = sc->sc_mib_phy.aCurrent_Reg_Domain; + break; + case AWICTL_CHANSET: + if (ifd->ifd_len < 3) + return ENOSPC; + ifd->ifd_len = 3; + buf[0] = sc->sc_bss.chanset; + buf[1] = sc->sc_scan_min; + buf[2] = sc->sc_scan_max; + break; + case AWICTL_RAWBPF: + if (ifd->ifd_len < 1) + return ENOSPC; + ifd->ifd_len = 1; + buf[0] = sc->sc_rawbpf; + break; + case AWICTL_DESSID: + case AWICTL_CESSID: + if (ifd->ifd_cmd == AWICTL_DESSID) + essid = sc->sc_mib_mac.aDesired_ESS_ID; + else + essid = sc->sc_bss.essid; + if (ifd->ifd_len < essid[1]) + return ENOSPC; + ifd->ifd_len = essid[1]; + if (ifd->ifd_len > 0) + memcpy(buf, essid, ifd->ifd_len); + break; + case AWICTL_MODE: + if (ifd->ifd_len < 1) + return ENOSPC; + ifd->ifd_len = 1; + if (sc->sc_mib_local.Network_Mode == 0) { + if (sc->sc_no_bssid) + buf[0] = AWICTL_MODE_NOBSSID; + else + buf[0] = AWICTL_MODE_ADHOC; + } else + buf[0] = AWICTL_MODE_INFRA; + break; + default: + error = EINVAL; + break; + } + if (error == 0 && ifd->ifd_len > 0) + error = copyout(ifd->ifd_data, buf, ifd->ifd_len); + return error; +} + +static int +awi_drvset(ifp, cmd, data) + struct ifnet *ifp; + u_long cmd; + caddr_t data; +{ + struct awi_softc *sc = ifp->if_softc; + struct ifdrv *ifd = (struct ifdrv *)data; + u_int8_t buf[AWICTL_BUFSIZE]; + u_int8_t oregion; + int error = 0; + + if (ifd->ifd_len > sizeof(buf)) + return EINVAL; + error = copyin(ifd->ifd_data, buf, ifd->ifd_len); + if (error) + return error; + + switch (ifd->ifd_cmd) { + case AWICTL_REGION: + if (ifd->ifd_len != 1) + return EINVAL; + oregion = sc->sc_mib_phy.aCurrent_Reg_Domain; + if (buf[0] == oregion) + break; + sc->sc_mib_phy.aCurrent_Reg_Domain = buf[0]; + error = awi_init_region(sc); + if (error) { + sc->sc_mib_phy.aCurrent_Reg_Domain = oregion; + break; + } + if (sc->sc_enabled) { + awi_stop(sc); + error = awi_init(sc); + } + break; + case AWICTL_CHANSET: + if (ifd->ifd_len != 3) + return EINVAL; + /* reset scan min/max */ + awi_init_region(sc); + if (buf[0] < sc->sc_scan_min || buf[0] > sc->sc_scan_max || + buf[1] < sc->sc_scan_min || buf[1] > sc->sc_scan_max || + buf[2] < sc->sc_scan_min || buf[2] > sc->sc_scan_max) + return EINVAL; + sc->sc_scan_cur = buf[0]; + sc->sc_scan_min = buf[1]; + sc->sc_scan_max = buf[2]; + if (sc->sc_mib_phy.IEEE_PHY_Type == AWI_PHY_TYPE_FH) + sc->sc_scan_set = sc->sc_scan_cur % 3 + 1; + if (sc->sc_enabled) { + awi_stop(sc); + error = awi_init(sc); + } + break; + case AWICTL_RAWBPF: + if (ifd->ifd_len != 1) + return EINVAL; + sc->sc_rawbpf = buf[0]; + break; + case AWICTL_DESSID: + if (ifd->ifd_len > IEEE80211_NWID_LEN) + return EINVAL; + if (sc->sc_mib_mac.aDesired_ESS_ID[1] == ifd->ifd_len && + memcmp(&sc->sc_mib_mac.aDesired_ESS_ID[2], buf, + ifd->ifd_len) == 0) + break; + memset(sc->sc_mib_mac.aDesired_ESS_ID, 0, AWI_ESS_ID_SIZE); + sc->sc_mib_mac.aDesired_ESS_ID[0] = IEEE80211_ELEMID_SSID; + sc->sc_mib_mac.aDesired_ESS_ID[1] = ifd->ifd_len; + memcpy(&sc->sc_mib_mac.aDesired_ESS_ID[2], buf, ifd->ifd_len); + if (sc->sc_enabled) { + awi_stop(sc); + error = awi_init(sc); + } + break; + case AWICTL_CESSID: + error = EINVAL; + break; + case AWICTL_MODE: + switch (buf[0]) { + case AWICTL_MODE_INFRA: + sc->sc_mib_local.Network_Mode = 1; + sc->sc_no_bssid = 0; + break; + case AWICTL_MODE_ADHOC: + sc->sc_mib_local.Network_Mode = 0; + sc->sc_no_bssid = 0; + break; + case AWICTL_MODE_NOBSSID: + sc->sc_mib_local.Network_Mode = 0; + sc->sc_no_bssid = 1; + break; + default: + return EINVAL; + } + if (sc->sc_enabled) { + awi_stop(sc); + error = awi_init(sc); + } + break; + default: + error = EINVAL; + break; + } + return error; +} + +int +awi_intr(arg) + void *arg; +{ + struct awi_softc *sc = arg; + u_int16_t status; + int error, handled = 0, ocansleep; + + if (sc->sc_invalid) + return 0; + + am79c930_gcr_setbits(&sc->sc_chip, + AM79C930_GCR_DISPWDN | AM79C930_GCR_ECINT); + awi_write_1(sc, AWI_DIS_PWRDN, 1); + ocansleep = sc->sc_cansleep; + sc->sc_cansleep = 0; + + for (;;) { + error = awi_intr_lock(sc); + if (error) + break; + status = awi_read_1(sc, AWI_INTSTAT); + awi_write_1(sc, AWI_INTSTAT, 0); + awi_write_1(sc, AWI_INTSTAT, 0); + status |= awi_read_1(sc, AWI_INTSTAT2) << 8; + awi_write_1(sc, AWI_INTSTAT2, 0); + DELAY(10); + awi_intr_unlock(sc); + if (!sc->sc_cmd_inprog) + status &= ~AWI_INT_CMD; /* make sure */ + if (status == 0) + break; + handled = 1; + if (status & AWI_INT_RX) + awi_rxint(sc); + if (status & AWI_INT_TX) + awi_txint(sc); + if (status & AWI_INT_CMD) + awi_cmd_done(sc); + if (status & AWI_INT_SCAN_CMPLT) { + if (sc->sc_status == AWI_ST_SCAN && + sc->sc_mgt_timer > 0) + (void)awi_next_scan(sc); + } + } + sc->sc_cansleep = ocansleep; + am79c930_gcr_clearbits(&sc->sc_chip, AM79C930_GCR_DISPWDN); + awi_write_1(sc, AWI_DIS_PWRDN, 0); + return handled; +} + +static int +awi_init(sc) + struct awi_softc *sc; +{ + int error, ostatus; + int n; + struct ifnet *ifp = sc->sc_ifp; +#ifdef __FreeBSD__ + struct ifmultiaddr *ifma; +#else + struct ether_multi *enm; + struct ether_multistep step; +#endif + + n = 0; + ifp->if_flags |= IFF_ALLMULTI; + sc->sc_mib_local.Accept_All_Multicast_Dis = 0; + if (ifp->if_flags & IFF_PROMISC) { + sc->sc_mib_mac.aPromiscuous_Enable = 1; + goto set_mib; + } + sc->sc_mib_mac.aPromiscuous_Enable = 0; + ifp->if_flags &= ~IFF_ALLMULTI; +#ifdef __FreeBSD__ + if (ifp->if_amcount != 0) + goto set_mib; + for (ifma = LIST_FIRST(&ifp->if_multiaddrs); ifma != NULL; + ifma = LIST_NEXT(ifma, ifma_link)) { + if (ifma->ifma_addr->sa_family != AF_LINK) + continue; + if (n == AWI_GROUP_ADDR_SIZE) + goto set_mib; + memcpy(sc->sc_mib_addr.aGroup_Addresses[n], + LLADDR((struct sockaddr_dl *)ifma->ifma_addr), + ETHER_ADDR_LEN); + n++; + } +#else + ETHER_FIRST_MULTI(step, &sc->sc_ec, enm); + while (enm != NULL) { + if (n == AWI_GROUP_ADDR_SIZE || + memcmp(enm->enm_addrlo, enm->enm_addrhi, ETHER_ADDR_LEN) + != 0) + goto set_mib; + memcpy(sc->sc_mib_addr.aGroup_Addresses[n], enm->enm_addrlo, + ETHER_ADDR_LEN); + n++; + ETHER_NEXT_MULTI(step, enm); + } +#endif + ifp->if_flags &= ~IFF_ALLMULTI; + sc->sc_mib_local.Accept_All_Multicast_Dis = 1; + + set_mib: + if (!sc->sc_enabled) { + sc->sc_enabled = 1; + if (sc->sc_enable) + (*sc->sc_enable)(sc); + sc->sc_status = AWI_ST_INIT; + error = awi_init_hw(sc); + if (error) + return error; + } + ostatus = sc->sc_status; + sc->sc_status = AWI_ST_INIT; + if ((error = awi_mib(sc, AWI_CMD_SET_MIB, AWI_MIB_LOCAL)) != 0 || + (error = awi_mib(sc, AWI_CMD_SET_MIB, AWI_MIB_ADDR)) != 0 || + (error = awi_mib(sc, AWI_CMD_SET_MIB, AWI_MIB_MAC)) != 0 || + (error = awi_mib(sc, AWI_CMD_SET_MIB, AWI_MIB_MGT)) != 0 || + (error = awi_mib(sc, AWI_CMD_SET_MIB, AWI_MIB_PHY)) != 0) { + awi_stop(sc); + return error; + } + if (ifp->if_flags & IFF_RUNNING) + sc->sc_status = AWI_ST_RUNNING; + else { + if (ostatus == AWI_ST_INIT) { + error = awi_init_txrx(sc); + if (error) + return error; + } + error = awi_start_scan(sc); + } + return error; +} + +static void +awi_stop(sc) + struct awi_softc *sc; +{ + struct ifnet *ifp = sc->sc_ifp; + struct awi_bss *bp; + struct mbuf *m; + + sc->sc_status = AWI_ST_INIT; + if (!sc->sc_invalid) { + (void)awi_cmd_wait(sc); + if (sc->sc_mib_local.Network_Mode && + sc->sc_status > AWI_ST_AUTH) + awi_send_deauth(sc); + awi_stop_txrx(sc); + } + ifp->if_flags &= ~(IFF_RUNNING|IFF_OACTIVE); + ifp->if_timer = 0; + sc->sc_tx_timer = sc->sc_rx_timer = sc->sc_mgt_timer = 0; + for (;;) { + IF_DEQUEUE(&sc->sc_mgtq, m); + if (m == NULL) + break; + m_freem(m); + } + for (;;) { + IF_DEQUEUE(&ifp->if_snd, m); + if (m == NULL) + break; + m_freem(m); + } + while ((bp = TAILQ_FIRST(&sc->sc_scan)) != NULL) + TAILQ_REMOVE(&sc->sc_scan, bp, list); +} + +static void +awi_watchdog(ifp) + struct ifnet *ifp; +{ + struct awi_softc *sc = ifp->if_softc; + int ocansleep; + + if (sc->sc_invalid) { + ifp->if_timer = 0; + return; + } + + ocansleep = sc->sc_cansleep; + sc->sc_cansleep = 0; + if (sc->sc_tx_timer && --sc->sc_tx_timer == 0) { + printf("%s: transmit timeout\n", sc->sc_dev.dv_xname); + awi_txint(sc); + } + if (sc->sc_rx_timer && --sc->sc_rx_timer == 0) { + printf("%s: no recent beacons from %s; rescanning\n", + sc->sc_dev.dv_xname, ether_sprintf(sc->sc_bss.bssid)); + awi_start_scan(sc); + } + if (sc->sc_mgt_timer && --sc->sc_mgt_timer == 0) { + switch (sc->sc_status) { + case AWI_ST_SCAN: + awi_stop_scan(sc); + break; + case AWI_ST_AUTH: + case AWI_ST_ASSOC: + /* restart scan */ + awi_start_scan(sc); + break; + default: + break; + } + } + + if (sc->sc_tx_timer == 0 && sc->sc_rx_timer == 0 && + sc->sc_mgt_timer == 0) + ifp->if_timer = 0; + else + ifp->if_timer = 1; + sc->sc_cansleep = ocansleep; +} + +static void +awi_start(ifp) + struct ifnet *ifp; +{ + struct awi_softc *sc = ifp->if_softc; + struct mbuf *m0, *m; + u_int32_t txd, frame, ntxd; + u_int8_t rate; + int len, sent = 0; + + for (;;) { + txd = sc->sc_txnext; + IF_DEQUEUE(&sc->sc_mgtq, m0); + if (m0 != NULL) { + if (awi_next_txd(sc, m0->m_pkthdr.len, &frame, &ntxd)) { + IF_PREPEND(&sc->sc_mgtq, m0); + ifp->if_flags |= IFF_OACTIVE; + break; + } + } else { + if (!(ifp->if_flags & IFF_RUNNING)) + break; + IF_DEQUEUE(&ifp->if_snd, m0); + if (m0 == NULL) + break; + if (awi_next_txd(sc, m0->m_pkthdr.len + + sizeof(struct ieee80211_frame), &frame, &ntxd)) { + IF_PREPEND(&ifp->if_snd, m0); + ifp->if_flags |= IFF_OACTIVE; + break; + } + AWI_BPF_MTAP(sc, m0, AWI_BPF_NORM); + m0 = awi_fix_txhdr(sc, m0); + if (m0 == NULL) { + ifp->if_oerrors++; + continue; + } + ifp->if_opackets++; + } + AWI_BPF_MTAP(sc, m0, AWI_BPF_RAW); + len = 0; + for (m = m0; m != NULL; m = m->m_next) { + awi_write_bytes(sc, frame + len, mtod(m, u_int8_t *), + m->m_len); + len += m->m_len; + } + m_freem(m0); + rate = sc->sc_tx_rate; /*XXX*/ + awi_write_1(sc, ntxd + AWI_TXD_STATE, 0); + awi_write_4(sc, txd + AWI_TXD_START, frame); + awi_write_4(sc, txd + AWI_TXD_NEXT, ntxd); + awi_write_4(sc, txd + AWI_TXD_LENGTH, len); + awi_write_1(sc, txd + AWI_TXD_RATE, rate); + awi_write_4(sc, txd + AWI_TXD_NDA, 0); + awi_write_4(sc, txd + AWI_TXD_NRA, 0); + awi_write_1(sc, txd + AWI_TXD_STATE, AWI_TXD_ST_OWN); + sc->sc_txnext = ntxd; + sent++; + } + if (sent) { + if (sc->sc_tx_timer == 0) + sc->sc_tx_timer = 5; + ifp->if_timer = 1; +#ifdef AWI_DEBUG + if (awi_verbose) + printf("awi_start: sent %d txdone %d txnext %d txbase %d txend %d\n", sent, sc->sc_txdone, sc->sc_txnext, sc->sc_txbase, sc->sc_txend); +#endif + } +} + +static void +awi_txint(sc) + struct awi_softc *sc; +{ + struct ifnet *ifp = sc->sc_ifp; + u_int8_t flags; + + while (sc->sc_txdone != sc->sc_txnext) { + flags = awi_read_1(sc, sc->sc_txdone + AWI_TXD_STATE); + if ((flags & AWI_TXD_ST_OWN) || !(flags & AWI_TXD_ST_DONE)) + break; + if (flags & AWI_TXD_ST_ERROR) + ifp->if_oerrors++; + sc->sc_txdone = awi_read_4(sc, sc->sc_txdone + AWI_TXD_NEXT) & + 0x7fff; + } + sc->sc_tx_timer = 0; + ifp->if_flags &= ~IFF_OACTIVE; +#ifdef AWI_DEBUG + if (awi_verbose) + printf("awi_txint: txdone %d txnext %d txbase %d txend %d\n", + sc->sc_txdone, sc->sc_txnext, sc->sc_txbase, sc->sc_txend); +#endif + awi_start(ifp); +} + +static struct mbuf * +awi_fix_txhdr(sc, m0) + struct awi_softc *sc; + struct mbuf *m0; +{ + struct ether_header eh; + struct ieee80211_frame *wh; + struct llc *llc; + + if (m0->m_len < sizeof(eh)) { + m0 = m_pullup(m0, sizeof(eh)); + if (m0 == NULL) + return NULL; + } + memcpy(&eh, mtod(m0, caddr_t), sizeof(eh)); + if (sc->sc_format_llc) { + m_adj(m0, sizeof(struct ether_header) - sizeof(struct llc)); + llc = mtod(m0, struct llc *); + llc->llc_dsap = llc->llc_ssap = LLC_SNAP_LSAP; + llc->llc_control = LLC_UI; + llc->llc_snap.org_code[0] = llc->llc_snap.org_code[1] = + llc->llc_snap.org_code[2] = 0; + llc->llc_snap.ether_type = eh.ether_type; + } + M_PREPEND(m0, sizeof(struct ieee80211_frame), M_DONTWAIT); + if (m0 == NULL) + return NULL; + wh = mtod(m0, struct ieee80211_frame *); + + wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_DATA; + LE_WRITE_2(wh->i_dur, 0); + LE_WRITE_2(wh->i_seq, 0); + if (sc->sc_mib_local.Network_Mode) { + wh->i_fc[1] = IEEE80211_FC1_DIR_TODS; + memcpy(wh->i_addr1, sc->sc_bss.bssid, ETHER_ADDR_LEN); + memcpy(wh->i_addr2, eh.ether_shost, ETHER_ADDR_LEN); + memcpy(wh->i_addr3, eh.ether_dhost, ETHER_ADDR_LEN); + } else { + wh->i_fc[1] = IEEE80211_FC1_DIR_NODS; + memcpy(wh->i_addr1, eh.ether_dhost, ETHER_ADDR_LEN); + memcpy(wh->i_addr2, eh.ether_shost, ETHER_ADDR_LEN); + memcpy(wh->i_addr3, sc->sc_bss.bssid, ETHER_ADDR_LEN); + } + return m0; +} + +static struct mbuf * +awi_fix_rxhdr(sc, m0) + struct awi_softc *sc; + struct mbuf *m0; +{ + struct ieee80211_frame wh; + struct ether_header *eh; + struct llc *llc; + + if (m0->m_len < sizeof(wh)) { + m_freem(m0); + return NULL; + } + llc = (struct llc *)(mtod(m0, caddr_t) + sizeof(wh)); + if (llc->llc_dsap == LLC_SNAP_LSAP && + llc->llc_ssap == LLC_SNAP_LSAP && + llc->llc_control == LLC_UI && + llc->llc_snap.org_code[0] == 0 && + llc->llc_snap.org_code[1] == 0 && + llc->llc_snap.org_code[2] == 0) { + memcpy(&wh, mtod(m0, caddr_t), sizeof(wh)); + m_adj(m0, sizeof(wh) + sizeof(*llc) - sizeof(*eh)); + eh = mtod(m0, struct ether_header *); + switch (wh.i_fc[1] & IEEE80211_FC1_DIR_MASK) { + case IEEE80211_FC1_DIR_NODS: + memcpy(eh->ether_dhost, wh.i_addr1, ETHER_ADDR_LEN); + memcpy(eh->ether_shost, wh.i_addr2, ETHER_ADDR_LEN); + break; + case IEEE80211_FC1_DIR_TODS: + memcpy(eh->ether_dhost, wh.i_addr3, ETHER_ADDR_LEN); + memcpy(eh->ether_shost, wh.i_addr2, ETHER_ADDR_LEN); + break; + case IEEE80211_FC1_DIR_FROMDS: + memcpy(eh->ether_dhost, wh.i_addr1, ETHER_ADDR_LEN); + memcpy(eh->ether_shost, wh.i_addr3, ETHER_ADDR_LEN); + break; + case IEEE80211_FC1_DIR_DSTODS: + m_freem(m0); + return NULL; + } + } else { + /* assuming ethernet encapsulation, just strip 802.11 header */ + m_adj(m0, sizeof(wh)); + } + return m0; +} + +static void +awi_input(sc, m, rxts, rssi) + struct awi_softc *sc; + struct mbuf *m; + u_int32_t rxts; + u_int8_t rssi; +{ + struct ifnet *ifp = sc->sc_ifp; + struct ieee80211_frame *wh; +#ifndef __NetBSD__ + struct ether_header *eh; +#endif + + AWI_BPF_MTAP(sc, m, AWI_BPF_RAW); + wh = mtod(m, struct ieee80211_frame *); + if ((wh->i_fc[0] & IEEE80211_FC0_VERSION_MASK) != + IEEE80211_FC0_VERSION_0) { + printf("%s; receive packet with wrong version: %x\n", + sc->sc_dev.dv_xname, wh->i_fc[0]); + m_freem(m); + return; + } +#ifdef AWI_DEBUG + if (awi_dump) + awi_dump_pkt(sc, m, rssi); +#endif + + if ((sc->sc_mib_local.Network_Mode || !sc->sc_no_bssid) && + sc->sc_status == AWI_ST_RUNNING) { + if (memcmp(wh->i_addr2, sc->sc_bss.bssid, ETHER_ADDR_LEN) == 0) { + sc->sc_rx_timer = 10; + sc->sc_bss.rssi = rssi; + } + } + switch (wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) { + case IEEE80211_FC0_TYPE_DATA: + if (sc->sc_mib_local.Network_Mode) { + if ((wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) != + IEEE80211_FC1_DIR_FROMDS) { + m_freem(m); + return; + } + } else { + if ((wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) != + IEEE80211_FC1_DIR_NODS) { + m_freem(m); + return; + } + } + m = awi_fix_rxhdr(sc, m); + if (m == NULL) { + ifp->if_ierrors++; + break; + } + ifp->if_ipackets++; + AWI_BPF_MTAP(sc, m, AWI_BPF_NORM); +#ifdef __NetBSD__ + m->m_flags |= M_HASFCS; + (*ifp->if_input)(ifp, m); +#else + eh = mtod(m, struct ether_header *); + m_adj(m, sizeof(*eh)); + m_adj(m, -ETHER_CRC_LEN); + ether_input(ifp, eh, m); +#endif + break; + case IEEE80211_FC0_TYPE_MGT: + if ((wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) != + IEEE80211_FC1_DIR_NODS) { + m_freem(m); + return; + } + switch (wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) { + case IEEE80211_FC0_SUBTYPE_PROBE_RESP: + case IEEE80211_FC0_SUBTYPE_BEACON: + awi_recv_beacon(sc, m, rxts, rssi); + break; + case IEEE80211_FC0_SUBTYPE_AUTH: + awi_recv_auth(sc, m); + break; + case IEEE80211_FC0_SUBTYPE_ASSOC_RESP: + case IEEE80211_FC0_SUBTYPE_REASSOC_RESP: + awi_recv_asresp(sc, m); + break; + case IEEE80211_FC0_SUBTYPE_DEAUTH: + if (sc->sc_mib_local.Network_Mode) + awi_send_auth(sc); + break; + case IEEE80211_FC0_SUBTYPE_DISASSOC: + if (sc->sc_mib_local.Network_Mode) + awi_send_asreq(sc, 1); + break; + } + m_freem(m); + break; + case IEEE80211_FC0_TYPE_CTL: + default: + /* should not come here */ + m_freem(m); + break; + } +} + +static void +awi_rxint(sc) + struct awi_softc *sc; +{ + u_int8_t state, rate, rssi; + u_int16_t len; + u_int32_t frame, next, rxts, rxoff; + struct mbuf *m; + + rxoff = sc->sc_rxdoff; + for (;;) { + state = awi_read_1(sc, rxoff + AWI_RXD_HOST_DESC_STATE); + if (state & AWI_RXD_ST_OWN) + break; + if (!(state & AWI_RXD_ST_CONSUMED)) { + if (state & AWI_RXD_ST_RXERROR) + sc->sc_ifp->if_ierrors++; + else { + len = awi_read_2(sc, rxoff + AWI_RXD_LEN); + rate = awi_read_1(sc, rxoff + AWI_RXD_RATE); + rssi = awi_read_1(sc, rxoff + AWI_RXD_RSSI); + frame = awi_read_4(sc, rxoff + AWI_RXD_START_FRAME) & 0x7fff; + rxts = awi_read_4(sc, rxoff + AWI_RXD_LOCALTIME); + m = awi_devget(sc, frame, len); + if (state & AWI_RXD_ST_LF) + awi_input(sc, m, rxts, rssi); + else + sc->sc_rxpend = m; + } + state |= AWI_RXD_ST_CONSUMED; + awi_write_1(sc, rxoff + AWI_RXD_HOST_DESC_STATE, state); + } + next = awi_read_4(sc, rxoff + AWI_RXD_NEXT); + if (next & AWI_RXD_NEXT_LAST) + break; + /* make sure the next pointer is correct */ + if (next != awi_read_4(sc, rxoff + AWI_RXD_NEXT)) + break; + state |= AWI_RXD_ST_OWN; + awi_write_1(sc, rxoff + AWI_RXD_HOST_DESC_STATE, state); + rxoff = next & 0x7fff; + } + sc->sc_rxdoff = rxoff; +} + +struct mbuf * +awi_devget(sc, off, len) + struct awi_softc *sc; + u_int32_t off; + u_int16_t len; +{ + struct mbuf *m; + struct mbuf *top, **mp = ⊤ + u_int tlen; + + top = sc->sc_rxpend; + if (top != NULL) { + sc->sc_rxpend = NULL; + top->m_pkthdr.len += len; + while ((m = *mp) != NULL) + mp = &m->m_next; + if (m->m_flags & M_EXT) + tlen = m->m_ext.ext_size; + else if (m->m_flags & M_PKTHDR) + tlen = MHLEN; + else + tlen = MLEN; + tlen -= m->m_len; + if (tlen > len) + tlen = len; + awi_read_bytes(sc, off, mtod(m, u_int8_t *) + m->m_len, tlen); + off += tlen; + len -= tlen; + } + + while (len > 0) { + if (top == NULL) { + MGETHDR(m, M_DONTWAIT, MT_DATA); + if (m == NULL) + return NULL; + m->m_pkthdr.rcvif = sc->sc_ifp; + m->m_pkthdr.len = len; + m->m_len = MHLEN; + } else { + MGET(m, M_DONTWAIT, MT_DATA); + if (m == NULL) { + m_freem(top); + return NULL; + } + m->m_len = MLEN; + } + if (len >= MINCLSIZE) { + MCLGET(m, M_DONTWAIT); + if (m->m_flags & M_EXT) + m->m_len = m->m_ext.ext_size; + } + if (m->m_len > len) + m->m_len = len; + awi_read_bytes(sc, off, mtod(m, u_int8_t *), m->m_len); + off += m->m_len; + len -= m->m_len; + *mp = m; + mp = &m->m_next; + } + return top; +} + +/* + * Initialize hardware and start firmware to accept commands. + * Called everytime after power on firmware. + */ + +static int +awi_init_hw(sc) + struct awi_softc *sc; +{ + u_int8_t status; + u_int16_t intmask; + int i, error; + u_int8_t banner[AWI_BANNER_LEN]; + + awi_drvstate(sc, AWI_DRV_RESET); + + /* reset firmware */ + am79c930_gcr_setbits(&sc->sc_chip, AM79C930_GCR_CORESET); + DELAY(100); + awi_write_1(sc, AWI_SELFTEST, 0); + am79c930_gcr_clearbits(&sc->sc_chip, AM79C930_GCR_CORESET); + DELAY(100); + + /* wait for selftest completion */ + for (i = 0; ; i++) { + if (i >= AWI_SELFTEST_TIMEOUT*hz/1000) { + printf("%s: failed to complete selftest (timeout)\n", + sc->sc_dev.dv_xname); + return ENXIO; + } + status = awi_read_1(sc, AWI_SELFTEST); + if ((status & 0xf0) == 0xf0) + break; + if (sc->sc_cansleep) { + sc->sc_sleep_cnt++; + (void)tsleep(sc, PWAIT, "awitst", 1); + sc->sc_sleep_cnt--; + } else { + DELAY(1000*1000/hz); + } + } + if (status != AWI_SELFTEST_PASSED) { + printf("%s: failed to complete selftest (code %x)\n", + sc->sc_dev.dv_xname, status); + return ENXIO; + } + + /* check banner to confirm firmware write it */ + awi_read_bytes(sc, AWI_BANNER, banner, AWI_BANNER_LEN); + if (memcmp(banner, "PCnetMobile:", 12) != 0) { + printf("%s: failed to complete selftest (bad banner)\n", + sc->sc_dev.dv_xname); + for (i = 0; i < AWI_BANNER_LEN; i++) + printf("%s%02x", i ? ":" : "\t", banner[i]); + printf("\n"); + return ENXIO; + } + + /* initializing interrupt */ + error = awi_intr_lock(sc); + if (error) + return error; + intmask = AWI_INT_GROGGY | AWI_INT_SCAN_CMPLT | + AWI_INT_TX | AWI_INT_RX | AWI_INT_CMD; + awi_write_1(sc, AWI_INTMASK, ~intmask & 0xff); + awi_write_1(sc, AWI_INTMASK2, 0); + awi_write_1(sc, AWI_INTSTAT, 0); + awi_write_1(sc, AWI_INTSTAT2, 0); + awi_intr_unlock(sc); + am79c930_gcr_setbits(&sc->sc_chip, AM79C930_GCR_ENECINT); + + /* issueing interface test command */ + error = awi_cmd(sc, AWI_CMD_NOP); + if (error) { + printf("%s: failed to complete selftest", sc->sc_dev.dv_xname); + if (error != EWOULDBLOCK) + printf(" (error %d)\n", error); + else if (sc->sc_cansleep) + printf(" (lost interrupt)\n"); + else + printf(" (command timeout)\n"); + } + return error; +} + +/* + * Extract the factory default MIB value from firmware and assign the driver + * default value. + * Called once at attaching the interface. + */ + +static int +awi_init_mibs(sc) + struct awi_softc *sc; +{ + int i, error; + u_int8_t *rate; + + if ((error = awi_mib(sc, AWI_CMD_GET_MIB, AWI_MIB_LOCAL)) != 0 || + (error = awi_mib(sc, AWI_CMD_GET_MIB, AWI_MIB_ADDR)) != 0 || + (error = awi_mib(sc, AWI_CMD_GET_MIB, AWI_MIB_MAC)) != 0 || + (error = awi_mib(sc, AWI_CMD_GET_MIB, AWI_MIB_MGT)) != 0 || + (error = awi_mib(sc, AWI_CMD_GET_MIB, AWI_MIB_PHY)) != 0) { + printf("%s: failed to get default mib value (error %d)\n", + sc->sc_dev.dv_xname, error); + return error; + } + + rate = sc->sc_mib_phy.aSuprt_Data_Rates; + sc->sc_tx_rate = AWI_RATE_1MBIT; + for (i = 0; i < rate[1]; i++) { + if (AWI_80211_RATE(rate[2 + i]) > sc->sc_tx_rate) + sc->sc_tx_rate = AWI_80211_RATE(rate[2 + i]); + } + awi_init_region(sc); + memset(&sc->sc_mib_mac.aDesired_ESS_ID, 0, AWI_ESS_ID_SIZE); + sc->sc_mib_mac.aDesired_ESS_ID[0] = IEEE80211_ELEMID_SSID; + sc->sc_mib_local.Fragmentation_Dis = 1; + sc->sc_mib_local.Accept_All_Multicast_Dis = 1; + + /* allocate buffers */ + sc->sc_txbase = AWI_BUFFERS; + sc->sc_txend = sc->sc_txbase + + (AWI_TXD_SIZE + sizeof(struct ieee80211_frame) + + sizeof(struct ether_header) + ETHERMTU) * AWI_NTXBUFS; + LE_WRITE_4(&sc->sc_mib_local.Tx_Buffer_Offset, sc->sc_txbase); + LE_WRITE_4(&sc->sc_mib_local.Tx_Buffer_Size, + sc->sc_txend - sc->sc_txbase); + LE_WRITE_4(&sc->sc_mib_local.Rx_Buffer_Offset, sc->sc_txend); + LE_WRITE_4(&sc->sc_mib_local.Rx_Buffer_Size, + AWI_BUFFERS_END - sc->sc_txend); + sc->sc_mib_local.Network_Mode = 1; + sc->sc_mib_local.Acting_as_AP = 0; + return 0; +} + +/* + * Start transmitter and receiver of firmware + * Called after awi_init_hw() to start operation. + */ + +static int +awi_init_txrx(sc) + struct awi_softc *sc; +{ + int error; + + /* start transmitter */ + sc->sc_txdone = sc->sc_txnext = sc->sc_txbase; + awi_write_4(sc, sc->sc_txbase + AWI_TXD_START, 0); + awi_write_4(sc, sc->sc_txbase + AWI_TXD_NEXT, 0); + awi_write_4(sc, sc->sc_txbase + AWI_TXD_LENGTH, 0); + awi_write_1(sc, sc->sc_txbase + AWI_TXD_RATE, 0); + awi_write_4(sc, sc->sc_txbase + AWI_TXD_NDA, 0); + awi_write_4(sc, sc->sc_txbase + AWI_TXD_NRA, 0); + awi_write_1(sc, sc->sc_txbase + AWI_TXD_STATE, 0); + awi_write_4(sc, AWI_CMD_PARAMS+AWI_CA_TX_DATA, sc->sc_txbase); + awi_write_4(sc, AWI_CMD_PARAMS+AWI_CA_TX_MGT, 0); + awi_write_4(sc, AWI_CMD_PARAMS+AWI_CA_TX_BCAST, 0); + awi_write_4(sc, AWI_CMD_PARAMS+AWI_CA_TX_PS, 0); + awi_write_4(sc, AWI_CMD_PARAMS+AWI_CA_TX_CF, 0); + error = awi_cmd(sc, AWI_CMD_INIT_TX); + if (error) + return error; + + /* start receiver */ + if (sc->sc_rxpend) { + m_freem(sc->sc_rxpend); + sc->sc_rxpend = NULL; + } + error = awi_cmd(sc, AWI_CMD_INIT_RX); + if (error) + return error; + sc->sc_rxdoff = awi_read_4(sc, AWI_CMD_PARAMS+AWI_CA_IRX_DATA_DESC); + sc->sc_rxmoff = awi_read_4(sc, AWI_CMD_PARAMS+AWI_CA_IRX_PS_DESC); + return 0; +} + +static void +awi_stop_txrx(sc) + struct awi_softc *sc; +{ + + (void)awi_cmd(sc, AWI_CMD_KILL_RX); + awi_write_1(sc, AWI_CMD_PARAMS+AWI_CA_FTX_DATA, 1); + awi_write_1(sc, AWI_CMD_PARAMS+AWI_CA_FTX_MGT, 0); + awi_write_1(sc, AWI_CMD_PARAMS+AWI_CA_FTX_BCAST, 0); + awi_write_1(sc, AWI_CMD_PARAMS+AWI_CA_FTX_PS, 0); + awi_write_1(sc, AWI_CMD_PARAMS+AWI_CA_FTX_CF, 0); + (void)awi_cmd(sc, AWI_CMD_FLUSH_TX); +} + +static int +awi_init_region(sc) + struct awi_softc *sc; +{ + + if (sc->sc_mib_phy.IEEE_PHY_Type == AWI_PHY_TYPE_FH) { + switch (sc->sc_mib_phy.aCurrent_Reg_Domain) { + case AWI_REG_DOMAIN_US: + case AWI_REG_DOMAIN_CA: + case AWI_REG_DOMAIN_EU: + sc->sc_scan_min = 0; + sc->sc_scan_max = 77; + break; + case AWI_REG_DOMAIN_ES: + sc->sc_scan_min = 0; + sc->sc_scan_max = 26; + break; + case AWI_REG_DOMAIN_FR: + sc->sc_scan_min = 0; + sc->sc_scan_max = 32; + break; + case AWI_REG_DOMAIN_JP: + sc->sc_scan_min = 6; + sc->sc_scan_max = 17; + break; + default: + return EINVAL; + } + sc->sc_scan_cur = sc->sc_scan_min; + sc->sc_scan_set = sc->sc_scan_cur % 3 + 1; + } else { + switch (sc->sc_mib_phy.aCurrent_Reg_Domain) { + case AWI_REG_DOMAIN_US: + case AWI_REG_DOMAIN_CA: + sc->sc_scan_min = 1; + sc->sc_scan_max = 11; + sc->sc_scan_cur = 3; + break; + case AWI_REG_DOMAIN_EU: + sc->sc_scan_min = 1; + sc->sc_scan_max = 13; + sc->sc_scan_cur = 3; + break; + case AWI_REG_DOMAIN_ES: + sc->sc_scan_min = 10; + sc->sc_scan_max = 11; + sc->sc_scan_cur = 10; + break; + case AWI_REG_DOMAIN_FR: + sc->sc_scan_min = 10; + sc->sc_scan_max = 13; + sc->sc_scan_cur = 10; + break; + case AWI_REG_DOMAIN_JP: + sc->sc_scan_min = 14; + sc->sc_scan_max = 14; + sc->sc_scan_cur = 14; + break; + default: + return EINVAL; + } + } + return 0; +} + +static int +awi_start_scan(sc) + struct awi_softc *sc; +{ + int error = 0; + + if (!sc->sc_mib_local.Network_Mode && sc->sc_no_bssid) { + memset(&sc->sc_bss, 0, sizeof(sc->sc_bss)); + sc->sc_bss.rxtime = 0; + memcpy(sc->sc_bss.essid, &sc->sc_mib_mac.aDesired_ESS_ID, + sizeof(sc->sc_bss.essid)); + if (sc->sc_mib_phy.IEEE_PHY_Type == AWI_PHY_TYPE_FH) { + sc->sc_bss.chanset = sc->sc_scan_set; + sc->sc_bss.pattern = sc->sc_scan_cur; + sc->sc_bss.index = 1; + sc->sc_bss.dwell_time = 19; /*XXX*/ + } else + sc->sc_bss.chanset = sc->sc_scan_cur; + sc->sc_status = AWI_ST_SETSS; + error = awi_set_ss(sc); + } else { + if (sc->sc_mib_local.Network_Mode) + awi_drvstate(sc, AWI_DRV_INFSC); + else + awi_drvstate(sc, AWI_DRV_ADHSC); + sc->sc_start_bss = 0; + sc->sc_active_scan = 1; + sc->sc_mgt_timer = AWI_ASCAN_WAIT / 1000; + sc->sc_ifp->if_timer = 1; + sc->sc_status = AWI_ST_SCAN; + error = awi_cmd_scan(sc); + } + return error; +} + +static int +awi_next_scan(sc) + struct awi_softc *sc; +{ + int error; + + for (;;) { + /* + * The pattern parameter for FH phy should be incremented + * by 3. But BayStack 650 Access Points apparently always + * assign hop pattern set parameter to 1 for any pattern. + * So we try all combinations of pattern/set parameters. + * Since this causes no error, it may be a bug of + * PCnetMobile firmware. + */ + sc->sc_scan_cur++; + if (sc->sc_scan_cur > sc->sc_scan_max) { + sc->sc_scan_cur = sc->sc_scan_min; + if (sc->sc_mib_phy.IEEE_PHY_Type == AWI_PHY_TYPE_FH) + sc->sc_scan_set = (sc->sc_scan_set + 1) % 3; + } + error = awi_cmd_scan(sc); + if (error != EINVAL) + break; + } + return error; +} + +static void +awi_stop_scan(sc) + struct awi_softc *sc; +{ + struct ifnet *ifp = sc->sc_ifp; + struct awi_bss *bp, *sbp; + + bp = TAILQ_FIRST(&sc->sc_scan); + if (bp == NULL) { + notfound: + if (sc->sc_active_scan) { + if (ifp->if_flags & IFF_DEBUG) + printf("%s: entering passive scan mode\n", + sc->sc_dev.dv_xname); + sc->sc_active_scan = 0; + } + sc->sc_mgt_timer = AWI_PSCAN_WAIT / 1000; + ifp->if_timer = 1; + (void)awi_next_scan(sc); + return; + } + sbp = NULL; + for (; bp != NULL; bp = TAILQ_NEXT(bp, list)) { + if (bp->fails) { + /* + * The configuration of the access points may change + * during my scan. So we retries to associate with + * it unless there are any suitable AP. + */ + if (bp->fails < 3) + continue; + bp->fails = 0; + } + if (sc->sc_mib_mac.aDesired_ESS_ID[1] != 0 && + memcmp(&sc->sc_mib_mac.aDesired_ESS_ID, bp->essid, + sizeof(bp->essid) != 0)) + continue; + /* + * Since the firmware apparently scans not only the specified + * channel of SCAN command but all available channel within + * the region, we should filter out unnecessary responses here. + */ + if (sc->sc_mib_phy.IEEE_PHY_Type == AWI_PHY_TYPE_FH) { + if (bp->pattern < sc->sc_scan_min || + bp->pattern > sc->sc_scan_max) + continue; + } else { + if (bp->chanset < sc->sc_scan_min || + bp->chanset > sc->sc_scan_max) + continue; + } + if (sbp == NULL || bp->rssi > sbp->rssi) + sbp = bp; + } + if (sbp == NULL) + goto notfound; + sc->sc_bss = *sbp; + (void)awi_set_ss(sc); +} + +static void +awi_recv_beacon(sc, m0, rxts, rssi) + struct awi_softc *sc; + struct mbuf *m0; + u_int32_t rxts; + u_int8_t rssi; +{ + struct ieee80211_frame *wh; + struct awi_bss *bp; + u_int8_t *frame, *eframe; + u_int8_t *tstamp, *capinfo, *ssid, *rates, *parms; + u_int16_t bintval; + + if (sc->sc_status != AWI_ST_SCAN) + return; + wh = mtod(m0, struct ieee80211_frame *); + + frame = (u_int8_t *)&wh[1]; + eframe = mtod(m0, u_int8_t *) + m0->m_len; + /* + * XXX: + * timestamp [8] + * beacon interval [2] + * capability information [2] + * ssid [tlv] + * supported rates [tlv] + * parameter set [tlv] + * ... + */ + if (frame + 12 > eframe) { +#ifdef AWI_DEBUG + if (awi_verbose) + printf("awi_recv_beacon: frame too short \n"); +#endif + return; + } + tstamp = frame; + frame += 8; + bintval = LE_READ_2(frame); + frame += 2; + capinfo = frame; + frame += 2; + + if (sc->sc_mib_local.Network_Mode) { + if (!(capinfo[0] & IEEE80211_CAPINFO_ESS) || + (capinfo[0] & IEEE80211_CAPINFO_IBSS)) { +#ifdef AWI_DEBUG + if (awi_verbose) + printf("awi_recv_beacon: non ESS \n"); +#endif + return; + } + } else { + if ((capinfo[0] & IEEE80211_CAPINFO_ESS) || + !(capinfo[0] & IEEE80211_CAPINFO_IBSS)) { +#ifdef AWI_DEBUG + if (awi_verbose) + printf("awi_recv_beacon: non IBSS \n"); +#endif + return; + } + } + + ssid = rates = parms = NULL; + while (frame < eframe) { + switch (*frame) { + case IEEE80211_ELEMID_SSID: + ssid = frame; + break; + case IEEE80211_ELEMID_RATES: + rates = frame; + break; + case IEEE80211_ELEMID_FHPARMS: + case IEEE80211_ELEMID_DSPARMS: + parms = frame; + break; + } + frame += frame[1] + 2; + } + if (ssid == NULL || rates == NULL || parms == NULL) { +#ifdef AWI_DEBUG + if (awi_verbose) + printf("awi_recv_beacon: ssid=%p, rates=%p, parms=%p\n", + ssid, rates, parms); +#endif + return; + } + if (ssid[1] > IEEE80211_NWID_LEN) { +#ifdef AWI_DEBUG + if (awi_verbose) + printf("awi_recv_beacon: bad ssid len: %d from %s\n", + ssid[1], ether_sprintf(wh->i_addr2)); +#endif + return; + } + + for (bp = TAILQ_FIRST(&sc->sc_scan); bp != NULL; + bp = TAILQ_NEXT(bp, list)) { + if (memcmp(bp->esrc, wh->i_addr2, ETHER_ADDR_LEN) == 0 && + memcmp(bp->bssid, wh->i_addr3, ETHER_ADDR_LEN) == 0) + break; + } + if (bp == NULL) { + bp = malloc(sizeof(struct awi_bss), M_DEVBUF, M_NOWAIT); + if (bp == NULL) + return; + TAILQ_INSERT_TAIL(&sc->sc_scan, bp, list); + memcpy(bp->esrc, wh->i_addr2, ETHER_ADDR_LEN); + memcpy(bp->bssid, wh->i_addr3, ETHER_ADDR_LEN); + memset(bp->essid, 0, sizeof(bp->essid)); + memcpy(bp->essid, ssid, 2 + ssid[1]); + } + bp->rssi = rssi; + bp->rxtime = rxts; + memcpy(bp->timestamp, tstamp, sizeof(bp->timestamp)); + bp->interval = bintval; + if (sc->sc_mib_phy.IEEE_PHY_Type == AWI_PHY_TYPE_FH) { + bp->chanset = parms[4]; + bp->pattern = parms[5]; + bp->index = parms[6]; + bp->dwell_time = LE_READ_2(parms + 2); + } else { + bp->chanset = parms[2]; + bp->pattern = 0; + bp->index = 0; + bp->dwell_time = 0; + } + if (sc->sc_mgt_timer == 0) + awi_stop_scan(sc); +} + +static int +awi_set_ss(sc) + struct awi_softc *sc; +{ + struct ifnet *ifp = sc->sc_ifp; + struct awi_bss *bp; + int error; + + sc->sc_status = AWI_ST_SETSS; + bp = &sc->sc_bss; + if (ifp->if_flags & IFF_DEBUG) { + printf("%s: ch %d pat %d id %d dw %d iv %d bss %s ssid \"%s\"\n", + sc->sc_dev.dv_xname, bp->chanset, + bp->pattern, bp->index, bp->dwell_time, bp->interval, + ether_sprintf(bp->bssid), bp->essid + 2); + } + memcpy(&sc->sc_mib_mgt.aCurrent_BSS_ID, bp->bssid, ETHER_ADDR_LEN); + memcpy(&sc->sc_mib_mgt.aCurrent_ESS_ID, bp->essid, + AWI_ESS_ID_SIZE); + LE_WRITE_2(&sc->sc_mib_mgt.aBeacon_Period, bp->interval); + error = awi_mib(sc, AWI_CMD_SET_MIB, AWI_MIB_MGT); + return error; +} + +static void +awi_try_sync(sc) + struct awi_softc *sc; +{ + struct awi_bss *bp; + + sc->sc_status = AWI_ST_SYNC; + bp = &sc->sc_bss; + + if (sc->sc_cmd_inprog) { + if (awi_cmd_wait(sc)) + return; + } + sc->sc_cmd_inprog = 1; + awi_write_1(sc, AWI_CMD_PARAMS+AWI_CA_SYNC_SET, bp->chanset); + awi_write_1(sc, AWI_CMD_PARAMS+AWI_CA_SYNC_PATTERN, bp->pattern); + awi_write_1(sc, AWI_CMD_PARAMS+AWI_CA_SYNC_IDX, bp->index); + awi_write_1(sc, AWI_CMD_PARAMS+AWI_CA_SYNC_STARTBSS, + sc->sc_start_bss ? 1 : 0); + awi_write_2(sc, AWI_CMD_PARAMS+AWI_CA_SYNC_DWELL, bp->dwell_time); + awi_write_2(sc, AWI_CMD_PARAMS+AWI_CA_SYNC_MBZ, 0); + awi_write_bytes(sc, AWI_CMD_PARAMS+AWI_CA_SYNC_TIMESTAMP, + bp->timestamp, 8); + awi_write_4(sc, AWI_CMD_PARAMS+AWI_CA_SYNC_REFTIME, bp->rxtime); + (void)awi_cmd(sc, AWI_CMD_SYNC); +} + +static void +awi_sync_done(sc) + struct awi_softc *sc; +{ + struct ifnet *ifp = sc->sc_ifp; + + if (sc->sc_mib_local.Network_Mode) { + awi_drvstate(sc, AWI_DRV_INFSY); + awi_send_auth(sc); + } else { + printf("%s: synced with %s ssid \"%s\" at chanset %d\n", + sc->sc_dev.dv_xname, ether_sprintf(sc->sc_bss.bssid), + sc->sc_bss.essid + 2, sc->sc_bss.chanset); + awi_drvstate(sc, AWI_DRV_ADHSY); + sc->sc_status = AWI_ST_RUNNING; + ifp->if_flags |= IFF_RUNNING; + awi_start(ifp); + } +} + +static void +awi_send_deauth(sc) + struct awi_softc *sc; +{ + struct ifnet *ifp = sc->sc_ifp; + struct mbuf *m; + struct ieee80211_frame *wh; + u_int8_t *deauth; + + MGETHDR(m, M_DONTWAIT, MT_DATA); + if (m == NULL) + return; + if (ifp->if_flags & IFF_DEBUG) + printf("%s: sending deauth to %s\n", sc->sc_dev.dv_xname, + ether_sprintf(sc->sc_bss.bssid)); + + wh = mtod(m, struct ieee80211_frame *); + wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_MGT | + IEEE80211_FC0_SUBTYPE_AUTH; + wh->i_fc[1] = IEEE80211_FC1_DIR_NODS; + LE_WRITE_2(wh->i_dur, 0); + LE_WRITE_2(wh->i_seq, 0); + memcpy(wh->i_addr1, sc->sc_bss.bssid, ETHER_ADDR_LEN); + memcpy(wh->i_addr2, sc->sc_mib_addr.aMAC_Address, ETHER_ADDR_LEN); + memcpy(wh->i_addr3, sc->sc_bss.bssid, ETHER_ADDR_LEN); + + deauth = (u_int8_t *)&wh[1]; + LE_WRITE_2(deauth, IEEE80211_REASON_AUTH_LEAVE); + deauth += 2; + + m->m_pkthdr.len = m->m_len = deauth - mtod(m, u_int8_t *); + IF_ENQUEUE(&sc->sc_mgtq, m); + awi_start(ifp); + awi_drvstate(sc, AWI_DRV_INFTOSS); +} + +static void +awi_send_auth(sc) + struct awi_softc *sc; +{ + struct ifnet *ifp = sc->sc_ifp; + struct mbuf *m; + struct ieee80211_frame *wh; + u_int8_t *auth; + + MGETHDR(m, M_DONTWAIT, MT_DATA); + if (m == NULL) + return; + sc->sc_status = AWI_ST_AUTH; + if (ifp->if_flags & IFF_DEBUG) + printf("%s: sending auth to %s\n", sc->sc_dev.dv_xname, + ether_sprintf(sc->sc_bss.bssid)); + + wh = mtod(m, struct ieee80211_frame *); + wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_MGT | + IEEE80211_FC0_SUBTYPE_AUTH; + wh->i_fc[1] = IEEE80211_FC1_DIR_NODS; + LE_WRITE_2(wh->i_dur, 0); + LE_WRITE_2(wh->i_seq, 0); + memcpy(wh->i_addr1, sc->sc_bss.bssid, ETHER_ADDR_LEN); + memcpy(wh->i_addr2, sc->sc_mib_addr.aMAC_Address, ETHER_ADDR_LEN); + memcpy(wh->i_addr3, sc->sc_bss.bssid, ETHER_ADDR_LEN); + + auth = (u_int8_t *)&wh[1]; + /* algorithm number */ + LE_WRITE_2(auth, IEEE80211_AUTH_ALG_OPEN); + auth += 2; + /* sequence number */ + LE_WRITE_2(auth, 1); + auth += 2; + /* status */ + LE_WRITE_2(auth, 0); + auth += 2; + + m->m_pkthdr.len = m->m_len = auth - mtod(m, u_int8_t *); + IF_ENQUEUE(&sc->sc_mgtq, m); + awi_start(ifp); + + sc->sc_mgt_timer = AWI_TRANS_TIMEOUT / 1000; + ifp->if_timer = 1; +} + +static void +awi_recv_auth(sc, m0) + struct awi_softc *sc; + struct mbuf *m0; +{ + struct ieee80211_frame *wh; + u_int8_t *auth, *eframe; + struct awi_bss *bp; + u_int16_t status; + + wh = mtod(m0, struct ieee80211_frame *); + auth = (u_int8_t *)&wh[1]; + eframe = mtod(m0, u_int8_t *) + m0->m_len; + if (sc->sc_ifp->if_flags & IFF_DEBUG) + printf("%s: receive auth from %s\n", sc->sc_dev.dv_xname, + ether_sprintf(wh->i_addr2)); + + if (!sc->sc_mib_local.Network_Mode) { + /* XXX: 802.11 allow auth for IBSS */ + return; + } + if (sc->sc_status != AWI_ST_AUTH) + return; + /* algorithm number */ + if (LE_READ_2(auth) != IEEE80211_AUTH_ALG_OPEN) + return; + auth += 2; + /* sequence number */ + if (LE_READ_2(auth) != 2) + return; + auth += 2; + /* status */ + status = LE_READ_2(auth); + if (status != 0) { + printf("%s: authentication failed (reason %d)\n", + sc->sc_dev.dv_xname, status); + for (bp = TAILQ_FIRST(&sc->sc_scan); bp != NULL; + bp = TAILQ_NEXT(bp, list)) { + if (memcmp(bp->esrc, sc->sc_bss.esrc, ETHER_ADDR_LEN) + == 0) { + bp->fails++; + break; + } + } + return; + } + sc->sc_mgt_timer = 0; + awi_drvstate(sc, AWI_DRV_INFAUTH); + awi_send_asreq(sc, 0); +} + +static void +awi_send_asreq(sc, reassoc) + struct awi_softc *sc; + int reassoc; +{ + struct ifnet *ifp = sc->sc_ifp; + struct mbuf *m; + struct ieee80211_frame *wh; + u_int16_t lintval; + u_int8_t *asreq; + + MGETHDR(m, M_DONTWAIT, MT_DATA); + if (m == NULL) + return; + sc->sc_status = AWI_ST_ASSOC; + if (ifp->if_flags & IFF_DEBUG) + printf("%s: sending %sassoc req to %s\n", sc->sc_dev.dv_xname, + reassoc ? "re" : "", + ether_sprintf(sc->sc_bss.bssid)); + + wh = mtod(m, struct ieee80211_frame *); + wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_MGT; + if (reassoc) + wh->i_fc[0] |= IEEE80211_FC0_SUBTYPE_REASSOC_REQ; + else + wh->i_fc[0] |= IEEE80211_FC0_SUBTYPE_ASSOC_REQ; + wh->i_fc[1] = IEEE80211_FC1_DIR_NODS; + LE_WRITE_2(wh->i_dur, 0); + LE_WRITE_2(wh->i_seq, 0); + memcpy(wh->i_addr1, sc->sc_bss.bssid, ETHER_ADDR_LEN); + memcpy(wh->i_addr2, sc->sc_mib_addr.aMAC_Address, ETHER_ADDR_LEN); + memcpy(wh->i_addr3, sc->sc_bss.bssid, ETHER_ADDR_LEN); + + asreq = (u_int8_t *)&wh[1]; + + /* capability info */ + LE_WRITE_2(asreq, IEEE80211_CAPINFO_CF_POLLABLE); + asreq += 2; + /* listen interval */ + lintval = LE_READ_2(&sc->sc_mib_mgt.aListen_Interval); + LE_WRITE_2(asreq, lintval); + asreq += 2; + if (reassoc) { + /* current AP address */ + memcpy(asreq, sc->sc_bss.bssid, ETHER_ADDR_LEN); + asreq += ETHER_ADDR_LEN; + } + /* ssid */ + memcpy(asreq, sc->sc_bss.essid, 2 + sc->sc_bss.essid[1]); + asreq += 2 + asreq[1]; + /* supported rates */ + memcpy(asreq, &sc->sc_mib_phy.aSuprt_Data_Rates, 4); + asreq += 2 + asreq[1]; + + m->m_pkthdr.len = m->m_len = asreq - mtod(m, u_int8_t *); + IF_ENQUEUE(&sc->sc_mgtq, m); + awi_start(ifp); + + sc->sc_mgt_timer = AWI_TRANS_TIMEOUT / 1000; + ifp->if_timer = 1; +} + +static void +awi_recv_asresp(sc, m0) + struct awi_softc *sc; + struct mbuf *m0; +{ + struct ieee80211_frame *wh; + u_int8_t *asresp, *eframe; + u_int16_t status; + u_int8_t rate, *phy_rates; + struct awi_bss *bp; + int i, j; + + wh = mtod(m0, struct ieee80211_frame *); + asresp = (u_int8_t *)&wh[1]; + eframe = mtod(m0, u_int8_t *) + m0->m_len; + if (sc->sc_ifp->if_flags & IFF_DEBUG) + printf("%s: receive assoc resp from %s\n", sc->sc_dev.dv_xname, + ether_sprintf(wh->i_addr2)); + + if (!sc->sc_mib_local.Network_Mode) + return; + + if (sc->sc_status != AWI_ST_ASSOC) + return; + /* capability info */ + asresp += 2; + /* status */ + status = LE_READ_2(asresp); + if (status != 0) { + printf("%s: association failed (reason %d)\n", + sc->sc_dev.dv_xname, status); + for (bp = TAILQ_FIRST(&sc->sc_scan); bp != NULL; + bp = TAILQ_NEXT(bp, list)) { + if (memcmp(bp->esrc, sc->sc_bss.esrc, ETHER_ADDR_LEN) + == 0) { + bp->fails++; + break; + } + } + return; + } + asresp += 2; + /* association id */ + asresp += 2; + /* supported rates */ + rate = AWI_RATE_1MBIT; + for (i = 0; i < asresp[1]; i++) { + if (AWI_80211_RATE(asresp[2 + i]) <= rate) + continue; + phy_rates = sc->sc_mib_phy.aSuprt_Data_Rates; + for (j = 0; j < phy_rates[1]; j++) { + if (AWI_80211_RATE(asresp[2 + i]) == + AWI_80211_RATE(phy_rates[2 + j])) + rate = AWI_80211_RATE(asresp[2 + i]); + } + } + printf("%s: associated with %s ssid \"%s\"", + sc->sc_dev.dv_xname, ether_sprintf(sc->sc_bss.bssid), + sc->sc_bss.essid + 2); + if (sc->sc_mib_phy.IEEE_PHY_Type == AWI_PHY_TYPE_FH) + printf(" chanset %d pattern %d", + sc->sc_bss.chanset, sc->sc_bss.pattern); + else + printf(" channel %d", sc->sc_bss.chanset); + printf(" signal %d\n", sc->sc_bss.rssi); + sc->sc_tx_rate = rate; + sc->sc_mgt_timer = 0; + sc->sc_rx_timer = 10; + sc->sc_ifp->if_timer = 1; + sc->sc_status = AWI_ST_RUNNING; + sc->sc_ifp->if_flags |= IFF_RUNNING; + awi_drvstate(sc, AWI_DRV_INFASSOC); + while ((bp = TAILQ_FIRST(&sc->sc_scan)) != NULL) + TAILQ_REMOVE(&sc->sc_scan, bp, list); + awi_start(sc->sc_ifp); +} + +static int +awi_mib(sc, cmd, mib) + struct awi_softc *sc; + u_int8_t cmd; + u_int8_t mib; +{ + int error; + u_int8_t size, *ptr; + + switch (mib) { + case AWI_MIB_LOCAL: + ptr = (u_int8_t *)&sc->sc_mib_local; + size = sizeof(sc->sc_mib_local); + break; + case AWI_MIB_ADDR: + ptr = (u_int8_t *)&sc->sc_mib_addr; + size = sizeof(sc->sc_mib_addr); + break; + case AWI_MIB_MAC: + ptr = (u_int8_t *)&sc->sc_mib_mac; + size = sizeof(sc->sc_mib_mac); + break; + case AWI_MIB_STAT: + ptr = (u_int8_t *)&sc->sc_mib_stat; + size = sizeof(sc->sc_mib_stat); + break; + case AWI_MIB_MGT: + ptr = (u_int8_t *)&sc->sc_mib_mgt; + size = sizeof(sc->sc_mib_mgt); + break; + case AWI_MIB_PHY: + ptr = (u_int8_t *)&sc->sc_mib_phy; + size = sizeof(sc->sc_mib_phy); + break; + default: + return EINVAL; + } + if (sc->sc_cmd_inprog) { + error = awi_cmd_wait(sc); + if (error) { + printf("awi_mib: cmd %d inprog\n", + awi_read_1(sc, AWI_CMD)); + return error; + } + } + sc->sc_cmd_inprog = 1; + if (cmd == AWI_CMD_SET_MIB) + awi_write_bytes(sc, AWI_CMD_PARAMS+AWI_CA_MIB_DATA, ptr, size); + awi_write_1(sc, AWI_CMD_PARAMS+AWI_CA_MIB_TYPE, mib); + awi_write_1(sc, AWI_CMD_PARAMS+AWI_CA_MIB_SIZE, size); + awi_write_1(sc, AWI_CMD_PARAMS+AWI_CA_MIB_INDEX, 0); + error = awi_cmd(sc, cmd); + if (error) + return error; + if (cmd == AWI_CMD_GET_MIB) { + awi_read_bytes(sc, AWI_CMD_PARAMS+AWI_CA_MIB_DATA, ptr, size); +#ifdef AWI_DEBUG + if (awi_verbose) { + int i; + + printf("awi_mib: #%d:", mib); + for (i = 0; i < size; i++) + printf(" %02x", ptr[i]); + printf("\n"); + } +#endif + } + return 0; +} + +static int +awi_cmd_scan(sc) + struct awi_softc *sc; +{ + int error; + u_int8_t scan_mode; + + if (sc->sc_active_scan) + scan_mode = AWI_SCAN_ACTIVE; + else + scan_mode = AWI_SCAN_PASSIVE; + if (sc->sc_mib_mgt.aScan_Mode != scan_mode) { + sc->sc_mib_mgt.aScan_Mode = scan_mode; + error = awi_mib(sc, AWI_CMD_SET_MIB, AWI_MIB_MGT); + return error; + } + + if (sc->sc_cmd_inprog) { + error = awi_cmd_wait(sc); + if (error) + return error; + } + sc->sc_cmd_inprog = 1; + awi_write_2(sc, AWI_CMD_PARAMS+AWI_CA_SCAN_DURATION, + sc->sc_active_scan ? AWI_ASCAN_DURATION : AWI_PSCAN_DURATION); + if (sc->sc_mib_phy.IEEE_PHY_Type == AWI_PHY_TYPE_FH) { + awi_write_1(sc, AWI_CMD_PARAMS+AWI_CA_SCAN_SET, + sc->sc_scan_set); + awi_write_1(sc, AWI_CMD_PARAMS+AWI_CA_SCAN_PATTERN, + sc->sc_scan_cur); + awi_write_1(sc, AWI_CMD_PARAMS+AWI_CA_SCAN_IDX, 1); + } else { + awi_write_1(sc, AWI_CMD_PARAMS+AWI_CA_SCAN_SET, + sc->sc_scan_cur); + awi_write_1(sc, AWI_CMD_PARAMS+AWI_CA_SCAN_PATTERN, 0); + awi_write_1(sc, AWI_CMD_PARAMS+AWI_CA_SCAN_IDX, 0); + } + awi_write_1(sc, AWI_CMD_PARAMS+AWI_CA_SCAN_SUSP, 0); + return awi_cmd(sc, AWI_CMD_SCAN); +} + +static int +awi_cmd(sc, cmd) + struct awi_softc *sc; + u_int8_t cmd; +{ + u_int8_t status; + int error = 0; + + sc->sc_cmd_inprog = 1; + awi_write_1(sc, AWI_CMD_STATUS, AWI_STAT_IDLE); + awi_write_1(sc, AWI_CMD, cmd); + if (sc->sc_status != AWI_ST_INIT) + return 0; + error = awi_cmd_wait(sc); + if (error) + return error; + status = awi_read_1(sc, AWI_CMD_STATUS); + awi_write_1(sc, AWI_CMD, 0); + switch (status) { + case AWI_STAT_OK: + break; + case AWI_STAT_BADPARM: + return EINVAL; + default: + printf("%s: command %d failed %x\n", + sc->sc_dev.dv_xname, cmd, status); + return ENXIO; + } + return 0; +} + +static void +awi_cmd_done(sc) + struct awi_softc *sc; +{ + u_int8_t cmd, status; + + status = awi_read_1(sc, AWI_CMD_STATUS); + if (status == AWI_STAT_IDLE) + return; /* stray interrupt */ + + sc->sc_cmd_inprog = 0; + if (sc->sc_status == AWI_ST_INIT) { + wakeup(sc); + return; + } + cmd = awi_read_1(sc, AWI_CMD); + awi_write_1(sc, AWI_CMD, 0); + + if (status != AWI_STAT_OK) { + printf("%s: command %d failed %x\n", + sc->sc_dev.dv_xname, cmd, status); + return; + } + switch (sc->sc_status) { + case AWI_ST_SCAN: + if (cmd == AWI_CMD_SET_MIB) + awi_cmd_scan(sc); /* retry */ + break; + case AWI_ST_SETSS: + awi_try_sync(sc); + break; + case AWI_ST_SYNC: + awi_sync_done(sc); + break; + default: + break; + } +} + +static int +awi_next_txd(sc, len, framep, ntxdp) + struct awi_softc *sc; + int len; + u_int32_t *framep, *ntxdp; +{ + u_int32_t txd, ntxd, frame; + + txd = sc->sc_txnext; + frame = txd + AWI_TXD_SIZE; + if (frame + len > sc->sc_txend) + frame = sc->sc_txbase; + ntxd = frame + len; + if (ntxd + AWI_TXD_SIZE > sc->sc_txend) + ntxd = sc->sc_txbase; + *framep = frame; + *ntxdp = ntxd; + /* + * Determine if there are any room in ring buffer. + * --- send wait, === new data, +++ conflict (ENOBUFS) + * base........................end + * done----txd=====ntxd OK + * --txd=====done++++ntxd-- full + * --txd=====ntxd done-- OK + * ==ntxd done----txd=== OK + * ==done++++ntxd----txd=== full + * ++ntxd txd=====done++ full + */ + if (txd < ntxd) { + if (txd < sc->sc_txdone && ntxd + AWI_TXD_SIZE > sc->sc_txdone) + return ENOBUFS; + } else { + if (txd < sc->sc_txdone || ntxd + AWI_TXD_SIZE > sc->sc_txdone) + return ENOBUFS; + } + return 0; +} + +static int +awi_lock(sc) + struct awi_softc *sc; +{ + int error = 0; + + if (sc->sc_invalid) + return ENXIO; + if (curproc == NULL) { + /* + * XXX + * Though driver ioctl should be called with context, + * KAME ipv6 stack calls ioctl in interrupt for now. + * We simply abort the request if there are other + * ioctl requests in progress. + */ + if (sc->sc_busy) + return EWOULDBLOCK; + sc->sc_busy = 1; + sc->sc_cansleep = 0; + return 0; + } + while (sc->sc_busy) { + sc->sc_sleep_cnt++; + error = tsleep(sc, PWAIT | PCATCH, "awilck", 0); + sc->sc_sleep_cnt--; + if (error) + return error; + if (sc->sc_invalid) + return ENXIO; + } + sc->sc_busy = 1; + sc->sc_cansleep = 1; + return 0; +} + +static void +awi_unlock(sc) + struct awi_softc *sc; +{ + sc->sc_busy = 0; + sc->sc_cansleep = 0; + if (sc->sc_sleep_cnt) + wakeup(sc); +} + +static int +awi_intr_lock(sc) + struct awi_softc *sc; +{ + u_int8_t status; + int i, retry; + + status = 1; + for (retry = 0; retry < 10; retry++) { + for (i = 0; i < AWI_LOCKOUT_TIMEOUT*1000/5; i++) { + status = awi_read_1(sc, AWI_LOCKOUT_HOST); + if (status == 0) + break; + DELAY(5); + } + if (status != 0) + break; + awi_write_1(sc, AWI_LOCKOUT_MAC, 1); + status = awi_read_1(sc, AWI_LOCKOUT_HOST); + if (status == 0) + break; + awi_write_1(sc, AWI_LOCKOUT_MAC, 0); + } + if (status != 0) { + printf("%s: failed to lock interrupt\n", + sc->sc_dev.dv_xname); + return ENXIO; + } + return 0; +} + +static void +awi_intr_unlock(sc) + struct awi_softc *sc; +{ + + awi_write_1(sc, AWI_LOCKOUT_MAC, 0); +} + +static int +awi_cmd_wait(sc) + struct awi_softc *sc; +{ + int i, error = 0; + + i = 0; + while (sc->sc_cmd_inprog) { + if (sc->sc_invalid) + return ENXIO; + if (sc->sc_cansleep) { + sc->sc_sleep_cnt++; + error = tsleep(sc, PWAIT, "awicmd", + AWI_CMD_TIMEOUT*hz/1000); + sc->sc_sleep_cnt--; + } else { + if (awi_read_1(sc, AWI_CMD_STATUS) != AWI_STAT_IDLE) { + awi_cmd_done(sc); + break; + } + if (i++ >= AWI_CMD_TIMEOUT*1000/10) + error = EWOULDBLOCK; + else + DELAY(10); + } + if (error) + break; + } + return error; +} + +#ifdef AWI_DEBUG +static void +awi_dump_pkt(sc, m, rssi) + struct awi_softc *sc; + struct mbuf *m; + u_int8_t rssi; +{ + struct ieee80211_frame *wh; + int i, l; + + wh = mtod(m, struct ieee80211_frame *); + + if (awi_dump_mask != 0 && + ((wh->i_fc[1] & IEEE80211_FC1_DIR_MASK)==IEEE80211_FC1_DIR_NODS) && + ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK)==IEEE80211_FC0_TYPE_MGT)) { + if ((AWI_DUMP_MASK(wh->i_fc[0]) & awi_dump_mask) != 0) + return; + } + if (awi_dump_mask < 0 && + (wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK)==IEEE80211_FC0_TYPE_DATA) + return; + + switch (wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) { + case IEEE80211_FC1_DIR_NODS: + printf("rx: NODS %s", ether_sprintf(wh->i_addr2)); + printf("->%s", ether_sprintf(wh->i_addr1)); + printf("(%s)", ether_sprintf(wh->i_addr3)); + break; + case IEEE80211_FC1_DIR_TODS: + printf("rx: TODS %s", ether_sprintf(wh->i_addr2)); + printf("->%s", ether_sprintf(wh->i_addr3)); + printf("(%s)", ether_sprintf(wh->i_addr1)); + break; + case IEEE80211_FC1_DIR_FROMDS: + printf("rx: FRDS %s", ether_sprintf(wh->i_addr3)); + printf("->%s", ether_sprintf(wh->i_addr1)); + printf("(%s)", ether_sprintf(wh->i_addr2)); + break; + case IEEE80211_FC1_DIR_DSTODS: + printf("rx: DSDS %s", ether_sprintf((u_int8_t *)&wh[1])); + printf("->%s", ether_sprintf(wh->i_addr3)); + printf("(%s", ether_sprintf(wh->i_addr2)); + printf("->%s)", ether_sprintf(wh->i_addr1)); + break; + } + switch (wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) { + case IEEE80211_FC0_TYPE_DATA: + printf(" data"); + break; + case IEEE80211_FC0_TYPE_MGT: + switch (wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) { + case IEEE80211_FC0_SUBTYPE_PROBE_REQ: + printf(" probe_req"); + break; + case IEEE80211_FC0_SUBTYPE_PROBE_RESP: + printf(" probe_resp"); + break; + case IEEE80211_FC0_SUBTYPE_BEACON: + printf(" beacon"); + break; + case IEEE80211_FC0_SUBTYPE_AUTH: + printf(" auth"); + break; + case IEEE80211_FC0_SUBTYPE_ASSOC_REQ: + printf(" assoc_req"); + break; + case IEEE80211_FC0_SUBTYPE_ASSOC_RESP: + printf(" assoc_resp"); + break; + case IEEE80211_FC0_SUBTYPE_REASSOC_REQ: + printf(" reassoc_req"); + break; + case IEEE80211_FC0_SUBTYPE_REASSOC_RESP: + printf(" reassoc_resp"); + break; + case IEEE80211_FC0_SUBTYPE_DEAUTH: + printf(" deauth"); + break; + case IEEE80211_FC0_SUBTYPE_DISASSOC: + printf(" disassoc"); + break; + default: + printf(" mgt#%d", + wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK); + break; + } + break; + default: + printf(" type#%d", + wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK); + break; + } + printf(" +%d\n", rssi); + if (awi_dump_len > 0) { + l = m->m_len; + if (l > awi_dump_len + sizeof(*wh)) + l = awi_dump_len + sizeof(*wh); + i = sizeof(*wh); + if (awi_dump_hdr) + i = 0; + for (; i < l; i++) { + if ((i & 1) == 0) + printf(" "); + printf("%02x", mtod(m, u_int8_t *)[i]); + } + printf("\n"); + } +} +#endif diff --git a/sys/dev/awi/awictl.h b/sys/dev/awi/awictl.h new file mode 100644 index 0000000000000..c765377c73ea0 --- /dev/null +++ b/sys/dev/awi/awictl.h @@ -0,0 +1,72 @@ +/* $NetBSD: awictl.h,v 1.1 2000/03/23 06:04:24 onoe Exp $ */ +/* $FreeBSD$ */ + +/* + * Copyright (c) 2000 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Atsushi Onoe + * + * 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. + */ + +#ifndef _IC_AWICTL_H +#define _IC_AWICTL_H + +#define AWICTL_BUFSIZE 32 + +#define AWICTL_REGION 1 /* u_int8_t: region code */ +#define AWICTL_CHANSET 2 /* u_int8_t[3]: cur, min, max */ +#define AWICTL_RAWBPF 3 /* u_int8_t: pass raw 802.11 header to bpf */ +#define AWICTL_DESSID 4 /* u_int8_t[IEEE80211_NWID_LEN]: desired ESSID*/ +#define AWICTL_CESSID 5 /* u_int8_t[IEEE80211_NWID_LEN]: current ESSID*/ +#define AWICTL_MODE 6 /* u_int8_t: mode */ +#define AWICTL_MODE_INFRA 0 /* infrastructure mode */ +#define AWICTL_MODE_ADHOC 1 /* adhoc mode */ +#define AWICTL_MODE_NOBSSID 2 /* adhoc without bssid mode */ + +#ifndef SIOCSDRVSPEC +#define SIOCSDRVSPEC _IOW('i', 123, struct ifdrv) +#endif + +#ifndef SIOCGDRVSPEC +#define SIOCGDRVSPEC _IOWR('i', 122, struct ifdrv) +#endif + +#ifdef __FreeBSD__ +struct ifdrv { + char ifd_name[IFNAMSIZ]; /* if name, e.g. "en0" */ + unsigned long ifd_cmd; + size_t ifd_len; + void *ifd_data; +}; +#endif + +#endif /* !_IC_AWICTL_H */ diff --git a/sys/dev/awi/awireg.h b/sys/dev/awi/awireg.h new file mode 100644 index 0000000000000..dc936eb730ec3 --- /dev/null +++ b/sys/dev/awi/awireg.h @@ -0,0 +1,460 @@ +/* $NetBSD: awireg.h,v 1.3 2000/03/22 11:22:22 onoe Exp $ */ +/* $FreeBSD$ */ + +/*- + * Copyright (c) 1999 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Bill Sommerfeld + * + * 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. + */ + +/* + * The firmware typically loaded onto Am79C930-based 802.11 interfaces + * uses a 32k or larger shared memory buffer to communicate with the + * host. + * + * Depending on the exact configuration of the device, this buffer may + * either be mapped into PCMCIA memory space, or accessible a byte at + * a type through PCMCIA I/O space. + * + * This header defines offsets into this shared memory. + */ + + +/* + * LAST_TXD block. 5 32-bit words. + * + * There are five different output queues; this defines pointers to + * the last completed descriptor for each one. + */ +#define AWI_LAST_TXD 0x3ec /* last completed Tx Descr */ + +#define AWI_LAST_BCAST_TXD AWI_LAST_TXD+0 +#define AWI_LAST_MGT_TXD AWI_LAST_TXD+4 +#define AWI_LAST_DATA_TXD AWI_LAST_TXD+8 +#define AWI_LAST_PS_POLL_TXD AWI_LAST_TXD+12 +#define AWI_LAST_CF_POLL_TXD AWI_LAST_TXD+16 + +/* + * Banner block; null-terminated string. + * + * The doc says it contains + * "PCnetMobile:v2.00 mmddyy APIx.x\0" + */ + +#define AWI_BANNER 0x480 /* Version string */ +#define AWI_BANNER_LEN 0x20 + +/* + * Command block protocol: + * write command byte to a zero value. + * write command status to a zero value. + * write arguments to AWI_COMMAND_PARAMS + * write command byte to a non-zero value. + * wait for command status to be non-zero. + * write command byte to a zero value. + * write command status to a zero value. + */ + +#define AWI_CMD 0x4a0 /* Command opcode byte */ + +#define AWI_CMD_IDLE 0x0 +#define AWI_CMD_NOP 0x1 + +#define AWI_CMD_SET_MIB 0x2 +#define AWI_CMD_GET_MIB 0x9 + +#define AWI_CA_MIB_TYPE 0x0 +#define AWI_CA_MIB_SIZE 0x1 +#define AWI_CA_MIB_INDEX 0x2 +#define AWI_CA_MIB_DATA 0x4 + +#define AWI_MIB_LOCAL 0x0 +#define AWI_MIB_ADDR 0x2 +#define AWI_MIB_MAC 0x3 +#define AWI_MIB_STAT 0x4 +#define AWI_MIB_MGT 0x5 +#define AWI_MIB_DRVR 0x6 +#define AWI_MIB_PHY 0x7 + + +#define AWI_CMD_INIT_TX 0x3 + +#define AWI_CA_TX_LEN 0x14 +#define AWI_CA_TX_DATA 0x0 +#define AWI_CA_TX_MGT 0x4 +#define AWI_CA_TX_BCAST 0x8 +#define AWI_CA_TX_PS 0xc +#define AWI_CA_TX_CF 0x10 + +#define AWI_CMD_FLUSH_TX 0x4 + +#define AWI_CA_FTX_LEN 0x5 +#define AWI_CA_FTX_DATA 0x0 +#define AWI_CA_FTX_MGT 0x1 +#define AWI_CA_FTX_BCAST 0x2 +#define AWI_CA_FTX_PS 0x3 +#define AWI_CA_FTX_CF 0x4 + +#define AWI_CMD_INIT_RX 0x5 +#define AWI_CA_IRX_LEN 0x8 +#define AWI_CA_IRX_DATA_DESC 0x0 /* return */ +#define AWI_CA_IRX_PS_DESC 0x4 /* return */ + +#define AWI_CMD_KILL_RX 0x6 + +#define AWI_CMD_SLEEP 0x7 +#define AWI_CA_SLEEP_LEN 0x8 +#define AWI_CA_WAKEUP 0x0 /* uint64 */ + +#define AWI_CMD_WAKE 0x8 + +#define AWI_CMD_SCAN 0xa +#define AWI_CA_SCAN_LEN 0x6 +#define AWI_CA_SCAN_DURATION 0x0 +#define AWI_CA_SCAN_SET 0x2 +#define AWI_CA_SCAN_PATTERN 0x3 +#define AWI_CA_SCAN_IDX 0x4 +#define AWI_CA_SCAN_SUSP 0x5 + +#define AWI_CMD_SYNC 0xb +#define AWI_CA_SYNC_LEN 0x14 +#define AWI_CA_SYNC_SET 0x0 +#define AWI_CA_SYNC_PATTERN 0x1 +#define AWI_CA_SYNC_IDX 0x2 +#define AWI_CA_SYNC_STARTBSS 0x3 +#define AWI_CA_SYNC_DWELL 0x4 +#define AWI_CA_SYNC_MBZ 0x6 +#define AWI_CA_SYNC_TIMESTAMP 0x8 +#define AWI_CA_SYNC_REFTIME 0x10 + +#define AWI_CMD_RESUME 0xc + +#define AWI_CMD_STATUS 0x4a1 /* Command status */ + +#define AWI_STAT_IDLE 0x0 +#define AWI_STAT_OK 0x1 +#define AWI_STAT_BADCMD 0x2 +#define AWI_STAT_BADPARM 0x3 +#define AWI_STAT_NOTIMP 0x4 +#define AWI_STAT_BADRES 0x5 +#define AWI_STAT_BADMODE 0x6 + +#define AWI_ERROR_OFFSET 0x4a2 /* Offset to erroneous parameter */ +#define AWI_CMD_PARAMS 0x4a4 /* Command parameters */ + +#define AWI_CSB 0x4f0 /* Control/Status block */ + +#define AWI_SELFTEST 0x4f0 + +#define AWI_SELFTEST_INIT 0x00 /* initial */ +#define AWI_SELFTEST_FIRMCKSUM 0x01 /* firmware cksum running */ +#define AWI_SELFTEST_HARDWARE 0x02 /* hardware tests running */ +#define AWI_SELFTEST_MIB 0x03 /* mib initializing */ + +#define AWI_SELFTEST_MIB_FAIL 0xfa +#define AWI_SELFTEST_RADIO_FAIL 0xfb +#define AWI_SELFTEST_MAC_FAIL 0xfc +#define AWI_SELFTEST_FLASH_FAIL 0xfd +#define AWI_SELFTEST_RAM_FAIL 0xfe +#define AWI_SELFTEST_PASSED 0xff + +#define AWI_STA_STATE 0x4f1 + +#define AWI_STA_AP 0x20 /* acting as AP */ +#define AWI_STA_NOPSP 0x10 /* Power Saving disabled */ +#define AWI_STA_DOZE 0x08 /* about to go to sleep */ +#define AWI_STA_PSP 0x04 /* enable PSP */ +#define AWI_STA_RXEN 0x02 /* enable RX */ +#define AWI_STA_TXEN 0x01 /* enable TX */ + +#define AWI_INTSTAT 0x4f3 +#define AWI_INTMASK 0x4f4 + +/* Bits in AWI_INTSTAT/AWI_INTMASK */ + +#define AWI_INT_GROGGY 0x80 /* about to wake up */ +#define AWI_INT_CFP_ENDING 0x40 /* cont. free period ending */ +#define AWI_INT_DTIM 0x20 /* beacon outgoing */ +#define AWI_INT_CFP_START 0x10 /* cont. free period starting */ +#define AWI_INT_SCAN_CMPLT 0x08 /* scan complete */ +#define AWI_INT_TX 0x04 /* tx done */ +#define AWI_INT_RX 0x02 /* rx done */ +#define AWI_INT_CMD 0x01 /* cmd done */ + +/* + * The following are used to implement a locking protocol between host + * and MAC to protect the interrupt status and mask fields. + * + * driver: read lockout_host byte; if zero, set lockout_mac to non-zero, + * then reread lockout_host byte; if still zero, host has lock. + * if non-zero, clear lockout_mac, loop. + */ + +#define AWI_LOCKOUT_MAC 0x4f5 +#define AWI_LOCKOUT_HOST 0x4f6 + + +#define AWI_INTSTAT2 0x4f7 +#define AWI_INTMASK2 0x4fd + +/* Bits in AWI_INTSTAT2/INTMASK2 */ +#define AWI_INT2_RXMGT 0x80 /* mgt/ps recieved */ +#define AWI_INT2_RXDATA 0x40 /* data received */ +#define AWI_INT2_TXMGT 0x10 /* mgt tx done */ +#define AWI_INT2_TXCF 0x08 /* CF tx done */ +#define AWI_INT2_TXPS 0x04 /* PS tx done */ +#define AWI_INT2_TXBCAST 0x02 /* Broadcast tx done */ +#define AWI_INT2_TXDATA 0x01 /* data tx done */ + +#define AWI_DIS_PWRDN 0x4fc /* disable powerdown if set */ + +#define AWI_DRIVERSTATE 0x4fe /* driver state */ + +#define AWI_DRV_STATEMASK 0x0f + +#define AWI_DRV_RESET 0x0 +#define AWI_DRV_INFSY 0x1 /* inf synced */ +#define AWI_DRV_ADHSC 0x2 /* adhoc scan */ +#define AWI_DRV_ADHSY 0x3 /* adhoc synced */ +#define AWI_DRV_INFSC 0x4 /* inf scanning */ +#define AWI_DRV_INFAUTH 0x5 /* inf authed */ +#define AWI_DRV_INFASSOC 0x6 /* inf associated */ +#define AWI_DRV_INFTOSS 0x7 /* inf handoff */ +#define AWI_DRV_APNONE 0x8 /* AP activity: no assoc */ +#define AWI_DRV_APQUIET 0xc /* AP: >=one assoc, no traffic */ +#define AWI_DRV_APLO 0xd /* AP: >=one assoc, light tfc */ +#define AWI_DRV_APMED 0xe /* AP: >=one assoc, mod tfc */ +#define AWI_DRV_APHIGH 0xf /* AP: >=one assoc, heavy tfc */ + +#define AWI_DRV_AUTORXLED 0x10 +#define AWI_DRV_AUTOTXLED 0x20 +#define AWI_DRV_RXLED 0x40 +#define AWI_DRV_TXLED 0x80 + +#define AWI_VBM 0x500 /* Virtual Bit Map */ + +#define AWI_BUFFERS 0x600 /* Buffers */ +#define AWI_BUFFERS_END 0x6000 + +/* + * Receive descriptors; there are a linked list of these chained + * through the "NEXT" fields, starting from XXX + */ + +#define AWI_RXD_SIZE 0x18 + +#define AWI_RXD_NEXT 0x4 +#define AWI_RXD_NEXT_LAST 0x80000000 + + +#define AWI_RXD_HOST_DESC_STATE 0x9 + +#define AWI_RXD_ST_OWN 0x80 /* host owns this */ +#define AWI_RXD_ST_CONSUMED 0x40 /* host is done */ +#define AWI_RXD_ST_LF 0x20 /* last frag */ +#define AWI_RXD_ST_CRC 0x08 /* CRC error */ +#define AWI_RXD_ST_OFLO 0x02 /* possible buffer overrun */ +#define AWI_RXD_ST_RXERROR 0x01 /* this frame is borked; discard me */ + +#define AWI_RXD_RSSI 0xa /* 1 byte: radio strength indicator */ +#define AWI_RXD_INDEX 0xb /* 1 byte: FH hop index or DS channel */ +#define AWI_RXD_LOCALTIME 0xc /* 4 bytes: local time of RX */ +#define AWI_RXD_START_FRAME 0x10 /* 4 bytes: ptr to first received byte */ +#define AWI_RXD_LEN 0x14 /* 2 bytes: rx len in bytes */ +#define AWI_RXD_RATE 0x16 /* 1 byte: rx rate in 1e5 bps */ + +/* + * Transmit descriptors. + */ + +#define AWI_TXD_SIZE 0x18 + +#define AWI_TXD_START 0x00 /* pointer to start of frame */ +#define AWI_TXD_NEXT 0x04 /* pointer to next TXD */ +#define AWI_TXD_LENGTH 0x08 /* length of frame */ +#define AWI_TXD_STATE 0x0a /* state */ + +#define AWI_TXD_ST_OWN 0x80 /* MAC owns this */ +#define AWI_TXD_ST_DONE 0x40 /* MAC is done */ +#define AWI_TXD_ST_REJ 0x20 /* MAC doesn't like */ +#define AWI_TXD_ST_MSDU 0x10 /* MSDU timeout */ +#define AWI_TXD_ST_ABRT 0x08 /* TX aborted */ +#define AWI_TXD_ST_RETURNED 0x04 /* TX returned */ +#define AWI_TXD_ST_RETRY 0x02 /* TX retries exceeded */ +#define AWI_TXD_ST_ERROR 0x01 /* TX error */ + +#define AWI_TXD_RATE 0x0b /* rate */ + +#define AWI_RATE_1MBIT 10 +#define AWI_RATE_2MBIT 20 + +#define AWI_TXD_NDA 0x0c /* num DIFS attempts */ +#define AWI_TXD_NDF 0x0d /* num DIFS failures */ +#define AWI_TXD_NSA 0x0e /* num SIFS attempts */ +#define AWI_TXD_NSF 0x0f /* num SIFS failures */ + +#define AWI_TXD_NRA 0x14 /* num RTS attempts */ +#define AWI_TXD_NDTA 0x15 /* num data attempts */ +#define AWI_TXD_CTL 0x16 /* control */ + +#define AWI_TXD_CTL_PSN 0x80 /* preserve sequence in MAC frame */ +#define AWI_TXD_CTL_BURST 0x02 /* host is doing 802.11 fragmt. */ +#define AWI_TXD_CTL_FRAGS 0x01 /* override normal fragmentation */ + +/* + * MIB structures. + */ + +#define AWI_ESS_ID_SIZE (IEEE80211_NWID_LEN+2) +struct awi_mib_local { + u_int8_t Fragmentation_Dis; + u_int8_t Add_PLCP_Dis; + u_int8_t MAC_Hdr_Prsv; + u_int8_t Rx_Mgmt_Que_En; + u_int8_t Re_Assembly_Dis; + u_int8_t Strip_PLCP_Dis; + u_int8_t Rx_Error_Dis; + u_int8_t Power_Saving_Mode_Dis; + u_int8_t Accept_All_Multicast_Dis; + u_int8_t Check_Seq_Cntl_Dis; + u_int8_t Flush_CFP_Queue_On_CF_End; + u_int8_t Network_Mode; + u_int8_t PWD_Lvl; + u_int8_t CFP_Mode; + u_int8_t Tx_Buffer_Offset[4]; + u_int8_t Tx_Buffer_Size[4]; + u_int8_t Rx_Buffer_Offset[4]; + u_int8_t Rx_Buffer_Size[4]; + u_int8_t Acting_as_AP; + u_int8_t Fill_CFP; +}; + +struct awi_mib_mac { + u_int8_t _Reserved1[2]; + u_int8_t _Reserved2[2]; + u_int8_t aRTS_Threshold[2]; + u_int8_t aCW_max[2]; + u_int8_t aCW_min[2]; + u_int8_t aPromiscuous_Enable; + u_int8_t _Reserved3; + u_int8_t _Reserved4[4]; + u_int8_t aShort_Retry_Limit; + u_int8_t aLong_Retry_Limit; + u_int8_t aMax_Frame_Length[2]; + u_int8_t aFragmentation_Threshold[2]; + u_int8_t aProbe_Delay[2]; + u_int8_t aMin_Probe_Response_Time[2]; + u_int8_t aMax_Probe_Response_Time[2]; + u_int8_t aMax_Transmit_MSDU_Lifetime[4]; + u_int8_t aMax_Receive_MSDU_Lifetime[4]; + u_int8_t aStation_Basic_Rate[2]; + u_int8_t aDesired_ESS_ID[AWI_ESS_ID_SIZE]; +}; + +struct awi_mib_stat { + u_int8_t aTransmitted_MPDU_Count[4]; + u_int8_t aTransmitted_MSDU_Count[4]; + u_int8_t aOctets_Transmitted_Cnt[4]; + u_int8_t aMulticast_Transmitted_Frame_Count[2]; + u_int8_t aBroadcast_Transmitted_Frame_Count[2]; + u_int8_t aFailed_Count[4]; + u_int8_t aRetry_Count[4]; + u_int8_t aMultiple_Retry_Count[4]; + u_int8_t aFrame_Duplicate_Count[4]; + u_int8_t aRTS_Success_Count[4]; + u_int8_t aRTS_Failure_Count[4]; + u_int8_t aACK_Failure_Count[4]; + u_int8_t aReceived_Frame_Count [4]; + u_int8_t aOctets_Received_Count[4]; + u_int8_t aMulticast_Received_Count[2]; + u_int8_t aBroadcast_Received_Count[2]; + u_int8_t aFCS_Error_Count[4]; + u_int8_t aError_Count[4]; + u_int8_t aWEP_Undecryptable_Count[4]; +}; + +struct awi_mib_mgt { + u_int8_t aPower_Mgt_Mode; + u_int8_t aScan_Mode; +#define AWI_SCAN_PASSIVE 0x00 +#define AWI_SCAN_ACTIVE 0x01 +#define AWI_SCAN_BACKGROUND 0x02 + u_int8_t aScan_State; + u_int8_t aDTIM_Period; + u_int8_t aATIM_Window[2]; + u_int8_t Wep_Required; + u_int8_t _Reserved1; + u_int8_t aBeacon_Period[2]; + u_int8_t aPassive_Scan_Duration[2]; + u_int8_t aListen_Interval[2]; + u_int8_t aMedium_Occupancy_Limit[2]; + u_int8_t aMax_MPDU_Time[2]; + u_int8_t aCFP_Max_Duration[2]; + u_int8_t aCFP_Rate; + u_int8_t Do_Not_Receive_DTIMs; + u_int8_t aStation_ID[2]; + u_int8_t aCurrent_BSS_ID[ETHER_ADDR_LEN]; + u_int8_t aCurrent_ESS_ID[AWI_ESS_ID_SIZE]; +}; + +#define AWI_GROUP_ADDR_SIZE 4 +struct awi_mib_addr { + u_int8_t aMAC_Address[ETHER_ADDR_LEN]; + u_int8_t aGroup_Addresses[AWI_GROUP_ADDR_SIZE][ETHER_ADDR_LEN]; + u_int8_t aTransmit_Enable_Status; + u_int8_t _Reserved1; +}; + +#define AWI_PWR_LEVEL_SIZE 4 +struct awi_mib_phy { + u_int8_t aSlot_Time[2]; + u_int8_t aSIFS[2]; + u_int8_t aMPDU_Maximum[2]; + u_int8_t aHop_Time[2]; + u_int8_t aSuprt_Data_Rates[4]; + u_int8_t aCurrent_Reg_Domain; +#define AWI_REG_DOMAIN_US 0x10 +#define AWI_REG_DOMAIN_CA 0x20 +#define AWI_REG_DOMAIN_EU 0x30 +#define AWI_REG_DOMAIN_ES 0x31 +#define AWI_REG_DOMAIN_FR 0x32 +#define AWI_REG_DOMAIN_JP 0x40 + u_int8_t aPreamble_Lngth; + u_int8_t aPLCP_Hdr_Lngth; + u_int8_t Pwr_Up_Time[AWI_PWR_LEVEL_SIZE][2]; + u_int8_t IEEE_PHY_Type; +#define AWI_PHY_TYPE_FH 1 +#define AWI_PHY_TYPE_DS 2 +#define AWI_PHY_TYPE_IR 3 + u_int8_t RCR_33A_Bits[8]; +}; diff --git a/sys/dev/awi/awivar.h b/sys/dev/awi/awivar.h new file mode 100644 index 0000000000000..4d609ad77b6f3 --- /dev/null +++ b/sys/dev/awi/awivar.h @@ -0,0 +1,188 @@ +/* $NetBSD: awivar.h,v 1.6 2000/03/22 11:22:22 onoe Exp $ */ +/* $FreeBSD$ */ + +/* + * Copyright (c) 2000 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Atsushi Onoe + * + * 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. + */ + +/* timer values in msec */ +#define AWI_SELFTEST_TIMEOUT 5000 +#define AWI_CMD_TIMEOUT 2000 +#define AWI_LOCKOUT_TIMEOUT 50 +#define AWI_ASCAN_DURATION 100 +#define AWI_ASCAN_WAIT 3000 +#define AWI_PSCAN_DURATION 200 +#define AWI_PSCAN_WAIT 5000 +#define AWI_TRANS_TIMEOUT 2000 + +#define AWI_NTXBUFS 4 + +enum awi_status { + AWI_ST_INIT, + AWI_ST_SCAN, + AWI_ST_SETSS, + AWI_ST_SYNC, + AWI_ST_AUTH, + AWI_ST_ASSOC, + AWI_ST_RUNNING +}; + +struct awi_bss +{ + TAILQ_ENTRY(awi_bss) list; + u_int8_t esrc[ETHER_ADDR_LEN]; + u_int8_t chanset; /* channel set to use */ + u_int8_t pattern; /* hop pattern to use */ + u_int8_t index; /* index to use */ + u_int8_t rssi; /* strength of this beacon */ + u_int16_t dwell_time; /* dwell time */ + u_int8_t timestamp[8]; /* timestamp of this bss */ + u_int8_t bssid[ETHER_ADDR_LEN]; + u_int32_t rxtime; /* unit's local time */ + u_int16_t interval; /* beacon interval */ + u_int8_t txrate; + u_int8_t fails; + u_int8_t essid[IEEE80211_NWID_LEN + 2]; +}; + +struct awi_softc +{ +#ifdef __NetBSD__ + struct device sc_dev; + struct ethercom sc_ec; + void *sc_ih; /* interrupt handler */ +#endif +#ifdef __FreeBSD__ +#if __FreeBSD__ >= 4 + struct { + char dv_xname[64]; /*XXX*/ + } sc_dev; +#else + struct device sc_dev; +#endif + struct arpcom sc_ec; + struct callout_handle sc_tohandle; +#endif + struct am79c930_softc sc_chip; + struct ifnet *sc_ifp; + int (*sc_enable) __P((struct awi_softc *)); + void (*sc_disable) __P((struct awi_softc *)); + + struct ifmedia sc_media; + enum awi_status sc_status; + unsigned int sc_enabled:1, + sc_busy:1, + sc_cansleep:1, + sc_invalid:1, + sc_cmd_inprog:1, + sc_format_llc:1, + sc_start_bss:1, + sc_rawbpf:1, + sc_no_bssid:1, + sc_active_scan:1; + int sc_sleep_cnt; + + int sc_mgt_timer; + + TAILQ_HEAD(, awi_bss) sc_scan; + u_int8_t sc_scan_cur; + u_int8_t sc_scan_min; + u_int8_t sc_scan_max; + u_int8_t sc_scan_set; + struct awi_bss sc_bss; + + int sc_rx_timer; + u_int32_t sc_rxdoff; + u_int32_t sc_rxmoff; + struct mbuf *sc_rxpend; + + int sc_tx_timer; + u_int8_t sc_tx_rate; + struct ifqueue sc_mgtq; + u_int32_t sc_txbase; + u_int32_t sc_txend; + u_int32_t sc_txnext; + u_int32_t sc_txdone; + + struct awi_mib_local sc_mib_local; + struct awi_mib_addr sc_mib_addr; + struct awi_mib_mac sc_mib_mac; + struct awi_mib_stat sc_mib_stat; + struct awi_mib_mgt sc_mib_mgt; + struct awi_mib_phy sc_mib_phy; +}; + +#define awi_read_1(sc, off) ((sc)->sc_chip.sc_ops->read_1)(&sc->sc_chip, off) +#define awi_read_2(sc, off) ((sc)->sc_chip.sc_ops->read_2)(&sc->sc_chip, off) +#define awi_read_4(sc, off) ((sc)->sc_chip.sc_ops->read_4)(&sc->sc_chip, off) +#define awi_read_bytes(sc, off, ptr, len) ((sc)->sc_chip.sc_ops->read_bytes)(&sc->sc_chip, off, ptr, len) + +#define awi_write_1(sc, off, val) \ + ((sc)->sc_chip.sc_ops->write_1)(&sc->sc_chip, off, val) +#define awi_write_2(sc, off, val) \ + ((sc)->sc_chip.sc_ops->write_2)(&sc->sc_chip, off, val) +#define awi_write_4(sc, off, val) \ + ((sc)->sc_chip.sc_ops->write_4)(&sc->sc_chip, off, val) +#define awi_write_bytes(sc, off, ptr, len) \ + ((sc)->sc_chip.sc_ops->write_bytes)(&sc->sc_chip, off, ptr, len) + +#define awi_drvstate(sc, state) \ + awi_write_1(sc, AWI_DRIVERSTATE, \ + ((state) | AWI_DRV_AUTORXLED|AWI_DRV_AUTOTXLED)) + +/* unalligned little endian access */ +#define LE_READ_2(p) \ + (((u_int8_t *)(p))[0] | (((u_int8_t *)(p))[1] << 8)) +#define LE_READ_4(p) \ + (((u_int8_t *)(p))[0] | (((u_int8_t *)(p))[1] << 8) | \ + (((u_int8_t *)(p))[2] << 16) | (((u_int8_t *)(p))[3] << 24)) +#define LE_WRITE_2(p, v) \ + ((((u_int8_t *)(p))[0] = ((u_int32_t)(v) & 0xff)), \ + (((u_int8_t *)(p))[1] = (((u_int32_t)(v) >> 8) & 0xff))) +#define LE_WRITE_4(p, v) \ + ((((u_int8_t *)(p))[0] = ((u_int32_t)(v) & 0xff)), \ + (((u_int8_t *)(p))[1] = (((u_int32_t)(v) >> 8) & 0xff)), \ + (((u_int8_t *)(p))[2] = (((u_int32_t)(v) >> 16) & 0xff)), \ + (((u_int8_t *)(p))[3] = (((u_int32_t)(v) >> 24) & 0xff))) + +#define AWI_80211_RATE(rate) (((rate) & 0x7f) * 5) + +int awi_attach __P((struct awi_softc *)); +int awi_intr __P((void *)); +void awi_reset __P((struct awi_softc *)); +#ifdef __NetBSD__ +int awi_activate __P((struct device *, enum devact)); +int awi_detach __P((struct awi_softc *)); +#endif diff --git a/sys/dev/awi/if_awi_pccard.c b/sys/dev/awi/if_awi_pccard.c new file mode 100644 index 0000000000000..45d43e54ffd1b --- /dev/null +++ b/sys/dev/awi/if_awi_pccard.c @@ -0,0 +1,245 @@ +/* + * Copyright (c) 2000 Atsushi Onoe <onoe@sm.sony.co.jp> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/socket.h> + +#include <sys/module.h> +#include <sys/bus.h> + +#include <machine/bus.h> +#include <machine/resource.h> +#include <sys/rman.h> + +#include <net/if.h> +#include <net/if_arp.h> +#include <net/if_media.h> +#include <net/ethernet.h> +#include <net/if_ieee80211.h> + +#include <machine/clock.h> + +#include <dev/awi/am79c930reg.h> +#include <dev/awi/am79c930var.h> +#include <dev/awi/awireg.h> +#include <dev/awi/awivar.h> + +struct awi_pccard_softc { + struct awi_softc sc_awi; + + u_int8_t sc_version[AWI_BANNER_LEN]; + int sc_intr_mask; + void *sc_intrhand; + struct resource *sc_irq_res; + int sc_irq_rid; + struct resource *sc_port_res; + int sc_port_rid; + struct resource *sc_mem_res; + int sc_mem_rid; +}; + +/* + * Initialize the device - called from Slot manager. + */ +static int +awi_pccard_probe(device_t dev) +{ + struct awi_pccard_softc *psc = device_get_softc(dev); + struct awi_softc *sc = &psc->sc_awi; + int error = 0; + + psc->sc_port_rid = 0; + psc->sc_port_res = bus_alloc_resource(dev, SYS_RES_IOPORT, + &psc->sc_port_rid, 0, ~0, 16, RF_ACTIVE); + if (!psc->sc_port_res) + return ENOMEM; + + sc->sc_chip.sc_iot = I386_BUS_SPACE_IO; + sc->sc_chip.sc_ioh = + (bus_space_handle_t)rman_get_start(psc->sc_port_res); + am79c930_chip_init(&sc->sc_chip, 0); + DELAY(1000); + + awi_read_bytes(sc, AWI_BANNER, psc->sc_version, AWI_BANNER_LEN); + if (memcmp(psc->sc_version, "PCnetMobile:", 12) != 0) { + device_printf(dev, "awi_pccard_probe: bad banner: %12D\n", + psc->sc_version, " "); + error = ENXIO; + } else + device_set_desc(dev, psc->sc_version); + bus_release_resource(dev, SYS_RES_IOPORT, psc->sc_port_rid, + psc->sc_port_res); + + return error; +} + +static int +awi_pccard_attach(device_t dev) +{ + struct awi_pccard_softc *psc = device_get_softc(dev); + struct awi_softc *sc = &psc->sc_awi; + struct ifnet *ifp = &sc->sc_ec.ac_if; + int error = 0; + + psc->sc_port_res = 0; + psc->sc_irq_res = 0; + psc->sc_mem_res = 0; + psc->sc_intrhand = 0; + + ifp->if_name = device_get_name(dev); + ifp->if_unit = device_get_unit(dev); + if (ifp->if_name == NULL) { + printf("awi%d: awi_pccard_attach: cannot get device name\n", + device_get_unit(dev)); + goto fail; + } + snprintf(sc->sc_dev.dv_xname, sizeof(sc->sc_dev.dv_xname), + "%s%d", ifp->if_name, ifp->if_unit); + + psc->sc_port_rid = 0; + psc->sc_port_res = bus_alloc_resource(dev, SYS_RES_IOPORT, + &psc->sc_port_rid, 0, ~0, 16, RF_ACTIVE); + if (!psc->sc_port_res) { + device_printf(dev, "awi_pccard_attach: port alloc failed\n"); + goto fail; + } + sc->sc_chip.sc_iot = I386_BUS_SPACE_IO; + sc->sc_chip.sc_ioh = + (bus_space_handle_t)rman_get_start(psc->sc_port_res); + + psc->sc_irq_rid = 0; + psc->sc_irq_res = bus_alloc_resource(dev, SYS_RES_IRQ, + &psc->sc_irq_rid, 0, ~0, 1, RF_ACTIVE); + if (!psc->sc_irq_res) { + device_printf(dev, "awi_pccard_attach: irq alloc failed\n"); + goto fail; + } + + psc->sc_mem_rid = 0; + psc->sc_mem_res = bus_alloc_resource(dev, SYS_RES_MEMORY, + &psc->sc_mem_rid, 0, ~0, 0x8000, RF_ACTIVE); + if (psc->sc_mem_res) { + sc->sc_chip.sc_memt = I386_BUS_SPACE_MEM; + sc->sc_chip.sc_memh = + (bus_space_handle_t)rman_get_virtual(psc->sc_mem_res); + am79c930_chip_init(&sc->sc_chip, 1); + } else + am79c930_chip_init(&sc->sc_chip, 0); + + error = bus_setup_intr(dev, psc->sc_irq_res, INTR_TYPE_NET, + (void (*)(void *))awi_intr, sc, &psc->sc_intrhand); + if (error) { + device_printf(dev, "awi_pccard_attach: intr setup failed\n"); + goto fail; + } + + sc->sc_cansleep = 1; + sc->sc_enabled = 1; + sc->sc_ifp = &sc->sc_ec.ac_if; + + error = awi_attach(sc); + sc->sc_enabled = 0; /*XXX*/ + if (error == 0) + return 0; + device_printf(dev, "awi_pccard_attach: awi_attach failed\n"); + + fail: + if (psc->sc_intrhand) { + bus_teardown_intr(dev, psc->sc_irq_res, psc->sc_intrhand); + psc->sc_intrhand = 0; + } + if (psc->sc_port_res) { + bus_release_resource(dev, SYS_RES_IOPORT, psc->sc_port_rid, + psc->sc_port_res); + psc->sc_port_res = 0; + } + if (psc->sc_irq_res) { + bus_release_resource(dev, SYS_RES_IRQ, psc->sc_irq_rid, + psc->sc_irq_res); + psc->sc_irq_res = 0; + } + if (psc->sc_mem_res) { + bus_release_resource(dev, SYS_RES_MEMORY, psc->sc_mem_rid, + psc->sc_mem_res); + psc->sc_mem_res = 0; + } + if (error == 0) + error = ENXIO; + return error; +} + +static int +awi_pccard_detach(device_t dev) +{ + struct awi_pccard_softc *psc = device_get_softc(dev); + struct awi_softc *sc = &psc->sc_awi; + struct ifnet *ifp = &sc->sc_ec.ac_if; + + if_detach(ifp); + ifp->if_flags &= ~IFF_RUNNING; + if (psc->sc_intrhand) { + bus_teardown_intr(dev, psc->sc_irq_res, psc->sc_intrhand); + psc->sc_intrhand = 0; + } + if (psc->sc_port_res) { + bus_release_resource(dev, SYS_RES_IOPORT, psc->sc_port_rid, + psc->sc_port_res); + psc->sc_port_res = 0; + } + if (psc->sc_irq_res) { + bus_release_resource(dev, SYS_RES_IRQ, psc->sc_irq_rid, + psc->sc_irq_res); + psc->sc_irq_res = 0; + } + if (psc->sc_mem_res) { + bus_release_resource(dev, SYS_RES_MEMORY, psc->sc_mem_rid, + psc->sc_mem_res); + psc->sc_mem_res = 0; + } + return 0; +} + +static device_method_t awi_pccard_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, awi_pccard_probe), + DEVMETHOD(device_attach, awi_pccard_attach), + DEVMETHOD(device_detach, awi_pccard_detach), + + { 0, 0 } +}; + +static driver_t awi_pccard_driver = { + "awi", + awi_pccard_methods, + sizeof(struct awi_pccard_softc), +}; + +extern devclass_t awi_devclass; + +DRIVER_MODULE(awi, pccard, awi_pccard_driver, awi_devclass, 0, 0); diff --git a/sys/dev/awi/if_awi_pcmcia.c b/sys/dev/awi/if_awi_pcmcia.c new file mode 100644 index 0000000000000..ca77ef0620a0a --- /dev/null +++ b/sys/dev/awi/if_awi_pcmcia.c @@ -0,0 +1,445 @@ +/* $NetBSD: if_awi_pcmcia.c,v 1.14 2000/03/22 22:33:47 mycroft Exp $ */ +/* $FreeBSD$ */ + +/*- + * Copyright (c) 1999 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Bill Sommerfeld + * + * 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. + */ + +/* + * PCMCIA attachment for BayStack 650 802.11FH PCMCIA card, + * based on the AMD 79c930 802.11 controller chip. + * + * This attachment can probably be trivally adapted for other FH and + * DS cards based on the same chipset. + */ + +#include "opt_inet.h" +#include "opt_ns.h" +#include "bpfilter.h" + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/mbuf.h> +#include <sys/socket.h> +#include <sys/ioctl.h> +#include <sys/errno.h> +#include <sys/syslog.h> +#include <sys/select.h> +#include <sys/device.h> + +#include <net/if.h> +#include <net/if_dl.h> +#include <net/if_ether.h> +#include <net/if_media.h> +#include <net/if_ieee80211.h> + +#ifdef INET +#include <netinet/in.h> +#include <netinet/in_systm.h> +#include <netinet/in_var.h> +#include <netinet/ip.h> +#include <netinet/if_inarp.h> +#endif + +#ifdef NS +#include <netns/ns.h> +#include <netns/ns_if.h> +#endif + +#if NBPFILTER > 0 +#include <net/bpf.h> +#include <net/bpfdesc.h> +#endif + +#include <machine/cpu.h> +#include <machine/bus.h> +#include <machine/intr.h> + +#include <dev/ic/am79c930reg.h> +#include <dev/ic/am79c930var.h> +#include <dev/ic/awireg.h> +#include <dev/ic/awivar.h> + +#include <dev/pcmcia/pcmciareg.h> +#include <dev/pcmcia/pcmciavar.h> +#include <dev/pcmcia/pcmciadevs.h> + +static int awi_pcmcia_match __P((struct device *, struct cfdata *, void *)); +static void awi_pcmcia_attach __P((struct device *, struct device *, void *)); +static int awi_pcmcia_detach __P((struct device *, int)); +static int awi_pcmcia_enable __P((struct awi_softc *)); +static void awi_pcmcia_disable __P((struct awi_softc *)); +static void awi_pcmcia_powerhook __P((int, void *)); + +struct awi_pcmcia_softc { + struct awi_softc sc_awi; /* real "awi" softc */ + + /* PCMCIA-specific goo */ + struct pcmcia_io_handle sc_pcioh; /* PCMCIA i/o space info */ + struct pcmcia_mem_handle sc_memh; /* PCMCIA memory space info */ + int sc_io_window; /* our i/o window */ + int sc_mem_window; /* our memory window */ + struct pcmcia_function *sc_pf; /* our PCMCIA function */ + void *sc_powerhook; /* power hook descriptor */ +}; + +static int awi_pcmcia_find __P((struct awi_pcmcia_softc *, + struct pcmcia_attach_args *, struct pcmcia_config_entry *)); + +struct cfattach awi_pcmcia_ca = { + sizeof(struct awi_pcmcia_softc), awi_pcmcia_match, awi_pcmcia_attach, + awi_pcmcia_detach, awi_activate +}; + +#if __NetBSD_Version__ <= 104120000 +#define PCMCIA_VENDOR_BAY 0x01eb /* Bay Networks */ +#define PCMCIA_PRODUCT_BAY_STACK_650 0x804 +#endif + +static struct awi_pcmcia_product { + u_int32_t app_vendor; /* vendor ID */ + u_int32_t app_product; /* product ID */ + const char *app_cisinfo[4]; /* CIS information */ + const char *app_name; /* product name */ +} awi_pcmcia_products[] = { + { PCMCIA_VENDOR_BAY, PCMCIA_PRODUCT_BAY_STACK_650, + PCMCIA_CIS_BAY_STACK_650, PCMCIA_STR_BAY_STACK_650 }, + + { PCMCIA_VENDOR_BAY, PCMCIA_PRODUCT_BAY_STACK_660, + PCMCIA_CIS_BAY_STACK_660, PCMCIA_STR_BAY_STACK_660 }, + + { PCMCIA_VENDOR_BAY, PCMCIA_PRODUCT_BAY_SURFER_PRO, + PCMCIA_CIS_BAY_SURFER_PRO, PCMCIA_STR_BAY_SURFER_PRO }, + + { PCMCIA_VENDOR_AMD, PCMCIA_PRODUCT_AMD_AM79C930, + PCMCIA_CIS_AMD_AM79C930, PCMCIA_STR_AMD_AM79C930 }, + + { PCMCIA_VENDOR_ICOM, PCMCIA_PRODUCT_ICOM_SL200, + PCMCIA_CIS_ICOM_SL200, PCMCIA_STR_ICOM_SL200 }, + + { PCMCIA_VENDOR_NOKIA, PCMCIA_PRODUCT_NOKIA_C020_WLAN, + PCMCIA_CIS_NOKIA_C020_WLAN, PCMCIA_STR_NOKIA_C020_WLAN }, + + { 0, 0, + { NULL, NULL, NULL, NULL }, NULL }, +}; + +static struct awi_pcmcia_product * + awi_pcmcia_lookup __P((struct pcmcia_attach_args *)); + +static struct awi_pcmcia_product * +awi_pcmcia_lookup(pa) + struct pcmcia_attach_args *pa; +{ + struct awi_pcmcia_product *app; + + for (app = awi_pcmcia_products; app->app_name != NULL; app++) { + /* match by vendor/product id */ + if (pa->manufacturer != PCMCIA_VENDOR_INVALID && + pa->manufacturer == app->app_vendor && + pa->product != PCMCIA_PRODUCT_INVALID && + pa->product == app->app_product) + return (app); + + /* match by CIS information */ + if (pa->card->cis1_info[0] != NULL && + app->app_cisinfo[0] != NULL && + strcmp(pa->card->cis1_info[0], app->app_cisinfo[0]) == 0 && + pa->card->cis1_info[1] != NULL && + app->app_cisinfo[1] != NULL && + strcmp(pa->card->cis1_info[1], app->app_cisinfo[1]) == 0) + return (app); + } + + return (NULL); +} + +static int +awi_pcmcia_enable(sc) + struct awi_softc *sc; +{ + struct awi_pcmcia_softc *psc = (struct awi_pcmcia_softc *)sc; + struct pcmcia_function *pf = psc->sc_pf; + + /* establish the interrupt. */ + sc->sc_ih = pcmcia_intr_establish(pf, IPL_NET, awi_intr, sc); + if (sc->sc_ih == NULL) { + printf("%s: couldn't establish interrupt\n", + sc->sc_dev.dv_xname); + return (1); + } + + if (pcmcia_function_enable(pf)) { + pcmcia_intr_disestablish(pf, sc->sc_ih); + return (1); + } + DELAY(1000); + + return (0); +} + +static void +awi_pcmcia_disable(sc) + struct awi_softc *sc; +{ + struct awi_pcmcia_softc *psc = (struct awi_pcmcia_softc *)sc; + struct pcmcia_function *pf = psc->sc_pf; + + pcmcia_function_disable(pf); + pcmcia_intr_disestablish(pf, sc->sc_ih); +} + +static int +awi_pcmcia_match(parent, match, aux) + struct device *parent; + struct cfdata *match; + void *aux; +{ + struct pcmcia_attach_args *pa = aux; + + if (awi_pcmcia_lookup(pa) != NULL) + return (1); + + return (0); +} + +static int +awi_pcmcia_find(psc, pa, cfe) + struct awi_pcmcia_softc *psc; + struct pcmcia_attach_args *pa; + struct pcmcia_config_entry *cfe; +{ + struct awi_softc *sc = &psc->sc_awi; + int fail = 0; + u_int8_t version[AWI_BANNER_LEN]; + + /* + * see if we can read the firmware version sanely + * through the i/o ports. + * if not, try a different CIS string.. + */ + if (pcmcia_io_alloc(psc->sc_pf, cfe->iospace[0].start, + cfe->iospace[0].length, AM79C930_IO_ALIGN, + &psc->sc_pcioh) != 0) + goto fail; + + if (pcmcia_io_map(psc->sc_pf, PCMCIA_WIDTH_AUTO, 0, psc->sc_pcioh.size, + &psc->sc_pcioh, &psc->sc_io_window)) + goto fail_io_free; + + /* Enable the card. */ + pcmcia_function_init(psc->sc_pf, cfe); + if (pcmcia_function_enable(psc->sc_pf)) + goto fail_io_unmap; + + sc->sc_chip.sc_bustype = AM79C930_BUS_PCMCIA; + sc->sc_chip.sc_iot = psc->sc_pcioh.iot; + sc->sc_chip.sc_ioh = psc->sc_pcioh.ioh; + am79c930_chip_init(&sc->sc_chip, 0); + + DELAY(1000); + + awi_read_bytes(sc, AWI_BANNER, version, AWI_BANNER_LEN); + + if (memcmp(version, "PCnetMobile:", 12) == 0) + return (0); + + fail++; + pcmcia_function_disable(psc->sc_pf); + + fail_io_unmap: + fail++; + pcmcia_io_unmap(psc->sc_pf, psc->sc_io_window); + + fail_io_free: + fail++; + pcmcia_io_free(psc->sc_pf, &psc->sc_pcioh); + fail: + fail++; + psc->sc_io_window = -1; + return (fail); +} + +static void +awi_pcmcia_attach(parent, self, aux) + struct device *parent, *self; + void *aux; +{ + struct awi_pcmcia_softc *psc = (void *)self; + struct awi_softc *sc = &psc->sc_awi; + struct awi_pcmcia_product *app; + struct pcmcia_attach_args *pa = aux; + struct pcmcia_config_entry *cfe; + bus_addr_t memoff; + + app = awi_pcmcia_lookup(pa); + if (app == NULL) + panic("awi_pcmcia_attach: impossible"); + + psc->sc_pf = pa->pf; + + for (cfe = SIMPLEQ_FIRST(&pa->pf->cfe_head); cfe != NULL; + cfe = SIMPLEQ_NEXT(cfe, cfe_list)) { + if (cfe->iftype != PCMCIA_IFTYPE_IO) + continue; + if (cfe->num_iospace < 1) + continue; + if (cfe->iospace[0].length < AM79C930_IO_SIZE) + continue; + + if (awi_pcmcia_find(psc, pa, cfe) == 0) + break; + } + if (cfe == NULL) { + printf(": no suitable CIS info found\n"); + goto no_config_entry; + } + + sc->sc_enabled = 1; + printf(": %s\n", app->app_name); + + psc->sc_mem_window = -1; + if (pcmcia_mem_alloc(psc->sc_pf, AM79C930_MEM_SIZE, + &psc->sc_memh) != 0) { + printf("%s: unable to allocate memory space; using i/o only\n", + sc->sc_dev.dv_xname); + } else if (pcmcia_mem_map(psc->sc_pf, + PCMCIA_WIDTH_MEM8|PCMCIA_MEM_COMMON, AM79C930_MEM_BASE, + AM79C930_MEM_SIZE, &psc->sc_memh, &memoff, &psc->sc_mem_window)) { + printf("%s: unable to map memory space; using i/o only\n", + sc->sc_dev.dv_xname); + pcmcia_mem_free(psc->sc_pf, &psc->sc_memh); + } else { + sc->sc_chip.sc_memt = psc->sc_memh.memt; + sc->sc_chip.sc_memh = psc->sc_memh.memh; + am79c930_chip_init(&sc->sc_chip, 1); + } + + sc->sc_enable = awi_pcmcia_enable; + sc->sc_disable = awi_pcmcia_disable; + + /* establish the interrupt. */ + sc->sc_ih = pcmcia_intr_establish(psc->sc_pf, IPL_NET, awi_intr, sc); + if (sc->sc_ih == NULL) { + printf("%s: couldn't establish interrupt\n", + sc->sc_dev.dv_xname); + goto no_interrupt; + } + sc->sc_ifp = &sc->sc_ec.ec_if; + sc->sc_cansleep = 1; + + if (awi_attach(sc) != 0) { + printf("%s: failed to attach controller\n", + sc->sc_dev.dv_xname); + goto attach_failed; + } + psc->sc_powerhook = powerhook_establish(awi_pcmcia_powerhook, psc); + + sc->sc_enabled = 0; + /* disable device and disestablish the interrupt */ + awi_pcmcia_disable(sc); + return; + + attach_failed: + pcmcia_intr_disestablish(psc->sc_pf, sc->sc_ih); + + no_interrupt: + /* Unmap our memory window and space */ + if (psc->sc_mem_window != -1) { + pcmcia_mem_unmap(psc->sc_pf, psc->sc_mem_window); + pcmcia_mem_free(psc->sc_pf, &psc->sc_memh); + } + + /* Unmap our i/o window and space */ + pcmcia_io_unmap(psc->sc_pf, psc->sc_io_window); + pcmcia_io_free(psc->sc_pf, &psc->sc_pcioh); + + /* Disable the function */ + pcmcia_function_disable(psc->sc_pf); + + no_config_entry: + psc->sc_io_window = -1; +} + + +static int +awi_pcmcia_detach(self, flags) + struct device *self; + int flags; +{ + struct awi_pcmcia_softc *psc = (struct awi_pcmcia_softc *)self; + int error; + + if (psc->sc_io_window == -1) + /* Nothing to detach. */ + return (0); + + if (psc->sc_powerhook != NULL) + powerhook_disestablish(psc->sc_powerhook); + + error = awi_detach(&psc->sc_awi); + if (error != 0) + return (error); + + /* Unmap our memory window and free memory space */ + if (psc->sc_mem_window != -1) { + pcmcia_mem_unmap(psc->sc_pf, psc->sc_mem_window); + pcmcia_mem_free(psc->sc_pf, &psc->sc_memh); + } + + /* Unmap our i/o window. */ + pcmcia_io_unmap(psc->sc_pf, psc->sc_io_window); + + /* Free our i/o space. */ + pcmcia_io_free(psc->sc_pf, &psc->sc_pcioh); + return (0); +} + +static void +awi_pcmcia_powerhook(why, arg) + int why; + void *arg; +{ + struct awi_pcmcia_softc *psc = arg; + struct awi_softc *sc = &psc->sc_awi; + int ocansleep; + + if (why == PWR_RESUME) { + ocansleep = sc->sc_cansleep; + sc->sc_cansleep = 0; + awi_reset(sc); + sc->sc_cansleep = ocansleep; + } +} |
