diff options
Diffstat (limited to 'sys')
-rw-r--r-- | sys/dev/ic/am79c930.c | 380 | ||||
-rw-r--r-- | sys/dev/ic/am79c930reg.h | 375 | ||||
-rw-r--r-- | sys/dev/ic/am79c930var.h | 79 | ||||
-rw-r--r-- | sys/dev/ic/awi.c | 2622 | ||||
-rw-r--r-- | sys/dev/ic/awireg.h | 510 | ||||
-rw-r--r-- | sys/dev/ic/awivar.h | 183 | ||||
-rw-r--r-- | sys/dev/pcmcia/files.pcmcia | 10 | ||||
-rw-r--r-- | sys/dev/pcmcia/if_awi_pcmcia.c | 412 |
8 files changed, 4570 insertions, 1 deletions
diff --git a/sys/dev/ic/am79c930.c b/sys/dev/ic/am79c930.c new file mode 100644 index 00000000000..4e8a524c08c --- /dev/null +++ b/sys/dev/ic/am79c930.c @@ -0,0 +1,380 @@ +/* $NetBSD: am79c930.c,v 1.2 1999/11/05 05:13:36 sommerfeld Exp $ */ +/* $OpenBSD: am79c930.c,v 1.1 1999/12/16 02:56:56 deraadt Exp $ */ + +/*- + * 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> +#include <sys/device.h> + +#include <machine/cpu.h> +#include <machine/bus.h> +#include <machine/intr.h> + +#include <dev/ic/am79c930reg.h> +#include <dev/ic/am79c930var.h> + +#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; +{ + /* XXX bank-switching? */ + 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; +{ + io_write_1(sc, off, val & 0xff); + io_write_1(sc, off+1, (val >> 8) & 0xff); +} + +static void io_write_4 (sc, off, val) + struct am79c930_softc *sc; + u_int32_t off; + u_int32_t val; +{ + /* XXX higher offset values needed for bank-switching! */ + io_write_1(sc, off, val & 0xff); + io_write_1(sc, off+1, (val >> 8) & 0xff); + io_write_1(sc, off+2, (val >> 16) & 0xff); + io_write_1(sc, off+3, (val >> 24) & 0xff); +} + +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; + /* XXX higher offset values needed for bank-switching! */ + + for (i=0; i<len; i++) + io_write_1 (sc, off+i, 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; +{ + return io_read_1 (sc, off) | + (io_read_1 (sc, off+1) << 8); +} + +static u_int32_t io_read_4 (sc, off) + struct am79c930_softc *sc; + u_int32_t off; +{ + /* XXX bank-switching? */ + return io_read_1 (sc, off) | + (io_read_1 (sc, off+1) << 8) | + (io_read_1 (sc, off+2) << 16) | + (io_read_1 (sc, off+3) << 24); +} + +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; + + for (i=0; i<len; i++) + ptr[i] = io_read_1(sc, off+i); +} + + + +static void mem_write_1 (sc, off, val) + struct am79c930_softc *sc; + u_int32_t off; + u_int8_t val; +{ + /* XXX higher offset values needed for bank-switching! */ + 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; +{ + /* XXX higher offset values needed for bank-switching! */ + bus_space_write_1(sc->sc_memt, sc->sc_memh, off, val & 0xff); + bus_space_write_1(sc->sc_memt, sc->sc_memh, 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; +{ + /* XXX higher offset values needed for bank-switching! */ + bus_space_write_1(sc->sc_memt, sc->sc_memh, off, val & 0xff); + bus_space_write_1(sc->sc_memt, sc->sc_memh, off+1, (val >> 8) & 0xff); + bus_space_write_1(sc->sc_memt, sc->sc_memh, off+2, (val >> 16) & 0xff); + bus_space_write_1(sc->sc_memt, sc->sc_memh, 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; +{ + int i; + /* XXX higher offset values needed for bank-switching! */ + + for (i=0; i<len; i++) + bus_space_write_1 (sc->sc_memt, sc->sc_memh, off+i, ptr[i]); +} + + +static u_int8_t mem_read_1 (sc, off) + struct am79c930_softc *sc; + u_int32_t off; +{ + /* XXX higher offset values needed for bank-switching! */ + 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; +{ + /* XXX higher offset values needed for bank-switching! */ + 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; +{ + /* XXX higher offset values needed for bank-switching! */ + 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; +{ + int i; + + /* XXX higher offset values needed for bank-switching! */ + + for (i=0; i<len; i++) + ptr[i] = bus_space_read_1(sc->sc_memt, sc->sc_memh, off+i); +} + + + + +/* + * 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/ic/am79c930reg.h b/sys/dev/ic/am79c930reg.h new file mode 100644 index 00000000000..9dfcd537086 --- /dev/null +++ b/sys/dev/ic/am79c930reg.h @@ -0,0 +1,375 @@ +/* $NetBSD: am79c930reg.h,v 1.2 1999/11/05 05:13:36 sommerfeld Exp $ */ +/* $OpenBSD: am79c930reg.h,v 1.1 1999/12/16 02:56:56 deraadt Exp $ */ + +/*- + * 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_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 */ +/* $NetBSD: am79c930reg.h,v 1.2 1999/11/05 05:13:36 sommerfeld Exp $ */ +/* $OpenBSD: am79c930reg.h,v 1.1 1999/12/16 02:56:56 deraadt Exp $ */ + +/*- + * 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_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 */ +/* $NetBSD: am79c930reg.h,v 1.2 1999/11/05 05:13:36 sommerfeld Exp $ */ +/* $OpenBSD: am79c930reg.h,v 1.1 1999/12/16 02:56:56 deraadt Exp $ */ + +/*- + * 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_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/ic/am79c930var.h b/sys/dev/ic/am79c930var.h new file mode 100644 index 00000000000..0d8a221126f --- /dev/null +++ b/sys/dev/ic/am79c930var.h @@ -0,0 +1,79 @@ +/* $NetBSD: am79c930var.h,v 1.2 1999/11/05 05:13:36 sommerfeld Exp $ */ +/* $OpenBSD: am79c930var.h,v 1.1 1999/12/16 02:56:56 deraadt Exp $ */ + +/*- + * 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/ic/awi.c b/sys/dev/ic/awi.c new file mode 100644 index 00000000000..97f3b609a10 --- /dev/null +++ b/sys/dev/ic/awi.c @@ -0,0 +1,2622 @@ +/* $NetBSD: awi.c,v 1.8 1999/11/09 14:58:07 sommerfeld Exp $ */ +/* $OpenBSD: awi.c,v 1.1 1999/12/16 02:56:56 deraadt Exp $ */ + +/*- + * 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. + */ +/* + * Driver for AMD 802.11 firmware. + * Uses am79c930 chip driver to talk to firmware running on the am79c930. + * + * More-or-less a generic ethernet-like if driver, with 802.11 gorp added. + */ + +/* + * todo: + * - flush tx queue on resynch. + * - clear oactive on "down". + * - rewrite copy-into-mbuf code + * - mgmt state machine gets stuck retransmitting assoc requests. + * - multicast filter. + * - fix device reset so it's more likely to work + * - show status goo through ifmedia. + * + * more todo: + * - deal with more 802.11 frames. + * - send reassoc request + * - deal with reassoc response + * - send/deal with disassociation + * - deal with "full" access points (no room for me). + * - power save mode + * + * later: + * - SSID preferences + * - need ioctls for poking at the MIBs + * - implement ad-hoc mode (including bss creation). + * - decide when to do "ad hoc" vs. infrastructure mode (IFF_LINK flags?) + * (focus on inf. mode since that will be needed for ietf) + * - deal with DH vs. FH versions of the card + * - deal with faster cards (2mb/s) + * - ?WEP goo (mmm, rc4) (it looks not particularly useful). + * - ifmedia revision. + * - common 802.11 mibish things. + * - common 802.11 media layer. + */ + +#include "bpfilter.h" + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.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> +#if NRND > 0 +#include <sys/rnd.h> +#endif + +#include <net/if.h> +#include <net/if_dl.h> +#include <net/if_media.h> + +#ifdef INET +#include <netinet/in.h> +#include <netinet/in_systm.h> +#include <netinet/in_var.h> +#include <netinet/ip.h> +#include <netinet/if_ether.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> + +#ifndef ETHER_CRC_LEN +#define ETHER_CRC_LEN 4 +#endif + +void awi_insane __P((struct awi_softc *sc)); +int awi_intlock __P((struct awi_softc *sc)); +void awi_intunlock __P((struct awi_softc *sc)); +void awi_intrinit __P((struct awi_softc *sc)); +u_int8_t awi_read_intst __P((struct awi_softc *sc)); +void awi_stop __P((struct awi_softc *sc)); +void awi_flush __P((struct awi_softc *sc)); +void awi_init __P((struct awi_softc *sc)); +void awi_set_mc __P((struct awi_softc *sc)); +void awi_rxint __P((struct awi_softc *)); +void awi_txint __P((struct awi_softc *)); +void awi_tx_packet __P((struct awi_softc *, int, struct mbuf *)); + +void awi_rcv __P((struct awi_softc *, struct mbuf *, u_int32_t, u_int8_t)); +void awi_rcv_mgt __P((struct awi_softc *, struct mbuf *, u_int32_t, u_int8_t)); +void awi_rcv_data __P((struct awi_softc *, struct mbuf *)); +void awi_rcv_ctl __P((struct awi_softc *, struct mbuf *)); + +int awi_enable __P((struct awi_softc *sc)); +void awi_disable __P((struct awi_softc *sc)); + +void awi_zero __P((struct awi_softc *, u_int32_t, u_int32_t)); + +void awi_cmd __P((struct awi_softc *, u_int8_t)); +void awi_cmd_test_if __P((struct awi_softc *)); +void awi_cmd_get_mib __P((struct awi_softc *sc, u_int8_t, u_int8_t, u_int8_t)); +void awi_cmd_txinit __P((struct awi_softc *sc)); +void awi_cmd_scan __P((struct awi_softc *sc)); +void awi_scan_next __P((struct awi_softc *sc)); +void awi_try_sync __P((struct awi_softc *sc)); +void awi_cmd_set_ss __P((struct awi_softc *sc)); +void awi_cmd_set_promisc __P((struct awi_softc *sc)); +void awi_cmd_set_allmulti __P((struct awi_softc *sc)); +void awi_cmd_set_infra __P((struct awi_softc *sc)); +void awi_cmd_set_notap __P((struct awi_softc *sc)); +void awi_cmd_get_myaddr __P((struct awi_softc *sc)); + + +void awi_cmd_scan_done __P((struct awi_softc *sc, u_int8_t)); +void awi_cmd_sync_done __P((struct awi_softc *sc, u_int8_t)); +void awi_cmd_set_ss_done __P((struct awi_softc *sc, u_int8_t)); +void awi_cmd_set_allmulti_done __P((struct awi_softc *sc, u_int8_t)); +void awi_cmd_set_promisc_done __P((struct awi_softc *sc, u_int8_t)); +void awi_cmd_set_infra_done __P((struct awi_softc *sc, u_int8_t)); +void awi_cmd_set_notap_done __P((struct awi_softc *sc, u_int8_t)); +void awi_cmd_get_myaddr_done __P((struct awi_softc *sc, u_int8_t)); + +void awi_reset __P((struct awi_softc *)); +void awi_init_1 __P((struct awi_softc *)); +void awi_init_2 __P((struct awi_softc *, u_int8_t)); +void awi_mibdump __P((struct awi_softc *, u_int8_t)); +void awi_init_read_bufptrs_done __P((struct awi_softc *, u_int8_t)); +void awi_init_4 __P((struct awi_softc *, u_int8_t)); +void awi_init_5 __P((struct awi_softc *, u_int8_t)); +void awi_init_6 __P((struct awi_softc *, u_int8_t)); +void awi_running __P((struct awi_softc *)); + +void awi_init_txdescr __P((struct awi_softc *)); +void awi_init_txd __P((struct awi_softc *, int, int, int, int)); + +void awi_watchdog __P((struct ifnet *)); +void awi_start __P((struct ifnet *)); +int awi_ioctl __P((struct ifnet *, u_long, caddr_t)); +void awi_dump_rxchain __P((struct awi_softc *, char *, u_int32_t *)); + +void awi_send_frame __P((struct awi_softc *, struct mbuf *)); +void awi_send_authreq __P((struct awi_softc *)); +void awi_send_assocreq __P((struct awi_softc *)); +void awi_parse_tlv __P((u_int8_t *base, u_int8_t *end, u_int8_t **vals, u_int8_t *lens, size_t nattr)); + +u_int8_t *awi_add_rates __P((struct awi_softc *, struct mbuf *, u_int8_t *)); +u_int8_t *awi_add_ssid __P((struct awi_softc *, struct mbuf *, u_int8_t *)); +void * awi_init_hdr __P((struct awi_softc *, struct mbuf *, int, int)); + +void awi_hexdump __P((char *tag, u_int8_t *data, int len)); +void awi_card_hexdump __P((struct awi_softc *, char *tag, u_int32_t offset, int len)); + +int awi_drop_output __P((struct ifnet *, struct mbuf *, + struct sockaddr *, struct rtentry *)); +void awi_drop_input __P((struct ifnet *, struct mbuf *)); +struct mbuf *awi_output_kludge __P((struct awi_softc *, struct mbuf *)); +void awi_set_timer __P((struct awi_softc *)); +void awi_restart_scan __P((struct awi_softc *)); + +struct awi_rxd +{ + u_int32_t next; + u_int16_t len; + u_int8_t state, rate, rssi, index; + u_int32_t frame; + u_int32_t rxts; +}; + +void awi_copy_rxd __P((struct awi_softc *, u_int32_t, struct awi_rxd *)); +u_int32_t awi_parse_rxd __P((struct awi_softc *, u_int32_t, struct awi_rxd *)); + +static const u_int8_t snap_magic[] = { 0xaa, 0xaa, 3, 0, 0, 0 }; + +int awi_scan_keepalive = 10; + +/* + * attach (called by bus-specific front end) + * + * look for banner message + * wait for selftests to complete (up to 2s??? eeee.) + * (do this with a timeout!!??!!) + * on timeout completion: + * issue test_interface command. + * get_mib command to locate TX buffer. + * set_mib command to set any non-default variables. + * init tx first. + * init rx second with enable receiver command + * + * mac mgmt portion executes sync command to start BSS + * + */ + +/* + * device shutdown routine. + */ + +/* + * device appears to be insane. rather than hanging, whap device upside + * the head on next timeout. + */ + +void +awi_insane(sc) + struct awi_softc *sc; +{ + struct ifnet *ifp = sc->sc_ifp; + printf("%s: device timeout\n", sc->sc_dev.dv_xname); + + /* whap device on next timeout. */ + sc->sc_state = AWI_ST_INSANE; + ifp->if_timer = 1; +} + +void +awi_set_timer (sc) + struct awi_softc *sc; +{ + if (sc->sc_tx_timer || sc->sc_scan_timer || + sc->sc_mgt_timer || sc->sc_cmd_timer) + sc->sc_ifp->if_timer = 1; +} + + +/* + * Copy m0 into the given TX descriptor and give the descriptor to the + * device so it starts transmiting.. + */ + +void +awi_tx_packet (sc, txd, m0) + struct awi_softc *sc; + int txd; + struct mbuf *m0; +{ + u_int32_t frame = sc->sc_txd[txd].frame; + u_int32_t len = sc->sc_txd[txd].len; + struct mbuf *m; + + for (m = m0; m != NULL; m = m->m_next) { + u_int32_t nmove; + nmove = min(len, m->m_len); + awi_write_bytes (sc, frame, m->m_data, nmove); + if (nmove != m->m_len) { + printf("%s: large frame truncated\n", + sc->sc_dev.dv_xname); + break; + } + frame += nmove; + len -= nmove; + } + + awi_init_txd (sc, + txd, + AWI_TXD_ST_OWN, + frame - sc->sc_txd[txd].frame, + AWI_RATE_1MBIT); + +#if 0 + awi_card_hexdump (sc, "txd to go", sc->sc_txd[txd].descr, + AWI_TXD_SIZE); +#endif + +} + +/* + * XXX KLUDGE XXX + * + * Convert ethernet-formatted frame into 802.11 data frame + * for infrastructure mode. + */ + +struct mbuf * +awi_output_kludge (sc, m0) + struct awi_softc *sc; + struct mbuf *m0; +{ + u_int8_t *framehdr; + u_int8_t *llchdr; + u_int8_t dstaddr[ETHER_ADDR_LEN]; + struct awi_mac_header *amhdr; + u_int16_t etype; + struct ether_header *eh = mtod(m0, struct ether_header *); + +#if 0 + awi_hexdump("etherframe", m0->m_data, m0->m_len); +#endif + + bcopy(eh->ether_dhost, dstaddr, sizeof(dstaddr)); + etype = eh->ether_type; + + m_adj(m0, sizeof(struct ether_header)); + + M_PREPEND(m0, sizeof(struct awi_mac_header) + 8, M_DONTWAIT); + + if (m0 == NULL) { + printf("oops, prepend failed\n"); + return NULL; + } + + if (m0->m_len < 32) { + printf("oops, prepend only left %d bytes\n", m0->m_len); + m_freem(m0); + return NULL; + } + framehdr = mtod(m0, u_int8_t *); + amhdr = mtod(m0, struct awi_mac_header *); + + amhdr->awi_fc = IEEEWL_FC_VERS | + IEEEWL_FC_TYPE_DATA<<IEEEWL_FC_TYPE_SHIFT; + amhdr->awi_f2 = IEEEWL_FC2_TODS; + + bcopy(dstaddr, amhdr->awi_addr3, ETHER_ADDR_LEN); /* ether DST */ + bcopy(sc->sc_active_bss.bss_id, amhdr->awi_addr1, ETHER_ADDR_LEN); + bcopy(sc->sc_my_addr, amhdr->awi_addr2, ETHER_ADDR_LEN); + amhdr->awi_duration = 0; + amhdr->awi_seqctl = 0; + llchdr = (u_int8_t *) (amhdr + 1); + bcopy(snap_magic, llchdr, 6); + bcopy(&etype, llchdr+6, 2); + + return m0; +} +/* + * device start routine + * + * loop while there are free tx buffer descriptors and mbufs in the queue: + * -> copy mbufs to tx buffer and free mbufs. + * -> mark txd as good to go (OWN bit set, all others clear) + */ + +void +awi_start(ifp) + struct ifnet *ifp; +{ + struct awi_softc *sc = ifp->if_softc; + struct mbuf *m0; + int opending; + + if ((ifp->if_flags & IFF_RUNNING) == 0) { + printf("%s: start called while not running\n", + sc->sc_dev.dv_xname); + return; + } + + /* + * loop through send queue, setting up tx descriptors + * until we either run out of stuff to send, or descriptors + * to send them in. + */ + opending = sc->sc_txpending; + + while (sc->sc_txpending < sc->sc_ntxd) { + /* + * Grab a packet off the queue. + */ + IF_DEQUEUE (&sc->sc_mgtq, m0); + + if (m0 == NULL) { + /* XXX defer sending if not synched yet? */ + IF_DEQUEUE (&ifp->if_snd, m0); + if (m0 == NULL) + break; +#if NBPFILTER > 0 + /* + * Pass packet to bpf if there is a listener. + */ + if (ifp->if_bpf) + bpf_mtap(ifp->if_bpf, m0); +#endif + /* + * We've got an ethernet-format frame. + * we need to mangle it into 802.11 form.. + */ + m0 = awi_output_kludge(sc, m0); + if (m0 == NULL) + continue; + } + + awi_tx_packet(sc, sc->sc_txnext, m0); + + sc->sc_txpending++; + sc->sc_txnext = (sc->sc_txnext + 1) % sc->sc_ntxd; + + m_freem(m0); + } + if (sc->sc_txpending >= sc->sc_ntxd) { + /* no more slots available.. */ + ifp->if_flags |= IFF_OACTIVE; + } + if (sc->sc_txpending != opending) { + /* set watchdog timer in case unit flakes out */ + if (sc->sc_tx_timer == 0) + sc->sc_tx_timer = 5; + awi_set_timer(sc); + } +} + +int +awi_enable(sc) + struct awi_softc *sc; +{ + if (sc->sc_enabled == 0) { + if ((sc->sc_enable != NULL) && ((*sc->sc_enable)(sc) != 0)) { + printf("%s: device enable failed\n", + sc->sc_dev.dv_xname); + return (EIO); + } + awi_init(sc); + } + sc->sc_enabled = 1; + return 0; +} + +void +awi_disable(sc) + struct awi_softc *sc; +{ + if (sc->sc_enabled != 0 && sc->sc_disable != NULL) { + (*sc->sc_disable)(sc); + sc->sc_enabled = 0; + } +} + + + +int +awi_intlock(sc) + struct awi_softc *sc; +{ + int i, j; + u_int8_t lockout; + + DELAY(5); + for (j=0; j<10; j++) { + for (i=0; i<AWI_LOCKOUT_SPIN; i++) { + lockout = awi_read_1(sc, AWI_LOCKOUT_HOST); + if (!lockout) + break; + DELAY(5); + } + if (lockout) + break; + awi_write_1 (sc, AWI_LOCKOUT_MAC, 1); + lockout = awi_read_1(sc, AWI_LOCKOUT_HOST); + + if (!lockout) + break; + /* oops, lost the race.. try again */ + awi_write_1 (sc, AWI_LOCKOUT_MAC, 0); + } + + if (lockout) { + awi_insane(sc); + return 0; + } + return 1; +} + +void +awi_intunlock(sc) + struct awi_softc *sc; +{ + awi_write_1 (sc, AWI_LOCKOUT_MAC, 0); +} + +void +awi_intrinit(sc) + struct awi_softc *sc; +{ + u_int8_t intmask; + + am79c930_gcr_setbits(&sc->sc_chip, AM79C930_GCR_ENECINT); + + intmask = AWI_INT_GROGGY|AWI_INT_SCAN_CMPLT| + AWI_INT_TX|AWI_INT_RX|AWI_INT_CMD; + + intmask = ~intmask; + + if (!awi_intlock(sc)) + return; + + awi_write_1(sc, AWI_INTMASK, intmask); + awi_write_1(sc, AWI_INTMASK2, 0); + + awi_intunlock(sc); +} + +void awi_hexdump (char *tag, u_int8_t *data, int len) +{ + int i; + + printf("%s:", tag); + for (i=0; i<len; i++) { + printf(" %02x", data[i]); + } + printf("\n"); +} + +void awi_card_hexdump (sc, tag, offset, len) + struct awi_softc *sc; + char *tag; + u_int32_t offset; + int len; +{ + int i; + + printf("%s:", tag); + for (i=0; i<len; i++) { + printf(" %02x", awi_read_1(sc, offset+i)); + } + printf("\n"); +} + +u_int8_t +awi_read_intst(sc) + struct awi_softc *sc; +{ + u_int8_t state; + + if (!awi_intlock(sc)) + return 0; + + /* we have int lock.. */ + + state = awi_read_1 (sc, AWI_INTSTAT); + awi_write_1(sc, AWI_INTSTAT, 0); + + awi_intunlock(sc); + + return state; +} + + +void +awi_parse_tlv (u_int8_t *base, u_int8_t *end, u_int8_t **vals, u_int8_t *lens, size_t nattr) +{ + u_int8_t tag, len; + + int i; + + for (i=0; i<nattr; i++) { + vals[i] = NULL; + lens[i] = 0; + } + + while (base < end) { + tag = base[0]; + len = base[1]; + + base += 2; + + if (tag < nattr) { + lens[tag] = len; + vals[tag] = base; + } + base += len; + } +} + +void +awi_send_frame (sc, m) + struct awi_softc *sc; + struct mbuf *m; +{ + IF_ENQUEUE(&sc->sc_mgtq, m); + + awi_start(sc->sc_ifp); +} + +void * +awi_init_hdr (sc, m, f1, f2) + struct awi_softc *sc; + struct mbuf *m; + int f1; + int f2; +{ + struct awi_mac_header *amhp; + + /* + * initialize 802.11 mac header in mbuf, return pointer to next byte.. + */ + + amhp = mtod(m, struct awi_mac_header *); + + amhp->awi_fc = f1; + amhp->awi_f2 = f2; + amhp->awi_duration = 0; + + bcopy(sc->sc_active_bss.bss_id, amhp->awi_addr1, ETHER_ADDR_LEN); + bcopy(sc->sc_my_addr, amhp->awi_addr2, ETHER_ADDR_LEN); + bcopy(sc->sc_active_bss.bss_id, amhp->awi_addr3, ETHER_ADDR_LEN); + + amhp->awi_seqctl = 0; + + return amhp+1; +} + + + +u_int8_t * +awi_add_rates (sc, m, ptr) + struct awi_softc *sc; + struct mbuf *m; + u_int8_t *ptr; +{ + *ptr++ = 1; /* XXX */ + *ptr++ = 1; /* XXX */ + *ptr++ = 0x82; /* XXX */ + return ptr; +} + +u_int8_t * +awi_add_ssid (sc, m, ptr) + struct awi_softc *sc; + struct mbuf *m; + u_int8_t *ptr; +{ + int len = sc->sc_active_bss.sslen; + *ptr++ = 0; /* XXX */ + *ptr++ = len; + bcopy(sc->sc_active_bss.ssid, ptr, len); + ptr += len; + return ptr; +} + + + +void +awi_send_authreq (sc) + struct awi_softc *sc; +{ + struct mbuf *m; + struct awi_auth_hdr *amahp; + u_int8_t *tlvptr; + + MGETHDR(m, M_DONTWAIT, MT_DATA); + + /* + * form an "association request" message. + */ + + /* + * auth alg number. 2 bytes. = 0 + * auth txn seq number = 2 bytes = 1 + * status code = 2 bytes = 0 + * challenge text (not present) + */ + + if (m == 0) + return; /* we'll try again later.. */ + + amahp = awi_init_hdr (sc, m, + (IEEEWL_FC_VERS | + (IEEEWL_FC_TYPE_MGT << IEEEWL_FC_TYPE_SHIFT) | + (IEEEWL_SUBTYPE_AUTH << IEEEWL_FC_SUBTYPE_SHIFT)), + 0); + + amahp->awi_algno[0] = 0; + amahp->awi_algno[1] = 0; + amahp->awi_seqno[0] = 1; + amahp->awi_seqno[1] = 0; + amahp->awi_status[0] = 0; + amahp->awi_status[1] = 0; + + /* + * form an "authentication" message. + */ + + tlvptr = (u_int8_t *)(amahp+1); + + tlvptr = awi_add_ssid(sc, m, tlvptr); + tlvptr = awi_add_rates(sc, m, tlvptr); + + m->m_len = tlvptr - mtod(m, u_int8_t *); + + if (sc->sc_ifp->if_flags & IFF_DEBUG) { + printf("%s: sending auth request\n", + sc->sc_dev.dv_xname); + awi_hexdump("frame", m->m_data, m->m_len); + } + + awi_send_frame(sc, m); + + sc->sc_mgt_timer = 2; + awi_set_timer(sc); +} + +void +awi_send_assocreq (sc) + struct awi_softc *sc; +{ + struct mbuf *m; + struct awi_assoc_hdr *amahp; + u_int8_t *tlvptr; + + MGETHDR(m, M_DONTWAIT, MT_DATA); + + /* + * form an "association request" message. + */ + + if (m == 0) + return; /* we'll try again later.. */ + + /* + * cap info (2 bytes) + * listen interval (2 bytes) + * ssid (variable) + * supported rates (variable) + */ + + amahp = awi_init_hdr (sc, m, + IEEEWL_FC_TYPE_MGT, IEEEWL_SUBTYPE_ASSOCREQ); + + amahp->awi_cap_info[0] = 4; /* XXX magic (CF-pollable) */ + amahp->awi_cap_info[1] = 0; + amahp->awi_li[0] = 1; + amahp->awi_li[1] = 0; + + tlvptr = (u_int8_t *)(amahp+1); + + tlvptr = awi_add_ssid(sc, m, tlvptr); + tlvptr = awi_add_rates(sc, m, tlvptr); + + m->m_len = tlvptr - mtod(m, u_int8_t *); + + + if (sc->sc_ifp->if_flags & IFF_DEBUG) { + printf("%s: sending assoc request\n", + sc->sc_dev.dv_xname); + awi_hexdump("frame", m->m_data, m->m_len); + } + + awi_send_frame(sc, m); + + sc->sc_mgt_timer = 2; + awi_set_timer(sc); +} + +#if 0 +void +awi_send_reassocreq (sc) +{ + + /* + * form an "reassociation request" message. + */ + + /* 2 bytes frame control + 00100000 00000000 + 2 bytes goo + 00000000 00000000 + address 1: bssid + address 2: my address + address 3: bssid + 2 bytes seq/ctl + 00000000 00000000 + + cap info (2 bytes) + listen interval (2 bytes) + current ap address (6 bytes) + ssid (variable) + supported rates (va + */ +} + +#endif + +void +awi_rcv_ctl (sc, m) + struct awi_softc *sc; + struct mbuf *m; +{ + printf("%s: ctl\n", sc->sc_dev.dv_xname); +} + +void +awi_rcv_data (sc, m) + struct awi_softc *sc; + struct mbuf *m; +{ + struct ifnet *ifp = sc->sc_ifp; + u_int8_t *llc; + u_int8_t *to, *from; + struct awi_mac_header *amhp; + + sc->sc_scan_timer = awi_scan_keepalive; /* user data is as good + as a beacon as a keepalive.. */ + + amhp = mtod(m, struct awi_mac_header *); + + /* + * we have: 4 bytes useless goo. + * 3 x 6 bytes MAC addresses. + * 2 bytes goo. + * 802.x LLC header, SNAP header, and data. + * + * for now, we fake up a "normal" ethernet header and feed + * this to the appropriate input routine. + */ + + llc = (u_int8_t *)(amhp+1); + + if (amhp->awi_f2 & IEEEWL_FC2_TODS) { + printf("drop packet to DS\n"); + goto drop; + } + + to = amhp->awi_addr1; + if (amhp->awi_f2 & IEEEWL_FC2_FROMDS) + from = amhp->awi_addr3; + else + from = amhp->awi_addr2; + if (memcmp (llc, snap_magic, 6) != 0) + goto drop; + + /* XXX overwrite llc with "from" address */ + /* XXX overwrite llc-6 with "to" address */ + bcopy(from, llc, ETHER_ADDR_LEN); + bcopy(to, llc-6, ETHER_ADDR_LEN); + + m_adj(m, sizeof(struct awi_mac_header) + sizeof(struct awi_llc_header) + - sizeof(struct ether_header)); + +#if NBPFILTER > 0 + /* + * Pass packet to bpf if there is a listener. + */ + if (ifp->if_bpf) + bpf_mtap(ifp->if_bpf, m); +#endif + +#if __NetBSD_Version__ > 104010000 + m->m_flags |= M_HASFCS; + (*ifp->if_input)(ifp, m); +#else + { + struct ether_header *eh; + eh = mtod(m, struct ether_header *); + m_adj(m, sizeof(*eh)); + m_adj(m, -ETHER_CRC_LEN); + ether_input(ifp, eh, m); + } +#endif + return; + drop: + m_freem(m); +} + +void +awi_rcv_mgt (sc, m, rxts, rssi) + struct awi_softc *sc; + struct mbuf *m; + u_int32_t rxts; + u_int8_t rssi; +{ + u_int8_t subtype; + u_int8_t *framehdr, *mgthdr, *end, *timestamp; + struct awi_auth_hdr *auhp; + struct ifnet *ifp = sc->sc_ifp; + +#define IEEEWL_MGT_NATTR 10 /* XXX */ + u_int8_t *attr[IEEEWL_MGT_NATTR]; + u_int8_t attrlen[IEEEWL_MGT_NATTR]; + u_int8_t *addr1, *addr2, *addr3; + u_int8_t *sa, *da, *bss; + + framehdr = mtod(m, u_int8_t *); + + /* + * mgt frame: + * 2 bytes frame goo + * 2 bytes duration + * 6 bytes a1 + * 6 bytes a2 + * 6 bytes a3 + * 2 bytes seq control. + * -- + * 24 bytes goo. + */ + + subtype = (framehdr[IEEEWL_FC] & IEEEWL_FC_SUBTYPE_MASK) + >> IEEEWL_FC_SUBTYPE_SHIFT; + + addr1 = framehdr + 4; /* XXX */ + addr2 = addr1+ETHER_ADDR_LEN; + addr3 = addr2+ETHER_ADDR_LEN; + + /* XXX look at to/from DS bits here!! */ + da = addr1; + sa = addr3; + bss = addr2; + + framehdr = mtod(m, u_int8_t *); + end = framehdr + m->m_len; + end -= 4; /* trim TLV */ + + mgthdr = framehdr + 24; /* XXX magic */ + + switch (subtype) { + + case IEEEWL_SUBTYPE_ASSOCRESP: + /* + * this acknowledges that the AP will be forwarding traffic + * for us.. + * + * contains: + * cap info + * status code + * AId + * supported rates. + */ + if (ifp->if_flags & IFF_DEBUG) { + printf("%s: got assoc resp\n", + sc->sc_dev.dv_xname); + awi_hexdump("assocresp", m->m_data, m->m_len); + } + awi_drvstate (sc, AWI_DRV_INFASSOC); + sc->sc_state = AWI_ST_RUNNING; + sc->sc_mgt_timer = AWI_ASSOC_REFRESH; + awi_set_timer(sc); + if (sc->sc_new_bss) { + printf("%s: associated with %s, SSID: %s\n", + sc->sc_dev.dv_xname, + ether_sprintf(sc->sc_active_bss.bss_id), + sc->sc_active_bss.ssid); + sc->sc_new_bss = 0; + } + + /* XXX set media status to "i see carrier" */ + break; + + case IEEEWL_SUBTYPE_REASSOCRESP: + /* + * this indicates that we've moved from one AP to another + * within the same DS. + */ + printf("reassoc_resp\n"); + + break; + + case IEEEWL_SUBTYPE_PROBEREQ: + /* discard */ + break; + + case IEEEWL_SUBTYPE_PROBERESP: + /* + * 8 bytes timestamp. + * 2 bytes beacon intvl. + * 2 bytes cap info. + * then tlv data.. + */ + timestamp = mgthdr; + + if (ifp->if_flags & IFF_DEBUG) { + printf("%s: got probe resp\n", + sc->sc_dev.dv_xname); + awi_hexdump("proberesp", m->m_data, m->m_len); + } + /* now, into the tlv goo.. */ + mgthdr += 12; /* XXX magic */ + awi_parse_tlv (mgthdr, end, attr, attrlen, IEEEWL_MGT_NATTR); + + if (attr[IEEEWL_MGT_TLV_SSID] && + attr[IEEEWL_MGT_TLV_FHPARMS] && + attrlen[IEEEWL_MGT_TLV_SSID] < AWI_SSID_LEN) { + struct awi_bss_binding *bp = NULL; + int i; + + for (i=0; i< sc->sc_nbindings; i++) { + struct awi_bss_binding *bp1 = + &sc->sc_bindings[i]; + if (memcmp(bp1->bss_id, bss, ETHER_ADDR_LEN) == 0) { + bp = bp1; + break; + } + } + + if (bp == NULL && sc->sc_nbindings < NBND) { + bp = &sc->sc_bindings[sc->sc_nbindings++]; + } + if (bp != NULL) { + u_int8_t *fhparms = + attr[IEEEWL_MGT_TLV_FHPARMS]; + + bp->sslen = attrlen[IEEEWL_MGT_TLV_SSID]; + + bcopy(attr[IEEEWL_MGT_TLV_SSID], bp->ssid, + bp->sslen); + bp->ssid[bp->sslen] = 0; + + bcopy(bss, bp->bss_id, ETHER_ADDR_LEN); + + /* XXX more magic numbers.. */ + bp->dwell_time = fhparms[0] | (fhparms[1]<<8); + bp->chanset = fhparms[2]; + bp->pattern = fhparms[3]; + bp->index = fhparms[4]; + bp->rssi = rssi; + bp->rxtime = rxts; + bcopy(timestamp, bp->bss_timestamp, 8); + } + } + + break; + + case IEEEWL_SUBTYPE_BEACON: + if ((ifp->if_flags & (IFF_DEBUG|IFF_LINK2)) == + (IFF_DEBUG|IFF_LINK2)) { + printf("%s: beacon from %s\n", + sc->sc_dev.dv_xname, + ether_sprintf(addr2)); + awi_hexdump("beacon", m->m_data, m->m_len); + } + /* + * Note that AP is still alive so we don't have to go looking + * for one for a while. + * + * XXX Beacons from other AP's should be recorded for + * potential use if we lose this AP.. (also, may want + * to notice if rssi of new AP is significantly + * stronger than old one and jump ship..) + */ + if ((sc->sc_state >= AWI_ST_SYNCED) && + (memcmp (addr2, sc->sc_active_bss.bss_id, + ETHER_ADDR_LEN) == 0)) { + sc->sc_scan_timer = awi_scan_keepalive; + awi_set_timer(sc); + } + + break; + + case IEEEWL_SUBTYPE_DISSOC: + printf("dissoc\n"); + + break; + + case IEEEWL_SUBTYPE_AUTH: + if (ifp->if_flags & IFF_DEBUG) { + printf("%s: got auth\n", + sc->sc_dev.dv_xname); + awi_hexdump("auth", m->m_data, m->m_len); + } + /* + * woohoo! somebody likes us! + */ + + auhp = (struct awi_auth_hdr *)mgthdr; + + if ((auhp->awi_status[0] == 0) && (auhp->awi_status[1] == 0)) + { + awi_drvstate (sc, AWI_DRV_INFAUTH); + sc->sc_state = AWI_ST_AUTHED; + awi_send_assocreq (sc); + } + break; + + case IEEEWL_SUBTYPE_DEAUTH: + if (ifp->if_flags & IFF_DEBUG) { + printf("%s: got deauth\n", + sc->sc_dev.dv_xname); + awi_hexdump("deauth", m->m_data, m->m_len); + } + sc->sc_state = AWI_ST_SYNCED; + sc->sc_new_bss = 1; + awi_send_authreq(sc); + break; + default: + printf("unk mgt subtype %x\n", subtype); + break; + } + m_freem(m); /* done.. */ +} + + + + + +/* + * Do 802.11 receive processing. "m" contains a receive frame; + * rxts is the local receive timestamp + */ + +void +awi_rcv (sc, m, rxts, rssi) + struct awi_softc *sc; + struct mbuf *m; + u_int32_t rxts; + u_int8_t rssi; +{ + u_int8_t *framehdr; + u_int8_t framectl; + + framehdr = mtod(m, u_int8_t *); + + /* + * peek at first byte of frame header. + * check version subfield (must be zero) + * check type subfield (00 = mgt, 01 = ctl, 10 = data) + * check subtype field (next four bits) + */ + + /* + * Not counting WDS mode, the IEEE 802.11 frame header format + * has *three* MAC addresses. + * (source, destination, and BSS). + * + * The BSS indicates which wireless "cable segment" we're part of; + * we discover this dynamically.. + * + * Not content to put them in a fixed order, the exact + * ordering of these addresses depends on other attribute bits + * in the frame control word! + * + * an alternate presentation which is more self-consistent: + * address 1 is the "wireless destination" -- either the + * station address, + * for wireless->wireless traffic, or the BSS id of an AP. + * + * address 2 is the "wireless source" -- either the + * station address of a wireless node, or the BSS id of an AP. + * + * address 3 is the "other address" -- for STA->AP, the + * eventual destination; for AP->STA, the original source, and + * for ad-hoc mode, the BSS id.. + */ + + framectl = framehdr[IEEEWL_FC]; + + if ((framectl & IEEEWL_FC_VERS_MASK) != IEEEWL_FC_VERS) { + printf("wrong vers. drop"); + goto drop; + } + + switch (framectl & IEEEWL_FC_TYPE_MASK) { + case IEEEWL_FC_TYPE_MGT << IEEEWL_FC_TYPE_SHIFT: + awi_rcv_mgt (sc, m, rxts, rssi); + m = 0; + break; + + case IEEEWL_FC_TYPE_DATA << IEEEWL_FC_TYPE_SHIFT: + awi_rcv_data (sc, m); + m = 0; + break; + + case IEEEWL_FC_TYPE_CTL << IEEEWL_FC_TYPE_SHIFT: + awi_rcv_ctl (sc, m); + default: + goto drop; + } + + drop: + if (m) m_freem(m); +} + +void +awi_copy_rxd (sc, cur, rxd) + struct awi_softc *sc; + u_int32_t cur; + struct awi_rxd *rxd; +{ + if (sc->sc_ifp->if_flags & IFF_LINK0) { + printf("%x: ", cur); + awi_card_hexdump(sc, "rxd", cur, AWI_RXD_SIZE); + } + + rxd->next = awi_read_4(sc, cur + AWI_RXD_NEXT); + rxd->state = awi_read_1(sc, cur + AWI_RXD_HOST_DESC_STATE); + rxd->len = awi_read_2 (sc, cur + AWI_RXD_LEN); + rxd->rate = awi_read_1 (sc, cur + AWI_RXD_RATE); + rxd->rssi = awi_read_1 (sc, cur + AWI_RXD_RSSI); + rxd->index = awi_read_1 (sc, cur + AWI_RXD_INDEX); + rxd->frame = awi_read_4 (sc, cur + AWI_RXD_START_FRAME); + rxd->rxts = awi_read_4 (sc, cur + AWI_RXD_LOCALTIME); + + /* + * only the low order bits of "frame" and "next" are valid. + * (the documentation doesn't mention this). + */ + rxd->frame &= 0xffff; + rxd->next &= (0xffff | AWI_RXD_NEXT_LAST); + + /* + * XXX after masking, sanity check that rxd->frame and + * rxd->next lie within the receive area. + */ + if (sc->sc_ifp->if_flags & IFF_LINK0) { + printf("nxt %x frame %x state %b len %d\n", + rxd->next, rxd->frame, + rxd->state, AWI_RXD_ST_BITS, + rxd->len); + } +} + + +u_int32_t +awi_parse_rxd (sc, cur, rxd) + struct awi_softc *sc; + u_int32_t cur; + struct awi_rxd *rxd; +{ + struct mbuf *top; + struct ifnet *ifp = sc->sc_ifp; + u_int32_t next; + + if ((rxd->state & AWI_RXD_ST_CONSUMED) == 0) { + if (ifp->if_flags & IFF_LINK1) { + int xx = awi_read_1(sc, rxd->frame); + if (xx != (IEEEWL_FC_VERS | + (IEEEWL_FC_TYPE_MGT<<IEEEWL_FC_TYPE_SHIFT) | + (IEEEWL_SUBTYPE_BEACON << IEEEWL_FC_SUBTYPE_SHIFT))) { + char bitbuf[64]; + printf("floosh: %d state ", sc->sc_flushpkt); + snprintf(bitbuf, sizeof bitbuf, "%b", + rxd->state, AWI_RXD_ST_BITS); + awi_card_hexdump(sc, bitbuf, rxd->frame, + rxd->len); + } + + } + if ((sc->sc_flushpkt == 0) && + (sc->sc_nextpkt == NULL)) { + MGETHDR(top, M_DONTWAIT, MT_DATA); + + if (top == NULL) { + sc->sc_flushpkt = 1; + sc->sc_m = NULL; + sc->sc_mptr = NULL; + sc->sc_mleft = 0; + } else { + if (rxd->len >= MINCLSIZE) + MCLGET(top, M_DONTWAIT); + + top->m_pkthdr.rcvif = ifp; + top->m_pkthdr.len = 0; + top->m_len = 0; + + sc->sc_mleft = (top->m_flags & M_EXT) ? + MCLBYTES : MHLEN; + sc->sc_mptr = mtod(top, u_int8_t *); + sc->sc_m = top; + sc->sc_nextpkt = top; + } + } + if (sc->sc_flushpkt == 0) { + /* copy data into mbuf */ + + while (rxd->len > 0) { + int nmove = min (rxd->len, sc->sc_mleft); + + awi_read_bytes (sc, rxd->frame, sc->sc_mptr, + nmove); + + rxd->len -= nmove; + rxd->frame += nmove; + sc->sc_mleft -= nmove; + sc->sc_mptr += nmove; + + sc->sc_nextpkt->m_pkthdr.len += nmove; + sc->sc_m->m_len += nmove; + + if ((rxd->len > 0) && (sc->sc_mleft == 0)) { + struct mbuf *m1; + + /* Get next mbuf.. */ + MGET(m1, M_DONTWAIT, MT_DATA); + if (m1 == NULL) { + m_freem(sc->sc_nextpkt); + sc->sc_nextpkt = NULL; + sc->sc_flushpkt = 1; + sc->sc_m = NULL; + sc->sc_mptr = NULL; + sc->sc_mleft = 0; + break; + } + sc->sc_m->m_next = m1; + sc->sc_m = m1; + m1->m_len = 0; + + sc->sc_mleft = MLEN; + sc->sc_mptr = mtod(m1, u_int8_t *); + } + } + } + if (rxd->state & AWI_RXD_ST_LF) { + if (sc->sc_flushpkt) { + sc->sc_flushpkt = 0; + } + else if (sc->sc_nextpkt != NULL) { + struct mbuf *m = sc->sc_nextpkt; + sc->sc_nextpkt = NULL; + sc->sc_flushpkt = 0; + sc->sc_m = NULL; + sc->sc_mptr = NULL; + sc->sc_mleft = 0; + awi_rcv(sc, m, rxd->rxts, rxd->rssi); + } + } + } + rxd->state |= AWI_RXD_ST_CONSUMED; + awi_write_1(sc, cur + AWI_RXD_HOST_DESC_STATE, rxd->state); + next = cur; + if ((rxd->next & AWI_RXD_NEXT_LAST) == 0) { + rxd->state |= AWI_RXD_ST_OWN; + awi_write_1(sc, cur + AWI_RXD_HOST_DESC_STATE, rxd->state); + next = rxd->next; + } + return next; +} + +void +awi_dump_rxchain (sc, what, descr) + struct awi_softc *sc; + char *what; + u_int32_t *descr; +{ + u_int32_t cur, next; + struct awi_rxd rxd; + + cur = *descr; + + if (cur & AWI_RXD_NEXT_LAST) + return; + + do { + awi_copy_rxd(sc, cur, &rxd); + + next = awi_parse_rxd(sc, cur, &rxd); + if ((rxd.state & AWI_RXD_ST_OWN) && (next == cur)) { + printf("%s: loop in rxd list?", + sc->sc_dev.dv_xname); + break; + } + cur = next; + } while (rxd.state & AWI_RXD_ST_OWN); + + *descr = cur; +} + +void +awi_rxint (sc) + struct awi_softc *sc; +{ + awi_dump_rxchain (sc, "mgt", &sc->sc_rx_mgt_desc); + awi_dump_rxchain (sc, "data", &sc->sc_rx_data_desc); +} + +void +awi_init_txd (sc, tx, flag, len, rate) + struct awi_softc *sc; + int tx; + int flag; + int len; + int rate; +{ + u_int32_t txdbase = sc->sc_txd[tx].descr; + u_int32_t framebase = sc->sc_txd[tx].frame; + u_int32_t nextbase = sc->sc_txd[(tx+1)%sc->sc_ntxd].descr; + + awi_write_4 (sc, txdbase + AWI_TXD_START, framebase); + awi_write_4 (sc, txdbase + AWI_TXD_NEXT, nextbase); + awi_write_4 (sc, txdbase + AWI_TXD_LENGTH, len); + awi_write_1 (sc, txdbase + AWI_TXD_RATE, rate); + /* zeroize tail end of txd */ + awi_write_4 (sc, txdbase + AWI_TXD_NDA, 0); + awi_write_4 (sc, txdbase + AWI_TXD_NRA, 0); + /* Init state last; firmware keys off of this to know when to start tx */ + awi_write_1 (sc, txdbase + AWI_TXD_STATE, flag); +} + +void +awi_init_txdescr (sc) + struct awi_softc *sc; +{ + int i; + u_int32_t offset = sc->sc_txbase; + + sc->sc_txfirst = 0; + sc->sc_txnext = 0; + + sc->sc_ntxd = sc->sc_txlen / (AWI_FRAME_SIZE + AWI_TXD_SIZE); + if (sc->sc_ntxd > NTXD) { + sc->sc_ntxd = NTXD; + printf("oops, no, only %d\n", sc->sc_ntxd); + } + + /* Allocate TXD's */ + for (i=0; i<sc->sc_ntxd; i++) { + sc->sc_txd[i].descr = offset; + offset += AWI_TXD_SIZE; + } + /* now, allocate buffer space to each txd.. */ + for (i=0; i<sc->sc_ntxd; i++) { + sc->sc_txd[i].frame = offset; + sc->sc_txd[i].len = AWI_FRAME_SIZE; + offset += AWI_FRAME_SIZE; + + } + + /* now, initialize the TX descriptors into a circular linked list. */ + + for (i= 0; i<sc->sc_ntxd; i++) { + awi_init_txd(sc, i, 0, 0, 0); + } +} + +void +awi_txint (sc) + struct awi_softc *sc; +{ + struct ifnet *ifp = sc->sc_ifp; + int txfirst; + + sc->sc_tx_timer = 0; + + txfirst = sc->sc_txfirst; + while (sc->sc_txpending > 0) { + u_int8_t flags = awi_read_1 (sc, sc->sc_txd[txfirst].descr + + AWI_TXD_STATE); + + if (flags & AWI_TXD_ST_OWN) + break; + + if (flags & AWI_TXD_ST_ERROR) { + /* increment oerrs */; + } + + txfirst = (txfirst + 1) % sc->sc_ntxd; + sc->sc_txpending--; + } + + sc->sc_txfirst = txfirst; + + if (sc->sc_txpending < sc->sc_ntxd) + ifp->if_flags &= ~IFF_OACTIVE; + + /* + * see which descriptors are done.. + */ + + awi_start(sc->sc_ifp); +} + + + + +/* + * device interrupt routine. + * + * lock out MAC + * loop: + * look at intr status, DTRT. + * + * on tx done, reclaim free buffers from tx, call start. + * on rx done, look at rx queue, copy to mbufs, mark as free, + * hand to ether media layer rx routine. + * on cmd done, call cmd cmpl continuation. + * + */ + +int +awi_intr(arg) + void *arg; +{ + struct awi_softc *sc = arg; + int handled = 0; + + if (sc->sc_state == AWI_ST_OFF) { + u_int8_t intstate = awi_read_intst (sc); + return intstate != 0; + } + + /* disable power down, (and implicitly ack interrupt) */ + am79c930_gcr_setbits(&sc->sc_chip, AM79C930_GCR_DISPWDN); + awi_write_1(sc, AWI_DIS_PWRDN, 1); + + for (;;) { + u_int8_t intstate = awi_read_intst (sc); + + if (!intstate) + break; + + handled = 1; + + if (intstate & AWI_INT_RX) + awi_rxint(sc); + + if (intstate & AWI_INT_TX) + awi_txint(sc); + + if (intstate & AWI_INT_CMD) { + u_int8_t status; + + if (!(sc->sc_flags & AWI_FL_CMD_INPROG)) + printf("%s: no command in progress?\n", + sc->sc_dev.dv_xname); + status = awi_read_1(sc, AWI_CMD_STATUS); + awi_write_1 (sc, AWI_CMD, 0); + sc->sc_cmd_timer = 0; + sc->sc_flags &= ~AWI_FL_CMD_INPROG; + + if (sc->sc_completion) + (*sc->sc_completion)(sc, status); + } + if (intstate & AWI_INT_SCAN_CMPLT) { + if (sc->sc_flags & AWI_FL_CMD_INPROG) { + panic("i can't take it any more"); + } + /* + * scan completion heuristic.. + */ + if ((sc->sc_nbindings >= NBND) + || ((sc->sc_scan_timer == 0) && + (sc->sc_nbindings > 0))) + awi_try_sync(sc); + else + awi_scan_next(sc); + } + + } + /* reenable power down */ + am79c930_gcr_clearbits(&sc->sc_chip, AM79C930_GCR_DISPWDN); + awi_write_1(sc, AWI_DIS_PWRDN, 0); + + return handled; +} + +/* + * flush tx queues.. + */ + +void +awi_flush(sc) + struct awi_softc *sc; +{ + struct ifnet *ifp = sc->sc_ifp; + struct mbuf *m; + + do { + IF_DEQUEUE (&sc->sc_mgtq, m); + m_freem(m); + } while (m != NULL); + + do { + IF_DEQUEUE (&ifp->if_snd, m); + m_freem(m); + } while (m != NULL); +} + + + +/* + * device stop routine + */ + +void +awi_stop(sc) + struct awi_softc *sc; +{ + struct ifnet *ifp = sc->sc_ifp; + + awi_flush(sc); + + /* Turn off timer.. */ + ifp->if_timer = 0; + sc->sc_state = AWI_ST_OFF; + (void) awi_read_intst (sc); + /* + * XXX for pcmcia, there's no point in disabling the device, + * as it's about to be powered off.. + * for non-PCMCIA attachments, we should, however, stop + * the receiver and transmitter here. + */ +} + +/* + * Watchdog routine, triggered by timer. + * This does periodic maintainance-type tasks on the interface. + */ + +void +awi_watchdog(ifp) + struct ifnet *ifp; +{ + struct awi_softc *sc = ifp->if_softc; + u_int8_t test; + int i; + + if (sc->sc_state == AWI_ST_OFF) + /* nothing to do */ + return; + else if (sc->sc_state == AWI_ST_INSANE) { + awi_reset(sc); + return; + } else if (sc->sc_state == AWI_ST_SELFTEST) { + /* check for selftest completion.. */ + test = awi_read_1(sc, AWI_SELFTEST); + if ((test & 0xf0) == 0xf0) { /* XXX magic numbers */ + if (test == AWI_SELFTEST_PASSED) { + awi_init_1(sc); + } else { + printf("%s: selftest failed (code %x)\n", + sc->sc_dev.dv_xname, test); + awi_reset(sc); + } + } + sc->sc_selftest_tries++; + /* still running. try again on next tick */ + if (sc->sc_selftest_tries < 5) { + ifp->if_timer = 1; + } else { + /* + * XXX should power down card, wait 1s, power it back + * up again.. + */ + printf("%s: device failed to complete selftest (code %x)\n", + sc->sc_dev.dv_xname, test); + ifp->if_timer = 0; + } + return; + } + + + /* + * command timer: if it goes to zero, device failed to respond. + * boot to the head. + */ + if (sc->sc_cmd_timer) { + sc->sc_cmd_timer--; + if (sc->sc_cmd_timer == 0) { + sc->sc_flags &= ~AWI_FL_CMD_INPROG; + + printf("%s: timeout waiting for command completion\n", + sc->sc_dev.dv_xname); + test = awi_read_1(sc, AWI_CMD_STATUS); + printf("%s: cmd status: %x\n", sc->sc_dev.dv_xname, test); + test = awi_read_1(sc, AWI_CMD); + printf("%s: cmd: %x\n", sc->sc_dev.dv_xname, test); + awi_card_hexdump(sc, "CSB", AWI_CSB, 16); + awi_reset(sc); + return; + } + } + /* + * Transmit timer. If it goes to zero, device failed to deliver a + * tx complete interrupt. boot to the head. + */ + if (sc->sc_tx_timer) { + sc->sc_tx_timer--; + if ((sc->sc_tx_timer == 0) && (sc->sc_txpending)) { + awi_card_hexdump(sc, "CSB", AWI_CSB, 16); + printf("%s: transmit timeout\n", sc->sc_dev.dv_xname); + awi_card_hexdump(sc, "last_txd", AWI_LAST_TXD, 5*4); + for (i=0; i<sc->sc_ntxd; i++) { + awi_card_hexdump(sc, "txd", + sc->sc_txd[i].descr, AWI_TXD_SIZE); + } + awi_reset(sc); + return; + } + } + /* + * Scan timer. + * When synched, this is used to notice when we've stopped + * receiving beacons and should attempt to resynch. + * + * When unsynched, this is used to notice if we've received an + * interesting probe response and should synch up. + */ + + if (sc->sc_scan_timer) { + sc->sc_scan_timer--; + if (sc->sc_scan_timer == 0) { + if (sc->sc_state == AWI_ST_SCAN) { + /* + * XXX what if device fails to deliver + * a scan-completion interrupt? + */ + } else { + printf("%s: no recent beacon from %s; rescanning\n", + sc->sc_dev.dv_xname, + ether_sprintf(sc->sc_active_bss.bss_id)); + awi_restart_scan(sc); + } + } + } + + /* + * Management timer. Used to know when to send auth + * requests and associate requests. + */ + if (sc->sc_mgt_timer) { + sc->sc_mgt_timer--; + if (sc->sc_mgt_timer == 0) { + switch (sc->sc_state) + { + case AWI_ST_SYNCED: + case AWI_ST_RUNNING: + sc->sc_state = AWI_ST_SYNCED; + awi_send_authreq(sc); + break; + case AWI_ST_AUTHED: + awi_send_assocreq(sc); + break; + default: + printf("weird state for mgt timeout!\n"); + break; + } + } + } + awi_set_timer(sc); +} + +void +awi_set_mc (sc) + struct awi_softc *sc; +{ + /* XXX not implemented yet.. */ +} + +/* + * init routine + */ + +/* + * ioctl routine + * SIOCSIFADDR sets IFF_UP + * SIOCIFMTU + * SIOCSIFFLAGS + * SIOCADDMULTI/SIOCDELMULTI + */ + +int +awi_ioctl(ifp, cmd, data) + register struct ifnet *ifp; + u_long cmd; + caddr_t data; +{ + struct awi_softc *sc = ifp->if_softc; + struct ifaddr *ifa = (struct ifaddr *)data; + struct ifreq *ifr = (struct ifreq *)data; + int s, error = 0; + + s = splnet(); + + switch (cmd) { + case SIOCSIFADDR: + if ((error = awi_enable(sc)) != 0) + break; + + ifp->if_flags |= IFF_UP; + + /* XXX other AF support: inet6, NS, ... */ + switch (ifa->ifa_addr->sa_family) { +#ifdef INET + case AF_INET: + arp_ifinit(&sc->sc_ec, ifa); + break; +#endif + default: + break; + } + break; + + case SIOCSIFFLAGS: + if ((ifp->if_flags & IFF_UP) == 0 && + (sc->sc_state != AWI_ST_OFF)) { + /* + * If interface is marked down and it is enabled, then + * stop it. + */ + ifp->if_flags &= ~IFF_RUNNING; + awi_stop(sc); + awi_disable(sc); + } else if ((ifp->if_flags & IFF_UP) != 0 && + (ifp->if_flags & IFF_RUNNING) == 0) { + /* + * If interface is marked up and it is stopped, then + * start it. + */ + if ((error = awi_enable(sc)) != 0) + break; + } else if ((ifp->if_flags & IFF_UP) != 0) { + /* + * Deal with other flags that change hardware + * state, i.e. IFF_PROMISC. + */ + awi_set_mc(sc); + } + break; + case SIOCADDMULTI: + case SIOCDELMULTI: + error = (cmd == SIOCADDMULTI) ? + ether_addmulti(ifr, &sc->sc_ec) : + ether_delmulti(ifr, &sc->sc_ec); + if (error == ENETRESET) { + error = 0; + awi_set_mc(sc); + } + break; + + default: + error = EINVAL; + break; + + } + splx(s); + return error; + +} + +int awi_activate (self, act) + struct device *self; + enum devact act; +{ + int s = splnet(); + panic("awi_activate"); + +#if 0 + switch (act) { + case DVACT_ACTIVATE: + rv = EOPNOTSUPP; + break; + + case DVACT_DEACTIVATE: +#ifdef notyet + /* First, kill off the interface. */ + if_detach(sc->sc_ethercom.ec_if); +#endif + + /* Now disable the interface. */ + awidisable(sc); + break; + } +#endif + splx(s); + +} + +int +awi_drop_output (ifp, m0, dst, rt0) + struct ifnet *ifp; + struct mbuf *m0; + struct sockaddr *dst; + struct rtentry *rt0; +{ + m_freem(m0); + return 0; +} + +void +awi_drop_input (ifp, m0) + struct ifnet *ifp; + struct mbuf *m0; +{ + m_freem(m0); +} + +int awi_attach (sc, macaddr) + struct awi_softc *sc; + u_int8_t *macaddr; +{ + struct ifnet *ifp = &sc->sc_ec.ac_if; + u_int8_t version[AWI_BANNER_LEN]; + + sc->sc_ifp = ifp; + sc->sc_nextpkt = NULL; + sc->sc_m = NULL; + sc->sc_mptr = NULL; + sc->sc_mleft = 0; + sc->sc_flushpkt = 0; + + awi_read_bytes (sc, AWI_BANNER, version, AWI_BANNER_LEN); + printf("%s: firmware %s\n", sc->sc_dev.dv_xname, version); + + bcopy(macaddr, sc->sc_my_addr, ETHER_ADDR_LEN); + printf("%s: 802.11 address %s\n", sc->sc_dev.dv_xname, + ether_sprintf(sc->sc_my_addr)); + + bcopy(sc->sc_dev.dv_xname, ifp->if_xname, IFNAMSIZ); + ifp->if_softc = sc; + ifp->if_start = awi_start; + ifp->if_ioctl = awi_ioctl; + ifp->if_watchdog = awi_watchdog; + ifp->if_mtu = ETHERMTU; + /* XXX simplex may not be correct here.. */ + ifp->if_flags = + IFF_BROADCAST | IFF_SIMPLEX | IFF_NOTRAILERS | IFF_MULTICAST; + + sc->sc_mgtq.ifq_maxlen = 5; + + if_attach(ifp); + ether_ifattach(ifp); + ifp->if_hdrlen = 32; /* 802.11 headers are bigger.. */ + +#if NBPFILTER > 0 + bpfattach(&ifp->if_bpf, ifp, DLT_EN10MB, sizeof(struct ether_header)); +#endif + return 0; +} + +void +awi_zero (sc, from, to) + struct awi_softc *sc; + u_int32_t from, to; +{ + u_int32_t i; + for (i=from; i<to; i++) + awi_write_1(sc, i, 0); +} + +void +awi_init (sc) + struct awi_softc *sc; +{ + struct ifnet *ifp = sc->sc_ifp; + + sc->sc_scan_duration = 100; /* scan for 100ms */ + + /* + * Maybe we should randomize these.... + */ + sc->sc_scan_chanset = IEEEWL_FH_CHANSET_MIN; + sc->sc_scan_pattern = IEEEWL_FH_PATTERN_MIN; + + sc->sc_flags &= ~AWI_FL_CMD_INPROG; + + ifp->if_flags &= ~(IFF_RUNNING|IFF_OACTIVE); + ifp->if_timer = 0; + + sc->sc_cmd_timer = 0; + sc->sc_tx_timer = 0; + sc->sc_mgt_timer = 0; + sc->sc_scan_timer = 0; + + sc->sc_nbindings = 0; + + /* + * this reset sequence doesn't seem to always do the trick. + * hard-power-cycling the card may do it.. + */ + + /* + * reset the hardware, just to be sure. + * (bring out the big hammer here..) + */ + /* XXX insert delay here? */ + + am79c930_gcr_setbits (&sc->sc_chip, AM79C930_GCR_CORESET); + delay(10); /* XXX arbitrary value */ + + /* + * clear control memory regions (firmware should do this but...) + */ + awi_zero(sc, AWI_LAST_TXD, AWI_BUFFERS); + + awi_drvstate(sc, AWI_DRV_RESET); + sc->sc_selftest_tries = 0; + + /* + * release reset + */ + am79c930_gcr_clearbits (&sc->sc_chip, AM79C930_GCR_CORESET); + delay(10); + + sc->sc_state = AWI_ST_SELFTEST; + ifp->if_timer = 1; + +} + +void +awi_cmd (sc, opcode) + struct awi_softc *sc; + u_int8_t opcode; +{ + if (sc->sc_flags & AWI_FL_CMD_INPROG) + panic("%s: command reentered", sc->sc_dev.dv_xname); + + sc->sc_flags |= AWI_FL_CMD_INPROG; + + /* issue test-interface command */ + awi_write_1(sc, AWI_CMD, opcode); + + awi_write_1(sc, AWI_CMD_STATUS, 0); + + sc->sc_cmd_timer = 2; + awi_set_timer(sc); +} + +void +awi_cmd_test_if (sc) + struct awi_softc *sc; +{ + awi_cmd (sc, AWI_CMD_NOP); +} + +void +awi_cmd_get_mib (sc, var, offset, len) + struct awi_softc *sc; + u_int8_t var; + u_int8_t offset; + u_int8_t len; +{ + awi_write_1(sc, AWI_CMD_PARAMS+AWI_CA_MIB_TYPE, var); + awi_write_1(sc, AWI_CMD_PARAMS+AWI_CA_MIB_SIZE, len); + awi_write_1(sc, AWI_CMD_PARAMS+AWI_CA_MIB_INDEX, offset); + + awi_cmd (sc, AWI_CMD_GET_MIB); +} + +void +awi_cmd_txinit (sc) + struct awi_softc *sc; +{ + 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); + + awi_cmd (sc, AWI_CMD_INIT_TX); +} + +int awi_max_chan = -1; +int awi_min_chan = 1000; +int awi_max_pattern = -1; +int awi_min_pattern = 1000; + + +/* + * timeout-driven routine: complete device init once device has passed + * selftest. + */ + +void awi_init_1 (sc) + struct awi_softc *sc; +{ + struct ifnet *ifp = sc->sc_ifp; + + awi_intrinit(sc); + + sc->sc_state = AWI_ST_IFTEST; + + if (ifp->if_flags & IFF_DEBUG) { + awi_card_hexdump(sc, "init_1 CSB", AWI_CSB, 16); + sc->sc_completion = awi_mibdump; + } else + sc->sc_completion = awi_init_2; + + sc->sc_curmib = 0; + + awi_cmd_test_if (sc); +} + +void awi_mibdump (sc, status) + struct awi_softc *sc; + u_int8_t status; +{ + u_int8_t mibblk[256]; + + if (status != AWI_STAT_OK) { + printf("%s: pre-mibread failed (card unhappy?)\n", + sc->sc_dev.dv_xname); + awi_reset(sc); + return; + } + + if (sc->sc_curmib != 0) { + awi_read_bytes(sc, AWI_CMD_PARAMS+AWI_CA_MIB_DATA, + mibblk, 72); + awi_hexdump("mib", mibblk, 72); + } + if (sc->sc_curmib > AWI_MIB_LAST) { + awi_init_2 (sc, status); + } else { + sc->sc_completion = awi_mibdump; + printf("mib %d\n", sc->sc_curmib); + awi_cmd_get_mib (sc, sc->sc_curmib, 0, 30); + sc->sc_curmib++; + /* skip over reserved MIB's.. */ + if ((sc->sc_curmib == 1) || (sc->sc_curmib == 6)) + sc->sc_curmib++; + } +} + + +/* + * called on completion of test-interface command in first-stage init. + */ + +void awi_init_2 (sc, status) + struct awi_softc *sc; + u_int8_t status; +{ + /* did it succeed? */ + if (status != AWI_STAT_OK) { + printf("%s: nop failed (card unhappy?)\n", + sc->sc_dev.dv_xname); + awi_reset(sc); + } + + sc->sc_state = AWI_ST_MIB_GET; + sc->sc_completion = awi_init_read_bufptrs_done; + + awi_cmd_get_mib (sc, AWI_MIB_LOCAL, 0, AWI_MIB_LOCAL_SIZE); +} + +void awi_init_read_bufptrs_done (sc, status) + struct awi_softc *sc; + u_int8_t status; +{ + if (status != AWI_STAT_OK) { + printf("%s: get_mib failed (card unhappy?)\n", + sc->sc_dev.dv_xname); + awi_reset(sc); + } + + sc->sc_txbase = awi_read_4 (sc, + AWI_CMD_PARAMS+AWI_CA_MIB_DATA+AWI_MIB_LOCAL_TXB_OFFSET); + sc->sc_txlen = awi_read_4 (sc, + AWI_CMD_PARAMS+AWI_CA_MIB_DATA+AWI_MIB_LOCAL_TXB_SIZE); + sc->sc_rxbase = awi_read_4 (sc, + AWI_CMD_PARAMS+AWI_CA_MIB_DATA+AWI_MIB_LOCAL_RXB_OFFSET); + sc->sc_rxlen = awi_read_4 (sc, + AWI_CMD_PARAMS+AWI_CA_MIB_DATA+AWI_MIB_LOCAL_RXB_SIZE); + /* + * XXX consider repartitioning buffer space to allow for + * more efficient usage. + * 6144: 3 txds, 1476 waste (current partition) + * better splits: + * 4864: 3 txds, 196 waste + * 6400: 4 txds, 176 waste + * 7936: 5 txds, 156 waste + */ + +#if 0 + printf("tx offset: %x\n", sc->sc_txbase); + printf("tx size: %x\n", sc->sc_txlen); + printf("rx offset: %x\n", sc->sc_rxbase); + printf("rx size: %x\n", sc->sc_rxlen); +#endif + + sc->sc_state = AWI_ST_MIB_SET; + awi_cmd_set_notap(sc); +} + +void awi_cmd_set_notap (sc) + struct awi_softc *sc; +{ + awi_write_1(sc, AWI_CMD_PARAMS+AWI_CA_MIB_TYPE, AWI_MIB_LOCAL); + awi_write_1(sc, AWI_CMD_PARAMS+AWI_CA_MIB_SIZE, 1); + awi_write_1(sc, AWI_CMD_PARAMS+AWI_CA_MIB_INDEX, + AWI_MIB_LOCAL_ACTING_AS_AP); + + awi_write_1(sc, AWI_CMD_PARAMS+AWI_CA_MIB_DATA, 0); + sc->sc_completion = awi_cmd_set_notap_done; + awi_cmd (sc, AWI_CMD_SET_MIB); +} + +void awi_cmd_set_notap_done (sc, status) + struct awi_softc *sc; + u_int8_t status; +{ + if (status != AWI_STAT_OK) { + int erroffset = awi_read_1 (sc, AWI_ERROR_OFFSET); + printf("%s: set_infra failed (card unhappy?); erroffset %d\n", + sc->sc_dev.dv_xname, + erroffset); + awi_reset(sc); + return; + } + awi_cmd_set_infra (sc); +} + +void awi_cmd_set_infra (sc) + struct awi_softc *sc; +{ + + awi_write_1(sc, AWI_CMD_PARAMS+AWI_CA_MIB_TYPE, AWI_MIB_LOCAL); + awi_write_1(sc, AWI_CMD_PARAMS+AWI_CA_MIB_SIZE, 1); + awi_write_1(sc, AWI_CMD_PARAMS+AWI_CA_MIB_INDEX, + AWI_MIB_LOCAL_INFRA_MODE); + + awi_write_1(sc, AWI_CMD_PARAMS+AWI_CA_MIB_DATA, 1); + sc->sc_completion = awi_cmd_set_infra_done; + awi_cmd (sc, AWI_CMD_SET_MIB); +} + +void awi_cmd_set_infra_done (sc, status) + struct awi_softc *sc; + u_int8_t status; +{ +#if 0 + printf("set_infra done\n"); +#endif + if (status != AWI_STAT_OK) { + int erroffset = awi_read_1 (sc, AWI_ERROR_OFFSET); + printf("%s: set_infra failed (card unhappy?); erroffset %d\n", + sc->sc_dev.dv_xname, + erroffset); + awi_reset(sc); + return; + } +#if 0 + printf("%s: set_infra done\n", sc->sc_dev.dv_xname); +#endif + awi_cmd_set_allmulti (sc); +} + +void awi_cmd_set_allmulti (sc) + struct awi_softc *sc; +{ + awi_write_1(sc, AWI_CMD_PARAMS+AWI_CA_MIB_TYPE, AWI_MIB_LOCAL); + awi_write_1(sc, AWI_CMD_PARAMS+AWI_CA_MIB_SIZE, 1); + awi_write_1(sc, AWI_CMD_PARAMS+AWI_CA_MIB_INDEX, + AWI_MIB_LOCAL_FILTMULTI); + awi_write_1(sc, AWI_CMD_PARAMS+AWI_CA_MIB_DATA, 0); + sc->sc_completion = awi_cmd_set_allmulti_done; + awi_cmd (sc, AWI_CMD_SET_MIB); +} + +void awi_cmd_set_allmulti_done (sc, status) + struct awi_softc *sc; + u_int8_t status; +{ + if (status != AWI_STAT_OK) { + int erroffset = awi_read_1 (sc, AWI_ERROR_OFFSET); + printf("%s: set_almulti_done failed (card unhappy?); erroffset %d\n", + sc->sc_dev.dv_xname, + erroffset); + awi_reset(sc); + return; + } + awi_cmd_set_promisc (sc); +} + +void awi_cmd_set_promisc (sc) + struct awi_softc *sc; +{ + awi_write_1(sc, AWI_CMD_PARAMS+AWI_CA_MIB_TYPE, AWI_MIB_MAC); + awi_write_1(sc, AWI_CMD_PARAMS+AWI_CA_MIB_SIZE, 1); + awi_write_1(sc, AWI_CMD_PARAMS+AWI_CA_MIB_INDEX, + AWI_MIB_MAC_PROMISC); + awi_write_1(sc, AWI_CMD_PARAMS+AWI_CA_MIB_DATA, 0); /* XXX */ + sc->sc_completion = awi_cmd_set_promisc_done; + awi_cmd (sc, AWI_CMD_SET_MIB); +} + +void awi_cmd_set_promisc_done (sc, status) + struct awi_softc *sc; + u_int8_t status; +{ +#if 0 + printf("set promisc_done\n"); +#endif + + if (status != AWI_STAT_OK) { + int erroffset = awi_read_1 (sc, AWI_ERROR_OFFSET); + printf("%s: set_promisc_done failed (card unhappy?); erroffset %d\n", + sc->sc_dev.dv_xname, + erroffset); + awi_reset(sc); + return; + } +#if 0 + printf("%s: set_promisc done\n", sc->sc_dev.dv_xname); +#endif + + awi_init_txdescr(sc); + + sc->sc_state = AWI_ST_TXINIT; + sc->sc_completion = awi_init_4; + awi_cmd_txinit(sc); +} + +void +awi_init_4 (sc, status) + struct awi_softc *sc; + u_int8_t status; +{ +#if 0 + printf("%s: awi_init_4, st %x\n", sc->sc_dev.dv_xname, status); + awi_card_hexdump(sc, "init_4 CSB", AWI_CSB, 16); +#endif + + if (status != AWI_STAT_OK) { + int erroffset = awi_read_1 (sc, AWI_ERROR_OFFSET); + printf("%s: init_tx failed (card unhappy?); erroffset %d\n", + sc->sc_dev.dv_xname, + erroffset); + awi_reset(sc); + return; + } + + sc->sc_state = AWI_ST_RXINIT; + sc->sc_completion = awi_init_5; + + awi_cmd (sc, AWI_CMD_INIT_RX); +} + +void awi_init_5 (sc, status) + struct awi_softc *sc; + u_int8_t status; +{ +#if 0 + struct ifnet *ifp = sc->sc_ifp; +#endif + +#if 0 + printf("%s: awi_init_5, st %x\n", sc->sc_dev.dv_xname, status); + awi_card_hexdump(sc, "init_5 CSB", AWI_CSB, 16); +#endif + + if (status != AWI_STAT_OK) { + printf("%s: init_rx failed (card unhappy?)\n", + sc->sc_dev.dv_xname); + awi_reset(sc); + return; + } + + sc->sc_rx_data_desc = awi_read_4(sc, AWI_CMD_PARAMS+AWI_CA_IRX_DATA_DESC); + sc->sc_rx_mgt_desc = awi_read_4(sc, AWI_CMD_PARAMS+AWI_CA_IRX_PS_DESC); + +#if 0 + printf("%s: data desc %x, mgt desc %x\n", sc->sc_dev.dv_xname, + sc->sc_rx_data_desc, sc->sc_rx_mgt_desc); +#endif + awi_restart_scan(sc); +} + +void awi_restart_scan (sc) + struct awi_softc *sc; +{ + if (sc->sc_ifp->if_flags & IFF_DEBUG) { + printf("%s: starting scan\n", sc->sc_dev.dv_xname); + } + sc->sc_scan_timer = 2; + sc->sc_mgt_timer = 0; + awi_set_timer(sc); + + sc->sc_nbindings = 0; + sc->sc_state = AWI_ST_SCAN; + awi_drvstate (sc, AWI_DRV_INFSC); + awi_cmd_scan (sc); +} + +void +awi_cmd_scan (sc) + struct awi_softc *sc; +{ + + awi_write_2 (sc, AWI_CMD_PARAMS+AWI_CA_SCAN_DURATION, + sc->sc_scan_duration); + awi_write_1 (sc, AWI_CMD_PARAMS+AWI_CA_SCAN_SET, + sc->sc_scan_chanset); + awi_write_1 (sc, AWI_CMD_PARAMS+AWI_CA_SCAN_PATTERN, + sc->sc_scan_pattern); + awi_write_1 (sc, AWI_CMD_PARAMS+AWI_CA_SCAN_IDX, 1); + awi_write_1 (sc, AWI_CMD_PARAMS+AWI_CA_SCAN_SUSP, 0); + + sc->sc_completion = awi_cmd_scan_done; + awi_cmd (sc, AWI_CMD_SCAN); +} + +void +awi_cmd_scan_done (sc, status) + struct awi_softc *sc; + u_int8_t status; +{ +#if 0 + int erroffset; +#endif + if (status == AWI_STAT_OK) { + if (sc->sc_scan_chanset > awi_max_chan) + awi_max_chan = sc->sc_scan_chanset; + if (sc->sc_scan_chanset < awi_min_chan) + awi_min_chan = sc->sc_scan_chanset; + if (sc->sc_scan_pattern > awi_max_pattern) + awi_max_pattern = sc->sc_scan_pattern; + if (sc->sc_scan_pattern < awi_min_pattern) + awi_min_pattern = sc->sc_scan_pattern; + + return; + } +#if 0 + erroffset = awi_read_1 (sc, AWI_ERROR_OFFSET); + printf("%s: scan failed; erroffset %d\n", sc->sc_dev.dv_xname, + erroffset); +#endif + /* wait for response or scan timeout.. */ +} + +void +awi_scan_next (sc) + struct awi_softc *sc; +{ + sc->sc_scan_pattern++; + if (sc->sc_scan_pattern > IEEEWL_FH_PATTERN_MAX) { + sc->sc_scan_pattern = IEEEWL_FH_PATTERN_MIN; + + sc->sc_scan_chanset++; + if (sc->sc_scan_chanset > IEEEWL_FH_CHANSET_MAX) + sc->sc_scan_chanset = IEEEWL_FH_CHANSET_MIN; + } +#if 0 + printf("scan: pattern %x chanset %x\n", sc->sc_scan_pattern, + sc->sc_scan_chanset); +#endif + + awi_cmd_scan(sc); +} + +void +awi_try_sync (sc) + struct awi_softc *sc; +{ + int max_rssi = 0, best = 0; + int i; + struct awi_bss_binding *bp = NULL; + + awi_flush(sc); + + if (sc->sc_ifp->if_flags & IFF_DEBUG) { + printf("%s: looking for best of %d\n", + sc->sc_dev.dv_xname, sc->sc_nbindings); + } + /* pick one with best rssi */ + for (i=0; i<sc->sc_nbindings; i++) { + bp = &sc->sc_bindings[i]; + + if (bp->rssi > max_rssi) { + max_rssi = bp->rssi; + best = i; + } + } + + if (bp == NULL) { + printf("%s: no beacons seen\n", sc->sc_dev.dv_xname); + awi_scan_next(sc); + return; + } + + if (sc->sc_ifp->if_flags & IFF_DEBUG) { + printf("%s: best %d\n", sc->sc_dev.dv_xname, best); + } + sc->sc_scan_timer = awi_scan_keepalive; + + bp = &sc->sc_bindings[best]; + bcopy(bp, &sc->sc_active_bss, sizeof(*bp)); + sc->sc_new_bss = 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, 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->bss_timestamp, 8); + awi_write_4 (sc, AWI_CMD_PARAMS+AWI_CA_SYNC_REFTIME, bp->rxtime); + + sc->sc_completion = awi_cmd_sync_done; + + awi_cmd (sc, AWI_CMD_SYNC); + +} + +void +awi_cmd_sync_done (sc, status) + struct awi_softc *sc; + u_int8_t status; +{ + if (status != AWI_STAT_OK) { + int erroffset = awi_read_1 (sc, AWI_ERROR_OFFSET); + printf("%s: sync_done failed (card unhappy?); erroffset %d\n", + sc->sc_dev.dv_xname, + erroffset); + awi_reset(sc); + return; + } + + /* + * at this point, the card should be synchronized with the AP + * we heard from. tell the card what BSS and ESS it's running in.. + */ + + awi_drvstate (sc, AWI_DRV_INFSY); + if (sc->sc_ifp->if_flags & IFF_DEBUG) { + printf("%s: sync done, setting bss/iss parameters\n", + sc->sc_dev.dv_xname); + awi_hexdump ("bss", sc->sc_active_bss.bss_id, ETHER_ADDR_LEN); + printf("ssid: %s\n", sc->sc_active_bss.ssid); + } + + awi_cmd_set_ss (sc); +} + + +void awi_cmd_set_ss (sc) + struct awi_softc *sc; +{ + awi_write_1(sc, AWI_CMD_PARAMS+AWI_CA_MIB_TYPE, AWI_MIB_MAC_MGT); + awi_write_1(sc, AWI_CMD_PARAMS+AWI_CA_MIB_SIZE, + ETHER_ADDR_LEN + AWI_MIB_MGT_ESS_SIZE); + awi_write_1(sc, AWI_CMD_PARAMS+AWI_CA_MIB_INDEX, + AWI_MIB_MGT_BSS_ID); + + awi_write_bytes(sc, AWI_CMD_PARAMS+AWI_CA_MIB_DATA, + sc->sc_active_bss.bss_id, ETHER_ADDR_LEN); + awi_write_1(sc, AWI_CMD_PARAMS+AWI_CA_MIB_DATA+ETHER_ADDR_LEN, + 0); /* XXX */ + awi_write_1(sc, AWI_CMD_PARAMS+AWI_CA_MIB_DATA+ETHER_ADDR_LEN+1, + sc->sc_active_bss.sslen); + awi_write_bytes(sc, AWI_CMD_PARAMS+AWI_CA_MIB_DATA+8, + sc->sc_active_bss.ssid, AWI_MIB_MGT_ESS_SIZE-2); + + sc->sc_completion = awi_cmd_set_ss_done; + awi_cmd (sc, AWI_CMD_SET_MIB); +} + +void awi_cmd_set_ss_done (sc, status) + struct awi_softc *sc; + u_int8_t status; +{ + if (status != AWI_STAT_OK) { + int erroffset = awi_read_1 (sc, AWI_ERROR_OFFSET); + printf("%s: set_ss_done failed (card unhappy?); erroffset %d\n", + sc->sc_dev.dv_xname, + erroffset); + awi_reset(sc); + return; + } +#if 0 + printf("%s: set_ss done\n", sc->sc_dev.dv_xname); +#endif + + awi_running (sc); + + /* + * now, we *should* be getting broadcast frames.. + */ + sc->sc_state = AWI_ST_SYNCED; + awi_send_authreq (sc); + +} + +void awi_running (sc) + struct awi_softc *sc; + +{ + struct ifnet *ifp = sc->sc_ifp; + + /* + * Who knows what it is to be running? + * Only he who is running knows.. + */ + ifp->if_flags |= IFF_RUNNING; + awi_start(ifp); +} + + +void awi_reset (sc) + struct awi_softc *sc; +{ + printf("%s: reset\n", sc->sc_dev.dv_xname); + +} diff --git a/sys/dev/ic/awireg.h b/sys/dev/ic/awireg.h new file mode 100644 index 00000000000..76c97ee5309 --- /dev/null +++ b/sys/dev/ic/awireg.h @@ -0,0 +1,510 @@ +/* $NetBSD: awireg.h,v 1.2 1999/11/05 05:13:36 sommerfeld Exp $ */ +/* $OpenBSD: awireg.h,v 1.1 1999/12/16 02:56:56 deraadt Exp $ */ + +/*- + * 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_MAC_ADDR 0x2 +#define AWI_MIB_MAC 0x3 +#define AWI_MIB_MAC_STAT 0x4 +#define AWI_MIB_MAC_MGT 0x5 +#define AWI_MIB_DRVR_MAC 0x6 +#define AWI_MIB_PHY 0x7 + +#define AWI_MIB_LAST AWI_MIB_PHY + + +#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 */ + +#define AWI_INT_BITS "\20\1CMD\2RX\3TX\4SCAN\5CFPST\6DTIM\7CFPE\10GROGGY" + +/* + * 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 0x08f /* 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 */ + +/* + * 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_ST_BITS "\20\1ERROR\2OVERRUN\4CRC\6LF\7CONSUMED\10OWN" + +#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. + */ + +/* + * MIB 0: Local MIB + */ + +#define AWI_MIB_LOCAL_NOFRAG 0 +#define AWI_MIB_LOCAL_NOPLCP 1 +#define AWI_MIB_LOCAL_MACPRES 2 +#define AWI_MIB_LOCAL_RXMGTQ 3 +#define AWI_MIB_LOCAL_NOREASM 4 +#define AWI_MIB_LOCAL_NOSTRIPPLCP 5 +#define AWI_MIB_LOCAL_NORXERROR 6 +#define AWI_MIB_LOCAL_NOPWRSAVE 7 + +#define AWI_MIB_LOCAL_FILTMULTI 8 +#define AWI_MIB_LOCAL_NOSEQCHECK 9 +#define AWI_MIB_LOCAL_CFPENDFLUSHCFPQ 10 +#define AWI_MIB_LOCAL_INFRA_MODE 11 +#define AWI_MIB_LOCAL_PWD_LEVEL 12 +#define AWI_MIB_LOCAL_CFPMODE 13 + +#define AWI_MIB_LOCAL_TXB_OFFSET 14 +#define AWI_MIB_LOCAL_TXB_SIZE 18 +#define AWI_MIB_LOCAL_RXB_OFFSET 22 +#define AWI_MIB_LOCAL_RXB_SIZE 26 + +#define AWI_MIB_LOCAL_ACTING_AS_AP 30 +#define AWI_MIB_LOCAL_FILL_CFP 31 +#define AWI_MIB_LOCAL_SIZE 32 + +/* + * MAC mib + */ + +#define AWI_MIB_MAC_RTS_THRESH 4 /* 2 bytes */ +#define AWI_MIB_MAC_CW_MAX 6 +#define AWI_MIB_MAC_CW_MIN 8 +#define AWI_MIB_MAC_PROMISC 10 +#define AWI_MIB_MAC_SHORT_RETRY 16 +#define AWI_MIB_MAC_LONG_RETRY 17 +#define AWI_MIB_MAC_MAX_FRAME 18 +#define AWI_MIB_MAC_MAX_FRAG 20 +#define AWI_MIB_MAC_PROBE_DELAY 22 +#define AWI_MIB_MAC_PROBE_RESP_MIN 24 +#define AWI_MIB_MAC_PROBE_RESP_MAX 26 +#define AWI_MIB_MAC_MAX_TX_MSDU_LIFE 28 +#define AWI_MIB_MAC_MAX_RX_MSDU_LIFE 32 +#define AWI_MIB_MAC_STATION_BASE_RATE 36 +#define AWI_MIB_MAC_DES_ESSID 38 /* 34 bytes */ + +/* + * MGT mib. + */ + +#define AWI_MIB_MGT_POWER_MODE 0 +#define AWI_MIB_MGT_SCAN_MODE 1 +#define AWI_MIB_MGT_SCAN_STATE 2 +#define AWI_MIB_MGT_DTIM_PERIOD 3 +#define AWI_MIB_MGT_ATIM_WINDOW 4 +#define AWI_MIB_MGT_WEPREQ 6 +#define AWI_MIB_MGT_BEACON_PD 8 +#define AWI_MIB_MGT_PASSIVE_SCAN 10 +#define AWI_MIB_MGT_LISTEN_INT 12 +#define AWI_MIB_MGT_MEDIUP_OCC 14 +#define AWI_MIB_MGT_MAX_MPDU_TIME 16 +#define AWI_MIB_MGT_CFP_MAX_DUR 18 +#define AWI_MIB_MGT_CFP_RATE 20 +#define AWI_MIB_MGT_NO_DTMS 21 +#define AWI_MIB_MGT_STATION_ID 22 +#define AWI_MIB_MGT_BSS_ID 24 +#define AWI_MIB_MGT_ESS_ID 30 /* 34 bytes */ +#define AWI_MIB_MGT_ESS_SIZE 34 + + +/* + * MAC address group. + */ + +#define AWI_MIB_MAC_ADDR_MINE 0 +#define AWI_MIB_MAC_ADDR_MULTI0 6 +#define AWI_MIB_MAC_ADDR_MULTI1 12 +#define AWI_MIB_MAC_ADDR_MULTI2 18 +#define AWI_MIB_MAC_ADDR_MULTI3 24 + +#define AWI_MIB_MAC_ADDR_TXEN 30 + +/* + * 802.11 media layer goo. + * Should be split out into separate module independant of this driver. + */ + +#define IEEEWL_FC 0 /* frame control */ + +#define IEEEWL_FC_VERS 0 +#define IEEEWL_FC_VERS_MASK 0x03 + +#define IEEEWL_FC_TYPE_MGT 0 +#define IEEEWL_FC_TYPE_CTL 1 +#define IEEEWL_FC_TYPE_DATA 2 + +#define IEEEWL_FC_TYPE_MASK 0x0c +#define IEEEWL_FC_TYPE_SHIFT 2 + +#define IEEEWL_FC_SUBTYPE_MASK 0xf0 +#define IEEEWL_FC_SUBTYPE_SHIFT 4 + +#define IEEEWL_SUBTYPE_ASSOCREQ 0x00 +#define IEEEWL_SUBTYPE_ASSOCRESP 0x01 +#define IEEEWL_SUBTYPE_REASSOCREQ 0x02 +#define IEEEWL_SUBTYPE_REASSOCRESP 0x03 +#define IEEEWL_SUBTYPE_PROBEREQ 0x04 +#define IEEEWL_SUBTYPE_PROBERESP 0x05 + +#define IEEEWL_SUBTYPE_BEACON 0x08 +#define IEEEWL_SUBTYPE_DISSOC 0x0a +#define IEEEWL_SUBTYPE_AUTH 0x0b +#define IEEEWL_SUBTYPE_DEAUTH 0x0c + +#define IEEEWL_FC2 1 /* second byte of fc */ + +/* + * TLV tags for things we care about.. + */ +#define IEEEWL_MGT_TLV_SSID 0 +#define IEEEWL_MGT_TLV_FHPARMS 2 + +/* + * misc frame control bits in second byte of frame control word. + * there are others, but we don't ever want to set them.. + */ + +#define IEEEWL_FC2_DSMASK 0x03 + +#define IEEEWL_FC2_TODS 0x01 +#define IEEEWL_FC2_FROMDS 0x02 + +#define IEEEWL_FH_CHANSET_MIN 1 +#define IEEEWL_FH_CHANSET_MAX 3 +#define IEEEWL_FH_PATTERN_MIN 0 +#define IEEEWL_FH_PATTERN_MAX 77 + +struct awi_mac_header +{ + u_int8_t awi_fc; + u_int8_t awi_f2; + u_int16_t awi_duration; + u_int8_t awi_addr1[6]; + u_int8_t awi_addr2[6]; + u_int8_t awi_addr3[6]; + u_int16_t awi_seqctl; +}; + +struct awi_llc_header +{ + u_int8_t awi_llc_goo[8]; +}; + +struct awi_assoc_hdr +{ + u_int8_t awi_cap_info[2]; + u_int8_t awi_li[2]; +}; + +struct awi_auth_hdr +{ + u_int8_t awi_algno[2]; + u_int8_t awi_seqno[2]; + u_int8_t awi_status[2]; +}; diff --git a/sys/dev/ic/awivar.h b/sys/dev/ic/awivar.h new file mode 100644 index 00000000000..d50fa7c9b66 --- /dev/null +++ b/sys/dev/ic/awivar.h @@ -0,0 +1,183 @@ +/* $NetBSD: awivar.h,v 1.4 1999/11/09 14:58:07 sommerfeld Exp $ */ +/* $OpenBSD: awivar.h,v 1.1 1999/12/16 02:56:56 deraadt Exp $ */ + +/*- + * 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. + */ + + +enum awi_state { + AWI_ST_OFF, /* powered off */ + AWI_ST_SELFTEST, /* waiting for selftest to complete*/ + AWI_ST_IFTEST, /* waiting for interface to respond */ + AWI_ST_MIB_GET, /* fetching MIB variables */ + AWI_ST_MIB_SET, /* stuffing MIB variables */ + AWI_ST_TXINIT, /* initializing TX side */ + AWI_ST_RXINIT, /* initializing RX side */ + AWI_ST_SCAN, /* hunting for a BSS */ + AWI_ST_SYNCED, /* synced? trying to auth.. */ + /* there are probably some missing 802.11 states here.. */ + AWI_ST_AUTHED, /* authenticated */ + AWI_ST_RUNNING, /* ready to send user data.. */ + AWI_ST_INSANE, /* failed to respond.. */ +}; + +#define AWI_FL_CMD_INPROG 0x0001 + +#define AWI_SSID_LEN 33 + +struct awi_bss_binding +{ + 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; /* strenght of this beacon */ + u_int16_t dwell_time; /* dwell time */ + u_int8_t bss_timestamp[8]; /* timestamp of this bss */ + u_int8_t bss_id[6]; + u_int32_t rxtime; /* unit's local time */ + u_int8_t sslen; + u_int8_t ssid[AWI_SSID_LEN]; +}; + +#define NBND 4 +#define NTXD 4 + +struct awi_txbd +{ + u_int32_t descr; /* offset to descriptor */ + u_int32_t frame; /* offset to frame */ + u_int32_t len; /* frame length */ +}; + +struct awi_softc +{ + struct device sc_dev; + struct am79c930_softc sc_chip; + struct arpcom sc_ec; + int sc_enabled; + enum awi_state sc_state; + int sc_flags; + void *sc_ih; /* interrupt handler */ + struct ifnet *sc_ifp; /* XXX */ + int (*sc_enable) __P((struct awi_softc *)); + void (*sc_disable) __P((struct awi_softc *)); + void (*sc_completion) __P((struct awi_softc *, + u_int8_t)); + + struct ifqueue sc_mgtq; + + u_int32_t sc_txbase; + u_int32_t sc_txlen; + u_int32_t sc_rxbase; + u_int32_t sc_rxlen; + + u_int32_t sc_rx_data_desc; + u_int32_t sc_rx_mgt_desc; + + u_int16_t sc_scan_duration; + u_int8_t sc_scan_chanset; + u_int8_t sc_scan_pattern; + + int sc_nbindings; + + u_int8_t sc_my_addr[6]; + + int sc_new_bss; + struct awi_bss_binding sc_active_bss; + /* + * BSS's found during a scan.. XXX doesn't need to be in-line + */ + struct awi_bss_binding sc_bindings[NBND]; + + int sc_txpending; + int sc_ntxd; + int sc_txnext; /* next txd to be given to driver */ + int sc_txfirst; /* first unsent txd dev has */ + struct awi_txbd sc_txd[NTXD]; + u_int8_t sc_curmib; + + int sc_scan_timer; + int sc_tx_timer; + int sc_mgt_timer; + int sc_cmd_timer; + int sc_selftest_tries; + + /* + * packet parsing state. + */ + + struct mbuf *sc_nextpkt; + struct mbuf *sc_m; + u_int8_t *sc_mptr; + u_int32_t sc_mleft; + int sc_flushpkt; +}; + +extern int awi_activate __P((struct device *, enum devact)); +extern int awi_attach __P((struct awi_softc *, u_int8_t *macaddr)); +extern void awi_init __P((struct awi_softc *)); +extern void awi_stop __P((struct awi_softc *)); + +#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)); + +/* Number of trips around the loop waiting for the device.. */ + +#define AWI_LOCKOUT_SPIN 10000 /* 10ms */ + +/* 24-byte mac header + 8 byte SNAP header + 1500-byte ether MTU */ +#define AWI_FRAME_SIZE 1532 + +/* refresh associations every 300s */ + +#define AWI_ASSOC_REFRESH 300 + +extern int awi_intr __P((void *)); diff --git a/sys/dev/pcmcia/files.pcmcia b/sys/dev/pcmcia/files.pcmcia index 4a2797c8dc4..32e338c45d5 100644 --- a/sys/dev/pcmcia/files.pcmcia +++ b/sys/dev/pcmcia/files.pcmcia @@ -1,4 +1,4 @@ -# $OpenBSD: files.pcmcia,v 1.24 1999/08/13 20:34:17 fgsch Exp $ +# $OpenBSD: files.pcmcia,v 1.25 1999/12/16 02:56:56 deraadt Exp $ # $NetBSD: files.pcmcia,v 1.9 1998/06/21 18:45:41 christos Exp $ # # Config.new file and device description for machine-independent PCMCIA code. @@ -69,3 +69,11 @@ file dev/pcmcia/if_cnw.c cnw device wi: ether, ifnet attach wi at pcmcia file dev/pcmcia/if_wi.c wi + +# AMD 79c930-based 802.11 cards (including BayStack 650 FH card). +device awi: ether, ifnet +attach awi at pcmcia with awi_pcmcia + +file dev/pcmcia/if_awi_pcmcia.c awi_pcmcia +file dev/ic/awi.c awi +file dev/ic/am79c930.c awi diff --git a/sys/dev/pcmcia/if_awi_pcmcia.c b/sys/dev/pcmcia/if_awi_pcmcia.c new file mode 100644 index 00000000000..b9f17b74630 --- /dev/null +++ b/sys/dev/pcmcia/if_awi_pcmcia.c @@ -0,0 +1,412 @@ +/* $NetBSD: if_awi_pcmcia.c,v 1.5 1999/11/06 16:43:54 sommerfeld Exp $ */ +/* $OpenBSD: if_awi_pcmcia.c,v 1.1 1999/12/16 02:56:57 deraadt Exp $ */ + +/*- + * 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 "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_media.h> + +#ifdef INET +#include <netinet/in.h> +#include <netinet/in_systm.h> +#include <netinet/in_var.h> +#include <netinet/ip.h> +#endif + +#include <netinet/if_ether.h> + +#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> + +int awi_pcmcia_match __P((struct device *, void *, void *)); +void awi_pcmcia_attach __P((struct device *, struct device *, void *)); +int awi_pcmcia_detach __P((struct device *, int)); + +int awi_pcmcia_get_enaddr __P((struct pcmcia_tuple *, void *)); +int awi_pcmcia_enable __P((struct awi_softc *)); +void awi_pcmcia_disable __P((struct awi_softc *)); + +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 */ + int sc_io_window; /* our i/o window */ + struct pcmcia_function *sc_pf; /* our PCMCIA function */ +}; + +int awi_pcmcia_find __P((struct awi_pcmcia_softc *, + struct pcmcia_attach_args *, struct pcmcia_config_entry *)); + +/* Autoconfig definition of driver back-end */ +struct cfdriver awi_cd = { + NULL, "awi", DV_IFNET +}; + +struct cfattach awi_pcmcia_ca = { + sizeof(struct awi_pcmcia_softc), awi_pcmcia_match, awi_pcmcia_attach, + awi_pcmcia_detach, /* awi_activate */ 0 +}; + +/* + * XXX following is common to most PCMCIA NIC's and belongs + * in common code + */ + +struct awi_pcmcia_get_enaddr_args { + int got_enaddr; + u_int8_t enaddr[ETHER_ADDR_LEN]; +}; + +int awi_pcmcia_get_enaddr __P((struct pcmcia_tuple *, void *)); + +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); + } + return (pcmcia_function_enable(pf)); +} + +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); +} + +int +awi_pcmcia_match(parent, match, aux) + struct device *parent; + void *match; + void *aux; +{ + struct pcmcia_attach_args *pa = aux; + + if (pa->manufacturer != PCMCIA_VENDOR_BAY) + return (0); + + if (pa->product == PCMCIA_PRODUCT_BAY_STACK_650) + return (1); + + return (0); +} + +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, 0, &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_iot = psc->sc_pcioh.iot; + sc->sc_chip.sc_ioh = psc->sc_pcioh.ioh; + am79c930_chip_init(&sc->sc_chip, 0); + + DELAY(100); + + 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++; + return fail; +} + + + +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 pcmcia_attach_args *pa = aux; + struct pcmcia_config_entry *cfe; + struct pcmcia_mem_handle memh; + struct awi_pcmcia_get_enaddr_args pgea; + bus_addr_t memoff; + int memwin; + +#if 1 + int i, j; + + for (cfe = pa->pf->cfe_head.sqh_first, i=0; + cfe != NULL; + cfe = cfe->cfe_list.sqe_next, i++) { + printf("%d: %d memspaces, %d iospaces\n", + i, cfe->num_memspace, cfe->num_iospace); + printf("%d: number %d flags %x iftype %d iomask %lx irqmask %x maxtwins %x\n", + i, cfe->number, cfe->flags, cfe->iftype, cfe->iomask, + cfe->irqmask, cfe->maxtwins); + for (j=0; j<cfe->num_memspace; j++) { + printf("%d: mem %d: len %lx card %lx host %lx\n", + i, j, + cfe->memspace[j].length, + cfe->memspace[j].cardaddr, + cfe->memspace[j].hostaddr); + } + for (j=0; j<cfe->num_iospace; j++) { + printf("%d: io %d: len %lx start %lx\n", + i, j, + cfe->iospace[j].length, + cfe->iospace[j].start); + } + } +#endif + + 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"); + return; + } + + sc->sc_enabled = 1; + sc->sc_state = AWI_ST_SELFTEST; + printf(": BayStack 650 Wireless (802.11)\n"); + + if (pcmcia_mem_alloc(psc->sc_pf, AM79C930_MEM_SIZE, &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_MEM_COMMON, + AM79C930_MEM_BASE, AM79C930_MEM_SIZE, + &memh, &memoff, &memwin)) { + printf("%s: unable to map memory space; using i/o only\n", + sc->sc_dev.dv_xname); + pcmcia_mem_free(psc->sc_pf, &memh); + } else { + sc->sc_chip.sc_memt = memh.memt; + sc->sc_chip.sc_memh = memh.memh; + am79c930_chip_init(&sc->sc_chip, 1); + } + + sc->sc_chip.sc_bustype = AM79C930_BUS_PCMCIA; + + sc->sc_enable = awi_pcmcia_enable; + sc->sc_disable = awi_pcmcia_disable; + + /* Read station address. */ + pgea.got_enaddr = 0; + if (pcmcia_scan_cis(parent, awi_pcmcia_get_enaddr, &pgea) == -1) { + printf("%s: Couldn't read CIS to get ethernet address\n", + sc->sc_dev.dv_xname); + return; + } else if (!pgea.got_enaddr) { + printf("%s: Couldn't get ethernet address from CIS\n", + sc->sc_dev.dv_xname); + return; + } else +#ifdef DIAGNOSTIC + printf("%s: Ethernet address from CIS: %s\n", + sc->sc_dev.dv_xname, ether_sprintf(pgea.enaddr)) +#endif + ; + + awi_attach(sc, pgea.enaddr); + +#ifndef NETBSD_ORIGINAL + awi_init(sc); + awi_stop(sc); +#endif + +#ifdef notyet /* NETBSD_ORIGINAL */ + sc->sc_state = AWI_ST_OFF; + + sc->sc_enabled = 0; + /* + * XXX This should be done once the framework has enable/disable hooks. + */ + pcmcia_function_disable(psc->sc_pf); +#endif /* notyet */ +} + + +int +awi_pcmcia_detach(self, flags) + struct device *self; + int flags; +{ + struct awi_pcmcia_softc *psc = (struct awi_pcmcia_softc *)self; + + /* 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); + +#ifdef notyet + /* + * Our softc is about to go away, so drop our reference + * to the ifnet. + */ + if_delref(psc->sc_awi.sc_ethercom.ec_if); + return (0); +#else + return (EBUSY); +#endif +} + +/* + * XXX copied verbatim from if_mbe_pcmcia.c. + * this function should be in common pcmcia code.. + */ + +int +awi_pcmcia_get_enaddr(tuple, arg) + struct pcmcia_tuple *tuple; + void *arg; +{ + struct awi_pcmcia_get_enaddr_args *p = arg; + int i; + + if (tuple->code == PCMCIA_CISTPL_FUNCE) { + if (tuple->length < 2) /* sub code and ether addr length */ + return (0); + + if ((pcmcia_tuple_read_1(tuple, 0) != + PCMCIA_TPLFE_TYPE_LAN_NID) || + (pcmcia_tuple_read_1(tuple, 1) != ETHER_ADDR_LEN)) + return (0); + + for (i = 0; i < ETHER_ADDR_LEN; i++) + p->enaddr[i] = pcmcia_tuple_read_1(tuple, i + 2); + p->got_enaddr = 1; + return (1); + } + return (0); +} |