diff options
Diffstat (limited to 'sys/dev/wl/if_wl.c')
-rw-r--r-- | sys/dev/wl/if_wl.c | 2620 |
1 files changed, 0 insertions, 2620 deletions
diff --git a/sys/dev/wl/if_wl.c b/sys/dev/wl/if_wl.c deleted file mode 100644 index 08d0fc87c771..000000000000 --- a/sys/dev/wl/if_wl.c +++ /dev/null @@ -1,2620 +0,0 @@ -/*- - * 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 all copyright - * notices, this list of conditions and the following disclaimer. - * 2. The names of the authors may not be used to endorse or promote products - * derived from this software without specific prior written permission - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``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 AUTHORS 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. - * - */ -/* - * if_wl.c - original MACH, then BSDI ISA wavelan driver - * ported to mach by Anders Klemets - * to BSDI by Robert Morris - * to FreeBSD by Jim Binkley - * to FreeBSD 2.2+ by Michael Smith - * - * 2.2 update: - * Changed interface to match 2.1-2.2 differences. - * Implement IRQ selection logic in wlprobe() - * Implement PSA updating. - * Pruned heading comments for relevance. - * Ripped out all the 'interface counters' cruft. - * Cut the missing-interrupt timer back to 100ms. - * 2.2.1 update: - * now supports all multicast mode (mrouted will work), - * but unfortunately must do that by going into promiscuous mode - * NWID sysctl added so that normally promiscuous mode is NWID-specific - * but can be made NWID-inspecific - * 7/14/97 jrb - * - * Work done: - * Ported to FreeBSD, got promiscuous mode working with bpfs, - * and rewired timer routine. The i82586 will hang occasionally on output - * and the watchdog timer will kick it if so and log an entry. - * 2 second timeout there. Apparently the chip loses an interrupt. - * Code borrowed from if_ie.c for watchdog timer. - * - * The wavelan card is a 2mbit radio modem that emulates ethernet; - * i.e., it uses MAC addresses. This should not be a surprise since - * it uses an ethernet controller as a major hw item. - * It can broadcast, unicast or apparently multicast in a base cell - * using an omni-directional antennae that is - * about 800 feet around the base cell barring walls and metal. - * With directional antennae, it can be used point to point over a mile - * or so apparently (haven't tried that). - * - * There are ISA and pcmcia versions (not supported by this code). - * The ISA card has an Intel 82586 lan controller on it. It consists - * of 2 pieces of hw, the lan controller (intel) and a radio-modem. - * The latter has an extra set of controller registers that has nothing - * to do with the i82586 and allows setting and monitoring of radio - * signal strength, etc. There is a nvram area called the PSA that - * contains a number of setup variables including the IRQ and so-called - * NWID or Network ID. The NWID must be set the same for all radio - * cards to communicate (unless you are using the ATT/NCR roaming feature - * with their access points. There is no support for that here. Roaming - * involves a link-layer beacon sent out from the access points. End - * stations monitor the signal strength and only use the strongest - * access point). This driver assumes that the base ISA port, IRQ, - * and NWID are first set in nvram via the dos-side "instconf.exe" utility - * supplied with the card. This driver takes the ISA port from - * the kernel configuration setup, and then determines the IRQ either - * from the kernel config (if an explicit IRQ is set) or from the - * PSA on the card if not. - * The hw also magically just uses the IRQ set in the nvram. - * The NWID is used magically as well by the radio-modem - * to determine which packets to keep or throw out. - * - * sample config: - * - * device wl0 at isa? port 0x300 net irq ? - * - * Ifdefs: - * 1. WLDEBUG. (off) - if turned on enables IFF_DEBUG set via ifconfig debug - * 2. MULTICAST (on) - turned on and works up to and including mrouted - * 3. WLCACHE (off) - define to turn on a signal strength - * (and other metric) cache that is indexed by sender MAC address. - * Apps can read this out to learn the remote signal strength of a - * sender. Note that it has a switch so that it only stores - * broadcast/multicast senders but it could be set to store unicast - * too only. Size is hardwired in if_wl_wavelan.h - * - * one further note: promiscuous mode is a curious thing. In this driver, - * promiscuous mode apparently CAN catch ALL packets and ignore the NWID - * setting. This is probably more useful in a sense (for snoopers) if - * you are interested in all traffic as opposed to if you are interested - * in just your own. There is a driver specific sysctl to turn promiscuous - * from just promiscuous to wildly promiscuous... - * - * This driver also knows how to load the synthesizers in the 2.4 Gz - * ISA Half-card, Product number 847647476 (USA/FCC IEEE Channel set). - * This product consists of a "mothercard" that contains the 82586, - * NVRAM that holds the PSA, and the ISA-buss interface custom ASIC. - * The radio transceiver is a "daughtercard" called the WaveMODEM which - * connects to the mothercard through two single-inline connectors: a - * 20-pin connector provides DC-power and modem signals, and a 3-pin - * connector which exports the antenna connection. The code herein - * loads the receive and transmit synthesizers and the corresponding - * transmitter output power value from an EEPROM controlled through - * additional registers via the MMC. The EEPROM address selected - * are those whose values are preset by the DOS utility programs - * provided with the product, and this provides compatible operation - * with the DOS Packet Driver software. A future modification will - * add the necessary functionality to this driver and to the wlconfig - * utility to completely replace the DOS Configuration Utilities. - * The 2.4 Gz WaveMODEM is described in document number 407-024692/E, - * and is available through Lucent Technologies OEM supply channels. - * --RAB 1997/06/08. - */ - -#define MULTICAST 1 - -/* - * Olivetti PC586 Mach Ethernet driver v1.0 - * Copyright Ing. C. Olivetti & C. S.p.A. 1988, 1989 - * All rights reserved. - * - */ -/* - Copyright 1988, 1989 by Olivetti Advanced Technology Center, Inc., -Cupertino, California. - - All Rights Reserved - - Permission to use, copy, modify, and distribute this software and -its documentation for any purpose and without fee is hereby -granted, provided that the above copyright notice appears in all -copies and that both the copyright notice and this permission notice -appear in supporting documentation, and that the name of Olivetti -not be used in advertising or publicity pertaining to distribution -of the software without specific, written prior permission. - - OLIVETTI DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE -INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, -IN NO EVENT SHALL OLIVETTI BE LIABLE FOR ANY SPECIAL, INDIRECT, OR -CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM -LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT, -NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUR OF OR IN CONNECTION -WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -*/ -/* - Copyright 1988, 1989 by Intel Corporation, Santa Clara, California. - - All Rights Reserved - -Permission to use, copy, modify, and distribute this software and -its documentation for any purpose and without fee is hereby -granted, provided that the above copyright notice appears in all -copies and that both the copyright notice and this permission notice -appear in supporting documentation, and that the name of Intel -not be used in advertising or publicity pertaining to distribution -of the software without specific, written prior permission. - -INTEL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE -INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, -IN NO EVENT SHALL INTEL BE LIABLE FOR ANY SPECIAL, INDIRECT, OR -CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM -LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT, -NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION -WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -*/ - -#include <sys/cdefs.h> -__FBSDID("$FreeBSD$"); - -/* - * NOTE: - * by rvb: - * 1. The best book on the 82586 is: - * LAN Components User's Manual by Intel - * The copy I found was dated 1984. This really tells you - * what the state machines are doing - * 2. In the current design, we only do one write at a time, - * though the hardware is capable of chaining and possibly - * even batching. The problem is that we only make one - * transmit buffer available in sram space. - */ - -#include "opt_wavelan.h" -#include "opt_inet.h" - -#include <sys/param.h> -#include <sys/systm.h> -#include <sys/kernel.h> -#include <sys/module.h> -#include <sys/sockio.h> -#include <sys/mbuf.h> -#include <sys/priv.h> -#include <sys/socket.h> -#include <sys/syslog.h> -#include <machine/bus.h> -#include <machine/resource.h> -#include <sys/bus.h> -#include <sys/rman.h> - -#include <sys/sysctl.h> - -#include <net/ethernet.h> -#include <net/if.h> -#include <net/if_var.h> -#include <net/if_arp.h> -#include <net/if_dl.h> -#include <net/if_types.h> - -#ifdef INET -#include <netinet/in.h> -#include <netinet/in_systm.h> -#include <netinet/ip.h> -#include <netinet/if_ether.h> -#endif - -#include <net/bpf.h> -#include <isa/isavar.h> - -/* was 1000 in original, fed to DELAY(x) */ -#define DELAYCONST 1000 -#include <dev/wl/if_wl_i82586.h> /* Definitions for the Intel chip */ -#include <dev/wl/if_wl.h> -#include <machine/if_wl_wavelan.h> - -static char t_packet[ETHERMTU + sizeof(struct ether_header) + sizeof(long)]; - -struct wl_softc { - device_t dev; - struct ifnet *ifp; - u_char psa[0x40]; - u_char nwid[2]; /* current radio modem nwid */ - int flags; - int tbusy; /* flag to determine if xmit is busy */ - u_short begin_fd; - u_short end_fd; - u_short end_rbd; - u_short hacr; /* latest host adapter CR command */ - short mode; - u_char chan24; /* 2.4 Gz: channel number/EEPROM Area # */ - u_short freq24; /* 2.4 Gz: resulting frequency */ - int rid_ioport; - int rid_irq; - struct resource *res_ioport; - struct resource *res_irq; - void *intr_cookie; - struct mtx wl_mtx; - struct callout watchdog_timer; -#ifdef WLCACHE - int w_sigitems; /* number of cached entries */ - /* array of cache entries */ - struct w_sigcache w_sigcache[ MAXCACHEITEMS ]; - int w_nextcache; /* next free cache entry */ - int w_wrapindex; /* next "free" cache entry */ -#endif -}; - -#define WL_LOCK(_sc) mtx_lock(&(_sc)->wl_mtx) -#define WL_LOCK_ASSERT(_sc) mtx_assert(&(_sc)->wl_mtx, MA_OWNED) -#define WL_UNLOCK(_sc) mtx_unlock(&(_sc)->wl_mtx) - -static int wlprobe(device_t); -static int wlattach(device_t); -static int wldetach(device_t); - -static device_method_t wl_methods[] = { - DEVMETHOD(device_probe, wlprobe), - DEVMETHOD(device_attach, wlattach), - DEVMETHOD(device_detach, wldetach), - { 0, 0} -}; - -static driver_t wl_driver = { - "wl", - wl_methods, - sizeof (struct wl_softc) -}; - -devclass_t wl_devclass; -DRIVER_MODULE(wl, isa, wl_driver, wl_devclass, 0, 0); -MODULE_DEPEND(wl, isa, 1, 1, 1); -MODULE_DEPEND(wl, ether, 1, 1, 1); - -static struct isa_pnp_id wl_ids[] = { - {0, NULL} -}; - -/* - * XXX The Wavelan appears to be prone to dropping stuff if you talk to - * it too fast. This disgusting hack inserts a delay after each packet - * is queued which helps avoid this behaviour on fast systems. - */ -static int wl_xmit_delay = 250; -SYSCTL_INT(_machdep, OID_AUTO, wl_xmit_delay, CTLFLAG_RW, &wl_xmit_delay, 0, ""); - -/* - * not XXX, but ZZZ (bizarre). - * promiscuous mode can be toggled to ignore NWIDs. By default, - * it does not. Caution should be exercised about combining - * this mode with IFF_ALLMULTI which puts this driver in - * promiscuous mode. - */ -static int wl_ignore_nwid = 0; -SYSCTL_INT(_machdep, OID_AUTO, wl_ignore_nwid, CTLFLAG_RW, &wl_ignore_nwid, 0, ""); - -/* - * Emit diagnostics about transmission problems - */ -static int xmt_watch = 0; -SYSCTL_INT(_machdep, OID_AUTO, wl_xmit_watch, CTLFLAG_RW, &xmt_watch, 0, ""); - -/* - * Collect SNR statistics - */ -static int gathersnr = 0; -SYSCTL_INT(_machdep, OID_AUTO, wl_gather_snr, CTLFLAG_RW, &gathersnr, 0, ""); - -static int wl_allocate_resources(device_t device); -static int wl_deallocate_resources(device_t device); -static void wlstart(struct ifnet *ifp); -static void wlstart_locked(struct ifnet *ifp); -static void wlinit(void *xsc); -static void wlinit_locked(struct wl_softc *sc); -static int wlioctl(struct ifnet *ifp, u_long cmd, caddr_t data); -static void wlwatchdog(void *arg); -static void wlintr(void *arg); -static void wlxmt(struct wl_softc *sc, struct mbuf *m); -static int wldiag(struct wl_softc *sc); -static int wlconfig(struct wl_softc *sc); -static int wlcmd(struct wl_softc *sc, char *str); -static void wlmmcstat(struct wl_softc *sc); -static u_short wlbldru(struct wl_softc *sc); -static u_short wlmmcread(struct wl_softc *sc, u_short reg); -static void wlinitmmc(struct wl_softc *sc); -static int wlhwrst(struct wl_softc *sc); -static void wlrustrt(struct wl_softc *sc); -static void wlbldcu(struct wl_softc *sc); -static int wlack(struct wl_softc *sc); -static int wlread(struct wl_softc *sc, u_short fd_p); -static void getsnr(struct wl_softc *sc); -static void wlrcv(struct wl_softc *sc); -static int wlrequeue(struct wl_softc *sc, u_short fd_p); -static void wlsftwsleaze(u_short *countp, u_char **mb_pp, struct mbuf **tm_pp, struct wl_softc *sc); -static void wlhdwsleaze(u_short *countp, u_char **mb_pp, struct mbuf **tm_pp, struct wl_softc *sc); -#ifdef WLDEBUG -static void wltbd(struct wl_softc *sc); -#endif -static void wlgetpsa(struct wl_softc *sc, u_char *buf); -static void wlsetpsa(struct wl_softc *sc); -static u_short wlpsacrc(u_char *buf); -static void wldump(struct wl_softc *sc); -#ifdef WLCACHE -static void wl_cache_store(struct wl_softc *, struct ether_header *, struct mbuf *); -static void wl_cache_zero(struct wl_softc *sc); -#endif - -/* array for maping irq numbers to values for the irq parameter register */ -static int irqvals[16] = { - 0, 0, 0, 0x01, 0x02, 0x04, 0, 0x08, 0, 0, 0x10, 0x20, 0x40, 0, 0, 0x80 -}; - -/* - * wlprobe: - * - * This function "probes" or checks for the WaveLAN board on the bus to - * see if it is there. As far as I can tell, the best break between this - * routine and the attach code is to simply determine whether the board - * is configured in properly. Currently my approach to this is to write - * and read a word from the SRAM on the board being probed. If the word - * comes back properly then we assume the board is there. The config - * code expects to see a successful return from the probe routine before - * attach will be called. - * - * input : address device is mapped to, and unit # being checked - * output : a '1' is returned if the board exists, and a 0 otherwise - * - */ -static int -wlprobe(device_t device) -{ - struct wl_softc *sc; - char *str = "wl%d: board out of range [0..%d]\n"; - u_char inbuf[100]; - rman_res_t junk, sirq; - int error, irq; - - error = ISA_PNP_PROBE(device_get_parent(device), device, wl_ids); - if (error == ENXIO || error == 0) - return (error); - - sc = device_get_softc(device); - error = wl_allocate_resources(device); - if (error) - goto errexit; - - /* TBD. not true. - * regular CMD() will not work, since no softc yet - */ -#define PCMD(sc, hacr) WL_WRITE_2((sc), HACR, (hacr)) - - PCMD(sc, HACR_RESET); /* reset the board */ - DELAY(DELAYCONST); /* >> 4 clocks at 6MHz */ - PCMD(sc, HACR_RESET); /* reset the board */ - DELAY(DELAYCONST); /* >> 4 clocks at 6MHz */ - - /* clear reset command and set PIO#1 in autoincrement mode */ - PCMD(sc, HACR_DEFAULT); - PCMD(sc, HACR_DEFAULT); - WL_WRITE_2(sc, PIOR1, 0); /* go to beginning of RAM */ - WL_WRITE_MULTI_2(sc, PIOP1, str, strlen(str)/2+1); /* write string */ - - WL_WRITE_2(sc, PIOR1, 0); /* rewind */ - WL_READ_MULTI_2(sc, PIOP1, inbuf, strlen(str)/2+1); /* read result */ - - if (bcmp(str, inbuf, strlen(str))) { - error = ENXIO; - goto errexit; - } - - sc->chan24 = 0; /* 2.4 Gz: config channel */ - sc->freq24 = 0; /* 2.4 Gz: frequency */ - - /* read the PSA from the board into temporary storage */ - wlgetpsa(sc, inbuf); - - /* We read the IRQ value from the PSA on the board. */ - for (irq = 15; irq >= 0; irq--) - if (irqvals[irq] == inbuf[WLPSA_IRQNO]) - break; - if ((irq == 0) || (irqvals[irq] == 0)){ - device_printf(device, "PSA corrupt (invalid IRQ value)\n"); - } else { - /* - * If the IRQ requested by the PSA is already claimed by another - * device, the board won't work, but the user can still access the - * driver to change the IRQ. - */ - if (bus_get_resource(device, SYS_RES_IRQ, 0, &sirq, &junk)) - goto errexit; - if (irq != (int)sirq) - device_printf(device, "board is configured for interrupt %d\n", - irq); - } - wl_deallocate_resources(device); - return (0); - -errexit: - wl_deallocate_resources(device); - return (error); -} - -/* - * wlattach: - * - * This function attaches a WaveLAN board to the "system". The rest of - * runtime structures are initialized here (this routine is called after - * a successful probe of the board). Once the ethernet address is read - * and stored, the board's ifnet structure is attached and readied. - * - * input : isa_dev structure setup in autoconfig - * output : board structs and ifnet is setup - * - */ -static int -wlattach(device_t device) -{ - struct wl_softc *sc; - int error, i, j; - struct ifnet *ifp; - u_char eaddr[6]; - - sc = device_get_softc(device); - sc->dev = device; - ifp = sc->ifp = if_alloc(IFT_ETHER); - if (ifp == NULL) { - device_printf(device, "can not if_alloc()\n"); - return (ENOSPC); - } - - mtx_init(&sc->wl_mtx, device_get_nameunit(device), MTX_NETWORK_LOCK, - MTX_DEF); - callout_init_mtx(&sc->watchdog_timer, &sc->wl_mtx, 0); - - error = wl_allocate_resources(device); - if (error) { - wl_deallocate_resources(device); - return (ENXIO); - } - -#ifdef WLDEBUG - printf("wlattach: base %jx, unit %d\n", rman_get_start(sc->res_ioport), - device_get_unit(device)); -#endif - - sc->flags = 0; - sc->mode = 0; - sc->hacr = HACR_RESET; - CMD(sc); /* reset the board */ - DELAY(DELAYCONST); /* >> 4 clocks at 6MHz */ - - /* clear reset command and set PIO#2 in parameter access mode */ - sc->hacr = (HACR_DEFAULT & ~HACR_16BITS); - CMD(sc); - - /* Read the PSA from the board for our later reference */ - wlgetpsa(sc, sc->psa); - - /* fetch NWID */ - sc->nwid[0] = sc->psa[WLPSA_NWID]; - sc->nwid[1] = sc->psa[WLPSA_NWID+1]; - - /* fetch MAC address - decide which one first */ - if (sc->psa[WLPSA_MACSEL] & 1) - j = WLPSA_LOCALMAC; - else - j = WLPSA_UNIMAC; - for (i=0; i < WAVELAN_ADDR_SIZE; ++i) - eaddr[i] = sc->psa[j + i]; - - /* enter normal 16 bit mode operation */ - sc->hacr = HACR_DEFAULT; - CMD(sc); - - wlinitmmc(sc); - WL_WRITE_2(sc, PIOR1, OFFSET_SCB + 8); /* address of scb_crcerrs */ - WL_WRITE_2(sc, PIOP1, 0); /* clear scb_crcerrs */ - WL_WRITE_2(sc, PIOP1, 0); /* clear scb_alnerrs */ - WL_WRITE_2(sc, PIOP1, 0); /* clear scb_rscerrs */ - WL_WRITE_2(sc, PIOP1, 0); /* clear scb_ovrnerrs */ - - ifp->if_softc = sc; - ifp->if_mtu = WAVELAN_MTU; - ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX; -#ifdef WLDEBUG - ifp->if_flags |= IFF_DEBUG; -#endif -#if MULTICAST - ifp->if_flags |= IFF_MULTICAST; -#endif /* MULTICAST */ - if_initname(ifp, device_get_name(device), device_get_unit(device)); - ifp->if_init = wlinit; - ifp->if_start = wlstart; - ifp->if_ioctl = wlioctl; - ifp->if_snd.ifq_maxlen = ifqmaxlen; - /* no entries - ifp->if_done - ifp->if_reset - */ - ether_ifattach(ifp, eaddr); - - if_printf(ifp, "NWID 0x%02x%02x", sc->nwid[0], sc->nwid[1]); - if (sc->freq24) - printf(", Freq %d MHz",sc->freq24); /* 2.4 Gz */ - printf("\n"); /* 2.4 Gz */ - - bus_setup_intr(device, sc->res_irq, INTR_TYPE_NET, NULL, wlintr, sc, &sc->intr_cookie); - - if (bootverbose) - wldump(sc); - return (0); -} - -static int -wldetach(device_t device) -{ - struct wl_softc *sc = device_get_softc(device); - struct ifnet *ifp; - - ifp = sc->ifp; - ether_ifdetach(ifp); - - WL_LOCK(sc); - - /* reset the board */ - sc->hacr = HACR_RESET; - CMD(sc); - sc->hacr = HACR_DEFAULT; - CMD(sc); - callout_stop(&sc->watchdog_timer); - WL_UNLOCK(sc); - callout_drain(&sc->watchdog_timer); - - if (sc->intr_cookie != NULL) { - bus_teardown_intr(device, sc->res_irq, sc->intr_cookie); - sc->intr_cookie = NULL; - } - - wl_deallocate_resources(device); - if_free(ifp); - mtx_destroy(&sc->wl_mtx); - return (0); -} - -static int -wl_allocate_resources(device_t device) -{ - struct wl_softc *sc = device_get_softc(device); - int ports = 16; /* Number of ports */ - - sc->res_ioport = bus_alloc_resource_anywhere(device, SYS_RES_IOPORT, - &sc->rid_ioport, ports, RF_ACTIVE); - if (sc->res_ioport == NULL) - goto errexit; - - sc->res_irq = bus_alloc_resource_any(device, SYS_RES_IRQ, - &sc->rid_irq, RF_SHAREABLE|RF_ACTIVE); - if (sc->res_irq == NULL) - goto errexit; - return (0); - -errexit: - wl_deallocate_resources(device); - return (ENXIO); -} - -static int -wl_deallocate_resources(device_t device) -{ - struct wl_softc *sc = device_get_softc(device); - - if (sc->res_irq != 0) { - bus_release_resource(device, SYS_RES_IRQ, - sc->rid_irq, sc->res_irq); - sc->res_irq = 0; - } - if (sc->res_ioport != 0) { - bus_release_resource(device, SYS_RES_IOPORT, - sc->rid_ioport, sc->res_ioport); - sc->res_ioport = 0; - } - return (0); -} - -/* - * Print out interesting information about the 82596. - */ -static void -wldump(struct wl_softc *sc) -{ - int i; - - printf("hasr %04x\n", WL_READ_2(sc, HASR)); - - printf("scb at %04x:\n ", OFFSET_SCB); - WL_WRITE_2(sc, PIOR1, OFFSET_SCB); - for (i = 0; i < 8; i++) - printf("%04x ", WL_READ_2(sc, PIOP1)); - printf("\n"); - - printf("cu at %04x:\n ", OFFSET_CU); - WL_WRITE_2(sc, PIOR1, OFFSET_CU); - for (i = 0; i < 8; i++) - printf("%04x ", WL_READ_2(sc, PIOP1)); - printf("\n"); - - printf("tbd at %04x:\n ", OFFSET_TBD); - WL_WRITE_2(sc, PIOR1, OFFSET_TBD); - for (i = 0; i < 4; i++) - printf("%04x ", WL_READ_2(sc, PIOP1)); - printf("\n"); -} - -/* Initialize the Modem Management Controller */ -static void -wlinitmmc(struct wl_softc *sc) -{ - int configured; - int mode = sc->mode; - int i; /* 2.4 Gz */ - - /* enter 8 bit operation */ - sc->hacr = (HACR_DEFAULT & ~HACR_16BITS); - CMD(sc); - - configured = sc->psa[WLPSA_CONFIGURED] & 1; - - /* - * Set default modem control parameters. Taken from NCR document - * 407-0024326 Rev. A - */ - MMC_WRITE(MMC_JABBER_ENABLE, 0x01); - MMC_WRITE(MMC_ANTEN_SEL, 0x02); - MMC_WRITE(MMC_IFS, 0x20); - MMC_WRITE(MMC_MOD_DELAY, 0x04); - MMC_WRITE(MMC_JAM_TIME, 0x38); - MMC_WRITE(MMC_DECAY_PRM, 0x00); /* obsolete ? */ - MMC_WRITE(MMC_DECAY_UPDAT_PRM, 0x00); - if (!configured) { - MMC_WRITE(MMC_LOOPT_SEL, 0x00); - if (sc->psa[WLPSA_COMPATNO] & 1) { - MMC_WRITE(MMC_THR_PRE_SET, 0x01); /* 0x04 for AT and 0x01 for MCA */ - } else { - MMC_WRITE(MMC_THR_PRE_SET, 0x04); /* 0x04 for AT and 0x01 for MCA */ - } - MMC_WRITE(MMC_QUALITY_THR, 0x03); - } else { - /* use configuration defaults from parameter storage area */ - if (sc->psa[WLPSA_NWIDENABLE] & 1) { - if ((mode & (MOD_PROM | MOD_ENAL)) && wl_ignore_nwid) { - MMC_WRITE(MMC_LOOPT_SEL, 0x40); - } else { - MMC_WRITE(MMC_LOOPT_SEL, 0x00); - } - } else { - MMC_WRITE(MMC_LOOPT_SEL, 0x40); /* disable network id check */ - } - MMC_WRITE(MMC_THR_PRE_SET, sc->psa[WLPSA_THRESH]); - MMC_WRITE(MMC_QUALITY_THR, sc->psa[WLPSA_QUALTHRESH]); - } - MMC_WRITE(MMC_FREEZE, 0x00); - MMC_WRITE(MMC_ENCR_ENABLE, 0x00); - - MMC_WRITE(MMC_NETW_ID_L,sc->nwid[1]); /* set NWID */ - MMC_WRITE(MMC_NETW_ID_H,sc->nwid[0]); - - /* enter normal 16 bit mode operation */ - sc->hacr = HACR_DEFAULT; - CMD(sc); - CMD(sc); /* virtualpc1 needs this! */ - - if (sc->psa[WLPSA_COMPATNO]== /* 2.4 Gz: half-card ver */ - WLPSA_COMPATNO_WL24B) { /* 2.4 Gz */ - i=sc->chan24<<4; /* 2.4 Gz: position ch # */ - MMC_WRITE(MMC_EEADDR,i+0x0f); /* 2.4 Gz: named ch, wc=16 */ - MMC_WRITE(MMC_EECTRL,MMC_EECTRL_DWLD+ /* 2.4 Gz: Download Synths */ - MMC_EECTRL_EEOP_READ); /* 2.4 Gz: Read EEPROM */ - for (i=0; i<1000; ++i) { /* 2.4 Gz: wait for download */ - DELAY(40); /* 2.4 Gz */ - if ((wlmmcread(sc, MMC_EECTRLstat) /* 2.4 Gz: check DWLD and */ - &(MMC_EECTRLstat_DWLD /* 2.4 Gz: EEBUSY */ - +MMC_EECTRLstat_EEBUSY))==0) /* 2.4 Gz: */ - break; /* 2.4 Gz: download finished */ - } /* 2.4 Gz */ - if (i==1000) printf("wl: synth load failed\n"); /* 2.4 Gz */ - MMC_WRITE(MMC_EEADDR,0x61); /* 2.4 Gz: default pwr, wc=2 */ - MMC_WRITE(MMC_EECTRL,MMC_EECTRL_DWLD+ /* 2.4 Gz: Download Xmit Pwr */ - MMC_EECTRL_EEOP_READ); /* 2.4 Gz: Read EEPROM */ - for (i=0; i<1000; ++i) { /* 2.4 Gz: wait for download */ - DELAY(40); /* 2.4 Gz */ - if ((wlmmcread(sc, MMC_EECTRLstat) /* 2.4 Gz: check DWLD and */ - &(MMC_EECTRLstat_DWLD /* 2.4 Gz: EEBUSY */ - +MMC_EECTRLstat_EEBUSY))==0) /* 2.4 Gz: */ - break; /* 2.4 Gz: download finished */ - } /* 2.4 Gz */ - if (i==1000) printf("wl: xmit pwr load failed\n"); /* 2.4 Gz */ - MMC_WRITE(MMC_ANALCTRL, /* 2.4 Gz: EXT ant+polarity */ - MMC_ANALCTRL_ANTPOL + /* 2.4 Gz: */ - MMC_ANALCTRL_EXTANT); /* 2.4 Gz: */ - i=sc->chan24<<4; /* 2.4 Gz: position ch # */ - MMC_WRITE(MMC_EEADDR,i); /* 2.4 Gz: get frequency */ - MMC_WRITE(MMC_EECTRL, /* 2.4 Gz: EEPROM read */ - MMC_EECTRL_EEOP_READ); /* 2.4 Gz: */ - DELAY(40); /* 2.4 Gz */ - i = wlmmcread(sc, MMC_EEDATALrv) /* 2.4 Gz: freq val */ - + (wlmmcread(sc, MMC_EEDATAHrv)<<8); /* 2.4 Gz */ - sc->freq24 = (i>>6)+2400; /* 2.4 Gz: save real freq */ - } -} - -/* - * wlinit: - * - * Another routine that interfaces the "if" layer to this driver. - * Simply resets the structures that are used by "upper layers". - * As well as calling wlhwrst that does reset the WaveLAN board. - * - * input : softc pointer for this interface - * output : structures (if structs) and board are reset - * - */ -static void -wlinit(void *xsc) -{ - struct wl_softc *sc = xsc; - - WL_LOCK(sc); - wlinit_locked(sc); - WL_UNLOCK(sc); -} - -static void -wlinit_locked(struct wl_softc *sc) -{ - struct ifnet *ifp = sc->ifp; - int stat; - -#ifdef WLDEBUG - if (sc->ifp->if_flags & IFF_DEBUG) - if_printf(ifp, "entered wlinit()\n"); -#endif - WL_LOCK_ASSERT(sc); - if ((stat = wlhwrst(sc)) == TRUE) { - sc->ifp->if_drv_flags |= IFF_DRV_RUNNING; /* same as DSF_RUNNING */ - /* - * OACTIVE is used by upper-level routines - * and must be set - */ - sc->ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; /* same as tbusy below */ - - sc->flags |= DSF_RUNNING; - sc->tbusy = 0; - callout_stop(&sc->watchdog_timer); - - wlstart_locked(ifp); - } else { - if_printf(ifp, "init(): trouble resetting board.\n"); - } -} - -/* - * wlhwrst: - * - * This routine resets the WaveLAN board that corresponds to the - * board number passed in. - * - * input : board number to do a hardware reset - * output : board is reset - * - */ -static int -wlhwrst(struct wl_softc *sc) -{ - -#ifdef WLDEBUG - if (sc->ifp->if_flags & IFF_DEBUG) - if_printf(sc->ifp, "entered wlhwrst()\n"); -#endif - sc->hacr = HACR_RESET; - CMD(sc); /* reset the board */ - - /* clear reset command and set PIO#1 in autoincrement mode */ - sc->hacr = HACR_DEFAULT; - CMD(sc); - -#ifdef WLDEBUG - if (sc->ifp->if_flags & IFF_DEBUG) - wlmmcstat(sc); /* Display MMC registers */ -#endif /* WLDEBUG */ - wlbldcu(sc); /* set up command unit structures */ - - if (wldiag(sc) == 0) - return(0); - - if (wlconfig(sc) == 0) - return(0); - /* - * insert code for loopback test here - */ - wlrustrt(sc); /* start receive unit */ - - /* enable interrupts */ - sc->hacr = (HACR_DEFAULT | HACR_INTRON); - CMD(sc); - - return(1); -} - -/* - * wlbldcu: - * - * This function builds up the command unit structures. It inits - * the scp, iscp, scb, cb, tbd, and tbuf. - * - */ -static void -wlbldcu(struct wl_softc *sc) -{ - scp_t scp; - iscp_t iscp; - scb_t scb; - ac_t cb; - tbd_t tbd; - int i; - - bzero(&scp, sizeof(scp)); - scp.scp_sysbus = 0; - scp.scp_iscp = OFFSET_ISCP; - scp.scp_iscp_base = 0; - WL_WRITE_2(sc, PIOR1, OFFSET_SCP); - WL_WRITE_MULTI_2(sc, PIOP1, &scp, sizeof(scp_t)/2); - - bzero(&iscp, sizeof(iscp)); - iscp.iscp_busy = 1; - iscp.iscp_scb_offset = OFFSET_SCB; - iscp.iscp_scb = 0; - iscp.iscp_scb_base = 0; - WL_WRITE_2(sc, PIOR1, OFFSET_ISCP); - WL_WRITE_MULTI_2(sc, PIOP1, &iscp, sizeof(iscp_t)/2); - - scb.scb_status = 0; - scb.scb_command = SCB_RESET; - scb.scb_cbl_offset = OFFSET_CU; - scb.scb_rfa_offset = OFFSET_RU; - scb.scb_crcerrs = 0; - scb.scb_alnerrs = 0; - scb.scb_rscerrs = 0; - scb.scb_ovrnerrs = 0; - WL_WRITE_2(sc, PIOR1, OFFSET_SCB); - WL_WRITE_MULTI_2(sc, PIOP1, &scb, sizeof(scb_t)/2); - - SET_CHAN_ATTN(sc); - - WL_WRITE_2(sc, PIOR0, OFFSET_ISCP + 0); /* address of iscp_busy */ - for (i = 1000000; WL_READ_2(sc, PIOP0) && (i-- > 0); ) - continue; - if (i <= 0) - device_printf(sc->dev, "bldcu(): iscp_busy timeout.\n"); - WL_WRITE_2(sc, PIOR0, OFFSET_SCB + 0); /* address of scb_status */ - for (i = STATUS_TRIES; i-- > 0; ) { - if (WL_READ_2(sc, PIOP0) == (SCB_SW_CX|SCB_SW_CNA)) - break; - } - if (i <= 0) - device_printf(sc->dev, "bldcu(): not ready after reset.\n"); - wlack(sc); - - cb.ac_status = 0; - cb.ac_command = AC_CW_EL; /* NOP */ - cb.ac_link_offset = OFFSET_CU; - WL_WRITE_2(sc, PIOR1, OFFSET_CU); - WL_WRITE_MULTI_2(sc, PIOP1, &cb, 6/2); - - tbd.act_count = 0; - tbd.next_tbd_offset = I82586NULL; - tbd.buffer_addr = 0; - tbd.buffer_base = 0; - WL_WRITE_2(sc, PIOR1, OFFSET_TBD); - WL_WRITE_MULTI_2(sc, PIOP1, &tbd, sizeof(tbd_t)/2); -} - -/* - * wlstart: - * - * send a packet - * - * input : board number - * output : stuff sent to board if any there - * - */ -static void -wlstart(struct ifnet *ifp) -{ - struct wl_softc *sc = ifp->if_softc; - - WL_LOCK(sc); - wlstart_locked(ifp); - WL_UNLOCK(sc); -} - -static void -wlstart_locked(struct ifnet *ifp) -{ - struct mbuf *m; - struct wl_softc *sc = ifp->if_softc; - int scb_status, cu_status, scb_command; - - WL_LOCK_ASSERT(sc); -#ifdef WLDEBUG - if (sc->ifp->if_flags & IFF_DEBUG) - if_printf(ifp, "entered wlstart()\n"); -#endif - - WL_WRITE_2(sc, PIOR1, OFFSET_CU); - cu_status = WL_READ_2(sc, PIOP1); - WL_WRITE_2(sc, PIOR0,OFFSET_SCB + 0); /* scb_status */ - scb_status = WL_READ_2(sc, PIOP0); - WL_WRITE_2(sc, PIOR0, OFFSET_SCB + 2); - scb_command = WL_READ_2(sc, PIOP0); - - /* - * don't need OACTIVE check as tbusy here checks to see - * if we are already busy - */ - if (sc->tbusy) { - if ((scb_status & 0x0700) == SCB_CUS_IDLE && - (cu_status & AC_SW_B) == 0){ - sc->tbusy = 0; - callout_stop(&sc->watchdog_timer); - sc->ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; - /* - * This is probably just a race. The xmt'r is just - * became idle but WE have masked interrupts so ... - */ -#ifdef WLDEBUG - if_printf(ifp, "CU idle, scb %04x %04x cu %04x\n", - scb_status, scb_command, cu_status); -#endif - if (xmt_watch) printf("!!"); - } else { - return; /* genuinely still busy */ - } - } else if ((scb_status & 0x0700) == SCB_CUS_ACTV || - (cu_status & AC_SW_B)){ -#ifdef WLDEBUG - if_printf(ifp, "CU unexpectedly busy; scb %04x cu %04x\n", - scb_status, cu_status); -#endif - if (xmt_watch) - if_printf(ifp, "busy?!\n"); - return; /* hey, why are we busy? */ - } - - /* get ourselves some data */ - IF_DEQUEUE(&ifp->if_snd, m); - if (m != NULL) { - /* let BPF see it before we commit it */ - BPF_MTAP(ifp, m); - sc->tbusy++; - /* set the watchdog timer so that if the board - * fails to interrupt we will restart - */ - /* try 10 ms, not very long */ - callout_reset(&sc->watchdog_timer, hz / 100, wlwatchdog, sc); - sc->ifp->if_drv_flags |= IFF_DRV_OACTIVE; - if_inc_counter(sc->ifp, IFCOUNTER_OPACKETS, 1); - wlxmt(sc, m); - } else { - sc->ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; - } - return; -} - -/* - * wlread: - * - * This routine does the actual copy of data (including ethernet header - * structure) from the WaveLAN to an mbuf chain that will be passed up - * to the "if" (network interface) layer. NOTE: we currently - * don't handle trailer protocols, so if that is needed, it will - * (at least in part) be added here. For simplicities sake, this - * routine copies the receive buffers from the board into a local (stack) - * buffer until the frame has been copied from the board. Once in - * the local buffer, the contents are copied to an mbuf chain that - * is then enqueued onto the appropriate "if" queue. - * - * input : board number, and a frame descriptor address - * output : the packet is put into an mbuf chain, and passed up - * assumes : if any errors occur, packet is "dropped on the floor" - * - */ -static int -wlread(struct wl_softc *sc, u_short fd_p) -{ - struct ifnet *ifp = sc->ifp; - fd_t fd; - struct ether_header *eh; - struct mbuf *m; - rbd_t rbd; - u_char *mb_p; - u_short mlen, len; - u_short bytes_in_msg, bytes_in_mbuf, bytes; - - WL_LOCK_ASSERT(sc); - -#ifdef WLDEBUG - if (sc->ifp->if_flags & IFF_DEBUG) - if_printf(ifp, "entered wlread()\n"); -#endif - if (!((ifp->if_flags & IFF_UP) && (ifp->if_drv_flags & IFF_DRV_RUNNING))) { - if_printf(ifp, "read(): board is not running.\n"); - sc->hacr &= ~HACR_INTRON; - CMD(sc); /* turn off interrupts */ - } - - /* - * Collect message size. - */ - WL_WRITE_2(sc, PIOR1, fd_p); - WL_READ_MULTI_2(sc, PIOP1, &fd, sizeof(fd_t)/2); - if (fd.rbd_offset == I82586NULL) { - if (wlhwrst(sc) != TRUE) { - sc->hacr &= ~HACR_INTRON; - CMD(sc); /* turn off interrupts */ - if_printf(ifp, "read(): hwrst trouble.\n"); - } - return 0; - } - - WL_WRITE_2(sc, PIOR1, fd.rbd_offset); - WL_READ_MULTI_2(sc, PIOP1, &rbd, sizeof(rbd_t)/2); - bytes_in_msg = rbd.status & RBD_SW_COUNT; - - /* - * Allocate a cluster'd mbuf to receive the packet. - */ - m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR); - if (m == NULL) { - if (wlhwrst(sc) != TRUE) { - sc->hacr &= ~HACR_INTRON; - CMD(sc); /* turn off interrupts */ - if_printf(ifp, "read(): hwrst trouble.\n"); - } - return 0; - } - m->m_pkthdr.len = m->m_len = MCLBYTES; - m_adj(m, ETHER_ALIGN); /* align IP header */ - - /* - * Collect the message data. - */ - mlen = 0; - mb_p = mtod(m, u_char *); - bytes_in_mbuf = m->m_len; - - /* Put the ethernet header inside the mbuf. */ - bcopy(&fd.destination[0], mb_p, 14); - mb_p += 14; - mlen += 14; - bytes_in_mbuf -= 14; - - bytes = min(bytes_in_mbuf, bytes_in_msg); - for (;;) { - if (bytes & 1) { - len = bytes + 1; - } else { - len = bytes; - } - WL_WRITE_2(sc, PIOR1, rbd.buffer_addr); - WL_READ_MULTI_2(sc, PIOP1, mb_p, len/2); - mlen += bytes; - - if (bytes > bytes_in_mbuf) { - /* XXX something wrong, a packet should fit in 1 cluster */ - m_freem(m); - if_printf(ifp, "read(): packet too large (%u > %u)\n", - bytes, bytes_in_mbuf); - if (wlhwrst(sc) != TRUE) { - sc->hacr &= ~HACR_INTRON; - CMD(sc); /* turn off interrupts */ - if_printf(ifp, "read(): hwrst trouble.\n"); - } - return 0; - } - mb_p += bytes; - bytes_in_mbuf -= bytes; - bytes_in_msg -= bytes; - if (bytes_in_msg == 0) { - if (rbd.status & RBD_SW_EOF || rbd.next_rbd_offset == I82586NULL) { - break; - } - WL_WRITE_2(sc, PIOR1, rbd.next_rbd_offset); - WL_READ_MULTI_2(sc, PIOP1, &rbd, sizeof(rbd_t)/2); - bytes_in_msg = rbd.status & RBD_SW_COUNT; - } else { - rbd.buffer_addr += bytes; - } - - bytes = min(bytes_in_mbuf, bytes_in_msg); - } - - m->m_pkthdr.len = m->m_len = mlen; - m->m_pkthdr.rcvif = ifp; - - /* - * If hw is in promiscuous mode (note that I said hardware, not if - * IFF_PROMISC is set in ifnet flags), then if this is a unicast - * packet and the MAC dst is not us, drop it. This check in normally - * inside ether_input(), but IFF_MULTI causes hw promisc without - * a bpf listener, so this is wrong. - * Greg Troxel <gdt@ir.bbn.com>, 1998-08-07 - */ - /* - * TBD: also discard packets where NWID does not match. - * However, there does not appear to be a way to read the nwid - * for a received packet. -gdt 1998-08-07 - */ - /* XXX verify mbuf length */ - eh = mtod(m, struct ether_header *); - if ( -#ifdef WL_USE_IFNET_PROMISC_CHECK /* not defined */ - (sc->ifp->if_flags & (IFF_PROMISC|IFF_ALLMULTI)) -#else - /* hw is in promisc mode if this is true */ - (sc->mode & (MOD_PROM | MOD_ENAL)) -#endif - && - (eh->ether_dhost[0] & 1) == 0 && /* !mcast and !bcast */ - bcmp(eh->ether_dhost, IF_LLADDR(sc->ifp), - sizeof(eh->ether_dhost)) != 0 ) { - m_freem(m); - return 1; - } - -#ifdef WLDEBUG - if (sc->ifp->if_flags & IFF_DEBUG) - if_printf(ifp, "wlrecv %u bytes\n", mlen); -#endif - -#ifdef WLCACHE - wl_cache_store(sc, eh, m); -#endif - - /* - * received packet is now in a chain of mbuf's. next step is - * to pass the packet upwards. - */ - WL_UNLOCK(sc); - (*ifp->if_input)(ifp, m); - WL_LOCK(sc); - return 1; -} - -/* - * wlioctl: - * - * This routine processes an ioctl request from the "if" layer - * above. - * - * input : pointer the appropriate "if" struct, command, and data - * output : based on command appropriate action is taken on the - * WaveLAN board(s) or related structures - * return : error is returned containing exit conditions - * - */ -static int -wlioctl(struct ifnet *ifp, u_long cmd, caddr_t data) -{ - struct ifreq *ifr = (struct ifreq *)data; - struct wl_softc *sc = ifp->if_softc; - short mode = 0; - int error = 0; - struct thread *td = curthread; /* XXX */ - int irq, irqval, i, isroot; - char psa_buf[0x40]; - char eeprom_buf[0x80]; -#ifdef WLCACHE - size_t size; - char * cpt; -#endif - -#ifdef WLDEBUG - if (sc->ifp->if_flags & IFF_DEBUG) - if_printf(ifp, "entered wlioctl()\n"); -#endif - switch (cmd) { - case SIOCSIFFLAGS: - WL_LOCK(sc); - if (ifp->if_flags & IFF_ALLMULTI) { - mode |= MOD_ENAL; - } - if (ifp->if_flags & IFF_PROMISC) { - mode |= MOD_PROM; - } - if (ifp->if_flags & IFF_LINK0) { - mode |= MOD_PROM; - } - /* - * force a complete reset if the receive multicast/ - * promiscuous mode changes so that these take - * effect immediately. - * - */ - if (sc->mode != mode) { - sc->mode = mode; - if (sc->flags & DSF_RUNNING) { - sc->flags &= ~DSF_RUNNING; - wlinit_locked(sc); - } - } - /* if interface is marked DOWN and still running then - * stop it. - */ - if ((ifp->if_flags & IFF_UP) == 0 && sc->flags & DSF_RUNNING) { - if_printf(ifp, "ioctl(): board is not running\n"); - sc->flags &= ~DSF_RUNNING; - sc->hacr &= ~HACR_INTRON; - CMD(sc); /* turn off interrupts */ - } - /* else if interface is UP and RUNNING, start it - */ - else if (ifp->if_flags & IFF_UP && (sc->flags & DSF_RUNNING) == 0) { - wlinit_locked(sc); - } - - /* if WLDEBUG set on interface, then printf rf-modem regs - */ - if (ifp->if_flags & IFF_DEBUG) - wlmmcstat(sc); - WL_UNLOCK(sc); - break; -#if MULTICAST - case SIOCADDMULTI: - case SIOCDELMULTI: - - wlinit(sc); - break; -#endif /* MULTICAST */ - - /* DEVICE SPECIFIC */ - - - /* copy the PSA out to the caller */ - case SIOCGWLPSA: - /* work out if they're root */ - isroot = (priv_check(td, PRIV_NET80211_GETKEY) == 0); - - bzero(psa_buf, sizeof(psa_buf)); - WL_LOCK(sc); - for (i = 0; i < 0x40; i++) { - /* don't hand the DES key out to non-root users */ - if ((i > WLPSA_DESKEY) && (i < (WLPSA_DESKEY + 8)) && !isroot) - continue; - psa_buf[i] = sc->psa[i]; - } - WL_UNLOCK(sc); - - error = copyout(psa_buf, ifr->ifr_data, sizeof(psa_buf)); - break; - - - /* copy the PSA in from the caller; we only copy _some_ values */ - case SIOCSWLPSA: - /* root only */ - if ((error = priv_check(td, PRIV_DRIVER))) - break; - - error = copyin(ifr->ifr_data, psa_buf, sizeof(psa_buf)); - if (error) - break; - - /* check IRQ value */ - irqval = psa_buf[WLPSA_IRQNO]; - for (irq = 15; irq >= 0; irq--) - if (irqvals[irq] == irqval) - break; - if (irq == 0) /* oops */ - break; - WL_LOCK(sc); - /* new IRQ */ - sc->psa[WLPSA_IRQNO] = irqval; - - /* local MAC */ - for (i = 0; i < 6; i++) - sc->psa[WLPSA_LOCALMAC + i] = psa_buf[WLPSA_LOCALMAC + i]; - - /* MAC select */ - sc->psa[WLPSA_MACSEL] = psa_buf[WLPSA_MACSEL]; - - /* default nwid */ - sc->psa[WLPSA_NWID] = psa_buf[WLPSA_NWID]; - sc->psa[WLPSA_NWID + 1] = psa_buf[WLPSA_NWID + 1]; - - wlsetpsa(sc); /* update the PSA */ - WL_UNLOCK(sc); - break; - - - /* get the current NWID out of the sc since we stored it there */ - case SIOCGWLCNWID: - WL_LOCK(sc); - ifr->ifr_data = (caddr_t) (sc->nwid[0] << 8 | sc->nwid[1]); - WL_UNLOCK(sc); - break; - - - /* - * change the nwid dynamically. This - * ONLY changes the radio modem and does not - * change the PSA. - * - * 2 steps: - * 1. save in softc "soft registers" - * 2. save in radio modem (MMC) - */ - case SIOCSWLCNWID: - /* root only */ - if ((error = priv_check(td, PRIV_DRIVER))) - break; - WL_LOCK(sc); - if (!(ifp->if_flags & IFF_UP)) { - error = EIO; /* only allowed while up */ - } else { - /* - * soft c nwid shadows radio modem setting - */ - sc->nwid[0] = (int)ifr->ifr_data >> 8; - sc->nwid[1] = (int)ifr->ifr_data & 0xff; - MMC_WRITE(MMC_NETW_ID_L,sc->nwid[1]); - MMC_WRITE(MMC_NETW_ID_H,sc->nwid[0]); - } - WL_UNLOCK(sc); - break; - - /* copy the EEPROM in 2.4 Gz WaveMODEM out to the caller */ - case SIOCGWLEEPROM: - /* root only */ - if ((error = priv_check(td, PRIV_DRIVER))) - break; - - bzero(eeprom_buf, sizeof(eeprom_buf)); - WL_LOCK(sc); - for (i=0x00; i<0x80; ++i) { /* 2.4 Gz: size of EEPROM */ - MMC_WRITE(MMC_EEADDR,i); /* 2.4 Gz: get frequency */ - MMC_WRITE(MMC_EECTRL, /* 2.4 Gz: EEPROM read */ - MMC_EECTRL_EEOP_READ); /* 2.4 Gz: */ - DELAY(40); /* 2.4 Gz */ - eeprom_buf[2 * i] = /* 2.4 Gz: pass low byte of */ - wlmmcread(sc, MMC_EEDATALrv); /* 2.4 Gz: EEPROM word */ - eeprom_buf[2 * i + 1] = /* 2.4 Gz: pass hi byte of */ - wlmmcread(sc, MMC_EEDATALrv); /* 2.4 Gz: EEPROM word */ - } - WL_UNLOCK(sc); - error = copyout(ifr->ifr_data, eeprom_buf, sizeof(eeprom_buf)); - break; - -#ifdef WLCACHE - /* zero (Delete) the wl cache */ - case SIOCDWLCACHE: - /* root only */ - if ((error = priv_check(td, PRIV_DRIVER))) - break; - WL_LOCK(sc); - wl_cache_zero(sc); - WL_UNLOCK(sc); - break; - - /* read out the number of used cache elements */ - case SIOCGWLCITEM: - WL_LOCK(sc); - ifr->ifr_data = (caddr_t) sc->w_sigitems; - WL_UNLOCK(sc); - break; - - /* read out the wl cache */ - case SIOCGWLCACHE: - WL_LOCK(sc); - size = sc->w_sigitems * sizeof(struct w_sigcache); - cpt = malloc(size, M_DEVBUF, M_NOWAIT | M_ZERO); - if (cpt == NULL) { - WL_UNLOCK(sc); - return (ENOMEM); - } - - bcopy(sc->w_sigcache, cpt, size); - WL_UNLOCK(sc); - - error = copyout(cpt, ifr->ifr_data, size); - free(cpt, M_DEVBUF); - break; -#endif - - default: - error = ether_ioctl(ifp, cmd, data); - break; - } - return (error); -} - -/* - * wlwatchdog(): - * - * Called if the timer set in wlstart expires before an interrupt is received - * from the wavelan. It seems to lose interrupts sometimes. - * The watchdog routine gets called if the transmitter failed to interrupt - * - * input : which board is timing out - * output : board reset - * - */ -static void -wlwatchdog(void *vsc) -{ - struct wl_softc *sc = vsc; - - log(LOG_ERR, "%s: wavelan device timeout on xmit\n", sc->ifp->if_xname); - if_inc_counter(sc->ifp, IFCOUNTER_OERRORS, 1); - wlinit_locked(sc); -} - -/* - * wlintr: - * - * This function is the interrupt handler for the WaveLAN - * board. This routine will be called whenever either a packet - * is received, or a packet has successfully been transferred and - * the unit is ready to transmit another packet. - * - * input : board number that interrupted - * output : either a packet is received, or a packet is transferred - * - */ -static void -wlintr(void *arg) -{ - struct wl_softc *sc = (struct wl_softc *)arg; - int ac_status; - u_short int_type, int_type1; - - WL_LOCK(sc); -#ifdef WLDEBUG - if (sc->ifp->if_flags & IFF_DEBUG) - if_printf(sc->ifp, "wlintr() called\n"); -#endif - - if ((int_type = WL_READ_2(sc, HASR)) & HASR_MMC_INTR) { - /* handle interrupt from the modem management controller */ - /* This will clear the interrupt condition */ - (void) wlmmcread(sc, MMC_DCE_STATUS); /* ignored for now */ - } - - if (!(int_type & HASR_INTR)){ /* return if no interrupt from 82586 */ - /* commented out. jrb. it happens when reinit occurs - printf("wlintr: int_type %x, dump follows\n", int_type); - wldump(sc); - */ - WL_UNLOCK(sc); - return; - } - - if (gathersnr) - getsnr(sc); - for (;;) { - WL_WRITE_2(sc, PIOR0, OFFSET_SCB + 0); /* get scb status */ - int_type = (WL_READ_2(sc, PIOP0) & SCB_SW_INT); - if (int_type == 0) /* no interrupts left */ - break; - - int_type1 = wlack(sc); /* acknowledge interrupt(s) */ - /* make sure no bits disappeared (others may appear) */ - if ((int_type & int_type1) != int_type) - printf("wlack() int bits disappeared : %04x != int_type %04x\n", - int_type1, int_type); - int_type = int_type1; /* go with the new status */ - /* - * incoming packet - */ - if (int_type & SCB_SW_FR) { - if_inc_counter(sc->ifp, IFCOUNTER_IPACKETS, 1); - wlrcv(sc); - } - /* - * receiver not ready - */ - if (int_type & SCB_SW_RNR) { - if_inc_counter(sc->ifp, IFCOUNTER_IERRORS, 1); -#ifdef WLDEBUG - if (sc->ifp->if_flags & IFF_DEBUG) - if_printf(sc->ifp, "intr(): receiver overrun! begin_fd = %x\n", - sc->begin_fd); -#endif - wlrustrt(sc); - } - /* - * CU not ready - */ - if (int_type & SCB_SW_CNA) { - /* - * At present, we don't care about CNA's. We - * believe they are a side effect of XMT. - */ - } - if (int_type & SCB_SW_CX) { - /* - * At present, we only request Interrupt for - * XMT. - */ - WL_WRITE_2(sc, PIOR1, OFFSET_CU); /* get command status */ - ac_status = WL_READ_2(sc, PIOP1); - - if (xmt_watch) { /* report some anomalies */ - - if (sc->tbusy == 0) { - if_printf(sc->ifp, "xmt intr but not busy, CU %04x\n", - ac_status); - } - if (ac_status == 0) { - if_printf(sc->ifp, "xmt intr but ac_status == 0\n"); - } - if (ac_status & AC_SW_A) { - if_printf(sc->ifp, "xmt aborted\n"); - } -#ifdef notdef - if (ac_status & TC_CARRIER) { - if_printf(sc->ifp, "no carrier\n"); - } -#endif /* notdef */ - if (ac_status & TC_CLS) { - if_printf(sc->ifp, "no CTS\n"); - } - if (ac_status & TC_DMA) { - if_printf(sc->ifp, "DMA underrun\n"); - } - if (ac_status & TC_DEFER) { - if_printf(sc->ifp, "xmt deferred\n"); - } - if (ac_status & TC_SQE) { - if_printf(sc->ifp, "heart beat\n"); - } - if (ac_status & TC_COLLISION) { - if_printf(sc->ifp, "too many collisions\n"); - } - } - /* if the transmit actually failed, or returned some status */ - if ((!(ac_status & AC_SW_OK)) || (ac_status & 0xfff)) { - if (ac_status & (TC_COLLISION | TC_CLS | TC_DMA)) { - if_inc_counter(sc->ifp, IFCOUNTER_OERRORS, 1); - } - /* count collisions */ - if_inc_counter(sc->ifp, IFCOUNTER_COLLISIONS, (ac_status & 0xf)); - /* if TC_COLLISION set and collision count zero, 16 collisions */ - if ((ac_status & 0x20) == 0x20) { - if_inc_counter(sc->ifp, IFCOUNTER_COLLISIONS, 0x10); - } - } - sc->tbusy = 0; - callout_stop(&sc->watchdog_timer); - sc->ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; - wlstart_locked(sc->ifp); - } - } - WL_UNLOCK(sc); - return; -} - -/* - * wlrcv: - * - * This routine is called by the interrupt handler to initiate a - * packet transfer from the board to the "if" layer above this - * driver. This routine checks if a buffer has been successfully - * received by the WaveLAN. If so, the routine wlread is called - * to do the actual transfer of the board data (including the - * ethernet header) into a packet (consisting of an mbuf chain). - * - * input : number of the board to check - * output : if a packet is available, it is "sent up" - * - */ -static void -wlrcv(struct wl_softc *sc) -{ - u_short fd_p, status, offset, link_offset; - -#ifdef WLDEBUG - if (sc->ifp->if_flags & IFF_DEBUG) - if_printf(sc->ifp, "entered wlrcv()\n"); -#endif - for (fd_p = sc->begin_fd; fd_p != I82586NULL; fd_p = sc->begin_fd) { - - WL_WRITE_2(sc, PIOR0, fd_p + 0); /* address of status */ - status = WL_READ_2(sc, PIOP0); - WL_WRITE_2(sc, PIOR1, fd_p + 4); /* address of link_offset */ - link_offset = WL_READ_2(sc, PIOP1); - offset = WL_READ_2(sc, PIOP1); /* rbd_offset */ - if (status == 0xffff || offset == 0xffff /*I82586NULL*/) { - if (wlhwrst(sc) != TRUE) - if_printf(sc->ifp, "rcv(): hwrst ffff trouble.\n"); - return; - } else if (status & AC_SW_C) { - if (status == (RFD_DONE|RFD_RSC)) { - /* lost one */ -#ifdef WLDEBUG - if (sc->ifp->if_flags & IFF_DEBUG) - if_printf(sc->ifp, "RCV: RSC %x\n", status); -#endif - if_inc_counter(sc->ifp, IFCOUNTER_IERRORS, 1); - } else if (!(status & RFD_OK)) { - if_printf(sc->ifp, "RCV: !OK %x\n", status); - if_inc_counter(sc->ifp, IFCOUNTER_IERRORS, 1); - } else if (status & 0xfff) { /* can't happen */ - if_printf(sc->ifp, "RCV: ERRs %x\n", status); - if_inc_counter(sc->ifp, IFCOUNTER_IERRORS, 1); - } else if (!wlread(sc, fd_p)) - return; - - if (!wlrequeue(sc, fd_p)) { - /* abort on chain error */ - if (wlhwrst(sc) != TRUE) - if_printf(sc->ifp, "rcv(): hwrst trouble.\n"); - return; - } - sc->begin_fd = link_offset; - } else { - break; - } - } - return; -} - -/* - * wlrequeue: - * - * This routine puts rbd's used in the last receive back onto the - * free list for the next receive. - * - */ -static int -wlrequeue(struct wl_softc *sc, u_short fd_p) -{ - fd_t fd; - u_short l_rbdp, f_rbdp, rbd_offset; - - WL_WRITE_2(sc, PIOR0, fd_p + 6); - rbd_offset = WL_READ_2(sc, PIOP0); - if ((f_rbdp = rbd_offset) != I82586NULL) { - l_rbdp = f_rbdp; - for (;;) { - WL_WRITE_2(sc, PIOR0, l_rbdp + 0); /* address of status */ - if (WL_READ_2(sc, PIOP0) & RBD_SW_EOF) - break; - WL_WRITE_2(sc, PIOP0, 0); - WL_WRITE_2(sc, PIOR0, l_rbdp + 2); /* next_rbd_offset */ - if ((l_rbdp = WL_READ_2(sc, PIOP0)) == I82586NULL) - break; - } - WL_WRITE_2(sc, PIOP0, 0); - WL_WRITE_2(sc, PIOR0, l_rbdp + 2); /* next_rbd_offset */ - WL_WRITE_2(sc, PIOP0, I82586NULL); - WL_WRITE_2(sc, PIOR0, l_rbdp + 8); /* address of size */ - WL_WRITE_2(sc, PIOP0, WL_READ_2(sc, PIOP0) | AC_CW_EL); - WL_WRITE_2(sc, PIOR0, sc->end_rbd + 2); - WL_WRITE_2(sc, PIOP0, f_rbdp); /* end_rbd->next_rbd_offset */ - WL_WRITE_2(sc, PIOR0, sc->end_rbd + 8); /* size */ - WL_WRITE_2(sc, PIOP0, WL_READ_2(sc, PIOP0) & ~AC_CW_EL); - sc->end_rbd = l_rbdp; - } - - fd.status = 0; - fd.command = AC_CW_EL; - fd.link_offset = I82586NULL; - fd.rbd_offset = I82586NULL; - WL_WRITE_2(sc, PIOR1, fd_p); - WL_WRITE_MULTI_2(sc, PIOP1, &fd, 8/2); - - WL_WRITE_2(sc, PIOR1, sc->end_fd + 2); /* addr of command */ - WL_WRITE_2(sc, PIOP1, 0); /* command = 0 */ - WL_WRITE_2(sc, PIOP1, fd_p); /* end_fd->link_offset = fd_p */ - sc->end_fd = fd_p; - - return 1; -} - -#ifdef WLDEBUG -static int xmt_debug = 0; -#endif /* WLDEBUG */ - -/* - * wlxmt: - * - * This routine fills in the appropriate registers and memory - * locations on the WaveLAN board and starts the board off on - * the transmit. - * - * input : pointers to board of interest's softc and the mbuf - * output : board memory and registers are set for xfer and attention - * - */ -static void -wlxmt(struct wl_softc *sc, struct mbuf *m) -{ - u_short xmtdata_p = OFFSET_TBUF; - u_short xmtshort_p; - struct mbuf *tm_p = m; - struct ether_header *eh_p = mtod(m, struct ether_header *); - u_char *mb_p = mtod(m, u_char *) + sizeof(struct ether_header); - u_short count = m->m_len - sizeof(struct ether_header); - ac_t cb; - u_short tbd_p = OFFSET_TBD; - u_short len, clen = 0; - int spin; - -#ifdef WLDEBUG - if (sc->ifp->if_flags & IFF_DEBUG) - if_printf(sc->ifp, "entered wlxmt()\n"); -#endif - - cb.ac_status = 0; - cb.ac_command = (AC_CW_EL|AC_TRANSMIT|AC_CW_I); - cb.ac_link_offset = I82586NULL; - WL_WRITE_2(sc, PIOR1, OFFSET_CU); - WL_WRITE_MULTI_2(sc, PIOP1, &cb, 6/2); - WL_WRITE_2(sc, PIOP1, OFFSET_TBD); /* cb.cmd.transmit.tbd_offset */ - WL_WRITE_MULTI_2(sc, PIOP1, eh_p->ether_dhost, WAVELAN_ADDR_SIZE/2); - WL_WRITE_2(sc, PIOP1, eh_p->ether_type); - -#ifdef WLDEBUG - if (sc->ifp->if_flags & IFF_DEBUG) { - if (xmt_debug) { - printf("XMT mbuf: L%d @%p ", count, (void *)mb_p); - printf("ether type %x\n", eh_p->ether_type); - } - } -#endif /* WLDEBUG */ - WL_WRITE_2(sc, PIOR0, OFFSET_TBD); - WL_WRITE_2(sc, PIOP0, 0); /* act_count */ - WL_WRITE_2(sc, PIOR1, OFFSET_TBD + 4); - WL_WRITE_2(sc, PIOP1, xmtdata_p); /* buffer_addr */ - WL_WRITE_2(sc, PIOP1, 0); /* buffer_base */ - for (;;) { - if (count) { - if (clen + count > WAVELAN_MTU) - break; - if (count & 1) - len = count + 1; - else - len = count; - WL_WRITE_2(sc, PIOR1, xmtdata_p); - WL_WRITE_MULTI_2(sc, PIOP1, mb_p, len/2); - clen += count; - WL_WRITE_2(sc, PIOR0, tbd_p); /* address of act_count */ - WL_WRITE_2(sc, PIOP0, WL_READ_2(sc, PIOP0) + count); - xmtdata_p += len; - if ((tm_p = tm_p->m_next) == (struct mbuf *)0) - break; - if (count & 1) { - /* go to the next descriptor */ - WL_WRITE_2(sc, PIOR0, tbd_p + 2); - tbd_p += sizeof (tbd_t); - WL_WRITE_2(sc, PIOP0, tbd_p); /* next_tbd_offset */ - WL_WRITE_2(sc, PIOR0, tbd_p); - WL_WRITE_2(sc, PIOP0, 0); /* act_count */ - WL_WRITE_2(sc, PIOR1, tbd_p + 4); - WL_WRITE_2(sc, PIOP1, xmtdata_p); /* buffer_addr */ - WL_WRITE_2(sc, PIOP1, 0); /* buffer_base */ - /* at the end -> coallesce remaining mbufs */ - if (tbd_p == OFFSET_TBD + (N_TBD-1) * sizeof (tbd_t)) { - wlsftwsleaze(&count, &mb_p, &tm_p, sc); - continue; - } - /* next mbuf short -> coallesce as needed */ - if ( (tm_p->m_next == (struct mbuf *) 0) || -#define HDW_THRESHOLD 55 - tm_p->m_len > HDW_THRESHOLD) - /* ok */; - else { - wlhdwsleaze(&count, &mb_p, &tm_p, sc); - continue; - } - } - } else if ((tm_p = tm_p->m_next) == (struct mbuf *)0) - break; - count = tm_p->m_len; - mb_p = mtod(tm_p, u_char *); -#ifdef WLDEBUG - if (sc->ifp->if_flags & IFF_DEBUG) - if (xmt_debug) - printf("mbuf+ L%d @%p ", count, (void *)mb_p); -#endif /* WLDEBUG */ - } -#ifdef WLDEBUG - if (sc->ifp->if_flags & IFF_DEBUG) - if (xmt_debug) - printf("CLEN = %d\n", clen); -#endif /* WLDEBUG */ - WL_WRITE_2(sc, PIOR0, tbd_p); - if (clen < ETHERMIN) { - WL_WRITE_2(sc, PIOP0, WL_READ_2(sc, PIOP0) + ETHERMIN - clen); - WL_WRITE_2(sc, PIOR1, xmtdata_p); - for (xmtshort_p = xmtdata_p; clen < ETHERMIN; clen += 2) - WL_WRITE_2(sc, PIOP1, 0); - } - WL_WRITE_2(sc, PIOP0, WL_READ_2(sc, PIOP0) | TBD_SW_EOF); - WL_WRITE_2(sc, PIOR0, tbd_p + 2); - WL_WRITE_2(sc, PIOP0, I82586NULL); -#ifdef WLDEBUG - if (sc->ifp->if_flags & IFF_DEBUG) { - if (xmt_debug) { - wltbd(sc); - printf("\n"); - } - } -#endif /* WLDEBUG */ - - WL_WRITE_2(sc, PIOR0, OFFSET_SCB + 2); /* address of scb_command */ - /* - * wait for 586 to clear previous command, complain if it takes - * too long - */ - for (spin = 1;;spin = (spin + 1) % 10000) { - if (WL_READ_2(sc, PIOP0) == 0) { /* it's done, we can go */ - break; - } - if ((spin == 0) && xmt_watch) { /* not waking up, and we care */ - if_printf(sc->ifp, "slow accepting xmit\n"); - } - } - WL_WRITE_2(sc, PIOP0, SCB_CU_STRT); /* new command */ - SET_CHAN_ATTN(sc); - - m_freem(m); - - /* XXX - * Pause to avoid transmit overrun problems. - * The required delay tends to vary with platform type, and may be - * related to interrupt loss. - */ - if (wl_xmit_delay) { - DELAY(wl_xmit_delay); - } - return; -} - -/* - * wlbldru: - * - * This function builds the linear linked lists of fd's and - * rbd's. Based on page 4-32 of 1986 Intel microcom handbook. - * - */ -static u_short -wlbldru(struct wl_softc *sc) -{ - fd_t fd; - rbd_t rbd; - u_short fd_p = OFFSET_RU; - u_short rbd_p = OFFSET_RBD; - int i; - - sc->begin_fd = fd_p; - for (i = 0; i < N_FD; i++) { - fd.status = 0; - fd.command = 0; - fd.link_offset = fd_p + sizeof(fd_t); - fd.rbd_offset = I82586NULL; - WL_WRITE_2(sc, PIOR1, fd_p); - WL_WRITE_MULTI_2(sc, PIOP1, &fd, 8/2); - fd_p = fd.link_offset; - } - fd_p -= sizeof(fd_t); - sc->end_fd = fd_p; - WL_WRITE_2(sc, PIOR1, fd_p + 2); - WL_WRITE_2(sc, PIOP1, AC_CW_EL); /* command */ - WL_WRITE_2(sc, PIOP1, I82586NULL); /* link_offset */ - fd_p = OFFSET_RU; - - WL_WRITE_2(sc, PIOR0, fd_p + 6); /* address of rbd_offset */ - WL_WRITE_2(sc, PIOP0, rbd_p); - WL_WRITE_2(sc, PIOR1, rbd_p); - for (i = 0; i < N_RBD; i++) { - rbd.status = 0; - rbd.buffer_addr = rbd_p + sizeof(rbd_t) + 2; - rbd.buffer_base = 0; - rbd.size = RCVBUFSIZE; - if (i != N_RBD-1) { - rbd_p += sizeof(ru_t); - rbd.next_rbd_offset = rbd_p; - } else { - rbd.next_rbd_offset = I82586NULL; - rbd.size |= AC_CW_EL; - sc->end_rbd = rbd_p; - } - WL_WRITE_MULTI_2(sc, PIOP1, &rbd, sizeof(rbd_t)/2); - WL_WRITE_2(sc, PIOR1, rbd_p); - } - return sc->begin_fd; -} - -/* - * wlrustrt: - * - * This routine starts the receive unit running. First checks if the - * board is actually ready, then the board is instructed to receive - * packets again. - * - */ -static void -wlrustrt(struct wl_softc *sc) -{ - u_short rfa; - -#ifdef WLDEBUG - if (sc->ifp->if_flags & IFF_DEBUG) - if_printf(sc->ifp, "entered wlrustrt()\n"); -#endif - WL_WRITE_2(sc, PIOR0, OFFSET_SCB); - if (WL_READ_2(sc, PIOP0) & SCB_RUS_READY){ - printf("wlrustrt: RUS_READY\n"); - return; - } - - WL_WRITE_2(sc, PIOR0, OFFSET_SCB + 2); - WL_WRITE_2(sc, PIOP0, SCB_RU_STRT); /* command */ - rfa = wlbldru(sc); - WL_WRITE_2(sc, PIOR0, OFFSET_SCB + 6); /* address of scb_rfa_offset */ - WL_WRITE_2(sc, PIOP0, rfa); - - SET_CHAN_ATTN(sc); - return; -} - -/* - * wldiag: - * - * This routine does a 586 op-code number 7, and obtains the - * diagnose status for the WaveLAN. - * - */ -static int -wldiag(struct wl_softc *sc) -{ - short status; - -#ifdef WLDEBUG - if (sc->ifp->if_flags & IFF_DEBUG) - if_printf(sc->ifp, "entered wldiag()\n"); -#endif - WL_WRITE_2(sc, PIOR0, OFFSET_SCB); - status = WL_READ_2(sc, PIOP0); - if (status & SCB_SW_INT) { - /* state is 2000 which seems ok - if_printf(sc->ifp, "diag(): unexpected initial state %\n", - WL_READ_2(sc, PIOP0)); - */ - wlack(sc); - } - WL_WRITE_2(sc, PIOR1, OFFSET_CU); - WL_WRITE_2(sc, PIOP1, 0); /* ac_status */ - WL_WRITE_2(sc, PIOP1, AC_DIAGNOSE|AC_CW_EL);/* ac_command */ - if (wlcmd(sc, "diag()") == 0) - return 0; - WL_WRITE_2(sc, PIOR0, OFFSET_CU); - if (WL_READ_2(sc, PIOP0) & 0x0800) { - if_printf(sc->ifp, "i82586 Self Test failed!\n"); - return 0; - } - return TRUE; -} - -/* - * wlconfig: - * - * This routine does a standard config of the WaveLAN board. - * - */ -static int -wlconfig(struct wl_softc *sc) -{ - configure_t configure; - -#if MULTICAST - struct ifmultiaddr *ifma; - u_char *addrp; - int cnt = 0; -#endif /* MULTICAST */ - -#ifdef WLDEBUG - if (sc->ifp->if_flags & IFF_DEBUG) - if_printf(sc->ifp, "entered wlconfig()\n"); -#endif - WL_WRITE_2(sc, PIOR0, OFFSET_SCB); - if (WL_READ_2(sc, PIOP0) & SCB_SW_INT) { - /* - if_printf(sc->ifp, "config(): unexpected initial state %x\n", - WL_READ_2(sc, PIOP0)); - */ - } - wlack(sc); - - WL_WRITE_2(sc, PIOR1, OFFSET_CU); - WL_WRITE_2(sc, PIOP1, 0); /* ac_status */ - WL_WRITE_2(sc, PIOP1, AC_CONFIGURE|AC_CW_EL); /* ac_command */ - -/* jrb hack */ - configure.fifolim_bytecnt = 0x080c; - configure.addrlen_mode = 0x0600; - configure.linprio_interframe = 0x2060; - configure.slot_time = 0xf200; - configure.hardware = 0x0008; /* tx even w/o CD */ - configure.min_frame_len = 0x0040; -#if 0 - /* This is the configuration block suggested by Marc Meertens - * <mmeerten@obelix.utrecht.NCR.COM> in an e-mail message to John - * Ioannidis on 10 Nov 92. - */ - configure.fifolim_bytecnt = 0x040c; - configure.addrlen_mode = 0x0600; - configure.linprio_interframe = 0x2060; - configure.slot_time = 0xf000; - configure.hardware = 0x0008; /* tx even w/o CD */ - configure.min_frame_len = 0x0040; -#else - /* - * below is the default board configuration from p2-28 from 586 book - */ - configure.fifolim_bytecnt = 0x080c; - configure.addrlen_mode = 0x2600; - configure.linprio_interframe = 0x7820; /* IFS=120, ACS=2 */ - configure.slot_time = 0xf00c; /* slottime=12 */ - configure.hardware = 0x0008; /* tx even w/o CD */ - configure.min_frame_len = 0x0040; -#endif - if (sc->mode & (MOD_PROM | MOD_ENAL)) - configure.hardware |= 1; - WL_WRITE_2(sc, PIOR1, OFFSET_CU + 6); - WL_WRITE_MULTI_2(sc, PIOP1, &configure, sizeof(configure_t)/2); - - if (wlcmd(sc, "config()-configure") == 0) - return 0; -#if MULTICAST - WL_WRITE_2(sc, PIOR1, OFFSET_CU); - WL_WRITE_2(sc, PIOP1, 0); /* ac_status */ - WL_WRITE_2(sc, PIOP1, AC_MCSETUP|AC_CW_EL); /* ac_command */ - WL_WRITE_2(sc, PIOR1, OFFSET_CU + 8); - if_maddr_rlock(sc->ifp); - TAILQ_FOREACH(ifma, &sc->ifp->if_multiaddrs, ifma_link) { - if (ifma->ifma_addr->sa_family != AF_LINK) - continue; - - addrp = LLADDR((struct sockaddr_dl *)ifma->ifma_addr); - WL_WRITE_2(sc, PIOP1, addrp[0] + (addrp[1] << 8)); - WL_WRITE_2(sc, PIOP1, addrp[2] + (addrp[3] << 8)); - WL_WRITE_2(sc, PIOP1, addrp[4] + (addrp[5] << 8)); - ++cnt; - } - if_maddr_runlock(sc->ifp); - WL_WRITE_2(sc, PIOR1, OFFSET_CU + 6); /* mc-cnt */ - WL_WRITE_2(sc, PIOP1, cnt * WAVELAN_ADDR_SIZE); - if (wlcmd(sc, "config()-mcaddress") == 0) - return 0; -#endif /* MULTICAST */ - - WL_WRITE_2(sc, PIOR1, OFFSET_CU); - WL_WRITE_2(sc, PIOP1, 0); /* ac_status */ - WL_WRITE_2(sc, PIOP1, AC_IASETUP|AC_CW_EL); /* ac_command */ - WL_WRITE_2(sc, PIOR1, OFFSET_CU + 6); - WL_WRITE_MULTI_2(sc, PIOP1, IF_LLADDR(sc->ifp), WAVELAN_ADDR_SIZE/2); - - if (wlcmd(sc, "config()-address") == 0) - return(0); - - wlinitmmc(sc); - - return(1); -} - -/* - * wlcmd: - * - * Set channel attention bit and busy wait until command has - * completed. Then acknowledge the command completion. - */ -static int -wlcmd(struct wl_softc *sc, char *str) -{ - int i; - - WL_WRITE_2(sc, PIOR0, OFFSET_SCB + 2); /* address of scb_command */ - WL_WRITE_2(sc, PIOP0, SCB_CU_STRT); - - SET_CHAN_ATTN(sc); - - WL_WRITE_2(sc, PIOR0, OFFSET_CU); - for (i = 0; i < 0xffff; i++) - if (WL_READ_2(sc, PIOP0) & AC_SW_C) - break; - if (i == 0xffff || !(WL_READ_2(sc, PIOP0) & AC_SW_OK)) { - if_printf(sc->ifp, "%s failed; status = %d, inw = %x, outw = %x\n", - str, WL_READ_2(sc, PIOP0) & AC_SW_OK, WL_READ_2(sc, PIOP0), - WL_READ_2(sc, PIOR0)); - WL_WRITE_2(sc, PIOR0, OFFSET_SCB); - printf("scb_status %x\n", WL_READ_2(sc, PIOP0)); - WL_WRITE_2(sc, PIOR0, OFFSET_SCB+2); - printf("scb_command %x\n", WL_READ_2(sc, PIOP0)); - WL_WRITE_2(sc, PIOR0, OFFSET_SCB+4); - printf("scb_cbl %x\n", WL_READ_2(sc, PIOP0)); - WL_WRITE_2(sc, PIOR0, OFFSET_CU+2); - printf("cu_cmd %x\n", WL_READ_2(sc, PIOP0)); - return(0); - } - - WL_WRITE_2(sc, PIOR0, OFFSET_SCB); - if ((WL_READ_2(sc, PIOP0) & SCB_SW_INT) && - (WL_READ_2(sc, PIOP0) != SCB_SW_CNA)) { - /* - if_printf(sc->ifp, "%s: unexpected final state %x\n", - str, WL_READ_2(sc, PIOP0)); - */ - } - wlack(sc); - return(TRUE); -} - -/* - * wlack: if the 82596 wants attention because it has finished - * sending or receiving a packet, acknowledge its desire and - * return bits indicating the kind of attention. wlack() returns - * these bits so that the caller can service exactly the - * conditions that wlack() acknowledged. - */ -static int -wlack(struct wl_softc *sc) -{ - int i; - u_short cmd; - - WL_WRITE_2(sc, PIOR1, OFFSET_SCB); - if (!(cmd = (WL_READ_2(sc, PIOP1) & SCB_SW_INT))) - return(0); -#ifdef WLDEBUG - if (sc->ifp->if_flags & IFF_DEBUG) - if_printf(sc->ifp, "doing a wlack()\n"); -#endif - WL_WRITE_2(sc, PIOP1, cmd); - SET_CHAN_ATTN(sc); - WL_WRITE_2(sc, PIOR0, OFFSET_SCB + 2); /* address of scb_command */ - for (i = 1000000; WL_READ_2(sc, PIOP0) && (i-- > 0); ) - continue; - if (i < 1) - if_printf(sc->ifp, "wlack(): board not accepting command.\n"); - return(cmd); -} - -#ifdef WLDEBUG -static void -wltbd(struct wl_softc *sc) -{ - u_short tbd_p = OFFSET_TBD; - tbd_t tbd; - int i = 0; - int sum = 0; - - for (;;) { - WL_WRITE_2(sc, PIOR1, tbd_p); - WL_READ_MULTI_2(sc, PIOP1, &tbd, sizeof(tbd_t)/2); - sum += (tbd.act_count & ~TBD_SW_EOF); - printf("%d: addr %x, count %d (%d), next %x, base %x\n", - i++, tbd.buffer_addr, - (tbd.act_count & ~TBD_SW_EOF), sum, - tbd.next_tbd_offset, tbd.buffer_base); - if (tbd.act_count & TBD_SW_EOF) - break; - tbd_p = tbd.next_tbd_offset; - } -} -#endif - -static void -wlhdwsleaze(u_short *countp, u_char **mb_pp, struct mbuf **tm_pp, struct wl_softc *sc) -{ - struct mbuf *tm_p = *tm_pp; - u_char *mb_p = *mb_pp; - u_short count = 0; - u_char *cp; - int len; - - /* - * can we get a run that will be coallesced or - * that terminates before breaking - */ - do { - count += tm_p->m_len; - if (tm_p->m_len & 1) - break; - } while ((tm_p = tm_p->m_next) != (struct mbuf *)0); - if ( (tm_p == (struct mbuf *)0) || - count > HDW_THRESHOLD) { - *countp = (*tm_pp)->m_len; - *mb_pp = mtod((*tm_pp), u_char *); - return; - } - - /* we need to copy */ - tm_p = *tm_pp; - mb_p = *mb_pp; - count = 0; - cp = (u_char *) t_packet; - for (;;) { - bcopy(mtod(tm_p, u_char *), cp, len = tm_p->m_len); - count += len; - if (count > HDW_THRESHOLD) - break; - cp += len; - if (tm_p->m_next == (struct mbuf *)0) - break; - tm_p = tm_p->m_next; - } - *countp = count; - *mb_pp = (u_char *) t_packet; - *tm_pp = tm_p; - return; -} - - -static void -wlsftwsleaze(u_short *countp, u_char **mb_pp, struct mbuf **tm_pp, struct wl_softc *sc) -{ - struct mbuf *tm_p = *tm_pp; - u_short count = 0; - u_char *cp = (u_char *) t_packet; - int len; - - /* we need to copy */ - for (;;) { - bcopy(mtod(tm_p, u_char *), cp, len = tm_p->m_len); - count += len; - cp += len; - if (tm_p->m_next == (struct mbuf *)0) - break; - tm_p = tm_p->m_next; - } - - *countp = count; - *mb_pp = (u_char *) t_packet; - *tm_pp = tm_p; - return; -} - -static void -wlmmcstat(struct wl_softc *sc) -{ - u_short tmp; - - device_printf(sc->dev, "DCE_STATUS: 0x%x, ", - wlmmcread(sc, MMC_DCE_STATUS) & 0x0f); - tmp = wlmmcread(sc, MMC_CORRECT_NWID_H) << 8; - tmp |= wlmmcread(sc, MMC_CORRECT_NWID_L); - printf("Correct NWID's: %d, ", tmp); - tmp = wlmmcread(sc, MMC_WRONG_NWID_H) << 8; - tmp |= wlmmcread(sc, MMC_WRONG_NWID_L); - printf("Wrong NWID's: %d\n", tmp); - printf("THR_PRE_SET: 0x%x, ", wlmmcread(sc, MMC_THR_PRE_SET)); - printf("SIGNAL_LVL: %d, SILENCE_LVL: %d\n", - wlmmcread(sc, MMC_SIGNAL_LVL), - wlmmcread(sc, MMC_SILENCE_LVL)); - printf("SIGN_QUAL: 0x%x, NETW_ID: %x:%x, DES: %d\n", - wlmmcread(sc, MMC_SIGN_QUAL), - wlmmcread(sc, MMC_NETW_ID_H), - wlmmcread(sc, MMC_NETW_ID_L), - wlmmcread(sc, MMC_DES_AVAIL)); -} - -static u_short -wlmmcread(struct wl_softc *sc, u_short reg) -{ - while (WL_READ_2(sc, HASR) & HASR_MMC_BUSY) - continue; - WL_WRITE_2(sc, MMCR,reg << 1); - while (WL_READ_2(sc, HASR) & HASR_MMC_BUSY) - continue; - return (u_short)WL_READ_2(sc, MMCR) >> 8; -} - -static void -getsnr(struct wl_softc *sc) -{ - MMC_WRITE(MMC_FREEZE,1); - /* - * SNR retrieval procedure : - * - * read signal level : wlmmcread(sc, MMC_SIGNAL_LVL); - * read silence level : wlmmcread(sc, MMC_SILENCE_LVL); - */ - MMC_WRITE(MMC_FREEZE,0); - /* - * SNR is signal:silence ratio. - */ -} - -/* -** wlgetpsa -** -** Reads the psa for the wavelan at (sc) into (buf) -*/ -static void -wlgetpsa(struct wl_softc *sc, u_char *buf) -{ - int i; - - PCMD(sc, HACR_DEFAULT & ~HACR_16BITS); - PCMD(sc, HACR_DEFAULT & ~HACR_16BITS); - - for (i = 0; i < 0x40; i++) { - WL_WRITE_2(sc, PIOR2, i); - buf[i] = WL_READ_1(sc, PIOP2); - } - PCMD(sc, HACR_DEFAULT); - PCMD(sc, HACR_DEFAULT); -} - -/* -** wlsetpsa -** -** Writes the psa for wavelan (unit) from the softc back to the -** board. Updates the CRC and sets the CRC OK flag. -** -** Do not call this when the board is operating, as it doesn't -** preserve the hacr. -*/ -static void -wlsetpsa(struct wl_softc *sc) -{ - int i; - u_short crc; - - crc = wlpsacrc(sc->psa); /* calculate CRC of PSA */ - sc->psa[WLPSA_CRCLOW] = crc & 0xff; - sc->psa[WLPSA_CRCHIGH] = (crc >> 8) & 0xff; - sc->psa[WLPSA_CRCOK] = 0x55; /* default to 'bad' until programming complete */ - - PCMD(sc, HACR_DEFAULT & ~HACR_16BITS); - PCMD(sc, HACR_DEFAULT & ~HACR_16BITS); - - for (i = 0; i < 0x40; i++) { - DELAY(DELAYCONST); - WL_WRITE_2(sc, PIOR2, i); /* write param memory */ - DELAY(DELAYCONST); - WL_WRITE_1(sc, PIOP2, sc->psa[i]); - } - DELAY(DELAYCONST); - WL_WRITE_2(sc, PIOR2, WLPSA_CRCOK); /* update CRC flag*/ - DELAY(DELAYCONST); - sc->psa[WLPSA_CRCOK] = 0xaa; /* OK now */ - WL_WRITE_1(sc, PIOP2, 0xaa); /* all OK */ - DELAY(DELAYCONST); - - PCMD(sc, HACR_DEFAULT); - PCMD(sc, HACR_DEFAULT); -} - -/* -** CRC routine provided by Christopher Giordano <cgiordan@gdeb.com>, -** from original code by Tomi Mikkonen (tomitm@remedy.fi) -*/ - -static u_int crc16_table[16] = { - 0x0000, 0xCC01, 0xD801, 0x1400, - 0xF001, 0x3C00, 0x2800, 0xE401, - 0xA001, 0x6C00, 0x7800, 0xB401, - 0x5000, 0x9C01, 0x8801, 0x4400 -}; - -static u_short -wlpsacrc(u_char *buf) -{ - u_short crc = 0; - int i, r1; - - for (i = 0; i < 0x3d; i++, buf++) { - /* lower 4 bits */ - r1 = crc16_table[crc & 0xF]; - crc = (crc >> 4) & 0x0FFF; - crc = crc ^ r1 ^ crc16_table[*buf & 0xF]; - - /* upper 4 bits */ - r1 = crc16_table[crc & 0xF]; - crc = (crc >> 4) & 0x0FFF; - crc = crc ^ r1 ^ crc16_table[(*buf >> 4) & 0xF]; - } - return(crc); -} -#ifdef WLCACHE - -/* - * wl_cache_store - * - * take input packet and cache various radio hw characteristics - * indexed by MAC address. - * - * Some things to think about: - * note that no space is malloced. - * We might hash the mac address if the cache were bigger. - * It is not clear that the cache is big enough. - * It is also not clear how big it should be. - * The cache is IP-specific. We don't care about that as - * we want it to be IP-specific. - * The last N recv. packets are saved. This will tend - * to reward agents and mobile hosts that beacon. - * That is probably fine for mobile ip. - */ - -/* globals for wavelan signal strength cache */ -/* this should go into softc structure above. -*/ - -/* set true if you want to limit cache items to broadcast/mcast - * only packets (not unicast) - */ -static int wl_cache_mcastonly = 1; -SYSCTL_INT(_machdep, OID_AUTO, wl_cache_mcastonly, CTLFLAG_RW, - &wl_cache_mcastonly, 0, ""); - -/* set true if you want to limit cache items to IP packets only -*/ -static int wl_cache_iponly = 1; -SYSCTL_INT(_machdep, OID_AUTO, wl_cache_iponly, CTLFLAG_RW, - &wl_cache_iponly, 0, ""); - -/* zero out the cache -*/ -static void -wl_cache_zero(struct wl_softc *sc) -{ - - bzero(&sc->w_sigcache[0], sizeof(struct w_sigcache) * MAXCACHEITEMS); - sc->w_sigitems = 0; - sc->w_nextcache = 0; - sc->w_wrapindex = 0; -} - -/* store hw signal info in cache. - * index is MAC address, but an ip src gets stored too - * There are two filters here controllable via sysctl: - * throw out unicast (on by default, but can be turned off) - * throw out non-ip (on by default, but can be turned off) - */ -static -void wl_cache_store (struct wl_softc *sc, struct ether_header *eh, - struct mbuf *m) -{ -#ifdef INET - struct ip *ip = NULL; /* Avoid GCC warning */ - int i; - int signal, silence; - int w_insertcache; /* computed index for cache entry storage */ - int ipflag = wl_cache_iponly; -#endif - - /* filters: - * 1. ip only - * 2. configurable filter to throw out unicast packets, - * keep multicast only. - */ - -#ifdef INET - /* reject if not IP packet - */ - if ( wl_cache_iponly && (ntohs(eh->ether_type) != 0x800)) { - return; - } - - /* check if broadcast or multicast packet. we toss - * unicast packets - */ - if (wl_cache_mcastonly && ((eh->ether_dhost[0] & 1) == 0)) { - return; - } - - /* find the ip header. we want to store the ip_src - * address. use the mtod macro(in mbuf.h) - * to typecast m to struct ip * - */ - if (ipflag) { - ip = mtod(m, struct ip *); - } - - /* do a linear search for a matching MAC address - * in the cache table - * . MAC address is 6 bytes, - * . var w_nextcache holds total number of entries already cached - */ - for (i = 0; i < sc->w_nextcache; i++) { - if (! bcmp(eh->ether_shost, sc->w_sigcache[i].macsrc, 6 )) { - /* Match!, - * so we already have this entry, - * update the data, and LRU age - */ - break; - } - } - - /* did we find a matching mac address? - * if yes, then overwrite a previously existing cache entry - */ - if (i < sc->w_nextcache ) { - w_insertcache = i; - } - /* else, have a new address entry,so - * add this new entry, - * if table full, then we need to replace entry - */ - else { - - /* check for space in cache table - * note: w_nextcache also holds number of entries - * added in the cache table - */ - if ( sc->w_nextcache < MAXCACHEITEMS ) { - w_insertcache = sc->w_nextcache; - sc->w_nextcache++; - sc->w_sigitems = sc->w_nextcache; - } - /* no space found, so simply wrap with wrap index - * and "zap" the next entry - */ - else { - if (sc->w_wrapindex == MAXCACHEITEMS) { - sc->w_wrapindex = 0; - } - w_insertcache = sc->w_wrapindex++; - } - } - - /* invariant: w_insertcache now points at some slot - * in cache. - */ - if (w_insertcache < 0 || w_insertcache >= MAXCACHEITEMS) { - log(LOG_ERR, - "wl_cache_store, bad index: %d of [0..%d], gross cache error\n", - w_insertcache, MAXCACHEITEMS); - return; - } - - /* store items in cache - * .ipsrc - * .macsrc - * .signal (0..63) ,silence (0..63) ,quality (0..15) - */ - if (ipflag) { - sc->w_sigcache[w_insertcache].ipsrc = ip->ip_src.s_addr; - } - bcopy( eh->ether_shost, sc->w_sigcache[w_insertcache].macsrc, 6); - signal = sc->w_sigcache[w_insertcache].signal = wlmmcread(sc, MMC_SIGNAL_LVL) & 0x3f; - silence = sc->w_sigcache[w_insertcache].silence = wlmmcread(sc, MMC_SILENCE_LVL) & 0x3f; - sc->w_sigcache[w_insertcache].quality = wlmmcread(sc, MMC_SIGN_QUAL) & 0x0f; - if (signal > 0) - sc->w_sigcache[w_insertcache].snr = - signal - silence; - else - sc->w_sigcache[w_insertcache].snr = 0; -#endif /* INET */ - -} -#endif /* WLCACHE */ |