if_wi.c revision 76438
146492Swpaul/* 246492Swpaul * Copyright (c) 1997, 1998, 1999 346492Swpaul * Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved. 446492Swpaul * 546492Swpaul * Redistribution and use in source and binary forms, with or without 646492Swpaul * modification, are permitted provided that the following conditions 746492Swpaul * are met: 846492Swpaul * 1. Redistributions of source code must retain the above copyright 946492Swpaul * notice, this list of conditions and the following disclaimer. 1046492Swpaul * 2. Redistributions in binary form must reproduce the above copyright 1146492Swpaul * notice, this list of conditions and the following disclaimer in the 1246492Swpaul * documentation and/or other materials provided with the distribution. 1346492Swpaul * 3. All advertising materials mentioning features or use of this software 1446492Swpaul * must display the following acknowledgement: 1546492Swpaul * This product includes software developed by Bill Paul. 1646492Swpaul * 4. Neither the name of the author nor the names of any co-contributors 1746492Swpaul * may be used to endorse or promote products derived from this software 1846492Swpaul * without specific prior written permission. 1946492Swpaul * 2046492Swpaul * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND 2146492Swpaul * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2246492Swpaul * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2346492Swpaul * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD 2446492Swpaul * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 2546492Swpaul * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 2646492Swpaul * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 2746492Swpaul * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 2846492Swpaul * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 2946492Swpaul * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 3046492Swpaul * THE POSSIBILITY OF SUCH DAMAGE. 3146492Swpaul */ 3246492Swpaul 3346492Swpaul/* 3446492Swpaul * Lucent WaveLAN/IEEE 802.11 PCMCIA driver for FreeBSD. 3546492Swpaul * 3646492Swpaul * Written by Bill Paul <wpaul@ctr.columbia.edu> 3746492Swpaul * Electrical Engineering Department 3846492Swpaul * Columbia University, New York City 3946492Swpaul */ 4046492Swpaul 4146492Swpaul/* 4247401Swpaul * The WaveLAN/IEEE adapter is the second generation of the WaveLAN 4346492Swpaul * from Lucent. Unlike the older cards, the new ones are programmed 4446492Swpaul * entirely via a firmware-driven controller called the Hermes. 4546492Swpaul * Unfortunately, Lucent will not release the Hermes programming manual 4646492Swpaul * without an NDA (if at all). What they do release is an API library 4746492Swpaul * called the HCF (Hardware Control Functions) which is supposed to 4846492Swpaul * do the device-specific operations of a device driver for you. The 4946492Swpaul * publically available version of the HCF library (the 'HCF Light') is 5047401Swpaul * a) extremely gross, b) lacks certain features, particularly support 5146492Swpaul * for 802.11 frames, and c) is contaminated by the GNU Public License. 5246492Swpaul * 5346492Swpaul * This driver does not use the HCF or HCF Light at all. Instead, it 5446492Swpaul * programs the Hermes controller directly, using information gleaned 5546492Swpaul * from the HCF Light code and corresponding documentation. 5646492Swpaul * 5746492Swpaul * This driver supports both the PCMCIA and ISA versions of the 5846492Swpaul * WaveLAN/IEEE cards. Note however that the ISA card isn't really 5946492Swpaul * anything of the sort: it's actually a PCMCIA bridge adapter 6046492Swpaul * that fits into an ISA slot, into which a PCMCIA WaveLAN card is 6146492Swpaul * inserted. Consequently, you need to use the pccard support for 6246492Swpaul * both the ISA and PCMCIA adapters. 6346492Swpaul */ 6446492Swpaul 6546492Swpaul#define WI_HERMES_AUTOINC_WAR /* Work around data write autoinc bug. */ 6646492Swpaul#define WI_HERMES_STATS_WAR /* Work around stats counter bug. */ 6753702Swpaul#define WICACHE /* turn on signal strength cache code */ 6846492Swpaul 6946492Swpaul#include <sys/param.h> 7046492Swpaul#include <sys/systm.h> 7146492Swpaul#include <sys/sockio.h> 7246492Swpaul#include <sys/mbuf.h> 7346492Swpaul#include <sys/kernel.h> 7446492Swpaul#include <sys/socket.h> 7553702Swpaul#include <sys/module.h> 7653702Swpaul#include <sys/bus.h> 7753702Swpaul#include <sys/syslog.h> 7853702Swpaul#include <sys/sysctl.h> 7946492Swpaul 8053702Swpaul#include <machine/bus.h> 8153702Swpaul#include <machine/resource.h> 8253702Swpaul#include <machine/md_var.h> 8353702Swpaul#include <machine/bus_pio.h> 8453702Swpaul#include <sys/rman.h> 8553702Swpaul 8674906Salfred#include <pci/pcireg.h> 8774906Salfred#include <pci/pcivar.h> 8874906Salfred 8946492Swpaul#include <net/if.h> 9046492Swpaul#include <net/if_arp.h> 9146492Swpaul#include <net/ethernet.h> 9246492Swpaul#include <net/if_dl.h> 9346492Swpaul#include <net/if_media.h> 9446492Swpaul#include <net/if_types.h> 9546492Swpaul 9646492Swpaul#include <netinet/in.h> 9746492Swpaul#include <netinet/in_systm.h> 9846492Swpaul#include <netinet/in_var.h> 9946492Swpaul#include <netinet/ip.h> 10046492Swpaul#include <netinet/if_ether.h> 10146492Swpaul 10246492Swpaul#include <net/bpf.h> 10346492Swpaul 10471161Speter#include <dev/pccard/pccardvar.h> 10571161Speter#include <dev/pccard/pccarddevs.h> 10671161Speter 10770808Speter#include <dev/wi/if_wavelan_ieee.h> 10870808Speter#include <dev/wi/if_wireg.h> 10946492Swpaul 11071161Speter#include "card_if.h" 11171161Speter 11246492Swpaul#if !defined(lint) 11346492Swpaulstatic const char rcsid[] = 11450477Speter "$FreeBSD: head/sys/dev/wi/if_wi.c 76438 2001-05-10 17:17:24Z wpaul $"; 11546492Swpaul#endif 11646492Swpaul 11746492Swpaul#ifdef foo 11847401Swpaulstatic u_int8_t wi_mcast_addr[6] = { 0x01, 0x60, 0x1D, 0x00, 0x01, 0x00 }; 11946492Swpaul#endif 12046492Swpaul 12153702Swpaulstatic void wi_intr __P((void *)); 12246492Swpaulstatic void wi_reset __P((struct wi_softc *)); 12346492Swpaulstatic int wi_ioctl __P((struct ifnet *, u_long, caddr_t)); 12446492Swpaulstatic void wi_init __P((void *)); 12546492Swpaulstatic void wi_start __P((struct ifnet *)); 12646492Swpaulstatic void wi_stop __P((struct wi_softc *)); 12746492Swpaulstatic void wi_watchdog __P((struct ifnet *)); 12846492Swpaulstatic void wi_rxeof __P((struct wi_softc *)); 12946492Swpaulstatic void wi_txeof __P((struct wi_softc *, int)); 13046492Swpaulstatic void wi_update_stats __P((struct wi_softc *)); 13146492Swpaulstatic void wi_setmulti __P((struct wi_softc *)); 13246492Swpaul 13346492Swpaulstatic int wi_cmd __P((struct wi_softc *, int, int)); 13446492Swpaulstatic int wi_read_record __P((struct wi_softc *, struct wi_ltv_gen *)); 13546492Swpaulstatic int wi_write_record __P((struct wi_softc *, struct wi_ltv_gen *)); 13646492Swpaulstatic int wi_read_data __P((struct wi_softc *, int, 13746492Swpaul int, caddr_t, int)); 13846492Swpaulstatic int wi_write_data __P((struct wi_softc *, int, 13946492Swpaul int, caddr_t, int)); 14046492Swpaulstatic int wi_seek __P((struct wi_softc *, int, int, int)); 14146492Swpaulstatic int wi_alloc_nicmem __P((struct wi_softc *, int, int *)); 14246492Swpaulstatic void wi_inquire __P((void *)); 14346492Swpaulstatic void wi_setdef __P((struct wi_softc *, struct wi_req *)); 14446492Swpaulstatic int wi_mgmt_xmit __P((struct wi_softc *, caddr_t, int)); 14546492Swpaul 14653702Swpaul#ifdef WICACHE 14753702Swpaulstatic 14853702Swpaulvoid wi_cache_store __P((struct wi_softc *, struct ether_header *, 14953702Swpaul struct mbuf *, unsigned short)); 15053702Swpaul#endif 15153702Swpaul 15274906Salfredstatic int wi_generic_attach __P((device_t)); 15371161Speterstatic int wi_pccard_match __P((device_t)); 15453702Swpaulstatic int wi_pccard_probe __P((device_t)); 15574906Salfredstatic int wi_pci_probe __P((device_t)); 15653702Swpaulstatic int wi_pccard_attach __P((device_t)); 15774906Salfredstatic int wi_pci_attach __P((device_t)); 15853702Swpaulstatic int wi_pccard_detach __P((device_t)); 15953702Swpaulstatic void wi_shutdown __P((device_t)); 16053702Swpaul 16174906Salfredstatic int wi_alloc __P((device_t, int)); 16253702Swpaulstatic void wi_free __P((device_t)); 16353702Swpaul 16453702Swpaulstatic device_method_t wi_pccard_methods[] = { 16553702Swpaul /* Device interface */ 16671161Speter DEVMETHOD(device_probe, pccard_compat_probe), 16771161Speter DEVMETHOD(device_attach, pccard_compat_attach), 16853702Swpaul DEVMETHOD(device_detach, wi_pccard_detach), 16953702Swpaul DEVMETHOD(device_shutdown, wi_shutdown), 17053702Swpaul 17171161Speter /* Card interface */ 17271161Speter DEVMETHOD(card_compat_match, wi_pccard_match), 17371161Speter DEVMETHOD(card_compat_probe, wi_pccard_probe), 17471161Speter DEVMETHOD(card_compat_attach, wi_pccard_attach), 17571161Speter 17653702Swpaul { 0, 0 } 17746492Swpaul}; 17846492Swpaul 17974906Salfredstatic device_method_t wi_pci_methods[] = { 18074906Salfred /* Device interface */ 18174906Salfred DEVMETHOD(device_probe, wi_pci_probe), 18274906Salfred DEVMETHOD(device_attach, wi_pci_attach), 18374906Salfred DEVMETHOD(device_detach, wi_pccard_detach), 18474906Salfred DEVMETHOD(device_shutdown, wi_shutdown), 18574906Salfred 18674906Salfred { 0, 0 } 18774906Salfred}; 18874906Salfred 18953702Swpaulstatic driver_t wi_pccard_driver = { 19046492Swpaul "wi", 19153702Swpaul wi_pccard_methods, 19253702Swpaul sizeof(struct wi_softc) 19346492Swpaul}; 19446492Swpaul 19574906Salfredstatic driver_t wi_pci_driver = { 19674906Salfred "wi", 19774906Salfred wi_pci_methods, 19874906Salfred sizeof(struct wi_softc) 19974906Salfred}; 20074906Salfred 20153702Swpaulstatic devclass_t wi_pccard_devclass; 20274906Salfredstatic devclass_t wi_pci_devclass; 20346492Swpaul 20453702SwpaulDRIVER_MODULE(if_wi, pccard, wi_pccard_driver, wi_pccard_devclass, 0, 0); 20574906SalfredDRIVER_MODULE(if_wi, pci, wi_pci_driver, wi_pci_devclass, 0, 0); 20653702Swpaul 20771161Speterstatic const struct pccard_product wi_pccard_products[] = { 20871161Speter { PCCARD_STR_LUCENT_WAVELAN_IEEE, PCCARD_VENDOR_LUCENT, 20971336Simp PCCARD_PRODUCT_LUCENT_WAVELAN_IEEE, 0, 21071336Simp PCCARD_CIS_LUCENT_WAVELAN_IEEE }, 21171161Speter}; 21271161Speter 21374906Salfredstatic char wi_device_desc[] = "WaveLAN/IEEE 802.11"; 21474906Salfred 21571161Speterstatic int wi_pccard_match(dev) 21671161Speter device_t dev; 21771161Speter{ 21871161Speter const struct pccard_product *pp; 21971161Speter 22071161Speter if ((pp = pccard_product_lookup(dev, wi_pccard_products, 22171161Speter sizeof(wi_pccard_products[0]), NULL)) != NULL) { 22271161Speter device_set_desc(dev, pp->pp_name); 22371161Speter return 0; 22471161Speter } 22571161Speter return ENXIO; 22671161Speter} 22771161Speter 22853702Swpaulstatic int wi_pccard_probe(dev) 22953702Swpaul device_t dev; 23046492Swpaul{ 23153702Swpaul struct wi_softc *sc; 23253702Swpaul int error; 23346492Swpaul 23453702Swpaul sc = device_get_softc(dev); 23546492Swpaul sc->wi_gone = 0; 23646492Swpaul 23774906Salfred error = wi_alloc(dev, 0); 23853702Swpaul if (error) 23953702Swpaul return (error); 24053702Swpaul 24153702Swpaul wi_free(dev); 24253702Swpaul 24346492Swpaul /* Make sure interrupts are disabled. */ 24446492Swpaul CSR_WRITE_2(sc, WI_INT_EN, 0); 24546492Swpaul CSR_WRITE_2(sc, WI_EVENT_ACK, 0xFFFF); 24646492Swpaul 24753702Swpaul return (0); 24846492Swpaul} 24946492Swpaul 25074906Salfredstatic int 25174906Salfredwi_pci_probe(dev) 25274906Salfred device_t dev; 25374906Salfred{ 25474906Salfred struct wi_softc *sc; 25574906Salfred 25674906Salfred sc = device_get_softc(dev); 25774998Swpaul if ((pci_get_vendor(dev) == WI_PCI_VENDOR_EUMITCOM) && 25874906Salfred (pci_get_device(dev) == WI_PCI_DEVICE_PRISM2STA)) { 25974906Salfred sc->wi_prism2 = 1; 26074998Swpaul device_set_desc(dev, 26174998Swpaul "PRISM2STA PCI WaveLAN/IEEE 802.11"); 26274906Salfred return (0); 26374906Salfred } 26474906Salfred return(ENXIO); 26574906Salfred} 26674906Salfred 26753702Swpaulstatic int wi_pccard_detach(dev) 26853702Swpaul device_t dev; 26946492Swpaul{ 27046492Swpaul struct wi_softc *sc; 27146492Swpaul struct ifnet *ifp; 27246492Swpaul 27353702Swpaul sc = device_get_softc(dev); 27467092Swpaul WI_LOCK(sc); 27546492Swpaul ifp = &sc->arpcom.ac_if; 27646492Swpaul 27746492Swpaul if (sc->wi_gone) { 27853702Swpaul device_printf(dev, "already unloaded\n"); 27967092Swpaul WI_UNLOCK(sc); 28053702Swpaul return(ENODEV); 28146492Swpaul } 28246492Swpaul 28353702Swpaul wi_stop(sc); 28458274Srwatson 28563090Sarchie ether_ifdetach(ifp, ETHER_BPF_SUPPORTED); 28654277Swpaul bus_teardown_intr(dev, sc->irq, sc->wi_intrhand); 28753702Swpaul wi_free(dev); 28846492Swpaul sc->wi_gone = 1; 28946492Swpaul 29067092Swpaul WI_UNLOCK(sc); 29167092Swpaul mtx_destroy(&sc->wi_mtx); 29246492Swpaul 29346492Swpaul return(0); 29446492Swpaul} 29546492Swpaul 29653702Swpaulstatic int wi_pccard_attach(device_t dev) 29746492Swpaul{ 29846492Swpaul struct wi_softc *sc; 29953702Swpaul int error; 30070073Swpaul u_int32_t flags; 30146492Swpaul 30253702Swpaul sc = device_get_softc(dev); 30346492Swpaul 30470073Swpaul /* 30570073Swpaul * XXX: quick hack to support Prism II chip. 30670073Swpaul * Currently, we need to set a flags in pccard.conf to specify 30770073Swpaul * which type chip is used. 30870073Swpaul * 30970073Swpaul * We need to replace this code in a future. 31070073Swpaul * It is better to use CIS than using a flag. 31170073Swpaul */ 31270073Swpaul flags = device_get_flags(dev); 31370073Swpaul#define WI_FLAGS_PRISM2 0x10000 31470073Swpaul if (flags & WI_FLAGS_PRISM2) { 31570073Swpaul sc->wi_prism2 = 1; 31670073Swpaul if (bootverbose) { 31770073Swpaul device_printf(dev, "found PrismII chip\n"); 31870073Swpaul } 31970073Swpaul } 32070073Swpaul else { 32170073Swpaul sc->wi_prism2 = 0; 32270073Swpaul if (bootverbose) { 32370073Swpaul device_printf(dev, "found Lucent chip\n"); 32470073Swpaul } 32570073Swpaul } 32670073Swpaul 32774906Salfred error = wi_alloc(dev, 0); 32853702Swpaul if (error) { 32953702Swpaul device_printf(dev, "wi_alloc() failed! (%d)\n", error); 33053702Swpaul return (error); 33153702Swpaul } 33274906Salfred return (wi_generic_attach(dev)); 33374906Salfred} 33453702Swpaul 33574906Salfredstatic int 33674906Salfredwi_pci_attach(device_t dev) 33774906Salfred{ 33874906Salfred struct wi_softc *sc; 33974906Salfred u_int32_t command, wanted; 34074906Salfred u_int16_t reg; 34174906Salfred int error; 34274906Salfred 34374906Salfred sc = device_get_softc(dev); 34474906Salfred 34574906Salfred command = pci_read_config(dev, PCIR_COMMAND, 4); 34674906Salfred wanted = PCIM_CMD_PORTEN|PCIM_CMD_MEMEN; 34774906Salfred command |= wanted; 34874906Salfred pci_write_config(dev, PCIR_COMMAND, command, 4); 34974906Salfred command = pci_read_config(dev, PCIR_COMMAND, 4); 35074906Salfred if ((command & wanted) != wanted) { 35174906Salfred device_printf(dev, "wi_pci_attach() failed to enable pci!\n"); 35274906Salfred return (ENXIO); 35374906Salfred } 35474906Salfred 35574906Salfred error = wi_alloc(dev, WI_PCI_IORES); 35674906Salfred if (error) 35774906Salfred return (error); 35874906Salfred 35974906Salfred device_set_desc(dev, wi_device_desc); 36074906Salfred 36174906Salfred /* Make sure interrupts are disabled. */ 36274906Salfred CSR_WRITE_2(sc, WI_INT_EN, 0); 36374906Salfred CSR_WRITE_2(sc, WI_EVENT_ACK, 0xFFFF); 36474906Salfred 36574998Swpaul sc->mem_rid = WI_PCI_MEMRES; 36674906Salfred sc->mem = bus_alloc_resource(dev, SYS_RES_MEMORY, &sc->mem_rid, 36774906Salfred 0, ~0, 1, RF_ACTIVE); 36874906Salfred if (sc->mem == NULL) { 36974906Salfred device_printf(dev, "couldn't allocate memory\n"); 37074906Salfred wi_free(dev); 37174906Salfred return (ENXIO); 37274906Salfred } 37374906Salfred sc->wi_bmemtag = rman_get_bustag(sc->mem); 37474906Salfred sc->wi_bmemhandle = rman_get_bushandle(sc->mem); 37574906Salfred 37674906Salfred /* 37774906Salfred * From Linux driver: 37874906Salfred * Write COR to enable PC card 37975149Simp * This is a subset of the protocol that the pccard bus code 38075149Simp * would do. 38174906Salfred */ 38274906Salfred CSM_WRITE_1(sc, WI_COR_OFFSET, WI_COR_VALUE); 38374906Salfred reg = CSM_READ_1(sc, WI_COR_OFFSET); 38474906Salfred 38574998Swpaul CSR_WRITE_2(sc, WI_HFA384X_SWSUPPORT0_OFF, WI_PRISM2STA_MAGIC); 38674998Swpaul reg = CSR_READ_2(sc, WI_HFA384X_SWSUPPORT0_OFF); 38774906Salfred if (reg != WI_PRISM2STA_MAGIC) { 38874906Salfred device_printf(dev, 38974998Swpaul "CSR_READ_2(WI_HFA384X_SWSUPPORT0_OFF) " 39074998Swpaul "wanted %d, got %d\n", WI_PRISM2STA_MAGIC, reg); 39174906Salfred wi_free(dev); 39274906Salfred return (ENXIO); 39374906Salfred } 39474906Salfred 39574906Salfred error = wi_generic_attach(dev); 39674906Salfred if (error != 0) 39774906Salfred return (error); 39874906Salfred 39974906Salfred return (0); 40074906Salfred} 40174906Salfred 40274906Salfredstatic int 40374906Salfredwi_generic_attach(device_t dev) 40474906Salfred{ 40574906Salfred struct wi_softc *sc; 40674906Salfred struct wi_ltv_macaddr mac; 40774906Salfred struct wi_ltv_gen gen; 40874906Salfred struct ifnet *ifp; 40974906Salfred int error; 41074906Salfred 41174906Salfred sc = device_get_softc(dev); 41274906Salfred ifp = &sc->arpcom.ac_if; 41374906Salfred 41453702Swpaul error = bus_setup_intr(dev, sc->irq, INTR_TYPE_NET, 41574998Swpaul wi_intr, sc, &sc->wi_intrhand); 41653702Swpaul 41753702Swpaul if (error) { 41853702Swpaul device_printf(dev, "bus_setup_intr() failed! (%d)\n", error); 41953702Swpaul wi_free(dev); 42053702Swpaul return (error); 42153702Swpaul } 42253702Swpaul 42371228Sbmilekic mtx_init(&sc->wi_mtx, device_get_nameunit(dev), MTX_DEF | MTX_RECURSE); 42467092Swpaul WI_LOCK(sc); 42567092Swpaul 42646492Swpaul /* Reset the NIC. */ 42746492Swpaul wi_reset(sc); 42846492Swpaul 42976438Swpaul /* 43076438Swpaul * Read the station address. 43176438Swpaul * And do it twice. I've seen PRISM-based cards that return 43276438Swpaul * an error when trying to read it the first time, which causes 43376438Swpaul * the probe to fail. 43476438Swpaul */ 43546492Swpaul mac.wi_type = WI_RID_MAC_NODE; 43646492Swpaul mac.wi_len = 4; 43776438Swpaul wi_read_record(sc, (struct wi_ltv_gen *)&mac)); 43875150Simp if ((error = wi_read_record(sc, (struct wi_ltv_gen *)&mac)) != 0) { 43975149Simp device_printf(dev, "mac read failed %d\n", error); 44075149Simp wi_free(dev); 44175149Simp return (error); 44275149Simp } 44346492Swpaul bcopy((char *)&mac.wi_mac_addr, 44446492Swpaul (char *)&sc->arpcom.ac_enaddr, ETHER_ADDR_LEN); 44546492Swpaul 44653702Swpaul device_printf(dev, "Ethernet address: %6D\n", 44746492Swpaul sc->arpcom.ac_enaddr, ":"); 44846492Swpaul 44946492Swpaul ifp->if_softc = sc; 45046492Swpaul ifp->if_unit = sc->wi_unit; 45146492Swpaul ifp->if_name = "wi"; 45246492Swpaul ifp->if_mtu = ETHERMTU; 45346492Swpaul ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; 45446492Swpaul ifp->if_ioctl = wi_ioctl; 45546492Swpaul ifp->if_output = ether_output; 45646492Swpaul ifp->if_start = wi_start; 45746492Swpaul ifp->if_watchdog = wi_watchdog; 45846492Swpaul ifp->if_init = wi_init; 45946492Swpaul ifp->if_baudrate = 10000000; 46046492Swpaul ifp->if_snd.ifq_maxlen = IFQ_MAXLEN; 46146492Swpaul 46246492Swpaul bzero(sc->wi_node_name, sizeof(sc->wi_node_name)); 46346492Swpaul bcopy(WI_DEFAULT_NODENAME, sc->wi_node_name, 46446492Swpaul sizeof(WI_DEFAULT_NODENAME) - 1); 46546492Swpaul 46646492Swpaul bzero(sc->wi_net_name, sizeof(sc->wi_net_name)); 46746492Swpaul bcopy(WI_DEFAULT_NETNAME, sc->wi_net_name, 46846492Swpaul sizeof(WI_DEFAULT_NETNAME) - 1); 46946492Swpaul 47046492Swpaul bzero(sc->wi_ibss_name, sizeof(sc->wi_ibss_name)); 47146492Swpaul bcopy(WI_DEFAULT_IBSS, sc->wi_ibss_name, 47246492Swpaul sizeof(WI_DEFAULT_IBSS) - 1); 47346492Swpaul 47446492Swpaul sc->wi_portnum = WI_DEFAULT_PORT; 47574139Sassar sc->wi_ptype = WI_PORTTYPE_BSS; 47646492Swpaul sc->wi_ap_density = WI_DEFAULT_AP_DENSITY; 47746492Swpaul sc->wi_rts_thresh = WI_DEFAULT_RTS_THRESH; 47846492Swpaul sc->wi_tx_rate = WI_DEFAULT_TX_RATE; 47946492Swpaul sc->wi_max_data_len = WI_DEFAULT_DATALEN; 48046492Swpaul sc->wi_create_ibss = WI_DEFAULT_CREATE_IBSS; 48146611Swpaul sc->wi_pm_enabled = WI_DEFAULT_PM_ENABLED; 48246611Swpaul sc->wi_max_sleep = WI_DEFAULT_MAX_SLEEP; 48346492Swpaul 48446563Swpaul /* 48546563Swpaul * Read the default channel from the NIC. This may vary 48646563Swpaul * depending on the country where the NIC was purchased, so 48746563Swpaul * we can't hard-code a default and expect it to work for 48846563Swpaul * everyone. 48946563Swpaul */ 49046563Swpaul gen.wi_type = WI_RID_OWN_CHNL; 49146563Swpaul gen.wi_len = 2; 49246563Swpaul wi_read_record(sc, &gen); 49346563Swpaul sc->wi_channel = gen.wi_val; 49446563Swpaul 49556965Swpaul /* 49656965Swpaul * Find out if we support WEP on this card. 49756965Swpaul */ 49856965Swpaul gen.wi_type = WI_RID_WEP_AVAIL; 49956965Swpaul gen.wi_len = 2; 50056965Swpaul wi_read_record(sc, &gen); 50156965Swpaul sc->wi_has_wep = gen.wi_val; 50256965Swpaul 50370073Swpaul if (bootverbose) { 50470073Swpaul device_printf(sc->dev, 50570073Swpaul __FUNCTION__ ":wi_has_wep = %d\n", 50670073Swpaul sc->wi_has_wep); 50770073Swpaul } 50870073Swpaul 50946492Swpaul bzero((char *)&sc->wi_stats, sizeof(sc->wi_stats)); 51046492Swpaul 51146492Swpaul wi_init(sc); 51246492Swpaul wi_stop(sc); 51346492Swpaul 51446492Swpaul /* 51563090Sarchie * Call MI attach routine. 51646492Swpaul */ 51763090Sarchie ether_ifattach(ifp, ETHER_BPF_SUPPORTED); 51853702Swpaul callout_handle_init(&sc->wi_stat_ch); 51967092Swpaul WI_UNLOCK(sc); 52046492Swpaul 52146492Swpaul return(0); 52246492Swpaul} 52346492Swpaul 52446492Swpaulstatic void wi_rxeof(sc) 52546492Swpaul struct wi_softc *sc; 52646492Swpaul{ 52746492Swpaul struct ifnet *ifp; 52846492Swpaul struct ether_header *eh; 52946492Swpaul struct wi_frame rx_frame; 53046492Swpaul struct mbuf *m; 53146492Swpaul int id; 53246492Swpaul 53346492Swpaul ifp = &sc->arpcom.ac_if; 53446492Swpaul 53546492Swpaul id = CSR_READ_2(sc, WI_RX_FID); 53646492Swpaul 53746492Swpaul /* First read in the frame header */ 53846492Swpaul if (wi_read_data(sc, id, 0, (caddr_t)&rx_frame, sizeof(rx_frame))) { 53946492Swpaul ifp->if_ierrors++; 54046492Swpaul return; 54146492Swpaul } 54246492Swpaul 54346492Swpaul if (rx_frame.wi_status & WI_STAT_ERRSTAT) { 54446492Swpaul ifp->if_ierrors++; 54546492Swpaul return; 54646492Swpaul } 54746492Swpaul 54846492Swpaul MGETHDR(m, M_DONTWAIT, MT_DATA); 54946492Swpaul if (m == NULL) { 55046492Swpaul ifp->if_ierrors++; 55146492Swpaul return; 55246492Swpaul } 55346492Swpaul MCLGET(m, M_DONTWAIT); 55446492Swpaul if (!(m->m_flags & M_EXT)) { 55546492Swpaul m_freem(m); 55646492Swpaul ifp->if_ierrors++; 55746492Swpaul return; 55846492Swpaul } 55946492Swpaul 56046492Swpaul eh = mtod(m, struct ether_header *); 56146492Swpaul m->m_pkthdr.rcvif = ifp; 56246492Swpaul 56346492Swpaul if (rx_frame.wi_status == WI_STAT_1042 || 56446492Swpaul rx_frame.wi_status == WI_STAT_TUNNEL || 56546492Swpaul rx_frame.wi_status == WI_STAT_WMP_MSG) { 56648553Swpaul if((rx_frame.wi_dat_len + WI_SNAPHDR_LEN) > MCLBYTES) { 56753702Swpaul device_printf(sc->dev, "oversized packet received " 56853702Swpaul "(wi_dat_len=%d, wi_status=0x%x)\n", 56948553Swpaul rx_frame.wi_dat_len, rx_frame.wi_status); 57048553Swpaul m_freem(m); 57148553Swpaul ifp->if_ierrors++; 57248553Swpaul return; 57348553Swpaul } 57446492Swpaul m->m_pkthdr.len = m->m_len = 57546492Swpaul rx_frame.wi_dat_len + WI_SNAPHDR_LEN; 57646492Swpaul 57775373Salfred#if 0 57846492Swpaul bcopy((char *)&rx_frame.wi_addr1, 57946492Swpaul (char *)&eh->ether_dhost, ETHER_ADDR_LEN); 58059328Swpaul if (sc->wi_ptype == WI_PORTTYPE_ADHOC) { 58159328Swpaul bcopy((char *)&rx_frame.wi_addr2, 58259328Swpaul (char *)&eh->ether_shost, ETHER_ADDR_LEN); 58359328Swpaul } else { 58459328Swpaul bcopy((char *)&rx_frame.wi_addr3, 58559328Swpaul (char *)&eh->ether_shost, ETHER_ADDR_LEN); 58659328Swpaul } 58775373Salfred#else 58875373Salfred bcopy((char *)&rx_frame.wi_dst_addr, 58975373Salfred (char *)&eh->ether_dhost, ETHER_ADDR_LEN); 59075373Salfred bcopy((char *)&rx_frame.wi_src_addr, 59175373Salfred (char *)&eh->ether_shost, ETHER_ADDR_LEN); 59275373Salfred#endif 59375373Salfred 59446492Swpaul bcopy((char *)&rx_frame.wi_type, 59575276Salfred (char *)&eh->ether_type, ETHER_TYPE_LEN); 59646492Swpaul 59746492Swpaul if (wi_read_data(sc, id, WI_802_11_OFFSET, 59846492Swpaul mtod(m, caddr_t) + sizeof(struct ether_header), 59946492Swpaul m->m_len + 2)) { 60046492Swpaul m_freem(m); 60146492Swpaul ifp->if_ierrors++; 60246492Swpaul return; 60346492Swpaul } 60446492Swpaul } else { 60548553Swpaul if((rx_frame.wi_dat_len + 60648553Swpaul sizeof(struct ether_header)) > MCLBYTES) { 60753702Swpaul device_printf(sc->dev, "oversized packet received " 60853702Swpaul "(wi_dat_len=%d, wi_status=0x%x)\n", 60948553Swpaul rx_frame.wi_dat_len, rx_frame.wi_status); 61048553Swpaul m_freem(m); 61148553Swpaul ifp->if_ierrors++; 61248553Swpaul return; 61348553Swpaul } 61446492Swpaul m->m_pkthdr.len = m->m_len = 61546492Swpaul rx_frame.wi_dat_len + sizeof(struct ether_header); 61646492Swpaul 61746492Swpaul if (wi_read_data(sc, id, WI_802_3_OFFSET, 61846492Swpaul mtod(m, caddr_t), m->m_len + 2)) { 61946492Swpaul m_freem(m); 62046492Swpaul ifp->if_ierrors++; 62146492Swpaul return; 62246492Swpaul } 62346492Swpaul } 62446492Swpaul 62546492Swpaul ifp->if_ipackets++; 62646492Swpaul 62746492Swpaul /* Receive packet. */ 62846492Swpaul m_adj(m, sizeof(struct ether_header)); 62953702Swpaul#ifdef WICACHE 63053702Swpaul wi_cache_store(sc, eh, m, rx_frame.wi_q_info); 63153702Swpaul#endif 63246492Swpaul ether_input(ifp, eh, m); 63346492Swpaul} 63446492Swpaul 63546492Swpaulstatic void wi_txeof(sc, status) 63646492Swpaul struct wi_softc *sc; 63746492Swpaul int status; 63846492Swpaul{ 63946492Swpaul struct ifnet *ifp; 64046492Swpaul 64146492Swpaul ifp = &sc->arpcom.ac_if; 64246492Swpaul 64346492Swpaul ifp->if_timer = 0; 64446492Swpaul ifp->if_flags &= ~IFF_OACTIVE; 64546492Swpaul 64646492Swpaul if (status & WI_EV_TX_EXC) 64746492Swpaul ifp->if_oerrors++; 64846492Swpaul else 64946492Swpaul ifp->if_opackets++; 65046492Swpaul 65146492Swpaul return; 65246492Swpaul} 65346492Swpaul 65446492Swpaulvoid wi_inquire(xsc) 65546492Swpaul void *xsc; 65646492Swpaul{ 65746492Swpaul struct wi_softc *sc; 65846492Swpaul struct ifnet *ifp; 65946492Swpaul 66046492Swpaul sc = xsc; 66146492Swpaul ifp = &sc->arpcom.ac_if; 66246492Swpaul 66346492Swpaul sc->wi_stat_ch = timeout(wi_inquire, sc, hz * 60); 66446492Swpaul 66546492Swpaul /* Don't do this while we're transmitting */ 66646492Swpaul if (ifp->if_flags & IFF_OACTIVE) 66746492Swpaul return; 66846492Swpaul 66946492Swpaul wi_cmd(sc, WI_CMD_INQUIRE, WI_INFO_COUNTERS); 67046492Swpaul 67146492Swpaul return; 67246492Swpaul} 67346492Swpaul 67446492Swpaulvoid wi_update_stats(sc) 67546492Swpaul struct wi_softc *sc; 67646492Swpaul{ 67746492Swpaul struct wi_ltv_gen gen; 67846492Swpaul u_int16_t id; 67946492Swpaul struct ifnet *ifp; 68046492Swpaul u_int32_t *ptr; 68175373Salfred int len, i; 68246492Swpaul u_int16_t t; 68346492Swpaul 68446492Swpaul ifp = &sc->arpcom.ac_if; 68546492Swpaul 68646492Swpaul id = CSR_READ_2(sc, WI_INFO_FID); 68746492Swpaul 68846492Swpaul wi_read_data(sc, id, 0, (char *)&gen, 4); 68946492Swpaul 69075373Salfred if (gen.wi_type != WI_INFO_COUNTERS) 69146492Swpaul return; 69246492Swpaul 69375373Salfred len = (gen.wi_len - 1 < sizeof(sc->wi_stats) / 4) ? 69475373Salfred gen.wi_len - 1 : sizeof(sc->wi_stats) / 4; 69546492Swpaul ptr = (u_int32_t *)&sc->wi_stats; 69646492Swpaul 69775373Salfred for (i = 0; i < len - 1; i++) { 69846492Swpaul t = CSR_READ_2(sc, WI_DATA1); 69946492Swpaul#ifdef WI_HERMES_STATS_WAR 70046492Swpaul if (t > 0xF000) 70146492Swpaul t = ~t & 0xFFFF; 70246492Swpaul#endif 70346492Swpaul ptr[i] += t; 70446492Swpaul } 70546492Swpaul 70646492Swpaul ifp->if_collisions = sc->wi_stats.wi_tx_single_retries + 70746492Swpaul sc->wi_stats.wi_tx_multi_retries + 70846492Swpaul sc->wi_stats.wi_tx_retry_limit; 70946492Swpaul 71046492Swpaul return; 71146492Swpaul} 71246492Swpaul 71353702Swpaulstatic void wi_intr(xsc) 71453702Swpaul void *xsc; 71546492Swpaul{ 71653702Swpaul struct wi_softc *sc = xsc; 71746492Swpaul struct ifnet *ifp; 71846492Swpaul u_int16_t status; 71946492Swpaul 72067092Swpaul WI_LOCK(sc); 72167092Swpaul 72246492Swpaul ifp = &sc->arpcom.ac_if; 72346492Swpaul 72475373Salfred if (sc->wi_gone || !(ifp->if_flags & IFF_UP)) { 72546492Swpaul CSR_WRITE_2(sc, WI_EVENT_ACK, 0xFFFF); 72646492Swpaul CSR_WRITE_2(sc, WI_INT_EN, 0); 72767092Swpaul WI_UNLOCK(sc); 72846492Swpaul return; 72946492Swpaul } 73046492Swpaul 73146492Swpaul /* Disable interrupts. */ 73246492Swpaul CSR_WRITE_2(sc, WI_INT_EN, 0); 73346492Swpaul 73446492Swpaul status = CSR_READ_2(sc, WI_EVENT_STAT); 73546492Swpaul CSR_WRITE_2(sc, WI_EVENT_ACK, ~WI_INTRS); 73646492Swpaul 73746492Swpaul if (status & WI_EV_RX) { 73846492Swpaul wi_rxeof(sc); 73946492Swpaul CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_RX); 74046492Swpaul } 74146492Swpaul 74246492Swpaul if (status & WI_EV_TX) { 74346492Swpaul wi_txeof(sc, status); 74446492Swpaul CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_TX); 74546492Swpaul } 74646492Swpaul 74746492Swpaul if (status & WI_EV_ALLOC) { 74846492Swpaul int id; 74975373Salfred 75046492Swpaul id = CSR_READ_2(sc, WI_ALLOC_FID); 75146492Swpaul CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_ALLOC); 75246492Swpaul if (id == sc->wi_tx_data_id) 75346492Swpaul wi_txeof(sc, status); 75446492Swpaul } 75546492Swpaul 75646492Swpaul if (status & WI_EV_INFO) { 75746492Swpaul wi_update_stats(sc); 75846492Swpaul CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_INFO); 75946492Swpaul } 76046492Swpaul 76146492Swpaul if (status & WI_EV_TX_EXC) { 76246492Swpaul wi_txeof(sc, status); 76346492Swpaul CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_TX_EXC); 76446492Swpaul } 76546492Swpaul 76646492Swpaul if (status & WI_EV_INFO_DROP) { 76746492Swpaul CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_INFO_DROP); 76846492Swpaul } 76946492Swpaul 77046492Swpaul /* Re-enable interrupts. */ 77146492Swpaul CSR_WRITE_2(sc, WI_INT_EN, WI_INTRS); 77246492Swpaul 77375373Salfred if (ifp->if_snd.ifq_head != NULL) { 77446492Swpaul wi_start(ifp); 77575373Salfred } 77646492Swpaul 77767092Swpaul WI_UNLOCK(sc); 77867092Swpaul 77946492Swpaul return; 78046492Swpaul} 78146492Swpaul 78246492Swpaulstatic int wi_cmd(sc, cmd, val) 78346492Swpaul struct wi_softc *sc; 78446492Swpaul int cmd; 78546492Swpaul int val; 78646492Swpaul{ 78746492Swpaul int i, s = 0; 78846492Swpaul 78970073Swpaul /* wait for the busy bit to clear */ 79075331Simp for (i = 500; i > 0; i--) { /* 5s */ 79170073Swpaul if (!(CSR_READ_2(sc, WI_COMMAND) & WI_CMD_BUSY)) { 79270073Swpaul break; 79370073Swpaul } 79470073Swpaul DELAY(10*1000); /* 10 m sec */ 79570073Swpaul } 79675229Salfred if (i == 0) { 79770073Swpaul return(ETIMEDOUT); 79870073Swpaul } 79970073Swpaul 80046492Swpaul CSR_WRITE_2(sc, WI_PARAM0, val); 80170073Swpaul CSR_WRITE_2(sc, WI_PARAM1, 0); 80270073Swpaul CSR_WRITE_2(sc, WI_PARAM2, 0); 80346492Swpaul CSR_WRITE_2(sc, WI_COMMAND, cmd); 80446492Swpaul 80546492Swpaul for (i = 0; i < WI_TIMEOUT; i++) { 80646492Swpaul /* 80746492Swpaul * Wait for 'command complete' bit to be 80846492Swpaul * set in the event status register. 80946492Swpaul */ 81046492Swpaul s = CSR_READ_2(sc, WI_EVENT_STAT) & WI_EV_CMD; 81146492Swpaul if (s) { 81246492Swpaul /* Ack the event and read result code. */ 81346492Swpaul s = CSR_READ_2(sc, WI_STATUS); 81446492Swpaul CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_CMD); 81546492Swpaul#ifdef foo 81646492Swpaul if ((s & WI_CMD_CODE_MASK) != (cmd & WI_CMD_CODE_MASK)) 81746492Swpaul return(EIO); 81846492Swpaul#endif 81946492Swpaul if (s & WI_STAT_CMD_RESULT) 82046492Swpaul return(EIO); 82146492Swpaul break; 82246492Swpaul } 82375373Salfred if (cmd == WI_CMD_INI) 82475373Salfred DELAY(100); 82546492Swpaul } 82646492Swpaul 82746492Swpaul if (i == WI_TIMEOUT) 82846492Swpaul return(ETIMEDOUT); 82946492Swpaul 83046492Swpaul return(0); 83146492Swpaul} 83246492Swpaul 83346492Swpaulstatic void wi_reset(sc) 83446492Swpaul struct wi_softc *sc; 83546492Swpaul{ 83675331Simp#define WI_INIT_TRIES 5 83775150Simp int i; 83875149Simp 83975331Simp for (i = 0; i < WI_INIT_TRIES; i++) { 84075149Simp if (wi_cmd(sc, WI_CMD_INI, 0) == 0) 84175149Simp break; 84275331Simp DELAY(50 * 1000); /* 50ms */ 84375149Simp } 84475331Simp if (i == WI_INIT_TRIES) 84553702Swpaul device_printf(sc->dev, "init failed\n"); 84675373Salfred 84746492Swpaul CSR_WRITE_2(sc, WI_INT_EN, 0); 84846492Swpaul CSR_WRITE_2(sc, WI_EVENT_ACK, 0xFFFF); 84946492Swpaul 85046492Swpaul /* Calibrate timer. */ 85146492Swpaul WI_SETVAL(WI_RID_TICK_TIME, 8); 85270073Swpaul 85346492Swpaul return; 85446492Swpaul} 85546492Swpaul 85646492Swpaul/* 85746492Swpaul * Read an LTV record from the NIC. 85846492Swpaul */ 85946492Swpaulstatic int wi_read_record(sc, ltv) 86046492Swpaul struct wi_softc *sc; 86146492Swpaul struct wi_ltv_gen *ltv; 86246492Swpaul{ 86346492Swpaul u_int16_t *ptr; 86446492Swpaul int i, len, code; 86570073Swpaul struct wi_ltv_gen *oltv, p2ltv; 86646492Swpaul 86770073Swpaul oltv = ltv; 86870073Swpaul if (sc->wi_prism2) { 86970073Swpaul switch (ltv->wi_type) { 87070073Swpaul case WI_RID_ENCRYPTION: 87170073Swpaul p2ltv.wi_type = WI_RID_P2_ENCRYPTION; 87270073Swpaul p2ltv.wi_len = 2; 87370073Swpaul ltv = &p2ltv; 87470073Swpaul break; 87570073Swpaul case WI_RID_TX_CRYPT_KEY: 87670073Swpaul p2ltv.wi_type = WI_RID_P2_TX_CRYPT_KEY; 87770073Swpaul p2ltv.wi_len = 2; 87870073Swpaul ltv = &p2ltv; 87970073Swpaul break; 88070073Swpaul } 88170073Swpaul } 88270073Swpaul 88346492Swpaul /* Tell the NIC to enter record read mode. */ 88446492Swpaul if (wi_cmd(sc, WI_CMD_ACCESS|WI_ACCESS_READ, ltv->wi_type)) 88546492Swpaul return(EIO); 88646492Swpaul 88747789Swpaul /* Seek to the record. */ 88847789Swpaul if (wi_seek(sc, ltv->wi_type, 0, WI_BAP1)) 88947789Swpaul return(EIO); 89046492Swpaul 89146492Swpaul /* 89246492Swpaul * Read the length and record type and make sure they 89346492Swpaul * match what we expect (this verifies that we have enough 89447401Swpaul * room to hold all of the returned data). 89546492Swpaul */ 89646492Swpaul len = CSR_READ_2(sc, WI_DATA1); 89746492Swpaul if (len > ltv->wi_len) 89846492Swpaul return(ENOSPC); 89946492Swpaul code = CSR_READ_2(sc, WI_DATA1); 90046492Swpaul if (code != ltv->wi_type) 90146492Swpaul return(EIO); 90246492Swpaul 90346492Swpaul ltv->wi_len = len; 90446492Swpaul ltv->wi_type = code; 90546492Swpaul 90646492Swpaul /* Now read the data. */ 90746492Swpaul ptr = <v->wi_val; 90846492Swpaul for (i = 0; i < ltv->wi_len - 1; i++) 90946492Swpaul ptr[i] = CSR_READ_2(sc, WI_DATA1); 91046492Swpaul 91170073Swpaul if (sc->wi_prism2) { 91270073Swpaul switch (oltv->wi_type) { 91370073Swpaul case WI_RID_TX_RATE: 91470073Swpaul case WI_RID_CUR_TX_RATE: 91570073Swpaul switch (ltv->wi_val) { 91670073Swpaul case 1: oltv->wi_val = 1; break; 91770073Swpaul case 2: oltv->wi_val = 2; break; 91870073Swpaul case 3: oltv->wi_val = 6; break; 91970073Swpaul case 4: oltv->wi_val = 5; break; 92070073Swpaul case 7: oltv->wi_val = 7; break; 92170073Swpaul case 8: oltv->wi_val = 11; break; 92270073Swpaul case 15: oltv->wi_val = 3; break; 92370073Swpaul default: oltv->wi_val = 0x100 + ltv->wi_val; break; 92470073Swpaul } 92570073Swpaul break; 92670073Swpaul case WI_RID_ENCRYPTION: 92770073Swpaul oltv->wi_len = 2; 92870073Swpaul if (ltv->wi_val & 0x01) 92970073Swpaul oltv->wi_val = 1; 93070073Swpaul else 93170073Swpaul oltv->wi_val = 0; 93270073Swpaul break; 93370073Swpaul case WI_RID_TX_CRYPT_KEY: 93470073Swpaul oltv->wi_len = 2; 93570073Swpaul oltv->wi_val = ltv->wi_val; 93670073Swpaul break; 93770073Swpaul } 93870073Swpaul } 93970073Swpaul 94046492Swpaul return(0); 94146492Swpaul} 94246492Swpaul 94346492Swpaul/* 94446492Swpaul * Same as read, except we inject data instead of reading it. 94546492Swpaul */ 94646492Swpaulstatic int wi_write_record(sc, ltv) 94746492Swpaul struct wi_softc *sc; 94846492Swpaul struct wi_ltv_gen *ltv; 94946492Swpaul{ 95046492Swpaul u_int16_t *ptr; 95146492Swpaul int i; 95270073Swpaul struct wi_ltv_gen p2ltv; 95346492Swpaul 95470073Swpaul if (sc->wi_prism2) { 95570073Swpaul switch (ltv->wi_type) { 95670073Swpaul case WI_RID_TX_RATE: 95770073Swpaul p2ltv.wi_type = WI_RID_TX_RATE; 95870073Swpaul p2ltv.wi_len = 2; 95970073Swpaul switch (ltv->wi_val) { 96070073Swpaul case 1: p2ltv.wi_val = 1; break; 96170073Swpaul case 2: p2ltv.wi_val = 2; break; 96270073Swpaul case 3: p2ltv.wi_val = 15; break; 96370073Swpaul case 5: p2ltv.wi_val = 4; break; 96470073Swpaul case 6: p2ltv.wi_val = 3; break; 96570073Swpaul case 7: p2ltv.wi_val = 7; break; 96670073Swpaul case 11: p2ltv.wi_val = 8; break; 96770073Swpaul default: return EINVAL; 96870073Swpaul } 96970073Swpaul ltv = &p2ltv; 97070073Swpaul break; 97170073Swpaul case WI_RID_ENCRYPTION: 97270073Swpaul p2ltv.wi_type = WI_RID_P2_ENCRYPTION; 97370073Swpaul p2ltv.wi_len = 2; 97470073Swpaul if (ltv->wi_val) 97570073Swpaul p2ltv.wi_val = 0x03; 97670073Swpaul else 97770073Swpaul p2ltv.wi_val = 0x90; 97870073Swpaul ltv = &p2ltv; 97970073Swpaul break; 98070073Swpaul case WI_RID_TX_CRYPT_KEY: 98170073Swpaul p2ltv.wi_type = WI_RID_P2_TX_CRYPT_KEY; 98270073Swpaul p2ltv.wi_len = 2; 98370073Swpaul p2ltv.wi_val = ltv->wi_val; 98470073Swpaul ltv = &p2ltv; 98570073Swpaul break; 98670073Swpaul case WI_RID_DEFLT_CRYPT_KEYS: 98770073Swpaul { 98870073Swpaul int error; 98970073Swpaul struct wi_ltv_str ws; 99074998Swpaul struct wi_ltv_keys *wk = 99174998Swpaul (struct wi_ltv_keys *)ltv; 99274998Swpaul 99370073Swpaul for (i = 0; i < 4; i++) { 99470073Swpaul ws.wi_len = 4; 99570073Swpaul ws.wi_type = WI_RID_P2_CRYPT_KEY0 + i; 99674998Swpaul memcpy(ws.wi_str, 99774998Swpaul &wk->wi_keys[i].wi_keydat, 5); 99870073Swpaul ws.wi_str[5] = '\0'; 99970073Swpaul error = wi_write_record(sc, 100070073Swpaul (struct wi_ltv_gen *)&ws); 100170073Swpaul if (error) 100270073Swpaul return error; 100370073Swpaul } 100470073Swpaul return 0; 100570073Swpaul } 100670073Swpaul } 100770073Swpaul } 100870073Swpaul 100947789Swpaul if (wi_seek(sc, ltv->wi_type, 0, WI_BAP1)) 101047789Swpaul return(EIO); 101146492Swpaul 101246492Swpaul CSR_WRITE_2(sc, WI_DATA1, ltv->wi_len); 101346492Swpaul CSR_WRITE_2(sc, WI_DATA1, ltv->wi_type); 101446492Swpaul 101546492Swpaul ptr = <v->wi_val; 101646492Swpaul for (i = 0; i < ltv->wi_len - 1; i++) 101746492Swpaul CSR_WRITE_2(sc, WI_DATA1, ptr[i]); 101846492Swpaul 101946492Swpaul if (wi_cmd(sc, WI_CMD_ACCESS|WI_ACCESS_WRITE, ltv->wi_type)) 102046492Swpaul return(EIO); 102146492Swpaul 102246492Swpaul return(0); 102346492Swpaul} 102446492Swpaul 102546492Swpaulstatic int wi_seek(sc, id, off, chan) 102646492Swpaul struct wi_softc *sc; 102746492Swpaul int id, off, chan; 102846492Swpaul{ 102946492Swpaul int i; 103046492Swpaul int selreg, offreg; 103175373Salfred int status; 103246492Swpaul 103346492Swpaul switch (chan) { 103446492Swpaul case WI_BAP0: 103546492Swpaul selreg = WI_SEL0; 103646492Swpaul offreg = WI_OFF0; 103746492Swpaul break; 103846492Swpaul case WI_BAP1: 103946492Swpaul selreg = WI_SEL1; 104046492Swpaul offreg = WI_OFF1; 104146492Swpaul break; 104246492Swpaul default: 104353702Swpaul device_printf(sc->dev, "invalid data path: %x\n", chan); 104446492Swpaul return(EIO); 104546492Swpaul } 104646492Swpaul 104746492Swpaul CSR_WRITE_2(sc, selreg, id); 104846492Swpaul CSR_WRITE_2(sc, offreg, off); 104946492Swpaul 105046492Swpaul for (i = 0; i < WI_TIMEOUT; i++) { 105175373Salfred status = CSR_READ_2(sc, offreg); 105275373Salfred if (!(status & (WI_OFF_BUSY|WI_OFF_ERR))) 105346492Swpaul break; 105446492Swpaul } 105546492Swpaul 105675373Salfred if (i == WI_TIMEOUT) { 105775373Salfred device_printf(sc->dev, "timeout in wi_seek to %x/%x; last status %x\n", 105875373Salfred id, off, status); 105946492Swpaul return(ETIMEDOUT); 106075373Salfred } 106146492Swpaul 106246492Swpaul return(0); 106346492Swpaul} 106446492Swpaul 106546492Swpaulstatic int wi_read_data(sc, id, off, buf, len) 106646492Swpaul struct wi_softc *sc; 106746492Swpaul int id, off; 106846492Swpaul caddr_t buf; 106946492Swpaul int len; 107046492Swpaul{ 107146492Swpaul int i; 107246492Swpaul u_int16_t *ptr; 107346492Swpaul 107446492Swpaul if (wi_seek(sc, id, off, WI_BAP1)) 107546492Swpaul return(EIO); 107646492Swpaul 107746492Swpaul ptr = (u_int16_t *)buf; 107846492Swpaul for (i = 0; i < len / 2; i++) 107946492Swpaul ptr[i] = CSR_READ_2(sc, WI_DATA1); 108046492Swpaul 108146492Swpaul return(0); 108246492Swpaul} 108346492Swpaul 108446492Swpaul/* 108546492Swpaul * According to the comments in the HCF Light code, there is a bug in 108646492Swpaul * the Hermes (or possibly in certain Hermes firmware revisions) where 108746492Swpaul * the chip's internal autoincrement counter gets thrown off during 108846492Swpaul * data writes: the autoincrement is missed, causing one data word to 108946492Swpaul * be overwritten and subsequent words to be written to the wrong memory 109046492Swpaul * locations. The end result is that we could end up transmitting bogus 109146492Swpaul * frames without realizing it. The workaround for this is to write a 109246492Swpaul * couple of extra guard words after the end of the transfer, then 109346492Swpaul * attempt to read then back. If we fail to locate the guard words where 109446492Swpaul * we expect them, we preform the transfer over again. 109546492Swpaul */ 109646492Swpaulstatic int wi_write_data(sc, id, off, buf, len) 109746492Swpaul struct wi_softc *sc; 109846492Swpaul int id, off; 109946492Swpaul caddr_t buf; 110046492Swpaul int len; 110146492Swpaul{ 110246492Swpaul int i; 110346492Swpaul u_int16_t *ptr; 110474838Salfred#ifdef WI_HERMES_AUTOINC_WAR 110574838Salfred int retries; 110646492Swpaul 110775373Salfred retries = 512; 110846492Swpaulagain: 110946492Swpaul#endif 111046492Swpaul 111146492Swpaul if (wi_seek(sc, id, off, WI_BAP0)) 111246492Swpaul return(EIO); 111346492Swpaul 111446492Swpaul ptr = (u_int16_t *)buf; 111546492Swpaul for (i = 0; i < (len / 2); i++) 111646492Swpaul CSR_WRITE_2(sc, WI_DATA0, ptr[i]); 111746492Swpaul 111846492Swpaul#ifdef WI_HERMES_AUTOINC_WAR 111946492Swpaul CSR_WRITE_2(sc, WI_DATA0, 0x1234); 112046492Swpaul CSR_WRITE_2(sc, WI_DATA0, 0x5678); 112146492Swpaul 112246492Swpaul if (wi_seek(sc, id, off + len, WI_BAP0)) 112346492Swpaul return(EIO); 112446492Swpaul 112546492Swpaul if (CSR_READ_2(sc, WI_DATA0) != 0x1234 || 112674998Swpaul CSR_READ_2(sc, WI_DATA0) != 0x5678) { 112774838Salfred if (--retries >= 0) 112874838Salfred goto again; 112974838Salfred device_printf(sc->dev, "wi_write_data device timeout\n"); 113074838Salfred return (EIO); 113174838Salfred } 113246492Swpaul#endif 113346492Swpaul 113446492Swpaul return(0); 113546492Swpaul} 113646492Swpaul 113746492Swpaul/* 113846492Swpaul * Allocate a region of memory inside the NIC and zero 113946492Swpaul * it out. 114046492Swpaul */ 114146492Swpaulstatic int wi_alloc_nicmem(sc, len, id) 114246492Swpaul struct wi_softc *sc; 114346492Swpaul int len; 114446492Swpaul int *id; 114546492Swpaul{ 114646492Swpaul int i; 114746492Swpaul 114846492Swpaul if (wi_cmd(sc, WI_CMD_ALLOC_MEM, len)) { 114974998Swpaul device_printf(sc->dev, 115074998Swpaul "failed to allocate %d bytes on NIC\n", len); 115146492Swpaul return(ENOMEM); 115246492Swpaul } 115346492Swpaul 115446492Swpaul for (i = 0; i < WI_TIMEOUT; i++) { 115546492Swpaul if (CSR_READ_2(sc, WI_EVENT_STAT) & WI_EV_ALLOC) 115646492Swpaul break; 115746492Swpaul } 115846492Swpaul 115975373Salfred if (i == WI_TIMEOUT) { 116075373Salfred device_printf(sc->dev, "time out allocating memory on card\n"); 116146492Swpaul return(ETIMEDOUT); 116275373Salfred } 116346492Swpaul 116446492Swpaul CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_ALLOC); 116546492Swpaul *id = CSR_READ_2(sc, WI_ALLOC_FID); 116646492Swpaul 116775373Salfred if (wi_seek(sc, *id, 0, WI_BAP0)) { 116875373Salfred device_printf(sc->dev, "seek failed while allocating memory on card\n"); 116947789Swpaul return(EIO); 117075373Salfred } 117146492Swpaul 117246492Swpaul for (i = 0; i < len / 2; i++) 117346492Swpaul CSR_WRITE_2(sc, WI_DATA0, 0); 117446492Swpaul 117546492Swpaul return(0); 117646492Swpaul} 117746492Swpaul 117846492Swpaulstatic void wi_setmulti(sc) 117946492Swpaul struct wi_softc *sc; 118046492Swpaul{ 118146492Swpaul struct ifnet *ifp; 118246492Swpaul int i = 0; 118346492Swpaul struct ifmultiaddr *ifma; 118446492Swpaul struct wi_ltv_mcast mcast; 118546492Swpaul 118646492Swpaul ifp = &sc->arpcom.ac_if; 118746492Swpaul 118846492Swpaul bzero((char *)&mcast, sizeof(mcast)); 118946492Swpaul 119046492Swpaul mcast.wi_type = WI_RID_MCAST; 119146492Swpaul mcast.wi_len = (3 * 16) + 1; 119246492Swpaul 119346492Swpaul if (ifp->if_flags & IFF_ALLMULTI || ifp->if_flags & IFF_PROMISC) { 119446492Swpaul wi_write_record(sc, (struct wi_ltv_gen *)&mcast); 119546492Swpaul return; 119646492Swpaul } 119746492Swpaul 119872084Sphk TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { 119946492Swpaul if (ifma->ifma_addr->sa_family != AF_LINK) 120046492Swpaul continue; 120146492Swpaul if (i < 16) { 120246492Swpaul bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr), 120346492Swpaul (char *)&mcast.wi_mcast[i], ETHER_ADDR_LEN); 120446492Swpaul i++; 120546492Swpaul } else { 120646492Swpaul bzero((char *)&mcast, sizeof(mcast)); 120746492Swpaul break; 120846492Swpaul } 120946492Swpaul } 121046492Swpaul 121146492Swpaul mcast.wi_len = (i * 3) + 1; 121246492Swpaul wi_write_record(sc, (struct wi_ltv_gen *)&mcast); 121346492Swpaul 121446492Swpaul return; 121546492Swpaul} 121646492Swpaul 121746492Swpaulstatic void wi_setdef(sc, wreq) 121846492Swpaul struct wi_softc *sc; 121946492Swpaul struct wi_req *wreq; 122046492Swpaul{ 122146492Swpaul struct sockaddr_dl *sdl; 122246492Swpaul struct ifaddr *ifa; 122346492Swpaul struct ifnet *ifp; 122446492Swpaul 122546492Swpaul ifp = &sc->arpcom.ac_if; 122646492Swpaul 122746492Swpaul switch(wreq->wi_type) { 122846492Swpaul case WI_RID_MAC_NODE: 122946492Swpaul ifa = ifnet_addrs[ifp->if_index - 1]; 123046492Swpaul sdl = (struct sockaddr_dl *)ifa->ifa_addr; 123146492Swpaul bcopy((char *)&wreq->wi_val, (char *)&sc->arpcom.ac_enaddr, 123246492Swpaul ETHER_ADDR_LEN); 123346492Swpaul bcopy((char *)&wreq->wi_val, LLADDR(sdl), ETHER_ADDR_LEN); 123446492Swpaul break; 123546492Swpaul case WI_RID_PORTTYPE: 123646492Swpaul sc->wi_ptype = wreq->wi_val[0]; 123746492Swpaul break; 123846492Swpaul case WI_RID_TX_RATE: 123946492Swpaul sc->wi_tx_rate = wreq->wi_val[0]; 124046492Swpaul break; 124146492Swpaul case WI_RID_MAX_DATALEN: 124246492Swpaul sc->wi_max_data_len = wreq->wi_val[0]; 124346492Swpaul break; 124446492Swpaul case WI_RID_RTS_THRESH: 124546492Swpaul sc->wi_rts_thresh = wreq->wi_val[0]; 124646492Swpaul break; 124746492Swpaul case WI_RID_SYSTEM_SCALE: 124846492Swpaul sc->wi_ap_density = wreq->wi_val[0]; 124946492Swpaul break; 125046492Swpaul case WI_RID_CREATE_IBSS: 125146492Swpaul sc->wi_create_ibss = wreq->wi_val[0]; 125246492Swpaul break; 125346563Swpaul case WI_RID_OWN_CHNL: 125446563Swpaul sc->wi_channel = wreq->wi_val[0]; 125546563Swpaul break; 125646492Swpaul case WI_RID_NODENAME: 125746492Swpaul bzero(sc->wi_node_name, sizeof(sc->wi_node_name)); 125846492Swpaul bcopy((char *)&wreq->wi_val[1], sc->wi_node_name, 30); 125946492Swpaul break; 126046492Swpaul case WI_RID_DESIRED_SSID: 126146492Swpaul bzero(sc->wi_net_name, sizeof(sc->wi_net_name)); 126246492Swpaul bcopy((char *)&wreq->wi_val[1], sc->wi_net_name, 30); 126346492Swpaul break; 126446492Swpaul case WI_RID_OWN_SSID: 126546492Swpaul bzero(sc->wi_ibss_name, sizeof(sc->wi_ibss_name)); 126646492Swpaul bcopy((char *)&wreq->wi_val[1], sc->wi_ibss_name, 30); 126746492Swpaul break; 126846611Swpaul case WI_RID_PM_ENABLED: 126946611Swpaul sc->wi_pm_enabled = wreq->wi_val[0]; 127046611Swpaul break; 127146611Swpaul case WI_RID_MAX_SLEEP: 127246611Swpaul sc->wi_max_sleep = wreq->wi_val[0]; 127346611Swpaul break; 127456965Swpaul case WI_RID_ENCRYPTION: 127556965Swpaul sc->wi_use_wep = wreq->wi_val[0]; 127656965Swpaul break; 127756965Swpaul case WI_RID_TX_CRYPT_KEY: 127856965Swpaul sc->wi_tx_key = wreq->wi_val[0]; 127956965Swpaul break; 128056965Swpaul case WI_RID_DEFLT_CRYPT_KEYS: 128156965Swpaul bcopy((char *)wreq, (char *)&sc->wi_keys, 128256965Swpaul sizeof(struct wi_ltv_keys)); 128356965Swpaul break; 128446492Swpaul default: 128546492Swpaul break; 128646492Swpaul } 128746492Swpaul 128846563Swpaul /* Reinitialize WaveLAN. */ 128946563Swpaul wi_init(sc); 129046563Swpaul 129146492Swpaul return; 129246492Swpaul} 129346492Swpaul 129446492Swpaulstatic int wi_ioctl(ifp, command, data) 129546492Swpaul struct ifnet *ifp; 129646492Swpaul u_long command; 129746492Swpaul caddr_t data; 129846492Swpaul{ 129967092Swpaul int error = 0; 130046492Swpaul struct wi_softc *sc; 130146492Swpaul struct wi_req wreq; 130246492Swpaul struct ifreq *ifr; 130361818Sroberto struct proc *p = curproc; 130446492Swpaul 130546492Swpaul sc = ifp->if_softc; 130667092Swpaul WI_LOCK(sc); 130746492Swpaul ifr = (struct ifreq *)data; 130846492Swpaul 130961818Sroberto if (sc->wi_gone) { 131061818Sroberto error = ENODEV; 131161818Sroberto goto out; 131261818Sroberto } 131346492Swpaul 131446492Swpaul switch(command) { 131546492Swpaul case SIOCSIFADDR: 131646492Swpaul case SIOCGIFADDR: 131746492Swpaul case SIOCSIFMTU: 131846492Swpaul error = ether_ioctl(ifp, command, data); 131946492Swpaul break; 132046492Swpaul case SIOCSIFFLAGS: 132146492Swpaul if (ifp->if_flags & IFF_UP) { 132246492Swpaul if (ifp->if_flags & IFF_RUNNING && 132346492Swpaul ifp->if_flags & IFF_PROMISC && 132446492Swpaul !(sc->wi_if_flags & IFF_PROMISC)) { 132546492Swpaul WI_SETVAL(WI_RID_PROMISC, 1); 132646492Swpaul } else if (ifp->if_flags & IFF_RUNNING && 132746492Swpaul !(ifp->if_flags & IFF_PROMISC) && 132846492Swpaul sc->wi_if_flags & IFF_PROMISC) { 132946492Swpaul WI_SETVAL(WI_RID_PROMISC, 0); 133046492Swpaul } else 133146492Swpaul wi_init(sc); 133246492Swpaul } else { 133346492Swpaul if (ifp->if_flags & IFF_RUNNING) { 133446492Swpaul wi_stop(sc); 133546492Swpaul } 133646492Swpaul } 133746492Swpaul sc->wi_if_flags = ifp->if_flags; 133846492Swpaul error = 0; 133946492Swpaul break; 134046492Swpaul case SIOCADDMULTI: 134146492Swpaul case SIOCDELMULTI: 134246492Swpaul wi_setmulti(sc); 134346492Swpaul error = 0; 134446492Swpaul break; 134546492Swpaul case SIOCGWAVELAN: 134646492Swpaul error = copyin(ifr->ifr_data, &wreq, sizeof(wreq)); 134746492Swpaul if (error) 134846492Swpaul break; 134965581Swpaul /* Don't show WEP keys to non-root users. */ 135065581Swpaul if (wreq.wi_type == WI_RID_DEFLT_CRYPT_KEYS && suser(p)) 135165581Swpaul break; 135246492Swpaul if (wreq.wi_type == WI_RID_IFACE_STATS) { 135346492Swpaul bcopy((char *)&sc->wi_stats, (char *)&wreq.wi_val, 135446492Swpaul sizeof(sc->wi_stats)); 135546492Swpaul wreq.wi_len = (sizeof(sc->wi_stats) / 2) + 1; 135656965Swpaul } else if (wreq.wi_type == WI_RID_DEFLT_CRYPT_KEYS) { 135756965Swpaul bcopy((char *)&sc->wi_keys, (char *)&wreq, 135856965Swpaul sizeof(struct wi_ltv_keys)); 135953702Swpaul } 136053702Swpaul#ifdef WICACHE 136153702Swpaul else if (wreq.wi_type == WI_RID_ZERO_CACHE) { 136253702Swpaul sc->wi_sigitems = sc->wi_nextitem = 0; 136353702Swpaul } else if (wreq.wi_type == WI_RID_READ_CACHE) { 136453702Swpaul char *pt = (char *)&wreq.wi_val; 136553702Swpaul bcopy((char *)&sc->wi_sigitems, 136653702Swpaul (char *)pt, sizeof(int)); 136753702Swpaul pt += (sizeof (int)); 136853702Swpaul wreq.wi_len = sizeof(int) / 2; 136953702Swpaul bcopy((char *)&sc->wi_sigcache, (char *)pt, 137053702Swpaul sizeof(struct wi_sigcache) * sc->wi_sigitems); 137153702Swpaul wreq.wi_len += ((sizeof(struct wi_sigcache) * 137253702Swpaul sc->wi_sigitems) / 2) + 1; 137353702Swpaul } 137453702Swpaul#endif 137553702Swpaul else { 137646492Swpaul if (wi_read_record(sc, (struct wi_ltv_gen *)&wreq)) { 137746492Swpaul error = EINVAL; 137846492Swpaul break; 137946492Swpaul } 138046492Swpaul } 138146492Swpaul error = copyout(&wreq, ifr->ifr_data, sizeof(wreq)); 138246492Swpaul break; 138346492Swpaul case SIOCSWAVELAN: 138461818Sroberto if ((error = suser(p))) 138561818Sroberto goto out; 138646492Swpaul error = copyin(ifr->ifr_data, &wreq, sizeof(wreq)); 138746492Swpaul if (error) 138846492Swpaul break; 138946492Swpaul if (wreq.wi_type == WI_RID_IFACE_STATS) { 139046492Swpaul error = EINVAL; 139146492Swpaul break; 139246492Swpaul } else if (wreq.wi_type == WI_RID_MGMT_XMIT) { 139346492Swpaul error = wi_mgmt_xmit(sc, (caddr_t)&wreq.wi_val, 139446492Swpaul wreq.wi_len); 139546492Swpaul } else { 139646492Swpaul error = wi_write_record(sc, (struct wi_ltv_gen *)&wreq); 139746492Swpaul if (!error) 139846492Swpaul wi_setdef(sc, &wreq); 139946492Swpaul } 140046492Swpaul break; 140146492Swpaul default: 140246492Swpaul error = EINVAL; 140346492Swpaul break; 140446492Swpaul } 140561818Srobertoout: 140667092Swpaul WI_UNLOCK(sc); 140746492Swpaul 140846492Swpaul return(error); 140946492Swpaul} 141046492Swpaul 141146492Swpaulstatic void wi_init(xsc) 141246492Swpaul void *xsc; 141346492Swpaul{ 141446492Swpaul struct wi_softc *sc = xsc; 141546492Swpaul struct ifnet *ifp = &sc->arpcom.ac_if; 141646492Swpaul struct wi_ltv_macaddr mac; 141746492Swpaul int id = 0; 141846492Swpaul 141967092Swpaul WI_LOCK(sc); 142067092Swpaul 142167092Swpaul if (sc->wi_gone) { 142267092Swpaul WI_UNLOCK(sc); 142346492Swpaul return; 142467092Swpaul } 142546492Swpaul 142646492Swpaul if (ifp->if_flags & IFF_RUNNING) 142746492Swpaul wi_stop(sc); 142846492Swpaul 142946492Swpaul wi_reset(sc); 143046492Swpaul 143146492Swpaul /* Program max data length. */ 143246492Swpaul WI_SETVAL(WI_RID_MAX_DATALEN, sc->wi_max_data_len); 143346492Swpaul 143447401Swpaul /* Enable/disable IBSS creation. */ 143546492Swpaul WI_SETVAL(WI_RID_CREATE_IBSS, sc->wi_create_ibss); 143646492Swpaul 143746492Swpaul /* Set the port type. */ 143846492Swpaul WI_SETVAL(WI_RID_PORTTYPE, sc->wi_ptype); 143946492Swpaul 144046492Swpaul /* Program the RTS/CTS threshold. */ 144146492Swpaul WI_SETVAL(WI_RID_RTS_THRESH, sc->wi_rts_thresh); 144246492Swpaul 144346492Swpaul /* Program the TX rate */ 144446492Swpaul WI_SETVAL(WI_RID_TX_RATE, sc->wi_tx_rate); 144546492Swpaul 144646492Swpaul /* Access point density */ 144746492Swpaul WI_SETVAL(WI_RID_SYSTEM_SCALE, sc->wi_ap_density); 144846492Swpaul 144946611Swpaul /* Power Management Enabled */ 145046611Swpaul WI_SETVAL(WI_RID_PM_ENABLED, sc->wi_pm_enabled); 145146611Swpaul 145246611Swpaul /* Power Managment Max Sleep */ 145346611Swpaul WI_SETVAL(WI_RID_MAX_SLEEP, sc->wi_max_sleep); 145446611Swpaul 145546492Swpaul /* Specify the IBSS name */ 145646492Swpaul WI_SETSTR(WI_RID_OWN_SSID, sc->wi_ibss_name); 145746492Swpaul 145846492Swpaul /* Specify the network name */ 145946492Swpaul WI_SETSTR(WI_RID_DESIRED_SSID, sc->wi_net_name); 146046492Swpaul 146146563Swpaul /* Specify the frequency to use */ 146246563Swpaul WI_SETVAL(WI_RID_OWN_CHNL, sc->wi_channel); 146346563Swpaul 146446492Swpaul /* Program the nodename. */ 146546492Swpaul WI_SETSTR(WI_RID_NODENAME, sc->wi_node_name); 146646492Swpaul 146746492Swpaul /* Set our MAC address. */ 146846492Swpaul mac.wi_len = 4; 146946492Swpaul mac.wi_type = WI_RID_MAC_NODE; 147046492Swpaul bcopy((char *)&sc->arpcom.ac_enaddr, 147146492Swpaul (char *)&mac.wi_mac_addr, ETHER_ADDR_LEN); 147246492Swpaul wi_write_record(sc, (struct wi_ltv_gen *)&mac); 147346492Swpaul 147456965Swpaul /* Configure WEP. */ 147556965Swpaul if (sc->wi_has_wep) { 147656965Swpaul WI_SETVAL(WI_RID_ENCRYPTION, sc->wi_use_wep); 147756965Swpaul WI_SETVAL(WI_RID_TX_CRYPT_KEY, sc->wi_tx_key); 147856965Swpaul sc->wi_keys.wi_len = (sizeof(struct wi_ltv_keys) / 2) + 1; 147956965Swpaul sc->wi_keys.wi_type = WI_RID_DEFLT_CRYPT_KEYS; 148056965Swpaul wi_write_record(sc, (struct wi_ltv_gen *)&sc->wi_keys); 148156965Swpaul } 148256965Swpaul 148346492Swpaul /* Initialize promisc mode. */ 148446492Swpaul if (ifp->if_flags & IFF_PROMISC) { 148546492Swpaul WI_SETVAL(WI_RID_PROMISC, 1); 148646492Swpaul } else { 148746492Swpaul WI_SETVAL(WI_RID_PROMISC, 0); 148846492Swpaul } 148946492Swpaul 149046492Swpaul /* Set multicast filter. */ 149146492Swpaul wi_setmulti(sc); 149246492Swpaul 149346492Swpaul /* Enable desired port */ 149446492Swpaul wi_cmd(sc, WI_CMD_ENABLE|sc->wi_portnum, 0); 149546492Swpaul 149675373Salfred if (wi_alloc_nicmem(sc, ETHER_MAX_LEN + sizeof(struct wi_frame) + 8, &id)) 149753702Swpaul device_printf(sc->dev, "tx buffer allocation failed\n"); 149846492Swpaul sc->wi_tx_data_id = id; 149946492Swpaul 150075373Salfred if (wi_alloc_nicmem(sc, ETHER_MAX_LEN + sizeof(struct wi_frame) + 8, &id)) 150153702Swpaul device_printf(sc->dev, "mgmt. buffer allocation failed\n"); 150246492Swpaul sc->wi_tx_mgmt_id = id; 150346492Swpaul 150446492Swpaul /* enable interrupts */ 150546492Swpaul CSR_WRITE_2(sc, WI_INT_EN, WI_INTRS); 150646492Swpaul 150746492Swpaul ifp->if_flags |= IFF_RUNNING; 150846492Swpaul ifp->if_flags &= ~IFF_OACTIVE; 150946492Swpaul 151046492Swpaul sc->wi_stat_ch = timeout(wi_inquire, sc, hz * 60); 151167092Swpaul WI_UNLOCK(sc); 151246492Swpaul 151346492Swpaul return; 151446492Swpaul} 151546492Swpaul 151646492Swpaulstatic void wi_start(ifp) 151746492Swpaul struct ifnet *ifp; 151846492Swpaul{ 151946492Swpaul struct wi_softc *sc; 152046492Swpaul struct mbuf *m0; 152146492Swpaul struct wi_frame tx_frame; 152246492Swpaul struct ether_header *eh; 152346492Swpaul int id; 152446492Swpaul 152546492Swpaul sc = ifp->if_softc; 152667092Swpaul WI_LOCK(sc); 152746492Swpaul 152867092Swpaul if (sc->wi_gone) { 152967092Swpaul WI_UNLOCK(sc); 153046492Swpaul return; 153167092Swpaul } 153246492Swpaul 153367092Swpaul if (ifp->if_flags & IFF_OACTIVE) { 153467092Swpaul WI_UNLOCK(sc); 153546492Swpaul return; 153667092Swpaul } 153746492Swpaul 153846492Swpaul IF_DEQUEUE(&ifp->if_snd, m0); 153967092Swpaul if (m0 == NULL) { 154067092Swpaul WI_UNLOCK(sc); 154146492Swpaul return; 154267092Swpaul } 154346492Swpaul 154446492Swpaul bzero((char *)&tx_frame, sizeof(tx_frame)); 154546492Swpaul id = sc->wi_tx_data_id; 154646492Swpaul eh = mtod(m0, struct ether_header *); 154746492Swpaul 154846492Swpaul /* 154947401Swpaul * Use RFC1042 encoding for IP and ARP datagrams, 155046492Swpaul * 802.3 for anything else. 155146492Swpaul */ 155275275Salfred if (ntohs(eh->ether_type) > ETHER_MAX_LEN) { 155346492Swpaul bcopy((char *)&eh->ether_dhost, 155446492Swpaul (char *)&tx_frame.wi_addr1, ETHER_ADDR_LEN); 155546492Swpaul bcopy((char *)&eh->ether_shost, 155646492Swpaul (char *)&tx_frame.wi_addr2, ETHER_ADDR_LEN); 155746492Swpaul bcopy((char *)&eh->ether_dhost, 155846492Swpaul (char *)&tx_frame.wi_dst_addr, ETHER_ADDR_LEN); 155946492Swpaul bcopy((char *)&eh->ether_shost, 156046492Swpaul (char *)&tx_frame.wi_src_addr, ETHER_ADDR_LEN); 156146492Swpaul 156246492Swpaul tx_frame.wi_dat_len = m0->m_pkthdr.len - WI_SNAPHDR_LEN; 156346492Swpaul tx_frame.wi_frame_ctl = WI_FTYPE_DATA; 156446492Swpaul tx_frame.wi_dat[0] = htons(WI_SNAP_WORD0); 156546492Swpaul tx_frame.wi_dat[1] = htons(WI_SNAP_WORD1); 156646492Swpaul tx_frame.wi_len = htons(m0->m_pkthdr.len - WI_SNAPHDR_LEN); 156746492Swpaul tx_frame.wi_type = eh->ether_type; 156846492Swpaul 156946492Swpaul m_copydata(m0, sizeof(struct ether_header), 157046492Swpaul m0->m_pkthdr.len - sizeof(struct ether_header), 157146492Swpaul (caddr_t)&sc->wi_txbuf); 157246492Swpaul 157346492Swpaul wi_write_data(sc, id, 0, (caddr_t)&tx_frame, 157446492Swpaul sizeof(struct wi_frame)); 157546492Swpaul wi_write_data(sc, id, WI_802_11_OFFSET, (caddr_t)&sc->wi_txbuf, 157646492Swpaul (m0->m_pkthdr.len - sizeof(struct ether_header)) + 2); 157746492Swpaul } else { 157846492Swpaul tx_frame.wi_dat_len = m0->m_pkthdr.len; 157946492Swpaul 158055831Swpaul eh->ether_type = htons(m0->m_pkthdr.len - WI_SNAPHDR_LEN); 158146492Swpaul m_copydata(m0, 0, m0->m_pkthdr.len, (caddr_t)&sc->wi_txbuf); 158246492Swpaul 158346492Swpaul wi_write_data(sc, id, 0, (caddr_t)&tx_frame, 158446492Swpaul sizeof(struct wi_frame)); 158546492Swpaul wi_write_data(sc, id, WI_802_3_OFFSET, (caddr_t)&sc->wi_txbuf, 158646492Swpaul m0->m_pkthdr.len + 2); 158746492Swpaul } 158846492Swpaul 158946492Swpaul /* 159046492Swpaul * If there's a BPF listner, bounce a copy of 159146492Swpaul * this frame to him. 159246492Swpaul */ 159346492Swpaul if (ifp->if_bpf) 159446492Swpaul bpf_mtap(ifp, m0); 159546492Swpaul 159646492Swpaul m_freem(m0); 159746492Swpaul 159846492Swpaul if (wi_cmd(sc, WI_CMD_TX|WI_RECLAIM, id)) 159953702Swpaul device_printf(sc->dev, "xmit failed\n"); 160046492Swpaul 160146492Swpaul ifp->if_flags |= IFF_OACTIVE; 160246492Swpaul 160346492Swpaul /* 160446492Swpaul * Set a timeout in case the chip goes out to lunch. 160546492Swpaul */ 160646492Swpaul ifp->if_timer = 5; 160746492Swpaul 160867092Swpaul WI_UNLOCK(sc); 160946492Swpaul return; 161046492Swpaul} 161146492Swpaul 161246492Swpaulstatic int wi_mgmt_xmit(sc, data, len) 161346492Swpaul struct wi_softc *sc; 161446492Swpaul caddr_t data; 161546492Swpaul int len; 161646492Swpaul{ 161746492Swpaul struct wi_frame tx_frame; 161846492Swpaul int id; 161946492Swpaul struct wi_80211_hdr *hdr; 162046492Swpaul caddr_t dptr; 162146492Swpaul 162246492Swpaul if (sc->wi_gone) 162346492Swpaul return(ENODEV); 162446492Swpaul 162546492Swpaul hdr = (struct wi_80211_hdr *)data; 162646492Swpaul dptr = data + sizeof(struct wi_80211_hdr); 162746492Swpaul 162846492Swpaul bzero((char *)&tx_frame, sizeof(tx_frame)); 162946492Swpaul id = sc->wi_tx_mgmt_id; 163046492Swpaul 163146492Swpaul bcopy((char *)hdr, (char *)&tx_frame.wi_frame_ctl, 163246492Swpaul sizeof(struct wi_80211_hdr)); 163346492Swpaul 163446492Swpaul tx_frame.wi_dat_len = len - WI_SNAPHDR_LEN; 163546492Swpaul tx_frame.wi_len = htons(len - WI_SNAPHDR_LEN); 163646492Swpaul 163746492Swpaul wi_write_data(sc, id, 0, (caddr_t)&tx_frame, sizeof(struct wi_frame)); 163846492Swpaul wi_write_data(sc, id, WI_802_11_OFFSET_RAW, dptr, 163946492Swpaul (len - sizeof(struct wi_80211_hdr)) + 2); 164046492Swpaul 164146492Swpaul if (wi_cmd(sc, WI_CMD_TX|WI_RECLAIM, id)) { 164253702Swpaul device_printf(sc->dev, "xmit failed\n"); 164346492Swpaul return(EIO); 164446492Swpaul } 164546492Swpaul 164646492Swpaul return(0); 164746492Swpaul} 164846492Swpaul 164946492Swpaulstatic void wi_stop(sc) 165046492Swpaul struct wi_softc *sc; 165146492Swpaul{ 165246492Swpaul struct ifnet *ifp; 165346492Swpaul 165467092Swpaul WI_LOCK(sc); 165567092Swpaul 165667092Swpaul if (sc->wi_gone) { 165767092Swpaul WI_UNLOCK(sc); 165846492Swpaul return; 165967092Swpaul } 166046492Swpaul 166146492Swpaul ifp = &sc->arpcom.ac_if; 166246492Swpaul 166370173Sjhb /* 166470173Sjhb * If the card is gone and the memory port isn't mapped, we will 166570173Sjhb * (hopefully) get 0xffff back from the status read, which is not 166670173Sjhb * a valid status value. 166770173Sjhb */ 166870173Sjhb if (CSR_READ_2(sc, WI_STATUS) != 0xffff) { 166970173Sjhb CSR_WRITE_2(sc, WI_INT_EN, 0); 167070173Sjhb wi_cmd(sc, WI_CMD_DISABLE|sc->wi_portnum, 0); 167170173Sjhb } 167246492Swpaul 167346492Swpaul untimeout(wi_inquire, sc, sc->wi_stat_ch); 167446492Swpaul 167546492Swpaul ifp->if_flags &= ~(IFF_RUNNING|IFF_OACTIVE); 167646492Swpaul 167767092Swpaul WI_UNLOCK(sc); 167846492Swpaul return; 167946492Swpaul} 168046492Swpaul 168146492Swpaulstatic void wi_watchdog(ifp) 168246492Swpaul struct ifnet *ifp; 168346492Swpaul{ 168446492Swpaul struct wi_softc *sc; 168546492Swpaul 168646492Swpaul sc = ifp->if_softc; 168746492Swpaul 168875199Salfred device_printf(sc->dev, "watchdog timeout\n"); 168946492Swpaul 169046492Swpaul wi_init(sc); 169146492Swpaul 169246492Swpaul ifp->if_oerrors++; 169346492Swpaul 169446492Swpaul return; 169546492Swpaul} 169646492Swpaul 169774906Salfredstatic int 169874906Salfredwi_alloc(dev, io_rid) 169953702Swpaul device_t dev; 170074906Salfred int io_rid; 170146492Swpaul{ 170253702Swpaul struct wi_softc *sc = device_get_softc(dev); 170353702Swpaul 170474906Salfred sc->iobase_rid = io_rid; 170574906Salfred sc->iobase = bus_alloc_resource(dev, SYS_RES_IOPORT, &sc->iobase_rid, 170670073Swpaul 0, ~0, (1 << 6), 170770073Swpaul rman_make_alignment_flags(1 << 6) | RF_ACTIVE); 170853702Swpaul if (!sc->iobase) { 170953702Swpaul device_printf(dev, "No I/O space?!\n"); 171053702Swpaul return (ENXIO); 171153702Swpaul } 171253702Swpaul 171374906Salfred sc->irq_rid = 0; 171474906Salfred sc->irq = bus_alloc_resource(dev, SYS_RES_IRQ, &sc->irq_rid, 171553702Swpaul 0, ~0, 1, RF_ACTIVE); 171653702Swpaul if (!sc->irq) { 171775219Salfred wi_free(dev); 171853702Swpaul device_printf(dev, "No irq?!\n"); 171953702Swpaul return (ENXIO); 172053702Swpaul } 172153702Swpaul 172253702Swpaul sc->dev = dev; 172353702Swpaul sc->wi_unit = device_get_unit(dev); 172453702Swpaul sc->wi_io_addr = rman_get_start(sc->iobase); 172553702Swpaul sc->wi_btag = rman_get_bustag(sc->iobase); 172653702Swpaul sc->wi_bhandle = rman_get_bushandle(sc->iobase); 172753702Swpaul 172853702Swpaul return (0); 172953702Swpaul} 173053702Swpaul 173153702Swpaulstatic void wi_free(dev) 173253702Swpaul device_t dev; 173353702Swpaul{ 173453702Swpaul struct wi_softc *sc = device_get_softc(dev); 173553702Swpaul 173675219Salfred if (sc->iobase != NULL) { 173775219Salfred bus_release_resource(dev, SYS_RES_IOPORT, sc->iobase_rid, sc->iobase); 173875219Salfred sc->iobase = NULL; 173975219Salfred } 174075219Salfred if (sc->irq != NULL) { 174175219Salfred bus_release_resource(dev, SYS_RES_IRQ, sc->irq_rid, sc->irq); 174275219Salfred sc->irq = NULL; 174375219Salfred } 174475219Salfred if (sc->mem != NULL) { 174574906Salfred bus_release_resource(dev, SYS_RES_MEMORY, sc->mem_rid, sc->mem); 174675219Salfred sc->mem = NULL; 174775219Salfred } 174853702Swpaul 174953702Swpaul return; 175053702Swpaul} 175153702Swpaul 175253702Swpaulstatic void wi_shutdown(dev) 175353702Swpaul device_t dev; 175453702Swpaul{ 175546492Swpaul struct wi_softc *sc; 175646492Swpaul 175753702Swpaul sc = device_get_softc(dev); 175846492Swpaul wi_stop(sc); 175946492Swpaul 176046492Swpaul return; 176146492Swpaul} 176253702Swpaul 176353702Swpaul#ifdef WICACHE 176453702Swpaul/* wavelan signal strength cache code. 176553702Swpaul * store signal/noise/quality on per MAC src basis in 176653702Swpaul * a small fixed cache. The cache wraps if > MAX slots 176753702Swpaul * used. The cache may be zeroed out to start over. 176853702Swpaul * Two simple filters exist to reduce computation: 176953702Swpaul * 1. ip only (literally 0x800) which may be used 177053702Swpaul * to ignore some packets. It defaults to ip only. 177153702Swpaul * it could be used to focus on broadcast, non-IP 802.11 beacons. 177253702Swpaul * 2. multicast/broadcast only. This may be used to 177353702Swpaul * ignore unicast packets and only cache signal strength 177453702Swpaul * for multicast/broadcast packets (beacons); e.g., Mobile-IP 177553702Swpaul * beacons and not unicast traffic. 177653702Swpaul * 177753702Swpaul * The cache stores (MAC src(index), IP src (major clue), signal, 177853702Swpaul * quality, noise) 177953702Swpaul * 178053702Swpaul * No apologies for storing IP src here. It's easy and saves much 178153702Swpaul * trouble elsewhere. The cache is assumed to be INET dependent, 178253702Swpaul * although it need not be. 178353702Swpaul */ 178453702Swpaul 178553702Swpaul#ifdef documentation 178653702Swpaul 178753702Swpaulint wi_sigitems; /* number of cached entries */ 178853702Swpaulstruct wi_sigcache wi_sigcache[MAXWICACHE]; /* array of cache entries */ 178953702Swpaulint wi_nextitem; /* index/# of entries */ 179053702Swpaul 179153702Swpaul 179253702Swpaul#endif 179353702Swpaul 179453702Swpaul/* control variables for cache filtering. Basic idea is 179553702Swpaul * to reduce cost (e.g., to only Mobile-IP agent beacons 179653702Swpaul * which are broadcast or multicast). Still you might 179753702Swpaul * want to measure signal strength with unicast ping packets 179853702Swpaul * on a pt. to pt. ant. setup. 179953702Swpaul */ 180053702Swpaul/* set true if you want to limit cache items to broadcast/mcast 180153702Swpaul * only packets (not unicast). Useful for mobile-ip beacons which 180253702Swpaul * are broadcast/multicast at network layer. Default is all packets 180353702Swpaul * so ping/unicast will work say with pt. to pt. antennae setup. 180453702Swpaul */ 180553702Swpaulstatic int wi_cache_mcastonly = 0; 180653702SwpaulSYSCTL_INT(_machdep, OID_AUTO, wi_cache_mcastonly, CTLFLAG_RW, 180753702Swpaul &wi_cache_mcastonly, 0, ""); 180853702Swpaul 180953702Swpaul/* set true if you want to limit cache items to IP packets only 181053702Swpaul*/ 181153702Swpaulstatic int wi_cache_iponly = 1; 181253702SwpaulSYSCTL_INT(_machdep, OID_AUTO, wi_cache_iponly, CTLFLAG_RW, 181353702Swpaul &wi_cache_iponly, 0, ""); 181453702Swpaul 181553702Swpaul/* 181653702Swpaul * Original comments: 181753702Swpaul * ----------------- 181853702Swpaul * wi_cache_store, per rx packet store signal 181953702Swpaul * strength in MAC (src) indexed cache. 182053702Swpaul * 182153702Swpaul * follows linux driver in how signal strength is computed. 182253702Swpaul * In ad hoc mode, we use the rx_quality field. 182353702Swpaul * signal and noise are trimmed to fit in the range from 47..138. 182453702Swpaul * rx_quality field MSB is signal strength. 182553702Swpaul * rx_quality field LSB is noise. 182653702Swpaul * "quality" is (signal - noise) as is log value. 182753702Swpaul * note: quality CAN be negative. 182853702Swpaul * 182953702Swpaul * In BSS mode, we use the RID for communication quality. 183053702Swpaul * TBD: BSS mode is currently untested. 183153702Swpaul * 183253702Swpaul * Bill's comments: 183353702Swpaul * --------------- 183453702Swpaul * Actually, we use the rx_quality field all the time for both "ad-hoc" 183553702Swpaul * and BSS modes. Why? Because reading an RID is really, really expensive: 183653702Swpaul * there's a bunch of PIO operations that have to be done to read a record 183753702Swpaul * from the NIC, and reading the comms quality RID each time a packet is 183853702Swpaul * received can really hurt performance. We don't have to do this anyway: 183953702Swpaul * the comms quality field only reflects the values in the rx_quality field 184053702Swpaul * anyway. The comms quality RID is only meaningful in infrastructure mode, 184153702Swpaul * but the values it contains are updated based on the rx_quality from 184253702Swpaul * frames received from the access point. 184353702Swpaul * 184453702Swpaul * Also, according to Lucent, the signal strength and noise level values 184553702Swpaul * can be converted to dBms by subtracting 149, so I've modified the code 184653702Swpaul * to do that instead of the scaling it did originally. 184753702Swpaul */ 184853702Swpaulstatic 184953702Swpaulvoid wi_cache_store (struct wi_softc *sc, struct ether_header *eh, 185053702Swpaul struct mbuf *m, unsigned short rx_quality) 185153702Swpaul{ 185253702Swpaul struct ip *ip = 0; 185353702Swpaul int i; 185453702Swpaul static int cache_slot = 0; /* use this cache entry */ 185553702Swpaul static int wrapindex = 0; /* next "free" cache entry */ 185653702Swpaul int sig, noise; 185753702Swpaul int sawip=0; 185853702Swpaul 185953702Swpaul /* filters: 186053702Swpaul * 1. ip only 186153702Swpaul * 2. configurable filter to throw out unicast packets, 186253702Swpaul * keep multicast only. 186353702Swpaul */ 186453702Swpaul 186575276Salfred if ((ntohs(eh->ether_type) == ETHERTYPE_IP)) { 186653702Swpaul sawip = 1; 186753702Swpaul } 186853702Swpaul 186953702Swpaul /* filter for ip packets only 187053702Swpaul */ 187153702Swpaul if (wi_cache_iponly && !sawip) { 187253702Swpaul return; 187353702Swpaul } 187453702Swpaul 187553702Swpaul /* filter for broadcast/multicast only 187653702Swpaul */ 187753702Swpaul if (wi_cache_mcastonly && ((eh->ether_dhost[0] & 1) == 0)) { 187853702Swpaul return; 187953702Swpaul } 188053702Swpaul 188153702Swpaul#ifdef SIGDEBUG 188253702Swpaul printf("wi%d: q value %x (MSB=0x%x, LSB=0x%x) \n", sc->wi_unit, 188353702Swpaul rx_quality & 0xffff, rx_quality >> 8, rx_quality & 0xff); 188453702Swpaul#endif 188553702Swpaul 188653702Swpaul /* find the ip header. we want to store the ip_src 188753702Swpaul * address. 188853702Swpaul */ 188953702Swpaul if (sawip) { 189053702Swpaul ip = mtod(m, struct ip *); 189153702Swpaul } 189253702Swpaul 189353702Swpaul /* do a linear search for a matching MAC address 189453702Swpaul * in the cache table 189553702Swpaul * . MAC address is 6 bytes, 189653702Swpaul * . var w_nextitem holds total number of entries already cached 189753702Swpaul */ 189853702Swpaul for(i = 0; i < sc->wi_nextitem; i++) { 189953702Swpaul if (! bcmp(eh->ether_shost , sc->wi_sigcache[i].macsrc, 6 )) { 190053702Swpaul /* Match!, 190153702Swpaul * so we already have this entry, 190253702Swpaul * update the data 190353702Swpaul */ 190453702Swpaul break; 190553702Swpaul } 190653702Swpaul } 190753702Swpaul 190853702Swpaul /* did we find a matching mac address? 190953702Swpaul * if yes, then overwrite a previously existing cache entry 191053702Swpaul */ 191153702Swpaul if (i < sc->wi_nextitem ) { 191253702Swpaul cache_slot = i; 191353702Swpaul } 191453702Swpaul /* else, have a new address entry,so 191553702Swpaul * add this new entry, 191653702Swpaul * if table full, then we need to replace LRU entry 191753702Swpaul */ 191853702Swpaul else { 191953702Swpaul 192053702Swpaul /* check for space in cache table 192153702Swpaul * note: wi_nextitem also holds number of entries 192253702Swpaul * added in the cache table 192353702Swpaul */ 192453702Swpaul if ( sc->wi_nextitem < MAXWICACHE ) { 192553702Swpaul cache_slot = sc->wi_nextitem; 192653702Swpaul sc->wi_nextitem++; 192753702Swpaul sc->wi_sigitems = sc->wi_nextitem; 192853702Swpaul } 192953702Swpaul /* no space found, so simply wrap with wrap index 193053702Swpaul * and "zap" the next entry 193153702Swpaul */ 193253702Swpaul else { 193353702Swpaul if (wrapindex == MAXWICACHE) { 193453702Swpaul wrapindex = 0; 193553702Swpaul } 193653702Swpaul cache_slot = wrapindex++; 193753702Swpaul } 193853702Swpaul } 193953702Swpaul 194053702Swpaul /* invariant: cache_slot now points at some slot 194153702Swpaul * in cache. 194253702Swpaul */ 194353702Swpaul if (cache_slot < 0 || cache_slot >= MAXWICACHE) { 194453702Swpaul log(LOG_ERR, "wi_cache_store, bad index: %d of " 194553702Swpaul "[0..%d], gross cache error\n", 194653702Swpaul cache_slot, MAXWICACHE); 194753702Swpaul return; 194853702Swpaul } 194953702Swpaul 195053702Swpaul /* store items in cache 195153702Swpaul * .ip source address 195253702Swpaul * .mac src 195353702Swpaul * .signal, etc. 195453702Swpaul */ 195553702Swpaul if (sawip) { 195653702Swpaul sc->wi_sigcache[cache_slot].ipsrc = ip->ip_src.s_addr; 195753702Swpaul } 195853702Swpaul bcopy( eh->ether_shost, sc->wi_sigcache[cache_slot].macsrc, 6); 195953702Swpaul 196053702Swpaul sig = (rx_quality >> 8) & 0xFF; 196153702Swpaul noise = rx_quality & 0xFF; 196253702Swpaul sc->wi_sigcache[cache_slot].signal = sig - 149; 196353702Swpaul sc->wi_sigcache[cache_slot].noise = noise - 149; 196453702Swpaul sc->wi_sigcache[cache_slot].quality = sig - noise; 196553702Swpaul 196653702Swpaul return; 196753702Swpaul} 196853702Swpaul#endif 1969