1139749Simp/*- 226003Smsmith * Redistribution and use in source and binary forms, with or without 326003Smsmith * modification, are permitted provided that the following conditions 426003Smsmith * are met: 526003Smsmith * 1. Redistributions of source code must retain all copyright 626003Smsmith * notices, this list of conditions and the following disclaimer. 726003Smsmith * 2. The names of the authors may not be used to endorse or promote products 897748Sschweikh * derived from this software without specific prior written permission 926003Smsmith * 1026003Smsmith * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR 1126003Smsmith * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 1226003Smsmith * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 1326003Smsmith * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, 1426003Smsmith * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 1526003Smsmith * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 1626003Smsmith * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 1726003Smsmith * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 1826003Smsmith * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 1926003Smsmith * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 2026003Smsmith * 2126003Smsmith */ 2226003Smsmith/* 2326003Smsmith * if_wl.c - original MACH, then BSDI ISA wavelan driver 2426003Smsmith * ported to mach by Anders Klemets 2526003Smsmith * to BSDI by Robert Morris 2626003Smsmith * to FreeBSD by Jim Binkley 2726003Smsmith * to FreeBSD 2.2+ by Michael Smith 2826003Smsmith * 2926003Smsmith * 2.2 update: 3026003Smsmith * Changed interface to match 2.1-2.2 differences. 3126003Smsmith * Implement IRQ selection logic in wlprobe() 3226003Smsmith * Implement PSA updating. 3326003Smsmith * Pruned heading comments for relevance. 3426003Smsmith * Ripped out all the 'interface counters' cruft. 3526003Smsmith * Cut the missing-interrupt timer back to 100ms. 3627817Smsmith * 2.2.1 update: 3727817Smsmith * now supports all multicast mode (mrouted will work), 3827817Smsmith * but unfortunately must do that by going into promiscuous mode 3927817Smsmith * NWID sysctl added so that normally promiscuous mode is NWID-specific 4027817Smsmith * but can be made NWID-inspecific 4127817Smsmith * 7/14/97 jrb 4226003Smsmith * 4326003Smsmith * Work done: 4426003Smsmith * Ported to FreeBSD, got promiscuous mode working with bpfs, 4526003Smsmith * and rewired timer routine. The i82586 will hang occasionally on output 4626003Smsmith * and the watchdog timer will kick it if so and log an entry. 4726003Smsmith * 2 second timeout there. Apparently the chip loses an interrupt. 4826003Smsmith * Code borrowed from if_ie.c for watchdog timer. 4926003Smsmith * 5026003Smsmith * The wavelan card is a 2mbit radio modem that emulates ethernet; 5126003Smsmith * i.e., it uses MAC addresses. This should not be a surprise since 5226003Smsmith * it uses an ethernet controller as a major hw item. 5326003Smsmith * It can broadcast, unicast or apparently multicast in a base cell 54108533Sschweikh * using an omni-directional antennae that is 5526003Smsmith * about 800 feet around the base cell barring walls and metal. 5626003Smsmith * With directional antennae, it can be used point to point over a mile 5726003Smsmith * or so apparently (haven't tried that). 5826003Smsmith * 5926003Smsmith * There are ISA and pcmcia versions (not supported by this code). 6026003Smsmith * The ISA card has an Intel 82586 lan controller on it. It consists 6126003Smsmith * of 2 pieces of hw, the lan controller (intel) and a radio-modem. 6226003Smsmith * The latter has an extra set of controller registers that has nothing 6326003Smsmith * to do with the i82586 and allows setting and monitoring of radio 6426003Smsmith * signal strength, etc. There is a nvram area called the PSA that 6526003Smsmith * contains a number of setup variables including the IRQ and so-called 6626003Smsmith * NWID or Network ID. The NWID must be set the same for all radio 6726003Smsmith * cards to communicate (unless you are using the ATT/NCR roaming feature 6826003Smsmith * with their access points. There is no support for that here. Roaming 6926003Smsmith * involves a link-layer beacon sent out from the access points. End 7026003Smsmith * stations monitor the signal strength and only use the strongest 7126003Smsmith * access point). This driver assumes that the base ISA port, IRQ, 7226003Smsmith * and NWID are first set in nvram via the dos-side "instconf.exe" utility 7326003Smsmith * supplied with the card. This driver takes the ISA port from 7426003Smsmith * the kernel configuration setup, and then determines the IRQ either 7526003Smsmith * from the kernel config (if an explicit IRQ is set) or from the 7626003Smsmith * PSA on the card if not. 7726003Smsmith * The hw also magically just uses the IRQ set in the nvram. 7826003Smsmith * The NWID is used magically as well by the radio-modem 7926003Smsmith * to determine which packets to keep or throw out. 8026003Smsmith * 8126003Smsmith * sample config: 8226003Smsmith * 8340565Sbde * device wl0 at isa? port 0x300 net irq ? 8426003Smsmith * 8526003Smsmith * Ifdefs: 8627817Smsmith * 1. WLDEBUG. (off) - if turned on enables IFF_DEBUG set via ifconfig debug 8727817Smsmith * 2. MULTICAST (on) - turned on and works up to and including mrouted 8827817Smsmith * 3. WLCACHE (off) - define to turn on a signal strength 8927817Smsmith * (and other metric) cache that is indexed by sender MAC address. 9027817Smsmith * Apps can read this out to learn the remote signal strength of a 9127817Smsmith * sender. Note that it has a switch so that it only stores 9227817Smsmith * broadcast/multicast senders but it could be set to store unicast 9327817Smsmith * too only. Size is hardwired in if_wl_wavelan.h 9426003Smsmith * 9526003Smsmith * one further note: promiscuous mode is a curious thing. In this driver, 9627817Smsmith * promiscuous mode apparently CAN catch ALL packets and ignore the NWID 9726003Smsmith * setting. This is probably more useful in a sense (for snoopers) if 9826003Smsmith * you are interested in all traffic as opposed to if you are interested 9927817Smsmith * in just your own. There is a driver specific sysctl to turn promiscuous 10027817Smsmith * from just promiscuous to wildly promiscuous... 10127817Smsmith * 10227817Smsmith * This driver also knows how to load the synthesizers in the 2.4 Gz 10327817Smsmith * ISA Half-card, Product number 847647476 (USA/FCC IEEE Channel set). 10427817Smsmith * This product consists of a "mothercard" that contains the 82586, 10527817Smsmith * NVRAM that holds the PSA, and the ISA-buss interface custom ASIC. 10627817Smsmith * The radio transceiver is a "daughtercard" called the WaveMODEM which 10727817Smsmith * connects to the mothercard through two single-inline connectors: a 10827817Smsmith * 20-pin connector provides DC-power and modem signals, and a 3-pin 10927817Smsmith * connector which exports the antenna connection. The code herein 11027817Smsmith * loads the receive and transmit synthesizers and the corresponding 11127817Smsmith * transmitter output power value from an EEPROM controlled through 11227817Smsmith * additional registers via the MMC. The EEPROM address selected 11327817Smsmith * are those whose values are preset by the DOS utility programs 11427817Smsmith * provided with the product, and this provides compatible operation 11527817Smsmith * with the DOS Packet Driver software. A future modification will 11627817Smsmith * add the necessary functionality to this driver and to the wlconfig 11727817Smsmith * utility to completely replace the DOS Configuration Utilities. 11827817Smsmith * The 2.4 Gz WaveMODEM is described in document number 407-024692/E, 11927817Smsmith * and is available through Lucent Technologies OEM supply channels. 12027817Smsmith * --RAB 1997/06/08. 12126003Smsmith */ 12226003Smsmith 12326003Smsmith#define MULTICAST 1 12426003Smsmith 12526003Smsmith/* 12626003Smsmith * Olivetti PC586 Mach Ethernet driver v1.0 12726003Smsmith * Copyright Ing. C. Olivetti & C. S.p.A. 1988, 1989 12826003Smsmith * All rights reserved. 12926003Smsmith * 13026003Smsmith */ 13126003Smsmith/* 13226003Smsmith Copyright 1988, 1989 by Olivetti Advanced Technology Center, Inc., 13326003SmsmithCupertino, California. 13426003Smsmith 13526003Smsmith All Rights Reserved 13626003Smsmith 13726003Smsmith Permission to use, copy, modify, and distribute this software and 13826003Smsmithits documentation for any purpose and without fee is hereby 13926003Smsmithgranted, provided that the above copyright notice appears in all 14026003Smsmithcopies and that both the copyright notice and this permission notice 14126003Smsmithappear in supporting documentation, and that the name of Olivetti 14226003Smsmithnot be used in advertising or publicity pertaining to distribution 14326003Smsmithof the software without specific, written prior permission. 14426003Smsmith 14526003Smsmith OLIVETTI DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE 14626003SmsmithINCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, 14726003SmsmithIN NO EVENT SHALL OLIVETTI BE LIABLE FOR ANY SPECIAL, INDIRECT, OR 14826003SmsmithCONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 14926003SmsmithLOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT, 15026003SmsmithNEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUR OF OR IN CONNECTION 15126003SmsmithWITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15226003Smsmith*/ 15326003Smsmith/* 15426003Smsmith Copyright 1988, 1989 by Intel Corporation, Santa Clara, California. 15526003Smsmith 15626003Smsmith All Rights Reserved 15726003Smsmith 15826003SmsmithPermission to use, copy, modify, and distribute this software and 15926003Smsmithits documentation for any purpose and without fee is hereby 16026003Smsmithgranted, provided that the above copyright notice appears in all 16126003Smsmithcopies and that both the copyright notice and this permission notice 16226003Smsmithappear in supporting documentation, and that the name of Intel 16326003Smsmithnot be used in advertising or publicity pertaining to distribution 16426003Smsmithof the software without specific, written prior permission. 16526003Smsmith 16626003SmsmithINTEL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE 16726003SmsmithINCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, 16826003SmsmithIN NO EVENT SHALL INTEL BE LIABLE FOR ANY SPECIAL, INDIRECT, OR 16926003SmsmithCONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 17026003SmsmithLOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT, 17126003SmsmithNEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION 17226003SmsmithWITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17326003Smsmith*/ 17426003Smsmith 175122625Sobrien#include <sys/cdefs.h> 176122625Sobrien__FBSDID("$FreeBSD: stable/10/sys/dev/wl/if_wl.c 320923 2017-07-12 22:16:54Z jhb $"); 177122625Sobrien 17826003Smsmith/* 17926003Smsmith * NOTE: 18026003Smsmith * by rvb: 18126003Smsmith * 1. The best book on the 82586 is: 18226003Smsmith * LAN Components User's Manual by Intel 18326003Smsmith * The copy I found was dated 1984. This really tells you 18426003Smsmith * what the state machines are doing 18526003Smsmith * 2. In the current design, we only do one write at a time, 18626003Smsmith * though the hardware is capable of chaining and possibly 18726003Smsmith * even batching. The problem is that we only make one 18826003Smsmith * transmit buffer available in sram space. 18926003Smsmith */ 19026003Smsmith 19126109Speter#include "opt_wavelan.h" 19232350Seivind#include "opt_inet.h" 19326003Smsmith 19426003Smsmith#include <sys/param.h> 19526003Smsmith#include <sys/systm.h> 19661011Speter#include <sys/kernel.h> 197129879Sphk#include <sys/module.h> 19826114Speter#include <sys/sockio.h> 19926003Smsmith#include <sys/mbuf.h> 200164033Srwatson#include <sys/priv.h> 20126003Smsmith#include <sys/socket.h> 20226003Smsmith#include <sys/syslog.h> 203113571Sjhay#include <machine/bus.h> 204113571Sjhay#include <machine/resource.h> 20561011Speter#include <sys/bus.h> 206113571Sjhay#include <sys/rman.h> 20726003Smsmith 20826003Smsmith#include <sys/sysctl.h> 20926003Smsmith 21050026Smdodd#include <net/ethernet.h> 21126003Smsmith#include <net/if.h> 212113571Sjhay#include <net/if_arp.h> 21326003Smsmith#include <net/if_dl.h> 214147256Sbrooks#include <net/if_types.h> 21526003Smsmith 21626003Smsmith#ifdef INET 21726003Smsmith#include <netinet/in.h> 21827817Smsmith#include <netinet/in_systm.h> 21927817Smsmith#include <netinet/ip.h> 22026003Smsmith#include <netinet/if_ether.h> 22126003Smsmith#endif 22226003Smsmith 22326003Smsmith#include <net/bpf.h> 224113571Sjhay#include <isa/isavar.h> 22526003Smsmith 22626003Smsmith/* was 1000 in original, fed to DELAY(x) */ 22726003Smsmith#define DELAYCONST 1000 228146019Snyan#include <dev/wl/if_wl_i82586.h> /* Definitions for the Intel chip */ 22979080Simp#include <dev/wl/if_wl.h> 23026003Smsmith#include <machine/if_wl_wavelan.h> 23126003Smsmith 23226003Smsmithstatic char t_packet[ETHERMTU + sizeof(struct ether_header) + sizeof(long)]; 23326003Smsmith 23426003Smsmithstruct wl_softc{ 235147256Sbrooks struct ifnet *ifp; 23626003Smsmith u_char psa[0x40]; 23726003Smsmith u_char nwid[2]; /* current radio modem nwid */ 23826003Smsmith short base; 23926003Smsmith short unit; 24026003Smsmith int flags; 24126003Smsmith int tbusy; /* flag to determine if xmit is busy */ 24226003Smsmith u_short begin_fd; 24326003Smsmith u_short end_fd; 24426003Smsmith u_short end_rbd; 24526003Smsmith u_short hacr; /* latest host adapter CR command */ 24626003Smsmith short mode; 24727817Smsmith u_char chan24; /* 2.4 Gz: channel number/EEPROM Area # */ 24827817Smsmith u_short freq24; /* 2.4 Gz: resulting frequency */ 249113571Sjhay int rid_ioport; 250113571Sjhay int rid_irq; 251113571Sjhay struct resource *res_ioport; 252113571Sjhay struct resource *res_irq; 253113571Sjhay void *intr_cookie; 254113571Sjhay bus_space_tag_t bt; 255113571Sjhay bus_space_handle_t bh; 256113572Sjhay struct mtx wl_mtx; 257113571Sjhay struct callout_handle watchdog_ch; 25827817Smsmith#ifdef WLCACHE 25927817Smsmith int w_sigitems; /* number of cached entries */ 26027817Smsmith /* array of cache entries */ 26127817Smsmith struct w_sigcache w_sigcache[ MAXCACHEITEMS ]; 26227817Smsmith int w_nextcache; /* next free cache entry */ 26327817Smsmith int w_wrapindex; /* next "free" cache entry */ 26427817Smsmith#endif 26526003Smsmith}; 26626003Smsmith 267113572Sjhay#define WL_LOCK(_sc) mtx_lock(&(_sc)->wl_mtx) 268122689Ssam#define WL_LOCK_ASSERT(_sc) mtx_assert(&(_sc)->wl_mtx, MA_OWNED) 269113572Sjhay#define WL_UNLOCK(_sc) mtx_unlock(&(_sc)->wl_mtx) 270113572Sjhay 271113571Sjhaystatic int wlprobe(device_t); 272113571Sjhaystatic int wlattach(device_t); 273113571Sjhaystatic int wldetach(device_t); 27426003Smsmith 275113571Sjhaystatic device_method_t wl_methods[] = { 276113571Sjhay DEVMETHOD(device_probe, wlprobe), 277113571Sjhay DEVMETHOD(device_attach, wlattach), 278113571Sjhay DEVMETHOD(device_detach, wldetach), 279113571Sjhay { 0, 0} 280113571Sjhay}; 28126003Smsmith 282113571Sjhaystatic driver_t wl_driver = { 28361011Speter "wl", 284113571Sjhay wl_methods, 285113571Sjhay sizeof (struct wl_softc) 28626003Smsmith}; 28726003Smsmith 288113571Sjhaydevclass_t wl_devclass; 289113571SjhayDRIVER_MODULE(wl, isa, wl_driver, wl_devclass, 0, 0); 290113571SjhayMODULE_DEPEND(wl, isa, 1, 1, 1); 291113571SjhayMODULE_DEPEND(wl, ether, 1, 1, 1); 292113571Sjhay 293113571Sjhaystatic struct isa_pnp_id wl_ids[] = { 294113571Sjhay {0, NULL} 295113571Sjhay}; 296113571Sjhay 29726003Smsmith/* 29826003Smsmith * XXX The Wavelan appears to be prone to dropping stuff if you talk to 29926003Smsmith * it too fast. This disgusting hack inserts a delay after each packet 30027817Smsmith * is queued which helps avoid this behaviour on fast systems. 30126003Smsmith */ 30227817Smsmithstatic int wl_xmit_delay = 250; 30326003SmsmithSYSCTL_INT(_machdep, OID_AUTO, wl_xmit_delay, CTLFLAG_RW, &wl_xmit_delay, 0, ""); 30426003Smsmith 30527817Smsmith/* 30627817Smsmith * not XXX, but ZZZ (bizarre). 30727817Smsmith * promiscuous mode can be toggled to ignore NWIDs. By default, 30827817Smsmith * it does not. Caution should be exercised about combining 30927817Smsmith * this mode with IFF_ALLMULTI which puts this driver in 31027817Smsmith * promiscuous mode. 31127817Smsmith */ 31227817Smsmithstatic int wl_ignore_nwid = 0; 31327817SmsmithSYSCTL_INT(_machdep, OID_AUTO, wl_ignore_nwid, CTLFLAG_RW, &wl_ignore_nwid, 0, ""); 31427817Smsmith 31526003Smsmith/* 31626003Smsmith * Emit diagnostics about transmission problems 31726003Smsmith */ 31826003Smsmithstatic int xmt_watch = 0; 31926003SmsmithSYSCTL_INT(_machdep, OID_AUTO, wl_xmit_watch, CTLFLAG_RW, &xmt_watch, 0, ""); 32026003Smsmith 32126003Smsmith/* 32226003Smsmith * Collect SNR statistics 32326003Smsmith */ 32426003Smsmithstatic int gathersnr = 0; 32526003SmsmithSYSCTL_INT(_machdep, OID_AUTO, wl_gather_snr, CTLFLAG_RW, &gathersnr, 0, ""); 32626003Smsmith 327113571Sjhaystatic int wl_allocate_resources(device_t device); 328113571Sjhaystatic int wl_deallocate_resources(device_t device); 32926003Smsmithstatic void wlstart(struct ifnet *ifp); 33026003Smsmithstatic void wlinit(void *xsc); 33136735Sdfrstatic int wlioctl(struct ifnet *ifp, u_long cmd, caddr_t data); 33227839Smsmithstatic timeout_t wlwatchdog; 333113571Sjhaystatic void wlintr(void *arg); 334113571Sjhaystatic void wlxmt(struct wl_softc *sc, struct mbuf *m); 335113571Sjhaystatic int wldiag(struct wl_softc *sc); 336113571Sjhaystatic int wlconfig(struct wl_softc *sc); 337113571Sjhaystatic int wlcmd(struct wl_softc *sc, char *str); 338113571Sjhaystatic void wlmmcstat(struct wl_softc *sc); 339113571Sjhaystatic u_short wlbldru(struct wl_softc *sc); 34026003Smsmithstatic u_short wlmmcread(u_int base, u_short reg); 341113571Sjhaystatic void wlinitmmc(struct wl_softc *sc); 342113571Sjhaystatic int wlhwrst(struct wl_softc *sc); 343113571Sjhaystatic void wlrustrt(struct wl_softc *sc); 344113571Sjhaystatic void wlbldcu(struct wl_softc *sc); 345113571Sjhaystatic int wlack(struct wl_softc *sc); 346113571Sjhaystatic int wlread(struct wl_softc *sc, u_short fd_p); 347113571Sjhaystatic void getsnr(struct wl_softc *sc); 348113571Sjhaystatic void wlrcv(struct wl_softc *sc); 349113571Sjhaystatic int wlrequeue(struct wl_softc *sc, u_short fd_p); 350113571Sjhaystatic void wlsftwsleaze(u_short *countp, u_char **mb_pp, struct mbuf **tm_pp, struct wl_softc *sc); 351113571Sjhaystatic void wlhdwsleaze(u_short *countp, u_char **mb_pp, struct mbuf **tm_pp, struct wl_softc *sc); 35279081Simp#ifdef WLDEBUG 353113571Sjhaystatic void wltbd(struct wl_softc *sc); 35479081Simp#endif 35526003Smsmithstatic void wlgetpsa(int base, u_char *buf); 356113571Sjhaystatic void wlsetpsa(struct wl_softc *sc); 35726003Smsmithstatic u_short wlpsacrc(u_char *buf); 358113571Sjhaystatic void wldump(struct wl_softc *sc); 35927817Smsmith#ifdef WLCACHE 360113571Sjhaystatic void wl_cache_store(struct wl_softc *, int, struct ether_header *, struct mbuf *); 361113571Sjhaystatic void wl_cache_zero(struct wl_softc *sc); 36227817Smsmith#endif 36326003Smsmith 36426003Smsmith/* array for maping irq numbers to values for the irq parameter register */ 36526003Smsmithstatic int irqvals[16] = { 36626003Smsmith 0, 0, 0, 0x01, 0x02, 0x04, 0, 0x08, 0, 0, 0x10, 0x20, 0x40, 0, 0, 0x80 36726003Smsmith}; 36826003Smsmith 36926003Smsmith/* 37026003Smsmith * wlprobe: 37126003Smsmith * 37226003Smsmith * This function "probes" or checks for the WaveLAN board on the bus to 37326003Smsmith * see if it is there. As far as I can tell, the best break between this 37426003Smsmith * routine and the attach code is to simply determine whether the board 37526003Smsmith * is configured in properly. Currently my approach to this is to write 37626003Smsmith * and read a word from the SRAM on the board being probed. If the word 37726003Smsmith * comes back properly then we assume the board is there. The config 37826003Smsmith * code expects to see a successful return from the probe routine before 37926003Smsmith * attach will be called. 38026003Smsmith * 38126003Smsmith * input : address device is mapped to, and unit # being checked 38226003Smsmith * output : a '1' is returned if the board exists, and a 0 otherwise 38326003Smsmith * 38426003Smsmith */ 38526003Smsmithstatic int 386113571Sjhaywlprobe(device_t device) 38726003Smsmith{ 388113571Sjhay struct wl_softc *sc; 389113571Sjhay short base; 39026003Smsmith char *str = "wl%d: board out of range [0..%d]\n"; 39126003Smsmith u_char inbuf[100]; 392113601Sjhay unsigned long junk, oldpri, sirq; 393113571Sjhay int error, irq; 39426003Smsmith 395113571Sjhay error = ISA_PNP_PROBE(device_get_parent(device), device, wl_ids); 396113571Sjhay if (error == ENXIO || error == 0) 397113571Sjhay return (error); 398113571Sjhay 399113571Sjhay sc = device_get_softc(device); 400113571Sjhay error = wl_allocate_resources(device); 401113571Sjhay if (error) 402113571Sjhay goto errexit; 403113571Sjhay 404113571Sjhay base = rman_get_start(sc->res_ioport); 405113571Sjhay 40626003Smsmith /* TBD. not true. 40726003Smsmith * regular CMD() will not work, since no softc yet 40826003Smsmith */ 40926003Smsmith#define PCMD(base, hacr) outw((base), (hacr)) 41026003Smsmith 411113601Sjhay oldpri = splimp(); 41226003Smsmith PCMD(base, HACR_RESET); /* reset the board */ 41326003Smsmith DELAY(DELAYCONST); /* >> 4 clocks at 6MHz */ 41426003Smsmith PCMD(base, HACR_RESET); /* reset the board */ 41526003Smsmith DELAY(DELAYCONST); /* >> 4 clocks at 6MHz */ 416113601Sjhay splx(oldpri); 41726003Smsmith 41826003Smsmith /* clear reset command and set PIO#1 in autoincrement mode */ 41926003Smsmith PCMD(base, HACR_DEFAULT); 42026003Smsmith PCMD(base, HACR_DEFAULT); 42126003Smsmith outw(PIOR1(base), 0); /* go to beginning of RAM */ 42226003Smsmith outsw(PIOP1(base), str, strlen(str)/2+1); /* write string */ 42326003Smsmith 42426003Smsmith outw(PIOR1(base), 0); /* rewind */ 42526003Smsmith insw(PIOP1(base), inbuf, strlen(str)/2+1); /* read result */ 42626003Smsmith 427113571Sjhay if (bcmp(str, inbuf, strlen(str))) { 428113571Sjhay error = ENXIO; 429113571Sjhay goto errexit; 430113571Sjhay } 43126003Smsmith 43227817Smsmith sc->chan24 = 0; /* 2.4 Gz: config channel */ 43327817Smsmith sc->freq24 = 0; /* 2.4 Gz: frequency */ 43427817Smsmith 43526003Smsmith /* read the PSA from the board into temporary storage */ 43626003Smsmith wlgetpsa(base, inbuf); 43726003Smsmith 43826003Smsmith /* We read the IRQ value from the PSA on the board. */ 43926003Smsmith for (irq = 15; irq >= 0; irq--) 44026003Smsmith if (irqvals[irq] == inbuf[WLPSA_IRQNO]) 44126003Smsmith break; 44226003Smsmith if ((irq == 0) || (irqvals[irq] == 0)){ 443113571Sjhay printf("wl%d: PSA corrupt (invalid IRQ value)\n", 444113571Sjhay device_get_unit(device)); 44526003Smsmith } else { 44626003Smsmith /* 44726003Smsmith * If the IRQ requested by the PSA is already claimed by another 44826003Smsmith * device, the board won't work, but the user can still access the 44926003Smsmith * driver to change the IRQ. 45026003Smsmith */ 451113571Sjhay if (bus_get_resource(device, SYS_RES_IRQ, 0, &sirq, &junk)) 452113571Sjhay goto errexit; 453113571Sjhay if (irq != (int)sirq) 454113571Sjhay printf("wl%d: board is configured for interrupt %d\n", 455113571Sjhay device_get_unit(device), irq); 45626003Smsmith } 457113571Sjhay wl_deallocate_resources(device); 458113571Sjhay return (0); 459113571Sjhay 460113571Sjhayerrexit: 461113571Sjhay wl_deallocate_resources(device); 462113571Sjhay return (error); 46326003Smsmith} 46426003Smsmith 46526003Smsmith/* 46626003Smsmith * wlattach: 46726003Smsmith * 46826003Smsmith * This function attaches a WaveLAN board to the "system". The rest of 46926003Smsmith * runtime structures are initialized here (this routine is called after 47026003Smsmith * a successful probe of the board). Once the ethernet address is read 47126003Smsmith * and stored, the board's ifnet structure is attached and readied. 47226003Smsmith * 47326003Smsmith * input : isa_dev structure setup in autoconfig 47426003Smsmith * output : board structs and ifnet is setup 47526003Smsmith * 47626003Smsmith */ 47726003Smsmithstatic int 478113571Sjhaywlattach(device_t device) 47926003Smsmith{ 480113571Sjhay struct wl_softc *sc; 481113571Sjhay short base; 482113571Sjhay int error, i, j; 483113571Sjhay int unit; 484113571Sjhay struct ifnet *ifp; 485147256Sbrooks u_char eaddr[6]; 48626003Smsmith 487113571Sjhay sc = device_get_softc(device); 488147256Sbrooks ifp = sc->ifp = if_alloc(IFT_ETHER); 489147256Sbrooks if (ifp == NULL) { 490147256Sbrooks device_printf(device, "can not if_alloc()\n"); 491147256Sbrooks return (ENOSPC); 492147256Sbrooks } 493113571Sjhay 494113572Sjhay mtx_init(&sc->wl_mtx, device_get_nameunit(device), MTX_NETWORK_LOCK, 495113572Sjhay MTX_DEF | MTX_RECURSE); 496113572Sjhay 497113571Sjhay error = wl_allocate_resources(device); 498113571Sjhay if (error) { 499113571Sjhay wl_deallocate_resources(device); 500113571Sjhay return (ENXIO); 501113571Sjhay } 502113571Sjhay 503113571Sjhay base = rman_get_start(sc->res_ioport); 504113571Sjhay unit = device_get_unit(device); 505113571Sjhay 50626003Smsmith#ifdef WLDEBUG 50726003Smsmith printf("wlattach: base %x, unit %d\n", base, unit); 50826003Smsmith#endif 509113571Sjhay 51026003Smsmith sc->base = base; 51126003Smsmith sc->unit = unit; 51226003Smsmith sc->flags = 0; 51326003Smsmith sc->mode = 0; 51426003Smsmith sc->hacr = HACR_RESET; 51529677Sgibbs callout_handle_init(&sc->watchdog_ch); 516113571Sjhay CMD(sc); /* reset the board */ 51726003Smsmith DELAY(DELAYCONST); /* >> 4 clocks at 6MHz */ 51826003Smsmith 51926003Smsmith /* clear reset command and set PIO#2 in parameter access mode */ 52026003Smsmith sc->hacr = (HACR_DEFAULT & ~HACR_16BITS); 521113571Sjhay CMD(sc); 52226003Smsmith 52326003Smsmith /* Read the PSA from the board for our later reference */ 52426003Smsmith wlgetpsa(base, sc->psa); 52526003Smsmith 52626003Smsmith /* fetch NWID */ 52726003Smsmith sc->nwid[0] = sc->psa[WLPSA_NWID]; 52826003Smsmith sc->nwid[1] = sc->psa[WLPSA_NWID+1]; 52926003Smsmith 53026003Smsmith /* fetch MAC address - decide which one first */ 53179082Simp if (sc->psa[WLPSA_MACSEL] & 1) 53226003Smsmith j = WLPSA_LOCALMAC; 53379082Simp else 53426003Smsmith j = WLPSA_UNIMAC; 53579082Simp for (i=0; i < WAVELAN_ADDR_SIZE; ++i) 536147256Sbrooks eaddr[i] = sc->psa[j + i]; 53726003Smsmith 53826003Smsmith /* enter normal 16 bit mode operation */ 53926003Smsmith sc->hacr = HACR_DEFAULT; 540113571Sjhay CMD(sc); 54126003Smsmith 542113571Sjhay wlinitmmc(sc); 54326003Smsmith outw(PIOR1(base), OFFSET_SCB + 8); /* address of scb_crcerrs */ 54426003Smsmith outw(PIOP1(base), 0); /* clear scb_crcerrs */ 54526003Smsmith outw(PIOP1(base), 0); /* clear scb_alnerrs */ 54626003Smsmith outw(PIOP1(base), 0); /* clear scb_rscerrs */ 54726003Smsmith outw(PIOP1(base), 0); /* clear scb_ovrnerrs */ 54826003Smsmith 54926003Smsmith ifp->if_softc = sc; 55026003Smsmith ifp->if_mtu = WAVELAN_MTU; 55126003Smsmith ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX; 55226003Smsmith#ifdef WLDEBUG 55326003Smsmith ifp->if_flags |= IFF_DEBUG; 55426003Smsmith#endif 55526003Smsmith#if MULTICAST 55626003Smsmith ifp->if_flags |= IFF_MULTICAST; 55741616Seivind#endif /* MULTICAST */ 558121816Sbrooks if_initname(ifp, device_get_name(device), device_get_unit(device)); 55926003Smsmith ifp->if_init = wlinit; 56026003Smsmith ifp->if_start = wlstart; 56126003Smsmith ifp->if_ioctl = wlioctl; 562207554Ssobomax ifp->if_snd.ifq_maxlen = ifqmaxlen; 56326003Smsmith /* no entries 56426003Smsmith ifp->if_done 56526003Smsmith ifp->if_reset 56626003Smsmith */ 567147256Sbrooks ether_ifattach(ifp, eaddr); 56826003Smsmith 569126966Smdodd if_printf(ifp, "NWID 0x%02x%02x", sc->nwid[0], sc->nwid[1]); 57027817Smsmith if (sc->freq24) 57127817Smsmith printf(", Freq %d MHz",sc->freq24); /* 2.4 Gz */ 57227817Smsmith printf("\n"); /* 2.4 Gz */ 57326003Smsmith 574166923Spiso bus_setup_intr(device, sc->res_irq, INTR_TYPE_NET, NULL, wlintr, sc, &sc->intr_cookie); 57527817Smsmith 57626003Smsmith if (bootverbose) 577113571Sjhay wldump(sc); 578320923Sjhb device_printf(device, 579320923Sjhb "WARNING: This driver is deprecated and will be removed.\n"); 580113571Sjhay return (0); 58126003Smsmith} 58226003Smsmith 583113571Sjhaystatic int 584113571Sjhaywldetach(device_t device) 585113571Sjhay{ 586113571Sjhay struct wl_softc *sc = device_get_softc(device); 587113571Sjhay device_t parent = device_get_parent(device); 588113571Sjhay struct ifnet *ifp; 589113571Sjhay 590147256Sbrooks ifp = sc->ifp; 591113571Sjhay ether_ifdetach(ifp); 592113571Sjhay 593113572Sjhay WL_LOCK(sc); 594113572Sjhay 595113571Sjhay /* reset the board */ 596113571Sjhay sc->hacr = HACR_RESET; 597113571Sjhay CMD(sc); 598113571Sjhay sc->hacr = HACR_DEFAULT; 599113571Sjhay CMD(sc); 600113571Sjhay 601113571Sjhay if (sc->intr_cookie != NULL) { 602113571Sjhay BUS_TEARDOWN_INTR(parent, device, sc->res_irq, sc->intr_cookie); 603113571Sjhay sc->intr_cookie = NULL; 604113571Sjhay } 605113571Sjhay 606113571Sjhay bus_generic_detach(device); 607113571Sjhay wl_deallocate_resources(device); 608113572Sjhay WL_UNLOCK(sc); 609150306Simp if_free(ifp); 610113572Sjhay mtx_destroy(&sc->wl_mtx); 611113571Sjhay return (0); 612113571Sjhay} 613113571Sjhay 614113571Sjhaystatic int 615113571Sjhaywl_allocate_resources(device_t device) 616113571Sjhay{ 617113571Sjhay struct wl_softc *sc = device_get_softc(device); 618113571Sjhay int ports = 16; /* Number of ports */ 619113571Sjhay 620113571Sjhay sc->res_ioport = bus_alloc_resource(device, SYS_RES_IOPORT, 621113571Sjhay &sc->rid_ioport, 0ul, ~0ul, ports, RF_ACTIVE); 622113571Sjhay if (sc->res_ioport == NULL) 623113571Sjhay goto errexit; 624113571Sjhay 625127135Snjl sc->res_irq = bus_alloc_resource_any(device, SYS_RES_IRQ, 626127135Snjl &sc->rid_irq, RF_SHAREABLE|RF_ACTIVE); 627113571Sjhay if (sc->res_irq == NULL) 628113571Sjhay goto errexit; 629113571Sjhay return (0); 630113571Sjhay 631113571Sjhayerrexit: 632113571Sjhay wl_deallocate_resources(device); 633113571Sjhay return (ENXIO); 634113571Sjhay} 635113571Sjhay 636113571Sjhaystatic int 637113571Sjhaywl_deallocate_resources(device_t device) 638113571Sjhay{ 639113571Sjhay struct wl_softc *sc = device_get_softc(device); 640113571Sjhay 641113571Sjhay if (sc->res_irq != 0) { 642113571Sjhay bus_release_resource(device, SYS_RES_IRQ, 643113571Sjhay sc->rid_irq, sc->res_irq); 644113571Sjhay sc->res_irq = 0; 645113571Sjhay } 646113571Sjhay if (sc->res_ioport != 0) { 647113571Sjhay bus_release_resource(device, SYS_RES_IOPORT, 648113571Sjhay sc->rid_ioport, sc->res_ioport); 649113571Sjhay sc->res_ioport = 0; 650113571Sjhay } 651113571Sjhay return (0); 652113571Sjhay} 653113571Sjhay 65426003Smsmith/* 65526003Smsmith * Print out interesting information about the 82596. 65626003Smsmith */ 65726003Smsmithstatic void 658113571Sjhaywldump(struct wl_softc *sc) 65926003Smsmith{ 660113571Sjhay int base = sc->base; 66126003Smsmith int i; 66226003Smsmith 66326003Smsmith printf("hasr %04x\n", inw(HASR(base))); 66426003Smsmith 66526003Smsmith printf("scb at %04x:\n ", OFFSET_SCB); 66626003Smsmith outw(PIOR1(base), OFFSET_SCB); 66779082Simp for (i = 0; i < 8; i++) 66826003Smsmith printf("%04x ", inw(PIOP1(base))); 66926003Smsmith printf("\n"); 67026003Smsmith 67126003Smsmith printf("cu at %04x:\n ", OFFSET_CU); 67226003Smsmith outw(PIOR1(base), OFFSET_CU); 67379082Simp for (i = 0; i < 8; i++) 67426003Smsmith printf("%04x ", inw(PIOP1(base))); 67526003Smsmith printf("\n"); 67626003Smsmith 67726003Smsmith printf("tbd at %04x:\n ", OFFSET_TBD); 67826003Smsmith outw(PIOR1(base), OFFSET_TBD); 67979082Simp for (i = 0; i < 4; i++) 68026003Smsmith printf("%04x ", inw(PIOP1(base))); 68126003Smsmith printf("\n"); 68226003Smsmith} 68326003Smsmith 68426003Smsmith/* Initialize the Modem Management Controller */ 68526003Smsmithstatic void 686113571Sjhaywlinitmmc(struct wl_softc *sc) 68726003Smsmith{ 688113571Sjhay int base = sc->base; 68926003Smsmith int configured; 690113571Sjhay int mode = sc->mode; 69127817Smsmith int i; /* 2.4 Gz */ 69226003Smsmith 69326003Smsmith /* enter 8 bit operation */ 694113571Sjhay sc->hacr = (HACR_DEFAULT & ~HACR_16BITS); 695113571Sjhay CMD(sc); 69626003Smsmith 697113571Sjhay configured = sc->psa[WLPSA_CONFIGURED] & 1; 69826003Smsmith 69926003Smsmith /* 70026003Smsmith * Set default modem control parameters. Taken from NCR document 70126003Smsmith * 407-0024326 Rev. A 70226003Smsmith */ 70326003Smsmith MMC_WRITE(MMC_JABBER_ENABLE, 0x01); 70426003Smsmith MMC_WRITE(MMC_ANTEN_SEL, 0x02); 70526003Smsmith MMC_WRITE(MMC_IFS, 0x20); 70626003Smsmith MMC_WRITE(MMC_MOD_DELAY, 0x04); 70726003Smsmith MMC_WRITE(MMC_JAM_TIME, 0x38); 70826003Smsmith MMC_WRITE(MMC_DECAY_PRM, 0x00); /* obsolete ? */ 70926003Smsmith MMC_WRITE(MMC_DECAY_UPDAT_PRM, 0x00); 71026003Smsmith if (!configured) { 71126003Smsmith MMC_WRITE(MMC_LOOPT_SEL, 0x00); 712113571Sjhay if (sc->psa[WLPSA_COMPATNO] & 1) { 71326003Smsmith MMC_WRITE(MMC_THR_PRE_SET, 0x01); /* 0x04 for AT and 0x01 for MCA */ 71426003Smsmith } else { 71526003Smsmith MMC_WRITE(MMC_THR_PRE_SET, 0x04); /* 0x04 for AT and 0x01 for MCA */ 71626003Smsmith } 71726003Smsmith MMC_WRITE(MMC_QUALITY_THR, 0x03); 71826003Smsmith } else { 71926003Smsmith /* use configuration defaults from parameter storage area */ 720113571Sjhay if (sc->psa[WLPSA_NWIDENABLE] & 1) { 72127817Smsmith if ((mode & (MOD_PROM | MOD_ENAL)) && wl_ignore_nwid) { 72226003Smsmith MMC_WRITE(MMC_LOOPT_SEL, 0x40); 72326003Smsmith } else { 72426003Smsmith MMC_WRITE(MMC_LOOPT_SEL, 0x00); 72526003Smsmith } 72626003Smsmith } else { 72726003Smsmith MMC_WRITE(MMC_LOOPT_SEL, 0x40); /* disable network id check */ 72826003Smsmith } 729113571Sjhay MMC_WRITE(MMC_THR_PRE_SET, sc->psa[WLPSA_THRESH]); 730113571Sjhay MMC_WRITE(MMC_QUALITY_THR, sc->psa[WLPSA_QUALTHRESH]); 73126003Smsmith } 73226003Smsmith MMC_WRITE(MMC_FREEZE, 0x00); 73326003Smsmith MMC_WRITE(MMC_ENCR_ENABLE, 0x00); 73426003Smsmith 735113571Sjhay MMC_WRITE(MMC_NETW_ID_L,sc->nwid[1]); /* set NWID */ 736113571Sjhay MMC_WRITE(MMC_NETW_ID_H,sc->nwid[0]); 73726003Smsmith 73826003Smsmith /* enter normal 16 bit mode operation */ 739113571Sjhay sc->hacr = HACR_DEFAULT; 740113571Sjhay CMD(sc); 741113571Sjhay CMD(sc); /* virtualpc1 needs this! */ 74227817Smsmith 743113571Sjhay if (sc->psa[WLPSA_COMPATNO]== /* 2.4 Gz: half-card ver */ 74427817Smsmith WLPSA_COMPATNO_WL24B) { /* 2.4 Gz */ 745113571Sjhay i=sc->chan24<<4; /* 2.4 Gz: position ch # */ 74627817Smsmith MMC_WRITE(MMC_EEADDR,i+0x0f); /* 2.4 Gz: named ch, wc=16 */ 74727817Smsmith MMC_WRITE(MMC_EECTRL,MMC_EECTRL_DWLD+ /* 2.4 Gz: Download Synths */ 74827817Smsmith MMC_EECTRL_EEOP_READ); /* 2.4 Gz: Read EEPROM */ 74927817Smsmith for (i=0; i<1000; ++i) { /* 2.4 Gz: wait for download */ 75027817Smsmith DELAY(40); /* 2.4 Gz */ 75127817Smsmith if ((wlmmcread(base,MMC_EECTRLstat) /* 2.4 Gz: check DWLD and */ 75227817Smsmith &(MMC_EECTRLstat_DWLD /* 2.4 Gz: EEBUSY */ 75327817Smsmith +MMC_EECTRLstat_EEBUSY))==0) /* 2.4 Gz: */ 75427817Smsmith break; /* 2.4 Gz: download finished */ 75527817Smsmith } /* 2.4 Gz */ 75627817Smsmith if (i==1000) printf("wl: synth load failed\n"); /* 2.4 Gz */ 75727817Smsmith MMC_WRITE(MMC_EEADDR,0x61); /* 2.4 Gz: default pwr, wc=2 */ 75827817Smsmith MMC_WRITE(MMC_EECTRL,MMC_EECTRL_DWLD+ /* 2.4 Gz: Download Xmit Pwr */ 75927817Smsmith MMC_EECTRL_EEOP_READ); /* 2.4 Gz: Read EEPROM */ 76027817Smsmith for (i=0; i<1000; ++i) { /* 2.4 Gz: wait for download */ 76127817Smsmith DELAY(40); /* 2.4 Gz */ 76227817Smsmith if ((wlmmcread(base,MMC_EECTRLstat) /* 2.4 Gz: check DWLD and */ 76327817Smsmith &(MMC_EECTRLstat_DWLD /* 2.4 Gz: EEBUSY */ 76427817Smsmith +MMC_EECTRLstat_EEBUSY))==0) /* 2.4 Gz: */ 76527817Smsmith break; /* 2.4 Gz: download finished */ 76627817Smsmith } /* 2.4 Gz */ 76727817Smsmith if (i==1000) printf("wl: xmit pwr load failed\n"); /* 2.4 Gz */ 76827817Smsmith MMC_WRITE(MMC_ANALCTRL, /* 2.4 Gz: EXT ant+polarity */ 76927817Smsmith MMC_ANALCTRL_ANTPOL + /* 2.4 Gz: */ 77027817Smsmith MMC_ANALCTRL_EXTANT); /* 2.4 Gz: */ 771113571Sjhay i=sc->chan24<<4; /* 2.4 Gz: position ch # */ 77227817Smsmith MMC_WRITE(MMC_EEADDR,i); /* 2.4 Gz: get frequency */ 77327817Smsmith MMC_WRITE(MMC_EECTRL, /* 2.4 Gz: EEPROM read */ 77427817Smsmith MMC_EECTRL_EEOP_READ); /* 2.4 Gz: */ 77527817Smsmith DELAY(40); /* 2.4 Gz */ 77627817Smsmith i = wlmmcread(base,MMC_EEDATALrv) /* 2.4 Gz: freq val */ 77727817Smsmith + (wlmmcread(base,MMC_EEDATAHrv)<<8); /* 2.4 Gz */ 778113571Sjhay sc->freq24 = (i>>6)+2400; /* 2.4 Gz: save real freq */ 77927817Smsmith } 78026003Smsmith} 78126003Smsmith 78226003Smsmith/* 78326003Smsmith * wlinit: 78426003Smsmith * 78526003Smsmith * Another routine that interfaces the "if" layer to this driver. 78626003Smsmith * Simply resets the structures that are used by "upper layers". 78726003Smsmith * As well as calling wlhwrst that does reset the WaveLAN board. 78826003Smsmith * 78926003Smsmith * input : softc pointer for this interface 79026003Smsmith * output : structures (if structs) and board are reset 79126003Smsmith * 79226003Smsmith */ 79326003Smsmithstatic void 79426003Smsmithwlinit(void *xsc) 79526003Smsmith{ 796113566Sjhay struct wl_softc *sc = xsc; 797147256Sbrooks struct ifnet *ifp = sc->ifp; 79826003Smsmith int stat; 799113601Sjhay u_long oldpri; 80026003Smsmith 80126003Smsmith#ifdef WLDEBUG 802147256Sbrooks if (sc->ifp->if_flags & IFF_DEBUG) 80326003Smsmith printf("wl%d: entered wlinit()\n",sc->unit); 80426003Smsmith#endif 805113605Sjhay WL_LOCK(sc); 806113601Sjhay oldpri = splimp(); 807113571Sjhay if ((stat = wlhwrst(sc)) == TRUE) { 808148887Srwatson sc->ifp->if_drv_flags |= IFF_DRV_RUNNING; /* same as DSF_RUNNING */ 80926003Smsmith /* 81026003Smsmith * OACTIVE is used by upper-level routines 81126003Smsmith * and must be set 81226003Smsmith */ 813148887Srwatson sc->ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; /* same as tbusy below */ 81426003Smsmith 81526003Smsmith sc->flags |= DSF_RUNNING; 81626003Smsmith sc->tbusy = 0; 81729677Sgibbs untimeout(wlwatchdog, sc, sc->watchdog_ch); 81826003Smsmith 81926003Smsmith wlstart(ifp); 82026003Smsmith } else { 82126003Smsmith printf("wl%d init(): trouble resetting board.\n", sc->unit); 82226003Smsmith } 823113601Sjhay splx(oldpri); 824113605Sjhay WL_UNLOCK(sc); 82526003Smsmith} 82626003Smsmith 82726003Smsmith/* 82826003Smsmith * wlhwrst: 82926003Smsmith * 83026003Smsmith * This routine resets the WaveLAN board that corresponds to the 83126003Smsmith * board number passed in. 83226003Smsmith * 83326003Smsmith * input : board number to do a hardware reset 83426003Smsmith * output : board is reset 83526003Smsmith * 83626003Smsmith */ 83726003Smsmithstatic int 838113571Sjhaywlhwrst(struct wl_softc *sc) 83926003Smsmith{ 84026003Smsmith 84126003Smsmith#ifdef WLDEBUG 842147256Sbrooks if (sc->ifp->if_flags & IFF_DEBUG) 843113571Sjhay printf("wl%d: entered wlhwrst()\n", sc->unit); 84426003Smsmith#endif 84526003Smsmith sc->hacr = HACR_RESET; 846113571Sjhay CMD(sc); /* reset the board */ 84726003Smsmith 84826003Smsmith /* clear reset command and set PIO#1 in autoincrement mode */ 84926003Smsmith sc->hacr = HACR_DEFAULT; 850113571Sjhay CMD(sc); 85126003Smsmith 85226003Smsmith#ifdef WLDEBUG 853147256Sbrooks if (sc->ifp->if_flags & IFF_DEBUG) 854113571Sjhay wlmmcstat(sc); /* Display MMC registers */ 85541616Seivind#endif /* WLDEBUG */ 856113571Sjhay wlbldcu(sc); /* set up command unit structures */ 85726003Smsmith 858113571Sjhay if (wldiag(sc) == 0) 85926003Smsmith return(0); 86026003Smsmith 861113571Sjhay if (wlconfig(sc) == 0) 86226003Smsmith return(0); 86326003Smsmith /* 86426003Smsmith * insert code for loopback test here 86526003Smsmith */ 866113571Sjhay wlrustrt(sc); /* start receive unit */ 86726003Smsmith 86826003Smsmith /* enable interrupts */ 86926003Smsmith sc->hacr = (HACR_DEFAULT | HACR_INTRON); 870113571Sjhay CMD(sc); 87126003Smsmith 87226003Smsmith return(1); 87326003Smsmith} 87426003Smsmith 87526003Smsmith/* 87626003Smsmith * wlbldcu: 87726003Smsmith * 87826003Smsmith * This function builds up the command unit structures. It inits 87926003Smsmith * the scp, iscp, scb, cb, tbd, and tbuf. 88026003Smsmith * 88126003Smsmith */ 88226003Smsmithstatic void 883113571Sjhaywlbldcu(struct wl_softc *sc) 88426003Smsmith{ 88526003Smsmith short base = sc->base; 88626003Smsmith scp_t scp; 88726003Smsmith iscp_t iscp; 88826003Smsmith scb_t scb; 88926003Smsmith ac_t cb; 89026003Smsmith tbd_t tbd; 891113571Sjhay int i; 89226003Smsmith 89326003Smsmith bzero(&scp, sizeof(scp)); 89426003Smsmith scp.scp_sysbus = 0; 89526003Smsmith scp.scp_iscp = OFFSET_ISCP; 89626003Smsmith scp.scp_iscp_base = 0; 89726003Smsmith outw(PIOR1(base), OFFSET_SCP); 89826003Smsmith outsw(PIOP1(base), &scp, sizeof(scp_t)/2); 89926003Smsmith 90026003Smsmith bzero(&iscp, sizeof(iscp)); 90126003Smsmith iscp.iscp_busy = 1; 90226003Smsmith iscp.iscp_scb_offset = OFFSET_SCB; 90326003Smsmith iscp.iscp_scb = 0; 90426003Smsmith iscp.iscp_scb_base = 0; 90526003Smsmith outw(PIOR1(base), OFFSET_ISCP); 90626003Smsmith outsw(PIOP1(base), &iscp, sizeof(iscp_t)/2); 90726003Smsmith 90826003Smsmith scb.scb_status = 0; 90926003Smsmith scb.scb_command = SCB_RESET; 91026003Smsmith scb.scb_cbl_offset = OFFSET_CU; 91126003Smsmith scb.scb_rfa_offset = OFFSET_RU; 91226003Smsmith scb.scb_crcerrs = 0; 91326003Smsmith scb.scb_alnerrs = 0; 91426003Smsmith scb.scb_rscerrs = 0; 91526003Smsmith scb.scb_ovrnerrs = 0; 91626003Smsmith outw(PIOR1(base), OFFSET_SCB); 91726003Smsmith outsw(PIOP1(base), &scb, sizeof(scb_t)/2); 91826003Smsmith 919113571Sjhay SET_CHAN_ATTN(sc); 92026003Smsmith 92126003Smsmith outw(PIOR0(base), OFFSET_ISCP + 0); /* address of iscp_busy */ 92279082Simp for (i = 1000000; inw(PIOP0(base)) && (i-- > 0); ) 92379082Simp continue; 92479082Simp if (i <= 0) 925113571Sjhay printf("wl%d bldcu(): iscp_busy timeout.\n", sc->unit); 92626003Smsmith outw(PIOR0(base), OFFSET_SCB + 0); /* address of scb_status */ 92726003Smsmith for (i = STATUS_TRIES; i-- > 0; ) { 92826003Smsmith if (inw(PIOP0(base)) == (SCB_SW_CX|SCB_SW_CNA)) 92926003Smsmith break; 93026003Smsmith } 93126003Smsmith if (i <= 0) 932113571Sjhay printf("wl%d bldcu(): not ready after reset.\n", sc->unit); 933113571Sjhay wlack(sc); 93426003Smsmith 93526003Smsmith cb.ac_status = 0; 93626003Smsmith cb.ac_command = AC_CW_EL; /* NOP */ 93726003Smsmith cb.ac_link_offset = OFFSET_CU; 93826003Smsmith outw(PIOR1(base), OFFSET_CU); 93926003Smsmith outsw(PIOP1(base), &cb, 6/2); 94026003Smsmith 94126003Smsmith tbd.act_count = 0; 94226003Smsmith tbd.next_tbd_offset = I82586NULL; 94326003Smsmith tbd.buffer_addr = 0; 94426003Smsmith tbd.buffer_base = 0; 94526003Smsmith outw(PIOR1(base), OFFSET_TBD); 94626003Smsmith outsw(PIOP1(base), &tbd, sizeof(tbd_t)/2); 94726003Smsmith} 94826003Smsmith 94926003Smsmith/* 95026003Smsmith * wlstart: 95126003Smsmith * 95226003Smsmith * send a packet 95326003Smsmith * 95426003Smsmith * input : board number 95526003Smsmith * output : stuff sent to board if any there 95626003Smsmith * 95726003Smsmith */ 95826003Smsmithstatic void 95926003Smsmithwlstart(struct ifnet *ifp) 96026003Smsmith{ 961113566Sjhay struct mbuf *m; 962113571Sjhay struct wl_softc *sc = ifp->if_softc; 963113566Sjhay short base = sc->base; 964113566Sjhay int scb_status, cu_status, scb_command; 96526003Smsmith 966113572Sjhay WL_LOCK(sc); 96726003Smsmith#ifdef WLDEBUG 968147256Sbrooks if (sc->ifp->if_flags & IFF_DEBUG) 969121816Sbrooks printf("%s: entered wlstart()\n", ifp->if_xname); 97026003Smsmith#endif 97126003Smsmith 97226003Smsmith outw(PIOR1(base), OFFSET_CU); 97326003Smsmith cu_status = inw(PIOP1(base)); 97426003Smsmith outw(PIOR0(base),OFFSET_SCB + 0); /* scb_status */ 97526003Smsmith scb_status = inw(PIOP0(base)); 97626003Smsmith outw(PIOR0(base), OFFSET_SCB + 2); 97726003Smsmith scb_command = inw(PIOP0(base)); 97826003Smsmith 97926003Smsmith /* 98026003Smsmith * don't need OACTIVE check as tbusy here checks to see 98126003Smsmith * if we are already busy 98226003Smsmith */ 98326003Smsmith if (sc->tbusy) { 98479082Simp if ((scb_status & 0x0700) == SCB_CUS_IDLE && 98526003Smsmith (cu_status & AC_SW_B) == 0){ 98626003Smsmith sc->tbusy = 0; 98729677Sgibbs untimeout(wlwatchdog, sc, sc->watchdog_ch); 988148887Srwatson sc->ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; 98926003Smsmith /* 99026003Smsmith * This is probably just a race. The xmt'r is just 99126003Smsmith * became idle but WE have masked interrupts so ... 99226003Smsmith */ 99326003Smsmith#ifdef WLDEBUG 994121816Sbrooks printf("%s: CU idle, scb %04x %04x cu %04x\n", 995121816Sbrooks ifp->if_xname, scb_status, scb_command, cu_status); 99626003Smsmith#endif 99726003Smsmith if (xmt_watch) printf("!!"); 99826003Smsmith } else { 999113572Sjhay WL_UNLOCK(sc); 100026003Smsmith return; /* genuinely still busy */ 100126003Smsmith } 100279082Simp } else if ((scb_status & 0x0700) == SCB_CUS_ACTV || 100379082Simp (cu_status & AC_SW_B)){ 100426003Smsmith#ifdef WLDEBUG 1005121816Sbrooks printf("%s: CU unexpectedly busy; scb %04x cu %04x\n", 1006121816Sbrooks ifp->if_xname, scb_status, cu_status); 100726003Smsmith#endif 1008121816Sbrooks if (xmt_watch) printf("%s: busy?!",ifp->if_xname); 1009113572Sjhay WL_UNLOCK(sc); 101026003Smsmith return; /* hey, why are we busy? */ 101126003Smsmith } 101226003Smsmith 101326003Smsmith /* get ourselves some data */ 1014147256Sbrooks ifp = sc->ifp; 101526003Smsmith IF_DEQUEUE(&ifp->if_snd, m); 101626003Smsmith if (m != (struct mbuf *)0) { 101726003Smsmith /* let BPF see it before we commit it */ 1018106937Ssam BPF_MTAP(ifp, m); 101926003Smsmith sc->tbusy++; 102026003Smsmith /* set the watchdog timer so that if the board 102126003Smsmith * fails to interrupt we will restart 102226003Smsmith */ 102326003Smsmith /* try 10 ticks, not very long */ 102429677Sgibbs sc->watchdog_ch = timeout(wlwatchdog, sc, 10); 1025148887Srwatson sc->ifp->if_drv_flags |= IFF_DRV_OACTIVE; 1026147256Sbrooks sc->ifp->if_opackets++; 1027113571Sjhay wlxmt(sc, m); 102826003Smsmith } else { 1029148887Srwatson sc->ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; 103026003Smsmith } 1031113572Sjhay WL_UNLOCK(sc); 103226003Smsmith return; 103326003Smsmith} 103426003Smsmith 103526003Smsmith/* 103626003Smsmith * wlread: 103726003Smsmith * 103826003Smsmith * This routine does the actual copy of data (including ethernet header 103926003Smsmith * structure) from the WaveLAN to an mbuf chain that will be passed up 104026003Smsmith * to the "if" (network interface) layer. NOTE: we currently 104126003Smsmith * don't handle trailer protocols, so if that is needed, it will 104226003Smsmith * (at least in part) be added here. For simplicities sake, this 104326003Smsmith * routine copies the receive buffers from the board into a local (stack) 104426003Smsmith * buffer until the frame has been copied from the board. Once in 104526003Smsmith * the local buffer, the contents are copied to an mbuf chain that 104626003Smsmith * is then enqueued onto the appropriate "if" queue. 104726003Smsmith * 1048108470Sschweikh * input : board number, and a frame descriptor address 104926003Smsmith * output : the packet is put into an mbuf chain, and passed up 105026003Smsmith * assumes : if any errors occur, packet is "dropped on the floor" 105126003Smsmith * 105226003Smsmith */ 105326003Smsmithstatic int 1054113571Sjhaywlread(struct wl_softc *sc, u_short fd_p) 105526003Smsmith{ 1056147256Sbrooks struct ifnet *ifp = sc->ifp; 1057113566Sjhay short base = sc->base; 1058113566Sjhay fd_t fd; 1059113566Sjhay struct ether_header *eh; 1060113566Sjhay struct mbuf *m; 1061113566Sjhay rbd_t rbd; 1062113566Sjhay u_char *mb_p; 1063113566Sjhay u_short mlen, len; 1064113566Sjhay u_short bytes_in_msg, bytes_in_mbuf, bytes; 106526003Smsmith 1066122689Ssam WL_LOCK_ASSERT(sc); 106726003Smsmith 106826003Smsmith#ifdef WLDEBUG 1069147256Sbrooks if (sc->ifp->if_flags & IFF_DEBUG) 1070113571Sjhay printf("wl%d: entered wlread()\n", sc->unit); 107126003Smsmith#endif 1072148887Srwatson if (!((ifp->if_flags & IFF_UP) && (ifp->if_drv_flags & IFF_DRV_RUNNING))) { 1073121816Sbrooks printf("%s read(): board is not running.\n", ifp->if_xname); 107426003Smsmith sc->hacr &= ~HACR_INTRON; 1075113571Sjhay CMD(sc); /* turn off interrupts */ 107626003Smsmith } 1077106937Ssam 1078106937Ssam /* 1079106937Ssam * Collect message size. 108026003Smsmith */ 108126003Smsmith outw(PIOR1(base), fd_p); 1082106937Ssam insw(PIOP1(base), &fd, sizeof(fd_t)/2); 108326003Smsmith if (fd.rbd_offset == I82586NULL) { 1084113571Sjhay if (wlhwrst(sc) != TRUE) { 1085106937Ssam sc->hacr &= ~HACR_INTRON; 1086113571Sjhay CMD(sc); /* turn off interrupts */ 1087113571Sjhay printf("wl%d read(): hwrst trouble.\n", sc->unit); 108826003Smsmith } 108926003Smsmith return 0; 109026003Smsmith } 109126003Smsmith 109226003Smsmith outw(PIOR1(base), fd.rbd_offset); 109326003Smsmith insw(PIOP1(base), &rbd, sizeof(rbd_t)/2); 109426003Smsmith bytes_in_msg = rbd.status & RBD_SW_COUNT; 1095106937Ssam 1096106937Ssam /* 1097106937Ssam * Allocate a cluster'd mbuf to receive the packet. 1098106937Ssam */ 1099243857Sglebius m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR); 1100106937Ssam if (m == NULL) { 1101113571Sjhay if (wlhwrst(sc) != TRUE) { 110226003Smsmith sc->hacr &= ~HACR_INTRON; 1103113571Sjhay CMD(sc); /* turn off interrupts */ 1104113571Sjhay printf("wl%d read(): hwrst trouble.\n", sc->unit); 110526003Smsmith } 110626003Smsmith return 0; 110726003Smsmith } 1108106937Ssam m->m_pkthdr.len = m->m_len = MCLBYTES; 1109106937Ssam m_adj(m, ETHER_ALIGN); /* align IP header */ 111027817Smsmith 1111106937Ssam /* 1112106937Ssam * Collect the message data. 111327817Smsmith */ 111426003Smsmith mlen = 0; 1115106937Ssam mb_p = mtod(m, u_char *); 1116113606Sjhay bytes_in_mbuf = m->m_len; 1117113606Sjhay 1118113606Sjhay /* Put the ethernet header inside the mbuf. */ 1119113606Sjhay bcopy(&fd.destination[0], mb_p, 14); 1120113606Sjhay mb_p += 14; 1121113606Sjhay mlen += 14; 1122113606Sjhay bytes_in_mbuf -= 14; 1123113606Sjhay 112426003Smsmith bytes = min(bytes_in_mbuf, bytes_in_msg); 112526003Smsmith for (;;) { 112626003Smsmith if (bytes & 1) { 112726003Smsmith len = bytes + 1; 112826003Smsmith } else { 112926003Smsmith len = bytes; 113026003Smsmith } 113126003Smsmith outw(PIOR1(base), rbd.buffer_addr); 113226003Smsmith insw(PIOP1(base), mb_p, len/2); 113326003Smsmith mlen += bytes; 113426003Smsmith 1135106937Ssam if (bytes > bytes_in_mbuf) { 1136106937Ssam /* XXX something wrong, a packet should fit in 1 cluster */ 1137106937Ssam m_freem(m); 1138106937Ssam printf("wl%d read(): packet too large (%u > %u)\n", 1139113571Sjhay sc->unit, bytes, bytes_in_mbuf); 1140113571Sjhay if (wlhwrst(sc) != TRUE) { 1141106937Ssam sc->hacr &= ~HACR_INTRON; 1142113571Sjhay CMD(sc); /* turn off interrupts */ 1143113571Sjhay printf("wl%d read(): hwrst trouble.\n", sc->unit); 114426003Smsmith } 1145106937Ssam return 0; 114626003Smsmith } 1147106937Ssam mb_p += bytes; 1148113606Sjhay bytes_in_mbuf -= bytes; 1149106937Ssam bytes_in_msg -= bytes; 1150106937Ssam if (bytes_in_msg == 0) { 1151106937Ssam if (rbd.status & RBD_SW_EOF || rbd.next_rbd_offset == I82586NULL) { 115226003Smsmith break; 115326003Smsmith } 1154106937Ssam outw(PIOR1(base), rbd.next_rbd_offset); 1155106937Ssam insw(PIOP1(base), &rbd, sizeof(rbd_t)/2); 1156106937Ssam bytes_in_msg = rbd.status & RBD_SW_COUNT; 115726003Smsmith } else { 115826003Smsmith rbd.buffer_addr += bytes; 115926003Smsmith } 116026003Smsmith 116126003Smsmith bytes = min(bytes_in_mbuf, bytes_in_msg); 116226003Smsmith } 116326003Smsmith 1164106937Ssam m->m_pkthdr.len = m->m_len = mlen; 1165106937Ssam m->m_pkthdr.rcvif = ifp; 116626003Smsmith 116726003Smsmith /* 116838447Smsmith * If hw is in promiscuous mode (note that I said hardware, not if 116938447Smsmith * IFF_PROMISC is set in ifnet flags), then if this is a unicast 117060536Sarchie * packet and the MAC dst is not us, drop it. This check in normally 117160536Sarchie * inside ether_input(), but IFF_MULTI causes hw promisc without 117238447Smsmith * a bpf listener, so this is wrong. 117338447Smsmith * Greg Troxel <gdt@ir.bbn.com>, 1998-08-07 117438447Smsmith */ 117538447Smsmith /* 117638447Smsmith * TBD: also discard packets where NWID does not match. 117738447Smsmith * However, there does not appear to be a way to read the nwid 117838447Smsmith * for a received packet. -gdt 1998-08-07 117938447Smsmith */ 1180106937Ssam /* XXX verify mbuf length */ 1181106937Ssam eh = mtod(m, struct ether_header *); 118238447Smsmith if ( 118338447Smsmith#ifdef WL_USE_IFNET_PROMISC_CHECK /* not defined */ 1184147256Sbrooks (sc->ifp->if_flags & (IFF_PROMISC|IFF_ALLMULTI)) 118538447Smsmith#else 118638447Smsmith /* hw is in promisc mode if this is true */ 118738447Smsmith (sc->mode & (MOD_PROM | MOD_ENAL)) 118838447Smsmith#endif 118938447Smsmith && 1190106937Ssam (eh->ether_dhost[0] & 1) == 0 && /* !mcast and !bcast */ 1191152315Sru bcmp(eh->ether_dhost, IF_LLADDR(sc->ifp), 1192106937Ssam sizeof(eh->ether_dhost)) != 0 ) { 119338447Smsmith m_freem(m); 119438447Smsmith return 1; 119538447Smsmith } 119626003Smsmith 119726003Smsmith#ifdef WLDEBUG 1198147256Sbrooks if (sc->ifp->if_flags & IFF_DEBUG) 1199113571Sjhay printf("wl%d: wlrecv %u bytes\n", sc->unit, mlen); 120026003Smsmith#endif 120126003Smsmith 120227817Smsmith#ifdef WLCACHE 1203113571Sjhay wl_cache_store(sc, base, eh, m); 120427817Smsmith#endif 120527817Smsmith 120626003Smsmith /* 120726003Smsmith * received packet is now in a chain of mbuf's. next step is 120826003Smsmith * to pass the packet upwards. 120926003Smsmith */ 1210122689Ssam WL_UNLOCK(sc); 1211106937Ssam (*ifp->if_input)(ifp, m); 1212122689Ssam WL_LOCK(sc); 121326003Smsmith return 1; 121426003Smsmith} 121526003Smsmith 121626003Smsmith/* 121726003Smsmith * wlioctl: 121826003Smsmith * 121926003Smsmith * This routine processes an ioctl request from the "if" layer 122026003Smsmith * above. 122126003Smsmith * 122226003Smsmith * input : pointer the appropriate "if" struct, command, and data 122326003Smsmith * output : based on command appropriate action is taken on the 122426003Smsmith * WaveLAN board(s) or related structures 122526003Smsmith * return : error is returned containing exit conditions 122626003Smsmith * 122726003Smsmith */ 122826003Smsmithstatic int 122936735Sdfrwlioctl(struct ifnet *ifp, u_long cmd, caddr_t data) 123026003Smsmith{ 1231113566Sjhay struct ifreq *ifr = (struct ifreq *)data; 1232113571Sjhay struct wl_softc *sc = ifp->if_softc; 123326003Smsmith short base = sc->base; 123426003Smsmith short mode = 0; 1235113601Sjhay int opri, error = 0; 123683366Sjulian struct thread *td = curthread; /* XXX */ 123779081Simp int irq, irqval, i, isroot; 123826003Smsmith caddr_t up; 123979081Simp#ifdef WLCACHE 124079081Simp int size; 124127817Smsmith char * cpt; 124279081Simp#endif 124326003Smsmith 1244113572Sjhay WL_LOCK(sc); 124526003Smsmith#ifdef WLDEBUG 1246147256Sbrooks if (sc->ifp->if_flags & IFF_DEBUG) 1247121816Sbrooks printf("%s: entered wlioctl()\n", ifp->if_xname); 124826003Smsmith#endif 1249113601Sjhay opri = splimp(); 125026003Smsmith switch (cmd) { 125126003Smsmith case SIOCSIFFLAGS: 125227817Smsmith if (ifp->if_flags & IFF_ALLMULTI) { 125326003Smsmith mode |= MOD_ENAL; 125427817Smsmith } 125527817Smsmith if (ifp->if_flags & IFF_PROMISC) { 125626003Smsmith mode |= MOD_PROM; 125727817Smsmith } 125879082Simp if (ifp->if_flags & IFF_LINK0) { 125926003Smsmith mode |= MOD_PROM; 126027817Smsmith } 126126003Smsmith /* 126226003Smsmith * force a complete reset if the recieve multicast/ 126326003Smsmith * promiscuous mode changes so that these take 126426003Smsmith * effect immediately. 126527817Smsmith * 126627817Smsmith */ 126726003Smsmith if (sc->mode != mode) { 126826003Smsmith sc->mode = mode; 126926003Smsmith if (sc->flags & DSF_RUNNING) { 127026003Smsmith sc->flags &= ~DSF_RUNNING; 127126003Smsmith wlinit(sc); 127226003Smsmith } 127326003Smsmith } 127426003Smsmith /* if interface is marked DOWN and still running then 127527817Smsmith * stop it. 127627817Smsmith */ 127726003Smsmith if ((ifp->if_flags & IFF_UP) == 0 && sc->flags & DSF_RUNNING) { 1278121816Sbrooks printf("%s ioctl(): board is not running\n", ifp->if_xname); 127926003Smsmith sc->flags &= ~DSF_RUNNING; 128026003Smsmith sc->hacr &= ~HACR_INTRON; 1281113571Sjhay CMD(sc); /* turn off interrupts */ 128226003Smsmith } 128326003Smsmith /* else if interface is UP and RUNNING, start it 128426003Smsmith */ 128526003Smsmith else if (ifp->if_flags & IFF_UP && (sc->flags & DSF_RUNNING) == 0) { 128626003Smsmith wlinit(sc); 128726003Smsmith } 128826003Smsmith 128926003Smsmith /* if WLDEBUG set on interface, then printf rf-modem regs 129027817Smsmith */ 129179082Simp if (ifp->if_flags & IFF_DEBUG) 1292113571Sjhay wlmmcstat(sc); 129326003Smsmith break; 129426003Smsmith#if MULTICAST 129526003Smsmith case SIOCADDMULTI: 129626003Smsmith case SIOCDELMULTI: 129727817Smsmith 129851309Sroberto wlinit(sc); 129926003Smsmith break; 130041616Seivind#endif /* MULTICAST */ 130126003Smsmith 130227817Smsmith /* DEVICE SPECIFIC */ 130327817Smsmith 130427817Smsmith 130526003Smsmith /* copy the PSA out to the caller */ 130626003Smsmith case SIOCGWLPSA: 130726003Smsmith /* pointer to buffer in user space */ 130826003Smsmith up = (void *)ifr->ifr_data; 130926003Smsmith /* work out if they're root */ 1310164033Srwatson isroot = (priv_check(td, PRIV_NET80211_GETKEY) == 0); 131126003Smsmith 131226003Smsmith for (i = 0; i < 0x40; i++) { 131326003Smsmith /* don't hand the DES key out to non-root users */ 131426003Smsmith if ((i > WLPSA_DESKEY) && (i < (WLPSA_DESKEY + 8)) && !isroot) 131526003Smsmith continue; 1316113572Sjhay if (subyte((up + i), sc->psa[i])) { 1317113572Sjhay WL_UNLOCK(sc); 131826003Smsmith return(EFAULT); 1319113572Sjhay } 132026003Smsmith } 132126003Smsmith break; 132226003Smsmith 132327817Smsmith 132426003Smsmith /* copy the PSA in from the caller; we only copy _some_ values */ 132526003Smsmith case SIOCSWLPSA: 132626003Smsmith /* root only */ 1327164033Srwatson if ((error = priv_check(td, PRIV_DRIVER))) 132826003Smsmith break; 132926003Smsmith error = EINVAL; /* assume the worst */ 133026003Smsmith /* pointer to buffer in user space containing data */ 133126003Smsmith up = (void *)ifr->ifr_data; 133226003Smsmith 133326003Smsmith /* check validity of input range */ 133426003Smsmith for (i = 0; i < 0x40; i++) 1335113572Sjhay if (fubyte(up + i) < 0) { 1336113572Sjhay WL_UNLOCK(sc); 133726003Smsmith return(EFAULT); 1338113572Sjhay } 133926003Smsmith 134026003Smsmith /* check IRQ value */ 134126003Smsmith irqval = fubyte(up+WLPSA_IRQNO); 134226003Smsmith for (irq = 15; irq >= 0; irq--) 134379082Simp if (irqvals[irq] == irqval) 134426003Smsmith break; 134526003Smsmith if (irq == 0) /* oops */ 134626003Smsmith break; 134726003Smsmith /* new IRQ */ 134826003Smsmith sc->psa[WLPSA_IRQNO] = irqval; 134926003Smsmith 135026003Smsmith /* local MAC */ 135126003Smsmith for (i = 0; i < 6; i++) 135226003Smsmith sc->psa[WLPSA_LOCALMAC+i] = fubyte(up+WLPSA_LOCALMAC+i); 135326003Smsmith 135426003Smsmith /* MAC select */ 135526003Smsmith sc->psa[WLPSA_MACSEL] = fubyte(up+WLPSA_MACSEL); 135626003Smsmith 135726003Smsmith /* default nwid */ 135826003Smsmith sc->psa[WLPSA_NWID] = fubyte(up+WLPSA_NWID); 135926003Smsmith sc->psa[WLPSA_NWID+1] = fubyte(up+WLPSA_NWID+1); 136026003Smsmith 136126003Smsmith error = 0; 1362113571Sjhay wlsetpsa(sc); /* update the PSA */ 136326003Smsmith break; 136426003Smsmith 136526003Smsmith 136626003Smsmith /* get the current NWID out of the sc since we stored it there */ 136726003Smsmith case SIOCGWLCNWID: 136826003Smsmith ifr->ifr_data = (caddr_t) (sc->nwid[0] << 8 | sc->nwid[1]); 136926003Smsmith break; 137026003Smsmith 137126003Smsmith 137226003Smsmith /* 137326003Smsmith * change the nwid dynamically. This 137426003Smsmith * ONLY changes the radio modem and does not 137526003Smsmith * change the PSA. 137626003Smsmith * 137726003Smsmith * 2 steps: 137826003Smsmith * 1. save in softc "soft registers" 137926003Smsmith * 2. save in radio modem (MMC) 138026003Smsmith */ 138126003Smsmith case SIOCSWLCNWID: 138226003Smsmith /* root only */ 1383164033Srwatson if ((error = priv_check(td, PRIV_DRIVER))) 138426003Smsmith break; 138526003Smsmith if (!(ifp->if_flags & IFF_UP)) { 138626003Smsmith error = EIO; /* only allowed while up */ 138726003Smsmith } else { 138826003Smsmith /* 138926003Smsmith * soft c nwid shadows radio modem setting 139026003Smsmith */ 139126003Smsmith sc->nwid[0] = (int)ifr->ifr_data >> 8; 139226003Smsmith sc->nwid[1] = (int)ifr->ifr_data & 0xff; 139326003Smsmith MMC_WRITE(MMC_NETW_ID_L,sc->nwid[1]); 139426003Smsmith MMC_WRITE(MMC_NETW_ID_H,sc->nwid[0]); 139526003Smsmith } 139626003Smsmith break; 139726003Smsmith 139827817Smsmith /* copy the EEPROM in 2.4 Gz WaveMODEM out to the caller */ 139927817Smsmith case SIOCGWLEEPROM: 140027817Smsmith /* root only */ 1401164033Srwatson if ((error = priv_check(td, PRIV_DRIVER))) 140227817Smsmith break; 140327817Smsmith /* pointer to buffer in user space */ 140427817Smsmith up = (void *)ifr->ifr_data; 140527817Smsmith 140627817Smsmith for (i=0x00; i<0x80; ++i) { /* 2.4 Gz: size of EEPROM */ 140727817Smsmith MMC_WRITE(MMC_EEADDR,i); /* 2.4 Gz: get frequency */ 140827817Smsmith MMC_WRITE(MMC_EECTRL, /* 2.4 Gz: EEPROM read */ 140927817Smsmith MMC_EECTRL_EEOP_READ); /* 2.4 Gz: */ 141027817Smsmith DELAY(40); /* 2.4 Gz */ 1411113572Sjhay if (subyte(up + 2*i, /* 2.4 Gz: pass low byte of */ 1412113572Sjhay wlmmcread(base,MMC_EEDATALrv))) {/* 2.4 Gz: EEPROM word */ 1413113572Sjhay WL_UNLOCK(sc); 1414113572Sjhay return(EFAULT); /* 2.4 Gz: */ 1415113572Sjhay } 141627817Smsmith if (subyte(up + 2*i+1, /* 2.4 Gz: pass hi byte of */ 1417113572Sjhay wlmmcread(base,MMC_EEDATALrv))) {/* 2.4 Gz: EEPROM word */ 1418113572Sjhay WL_UNLOCK(sc); 1419113572Sjhay return(EFAULT); /* 2.4 Gz: */ 1420113572Sjhay } 142127817Smsmith } 142227817Smsmith break; 142327817Smsmith 142427817Smsmith#ifdef WLCACHE 142527817Smsmith /* zero (Delete) the wl cache */ 142627817Smsmith case SIOCDWLCACHE: 142727817Smsmith /* root only */ 1428164033Srwatson if ((error = priv_check(td, PRIV_DRIVER))) 142927817Smsmith break; 1430113571Sjhay wl_cache_zero(sc); 143127817Smsmith break; 143227817Smsmith 143327817Smsmith /* read out the number of used cache elements */ 143427817Smsmith case SIOCGWLCITEM: 143527817Smsmith ifr->ifr_data = (caddr_t) sc->w_sigitems; 143627817Smsmith break; 143727817Smsmith 143827817Smsmith /* read out the wl cache */ 143927817Smsmith case SIOCGWLCACHE: 144027817Smsmith /* pointer to buffer in user space */ 144127817Smsmith up = (void *)ifr->ifr_data; 144227817Smsmith cpt = (char *) &sc->w_sigcache[0]; 144327817Smsmith size = sc->w_sigitems * sizeof(struct w_sigcache); 144427817Smsmith 144527817Smsmith for (i = 0; i < size; i++) { 1446113572Sjhay if (subyte((up + i), *cpt++)) { 1447113572Sjhay WL_UNLOCK(sc); 144827817Smsmith return(EFAULT); 1449113572Sjhay } 145027817Smsmith } 145127817Smsmith break; 145227817Smsmith#endif 145327817Smsmith 145426003Smsmith default: 1455106937Ssam error = ether_ioctl(ifp, cmd, data); 1456106937Ssam break; 145726003Smsmith } 1458113601Sjhay splx(opri); 1459113572Sjhay WL_UNLOCK(sc); 146026003Smsmith return (error); 146126003Smsmith} 146226003Smsmith 146326003Smsmith/* 146426003Smsmith * wlwatchdog(): 146526003Smsmith * 146626003Smsmith * Called if the timer set in wlstart expires before an interrupt is received 146726003Smsmith * from the wavelan. It seems to lose interrupts sometimes. 146826003Smsmith * The watchdog routine gets called if the transmitter failed to interrupt 146926003Smsmith * 147026003Smsmith * input : which board is timing out 147126003Smsmith * output : board reset 147226003Smsmith * 147326003Smsmith */ 147426003Smsmithstatic void 147527839Smsmithwlwatchdog(void *vsc) 147626003Smsmith{ 147727839Smsmith struct wl_softc *sc = vsc; 147826003Smsmith int unit = sc->unit; 147926003Smsmith 148026003Smsmith log(LOG_ERR, "wl%d: wavelan device timeout on xmit\n", unit); 1481113572Sjhay WL_LOCK(sc); 1482147256Sbrooks sc->ifp->if_oerrors++; 148326003Smsmith wlinit(sc); 1484113572Sjhay WL_UNLOCK(sc); 148526003Smsmith} 148626003Smsmith 148726003Smsmith/* 148826003Smsmith * wlintr: 148926003Smsmith * 149026003Smsmith * This function is the interrupt handler for the WaveLAN 149126003Smsmith * board. This routine will be called whenever either a packet 149226003Smsmith * is received, or a packet has successfully been transfered and 149326003Smsmith * the unit is ready to transmit another packet. 149426003Smsmith * 149526003Smsmith * input : board number that interrupted 149626003Smsmith * output : either a packet is received, or a packet is transfered 149726003Smsmith * 149826003Smsmith */ 149940565Sbdestatic void 1500113571Sjhaywlintr(void *arg) 150126003Smsmith{ 1502113571Sjhay struct wl_softc *sc = (struct wl_softc *)arg; 150326003Smsmith short base = sc->base; 150441591Sarchie int ac_status; 150526003Smsmith u_short int_type, int_type1; 150626003Smsmith 1507113572Sjhay WL_LOCK(sc); 150826003Smsmith#ifdef WLDEBUG 1509147256Sbrooks if (sc->ifp->if_flags & IFF_DEBUG) 1510113571Sjhay printf("wl%d: wlintr() called\n", sc->unit); 151126003Smsmith#endif 151226003Smsmith 151379082Simp if ((int_type = inw(HASR(base))) & HASR_MMC_INTR) { 151426003Smsmith /* handle interrupt from the modem management controler */ 151526003Smsmith /* This will clear the interrupt condition */ 151626003Smsmith (void) wlmmcread(base,MMC_DCE_STATUS); /* ignored for now */ 151726003Smsmith } 151826003Smsmith 151979082Simp if (!(int_type & HASR_INTR)){ /* return if no interrupt from 82586 */ 152026003Smsmith /* commented out. jrb. it happens when reinit occurs 152126003Smsmith printf("wlintr: int_type %x, dump follows\n", int_type); 152226003Smsmith wldump(unit); 152326003Smsmith */ 1524113572Sjhay WL_UNLOCK(sc); 152526003Smsmith return; 152626003Smsmith } 152726003Smsmith 152826003Smsmith if (gathersnr) 1529113571Sjhay getsnr(sc); 153079082Simp for (;;) { 153126003Smsmith outw(PIOR0(base), OFFSET_SCB + 0); /* get scb status */ 153226003Smsmith int_type = (inw(PIOP0(base)) & SCB_SW_INT); 153326003Smsmith if (int_type == 0) /* no interrupts left */ 153426003Smsmith break; 153526003Smsmith 1536113571Sjhay int_type1 = wlack(sc); /* acknowledge interrupt(s) */ 153726003Smsmith /* make sure no bits disappeared (others may appear) */ 153826003Smsmith if ((int_type & int_type1) != int_type) 153926003Smsmith printf("wlack() int bits disappeared : %04x != int_type %04x\n", 154026003Smsmith int_type1, int_type); 154126003Smsmith int_type = int_type1; /* go with the new status */ 154226003Smsmith /* 154326003Smsmith * incoming packet 154426003Smsmith */ 154526003Smsmith if (int_type & SCB_SW_FR) { 1546147256Sbrooks sc->ifp->if_ipackets++; 1547113571Sjhay wlrcv(sc); 154826003Smsmith } 154926003Smsmith /* 155026003Smsmith * receiver not ready 155126003Smsmith */ 155226003Smsmith if (int_type & SCB_SW_RNR) { 1553147256Sbrooks sc->ifp->if_ierrors++; 155426003Smsmith#ifdef WLDEBUG 1555147256Sbrooks if (sc->ifp->if_flags & IFF_DEBUG) 155626003Smsmith printf("wl%d intr(): receiver overrun! begin_fd = %x\n", 1557113571Sjhay sc->unit, sc->begin_fd); 155826003Smsmith#endif 1559113571Sjhay wlrustrt(sc); 156026003Smsmith } 156126003Smsmith /* 156226003Smsmith * CU not ready 156326003Smsmith */ 156426003Smsmith if (int_type & SCB_SW_CNA) { 156526003Smsmith /* 156626003Smsmith * At present, we don't care about CNA's. We 156726003Smsmith * believe they are a side effect of XMT. 156826003Smsmith */ 156926003Smsmith } 157026003Smsmith if (int_type & SCB_SW_CX) { 157126003Smsmith /* 157226003Smsmith * At present, we only request Interrupt for 157326003Smsmith * XMT. 157426003Smsmith */ 157526003Smsmith outw(PIOR1(base), OFFSET_CU); /* get command status */ 157626003Smsmith ac_status = inw(PIOP1(base)); 157726003Smsmith 157826003Smsmith if (xmt_watch) { /* report some anomalies */ 157926003Smsmith 158026003Smsmith if (sc->tbusy == 0) { 158126003Smsmith printf("wl%d: xmt intr but not busy, CU %04x\n", 1582113571Sjhay sc->unit, ac_status); 158326003Smsmith } 158426003Smsmith if (ac_status == 0) { 1585113571Sjhay printf("wl%d: xmt intr but ac_status == 0\n", sc->unit); 158626003Smsmith } 158726003Smsmith if (ac_status & AC_SW_A) { 1588113571Sjhay printf("wl%d: xmt aborted\n", sc->unit); 158926003Smsmith } 159026003Smsmith#ifdef notdef 159126003Smsmith if (ac_status & TC_CARRIER) { 1592113571Sjhay printf("wl%d: no carrier\n", sc->unit); 159326003Smsmith } 159441616Seivind#endif /* notdef */ 159526003Smsmith if (ac_status & TC_CLS) { 1596113571Sjhay printf("wl%d: no CTS\n", sc->unit); 159726003Smsmith } 159826003Smsmith if (ac_status & TC_DMA) { 1599113571Sjhay printf("wl%d: DMA underrun\n", sc->unit); 160026003Smsmith } 160126003Smsmith if (ac_status & TC_DEFER) { 1602113571Sjhay printf("wl%d: xmt deferred\n", sc->unit); 160326003Smsmith } 160426003Smsmith if (ac_status & TC_SQE) { 1605113571Sjhay printf("wl%d: heart beat\n", sc->unit); 160626003Smsmith } 160726003Smsmith if (ac_status & TC_COLLISION) { 1608113571Sjhay printf("wl%d: too many collisions\n", sc->unit); 160926003Smsmith } 161026003Smsmith } 161126003Smsmith /* if the transmit actually failed, or returned some status */ 161226003Smsmith if ((!(ac_status & AC_SW_OK)) || (ac_status & 0xfff)) { 161326003Smsmith if (ac_status & (TC_COLLISION | TC_CLS | TC_DMA)) { 1614147256Sbrooks sc->ifp->if_oerrors++; 161526003Smsmith } 161626003Smsmith /* count collisions */ 1617147256Sbrooks sc->ifp->if_collisions += (ac_status & 0xf); 161826003Smsmith /* if TC_COLLISION set and collision count zero, 16 collisions */ 161926003Smsmith if ((ac_status & 0x20) == 0x20) { 1620147256Sbrooks sc->ifp->if_collisions += 0x10; 162126003Smsmith } 162226003Smsmith } 162326003Smsmith sc->tbusy = 0; 162429677Sgibbs untimeout(wlwatchdog, sc, sc->watchdog_ch); 1625148887Srwatson sc->ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; 1626147256Sbrooks wlstart(sc->ifp); 162726003Smsmith } 162826003Smsmith } 1629113572Sjhay WL_UNLOCK(sc); 163026003Smsmith return; 163126003Smsmith} 163226003Smsmith 163326003Smsmith/* 163426003Smsmith * wlrcv: 163526003Smsmith * 163626003Smsmith * This routine is called by the interrupt handler to initiate a 163726003Smsmith * packet transfer from the board to the "if" layer above this 163826003Smsmith * driver. This routine checks if a buffer has been successfully 163926003Smsmith * received by the WaveLAN. If so, the routine wlread is called 164026003Smsmith * to do the actual transfer of the board data (including the 164126003Smsmith * ethernet header) into a packet (consisting of an mbuf chain). 164226003Smsmith * 164326003Smsmith * input : number of the board to check 164426003Smsmith * output : if a packet is available, it is "sent up" 164526003Smsmith * 164626003Smsmith */ 164726003Smsmithstatic void 1648113571Sjhaywlrcv(struct wl_softc *sc) 164926003Smsmith{ 165026003Smsmith short base = sc->base; 165126003Smsmith u_short fd_p, status, offset, link_offset; 165226003Smsmith 165326003Smsmith#ifdef WLDEBUG 1654147256Sbrooks if (sc->ifp->if_flags & IFF_DEBUG) 1655113571Sjhay printf("wl%d: entered wlrcv()\n", sc->unit); 165626003Smsmith#endif 165726003Smsmith for (fd_p = sc->begin_fd; fd_p != I82586NULL; fd_p = sc->begin_fd) { 165826003Smsmith 165926003Smsmith outw(PIOR0(base), fd_p + 0); /* address of status */ 166026003Smsmith status = inw(PIOP0(base)); 166126003Smsmith outw(PIOR1(base), fd_p + 4); /* address of link_offset */ 166226003Smsmith link_offset = inw(PIOP1(base)); 166326003Smsmith offset = inw(PIOP1(base)); /* rbd_offset */ 166426003Smsmith if (status == 0xffff || offset == 0xffff /*I82586NULL*/) { 1665113571Sjhay if (wlhwrst(sc) != TRUE) 1666113571Sjhay printf("wl%d rcv(): hwrst ffff trouble.\n", sc->unit); 166726003Smsmith return; 166826003Smsmith } else if (status & AC_SW_C) { 166926003Smsmith if (status == (RFD_DONE|RFD_RSC)) { 167026003Smsmith /* lost one */ 167126003Smsmith#ifdef WLDEBUG 1672147256Sbrooks if (sc->ifp->if_flags & IFF_DEBUG) 1673113571Sjhay printf("wl%d RCV: RSC %x\n", sc->unit, status); 167426003Smsmith#endif 1675147256Sbrooks sc->ifp->if_ierrors++; 167626003Smsmith } else if (!(status & RFD_OK)) { 1677113571Sjhay printf("wl%d RCV: !OK %x\n", sc->unit, status); 1678147256Sbrooks sc->ifp->if_ierrors++; 167926003Smsmith } else if (status & 0xfff) { /* can't happen */ 1680113571Sjhay printf("wl%d RCV: ERRs %x\n", sc->unit, status); 1681147256Sbrooks sc->ifp->if_ierrors++; 1682113571Sjhay } else if (!wlread(sc, fd_p)) 168326003Smsmith return; 168426003Smsmith 1685113571Sjhay if (!wlrequeue(sc, fd_p)) { 168626003Smsmith /* abort on chain error */ 1687113571Sjhay if (wlhwrst(sc) != TRUE) 1688113571Sjhay printf("wl%d rcv(): hwrst trouble.\n", sc->unit); 168926003Smsmith return; 169026003Smsmith } 169126003Smsmith sc->begin_fd = link_offset; 169226003Smsmith } else { 169326003Smsmith break; 169426003Smsmith } 169526003Smsmith } 169626003Smsmith return; 169726003Smsmith} 169826003Smsmith 169926003Smsmith/* 170026003Smsmith * wlrequeue: 170126003Smsmith * 170226003Smsmith * This routine puts rbd's used in the last receive back onto the 170326003Smsmith * free list for the next receive. 170426003Smsmith * 170526003Smsmith */ 170626003Smsmithstatic int 1707113571Sjhaywlrequeue(struct wl_softc *sc, u_short fd_p) 170826003Smsmith{ 170926003Smsmith short base = sc->base; 171026003Smsmith fd_t fd; 171126003Smsmith u_short l_rbdp, f_rbdp, rbd_offset; 171226003Smsmith 171326003Smsmith outw(PIOR0(base), fd_p + 6); 171426003Smsmith rbd_offset = inw(PIOP0(base)); 171526003Smsmith if ((f_rbdp = rbd_offset) != I82586NULL) { 171626003Smsmith l_rbdp = f_rbdp; 171779082Simp for (;;) { 171826003Smsmith outw(PIOR0(base), l_rbdp + 0); /* address of status */ 171979082Simp if (inw(PIOP0(base)) & RBD_SW_EOF) 172026003Smsmith break; 172126003Smsmith outw(PIOP0(base), 0); 172226003Smsmith outw(PIOR0(base), l_rbdp + 2); /* next_rbd_offset */ 172379082Simp if ((l_rbdp = inw(PIOP0(base))) == I82586NULL) 172426003Smsmith break; 172526003Smsmith } 172626003Smsmith outw(PIOP0(base), 0); 172726003Smsmith outw(PIOR0(base), l_rbdp + 2); /* next_rbd_offset */ 172826003Smsmith outw(PIOP0(base), I82586NULL); 172926003Smsmith outw(PIOR0(base), l_rbdp + 8); /* address of size */ 173026003Smsmith outw(PIOP0(base), inw(PIOP0(base)) | AC_CW_EL); 173126003Smsmith outw(PIOR0(base), sc->end_rbd + 2); 173226003Smsmith outw(PIOP0(base), f_rbdp); /* end_rbd->next_rbd_offset */ 173326003Smsmith outw(PIOR0(base), sc->end_rbd + 8); /* size */ 173426003Smsmith outw(PIOP0(base), inw(PIOP0(base)) & ~AC_CW_EL); 173526003Smsmith sc->end_rbd = l_rbdp; 173626003Smsmith } 173726003Smsmith 173826003Smsmith fd.status = 0; 173926003Smsmith fd.command = AC_CW_EL; 174026003Smsmith fd.link_offset = I82586NULL; 174126003Smsmith fd.rbd_offset = I82586NULL; 174226003Smsmith outw(PIOR1(base), fd_p); 174326003Smsmith outsw(PIOP1(base), &fd, 8/2); 174426003Smsmith 174526003Smsmith outw(PIOR1(base), sc->end_fd + 2); /* addr of command */ 174626003Smsmith outw(PIOP1(base), 0); /* command = 0 */ 174726003Smsmith outw(PIOP1(base), fd_p); /* end_fd->link_offset = fd_p */ 174826003Smsmith sc->end_fd = fd_p; 174926003Smsmith 175026003Smsmith return 1; 175126003Smsmith} 175226003Smsmith 175326003Smsmith#ifdef WLDEBUG 175426003Smsmithstatic int xmt_debug = 0; 175541616Seivind#endif /* WLDEBUG */ 175626003Smsmith 175726003Smsmith/* 175826003Smsmith * wlxmt: 175926003Smsmith * 176026003Smsmith * This routine fills in the appropriate registers and memory 176126003Smsmith * locations on the WaveLAN board and starts the board off on 176226003Smsmith * the transmit. 176326003Smsmith * 1764121816Sbrooks * input : pointers to board of interest's softc and the mbuf 176526003Smsmith * output : board memory and registers are set for xfer and attention 176626003Smsmith * 176726003Smsmith */ 176826003Smsmithstatic void 1769113571Sjhaywlxmt(struct wl_softc *sc, struct mbuf *m) 177026003Smsmith{ 1771113566Sjhay u_short xmtdata_p = OFFSET_TBUF; 1772113566Sjhay u_short xmtshort_p; 1773113566Sjhay struct mbuf *tm_p = m; 1774113566Sjhay struct ether_header *eh_p = mtod(m, struct ether_header *); 1775113566Sjhay u_char *mb_p = mtod(m, u_char *) + sizeof(struct ether_header); 1776113566Sjhay u_short count = m->m_len - sizeof(struct ether_header); 1777113566Sjhay ac_t cb; 1778113566Sjhay u_short tbd_p = OFFSET_TBD; 1779113566Sjhay u_short len, clen = 0; 1780113566Sjhay short base = sc->base; 1781113566Sjhay int spin; 178226003Smsmith 178326003Smsmith#ifdef WLDEBUG 1784147256Sbrooks if (sc->ifp->if_flags & IFF_DEBUG) 1785147256Sbrooks printf("%s: entered wlxmt()\n", sc->ifp->if_xname); 178626003Smsmith#endif 178726003Smsmith 178826003Smsmith cb.ac_status = 0; 178926003Smsmith cb.ac_command = (AC_CW_EL|AC_TRANSMIT|AC_CW_I); 179026003Smsmith cb.ac_link_offset = I82586NULL; 179126003Smsmith outw(PIOR1(base), OFFSET_CU); 179226003Smsmith outsw(PIOP1(base), &cb, 6/2); 179326003Smsmith outw(PIOP1(base), OFFSET_TBD); /* cb.cmd.transmit.tbd_offset */ 179426003Smsmith outsw(PIOP1(base), eh_p->ether_dhost, WAVELAN_ADDR_SIZE/2); 179526003Smsmith outw(PIOP1(base), eh_p->ether_type); 179626003Smsmith 179726003Smsmith#ifdef WLDEBUG 1798147256Sbrooks if (sc->ifp->if_flags & IFF_DEBUG) { 179926003Smsmith if (xmt_debug) { 180038505Sbde printf("XMT mbuf: L%d @%p ", count, (void *)mb_p); 180126003Smsmith printf("ether type %x\n", eh_p->ether_type); 180226003Smsmith } 180326003Smsmith } 180441616Seivind#endif /* WLDEBUG */ 180526003Smsmith outw(PIOR0(base), OFFSET_TBD); 180626003Smsmith outw(PIOP0(base), 0); /* act_count */ 180726003Smsmith outw(PIOR1(base), OFFSET_TBD + 4); 180826003Smsmith outw(PIOP1(base), xmtdata_p); /* buffer_addr */ 180926003Smsmith outw(PIOP1(base), 0); /* buffer_base */ 181026003Smsmith for (;;) { 181126003Smsmith if (count) { 181226003Smsmith if (clen + count > WAVELAN_MTU) 181326003Smsmith break; 181426003Smsmith if (count & 1) 181526003Smsmith len = count + 1; 181626003Smsmith else 181726003Smsmith len = count; 181826003Smsmith outw(PIOR1(base), xmtdata_p); 181926003Smsmith outsw(PIOP1(base), mb_p, len/2); 182026003Smsmith clen += count; 182126003Smsmith outw(PIOR0(base), tbd_p); /* address of act_count */ 182226003Smsmith outw(PIOP0(base), inw(PIOP0(base)) + count); 182326003Smsmith xmtdata_p += len; 182426003Smsmith if ((tm_p = tm_p->m_next) == (struct mbuf *)0) 182526003Smsmith break; 182626003Smsmith if (count & 1) { 182726003Smsmith /* go to the next descriptor */ 182826003Smsmith outw(PIOR0(base), tbd_p + 2); 182926003Smsmith tbd_p += sizeof (tbd_t); 183026003Smsmith outw(PIOP0(base), tbd_p); /* next_tbd_offset */ 183126003Smsmith outw(PIOR0(base), tbd_p); 183226003Smsmith outw(PIOP0(base), 0); /* act_count */ 183326003Smsmith outw(PIOR1(base), tbd_p + 4); 183426003Smsmith outw(PIOP1(base), xmtdata_p); /* buffer_addr */ 183526003Smsmith outw(PIOP1(base), 0); /* buffer_base */ 183626003Smsmith /* at the end -> coallesce remaining mbufs */ 183726003Smsmith if (tbd_p == OFFSET_TBD + (N_TBD-1) * sizeof (tbd_t)) { 1838113571Sjhay wlsftwsleaze(&count, &mb_p, &tm_p, sc); 183926003Smsmith continue; 184026003Smsmith } 184126003Smsmith /* next mbuf short -> coallesce as needed */ 184226003Smsmith if ( (tm_p->m_next == (struct mbuf *) 0) || 184326003Smsmith#define HDW_THRESHOLD 55 184426003Smsmith tm_p->m_len > HDW_THRESHOLD) 184526003Smsmith /* ok */; 184626003Smsmith else { 1847113571Sjhay wlhdwsleaze(&count, &mb_p, &tm_p, sc); 184826003Smsmith continue; 184926003Smsmith } 185026003Smsmith } 185126003Smsmith } else if ((tm_p = tm_p->m_next) == (struct mbuf *)0) 185226003Smsmith break; 185326003Smsmith count = tm_p->m_len; 185426003Smsmith mb_p = mtod(tm_p, u_char *); 185526003Smsmith#ifdef WLDEBUG 1856147256Sbrooks if (sc->ifp->if_flags & IFF_DEBUG) 185726003Smsmith if (xmt_debug) 185838505Sbde printf("mbuf+ L%d @%p ", count, (void *)mb_p); 185941616Seivind#endif /* WLDEBUG */ 186026003Smsmith } 186126003Smsmith#ifdef WLDEBUG 1862147256Sbrooks if (sc->ifp->if_flags & IFF_DEBUG) 186326003Smsmith if (xmt_debug) 186426003Smsmith printf("CLEN = %d\n", clen); 186541616Seivind#endif /* WLDEBUG */ 186626003Smsmith outw(PIOR0(base), tbd_p); 186726003Smsmith if (clen < ETHERMIN) { 186826003Smsmith outw(PIOP0(base), inw(PIOP0(base)) + ETHERMIN - clen); 186926003Smsmith outw(PIOR1(base), xmtdata_p); 187026003Smsmith for (xmtshort_p = xmtdata_p; clen < ETHERMIN; clen += 2) 187126003Smsmith outw(PIOP1(base), 0); 187226003Smsmith } 187326003Smsmith outw(PIOP0(base), inw(PIOP0(base)) | TBD_SW_EOF); 187426003Smsmith outw(PIOR0(base), tbd_p + 2); 187526003Smsmith outw(PIOP0(base), I82586NULL); 187626003Smsmith#ifdef WLDEBUG 1877147256Sbrooks if (sc->ifp->if_flags & IFF_DEBUG) { 187826003Smsmith if (xmt_debug) { 1879113571Sjhay wltbd(sc); 188026003Smsmith printf("\n"); 188126003Smsmith } 188226003Smsmith } 188341616Seivind#endif /* WLDEBUG */ 188426003Smsmith 188526003Smsmith outw(PIOR0(base), OFFSET_SCB + 2); /* address of scb_command */ 188626003Smsmith /* 188726003Smsmith * wait for 586 to clear previous command, complain if it takes 188826003Smsmith * too long 188926003Smsmith */ 189026003Smsmith for (spin = 1;;spin = (spin + 1) % 10000) { 189126003Smsmith if (inw(PIOP0(base)) == 0) { /* it's done, we can go */ 189226003Smsmith break; 189326003Smsmith } 189426003Smsmith if ((spin == 0) && xmt_watch) { /* not waking up, and we care */ 1895147256Sbrooks printf("%s: slow accepting xmit\n", sc->ifp->if_xname); 189626003Smsmith } 189726003Smsmith } 189826003Smsmith outw(PIOP0(base), SCB_CU_STRT); /* new command */ 1899113571Sjhay SET_CHAN_ATTN(sc); 190026003Smsmith 190126003Smsmith m_freem(m); 190226003Smsmith 190326003Smsmith /* XXX 190426003Smsmith * Pause to avoid transmit overrun problems. 190526003Smsmith * The required delay tends to vary with platform type, and may be 190626003Smsmith * related to interrupt loss. 190726003Smsmith */ 190826003Smsmith if (wl_xmit_delay) { 190926003Smsmith DELAY(wl_xmit_delay); 191026003Smsmith } 191126003Smsmith return; 191226003Smsmith} 191326003Smsmith 191426003Smsmith/* 191526003Smsmith * wlbldru: 191626003Smsmith * 191726003Smsmith * This function builds the linear linked lists of fd's and 191826003Smsmith * rbd's. Based on page 4-32 of 1986 Intel microcom handbook. 191926003Smsmith * 192026003Smsmith */ 192126003Smsmithstatic u_short 1922113571Sjhaywlbldru(struct wl_softc *sc) 192326003Smsmith{ 192426003Smsmith short base = sc->base; 192526003Smsmith fd_t fd; 192626003Smsmith rbd_t rbd; 192726003Smsmith u_short fd_p = OFFSET_RU; 192826003Smsmith u_short rbd_p = OFFSET_RBD; 192926003Smsmith int i; 193026003Smsmith 193126003Smsmith sc->begin_fd = fd_p; 193279082Simp for (i = 0; i < N_FD; i++) { 193326003Smsmith fd.status = 0; 193426003Smsmith fd.command = 0; 193526003Smsmith fd.link_offset = fd_p + sizeof(fd_t); 193626003Smsmith fd.rbd_offset = I82586NULL; 193726003Smsmith outw(PIOR1(base), fd_p); 193826003Smsmith outsw(PIOP1(base), &fd, 8/2); 193926003Smsmith fd_p = fd.link_offset; 194026003Smsmith } 194126003Smsmith fd_p -= sizeof(fd_t); 194226003Smsmith sc->end_fd = fd_p; 194326003Smsmith outw(PIOR1(base), fd_p + 2); 194426003Smsmith outw(PIOP1(base), AC_CW_EL); /* command */ 194526003Smsmith outw(PIOP1(base), I82586NULL); /* link_offset */ 194626003Smsmith fd_p = OFFSET_RU; 194726003Smsmith 194826003Smsmith outw(PIOR0(base), fd_p + 6); /* address of rbd_offset */ 194926003Smsmith outw(PIOP0(base), rbd_p); 195026003Smsmith outw(PIOR1(base), rbd_p); 195179082Simp for (i = 0; i < N_RBD; i++) { 195226003Smsmith rbd.status = 0; 195326003Smsmith rbd.buffer_addr = rbd_p + sizeof(rbd_t) + 2; 195426003Smsmith rbd.buffer_base = 0; 195526003Smsmith rbd.size = RCVBUFSIZE; 195626003Smsmith if (i != N_RBD-1) { 195726003Smsmith rbd_p += sizeof(ru_t); 195826003Smsmith rbd.next_rbd_offset = rbd_p; 195926003Smsmith } else { 196026003Smsmith rbd.next_rbd_offset = I82586NULL; 196126003Smsmith rbd.size |= AC_CW_EL; 196226003Smsmith sc->end_rbd = rbd_p; 196326003Smsmith } 196426003Smsmith outsw(PIOP1(base), &rbd, sizeof(rbd_t)/2); 196526003Smsmith outw(PIOR1(base), rbd_p); 196626003Smsmith } 196726003Smsmith return sc->begin_fd; 196826003Smsmith} 196926003Smsmith 197026003Smsmith/* 197126003Smsmith * wlrustrt: 197226003Smsmith * 197326003Smsmith * This routine starts the receive unit running. First checks if the 197426003Smsmith * board is actually ready, then the board is instructed to receive 197526003Smsmith * packets again. 197626003Smsmith * 197726003Smsmith */ 197826003Smsmithstatic void 1979113571Sjhaywlrustrt(struct wl_softc *sc) 198026003Smsmith{ 198126003Smsmith short base = sc->base; 198226003Smsmith u_short rfa; 198326003Smsmith 198426003Smsmith#ifdef WLDEBUG 1985147256Sbrooks if (sc->ifp->if_flags & IFF_DEBUG) 1986113571Sjhay printf("wl%d: entered wlrustrt()\n", sc->unit); 198726003Smsmith#endif 198826003Smsmith outw(PIOR0(base), OFFSET_SCB); 198926003Smsmith if (inw(PIOP0(base)) & SCB_RUS_READY){ 199026003Smsmith printf("wlrustrt: RUS_READY\n"); 199126003Smsmith return; 199226003Smsmith } 199326003Smsmith 199426003Smsmith outw(PIOR0(base), OFFSET_SCB + 2); 199526003Smsmith outw(PIOP0(base), SCB_RU_STRT); /* command */ 1996113571Sjhay rfa = wlbldru(sc); 199726003Smsmith outw(PIOR0(base), OFFSET_SCB + 6); /* address of scb_rfa_offset */ 199826003Smsmith outw(PIOP0(base), rfa); 199926003Smsmith 2000113571Sjhay SET_CHAN_ATTN(sc); 200126003Smsmith return; 200226003Smsmith} 200326003Smsmith 200426003Smsmith/* 200526003Smsmith * wldiag: 200626003Smsmith * 200726003Smsmith * This routine does a 586 op-code number 7, and obtains the 200826003Smsmith * diagnose status for the WaveLAN. 200926003Smsmith * 201026003Smsmith */ 201126003Smsmithstatic int 2012113571Sjhaywldiag(struct wl_softc *sc) 201326003Smsmith{ 2014113571Sjhay short base = sc->base; 2015113571Sjhay short status; 201626003Smsmith 201726003Smsmith#ifdef WLDEBUG 2018147256Sbrooks if (sc->ifp->if_flags & IFF_DEBUG) 2019113571Sjhay printf("wl%d: entered wldiag()\n", sc->unit); 202026003Smsmith#endif 202126003Smsmith outw(PIOR0(base), OFFSET_SCB); 202226003Smsmith status = inw(PIOP0(base)); 202326003Smsmith if (status & SCB_SW_INT) { 202426003Smsmith /* state is 2000 which seems ok 202526003Smsmith printf("wl%d diag(): unexpected initial state %\n", 2026113571Sjhay sc->unit, inw(PIOP0(base))); 202726003Smsmith */ 2028113571Sjhay wlack(sc); 202926003Smsmith } 203026003Smsmith outw(PIOR1(base), OFFSET_CU); 203126003Smsmith outw(PIOP1(base), 0); /* ac_status */ 203226003Smsmith outw(PIOP1(base), AC_DIAGNOSE|AC_CW_EL);/* ac_command */ 2033113571Sjhay if (wlcmd(sc, "diag()") == 0) 203426003Smsmith return 0; 203526003Smsmith outw(PIOR0(base), OFFSET_CU); 203626003Smsmith if (inw(PIOP0(base)) & 0x0800) { 2037113571Sjhay printf("wl%d: i82586 Self Test failed!\n", sc->unit); 203826003Smsmith return 0; 203926003Smsmith } 204026003Smsmith return TRUE; 204126003Smsmith} 204226003Smsmith 204326003Smsmith/* 204426003Smsmith * wlconfig: 204526003Smsmith * 204626003Smsmith * This routine does a standard config of the WaveLAN board. 204726003Smsmith * 204826003Smsmith */ 204926003Smsmithstatic int 2050113571Sjhaywlconfig(struct wl_softc *sc) 205126003Smsmith{ 2052113571Sjhay configure_t configure; 205326003Smsmith short base = sc->base; 205426003Smsmith 205526003Smsmith#if MULTICAST 205626114Speter struct ifmultiaddr *ifma; 205726114Speter u_char *addrp; 205827817Smsmith int cnt = 0; 205941616Seivind#endif /* MULTICAST */ 206026003Smsmith 206126003Smsmith#ifdef WLDEBUG 2062147256Sbrooks if (sc->ifp->if_flags & IFF_DEBUG) 2063113571Sjhay printf("wl%d: entered wlconfig()\n", sc->unit); 206426003Smsmith#endif 206526003Smsmith outw(PIOR0(base), OFFSET_SCB); 206626003Smsmith if (inw(PIOP0(base)) & SCB_SW_INT) { 206726003Smsmith /* 206826003Smsmith printf("wl%d config(): unexpected initial state %x\n", 2069113571Sjhay sc->unit, inw(PIOP0(base))); 207026003Smsmith */ 207126003Smsmith } 2072113571Sjhay wlack(sc); 207326003Smsmith 207426003Smsmith outw(PIOR1(base), OFFSET_CU); 207526003Smsmith outw(PIOP1(base), 0); /* ac_status */ 207626003Smsmith outw(PIOP1(base), AC_CONFIGURE|AC_CW_EL); /* ac_command */ 207726003Smsmith 207826003Smsmith/* jrb hack */ 207926003Smsmith configure.fifolim_bytecnt = 0x080c; 208026003Smsmith configure.addrlen_mode = 0x0600; 208126003Smsmith configure.linprio_interframe = 0x2060; 208226003Smsmith configure.slot_time = 0xf200; 208326003Smsmith configure.hardware = 0x0008; /* tx even w/o CD */ 208426003Smsmith configure.min_frame_len = 0x0040; 208526003Smsmith#if 0 208626003Smsmith /* This is the configuration block suggested by Marc Meertens 208726003Smsmith * <mmeerten@obelix.utrecht.NCR.COM> in an e-mail message to John 208826003Smsmith * Ioannidis on 10 Nov 92. 208926003Smsmith */ 209026003Smsmith configure.fifolim_bytecnt = 0x040c; 209126003Smsmith configure.addrlen_mode = 0x0600; 209226003Smsmith configure.linprio_interframe = 0x2060; 209326003Smsmith configure.slot_time = 0xf000; 209426003Smsmith configure.hardware = 0x0008; /* tx even w/o CD */ 209526003Smsmith configure.min_frame_len = 0x0040; 209626003Smsmith#else 209726003Smsmith /* 209826003Smsmith * below is the default board configuration from p2-28 from 586 book 209926003Smsmith */ 210026003Smsmith configure.fifolim_bytecnt = 0x080c; 210126003Smsmith configure.addrlen_mode = 0x2600; 210226003Smsmith configure.linprio_interframe = 0x7820; /* IFS=120, ACS=2 */ 210326003Smsmith configure.slot_time = 0xf00c; /* slottime=12 */ 210426003Smsmith configure.hardware = 0x0008; /* tx even w/o CD */ 210526003Smsmith configure.min_frame_len = 0x0040; 210626003Smsmith#endif 210779082Simp if (sc->mode & (MOD_PROM | MOD_ENAL)) 210826003Smsmith configure.hardware |= 1; 210926003Smsmith outw(PIOR1(base), OFFSET_CU + 6); 211026003Smsmith outsw(PIOP1(base), &configure, sizeof(configure_t)/2); 211126003Smsmith 2112113571Sjhay if (wlcmd(sc, "config()-configure") == 0) 211326003Smsmith return 0; 211426003Smsmith#if MULTICAST 211526003Smsmith outw(PIOR1(base), OFFSET_CU); 211626003Smsmith outw(PIOP1(base), 0); /* ac_status */ 211726003Smsmith outw(PIOP1(base), AC_MCSETUP|AC_CW_EL); /* ac_command */ 211826003Smsmith outw(PIOR1(base), OFFSET_CU + 8); 2119195049Srwatson if_maddr_rlock(sc->ifp); 2120147256Sbrooks TAILQ_FOREACH(ifma, &sc->ifp->if_multiaddrs, ifma_link) { 212126114Speter if (ifma->ifma_addr->sa_family != AF_LINK) 212226114Speter continue; 212327817Smsmith 212426114Speter addrp = LLADDR((struct sockaddr_dl *)ifma->ifma_addr); 212527817Smsmith outw(PIOP1(base), addrp[0] + (addrp[1] << 8)); 212627817Smsmith outw(PIOP1(base), addrp[2] + (addrp[3] << 8)); 212727817Smsmith outw(PIOP1(base), addrp[4] + (addrp[5] << 8)); 212827817Smsmith ++cnt; 212926114Speter } 2130195049Srwatson if_maddr_runlock(sc->ifp); 213126003Smsmith outw(PIOR1(base), OFFSET_CU + 6); /* mc-cnt */ 213226003Smsmith outw(PIOP1(base), cnt * WAVELAN_ADDR_SIZE); 2133113571Sjhay if (wlcmd(sc, "config()-mcaddress") == 0) 213426003Smsmith return 0; 213541616Seivind#endif /* MULTICAST */ 213626003Smsmith 213726003Smsmith outw(PIOR1(base), OFFSET_CU); 213826003Smsmith outw(PIOP1(base), 0); /* ac_status */ 213926003Smsmith outw(PIOP1(base), AC_IASETUP|AC_CW_EL); /* ac_command */ 214026003Smsmith outw(PIOR1(base), OFFSET_CU + 6); 2141152315Sru outsw(PIOP1(base), IF_LLADDR(sc->ifp), WAVELAN_ADDR_SIZE/2); 214226003Smsmith 2143113571Sjhay if (wlcmd(sc, "config()-address") == 0) 214426003Smsmith return(0); 214526003Smsmith 2146113571Sjhay wlinitmmc(sc); 214726003Smsmith 214826003Smsmith return(1); 214926003Smsmith} 215026003Smsmith 215126003Smsmith/* 215226003Smsmith * wlcmd: 215326003Smsmith * 215426003Smsmith * Set channel attention bit and busy wait until command has 215526003Smsmith * completed. Then acknowledge the command completion. 215626003Smsmith */ 215726003Smsmithstatic int 2158113571Sjhaywlcmd(struct wl_softc *sc, char *str) 215926003Smsmith{ 216026003Smsmith short base = sc->base; 216126003Smsmith int i; 216226003Smsmith 216326003Smsmith outw(PIOR0(base), OFFSET_SCB + 2); /* address of scb_command */ 216426003Smsmith outw(PIOP0(base), SCB_CU_STRT); 216526003Smsmith 2166113571Sjhay SET_CHAN_ATTN(sc); 216726003Smsmith 216826003Smsmith outw(PIOR0(base), OFFSET_CU); 216979082Simp for (i = 0; i < 0xffff; i++) 217026003Smsmith if (inw(PIOP0(base)) & AC_SW_C) 217126003Smsmith break; 217226003Smsmith if (i == 0xffff || !(inw(PIOP0(base)) & AC_SW_OK)) { 217326003Smsmith printf("wl%d: %s failed; status = %d, inw = %x, outw = %x\n", 2174113571Sjhay sc->unit, str, inw(PIOP0(base)) & AC_SW_OK, inw(PIOP0(base)), inw(PIOR0(base))); 217526003Smsmith outw(PIOR0(base), OFFSET_SCB); 217626003Smsmith printf("scb_status %x\n", inw(PIOP0(base))); 217726003Smsmith outw(PIOR0(base), OFFSET_SCB+2); 217826003Smsmith printf("scb_command %x\n", inw(PIOP0(base))); 217926003Smsmith outw(PIOR0(base), OFFSET_SCB+4); 218026003Smsmith printf("scb_cbl %x\n", inw(PIOP0(base))); 218126003Smsmith outw(PIOR0(base), OFFSET_CU+2); 218226003Smsmith printf("cu_cmd %x\n", inw(PIOP0(base))); 218326003Smsmith return(0); 218426003Smsmith } 218526003Smsmith 218626003Smsmith outw(PIOR0(base), OFFSET_SCB); 218726003Smsmith if ((inw(PIOP0(base)) & SCB_SW_INT) && (inw(PIOP0(base)) != SCB_SW_CNA)) { 218826003Smsmith /* 218926003Smsmith printf("wl%d %s: unexpected final state %x\n", 2190113571Sjhay sc->unit, str, inw(PIOP0(base))); 219126003Smsmith */ 219226003Smsmith } 2193113571Sjhay wlack(sc); 219426003Smsmith return(TRUE); 219526003Smsmith} 219626003Smsmith 219726003Smsmith/* 219826003Smsmith * wlack: if the 82596 wants attention because it has finished 219926003Smsmith * sending or receiving a packet, acknowledge its desire and 220026003Smsmith * return bits indicating the kind of attention. wlack() returns 220126003Smsmith * these bits so that the caller can service exactly the 220226003Smsmith * conditions that wlack() acknowledged. 220326003Smsmith */ 220426003Smsmithstatic int 2205113571Sjhaywlack(struct wl_softc *sc) 220626003Smsmith{ 220726003Smsmith int i; 2208113566Sjhay u_short cmd; 220926003Smsmith short base = sc->base; 221026003Smsmith 221126003Smsmith outw(PIOR1(base), OFFSET_SCB); 221279082Simp if (!(cmd = (inw(PIOP1(base)) & SCB_SW_INT))) 221326003Smsmith return(0); 221426003Smsmith#ifdef WLDEBUG 2215147256Sbrooks if (sc->ifp->if_flags & IFF_DEBUG) 2216113571Sjhay printf("wl%d: doing a wlack()\n", sc->unit); 221726003Smsmith#endif 221826003Smsmith outw(PIOP1(base), cmd); 2219113571Sjhay SET_CHAN_ATTN(sc); 222026003Smsmith outw(PIOR0(base), OFFSET_SCB + 2); /* address of scb_command */ 222179082Simp for (i = 1000000; inw(PIOP0(base)) && (i-- > 0); ) 222279082Simp continue; 222326003Smsmith if (i < 1) 2224113571Sjhay printf("wl%d wlack(): board not accepting command.\n", sc->unit); 222526003Smsmith return(cmd); 222626003Smsmith} 222726003Smsmith 222879081Simp#ifdef WLDEBUG 222926003Smsmithstatic void 2230113571Sjhaywltbd(struct wl_softc *sc) 223126003Smsmith{ 223226003Smsmith short base = sc->base; 223326003Smsmith u_short tbd_p = OFFSET_TBD; 223426003Smsmith tbd_t tbd; 223526003Smsmith int i = 0; 223626003Smsmith int sum = 0; 223726003Smsmith 223826003Smsmith for (;;) { 223926003Smsmith outw(PIOR1(base), tbd_p); 224026003Smsmith insw(PIOP1(base), &tbd, sizeof(tbd_t)/2); 224126003Smsmith sum += (tbd.act_count & ~TBD_SW_EOF); 224226003Smsmith printf("%d: addr %x, count %d (%d), next %x, base %x\n", 224326003Smsmith i++, tbd.buffer_addr, 224426003Smsmith (tbd.act_count & ~TBD_SW_EOF), sum, 224526003Smsmith tbd.next_tbd_offset, tbd.buffer_base); 224626003Smsmith if (tbd.act_count & TBD_SW_EOF) 224726003Smsmith break; 224826003Smsmith tbd_p = tbd.next_tbd_offset; 224926003Smsmith } 225026003Smsmith} 225179081Simp#endif 225226003Smsmith 225326003Smsmithstatic void 2254113571Sjhaywlhdwsleaze(u_short *countp, u_char **mb_pp, struct mbuf **tm_pp, struct wl_softc *sc) 225526003Smsmith{ 225626003Smsmith struct mbuf *tm_p = *tm_pp; 2257113571Sjhay u_char *mb_p = *mb_pp; 2258113571Sjhay u_short count = 0; 2259113571Sjhay u_char *cp; 226026003Smsmith int len; 226126003Smsmith 226226003Smsmith /* 226326003Smsmith * can we get a run that will be coallesced or 226426003Smsmith * that terminates before breaking 226526003Smsmith */ 226626003Smsmith do { 226726003Smsmith count += tm_p->m_len; 226826003Smsmith if (tm_p->m_len & 1) 226926003Smsmith break; 227026003Smsmith } while ((tm_p = tm_p->m_next) != (struct mbuf *)0); 227126003Smsmith if ( (tm_p == (struct mbuf *)0) || 227226003Smsmith count > HDW_THRESHOLD) { 227326003Smsmith *countp = (*tm_pp)->m_len; 227426003Smsmith *mb_pp = mtod((*tm_pp), u_char *); 227526003Smsmith return; 227626003Smsmith } 227726003Smsmith 227826003Smsmith /* we need to copy */ 227926003Smsmith tm_p = *tm_pp; 228026003Smsmith mb_p = *mb_pp; 228126003Smsmith count = 0; 228226003Smsmith cp = (u_char *) t_packet; 228326003Smsmith for (;;) { 228426003Smsmith bcopy(mtod(tm_p, u_char *), cp, len = tm_p->m_len); 228526003Smsmith count += len; 228626003Smsmith if (count > HDW_THRESHOLD) 228726003Smsmith break; 228826003Smsmith cp += len; 228926003Smsmith if (tm_p->m_next == (struct mbuf *)0) 229026003Smsmith break; 229126003Smsmith tm_p = tm_p->m_next; 229226003Smsmith } 229326003Smsmith *countp = count; 229426003Smsmith *mb_pp = (u_char *) t_packet; 229526003Smsmith *tm_pp = tm_p; 229626003Smsmith return; 229726003Smsmith} 229826003Smsmith 229926003Smsmith 230026003Smsmithstatic void 2301113571Sjhaywlsftwsleaze(u_short *countp, u_char **mb_pp, struct mbuf **tm_pp, struct wl_softc *sc) 230226003Smsmith{ 230326003Smsmith struct mbuf *tm_p = *tm_pp; 2304113571Sjhay u_short count = 0; 2305113571Sjhay u_char *cp = (u_char *) t_packet; 2306113571Sjhay int len; 230726003Smsmith 230826003Smsmith /* we need to copy */ 230926003Smsmith for (;;) { 231026003Smsmith bcopy(mtod(tm_p, u_char *), cp, len = tm_p->m_len); 231126003Smsmith count += len; 231226003Smsmith cp += len; 231326003Smsmith if (tm_p->m_next == (struct mbuf *)0) 231426003Smsmith break; 231526003Smsmith tm_p = tm_p->m_next; 231626003Smsmith } 231726003Smsmith 231826003Smsmith *countp = count; 231926003Smsmith *mb_pp = (u_char *) t_packet; 232026003Smsmith *tm_pp = tm_p; 232126003Smsmith return; 232226003Smsmith} 232326003Smsmith 232426003Smsmithstatic void 2325113571Sjhaywlmmcstat(struct wl_softc *sc) 232626003Smsmith{ 232726003Smsmith short base = sc->base; 232826003Smsmith u_short tmp; 232926003Smsmith 2330113571Sjhay printf("wl%d: DCE_STATUS: 0x%x, ", sc->unit, 233126003Smsmith wlmmcread(base,MMC_DCE_STATUS) & 0x0f); 233226003Smsmith tmp = wlmmcread(base,MMC_CORRECT_NWID_H) << 8; 233326003Smsmith tmp |= wlmmcread(base,MMC_CORRECT_NWID_L); 233426003Smsmith printf("Correct NWID's: %d, ", tmp); 233526003Smsmith tmp = wlmmcread(base,MMC_WRONG_NWID_H) << 8; 233626003Smsmith tmp |= wlmmcread(base,MMC_WRONG_NWID_L); 233726003Smsmith printf("Wrong NWID's: %d\n", tmp); 233826003Smsmith printf("THR_PRE_SET: 0x%x, ", wlmmcread(base,MMC_THR_PRE_SET)); 233926003Smsmith printf("SIGNAL_LVL: %d, SILENCE_LVL: %d\n", 234026003Smsmith wlmmcread(base,MMC_SIGNAL_LVL), 234126003Smsmith wlmmcread(base,MMC_SILENCE_LVL)); 234226003Smsmith printf("SIGN_QUAL: 0x%x, NETW_ID: %x:%x, DES: %d\n", 234326003Smsmith wlmmcread(base,MMC_SIGN_QUAL), 234426003Smsmith wlmmcread(base,MMC_NETW_ID_H), 234526003Smsmith wlmmcread(base,MMC_NETW_ID_L), 234626003Smsmith wlmmcread(base,MMC_DES_AVAIL)); 234726003Smsmith} 234826003Smsmith 234926003Smsmithstatic u_short 235026003Smsmithwlmmcread(u_int base, u_short reg) 235126003Smsmith{ 235279082Simp while (inw(HASR(base)) & HASR_MMC_BUSY) 235379082Simp continue; 235426003Smsmith outw(MMCR(base),reg << 1); 235579082Simp while (inw(HASR(base)) & HASR_MMC_BUSY) 235679082Simp continue; 235726003Smsmith return (u_short)inw(MMCR(base)) >> 8; 235826003Smsmith} 235926003Smsmith 236026003Smsmithstatic void 2361113571Sjhaygetsnr(struct wl_softc *sc) 236226003Smsmith{ 236326003Smsmith MMC_WRITE(MMC_FREEZE,1); 236426003Smsmith /* 236526003Smsmith * SNR retrieval procedure : 236626003Smsmith * 236726003Smsmith * read signal level : wlmmcread(base, MMC_SIGNAL_LVL); 236826003Smsmith * read silence level : wlmmcread(base, MMC_SILENCE_LVL); 236926003Smsmith */ 237026003Smsmith MMC_WRITE(MMC_FREEZE,0); 237126003Smsmith /* 237226003Smsmith * SNR is signal:silence ratio. 237326003Smsmith */ 237426003Smsmith} 237526003Smsmith 237626003Smsmith/* 237726003Smsmith** wlgetpsa 237826003Smsmith** 237926003Smsmith** Reads the psa for the wavelan at (base) into (buf) 238026003Smsmith*/ 238126003Smsmithstatic void 238226003Smsmithwlgetpsa(int base, u_char *buf) 238326003Smsmith{ 238426003Smsmith int i; 238526003Smsmith 238626003Smsmith PCMD(base, HACR_DEFAULT & ~HACR_16BITS); 238726003Smsmith PCMD(base, HACR_DEFAULT & ~HACR_16BITS); 238826003Smsmith 238926003Smsmith for (i = 0; i < 0x40; i++) { 239026003Smsmith outw(PIOR2(base), i); 239126003Smsmith buf[i] = inb(PIOP2(base)); 239226003Smsmith } 239326003Smsmith PCMD(base, HACR_DEFAULT); 239426003Smsmith PCMD(base, HACR_DEFAULT); 239526003Smsmith} 239626003Smsmith 239726003Smsmith/* 239826003Smsmith** wlsetpsa 239926003Smsmith** 240026003Smsmith** Writes the psa for wavelan (unit) from the softc back to the 240126003Smsmith** board. Updates the CRC and sets the CRC OK flag. 240226003Smsmith** 240326003Smsmith** Do not call this when the board is operating, as it doesn't 240426003Smsmith** preserve the hacr. 240526003Smsmith*/ 240626003Smsmithstatic void 2407113571Sjhaywlsetpsa(struct wl_softc *sc) 240826003Smsmith{ 240926003Smsmith short base = sc->base; 2410113601Sjhay int i, oldpri; 241126003Smsmith u_short crc; 241226003Smsmith 241326003Smsmith crc = wlpsacrc(sc->psa); /* calculate CRC of PSA */ 241426003Smsmith sc->psa[WLPSA_CRCLOW] = crc & 0xff; 241526003Smsmith sc->psa[WLPSA_CRCHIGH] = (crc >> 8) & 0xff; 241626003Smsmith sc->psa[WLPSA_CRCOK] = 0x55; /* default to 'bad' until programming complete */ 241726003Smsmith 2418113601Sjhay oldpri = splimp(); /* ick, long pause */ 2419113601Sjhay 242026003Smsmith PCMD(base, HACR_DEFAULT & ~HACR_16BITS); 242126003Smsmith PCMD(base, HACR_DEFAULT & ~HACR_16BITS); 242226003Smsmith 242326003Smsmith for (i = 0; i < 0x40; i++) { 242426003Smsmith DELAY(DELAYCONST); 242526003Smsmith outw(PIOR2(base),i); /* write param memory */ 242626003Smsmith DELAY(DELAYCONST); 242726003Smsmith outb(PIOP2(base), sc->psa[i]); 242826003Smsmith } 242926003Smsmith DELAY(DELAYCONST); 243026003Smsmith outw(PIOR2(base),WLPSA_CRCOK); /* update CRC flag*/ 243126003Smsmith DELAY(DELAYCONST); 243226003Smsmith sc->psa[WLPSA_CRCOK] = 0xaa; /* OK now */ 243326003Smsmith outb(PIOP2(base), 0xaa); /* all OK */ 243426003Smsmith DELAY(DELAYCONST); 243526003Smsmith 243626003Smsmith PCMD(base, HACR_DEFAULT); 243726003Smsmith PCMD(base, HACR_DEFAULT); 2438113601Sjhay 2439113601Sjhay splx(oldpri); 244026003Smsmith} 244126003Smsmith 244226003Smsmith/* 244326003Smsmith** CRC routine provided by Christopher Giordano <cgiordan@gdeb.com>, 244426003Smsmith** from original code by Tomi Mikkonen (tomitm@remedy.fi) 244526003Smsmith*/ 244626003Smsmith 244726003Smsmithstatic u_int crc16_table[16] = { 244826003Smsmith 0x0000, 0xCC01, 0xD801, 0x1400, 244926003Smsmith 0xF001, 0x3C00, 0x2800, 0xE401, 245026003Smsmith 0xA001, 0x6C00, 0x7800, 0xB401, 245126003Smsmith 0x5000, 0x9C01, 0x8801, 0x4400 245226003Smsmith}; 245326003Smsmith 245426003Smsmithstatic u_short 245526003Smsmithwlpsacrc(u_char *buf) 245626003Smsmith{ 245726003Smsmith u_short crc = 0; 245826003Smsmith int i, r1; 245926003Smsmith 246026003Smsmith for (i = 0; i < 0x3d; i++, buf++) { 246126003Smsmith /* lower 4 bits */ 246226003Smsmith r1 = crc16_table[crc & 0xF]; 246326003Smsmith crc = (crc >> 4) & 0x0FFF; 246426003Smsmith crc = crc ^ r1 ^ crc16_table[*buf & 0xF]; 246526003Smsmith 246626003Smsmith /* upper 4 bits */ 246726003Smsmith r1 = crc16_table[crc & 0xF]; 246826003Smsmith crc = (crc >> 4) & 0x0FFF; 246926003Smsmith crc = crc ^ r1 ^ crc16_table[(*buf >> 4) & 0xF]; 247026003Smsmith } 247126003Smsmith return(crc); 247226003Smsmith} 247327817Smsmith#ifdef WLCACHE 247427817Smsmith 247527817Smsmith/* 247627817Smsmith * wl_cache_store 247727817Smsmith * 247827817Smsmith * take input packet and cache various radio hw characteristics 247927817Smsmith * indexed by MAC address. 248027817Smsmith * 248127817Smsmith * Some things to think about: 248227817Smsmith * note that no space is malloced. 248327817Smsmith * We might hash the mac address if the cache were bigger. 248427817Smsmith * It is not clear that the cache is big enough. 248527817Smsmith * It is also not clear how big it should be. 248627817Smsmith * The cache is IP-specific. We don't care about that as 248727817Smsmith * we want it to be IP-specific. 248827817Smsmith * The last N recv. packets are saved. This will tend 248927817Smsmith * to reward agents and mobile hosts that beacon. 249027817Smsmith * That is probably fine for mobile ip. 249127817Smsmith */ 249227817Smsmith 249327817Smsmith/* globals for wavelan signal strength cache */ 249427817Smsmith/* this should go into softc structure above. 249527817Smsmith*/ 249627817Smsmith 249727817Smsmith/* set true if you want to limit cache items to broadcast/mcast 249827817Smsmith * only packets (not unicast) 249927817Smsmith */ 250027817Smsmithstatic int wl_cache_mcastonly = 1; 250127817SmsmithSYSCTL_INT(_machdep, OID_AUTO, wl_cache_mcastonly, CTLFLAG_RW, 250227817Smsmith &wl_cache_mcastonly, 0, ""); 250327817Smsmith 250427817Smsmith/* set true if you want to limit cache items to IP packets only 250527817Smsmith*/ 250627817Smsmithstatic int wl_cache_iponly = 1; 250727817SmsmithSYSCTL_INT(_machdep, OID_AUTO, wl_cache_iponly, CTLFLAG_RW, 250827817Smsmith &wl_cache_iponly, 0, ""); 250927817Smsmith 251027817Smsmith/* zero out the cache 251127817Smsmith*/ 251227817Smsmithstatic void 2513113571Sjhaywl_cache_zero(struct wl_softc *sc) 251427817Smsmith{ 251527817Smsmith 251627817Smsmith bzero(&sc->w_sigcache[0], sizeof(struct w_sigcache) * MAXCACHEITEMS); 251727817Smsmith sc->w_sigitems = 0; 251827817Smsmith sc->w_nextcache = 0; 251927817Smsmith sc->w_wrapindex = 0; 252027817Smsmith} 252127817Smsmith 252227817Smsmith/* store hw signal info in cache. 252327817Smsmith * index is MAC address, but an ip src gets stored too 252427817Smsmith * There are two filters here controllable via sysctl: 252527817Smsmith * throw out unicast (on by default, but can be turned off) 252627817Smsmith * throw out non-ip (on by default, but can be turned off) 252727817Smsmith */ 252827817Smsmithstatic 2529113571Sjhayvoid wl_cache_store (struct wl_softc *sc, int base, struct ether_header *eh, 253027817Smsmith struct mbuf *m) 253127817Smsmith{ 2532113571Sjhay#ifdef INET 253342546Seivind struct ip *ip = NULL; /* Avoid GCC warning */ 253427817Smsmith int i; 253527817Smsmith int signal, silence; 253627817Smsmith int w_insertcache; /* computed index for cache entry storage */ 253727817Smsmith int ipflag = wl_cache_iponly; 2538113571Sjhay#endif 253927817Smsmith 254027817Smsmith /* filters: 254127817Smsmith * 1. ip only 254227817Smsmith * 2. configurable filter to throw out unicast packets, 254327817Smsmith * keep multicast only. 254427817Smsmith */ 254527817Smsmith 254632350Seivind#ifdef INET 254727817Smsmith /* reject if not IP packet 254827817Smsmith */ 254927817Smsmith if ( wl_cache_iponly && (ntohs(eh->ether_type) != 0x800)) { 255027817Smsmith return; 255127817Smsmith } 255227817Smsmith 255327817Smsmith /* check if broadcast or multicast packet. we toss 255427817Smsmith * unicast packets 255527817Smsmith */ 255627817Smsmith if (wl_cache_mcastonly && ((eh->ether_dhost[0] & 1) == 0)) { 255727817Smsmith return; 255827817Smsmith } 255927817Smsmith 256027817Smsmith /* find the ip header. we want to store the ip_src 256127817Smsmith * address. use the mtod macro(in mbuf.h) 256227817Smsmith * to typecast m to struct ip * 256327817Smsmith */ 256427817Smsmith if (ipflag) { 256527817Smsmith ip = mtod(m, struct ip *); 256627817Smsmith } 256727817Smsmith 256827817Smsmith /* do a linear search for a matching MAC address 256927817Smsmith * in the cache table 257027817Smsmith * . MAC address is 6 bytes, 257127817Smsmith * . var w_nextcache holds total number of entries already cached 257227817Smsmith */ 257379082Simp for (i = 0; i < sc->w_nextcache; i++) { 257427817Smsmith if (! bcmp(eh->ether_shost, sc->w_sigcache[i].macsrc, 6 )) { 257527817Smsmith /* Match!, 257627817Smsmith * so we already have this entry, 257727817Smsmith * update the data, and LRU age 257827817Smsmith */ 257927817Smsmith break; 258027817Smsmith } 258127817Smsmith } 258227817Smsmith 258327817Smsmith /* did we find a matching mac address? 258427817Smsmith * if yes, then overwrite a previously existing cache entry 258527817Smsmith */ 258627817Smsmith if (i < sc->w_nextcache ) { 258727817Smsmith w_insertcache = i; 258827817Smsmith } 258927817Smsmith /* else, have a new address entry,so 259027817Smsmith * add this new entry, 259127817Smsmith * if table full, then we need to replace entry 259227817Smsmith */ 259327817Smsmith else { 259427817Smsmith 259527817Smsmith /* check for space in cache table 259627817Smsmith * note: w_nextcache also holds number of entries 259727817Smsmith * added in the cache table 259827817Smsmith */ 259927817Smsmith if ( sc->w_nextcache < MAXCACHEITEMS ) { 260027817Smsmith w_insertcache = sc->w_nextcache; 260127817Smsmith sc->w_nextcache++; 260227817Smsmith sc->w_sigitems = sc->w_nextcache; 260327817Smsmith } 260427817Smsmith /* no space found, so simply wrap with wrap index 260527817Smsmith * and "zap" the next entry 260627817Smsmith */ 260727817Smsmith else { 260827817Smsmith if (sc->w_wrapindex == MAXCACHEITEMS) { 260927817Smsmith sc->w_wrapindex = 0; 261027817Smsmith } 261127817Smsmith w_insertcache = sc->w_wrapindex++; 261227817Smsmith } 261327817Smsmith } 261427817Smsmith 261527817Smsmith /* invariant: w_insertcache now points at some slot 261627817Smsmith * in cache. 261727817Smsmith */ 261827817Smsmith if (w_insertcache < 0 || w_insertcache >= MAXCACHEITEMS) { 261927817Smsmith log(LOG_ERR, 262027817Smsmith "wl_cache_store, bad index: %d of [0..%d], gross cache error\n", 262127817Smsmith w_insertcache, MAXCACHEITEMS); 262227817Smsmith return; 262327817Smsmith } 262427817Smsmith 262527817Smsmith /* store items in cache 262627817Smsmith * .ipsrc 262727817Smsmith * .macsrc 262827817Smsmith * .signal (0..63) ,silence (0..63) ,quality (0..15) 262927817Smsmith */ 263027817Smsmith if (ipflag) { 263127817Smsmith sc->w_sigcache[w_insertcache].ipsrc = ip->ip_src.s_addr; 263227817Smsmith } 263327817Smsmith bcopy( eh->ether_shost, sc->w_sigcache[w_insertcache].macsrc, 6); 263427817Smsmith signal = sc->w_sigcache[w_insertcache].signal = wlmmcread(base, MMC_SIGNAL_LVL) & 0x3f; 263527817Smsmith silence = sc->w_sigcache[w_insertcache].silence = wlmmcread(base, MMC_SILENCE_LVL) & 0x3f; 263627817Smsmith sc->w_sigcache[w_insertcache].quality = wlmmcread(base, MMC_SIGN_QUAL) & 0x0f; 263727817Smsmith if (signal > 0) 263827817Smsmith sc->w_sigcache[w_insertcache].snr = 263927817Smsmith signal - silence; 264027817Smsmith else 264127817Smsmith sc->w_sigcache[w_insertcache].snr = 0; 264232350Seivind#endif /* INET */ 264327817Smsmith 264427817Smsmith} 264527817Smsmith#endif /* WLCACHE */ 2646