diff options
author | imp <imp@FreeBSD.org> | 2001-03-16 07:25:42 +0000 |
---|---|---|
committer | imp <imp@FreeBSD.org> | 2001-03-16 07:25:42 +0000 |
commit | dbf0f459dbfca89ab25228ca86727a04cabb99ee (patch) | |
tree | 2c3d4578f08fbace6ea649cc97da3005d7d44c37 /sys/dev | |
parent | 8b74d3ebb4eddbfde3eeadaae637e90effaf7f93 (diff) | |
download | FreeBSD-src-dbf0f459dbfca89ab25228ca86727a04cabb99ee.zip FreeBSD-src-dbf0f459dbfca89ab25228ca86727a04cabb99ee.tar.gz |
Commit port of cnw driver from Hiroyuki Aizu-san. This driver
supports Xircom netwave series of cards. I have one of these cards
(but am trying to find one or two to test with), but it compiles and
aizu-san says it works.
I don't know if this supports 802.11 or not. I've seen conflicting
information on this.
Submitted by: Hiroyuki Aizu <aizu@jaist.ac.jp>
Obtained from: NetBSD
Diffstat (limited to 'sys/dev')
-rw-r--r-- | sys/dev/cnw/if_cnw.c | 1745 | ||||
-rw-r--r-- | sys/dev/cnw/if_cnwioctl.h | 114 | ||||
-rw-r--r-- | sys/dev/cnw/if_cnwreg.h | 147 |
3 files changed, 2006 insertions, 0 deletions
diff --git a/sys/dev/cnw/if_cnw.c b/sys/dev/cnw/if_cnw.c new file mode 100644 index 0000000..2aff258 --- /dev/null +++ b/sys/dev/cnw/if_cnw.c @@ -0,0 +1,1745 @@ +/* $NetBSD: if_cnw.c,v 1.15 2000/10/16 10:26:41 itojun Exp $ */ +/* $FreeBSD$ */ + +/*- + * Copyright (c) 1998 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Michael Eriksson. + * + * 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. + */ + +/* + * Copyright (c) 1996, 1997 Berkeley Software Design, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that this notice is retained, + * the conditions in the following notices are met, and terms applying + * to contributors in the following notices also apply to Berkeley + * Software Design, Inc. + * + * 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 + * Berkeley Software Design, Inc. + * 4. Neither the name of the Berkeley Software Design, Inc. 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 BERKELEY SOFTWARE DESIGN, INC. ``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 BERKELEY SOFTWARE DESIGN, INC. 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. + * + * Paul Borman, December 1996 + * + * This driver is derived from a generic frame work which is + * Copyright(c) 1994,1995,1996 + * Yoichi Shinoda, Yoshitaka Tokugawa, WIDE Project, Wildboar Project + * and Foretune. All rights reserved. + * + * A linux driver was used as the "hardware reference manual" (i.e., + * to determine registers and a general outline of how the card works) + * That driver is publically available and copyright + * + * John Markus Bjørndalen + * Department of Computer Science + * University of Tromsø + * Norway + * johnm@staff.cs.uit.no, http://www.cs.uit.no/~johnm/ + */ + +/* + * This is a driver for the Xircom CreditCard Netwave (also known as + * the Netwave Airsurfer) wireless LAN PCMCIA adapter. + * + * When this driver was developed, the Linux Netwave driver was used + * as a hardware manual. That driver is Copyright (c) 1997 University + * of Tromsø, Norway. It is part of the Linix pcmcia-cs package that + * can be found at + * http://hyper.stanford.edu/HyperNews/get/pcmcia/home.html. The most + * recent version of the pcmcia-cs package when this driver was + * written was 3.0.6. + * + * Unfortunately, a lot of explicit numeric constants were used in the + * Linux driver. I have tried to use symbolic names whenever possible, + * but since I don't have any real hardware documentation, there's + * still one or two "magic numbers" :-(. + * + * Driver limitations: This driver doesn't do multicasting or receiver + * promiscuity, because of missing hardware documentation. I couldn't + * get receiver promiscuity to work, and I haven't even tried + * multicast. Volunteers are welcome, of course :-). + */ + +#if !defined(__FreeBSD__) +#include "opt_inet.h" +#include "bpfilter.h" + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/device.h> +#include <sys/socket.h> +#include <sys/mbuf.h> +#include <sys/ioctl.h> +#include <sys/proc.h> + +#include <net/if.h> + +#include <dev/pcmcia/if_cnwreg.h> +#include <dev/pcmcia/if_cnwioctl.h> + +#include <dev/pcmcia/pcmciareg.h> +#include <dev/pcmcia/pcmciavar.h> +#include <dev/pcmcia/pcmciadevs.h> + +#include <net/if_dl.h> +#include <net/if_ether.h> + +#ifdef INET +#include <netinet/in.h> +#include <netinet/in_systm.h> +#include <netinet/in_var.h> +#include <netinet/ip.h> +#include <netinet/if_inarp.h> +#endif + +#if NBPFILTER > 0 +#include <net/bpf.h> +#include <net/bpfdesc.h> +#endif + +/* + * Let these be patchable variables, initialized from macros that can + * be set in the kernel config file. Someone with lots of spare time + * could probably write a nice Netwave configuration program to do + * this a little bit more elegantly :-). + */ +#ifndef CNW_DOMAIN +#define CNW_DOMAIN 0x100 +#endif +int cnw_domain = CNW_DOMAIN; /* Domain */ +#ifndef CNW_SCRAMBLEKEY +#define CNW_SCRAMBLEKEY 0 +#endif +int cnw_skey = CNW_SCRAMBLEKEY; /* Scramble key */ + +/* + * The card appears to work much better when we only allow one packet + * "in the air" at a time. This is done by not allowing another packet + * on the card, even if there is room. Turning this off will allow the + * driver to stuff packets on the card as soon as a transmit buffer is + * available. This does increase the number of collisions, though. + * We can que a second packet if there are transmit buffers available, + * but we do not actually send the packet until the last packet has + * been written. + */ +#define ONE_AT_A_TIME + +/* + * Netwave cards choke if we try to use io memory address >= 0x400. + * Even though, CIS tuple does not talk about this. + * Use memory mapped access. + */ +#define MEMORY_MAPPED + +int cnw_match __P((struct device *, struct cfdata *, void *)); +void cnw_attach __P((struct device *, struct device *, void *)); +int cnw_detach __P((struct device *, int)); + +int cnw_activate __P((struct device *, enum devact)); + +struct cnw_softc { + struct device sc_dev; /* Device glue (must be first) */ + struct ethercom sc_ethercom; /* Ethernet common part */ + int sc_domain; /* Netwave domain */ + int sc_skey; /* Netwave scramble key */ + struct cnwstats sc_stats; + + /* PCMCIA-specific stuff */ + struct pcmcia_function *sc_pf; /* PCMCIA function */ +#ifndef MEMORY_MAPPED + struct pcmcia_io_handle sc_pcioh; /* PCMCIA I/O space handle */ + int sc_iowin; /* ...window */ + bus_space_tag_t sc_iot; /* ...bus_space tag */ + bus_space_handle_t sc_ioh; /* ...bus_space handle */ +#endif + struct pcmcia_mem_handle sc_pcmemh; /* PCMCIA memory handle */ + bus_addr_t sc_memoff; /* ...offset */ + int sc_memwin; /* ...window */ + bus_space_tag_t sc_memt; /* ...bus_space tag */ + bus_space_handle_t sc_memh; /* ...bus_space handle */ + void *sc_ih; /* Interrupt cookie */ + struct timeval sc_txlast; /* When the last xmit was made */ + int sc_active; /* Currently xmitting a packet */ + + int sc_resource; /* Resources alloc'ed on attach */ +#define CNW_RES_PCIC 1 +#define CNW_RES_IO 2 +#define CNW_RES_MEM 4 +#define CNW_RES_NET 8 +}; + +struct cfattach cnw_ca = { + sizeof(struct cnw_softc), cnw_match, cnw_attach, cnw_detach, + cnw_activate +}; + +#else /* FreeBSD */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/sockio.h> +#include <sys/mbuf.h> +#include <sys/malloc.h> +#include <sys/kernel.h> +#include <sys/proc.h> +#include <sys/ucred.h> +#include <sys/socket.h> +#include <sys/module.h> +#include <sys/bus.h> +#include <sys/syslog.h> +#include <sys/sysctl.h> + +#include <machine/bus.h> +#include <machine/resource.h> +#include <machine/clock.h> +#include <machine/md_var.h> +#include <machine/bus_pio.h> +#include <sys/rman.h> + +#include <net/if.h> +#include <net/if_arp.h> +#include <net/ethernet.h> +#include <net/if_dl.h> +#include <net/if_media.h> +#include <net/if_types.h> + +#include <netinet/in.h> +#include <netinet/in_systm.h> +#include <netinet/in_var.h> +#include <netinet/ip.h> +#include <netinet/if_ether.h> + +#include <net/bpf.h> + +#include "card_if.h" + +#include <dev/cnw/if_cnwioctl.h> +#include <dev/cnw/if_cnwreg.h> + +#ifndef CNW_DOMAIN +#define CNW_DOMAIN 0x100 +#endif +int cnw_domain = (int)CNW_DOMAIN; /* Domain */ + +#ifndef CNW_SCRAMBLEKEY +#define CNW_SCRAMBLEKEY 0 +#endif +int cnw_skey = CNW_SCRAMBLEKEY; /* Scramble key */ + +#define ONE_AT_A_TIME + +#ifndef MEMORY_MAPPED +#define MEMORY_MAPPED +#endif + +struct cnw_softc { + struct arpcom arpcom; + struct ifmedia ifmedia; + device_t dev; + struct cnwstats sc_stats; + int sc_domain; /* Netwave domain */ + int sc_skey; /* Netwave scramble key */ + + struct resource * mem_res; + struct resource * irq; + + bus_addr_t sc_memoff; /* ...offset */ + bus_space_tag_t sc_memt; /* ...bus_space tag */ + bus_space_handle_t sc_memh; /* ...bus_space handle */ + + void * cnw_intrhand; + int cnw_gone; + + struct timeval sc_txlast; /* When the last xmit was made */ + int sc_active; /* Currently xmitting a packet */ +}; + + +static void cnw_freebsd_init __P((void *)); +static void cnw_stop __P((struct cnw_softc *)); + +static int cnw_pccard_probe __P((device_t)); +static int cnw_pccard_attach __P((device_t)); +static int cnw_pccard_detach __P((device_t)); +static void cnw_shutdown __P((device_t)); +static int cnw_alloc __P((device_t)); +static void cnw_free __P((device_t)); + +static device_method_t cnw_pccard_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, cnw_pccard_probe), + DEVMETHOD(device_attach, cnw_pccard_attach), + DEVMETHOD(device_detach, cnw_pccard_detach), + DEVMETHOD(device_shutdown, cnw_shutdown), + + { 0, 0 } +}; + +static driver_t cnw_pccard_driver = { + "cnw", + cnw_pccard_methods, + sizeof(struct cnw_softc) +}; + +static devclass_t cnw_pccard_devclass; + +DRIVER_MODULE(if_cnw, pccard, cnw_pccard_driver, cnw_pccard_devclass, 0, 0); + +#endif /* !defined(__FreeBSD__) */ + +void cnw_reset __P((struct cnw_softc *)); +void cnw_init __P((struct cnw_softc *)); +#if !defined(__FreeBSD__) +int cnw_enable __P((struct cnw_softc *sc)); +void cnw_disable __P((struct cnw_softc *sc)); +void cnw_config __P((struct cnw_softc *sc, u_int8_t *)); +#endif +void cnw_start __P((struct ifnet *)); +void cnw_transmit __P((struct cnw_softc *, struct mbuf *)); +struct mbuf *cnw_read __P((struct cnw_softc *)); +void cnw_recv __P((struct cnw_softc *)); +#if !defined(__FreeBSD__) +int cnw_intr __P((void *arg)); +#else +void cnw_intr __P((void *arg)); +#endif +int cnw_ioctl __P((struct ifnet *, u_long, caddr_t)); +void cnw_watchdog __P((struct ifnet *)); +static int cnw_setdomain __P((struct cnw_softc *, int)); +static int cnw_setkey __P((struct cnw_softc *, int)); + +/* ---------------------------------------------------------------- */ + +/* Help routines */ +static int wait_WOC __P((struct cnw_softc *, int)); +static int read16 __P((struct cnw_softc *, int)); +static int cnw_cmd __P((struct cnw_softc *, int, int, int, int)); + +/* + * Wait until the WOC (Write Operation Complete) bit in the + * ASR (Adapter Status Register) is asserted. + */ +static int +wait_WOC(sc, line) + struct cnw_softc *sc; + int line; +{ + int i, asr; + + for (i = 0; i < 5000; i++) { +#ifndef MEMORY_MAPPED + asr = bus_space_read_1(sc->sc_iot, sc->sc_ioh, CNW_REG_ASR); +#else + asr = bus_space_read_1(sc->sc_memt, sc->sc_memh, + sc->sc_memoff + CNW_IOM_OFF + CNW_REG_ASR); +#endif + if (asr & CNW_ASR_WOC) + return (0); + DELAY(100); + } + if (line > 0) +#if !defined(__FreeBSD__) + printf("%s: wedged at line %d\n", sc->sc_dev.dv_xname, line); +#else + device_printf(sc->dev, "wedged at line %d\n", line); +#endif + return (1); +} +#define WAIT_WOC(sc) wait_WOC(sc, __LINE__) + + +/* + * Read a 16 bit value from the card. + */ +static int +read16(sc, offset) + struct cnw_softc *sc; + int offset; +{ + int hi, lo; + int offs = sc->sc_memoff + offset; + + /* This could presumably be done more efficient with + * bus_space_read_2(), but I don't know anything about the + * byte sex guarantees... Besides, this is pretty cheap as + * well :-) + */ + lo = bus_space_read_1(sc->sc_memt, sc->sc_memh, offs); + hi = bus_space_read_1(sc->sc_memt, sc->sc_memh, offs + 1); + return ((hi << 8) | lo); +} + + +/* + * Send a command to the card by writing it to the command buffer. + */ +int +cnw_cmd(sc, cmd, count, arg1, arg2) + struct cnw_softc *sc; + int cmd, count, arg1, arg2; +{ + int ptr = sc->sc_memoff + CNW_EREG_CB; + + if (wait_WOC(sc, 0)) { +#if !defined(__FreeBSD__) + printf("%s: wedged when issuing cmd 0x%x\n", + sc->sc_dev.dv_xname, cmd); +#else + device_printf(sc->dev, "wedged when issuing cmd 0x%x\n", cmd); +#endif + /* + * We'll continue anyway, as that's probably the best + * thing we can do; at least the user knows there's a + * problem, and can reset the interface with ifconfig + * down/up. + */ + } + + bus_space_write_1(sc->sc_memt, sc->sc_memh, ptr, cmd); + if (count > 0) { + bus_space_write_1(sc->sc_memt, sc->sc_memh, ptr + 1, arg1); + if (count > 1) + bus_space_write_1(sc->sc_memt, sc->sc_memh, + ptr + 2, arg2); + } + bus_space_write_1(sc->sc_memt, sc->sc_memh, + ptr + count + 1, CNW_CMD_EOC); + return (0); +} +#define CNW_CMD0(sc, cmd) \ + do { cnw_cmd(sc, cmd, 0, 0, 0); } while (0) +#define CNW_CMD1(sc, cmd, arg1) \ + do { cnw_cmd(sc, cmd, 1, arg1 , 0); } while (0) +#define CNW_CMD2(sc, cmd, arg1, arg2) \ + do { cnw_cmd(sc, cmd, 2, arg1, arg2); } while (0) + +/* ---------------------------------------------------------------- */ + +/* + * Reset the hardware. + */ +void +cnw_reset(sc) + struct cnw_softc *sc; +{ +#ifdef CNW_DEBUG + if (sc->sc_ethercom.ec_if.if_flags & IFF_DEBUG) + printf("%s: resetting\n", sc->sc_dev.dv_xname); +#endif + wait_WOC(sc, 0); +#ifndef MEMORY_MAPPED + bus_space_write_1(sc->sc_iot, sc->sc_ioh, CNW_REG_PMR, CNW_PMR_RESET); +#else + bus_space_write_1(sc->sc_memt, sc->sc_memh, + sc->sc_memoff + CNW_IOM_OFF + CNW_REG_PMR, CNW_PMR_RESET); +#endif + bus_space_write_1(sc->sc_memt, sc->sc_memh, + sc->sc_memoff + CNW_EREG_ASCC, CNW_ASR_WOC); +#ifndef MEMORY_MAPPED + bus_space_write_1(sc->sc_iot, sc->sc_ioh, CNW_REG_PMR, 0); +#else + bus_space_write_1(sc->sc_memt, sc->sc_memh, + sc->sc_memoff + CNW_IOM_OFF + CNW_REG_PMR, 0); +#endif +} + + +/* + * Initialize the card. + */ +void +cnw_init(sc) + struct cnw_softc *sc; +{ +#if !defined(__FreeBSD__) + struct ifnet *ifp = &sc->sc_ethercom.ec_if; +#else /* FreeBSD */ + struct ifnet *ifp = &sc->arpcom.ac_if; +#endif + const u_int8_t rxmode = + CNW_RXCONF_RXENA | CNW_RXCONF_BCAST | CNW_RXCONF_AMP; + + /* Reset the card */ + cnw_reset(sc); + + /* Issue a NOP to check the card */ + CNW_CMD0(sc, CNW_CMD_NOP); + + /* Set up receive configuration */ + CNW_CMD1(sc, CNW_CMD_SRC, + rxmode | ((ifp->if_flags & IFF_PROMISC) ? CNW_RXCONF_PRO : 0)); + + /* Set up transmit configuration */ + CNW_CMD1(sc, CNW_CMD_STC, CNW_TXCONF_TXENA); + + /* Set domain */ + CNW_CMD2(sc, CNW_CMD_SMD, sc->sc_domain, sc->sc_domain >> 8); + + /* Set scramble key */ + CNW_CMD2(sc, CNW_CMD_SSK, sc->sc_skey, sc->sc_skey >> 8); + + /* Enable interrupts */ + WAIT_WOC(sc); +#ifndef MEMORY_MAPPED + bus_space_write_1(sc->sc_iot, sc->sc_ioh, + CNW_REG_IMR, CNW_IMR_IENA | CNW_IMR_RFU1); +#else + bus_space_write_1(sc->sc_memt, sc->sc_memh, + sc->sc_memoff + CNW_IOM_OFF + CNW_REG_IMR, + CNW_IMR_IENA | CNW_IMR_RFU1); +#endif + + /* Enable receiver */ + CNW_CMD0(sc, CNW_CMD_ER); + + /* "Set the IENA bit in COR" */ + WAIT_WOC(sc); +#ifndef MEMORY_MAPPED + bus_space_write_1(sc->sc_iot, sc->sc_ioh, CNW_REG_COR, + CNW_COR_IENA | CNW_COR_LVLREQ); +#else + bus_space_write_1(sc->sc_memt, sc->sc_memh, + sc->sc_memoff + CNW_IOM_OFF + CNW_REG_COR, + CNW_COR_IENA | CNW_COR_LVLREQ); +#endif +} + + +#if !defined(__FreeBSD__) +/* + * Enable and initialize the card. + */ +int +cnw_enable(sc) + struct cnw_softc *sc; +{ + struct ifnet *ifp = &sc->sc_ethercom.ec_if; + + if ((ifp->if_flags & IFF_RUNNING) != 0) + return (0); + + sc->sc_ih = pcmcia_intr_establish(sc->sc_pf, IPL_NET, cnw_intr, sc); + if (sc->sc_ih == NULL) { + printf("%s: couldn't establish interrupt handler\n", + sc->sc_dev.dv_xname); + return (EIO); + } + if (pcmcia_function_enable(sc->sc_pf) != 0) { + printf("%s: couldn't enable card\n", sc->sc_dev.dv_xname); + return (EIO); + } + sc->sc_resource |= CNW_RES_PCIC; + cnw_init(sc); + ifp->if_flags &= ~IFF_OACTIVE; + ifp->if_flags |= IFF_RUNNING; + return (0); +} + + +/* + * Stop and disable the card. + */ +void +cnw_disable(sc) + struct cnw_softc *sc; +{ + struct ifnet *ifp = &sc->sc_ethercom.ec_if; + + if ((ifp->if_flags & IFF_RUNNING) == 0) + return; + + pcmcia_function_disable(sc->sc_pf); + sc->sc_resource &= ~CNW_RES_PCIC; + pcmcia_intr_disestablish(sc->sc_pf, sc->sc_ih); + ifp->if_flags &= ~IFF_RUNNING; + ifp->if_timer = 0; +} + +/* + * Match the hardware we handle. + */ +int +cnw_match(parent, match, aux) + struct device *parent; + struct cfdata *match; + void *aux; +{ + struct pcmcia_attach_args *pa = aux; + + if (pa->manufacturer == PCMCIA_VENDOR_XIRCOM && + pa->product == PCMCIA_PRODUCT_XIRCOM_CNW_801) + return 1; + if (pa->manufacturer == PCMCIA_VENDOR_XIRCOM && + pa->product == PCMCIA_PRODUCT_XIRCOM_CNW_802) + return 1; + return 0; +} + + +/* + * Attach the card. + */ +void +cnw_attach(parent, self, aux) + struct device *parent, *self; + void *aux; +{ + struct cnw_softc *sc = (void *) self; + struct pcmcia_attach_args *pa = aux; + struct ifnet *ifp = &sc->sc_ethercom.ec_if; + u_int8_t macaddr[ETHER_ADDR_LEN]; + int i; + bus_size_t memsize; + + sc->sc_resource = 0; + + /* Enable the card */ + sc->sc_pf = pa->pf; + pcmcia_function_init(sc->sc_pf, sc->sc_pf->cfe_head.sqh_first); + if (pcmcia_function_enable(sc->sc_pf)) { + printf(": function enable failed\n"); + return; + } + sc->sc_resource |= CNW_RES_PCIC; + + /* Map I/O register and "memory" */ +#ifndef MEMORY_MAPPED + if (pcmcia_io_alloc(sc->sc_pf, 0, CNW_IO_SIZE, CNW_IO_SIZE, + &sc->sc_pcioh) != 0) { + printf(": can't allocate i/o space\n"); + goto fail; + } + if (pcmcia_io_map(sc->sc_pf, PCMCIA_WIDTH_IO16, 0, + CNW_IO_SIZE, &sc->sc_pcioh, &sc->sc_iowin) != 0) { + printf(": can't map i/o space\n"); + pcmcia_io_free(sc->sc_pf, &sc->sc_pcioh); + goto fail; + } + sc->sc_iot = sc->sc_pcioh.iot; + sc->sc_ioh = sc->sc_pcioh.ioh; + sc->sc_resource |= CNW_RES_IO; +#endif +#ifndef MEMORY_MAPPED + memsize = CNW_MEM_SIZE; +#else + memsize = CNW_MEM_SIZE + CNW_IOM_SIZE; +#endif + if (pcmcia_mem_alloc(sc->sc_pf, memsize, &sc->sc_pcmemh) != 0) { + printf(": can't allocate memory\n"); + goto fail; + } + if (pcmcia_mem_map(sc->sc_pf, PCMCIA_WIDTH_MEM8|PCMCIA_MEM_COMMON, + CNW_MEM_ADDR, memsize, &sc->sc_pcmemh, &sc->sc_memoff, + &sc->sc_memwin) != 0) { + printf(": can't map memory\n"); + pcmcia_mem_free(sc->sc_pf, &sc->sc_pcmemh); + goto fail; + } + sc->sc_memt = sc->sc_pcmemh.memt; + sc->sc_memh = sc->sc_pcmemh.memh; + sc->sc_resource |= CNW_RES_MEM; + switch (pa->product) { + case PCMCIA_PRODUCT_XIRCOM_CNW_801: + printf(": %s\n", PCMCIA_STR_XIRCOM_CNW_801); + break; + case PCMCIA_PRODUCT_XIRCOM_CNW_802: + printf(": %s\n", PCMCIA_STR_XIRCOM_CNW_802); + break; + } + + /* Finish setup of softc */ + sc->sc_domain = cnw_domain; + sc->sc_skey = cnw_skey; + + /* Get MAC address */ + cnw_reset(sc); + for (i = 0; i < ETHER_ADDR_LEN; i++) + macaddr[i] = bus_space_read_1(sc->sc_memt, sc->sc_memh, + sc->sc_memoff + CNW_EREG_PA + i); + printf("%s: address %s\n", sc->sc_dev.dv_xname, + ether_sprintf(macaddr)); + + /* Set up ifnet structure */ + bcopy(sc->sc_dev.dv_xname, ifp->if_xname, IFNAMSIZ); + ifp->if_softc = sc; + ifp->if_start = cnw_start; + ifp->if_ioctl = cnw_ioctl; + ifp->if_watchdog = cnw_watchdog; + ifp->if_flags = IFF_BROADCAST | IFF_MULTICAST | IFF_SIMPLEX | + IFF_NOTRAILERS; + + /* Attach the interface */ + if_attach(ifp); + ether_ifattach(ifp, macaddr); +#if NBPFILTER > 0 + bpfattach(&sc->sc_ethercom.ec_if.if_bpf, ifp, DLT_EN10MB, + sizeof(struct ether_header)); +#endif + sc->sc_resource |= CNW_RES_NET; + + ifp->if_baudrate = IF_Mbps(1); + + /* Disable the card now, and turn it on when the interface goes up */ + pcmcia_function_disable(sc->sc_pf); + sc->sc_resource &= ~CNW_RES_PCIC; + return; + +fail: +#ifndef MEMORY_MAPPED + if ((sc->sc_resource & CNW_RES_IO) != 0) { + pcmcia_io_unmap(sc->sc_pf, sc->sc_iowin); + pcmcia_io_free(sc->sc_pf, &sc->sc_pcioh); + sc->sc_resource &= ~CNW_RES_IO; + } +#endif + if ((sc->sc_resource & CNW_RES_PCIC) != 0) { + pcmcia_function_disable(sc->sc_pf); + sc->sc_resource &= ~CNW_RES_PCIC; + } +} +#endif /* !defined(__FreeBSD__) */ + +/* + * Start outputting on the interface. + */ +void +cnw_start(ifp) + struct ifnet *ifp; +{ + struct cnw_softc *sc = ifp->if_softc; + struct mbuf *m0; + int lif; + int asr; +#ifdef ONE_AT_A_TIME + struct timeval now; +#endif + +#ifdef CNW_DEBUG + if (sc->sc_ethercom.ec_if.if_flags & IFF_DEBUG) + printf("%s: cnw_start\n", ifp->if_xname); + if (ifp->if_flags & IFF_OACTIVE) + printf("%s: cnw_start reentered\n", ifp->if_xname); +#endif + +#if defined(__FreeBSD__) + if (sc->cnw_gone) + return; +#endif + + ifp->if_flags |= IFF_OACTIVE; + + for (;;) { +#ifdef ONE_AT_A_TIME + microtime(&now); + now.tv_sec -= sc->sc_txlast.tv_sec; + now.tv_usec -= sc->sc_txlast.tv_usec; + if (now.tv_usec < 0) { + now.tv_usec += 1000000; + now.tv_sec--; + } + + /* + * Don't ship this packet out until the last + * packet has left the building. + * If we have not tried to send a packet for 1/5 + * a second then we assume we lost an interrupt, + * lets go on and send the next packet anyhow. + * + * I suppose we could check to see if it is okay + * to put additional packets on the card (beyond + * the one already waiting to be sent) but I don't + * think we would get any improvement in speed as + * we should have ample time to put the next packet + * on while this one is going out. + */ + if (sc->sc_active && now.tv_sec == 0 && now.tv_usec < 200000) + break; +#endif + + /* Make sure the link integrity field is on */ + WAIT_WOC(sc); + lif = bus_space_read_1(sc->sc_memt, sc->sc_memh, + sc->sc_memoff + CNW_EREG_LIF); + if (lif == 0) { +#ifdef CNW_DEBUG + if (sc->sc_ethercom.ec_if.if_flags & IFF_DEBUG) + printf("%s: link integrity %d\n", lif); +#endif + break; + } + + /* Is there any buffer space available on the card? */ + WAIT_WOC(sc); +#ifndef MEMORY_MAPPED + asr = bus_space_read_1(sc->sc_iot, sc->sc_ioh, CNW_REG_ASR); +#else + asr = bus_space_read_1(sc->sc_memt, sc->sc_memh, + sc->sc_memoff + CNW_IOM_OFF + CNW_REG_ASR); +#endif + if (!(asr & CNW_ASR_TXBA)) { +#ifdef CNW_DEBUG + if (sc->sc_ethercom.ec_if.if_flags & IFF_DEBUG) + printf("%s: no buffer space\n", ifp->if_xname); +#endif + break; + } + + sc->sc_stats.nws_tx++; + + IF_DEQUEUE(&ifp->if_snd, m0); + if (m0 == 0) + break; + +#if !defined(__FreeBSD__) +#if NBPFILTER > 0 + if (ifp->if_bpf) + bpf_mtap(ifp->if_bpf, m0); +#endif +#else /* FreeBSD */ + if (ifp->if_bpf) + bpf_mtap(ifp, m0); +#endif + + cnw_transmit(sc, m0); + ++ifp->if_opackets; + ifp->if_timer = 3; /* start watchdog timer */ + + microtime(&sc->sc_txlast); + sc->sc_active = 1; + } + + ifp->if_flags &= ~IFF_OACTIVE; +} + +/* + * Transmit a packet. + */ +void +cnw_transmit(sc, m0) + struct cnw_softc *sc; + struct mbuf *m0; +{ + int buffer, bufsize, bufoffset, bufptr, bufspace, len, mbytes, n; + struct mbuf *m; + u_int8_t *mptr; + + /* Get buffer info from card */ + buffer = read16(sc, CNW_EREG_TDP); + bufsize = read16(sc, CNW_EREG_TDP + 2); + bufoffset = read16(sc, CNW_EREG_TDP + 4); +#ifdef CNW_DEBUG + if (sc->sc_ethercom.ec_if.if_flags & IFF_DEBUG) + printf("%s: cnw_transmit b=0x%x s=%d o=0x%x\n", + sc->sc_dev.dv_xname, buffer, bufsize, bufoffset); +#endif + + /* Copy data from mbuf chain to card buffers */ + bufptr = sc->sc_memoff + buffer + bufoffset; + bufspace = bufsize; + len = 0; + for (m = m0; m; ) { + mptr = mtod(m, u_int8_t *); + mbytes = m->m_len; + len += mbytes; + while (mbytes > 0) { + if (bufspace == 0) { + buffer = read16(sc, buffer); + bufptr = sc->sc_memoff + buffer + bufoffset; + bufspace = bufsize; +#ifdef CNW_DEBUG + if (sc->sc_ethercom.ec_if.if_flags & IFF_DEBUG) + printf("%s: next buffer @0x%x\n", + sc->sc_dev.dv_xname, buffer); +#endif + } + n = mbytes <= bufspace ? mbytes : bufspace; + bus_space_write_region_1(sc->sc_memt, sc->sc_memh, + bufptr, mptr, n); + bufptr += n; + bufspace -= n; + mptr += n; + mbytes -= n; + } + MFREE(m, m0); + m = m0; + } + + /* Issue transmit command */ + CNW_CMD2(sc, CNW_CMD_TL, len, len >> 8); +} + + +/* + * Pull a packet from the card into an mbuf chain. + */ +struct mbuf * +cnw_read(sc) + struct cnw_softc *sc; +{ + struct mbuf *m, *top, **mp; + int totbytes, buffer, bufbytes, bufptr, mbytes, n; + u_int8_t *mptr; + + WAIT_WOC(sc); + totbytes = read16(sc, CNW_EREG_RDP); +#ifdef CNW_DEBUG + if (sc->sc_ethercom.ec_if.if_flags & IFF_DEBUG) + printf("%s: recv %d bytes\n", sc->sc_dev.dv_xname, totbytes); +#endif + buffer = CNW_EREG_RDP + 2; + bufbytes = 0; + bufptr = 0; /* XXX make gcc happy */ + + MGETHDR(m, M_DONTWAIT, MT_DATA); + if (m == 0) + return (0); +#if !defined(__FreeBSD__) + m->m_pkthdr.rcvif = &sc->sc_ethercom.ec_if; +#else /* FreeBSD */ + m->m_pkthdr.rcvif = &sc->arpcom.ac_if; +#endif + m->m_pkthdr.len = totbytes; + mbytes = MHLEN; + top = 0; + mp = ⊤ + + while (totbytes > 0) { + if (top) { + MGET(m, M_DONTWAIT, MT_DATA); + if (m == 0) { + m_freem(top); + return (0); + } + mbytes = MLEN; + } + if (totbytes >= MINCLSIZE) { + MCLGET(m, M_DONTWAIT); + if ((m->m_flags & M_EXT) == 0) { + m_free(m); + m_freem(top); + return (0); + } + mbytes = MCLBYTES; + } + if (!top) { + int pad = ALIGN(sizeof(struct ether_header)) - + sizeof(struct ether_header); + m->m_data += pad; + mbytes -= pad; + } + mptr = mtod(m, u_int8_t *); + mbytes = m->m_len = min(totbytes, mbytes); + totbytes -= mbytes; + while (mbytes > 0) { + if (bufbytes == 0) { + buffer = read16(sc, buffer); + bufbytes = read16(sc, buffer + 2); + bufptr = sc->sc_memoff + buffer + + read16(sc, buffer + 4); +#ifdef CNW_DEBUG + if (sc->sc_ethercom.ec_if.if_flags & IFF_DEBUG) + printf("%s: %d bytes @0x%x+0x%x\n", + sc->sc_dev.dv_xname, bufbytes, + buffer, bufptr - buffer - + sc->sc_memoff); +#endif + } + n = mbytes <= bufbytes ? mbytes : bufbytes; + bus_space_read_region_1(sc->sc_memt, sc->sc_memh, + bufptr, mptr, n); + bufbytes -= n; + bufptr += n; + mbytes -= n; + mptr += n; + } + *mp = m; + mp = &m->m_next; + } + + return (top); +} + + +/* + * Handle received packets. + */ +void +cnw_recv(sc) + struct cnw_softc *sc; +{ + int rser; +#if !defined(__FreeBSD__) + struct ifnet *ifp = &sc->sc_ethercom.ec_if; +#else + struct ifnet *ifp = &sc->arpcom.ac_if; + struct ether_header *eh; +#endif + struct mbuf *m; + + for (;;) { + WAIT_WOC(sc); + rser = bus_space_read_1(sc->sc_memt, sc->sc_memh, + sc->sc_memoff + CNW_EREG_RSER); + if (!(rser & CNW_RSER_RXAVAIL)) + return; + + /* Pull packet off card */ + m = cnw_read(sc); + + /* Acknowledge packet */ + CNW_CMD0(sc, CNW_CMD_SRP); + + /* Did we manage to get the packet from the interface? */ + if (m == 0) { + ++ifp->if_ierrors; + return; + } + ++ifp->if_ipackets; + +#if !defined(__FreeBSD__) +#if NBPFILTER > 0 + if (ifp->if_bpf) + bpf_mtap(ifp->if_bpf, m); +#endif +#else /* FreeBSD */ + if (ifp->if_bpf) + bpf_mtap(ifp, m); +#endif + + /* Pass the packet up. */ +#if !defined(__FreeBSD__) + (*ifp->if_input)(ifp, m); +#else + eh = mtod(m, struct ether_header *); + m_adj(m, sizeof(struct ether_header)); + ether_input(ifp, eh, m) ; +#endif + } +} + + +/* + * Interrupt handler. + */ +#if !defined(__FreeBSD__) +int +#else +void +#endif +cnw_intr(arg) + void *arg; +{ + struct cnw_softc *sc = arg; +#if !defined(__FreeBSD__) + struct ifnet *ifp = &sc->sc_ethercom.ec_if; +#else + struct ifnet *ifp = &sc->arpcom.ac_if; +#endif + int ret, status, rser, tser; + + +#if !defined(__FreeBSD__) + if ((sc->sc_ethercom.ec_if.if_flags & IFF_RUNNING) == 0 || + (sc->sc_dev.dv_flags & DVF_ACTIVE) == 0) + return (0); +#else + if ((ifp->if_flags & IFF_RUNNING) == 0) + return; +#endif + ifp->if_timer = 0; /* stop watchdog timer */ + + ret = 0; + for (;;) { + WAIT_WOC(sc); +#ifndef MEMORY_MAPPED + status = bus_space_read_1(sc->sc_iot, sc->sc_ioh, + CNW_REG_CCSR); +#else + status = bus_space_read_1(sc->sc_memt, sc->sc_memh, + sc->sc_memoff + CNW_IOM_OFF + CNW_REG_CCSR); +#endif + +#if !defined(__FreeBSD__) + if (!(status & 0x02)) { + if (ret == 0) + printf("%s: spurious interrupt\n", + sc->sc_dev.dv_xname); + return (ret); + } +#else + if (!(status & 0x02)) { + if (ret == 0) + device_printf(sc->dev, "spurious interrupt\n"); + return; + } +#endif + ret = 1; + +#ifndef MEMORY_MAPPED + status = bus_space_read_1(sc->sc_iot, sc->sc_ioh, CNW_REG_ASR); +#else + status = bus_space_read_1(sc->sc_memt, sc->sc_memh, + sc->sc_memoff + CNW_IOM_OFF + CNW_REG_ASR); +#endif + + /* Anything to receive? */ + if (status & CNW_ASR_RXRDY) { + sc->sc_stats.nws_rx++; + cnw_recv(sc); + } + + /* Receive error */ + if (status & CNW_ASR_RXERR) { + /* + * I get a *lot* of spurious receive errors + * (many per second), even when the interface + * is quiescent, so we don't increment + * if_ierrors here. + */ + rser = bus_space_read_1(sc->sc_memt, sc->sc_memh, + sc->sc_memoff + CNW_EREG_RSER); + + /* RX statistics */ + sc->sc_stats.nws_rxerr++; + if (rser & CNW_RSER_RXBIG) + sc->sc_stats.nws_rxframe++; + if (rser & CNW_RSER_RXCRC) + sc->sc_stats.nws_rxcrcerror++; + if (rser & CNW_RSER_RXOVERRUN) + sc->sc_stats.nws_rxoverrun++; + if (rser & CNW_RSER_RXOVERFLOW) + sc->sc_stats.nws_rxoverflow++; + if (rser & CNW_RSER_RXERR) + sc->sc_stats.nws_rxerrors++; + if (rser & CNW_RSER_RXAVAIL) + sc->sc_stats.nws_rxavail++; + + /* Clear error bits in RSER */ + WAIT_WOC(sc); + bus_space_write_1(sc->sc_memt, sc->sc_memh, + sc->sc_memoff + CNW_EREG_RSERW, + CNW_RSER_RXERR | + (rser & (CNW_RSER_RXCRC | CNW_RSER_RXBIG))); + /* Clear RXERR in ASR */ + WAIT_WOC(sc); + bus_space_write_1(sc->sc_memt, sc->sc_memh, + sc->sc_memoff + CNW_EREG_ASCC, CNW_ASR_RXERR); + } + + /* Transmit done */ + if (status & CNW_ASR_TXDN) { + tser = bus_space_read_1(sc->sc_memt, sc->sc_memh, + CNW_EREG_TSER); + + /* TX statistics */ + if (tser & CNW_TSER_TXERR) + sc->sc_stats.nws_txerrors++; + if (tser & CNW_TSER_TXNOAP) + sc->sc_stats.nws_txlostcd++; + if (tser & CNW_TSER_TXGU) + sc->sc_stats.nws_txabort++; + + if (tser & CNW_TSER_TXOK) { + sc->sc_stats.nws_txokay++; + sc->sc_stats.nws_txretries[status & 0xf]++; + WAIT_WOC(sc); + bus_space_write_1(sc->sc_memt, sc->sc_memh, + sc->sc_memoff + CNW_EREG_TSERW, + CNW_TSER_TXOK | CNW_TSER_RTRY); + } + + if (tser & CNW_TSER_ERROR) { + ++ifp->if_oerrors; + WAIT_WOC(sc); + bus_space_write_1(sc->sc_memt, sc->sc_memh, + sc->sc_memoff + CNW_EREG_TSERW, + (tser & CNW_TSER_ERROR) | + CNW_TSER_RTRY); + } + + sc->sc_active = 0; + ifp->if_flags &= ~IFF_OACTIVE; + + /* Continue to send packets from the queue */ +#if !defined(__FreeBSD__) + cnw_start(&sc->sc_ethercom.ec_if); +#else + cnw_start(ifp); +#endif + } + + } +} + + +/* + * Handle device ioctls. + */ +int +cnw_ioctl(ifp, cmd, data) + struct ifnet *ifp; + u_long cmd; + caddr_t data; +{ + struct cnw_softc *sc = ifp->if_softc; +#if !defined(__FreeBSD__) + struct ifaddr *ifa = (struct ifaddr *)data; +#endif + struct ifreq *ifr = (struct ifreq *)data; + int s, error = 0; + struct proc *p = curproc; /*XXX*/ + + s = splnet(); + +#if defined(__FreeBSD__) + if (sc->cnw_gone) { + splx(s); + return(ENODEV); + } +#endif + + switch (cmd) { + + case SIOCSIFADDR: +#if !defined(__FreeBSD__) + if (!(ifp->if_flags & IFF_RUNNING) && + (error = cnw_enable(sc)) != 0) + break; + ifp->if_flags |= IFF_UP; + switch (ifa->ifa_addr->sa_family) { +#ifdef INET + case AF_INET: + cnw_init(sc); + arp_ifinit(&sc->sc_ethercom.ec_if, ifa); + break; +#endif + default: + cnw_init(sc); + break; + } +#else + error = ether_ioctl(ifp, cmd, data); +#endif + break; + + case SIOCSIFFLAGS: +#if !defined(__FreeBSD__) + if ((ifp->if_flags & (IFF_UP | IFF_RUNNING)) == IFF_RUNNING) { + /* + * The interface is marked down and it is running, so + * stop it. + */ + cnw_disable(sc); + } else if ((ifp->if_flags & (IFF_UP | IFF_RUNNING)) == IFF_UP){ + /* + * The interface is marked up and it is stopped, so + * start it. + */ + error = cnw_enable(sc); + } else { + /* IFF_PROMISC may be changed */ + cnw_init(sc); + } +#else + if (ifp->if_flags & IFF_UP) { + cnw_freebsd_init(sc); + } else { + if (ifp->if_flags & IFF_RUNNING) { + cnw_stop(sc); + } else { + cnw_freebsd_init(sc); + } + } + +#endif + break; + + case SIOCADDMULTI: + case SIOCDELMULTI: +#if !defined(__FreeBSD__) + /* Update our multicast list. */ + error = (cmd == SIOCADDMULTI) ? + ether_addmulti(ifr, &sc->sc_ethercom) : + ether_delmulti(ifr, &sc->sc_ethercom); + if (error == ENETRESET || error == 0) { + cnw_init(sc); + error = 0; + } +#else + /* XXX */ + error = 0; +#endif + break; + + case SIOCGCNWDOMAIN: + ((struct ifreq *)data)->ifr_domain = sc->sc_domain; + break; + + case SIOCSCNWDOMAIN: +#if !defined(__FreeBSD__) + error = suser(p->p_ucred, &p->p_acflag); +#else + error = suser(p); +#endif + if (error) + break; + error = cnw_setdomain(sc, ifr->ifr_domain); + break; + + case SIOCSCNWKEY: +#if !defined(__FreeBSD__) + error = suser(p->p_ucred, &p->p_acflag); +#else + error = suser(p); +#endif + if (error) + break; + error = cnw_setkey(sc, (int)ifr->ifr_key); + break; + + case SIOCGCNWSTATUS: +#if !defined(__FreeBSD__) + error = suser(p->p_ucred, &p->p_acflag); +#else + error = suser(p); +#endif + if (error) + break; + if ((ifp->if_flags & IFF_RUNNING) == 0) + break; + bus_space_read_region_1(sc->sc_memt, sc->sc_memh, + sc->sc_memoff + CNW_EREG_CB, + ((struct cnwstatus *)data)->data, + sizeof(((struct cnwstatus *)data)->data)); + break; + + case SIOCGCNWSTATS: + bcopy((void *)&sc->sc_stats, + (void *)&(((struct cnwistats *)data)->stats), + sizeof(struct cnwstats)); + break; + + default: + error = EINVAL; + break; + } + + splx(s); + return (error); +} + + +/* + * Device timeout/watchdog routine. Entered if the device neglects to + * generate an interrupt after a transmit has been started on it. + */ +void +cnw_watchdog(ifp) + struct ifnet *ifp; +{ + struct cnw_softc *sc = ifp->if_softc; + +#if !defined(__FreeBSD__) + printf("%s: device timeout; card reset\n", sc->sc_dev.dv_xname); + ++ifp->if_oerrors; + cnw_init(sc); +#else + device_printf(sc->dev, "device timeout; card reset\n"); + ++ifp->if_oerrors; + cnw_freebsd_init(sc); +#endif +} + +int +cnw_setdomain(sc, domain) + struct cnw_softc *sc; + int domain; +{ + int s; + + if (domain & ~0x1ff) + return EINVAL; + + s = splnet(); + CNW_CMD2(sc, CNW_CMD_SMD, domain, domain >> 8); + splx(s); + + sc->sc_domain = domain; + return 0; +} + +int +cnw_setkey(sc, key) + struct cnw_softc *sc; + int key; +{ + int s; + + if (key & ~0xffff) + return EINVAL; + + s = splnet(); + CNW_CMD2(sc, CNW_CMD_SSK, key, key >> 8); + splx(s); + + sc->sc_skey = key; + return 0; +} + +#if !defined(__FreeBSD__) +int +cnw_activate(self, act) + struct device *self; + enum devact act; +{ + struct cnw_softc *sc = (struct cnw_softc *)self; + int rv = 0, s; + + s = splnet(); + switch (act) { + case DVACT_ACTIVATE: + rv = EOPNOTSUPP; + break; + + case DVACT_DEACTIVATE: + if_deactivate(&sc->sc_ethercom.ec_if); + break; + } + splx(s); + return (rv); +} + +int +cnw_detach(self, flags) + struct device *self; + int flags; +{ + struct cnw_softc *sc = (struct cnw_softc *)self; + struct ifnet *ifp = &sc->sc_ethercom.ec_if; + + /* cnw_disable() checks IFF_RUNNING */ + cnw_disable(sc); + + if ((sc->sc_resource & CNW_RES_NET) != 0) { +#if NBPFILTER > 0 + bpfdetach(ifp); +#endif + ether_ifdetach(ifp); + if_detach(ifp); + } + +#ifndef MEMORY_MAPPED + /* unmap and free our i/o windows */ + if ((sc->sc_resource & CNW_RES_IO) != 0) { + pcmcia_io_unmap(sc->sc_pf, sc->sc_iowin); + pcmcia_io_free(sc->sc_pf, &sc->sc_pcioh); + } +#endif + + /* unmap and free our memory windows */ + if ((sc->sc_resource & CNW_RES_MEM) != 0) { + pcmcia_mem_unmap(sc->sc_pf, sc->sc_memwin); + pcmcia_mem_free(sc->sc_pf, &sc->sc_pcmemh); + } + + return (0); +} +#endif + +#if defined(__FreeBSD__) +static void cnw_freebsd_init(xsc) + void *xsc; +{ + struct cnw_softc *sc = xsc; + struct ifnet *ifp = &sc->arpcom.ac_if; + int s; + + if (sc->cnw_gone) + return; + + s = splimp(); + cnw_init(sc); + +#if 0 + if (ifp->if_flags & IFF_RUNNING) + cnw_stop(sc); +#endif + + ifp->if_flags |= IFF_RUNNING; + ifp->if_flags &= ~IFF_OACTIVE; + +/* sc->cnw_stat_ch = timeout(cnw_inquire, sc, hz * 60); */ + + cnw_start(ifp); + + splx(s); + + return; +} + +static void cnw_stop(sc) + struct cnw_softc *sc; +{ + struct ifnet *ifp; + + if (sc->cnw_gone) + return; + + cnw_reset(sc); + + ifp = &sc->arpcom.ac_if; + ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE); + + return; +} + +static int cnw_pccard_probe(dev) + device_t dev; +{ + struct cnw_softc *sc; + int error; + + sc = device_get_softc(dev); + sc->cnw_gone = 0; + + error = cnw_alloc(dev); + if (error) + return (error); + + device_set_desc(dev, "Netwave AirSurfer Wireless LAN"); + cnw_free(dev); + + return (0); +} + +static int cnw_pccard_detach(dev) + device_t dev; +{ + struct cnw_softc *sc; + struct ifnet *ifp; +#if 0 + int s; + + s = splimp(); +#endif + + sc = device_get_softc(dev); + ifp = &sc->arpcom.ac_if; + + if (sc->cnw_gone) { + device_printf(dev, "already unloaded\n"); + return(ENODEV); + } + + cnw_stop(sc); + + ether_ifdetach(ifp, ETHER_BPF_SUPPORTED); + cnw_free(dev); + sc->cnw_gone = 1; + +#if 0 + splx(s); +#endif + device_printf(dev, "unload\n"); + + return(0); +} + +static int cnw_pccard_attach(device_t dev) +{ + struct cnw_softc *sc; + struct ifnet *ifp; + int i, error; + + sc = device_get_softc(dev); + ifp = &sc->arpcom.ac_if; + + error = cnw_alloc(dev); + if (error) { + device_printf(dev, "cnw_alloc() failed! (%d)\n", error); + return (error); + } + + error = bus_setup_intr(dev, sc->irq, INTR_TYPE_NET, + cnw_intr, sc, &sc->cnw_intrhand); + + if (error) { + device_printf(dev, "bus_setup_intr() failed! (%d)\n", error); + cnw_free(dev); + return (error); + } + + /* Set initial values */ + sc->sc_domain = cnw_domain; + sc->sc_skey = cnw_skey; + + /* Reset the NIC. */ + cnw_reset(sc); + + /* Get MAC address */ + for (i=0; i< ETHER_ADDR_LEN; i++) { + sc->arpcom.ac_enaddr[i] = + bus_space_read_1(sc->sc_memt, sc->sc_memh, + sc->sc_memoff + CNW_EREG_PA + i); + } + device_printf(dev, "Ethernet address: %6D\n", + sc->arpcom.ac_enaddr, ":"); + + ifp->if_softc = sc; + ifp->if_name = "cnw"; + ifp->if_unit = device_get_unit(dev); + ifp->if_timer = 0; + ifp->if_mtu = ETHERMTU; + ifp->if_flags = (IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST); + ifp->if_ioctl = cnw_ioctl; + ifp->if_output = ether_output; + ifp->if_start = cnw_start; +/* ifp->if_watchdog = 0; */ + ifp->if_watchdog = cnw_watchdog; + ifp->if_init = cnw_freebsd_init; + ifp->if_baudrate = 1 * 1000* 1000; + ifp->if_snd.ifq_maxlen = IFQ_MAXLEN; + + cnw_freebsd_init(sc); + cnw_stop(sc); + + /* + * Call MI attach routine. + */ + ether_ifattach(ifp, ETHER_BPF_SUPPORTED); +/* callout_handle_init(&sc->cnw_stat_ch); */ + + return(0); +} + +static void cnw_shutdown(dev) + device_t dev; +{ + struct cnw_softc *sc; + + sc = device_get_softc(dev); + cnw_stop(sc); + + return; +} + +static int cnw_alloc(dev) + device_t dev; +{ + struct cnw_softc *sc = device_get_softc(dev); + int rid; + int error; + + rid = 0; + sc->mem_res = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid, + 0, ~0, 1, RF_ACTIVE); + if (!sc->mem_res) { + device_printf(dev, "Cannot allocate attribute memory\n"); + return (ENOMEM); + } + sc->sc_memt = rman_get_bustag(sc->mem_res); + sc->sc_memh = rman_get_bushandle(sc->mem_res); + + + error = CARD_SET_MEMORY_OFFSET(device_get_parent(dev), + dev, rid, CNW_MEM_ADDR, NULL); + if (error) { + device_printf(dev, + "CARD_SET_MEMORY_OFFSET returned 0x%0x", error); + return(error); + } + + error = CARD_SET_RES_FLAGS(device_get_parent(dev), dev, + SYS_RES_MEMORY, rid, 2); + if (error) { + device_printf(dev, + "CARD_SET_RES_FLAGS returned 0x%0x\n", error); + return (error); + } + + rid = 0; + sc->irq = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, + 0, ~0, 1, RF_ACTIVE); + if (!sc->irq) { + device_printf(dev, "No irq?!\n"); + return (ENXIO); + } + + sc->dev = dev; + sc->sc_memoff = 0; + + return (0); +} + +static void cnw_free(dev) + device_t dev; +{ + struct cnw_softc *sc = device_get_softc(dev); + + if (sc->mem_res != NULL) { + bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res); + sc->mem_res = 0; + } + if (sc->irq != NULL) { + bus_teardown_intr(dev, sc->irq, sc->cnw_intrhand); + bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq); + sc->irq = 0; + } + + return; +} + +#endif /* FreeBSD */ diff --git a/sys/dev/cnw/if_cnwioctl.h b/sys/dev/cnw/if_cnwioctl.h new file mode 100644 index 0000000..04e8c3b --- /dev/null +++ b/sys/dev/cnw/if_cnwioctl.h @@ -0,0 +1,114 @@ +/* $NetBSD: if_cnwioctl.h,v 1.2 1999/11/29 12:54:00 itojun Exp $ */ +/* $FreeBSD$ */ + +/* + * Copyright (c) 1996, 1997 Berkeley Software Design, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that this notice is retained, + * the conditions in the following notices are met, and terms applying + * to contributors in the following notices also apply to Berkeley + * Software Design, Inc. + * + * 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 + * Berkeley Software Design, Inc. + * 4. Neither the name of the Berkeley Software Design, Inc. 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 BERKELEY SOFTWARE DESIGN, INC. ``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 BERKELEY SOFTWARE DESIGN, INC. 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. + * + * PAO2 Id: if_cnwioctl.h,v 1.1.8.1 1998/12/05 22:47:11 itojun Exp + * + * Paul Borman, December 1996 + * + * This driver is derived from a generic frame work which is + * Copyright(c) 1994,1995,1996 + * Yoichi Shinoda, Yoshitaka Tokugawa, WIDE Project, Wildboar Project + * and Foretune. All rights reserved. + * + * A linux driver was used as the "hardware reference manual" (i.e., + * to determine registers and a general outline of how the card works) + * That driver is publically available and copyright + * + * John Markus BjNxrndalen + * Department of Computer Science + * University of TromsNx + * Norway + * johnm@staff.cs.uit.no, http://www.cs.uit.no/~johnm/ + */ + +struct cnwstatus { + struct ifreq ifr; + u_char data[0x100]; +}; + +struct cnwstats { + u_quad_t nws_rx; + u_quad_t nws_rxerr; + u_quad_t nws_rxoverflow; + u_quad_t nws_rxoverrun; + u_quad_t nws_rxcrcerror; + u_quad_t nws_rxframe; + u_quad_t nws_rxerrors; + u_quad_t nws_rxavail; + u_quad_t nws_rxone; + u_quad_t nws_tx; + u_quad_t nws_txokay; + u_quad_t nws_txabort; + u_quad_t nws_txlostcd; + u_quad_t nws_txerrors; + u_quad_t nws_txretries[16]; +}; + +struct cnwistats { + struct ifreq ifr; + struct cnwstats stats; +}; + +struct cnwtrail { + u_char what; + u_char status; + u_short length; + struct timeval when; + struct timeval done; +}; + +struct cnwitrail { + struct ifreq ifr; + int head; + struct cnwtrail trail[128]; +}; + +#if !defined(__FreeBSD__) +#define ifr_domain ifr_ifru.ifru_flags /* domain */ +#define ifr_key ifr_ifru.ifru_flags /* scramble key */ +#else +#define ifr_domain ifr_flags /* domain */ +#define ifr_key ifr_flags /* scramble key */ +#endif + +#define SIOCSCNWDOMAIN _IOW('i', 254, struct ifreq) /* set domain */ +#define SIOCGCNWDOMAIN _IOWR('i', 253, struct ifreq) /* get domain */ +#define SIOCSCNWKEY _IOWR('i', 252, struct ifreq) /* set scramble key */ +#define SIOCGCNWSTATUS _IOWR('i', 251, struct cnwstatus)/* get raw status */ +#define SIOCGCNWSTATS _IOWR('i', 250, struct cnwistats)/* get stats */ +#define SIOCGCNWTRAIL _IOWR('i', 249, struct cnwitrail)/* get trail */ diff --git a/sys/dev/cnw/if_cnwreg.h b/sys/dev/cnw/if_cnwreg.h new file mode 100644 index 0000000..8b62d20 --- /dev/null +++ b/sys/dev/cnw/if_cnwreg.h @@ -0,0 +1,147 @@ +/* $NetBSD: if_cnwreg.h,v 1.3 2000/07/05 18:42:19 itojun Exp $ */ +/* $FreeBSD$ */ + +/*- + * Copyright (c) 1998 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Michael Eriksson. + * + * 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. + */ + + +/* I/O area */ +#define CNW_IO_SIZE 0x10 +/* I/O area can be accessed via mapped memory too */ +#define CNW_IOM_ADDR 0x28000 +#define CNW_IOM_SIZE CNW_IO_SIZE +#define CNW_IOM_OFF (CNW_IOM_ADDR - CNW_MEM_ADDR) + +/* I/O registers */ +#define CNW_REG_COR 0x0 +# define CNW_COR_IENA 0x01 /* Interrupt enable */ +# define CNW_COR_LVLREQ 0x40 /* Keep high */ +#define CNW_REG_CCSR 0x2 +#define CNW_REG_ASR 0x4 +# define CNW_ASR_TXBA 0x01 /* Trasmit buffer available */ +# define CNW_ASR_WOC 0x08 /* Write Operation Complete */ +# define CNW_ASR_TXDN 0x20 /* Transmit done */ +# define CNW_ASR_RXERR 0x40 /* Receive error */ +# define CNW_ASR_RXRDY 0x80 /* Packet received */ +#define CNW_REG_IOLOW 0x6 +#define CNW_REG_IOHI 0x7 +#define CNW_REG_IOCONTROL 0x8 +#define CNW_REG_IMR 0xa +# define CNW_IMR_IENA 0x02 /* Interrupt enable */ +# define CNW_IMR_RFU1 0x10 /* RFU intr mask, keep high */ +#define CNW_REG_PMR 0xc +# define CNW_PMR_RESET 0x80 +#define CNW_REG_DATA 0xf + + +/* Mapped memory */ +#define CNW_MEM_ADDR 0x20000 +#define CNW_MEM_SIZE 0x8000 + +/* Extended I/O registers (memory mapped) */ +#define CNW_EREG_CB 0x100 +#define CNW_EREG_ASCC 0x114 +#define CNW_EREG_RSER 0x120 +# define CNW_RSER_RXBIG 0x02 +# define CNW_RSER_RXCRC 0x04 +# define CNW_RSER_RXOVERRUN 0x08 +# define CNW_RSER_RXOVERFLOW 0x10 +# define CNW_RSER_RXERR 0x40 +# define CNW_RSER_RXAVAIL 0x80 +#define CNW_EREG_RSERW 0x124 +#define CNW_EREG_TSER 0x130 +# define CNW_TSER_RTRY 0x0f +# define CNW_TSER_TXERR 0x10 +# define CNW_TSER_TXOK 0x20 +# define CNW_TSER_TXNOAP 0x40 +# define CNW_TSER_TXGU 0x80 +# define CNW_TSER_ERROR (CNW_TSER_TXERR | CNW_TSER_TXNOAP | \ + CNW_TSER_TXGU) +#define CNW_EREG_TSERW 0x134 +#define CNW_EREG_TDP 0x140 +#define CNW_EREG_LIF 0x14e +#define CNW_EREG_RDP 0x150 +#define CNW_EREG_SPCQ 0x154 +#define CNW_EREG_SPU 0x155 +#define CNW_EREG_ISPLQ 0x156 +#define CNW_EREG_HHC 0x158 +#define CNW_EREG_PA 0x160 +#define CNW_EREG_ARW 0x166 +#define CNW_EREG_MHS 0x16b +#define CNW_EREG_NI 0x16e +#define CNW_EREG_CRBP 0x17a +#define CNW_EREG_EC 0x180 +#define CNW_EREG_STAT_RXERR 0x184 +#define CNW_EREG_STAT_FRAME 0x186 +#define CNW_EREG_STAT_IBEAT 0x188 +#define CNW_EREG_STAT_RXBUF 0x18e +#define CNW_EREG_STAT_RXMULTI 0x190 +#define CNW_EREG_STAT_TXRETRY 0x192 +#define CNW_EREG_STAT_TXABORT 0x194 +#define CNW_EREG_STAT_OBEAT 0x198 +#define CNW_EREG_STAT_TXOK 0x19a +#define CNW_EREG_STAT_TXSENT 0x19c + +/* + * Commands used in the extended command buffer + * CNW_EREG_CB (0x100-0x10f) + */ +#define CNW_CMD_NOP 0x00 +#define CNW_CMD_SRC 0x01 +# define CNW_RXCONF_RXENA 0x80 /* Receive Enable */ +# define CNW_RXCONF_MAC 0x20 /* MAC host receive mode */ +# define CNW_RXCONF_PRO 0x10 /* Promiscuous */ +# define CNW_RXCONF_AMP 0x08 /* Accept Multicast Packets */ +# define CNW_RXCONF_BCAST 0x04 /* Accept Broadcast Packets */ +#define CNW_CMD_STC 0x02 +# define CNW_TXCONF_TXENA 0x80 /* Transmit Enable */ +# define CNW_TXCONF_MAC 0x20 /* Host sends MAC mode */ +# define CNW_TXCONF_EUD 0x10 /* Enable Uni-Data packets */ +# define CNW_TXCONF_KEY 0x02 /* Scramble data packets */ +# define CNW_TXCONF_LOOP 0x01 /* Loopback mode */ +#define CNW_CMD_AMA 0x03 +#define CNW_CMD_DMA 0x04 +#define CNW_CMD_SAMA 0x05 +#define CNW_CMD_ER 0x06 +#define CNW_CMD_DR 0x07 +#define CNW_CMD_TL 0x08 +#define CNW_CMD_SRP 0x09 +#define CNW_CMD_SSK 0x0a +#define CNW_CMD_SMD 0x0b +#define CNW_CMD_SAPD 0x0c +#define CNW_CMD_SSS 0x11 +#define CNW_CMD_EOC 0x00 /* End-of-command marker */ + |