if_wi.c revision 74998
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 74998 2001-03-29 19:11:45Z 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 29053702Swpaul device_printf(dev, "unload\n"); 29167092Swpaul WI_UNLOCK(sc); 29267092Swpaul mtx_destroy(&sc->wi_mtx); 29346492Swpaul 29446492Swpaul return(0); 29546492Swpaul} 29646492Swpaul 29753702Swpaulstatic int wi_pccard_attach(device_t dev) 29846492Swpaul{ 29946492Swpaul struct wi_softc *sc; 30053702Swpaul int error; 30170073Swpaul u_int32_t flags; 30246492Swpaul 30353702Swpaul sc = device_get_softc(dev); 30446492Swpaul 30570073Swpaul /* 30670073Swpaul * XXX: quick hack to support Prism II chip. 30770073Swpaul * Currently, we need to set a flags in pccard.conf to specify 30870073Swpaul * which type chip is used. 30970073Swpaul * 31070073Swpaul * We need to replace this code in a future. 31170073Swpaul * It is better to use CIS than using a flag. 31270073Swpaul */ 31370073Swpaul flags = device_get_flags(dev); 31470073Swpaul#define WI_FLAGS_PRISM2 0x10000 31570073Swpaul if (flags & WI_FLAGS_PRISM2) { 31670073Swpaul sc->wi_prism2 = 1; 31770073Swpaul if (bootverbose) { 31870073Swpaul device_printf(dev, "found PrismII chip\n"); 31970073Swpaul } 32070073Swpaul } 32170073Swpaul else { 32270073Swpaul sc->wi_prism2 = 0; 32370073Swpaul if (bootverbose) { 32470073Swpaul device_printf(dev, "found Lucent chip\n"); 32570073Swpaul } 32670073Swpaul } 32770073Swpaul 32874906Salfred error = wi_alloc(dev, 0); 32953702Swpaul if (error) { 33053702Swpaul device_printf(dev, "wi_alloc() failed! (%d)\n", error); 33153702Swpaul return (error); 33253702Swpaul } 33374906Salfred return (wi_generic_attach(dev)); 33474906Salfred} 33553702Swpaul 33674906Salfredstatic int 33774906Salfredwi_pci_attach(device_t dev) 33874906Salfred{ 33974906Salfred struct wi_softc *sc; 34074906Salfred u_int32_t command, wanted; 34174906Salfred u_int16_t reg; 34274906Salfred int error; 34374906Salfred 34474906Salfred sc = device_get_softc(dev); 34574906Salfred 34674906Salfred command = pci_read_config(dev, PCIR_COMMAND, 4); 34774906Salfred wanted = PCIM_CMD_PORTEN|PCIM_CMD_MEMEN; 34874906Salfred command |= wanted; 34974906Salfred pci_write_config(dev, PCIR_COMMAND, command, 4); 35074906Salfred command = pci_read_config(dev, PCIR_COMMAND, 4); 35174906Salfred if ((command & wanted) != wanted) { 35274906Salfred device_printf(dev, "wi_pci_attach() failed to enable pci!\n"); 35374906Salfred return (ENXIO); 35474906Salfred } 35574906Salfred 35674906Salfred error = wi_alloc(dev, WI_PCI_IORES); 35774906Salfred if (error) 35874906Salfred return (error); 35974906Salfred 36074906Salfred device_set_desc(dev, wi_device_desc); 36174906Salfred 36274906Salfred /* Make sure interrupts are disabled. */ 36374906Salfred CSR_WRITE_2(sc, WI_INT_EN, 0); 36474906Salfred CSR_WRITE_2(sc, WI_EVENT_ACK, 0xFFFF); 36574906Salfred 36674998Swpaul sc->mem_rid = WI_PCI_MEMRES; 36774906Salfred sc->mem = bus_alloc_resource(dev, SYS_RES_MEMORY, &sc->mem_rid, 36874906Salfred 0, ~0, 1, RF_ACTIVE); 36974906Salfred if (sc->mem == NULL) { 37074906Salfred device_printf(dev, "couldn't allocate memory\n"); 37174906Salfred wi_free(dev); 37274906Salfred return (ENXIO); 37374906Salfred } 37474906Salfred sc->wi_bmemtag = rman_get_bustag(sc->mem); 37574906Salfred sc->wi_bmemhandle = rman_get_bushandle(sc->mem); 37674906Salfred 37774906Salfred /* 37874906Salfred * From Linux driver: 37974906Salfred * Write COR to enable PC card 38074906Salfred */ 38174906Salfred CSM_WRITE_1(sc, WI_COR_OFFSET, WI_COR_VALUE); 38274906Salfred reg = CSM_READ_1(sc, WI_COR_OFFSET); 38374906Salfred 38474998Swpaul CSR_WRITE_2(sc, WI_HFA384X_SWSUPPORT0_OFF, WI_PRISM2STA_MAGIC); 38574998Swpaul reg = CSR_READ_2(sc, WI_HFA384X_SWSUPPORT0_OFF); 38674906Salfred if (reg != WI_PRISM2STA_MAGIC) { 38774906Salfred device_printf(dev, 38874998Swpaul "CSR_READ_2(WI_HFA384X_SWSUPPORT0_OFF) " 38974998Swpaul "wanted %d, got %d\n", WI_PRISM2STA_MAGIC, reg); 39074906Salfred wi_free(dev); 39174906Salfred return (ENXIO); 39274906Salfred } 39374906Salfred 39474906Salfred error = wi_generic_attach(dev); 39574906Salfred if (error != 0) 39674906Salfred return (error); 39774906Salfred 39874906Salfred return (0); 39974906Salfred} 40074906Salfred 40174906Salfredstatic int 40274906Salfredwi_generic_attach(device_t dev) 40374906Salfred{ 40474906Salfred struct wi_softc *sc; 40574906Salfred struct wi_ltv_macaddr mac; 40674906Salfred struct wi_ltv_gen gen; 40774906Salfred struct ifnet *ifp; 40874906Salfred int error; 40974906Salfred 41074906Salfred sc = device_get_softc(dev); 41174906Salfred ifp = &sc->arpcom.ac_if; 41274906Salfred 41353702Swpaul error = bus_setup_intr(dev, sc->irq, INTR_TYPE_NET, 41474998Swpaul wi_intr, sc, &sc->wi_intrhand); 41553702Swpaul 41653702Swpaul if (error) { 41753702Swpaul device_printf(dev, "bus_setup_intr() failed! (%d)\n", error); 41853702Swpaul wi_free(dev); 41953702Swpaul return (error); 42053702Swpaul } 42153702Swpaul 42271228Sbmilekic mtx_init(&sc->wi_mtx, device_get_nameunit(dev), MTX_DEF | MTX_RECURSE); 42367092Swpaul WI_LOCK(sc); 42467092Swpaul 42546492Swpaul /* Reset the NIC. */ 42646492Swpaul wi_reset(sc); 42746492Swpaul 42846492Swpaul /* Read the station address. */ 42946492Swpaul mac.wi_type = WI_RID_MAC_NODE; 43046492Swpaul mac.wi_len = 4; 43146492Swpaul wi_read_record(sc, (struct wi_ltv_gen *)&mac); 43246492Swpaul bcopy((char *)&mac.wi_mac_addr, 43346492Swpaul (char *)&sc->arpcom.ac_enaddr, ETHER_ADDR_LEN); 43446492Swpaul 43553702Swpaul device_printf(dev, "Ethernet address: %6D\n", 43646492Swpaul sc->arpcom.ac_enaddr, ":"); 43746492Swpaul 43846492Swpaul ifp->if_softc = sc; 43946492Swpaul ifp->if_unit = sc->wi_unit; 44046492Swpaul ifp->if_name = "wi"; 44146492Swpaul ifp->if_mtu = ETHERMTU; 44246492Swpaul ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; 44346492Swpaul ifp->if_ioctl = wi_ioctl; 44446492Swpaul ifp->if_output = ether_output; 44546492Swpaul ifp->if_start = wi_start; 44646492Swpaul ifp->if_watchdog = wi_watchdog; 44746492Swpaul ifp->if_init = wi_init; 44846492Swpaul ifp->if_baudrate = 10000000; 44946492Swpaul ifp->if_snd.ifq_maxlen = IFQ_MAXLEN; 45046492Swpaul 45146492Swpaul bzero(sc->wi_node_name, sizeof(sc->wi_node_name)); 45246492Swpaul bcopy(WI_DEFAULT_NODENAME, sc->wi_node_name, 45346492Swpaul sizeof(WI_DEFAULT_NODENAME) - 1); 45446492Swpaul 45546492Swpaul bzero(sc->wi_net_name, sizeof(sc->wi_net_name)); 45646492Swpaul bcopy(WI_DEFAULT_NETNAME, sc->wi_net_name, 45746492Swpaul sizeof(WI_DEFAULT_NETNAME) - 1); 45846492Swpaul 45946492Swpaul bzero(sc->wi_ibss_name, sizeof(sc->wi_ibss_name)); 46046492Swpaul bcopy(WI_DEFAULT_IBSS, sc->wi_ibss_name, 46146492Swpaul sizeof(WI_DEFAULT_IBSS) - 1); 46246492Swpaul 46346492Swpaul sc->wi_portnum = WI_DEFAULT_PORT; 46474139Sassar sc->wi_ptype = WI_PORTTYPE_BSS; 46546492Swpaul sc->wi_ap_density = WI_DEFAULT_AP_DENSITY; 46646492Swpaul sc->wi_rts_thresh = WI_DEFAULT_RTS_THRESH; 46746492Swpaul sc->wi_tx_rate = WI_DEFAULT_TX_RATE; 46846492Swpaul sc->wi_max_data_len = WI_DEFAULT_DATALEN; 46946492Swpaul sc->wi_create_ibss = WI_DEFAULT_CREATE_IBSS; 47046611Swpaul sc->wi_pm_enabled = WI_DEFAULT_PM_ENABLED; 47146611Swpaul sc->wi_max_sleep = WI_DEFAULT_MAX_SLEEP; 47246492Swpaul 47346563Swpaul /* 47446563Swpaul * Read the default channel from the NIC. This may vary 47546563Swpaul * depending on the country where the NIC was purchased, so 47646563Swpaul * we can't hard-code a default and expect it to work for 47746563Swpaul * everyone. 47846563Swpaul */ 47946563Swpaul gen.wi_type = WI_RID_OWN_CHNL; 48046563Swpaul gen.wi_len = 2; 48146563Swpaul wi_read_record(sc, &gen); 48246563Swpaul sc->wi_channel = gen.wi_val; 48346563Swpaul 48456965Swpaul /* 48556965Swpaul * Find out if we support WEP on this card. 48656965Swpaul */ 48756965Swpaul gen.wi_type = WI_RID_WEP_AVAIL; 48856965Swpaul gen.wi_len = 2; 48956965Swpaul wi_read_record(sc, &gen); 49056965Swpaul sc->wi_has_wep = gen.wi_val; 49156965Swpaul 49270073Swpaul if (bootverbose) { 49370073Swpaul device_printf(sc->dev, 49470073Swpaul __FUNCTION__ ":wi_has_wep = %d\n", 49570073Swpaul sc->wi_has_wep); 49670073Swpaul } 49770073Swpaul 49846492Swpaul bzero((char *)&sc->wi_stats, sizeof(sc->wi_stats)); 49946492Swpaul 50046492Swpaul wi_init(sc); 50146492Swpaul wi_stop(sc); 50246492Swpaul 50346492Swpaul /* 50463090Sarchie * Call MI attach routine. 50546492Swpaul */ 50663090Sarchie ether_ifattach(ifp, ETHER_BPF_SUPPORTED); 50753702Swpaul callout_handle_init(&sc->wi_stat_ch); 50867092Swpaul WI_UNLOCK(sc); 50946492Swpaul 51046492Swpaul return(0); 51146492Swpaul} 51246492Swpaul 51346492Swpaulstatic void wi_rxeof(sc) 51446492Swpaul struct wi_softc *sc; 51546492Swpaul{ 51646492Swpaul struct ifnet *ifp; 51746492Swpaul struct ether_header *eh; 51846492Swpaul struct wi_frame rx_frame; 51946492Swpaul struct mbuf *m; 52046492Swpaul int id; 52146492Swpaul 52246492Swpaul ifp = &sc->arpcom.ac_if; 52346492Swpaul 52446492Swpaul id = CSR_READ_2(sc, WI_RX_FID); 52546492Swpaul 52646492Swpaul /* First read in the frame header */ 52746492Swpaul if (wi_read_data(sc, id, 0, (caddr_t)&rx_frame, sizeof(rx_frame))) { 52846492Swpaul ifp->if_ierrors++; 52946492Swpaul return; 53046492Swpaul } 53146492Swpaul 53246492Swpaul if (rx_frame.wi_status & WI_STAT_ERRSTAT) { 53346492Swpaul ifp->if_ierrors++; 53446492Swpaul return; 53546492Swpaul } 53646492Swpaul 53746492Swpaul MGETHDR(m, M_DONTWAIT, MT_DATA); 53846492Swpaul if (m == NULL) { 53946492Swpaul ifp->if_ierrors++; 54046492Swpaul return; 54146492Swpaul } 54246492Swpaul MCLGET(m, M_DONTWAIT); 54346492Swpaul if (!(m->m_flags & M_EXT)) { 54446492Swpaul m_freem(m); 54546492Swpaul ifp->if_ierrors++; 54646492Swpaul return; 54746492Swpaul } 54846492Swpaul 54946492Swpaul eh = mtod(m, struct ether_header *); 55046492Swpaul m->m_pkthdr.rcvif = ifp; 55146492Swpaul 55246492Swpaul if (rx_frame.wi_status == WI_STAT_1042 || 55346492Swpaul rx_frame.wi_status == WI_STAT_TUNNEL || 55446492Swpaul rx_frame.wi_status == WI_STAT_WMP_MSG) { 55548553Swpaul if((rx_frame.wi_dat_len + WI_SNAPHDR_LEN) > MCLBYTES) { 55653702Swpaul device_printf(sc->dev, "oversized packet received " 55753702Swpaul "(wi_dat_len=%d, wi_status=0x%x)\n", 55848553Swpaul rx_frame.wi_dat_len, rx_frame.wi_status); 55948553Swpaul m_freem(m); 56048553Swpaul ifp->if_ierrors++; 56148553Swpaul return; 56248553Swpaul } 56346492Swpaul m->m_pkthdr.len = m->m_len = 56446492Swpaul rx_frame.wi_dat_len + WI_SNAPHDR_LEN; 56546492Swpaul 56646492Swpaul bcopy((char *)&rx_frame.wi_addr1, 56746492Swpaul (char *)&eh->ether_dhost, ETHER_ADDR_LEN); 56859328Swpaul if (sc->wi_ptype == WI_PORTTYPE_ADHOC) { 56959328Swpaul bcopy((char *)&rx_frame.wi_addr2, 57059328Swpaul (char *)&eh->ether_shost, ETHER_ADDR_LEN); 57159328Swpaul } else { 57259328Swpaul bcopy((char *)&rx_frame.wi_addr3, 57359328Swpaul (char *)&eh->ether_shost, ETHER_ADDR_LEN); 57459328Swpaul } 57546492Swpaul bcopy((char *)&rx_frame.wi_type, 57646492Swpaul (char *)&eh->ether_type, sizeof(u_int16_t)); 57746492Swpaul 57846492Swpaul if (wi_read_data(sc, id, WI_802_11_OFFSET, 57946492Swpaul mtod(m, caddr_t) + sizeof(struct ether_header), 58046492Swpaul m->m_len + 2)) { 58146492Swpaul m_freem(m); 58246492Swpaul ifp->if_ierrors++; 58346492Swpaul return; 58446492Swpaul } 58546492Swpaul } else { 58648553Swpaul if((rx_frame.wi_dat_len + 58748553Swpaul sizeof(struct ether_header)) > MCLBYTES) { 58853702Swpaul device_printf(sc->dev, "oversized packet received " 58953702Swpaul "(wi_dat_len=%d, wi_status=0x%x)\n", 59048553Swpaul rx_frame.wi_dat_len, rx_frame.wi_status); 59148553Swpaul m_freem(m); 59248553Swpaul ifp->if_ierrors++; 59348553Swpaul return; 59448553Swpaul } 59546492Swpaul m->m_pkthdr.len = m->m_len = 59646492Swpaul rx_frame.wi_dat_len + sizeof(struct ether_header); 59746492Swpaul 59846492Swpaul if (wi_read_data(sc, id, WI_802_3_OFFSET, 59946492Swpaul mtod(m, caddr_t), m->m_len + 2)) { 60046492Swpaul m_freem(m); 60146492Swpaul ifp->if_ierrors++; 60246492Swpaul return; 60346492Swpaul } 60446492Swpaul } 60546492Swpaul 60646492Swpaul ifp->if_ipackets++; 60746492Swpaul 60846492Swpaul /* Receive packet. */ 60946492Swpaul m_adj(m, sizeof(struct ether_header)); 61053702Swpaul#ifdef WICACHE 61153702Swpaul wi_cache_store(sc, eh, m, rx_frame.wi_q_info); 61253702Swpaul#endif 61346492Swpaul ether_input(ifp, eh, m); 61446492Swpaul} 61546492Swpaul 61646492Swpaulstatic void wi_txeof(sc, status) 61746492Swpaul struct wi_softc *sc; 61846492Swpaul int status; 61946492Swpaul{ 62046492Swpaul struct ifnet *ifp; 62146492Swpaul 62246492Swpaul ifp = &sc->arpcom.ac_if; 62346492Swpaul 62446492Swpaul ifp->if_timer = 0; 62546492Swpaul ifp->if_flags &= ~IFF_OACTIVE; 62646492Swpaul 62746492Swpaul if (status & WI_EV_TX_EXC) 62846492Swpaul ifp->if_oerrors++; 62946492Swpaul else 63046492Swpaul ifp->if_opackets++; 63146492Swpaul 63246492Swpaul return; 63346492Swpaul} 63446492Swpaul 63546492Swpaulvoid wi_inquire(xsc) 63646492Swpaul void *xsc; 63746492Swpaul{ 63846492Swpaul struct wi_softc *sc; 63946492Swpaul struct ifnet *ifp; 64046492Swpaul 64146492Swpaul sc = xsc; 64246492Swpaul ifp = &sc->arpcom.ac_if; 64346492Swpaul 64446492Swpaul sc->wi_stat_ch = timeout(wi_inquire, sc, hz * 60); 64546492Swpaul 64646492Swpaul /* Don't do this while we're transmitting */ 64746492Swpaul if (ifp->if_flags & IFF_OACTIVE) 64846492Swpaul return; 64946492Swpaul 65046492Swpaul wi_cmd(sc, WI_CMD_INQUIRE, WI_INFO_COUNTERS); 65146492Swpaul 65246492Swpaul return; 65346492Swpaul} 65446492Swpaul 65546492Swpaulvoid wi_update_stats(sc) 65646492Swpaul struct wi_softc *sc; 65746492Swpaul{ 65846492Swpaul struct wi_ltv_gen gen; 65946492Swpaul u_int16_t id; 66046492Swpaul struct ifnet *ifp; 66146492Swpaul u_int32_t *ptr; 66246492Swpaul int i; 66346492Swpaul u_int16_t t; 66446492Swpaul 66546492Swpaul ifp = &sc->arpcom.ac_if; 66646492Swpaul 66746492Swpaul id = CSR_READ_2(sc, WI_INFO_FID); 66846492Swpaul 66946492Swpaul wi_read_data(sc, id, 0, (char *)&gen, 4); 67046492Swpaul 67146492Swpaul if (gen.wi_type != WI_INFO_COUNTERS || 67246492Swpaul gen.wi_len > (sizeof(sc->wi_stats) / 4) + 1) 67346492Swpaul return; 67446492Swpaul 67546492Swpaul ptr = (u_int32_t *)&sc->wi_stats; 67646492Swpaul 67746492Swpaul for (i = 0; i < gen.wi_len - 1; i++) { 67846492Swpaul t = CSR_READ_2(sc, WI_DATA1); 67946492Swpaul#ifdef WI_HERMES_STATS_WAR 68046492Swpaul if (t > 0xF000) 68146492Swpaul t = ~t & 0xFFFF; 68246492Swpaul#endif 68346492Swpaul ptr[i] += t; 68446492Swpaul } 68546492Swpaul 68646492Swpaul ifp->if_collisions = sc->wi_stats.wi_tx_single_retries + 68746492Swpaul sc->wi_stats.wi_tx_multi_retries + 68846492Swpaul sc->wi_stats.wi_tx_retry_limit; 68946492Swpaul 69046492Swpaul return; 69146492Swpaul} 69246492Swpaul 69353702Swpaulstatic void wi_intr(xsc) 69453702Swpaul void *xsc; 69546492Swpaul{ 69653702Swpaul struct wi_softc *sc = xsc; 69746492Swpaul struct ifnet *ifp; 69846492Swpaul u_int16_t status; 69946492Swpaul 70067092Swpaul WI_LOCK(sc); 70167092Swpaul 70246492Swpaul ifp = &sc->arpcom.ac_if; 70346492Swpaul 70446492Swpaul if (!(ifp->if_flags & IFF_UP)) { 70546492Swpaul CSR_WRITE_2(sc, WI_EVENT_ACK, 0xFFFF); 70646492Swpaul CSR_WRITE_2(sc, WI_INT_EN, 0); 70767092Swpaul WI_UNLOCK(sc); 70846492Swpaul return; 70946492Swpaul } 71046492Swpaul 71146492Swpaul /* Disable interrupts. */ 71246492Swpaul CSR_WRITE_2(sc, WI_INT_EN, 0); 71346492Swpaul 71446492Swpaul status = CSR_READ_2(sc, WI_EVENT_STAT); 71546492Swpaul CSR_WRITE_2(sc, WI_EVENT_ACK, ~WI_INTRS); 71646492Swpaul 71746492Swpaul if (status & WI_EV_RX) { 71846492Swpaul wi_rxeof(sc); 71946492Swpaul CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_RX); 72046492Swpaul } 72146492Swpaul 72246492Swpaul if (status & WI_EV_TX) { 72346492Swpaul wi_txeof(sc, status); 72446492Swpaul CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_TX); 72546492Swpaul } 72646492Swpaul 72746492Swpaul if (status & WI_EV_ALLOC) { 72846492Swpaul int id; 72946492Swpaul id = CSR_READ_2(sc, WI_ALLOC_FID); 73046492Swpaul CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_ALLOC); 73146492Swpaul if (id == sc->wi_tx_data_id) 73246492Swpaul wi_txeof(sc, status); 73346492Swpaul } 73446492Swpaul 73546492Swpaul if (status & WI_EV_INFO) { 73646492Swpaul wi_update_stats(sc); 73746492Swpaul CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_INFO); 73846492Swpaul } 73946492Swpaul 74046492Swpaul if (status & WI_EV_TX_EXC) { 74146492Swpaul wi_txeof(sc, status); 74246492Swpaul CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_TX_EXC); 74346492Swpaul } 74446492Swpaul 74546492Swpaul if (status & WI_EV_INFO_DROP) { 74646492Swpaul CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_INFO_DROP); 74746492Swpaul } 74846492Swpaul 74946492Swpaul /* Re-enable interrupts. */ 75046492Swpaul CSR_WRITE_2(sc, WI_INT_EN, WI_INTRS); 75146492Swpaul 75246492Swpaul if (ifp->if_snd.ifq_head != NULL) 75346492Swpaul wi_start(ifp); 75446492Swpaul 75567092Swpaul WI_UNLOCK(sc); 75667092Swpaul 75746492Swpaul return; 75846492Swpaul} 75946492Swpaul 76046492Swpaulstatic int wi_cmd(sc, cmd, val) 76146492Swpaul struct wi_softc *sc; 76246492Swpaul int cmd; 76346492Swpaul int val; 76446492Swpaul{ 76546492Swpaul int i, s = 0; 76646492Swpaul 76770073Swpaul /* wait for the busy bit to clear */ 76870073Swpaul for (i = 0; i < WI_TIMEOUT; i++) { 76970073Swpaul if (!(CSR_READ_2(sc, WI_COMMAND) & WI_CMD_BUSY)) { 77070073Swpaul break; 77170073Swpaul } 77270073Swpaul DELAY(10*1000); /* 10 m sec */ 77370073Swpaul } 77470073Swpaul 77570073Swpaul if (i == WI_TIMEOUT) { 77670073Swpaul return(ETIMEDOUT); 77770073Swpaul } 77870073Swpaul 77946492Swpaul CSR_WRITE_2(sc, WI_PARAM0, val); 78070073Swpaul CSR_WRITE_2(sc, WI_PARAM1, 0); 78170073Swpaul CSR_WRITE_2(sc, WI_PARAM2, 0); 78246492Swpaul CSR_WRITE_2(sc, WI_COMMAND, cmd); 78346492Swpaul 78446492Swpaul for (i = 0; i < WI_TIMEOUT; i++) { 78546492Swpaul /* 78646492Swpaul * Wait for 'command complete' bit to be 78746492Swpaul * set in the event status register. 78846492Swpaul */ 78946492Swpaul s = CSR_READ_2(sc, WI_EVENT_STAT) & WI_EV_CMD; 79046492Swpaul if (s) { 79146492Swpaul /* Ack the event and read result code. */ 79246492Swpaul s = CSR_READ_2(sc, WI_STATUS); 79346492Swpaul CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_CMD); 79446492Swpaul#ifdef foo 79546492Swpaul if ((s & WI_CMD_CODE_MASK) != (cmd & WI_CMD_CODE_MASK)) 79646492Swpaul return(EIO); 79746492Swpaul#endif 79846492Swpaul if (s & WI_STAT_CMD_RESULT) 79946492Swpaul return(EIO); 80046492Swpaul break; 80146492Swpaul } 80246492Swpaul } 80346492Swpaul 80446492Swpaul if (i == WI_TIMEOUT) 80546492Swpaul return(ETIMEDOUT); 80646492Swpaul 80746492Swpaul return(0); 80846492Swpaul} 80946492Swpaul 81046492Swpaulstatic void wi_reset(sc) 81146492Swpaul struct wi_softc *sc; 81246492Swpaul{ 81370073Swpaul#ifdef foo 81453702Swpaul wi_cmd(sc, WI_CMD_INI, 0); 81553702Swpaul DELAY(100000); 81653702Swpaul wi_cmd(sc, WI_CMD_INI, 0); 81770073Swpaul#endif 81853702Swpaul DELAY(100000); 81946492Swpaul if (wi_cmd(sc, WI_CMD_INI, 0)) 82053702Swpaul device_printf(sc->dev, "init failed\n"); 82146492Swpaul CSR_WRITE_2(sc, WI_INT_EN, 0); 82246492Swpaul CSR_WRITE_2(sc, WI_EVENT_ACK, 0xFFFF); 82346492Swpaul 82446492Swpaul /* Calibrate timer. */ 82546492Swpaul WI_SETVAL(WI_RID_TICK_TIME, 8); 82670073Swpaul 82746492Swpaul return; 82846492Swpaul} 82946492Swpaul 83046492Swpaul/* 83146492Swpaul * Read an LTV record from the NIC. 83246492Swpaul */ 83346492Swpaulstatic int wi_read_record(sc, ltv) 83446492Swpaul struct wi_softc *sc; 83546492Swpaul struct wi_ltv_gen *ltv; 83646492Swpaul{ 83746492Swpaul u_int16_t *ptr; 83846492Swpaul int i, len, code; 83970073Swpaul struct wi_ltv_gen *oltv, p2ltv; 84046492Swpaul 84170073Swpaul oltv = ltv; 84270073Swpaul if (sc->wi_prism2) { 84370073Swpaul switch (ltv->wi_type) { 84470073Swpaul case WI_RID_ENCRYPTION: 84570073Swpaul p2ltv.wi_type = WI_RID_P2_ENCRYPTION; 84670073Swpaul p2ltv.wi_len = 2; 84770073Swpaul ltv = &p2ltv; 84870073Swpaul break; 84970073Swpaul case WI_RID_TX_CRYPT_KEY: 85070073Swpaul p2ltv.wi_type = WI_RID_P2_TX_CRYPT_KEY; 85170073Swpaul p2ltv.wi_len = 2; 85270073Swpaul ltv = &p2ltv; 85370073Swpaul break; 85470073Swpaul } 85570073Swpaul } 85670073Swpaul 85746492Swpaul /* Tell the NIC to enter record read mode. */ 85846492Swpaul if (wi_cmd(sc, WI_CMD_ACCESS|WI_ACCESS_READ, ltv->wi_type)) 85946492Swpaul return(EIO); 86046492Swpaul 86147789Swpaul /* Seek to the record. */ 86247789Swpaul if (wi_seek(sc, ltv->wi_type, 0, WI_BAP1)) 86347789Swpaul return(EIO); 86446492Swpaul 86546492Swpaul /* 86646492Swpaul * Read the length and record type and make sure they 86746492Swpaul * match what we expect (this verifies that we have enough 86847401Swpaul * room to hold all of the returned data). 86946492Swpaul */ 87046492Swpaul len = CSR_READ_2(sc, WI_DATA1); 87146492Swpaul if (len > ltv->wi_len) 87246492Swpaul return(ENOSPC); 87346492Swpaul code = CSR_READ_2(sc, WI_DATA1); 87446492Swpaul if (code != ltv->wi_type) 87546492Swpaul return(EIO); 87646492Swpaul 87746492Swpaul ltv->wi_len = len; 87846492Swpaul ltv->wi_type = code; 87946492Swpaul 88046492Swpaul /* Now read the data. */ 88146492Swpaul ptr = <v->wi_val; 88246492Swpaul for (i = 0; i < ltv->wi_len - 1; i++) 88346492Swpaul ptr[i] = CSR_READ_2(sc, WI_DATA1); 88446492Swpaul 88570073Swpaul if (sc->wi_prism2) { 88670073Swpaul switch (oltv->wi_type) { 88770073Swpaul case WI_RID_TX_RATE: 88870073Swpaul case WI_RID_CUR_TX_RATE: 88970073Swpaul switch (ltv->wi_val) { 89070073Swpaul case 1: oltv->wi_val = 1; break; 89170073Swpaul case 2: oltv->wi_val = 2; break; 89270073Swpaul case 3: oltv->wi_val = 6; break; 89370073Swpaul case 4: oltv->wi_val = 5; break; 89470073Swpaul case 7: oltv->wi_val = 7; break; 89570073Swpaul case 8: oltv->wi_val = 11; break; 89670073Swpaul case 15: oltv->wi_val = 3; break; 89770073Swpaul default: oltv->wi_val = 0x100 + ltv->wi_val; break; 89870073Swpaul } 89970073Swpaul break; 90070073Swpaul case WI_RID_ENCRYPTION: 90170073Swpaul oltv->wi_len = 2; 90270073Swpaul if (ltv->wi_val & 0x01) 90370073Swpaul oltv->wi_val = 1; 90470073Swpaul else 90570073Swpaul oltv->wi_val = 0; 90670073Swpaul break; 90770073Swpaul case WI_RID_TX_CRYPT_KEY: 90870073Swpaul oltv->wi_len = 2; 90970073Swpaul oltv->wi_val = ltv->wi_val; 91070073Swpaul break; 91170073Swpaul } 91270073Swpaul } 91370073Swpaul 91446492Swpaul return(0); 91546492Swpaul} 91646492Swpaul 91746492Swpaul/* 91846492Swpaul * Same as read, except we inject data instead of reading it. 91946492Swpaul */ 92046492Swpaulstatic int wi_write_record(sc, ltv) 92146492Swpaul struct wi_softc *sc; 92246492Swpaul struct wi_ltv_gen *ltv; 92346492Swpaul{ 92446492Swpaul u_int16_t *ptr; 92546492Swpaul int i; 92670073Swpaul struct wi_ltv_gen p2ltv; 92746492Swpaul 92870073Swpaul if (sc->wi_prism2) { 92970073Swpaul switch (ltv->wi_type) { 93070073Swpaul case WI_RID_TX_RATE: 93170073Swpaul p2ltv.wi_type = WI_RID_TX_RATE; 93270073Swpaul p2ltv.wi_len = 2; 93370073Swpaul switch (ltv->wi_val) { 93470073Swpaul case 1: p2ltv.wi_val = 1; break; 93570073Swpaul case 2: p2ltv.wi_val = 2; break; 93670073Swpaul case 3: p2ltv.wi_val = 15; break; 93770073Swpaul case 5: p2ltv.wi_val = 4; break; 93870073Swpaul case 6: p2ltv.wi_val = 3; break; 93970073Swpaul case 7: p2ltv.wi_val = 7; break; 94070073Swpaul case 11: p2ltv.wi_val = 8; break; 94170073Swpaul default: return EINVAL; 94270073Swpaul } 94370073Swpaul ltv = &p2ltv; 94470073Swpaul break; 94570073Swpaul case WI_RID_ENCRYPTION: 94670073Swpaul p2ltv.wi_type = WI_RID_P2_ENCRYPTION; 94770073Swpaul p2ltv.wi_len = 2; 94870073Swpaul if (ltv->wi_val) 94970073Swpaul p2ltv.wi_val = 0x03; 95070073Swpaul else 95170073Swpaul p2ltv.wi_val = 0x90; 95270073Swpaul ltv = &p2ltv; 95370073Swpaul break; 95470073Swpaul case WI_RID_TX_CRYPT_KEY: 95570073Swpaul p2ltv.wi_type = WI_RID_P2_TX_CRYPT_KEY; 95670073Swpaul p2ltv.wi_len = 2; 95770073Swpaul p2ltv.wi_val = ltv->wi_val; 95870073Swpaul ltv = &p2ltv; 95970073Swpaul break; 96070073Swpaul case WI_RID_DEFLT_CRYPT_KEYS: 96170073Swpaul { 96270073Swpaul int error; 96370073Swpaul struct wi_ltv_str ws; 96474998Swpaul struct wi_ltv_keys *wk = 96574998Swpaul (struct wi_ltv_keys *)ltv; 96674998Swpaul 96770073Swpaul for (i = 0; i < 4; i++) { 96870073Swpaul ws.wi_len = 4; 96970073Swpaul ws.wi_type = WI_RID_P2_CRYPT_KEY0 + i; 97074998Swpaul memcpy(ws.wi_str, 97174998Swpaul &wk->wi_keys[i].wi_keydat, 5); 97270073Swpaul ws.wi_str[5] = '\0'; 97370073Swpaul error = wi_write_record(sc, 97470073Swpaul (struct wi_ltv_gen *)&ws); 97570073Swpaul if (error) 97670073Swpaul return error; 97770073Swpaul } 97870073Swpaul return 0; 97970073Swpaul } 98070073Swpaul } 98170073Swpaul } 98270073Swpaul 98347789Swpaul if (wi_seek(sc, ltv->wi_type, 0, WI_BAP1)) 98447789Swpaul return(EIO); 98546492Swpaul 98646492Swpaul CSR_WRITE_2(sc, WI_DATA1, ltv->wi_len); 98746492Swpaul CSR_WRITE_2(sc, WI_DATA1, ltv->wi_type); 98846492Swpaul 98946492Swpaul ptr = <v->wi_val; 99046492Swpaul for (i = 0; i < ltv->wi_len - 1; i++) 99146492Swpaul CSR_WRITE_2(sc, WI_DATA1, ptr[i]); 99246492Swpaul 99346492Swpaul if (wi_cmd(sc, WI_CMD_ACCESS|WI_ACCESS_WRITE, ltv->wi_type)) 99446492Swpaul return(EIO); 99546492Swpaul 99646492Swpaul return(0); 99746492Swpaul} 99846492Swpaul 99946492Swpaulstatic int wi_seek(sc, id, off, chan) 100046492Swpaul struct wi_softc *sc; 100146492Swpaul int id, off, chan; 100246492Swpaul{ 100346492Swpaul int i; 100446492Swpaul int selreg, offreg; 100546492Swpaul 100646492Swpaul switch (chan) { 100746492Swpaul case WI_BAP0: 100846492Swpaul selreg = WI_SEL0; 100946492Swpaul offreg = WI_OFF0; 101046492Swpaul break; 101146492Swpaul case WI_BAP1: 101246492Swpaul selreg = WI_SEL1; 101346492Swpaul offreg = WI_OFF1; 101446492Swpaul break; 101546492Swpaul default: 101653702Swpaul device_printf(sc->dev, "invalid data path: %x\n", chan); 101746492Swpaul return(EIO); 101846492Swpaul } 101946492Swpaul 102046492Swpaul CSR_WRITE_2(sc, selreg, id); 102146492Swpaul CSR_WRITE_2(sc, offreg, off); 102246492Swpaul 102346492Swpaul for (i = 0; i < WI_TIMEOUT; i++) { 102446492Swpaul if (!(CSR_READ_2(sc, offreg) & (WI_OFF_BUSY|WI_OFF_ERR))) 102546492Swpaul break; 102646492Swpaul } 102746492Swpaul 102846492Swpaul if (i == WI_TIMEOUT) 102946492Swpaul return(ETIMEDOUT); 103046492Swpaul 103146492Swpaul return(0); 103246492Swpaul} 103346492Swpaul 103446492Swpaulstatic int wi_read_data(sc, id, off, buf, len) 103546492Swpaul struct wi_softc *sc; 103646492Swpaul int id, off; 103746492Swpaul caddr_t buf; 103846492Swpaul int len; 103946492Swpaul{ 104046492Swpaul int i; 104146492Swpaul u_int16_t *ptr; 104246492Swpaul 104346492Swpaul if (wi_seek(sc, id, off, WI_BAP1)) 104446492Swpaul return(EIO); 104546492Swpaul 104646492Swpaul ptr = (u_int16_t *)buf; 104746492Swpaul for (i = 0; i < len / 2; i++) 104846492Swpaul ptr[i] = CSR_READ_2(sc, WI_DATA1); 104946492Swpaul 105046492Swpaul return(0); 105146492Swpaul} 105246492Swpaul 105346492Swpaul/* 105446492Swpaul * According to the comments in the HCF Light code, there is a bug in 105546492Swpaul * the Hermes (or possibly in certain Hermes firmware revisions) where 105646492Swpaul * the chip's internal autoincrement counter gets thrown off during 105746492Swpaul * data writes: the autoincrement is missed, causing one data word to 105846492Swpaul * be overwritten and subsequent words to be written to the wrong memory 105946492Swpaul * locations. The end result is that we could end up transmitting bogus 106046492Swpaul * frames without realizing it. The workaround for this is to write a 106146492Swpaul * couple of extra guard words after the end of the transfer, then 106246492Swpaul * attempt to read then back. If we fail to locate the guard words where 106346492Swpaul * we expect them, we preform the transfer over again. 106446492Swpaul */ 106546492Swpaulstatic int wi_write_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; 107374838Salfred#ifdef WI_HERMES_AUTOINC_WAR 107474838Salfred int retries; 107546492Swpaul 107674838Salfred retries = WI_TIMEOUT >> 4; 107746492Swpaulagain: 107846492Swpaul#endif 107946492Swpaul 108046492Swpaul if (wi_seek(sc, id, off, WI_BAP0)) 108146492Swpaul return(EIO); 108246492Swpaul 108346492Swpaul ptr = (u_int16_t *)buf; 108446492Swpaul for (i = 0; i < (len / 2); i++) 108546492Swpaul CSR_WRITE_2(sc, WI_DATA0, ptr[i]); 108646492Swpaul 108746492Swpaul#ifdef WI_HERMES_AUTOINC_WAR 108846492Swpaul CSR_WRITE_2(sc, WI_DATA0, 0x1234); 108946492Swpaul CSR_WRITE_2(sc, WI_DATA0, 0x5678); 109046492Swpaul 109146492Swpaul if (wi_seek(sc, id, off + len, WI_BAP0)) 109246492Swpaul return(EIO); 109346492Swpaul 109446492Swpaul if (CSR_READ_2(sc, WI_DATA0) != 0x1234 || 109574998Swpaul CSR_READ_2(sc, WI_DATA0) != 0x5678) { 109674838Salfred if (--retries >= 0) 109774838Salfred goto again; 109874838Salfred device_printf(sc->dev, "wi_write_data device timeout\n"); 109974838Salfred return (EIO); 110074838Salfred } 110146492Swpaul#endif 110246492Swpaul 110346492Swpaul return(0); 110446492Swpaul} 110546492Swpaul 110646492Swpaul/* 110746492Swpaul * Allocate a region of memory inside the NIC and zero 110846492Swpaul * it out. 110946492Swpaul */ 111046492Swpaulstatic int wi_alloc_nicmem(sc, len, id) 111146492Swpaul struct wi_softc *sc; 111246492Swpaul int len; 111346492Swpaul int *id; 111446492Swpaul{ 111546492Swpaul int i; 111646492Swpaul 111746492Swpaul if (wi_cmd(sc, WI_CMD_ALLOC_MEM, len)) { 111874998Swpaul device_printf(sc->dev, 111974998Swpaul "failed to allocate %d bytes on NIC\n", len); 112046492Swpaul return(ENOMEM); 112146492Swpaul } 112246492Swpaul 112346492Swpaul for (i = 0; i < WI_TIMEOUT; i++) { 112446492Swpaul if (CSR_READ_2(sc, WI_EVENT_STAT) & WI_EV_ALLOC) 112546492Swpaul break; 112646492Swpaul } 112746492Swpaul 112846492Swpaul if (i == WI_TIMEOUT) 112946492Swpaul return(ETIMEDOUT); 113046492Swpaul 113146492Swpaul CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_ALLOC); 113246492Swpaul *id = CSR_READ_2(sc, WI_ALLOC_FID); 113346492Swpaul 113447789Swpaul if (wi_seek(sc, *id, 0, WI_BAP0)) 113547789Swpaul return(EIO); 113646492Swpaul 113746492Swpaul for (i = 0; i < len / 2; i++) 113846492Swpaul CSR_WRITE_2(sc, WI_DATA0, 0); 113946492Swpaul 114046492Swpaul return(0); 114146492Swpaul} 114246492Swpaul 114346492Swpaulstatic void wi_setmulti(sc) 114446492Swpaul struct wi_softc *sc; 114546492Swpaul{ 114646492Swpaul struct ifnet *ifp; 114746492Swpaul int i = 0; 114846492Swpaul struct ifmultiaddr *ifma; 114946492Swpaul struct wi_ltv_mcast mcast; 115046492Swpaul 115146492Swpaul ifp = &sc->arpcom.ac_if; 115246492Swpaul 115346492Swpaul bzero((char *)&mcast, sizeof(mcast)); 115446492Swpaul 115546492Swpaul mcast.wi_type = WI_RID_MCAST; 115646492Swpaul mcast.wi_len = (3 * 16) + 1; 115746492Swpaul 115846492Swpaul if (ifp->if_flags & IFF_ALLMULTI || ifp->if_flags & IFF_PROMISC) { 115946492Swpaul wi_write_record(sc, (struct wi_ltv_gen *)&mcast); 116046492Swpaul return; 116146492Swpaul } 116246492Swpaul 116372084Sphk TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { 116446492Swpaul if (ifma->ifma_addr->sa_family != AF_LINK) 116546492Swpaul continue; 116646492Swpaul if (i < 16) { 116746492Swpaul bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr), 116846492Swpaul (char *)&mcast.wi_mcast[i], ETHER_ADDR_LEN); 116946492Swpaul i++; 117046492Swpaul } else { 117146492Swpaul bzero((char *)&mcast, sizeof(mcast)); 117246492Swpaul break; 117346492Swpaul } 117446492Swpaul } 117546492Swpaul 117646492Swpaul mcast.wi_len = (i * 3) + 1; 117746492Swpaul wi_write_record(sc, (struct wi_ltv_gen *)&mcast); 117846492Swpaul 117946492Swpaul return; 118046492Swpaul} 118146492Swpaul 118246492Swpaulstatic void wi_setdef(sc, wreq) 118346492Swpaul struct wi_softc *sc; 118446492Swpaul struct wi_req *wreq; 118546492Swpaul{ 118646492Swpaul struct sockaddr_dl *sdl; 118746492Swpaul struct ifaddr *ifa; 118846492Swpaul struct ifnet *ifp; 118946492Swpaul 119046492Swpaul ifp = &sc->arpcom.ac_if; 119146492Swpaul 119246492Swpaul switch(wreq->wi_type) { 119346492Swpaul case WI_RID_MAC_NODE: 119446492Swpaul ifa = ifnet_addrs[ifp->if_index - 1]; 119546492Swpaul sdl = (struct sockaddr_dl *)ifa->ifa_addr; 119646492Swpaul bcopy((char *)&wreq->wi_val, (char *)&sc->arpcom.ac_enaddr, 119746492Swpaul ETHER_ADDR_LEN); 119846492Swpaul bcopy((char *)&wreq->wi_val, LLADDR(sdl), ETHER_ADDR_LEN); 119946492Swpaul break; 120046492Swpaul case WI_RID_PORTTYPE: 120146492Swpaul sc->wi_ptype = wreq->wi_val[0]; 120246492Swpaul break; 120346492Swpaul case WI_RID_TX_RATE: 120446492Swpaul sc->wi_tx_rate = wreq->wi_val[0]; 120546492Swpaul break; 120646492Swpaul case WI_RID_MAX_DATALEN: 120746492Swpaul sc->wi_max_data_len = wreq->wi_val[0]; 120846492Swpaul break; 120946492Swpaul case WI_RID_RTS_THRESH: 121046492Swpaul sc->wi_rts_thresh = wreq->wi_val[0]; 121146492Swpaul break; 121246492Swpaul case WI_RID_SYSTEM_SCALE: 121346492Swpaul sc->wi_ap_density = wreq->wi_val[0]; 121446492Swpaul break; 121546492Swpaul case WI_RID_CREATE_IBSS: 121646492Swpaul sc->wi_create_ibss = wreq->wi_val[0]; 121746492Swpaul break; 121846563Swpaul case WI_RID_OWN_CHNL: 121946563Swpaul sc->wi_channel = wreq->wi_val[0]; 122046563Swpaul break; 122146492Swpaul case WI_RID_NODENAME: 122246492Swpaul bzero(sc->wi_node_name, sizeof(sc->wi_node_name)); 122346492Swpaul bcopy((char *)&wreq->wi_val[1], sc->wi_node_name, 30); 122446492Swpaul break; 122546492Swpaul case WI_RID_DESIRED_SSID: 122646492Swpaul bzero(sc->wi_net_name, sizeof(sc->wi_net_name)); 122746492Swpaul bcopy((char *)&wreq->wi_val[1], sc->wi_net_name, 30); 122846492Swpaul break; 122946492Swpaul case WI_RID_OWN_SSID: 123046492Swpaul bzero(sc->wi_ibss_name, sizeof(sc->wi_ibss_name)); 123146492Swpaul bcopy((char *)&wreq->wi_val[1], sc->wi_ibss_name, 30); 123246492Swpaul break; 123346611Swpaul case WI_RID_PM_ENABLED: 123446611Swpaul sc->wi_pm_enabled = wreq->wi_val[0]; 123546611Swpaul break; 123646611Swpaul case WI_RID_MAX_SLEEP: 123746611Swpaul sc->wi_max_sleep = wreq->wi_val[0]; 123846611Swpaul break; 123956965Swpaul case WI_RID_ENCRYPTION: 124056965Swpaul sc->wi_use_wep = wreq->wi_val[0]; 124156965Swpaul break; 124256965Swpaul case WI_RID_TX_CRYPT_KEY: 124356965Swpaul sc->wi_tx_key = wreq->wi_val[0]; 124456965Swpaul break; 124556965Swpaul case WI_RID_DEFLT_CRYPT_KEYS: 124656965Swpaul bcopy((char *)wreq, (char *)&sc->wi_keys, 124756965Swpaul sizeof(struct wi_ltv_keys)); 124856965Swpaul break; 124946492Swpaul default: 125046492Swpaul break; 125146492Swpaul } 125246492Swpaul 125346563Swpaul /* Reinitialize WaveLAN. */ 125446563Swpaul wi_init(sc); 125546563Swpaul 125646492Swpaul return; 125746492Swpaul} 125846492Swpaul 125946492Swpaulstatic int wi_ioctl(ifp, command, data) 126046492Swpaul struct ifnet *ifp; 126146492Swpaul u_long command; 126246492Swpaul caddr_t data; 126346492Swpaul{ 126467092Swpaul int error = 0; 126546492Swpaul struct wi_softc *sc; 126646492Swpaul struct wi_req wreq; 126746492Swpaul struct ifreq *ifr; 126861818Sroberto struct proc *p = curproc; 126946492Swpaul 127046492Swpaul sc = ifp->if_softc; 127167092Swpaul WI_LOCK(sc); 127246492Swpaul ifr = (struct ifreq *)data; 127346492Swpaul 127461818Sroberto if (sc->wi_gone) { 127561818Sroberto error = ENODEV; 127661818Sroberto goto out; 127761818Sroberto } 127846492Swpaul 127946492Swpaul switch(command) { 128046492Swpaul case SIOCSIFADDR: 128146492Swpaul case SIOCGIFADDR: 128246492Swpaul case SIOCSIFMTU: 128346492Swpaul error = ether_ioctl(ifp, command, data); 128446492Swpaul break; 128546492Swpaul case SIOCSIFFLAGS: 128646492Swpaul if (ifp->if_flags & IFF_UP) { 128746492Swpaul if (ifp->if_flags & IFF_RUNNING && 128846492Swpaul ifp->if_flags & IFF_PROMISC && 128946492Swpaul !(sc->wi_if_flags & IFF_PROMISC)) { 129046492Swpaul WI_SETVAL(WI_RID_PROMISC, 1); 129146492Swpaul } else if (ifp->if_flags & IFF_RUNNING && 129246492Swpaul !(ifp->if_flags & IFF_PROMISC) && 129346492Swpaul sc->wi_if_flags & IFF_PROMISC) { 129446492Swpaul WI_SETVAL(WI_RID_PROMISC, 0); 129546492Swpaul } else 129646492Swpaul wi_init(sc); 129746492Swpaul } else { 129846492Swpaul if (ifp->if_flags & IFF_RUNNING) { 129946492Swpaul wi_stop(sc); 130046492Swpaul } 130146492Swpaul } 130246492Swpaul sc->wi_if_flags = ifp->if_flags; 130346492Swpaul error = 0; 130446492Swpaul break; 130546492Swpaul case SIOCADDMULTI: 130646492Swpaul case SIOCDELMULTI: 130746492Swpaul wi_setmulti(sc); 130846492Swpaul error = 0; 130946492Swpaul break; 131046492Swpaul case SIOCGWAVELAN: 131146492Swpaul error = copyin(ifr->ifr_data, &wreq, sizeof(wreq)); 131246492Swpaul if (error) 131346492Swpaul break; 131465581Swpaul /* Don't show WEP keys to non-root users. */ 131565581Swpaul if (wreq.wi_type == WI_RID_DEFLT_CRYPT_KEYS && suser(p)) 131665581Swpaul break; 131746492Swpaul if (wreq.wi_type == WI_RID_IFACE_STATS) { 131846492Swpaul bcopy((char *)&sc->wi_stats, (char *)&wreq.wi_val, 131946492Swpaul sizeof(sc->wi_stats)); 132046492Swpaul wreq.wi_len = (sizeof(sc->wi_stats) / 2) + 1; 132156965Swpaul } else if (wreq.wi_type == WI_RID_DEFLT_CRYPT_KEYS) { 132256965Swpaul bcopy((char *)&sc->wi_keys, (char *)&wreq, 132356965Swpaul sizeof(struct wi_ltv_keys)); 132453702Swpaul } 132553702Swpaul#ifdef WICACHE 132653702Swpaul else if (wreq.wi_type == WI_RID_ZERO_CACHE) { 132753702Swpaul sc->wi_sigitems = sc->wi_nextitem = 0; 132853702Swpaul } else if (wreq.wi_type == WI_RID_READ_CACHE) { 132953702Swpaul char *pt = (char *)&wreq.wi_val; 133053702Swpaul bcopy((char *)&sc->wi_sigitems, 133153702Swpaul (char *)pt, sizeof(int)); 133253702Swpaul pt += (sizeof (int)); 133353702Swpaul wreq.wi_len = sizeof(int) / 2; 133453702Swpaul bcopy((char *)&sc->wi_sigcache, (char *)pt, 133553702Swpaul sizeof(struct wi_sigcache) * sc->wi_sigitems); 133653702Swpaul wreq.wi_len += ((sizeof(struct wi_sigcache) * 133753702Swpaul sc->wi_sigitems) / 2) + 1; 133853702Swpaul } 133953702Swpaul#endif 134053702Swpaul else { 134146492Swpaul if (wi_read_record(sc, (struct wi_ltv_gen *)&wreq)) { 134246492Swpaul error = EINVAL; 134346492Swpaul break; 134446492Swpaul } 134546492Swpaul } 134646492Swpaul error = copyout(&wreq, ifr->ifr_data, sizeof(wreq)); 134746492Swpaul break; 134846492Swpaul case SIOCSWAVELAN: 134961818Sroberto if ((error = suser(p))) 135061818Sroberto goto out; 135146492Swpaul error = copyin(ifr->ifr_data, &wreq, sizeof(wreq)); 135246492Swpaul if (error) 135346492Swpaul break; 135446492Swpaul if (wreq.wi_type == WI_RID_IFACE_STATS) { 135546492Swpaul error = EINVAL; 135646492Swpaul break; 135746492Swpaul } else if (wreq.wi_type == WI_RID_MGMT_XMIT) { 135846492Swpaul error = wi_mgmt_xmit(sc, (caddr_t)&wreq.wi_val, 135946492Swpaul wreq.wi_len); 136046492Swpaul } else { 136146492Swpaul error = wi_write_record(sc, (struct wi_ltv_gen *)&wreq); 136246492Swpaul if (!error) 136346492Swpaul wi_setdef(sc, &wreq); 136446492Swpaul } 136546492Swpaul break; 136646492Swpaul default: 136746492Swpaul error = EINVAL; 136846492Swpaul break; 136946492Swpaul } 137061818Srobertoout: 137167092Swpaul WI_UNLOCK(sc); 137246492Swpaul 137346492Swpaul return(error); 137446492Swpaul} 137546492Swpaul 137646492Swpaulstatic void wi_init(xsc) 137746492Swpaul void *xsc; 137846492Swpaul{ 137946492Swpaul struct wi_softc *sc = xsc; 138046492Swpaul struct ifnet *ifp = &sc->arpcom.ac_if; 138146492Swpaul struct wi_ltv_macaddr mac; 138246492Swpaul int id = 0; 138346492Swpaul 138467092Swpaul WI_LOCK(sc); 138567092Swpaul 138667092Swpaul if (sc->wi_gone) { 138767092Swpaul WI_UNLOCK(sc); 138846492Swpaul return; 138967092Swpaul } 139046492Swpaul 139146492Swpaul if (ifp->if_flags & IFF_RUNNING) 139246492Swpaul wi_stop(sc); 139346492Swpaul 139446492Swpaul wi_reset(sc); 139546492Swpaul 139646492Swpaul /* Program max data length. */ 139746492Swpaul WI_SETVAL(WI_RID_MAX_DATALEN, sc->wi_max_data_len); 139846492Swpaul 139947401Swpaul /* Enable/disable IBSS creation. */ 140046492Swpaul WI_SETVAL(WI_RID_CREATE_IBSS, sc->wi_create_ibss); 140146492Swpaul 140246492Swpaul /* Set the port type. */ 140346492Swpaul WI_SETVAL(WI_RID_PORTTYPE, sc->wi_ptype); 140446492Swpaul 140546492Swpaul /* Program the RTS/CTS threshold. */ 140646492Swpaul WI_SETVAL(WI_RID_RTS_THRESH, sc->wi_rts_thresh); 140746492Swpaul 140846492Swpaul /* Program the TX rate */ 140946492Swpaul WI_SETVAL(WI_RID_TX_RATE, sc->wi_tx_rate); 141046492Swpaul 141146492Swpaul /* Access point density */ 141246492Swpaul WI_SETVAL(WI_RID_SYSTEM_SCALE, sc->wi_ap_density); 141346492Swpaul 141446611Swpaul /* Power Management Enabled */ 141546611Swpaul WI_SETVAL(WI_RID_PM_ENABLED, sc->wi_pm_enabled); 141646611Swpaul 141746611Swpaul /* Power Managment Max Sleep */ 141846611Swpaul WI_SETVAL(WI_RID_MAX_SLEEP, sc->wi_max_sleep); 141946611Swpaul 142046492Swpaul /* Specify the IBSS name */ 142146492Swpaul WI_SETSTR(WI_RID_OWN_SSID, sc->wi_ibss_name); 142246492Swpaul 142346492Swpaul /* Specify the network name */ 142446492Swpaul WI_SETSTR(WI_RID_DESIRED_SSID, sc->wi_net_name); 142546492Swpaul 142646563Swpaul /* Specify the frequency to use */ 142746563Swpaul WI_SETVAL(WI_RID_OWN_CHNL, sc->wi_channel); 142846563Swpaul 142946492Swpaul /* Program the nodename. */ 143046492Swpaul WI_SETSTR(WI_RID_NODENAME, sc->wi_node_name); 143146492Swpaul 143246492Swpaul /* Set our MAC address. */ 143346492Swpaul mac.wi_len = 4; 143446492Swpaul mac.wi_type = WI_RID_MAC_NODE; 143546492Swpaul bcopy((char *)&sc->arpcom.ac_enaddr, 143646492Swpaul (char *)&mac.wi_mac_addr, ETHER_ADDR_LEN); 143746492Swpaul wi_write_record(sc, (struct wi_ltv_gen *)&mac); 143846492Swpaul 143956965Swpaul /* Configure WEP. */ 144056965Swpaul if (sc->wi_has_wep) { 144156965Swpaul WI_SETVAL(WI_RID_ENCRYPTION, sc->wi_use_wep); 144256965Swpaul WI_SETVAL(WI_RID_TX_CRYPT_KEY, sc->wi_tx_key); 144356965Swpaul sc->wi_keys.wi_len = (sizeof(struct wi_ltv_keys) / 2) + 1; 144456965Swpaul sc->wi_keys.wi_type = WI_RID_DEFLT_CRYPT_KEYS; 144556965Swpaul wi_write_record(sc, (struct wi_ltv_gen *)&sc->wi_keys); 144656965Swpaul } 144756965Swpaul 144846492Swpaul /* Initialize promisc mode. */ 144946492Swpaul if (ifp->if_flags & IFF_PROMISC) { 145046492Swpaul WI_SETVAL(WI_RID_PROMISC, 1); 145146492Swpaul } else { 145246492Swpaul WI_SETVAL(WI_RID_PROMISC, 0); 145346492Swpaul } 145446492Swpaul 145546492Swpaul /* Set multicast filter. */ 145646492Swpaul wi_setmulti(sc); 145746492Swpaul 145846492Swpaul /* Enable desired port */ 145946492Swpaul wi_cmd(sc, WI_CMD_ENABLE|sc->wi_portnum, 0); 146046492Swpaul 146146492Swpaul if (wi_alloc_nicmem(sc, 1518 + sizeof(struct wi_frame) + 8, &id)) 146253702Swpaul device_printf(sc->dev, "tx buffer allocation failed\n"); 146346492Swpaul sc->wi_tx_data_id = id; 146446492Swpaul 146546492Swpaul if (wi_alloc_nicmem(sc, 1518 + sizeof(struct wi_frame) + 8, &id)) 146653702Swpaul device_printf(sc->dev, "mgmt. buffer allocation failed\n"); 146746492Swpaul sc->wi_tx_mgmt_id = id; 146846492Swpaul 146946492Swpaul /* enable interrupts */ 147046492Swpaul CSR_WRITE_2(sc, WI_INT_EN, WI_INTRS); 147146492Swpaul 147246492Swpaul ifp->if_flags |= IFF_RUNNING; 147346492Swpaul ifp->if_flags &= ~IFF_OACTIVE; 147446492Swpaul 147546492Swpaul sc->wi_stat_ch = timeout(wi_inquire, sc, hz * 60); 147667092Swpaul WI_UNLOCK(sc); 147746492Swpaul 147846492Swpaul return; 147946492Swpaul} 148046492Swpaul 148146492Swpaulstatic void wi_start(ifp) 148246492Swpaul struct ifnet *ifp; 148346492Swpaul{ 148446492Swpaul struct wi_softc *sc; 148546492Swpaul struct mbuf *m0; 148646492Swpaul struct wi_frame tx_frame; 148746492Swpaul struct ether_header *eh; 148846492Swpaul int id; 148946492Swpaul 149046492Swpaul sc = ifp->if_softc; 149167092Swpaul WI_LOCK(sc); 149246492Swpaul 149367092Swpaul if (sc->wi_gone) { 149467092Swpaul WI_UNLOCK(sc); 149546492Swpaul return; 149667092Swpaul } 149746492Swpaul 149867092Swpaul if (ifp->if_flags & IFF_OACTIVE) { 149967092Swpaul WI_UNLOCK(sc); 150046492Swpaul return; 150167092Swpaul } 150246492Swpaul 150346492Swpaul IF_DEQUEUE(&ifp->if_snd, m0); 150467092Swpaul if (m0 == NULL) { 150567092Swpaul WI_UNLOCK(sc); 150646492Swpaul return; 150767092Swpaul } 150846492Swpaul 150946492Swpaul bzero((char *)&tx_frame, sizeof(tx_frame)); 151046492Swpaul id = sc->wi_tx_data_id; 151146492Swpaul eh = mtod(m0, struct ether_header *); 151246492Swpaul 151346492Swpaul /* 151447401Swpaul * Use RFC1042 encoding for IP and ARP datagrams, 151546492Swpaul * 802.3 for anything else. 151646492Swpaul */ 151755831Swpaul if (ntohs(eh->ether_type) > 1518) { 151846492Swpaul bcopy((char *)&eh->ether_dhost, 151946492Swpaul (char *)&tx_frame.wi_addr1, ETHER_ADDR_LEN); 152046492Swpaul bcopy((char *)&eh->ether_shost, 152146492Swpaul (char *)&tx_frame.wi_addr2, ETHER_ADDR_LEN); 152246492Swpaul bcopy((char *)&eh->ether_dhost, 152346492Swpaul (char *)&tx_frame.wi_dst_addr, ETHER_ADDR_LEN); 152446492Swpaul bcopy((char *)&eh->ether_shost, 152546492Swpaul (char *)&tx_frame.wi_src_addr, ETHER_ADDR_LEN); 152646492Swpaul 152746492Swpaul tx_frame.wi_dat_len = m0->m_pkthdr.len - WI_SNAPHDR_LEN; 152846492Swpaul tx_frame.wi_frame_ctl = WI_FTYPE_DATA; 152946492Swpaul tx_frame.wi_dat[0] = htons(WI_SNAP_WORD0); 153046492Swpaul tx_frame.wi_dat[1] = htons(WI_SNAP_WORD1); 153146492Swpaul tx_frame.wi_len = htons(m0->m_pkthdr.len - WI_SNAPHDR_LEN); 153246492Swpaul tx_frame.wi_type = eh->ether_type; 153346492Swpaul 153446492Swpaul m_copydata(m0, sizeof(struct ether_header), 153546492Swpaul m0->m_pkthdr.len - sizeof(struct ether_header), 153646492Swpaul (caddr_t)&sc->wi_txbuf); 153746492Swpaul 153846492Swpaul wi_write_data(sc, id, 0, (caddr_t)&tx_frame, 153946492Swpaul sizeof(struct wi_frame)); 154046492Swpaul wi_write_data(sc, id, WI_802_11_OFFSET, (caddr_t)&sc->wi_txbuf, 154146492Swpaul (m0->m_pkthdr.len - sizeof(struct ether_header)) + 2); 154246492Swpaul } else { 154346492Swpaul tx_frame.wi_dat_len = m0->m_pkthdr.len; 154446492Swpaul 154555831Swpaul eh->ether_type = htons(m0->m_pkthdr.len - WI_SNAPHDR_LEN); 154646492Swpaul m_copydata(m0, 0, m0->m_pkthdr.len, (caddr_t)&sc->wi_txbuf); 154746492Swpaul 154846492Swpaul wi_write_data(sc, id, 0, (caddr_t)&tx_frame, 154946492Swpaul sizeof(struct wi_frame)); 155046492Swpaul wi_write_data(sc, id, WI_802_3_OFFSET, (caddr_t)&sc->wi_txbuf, 155146492Swpaul m0->m_pkthdr.len + 2); 155246492Swpaul } 155346492Swpaul 155446492Swpaul /* 155546492Swpaul * If there's a BPF listner, bounce a copy of 155646492Swpaul * this frame to him. 155746492Swpaul */ 155846492Swpaul if (ifp->if_bpf) 155946492Swpaul bpf_mtap(ifp, m0); 156046492Swpaul 156146492Swpaul m_freem(m0); 156246492Swpaul 156346492Swpaul if (wi_cmd(sc, WI_CMD_TX|WI_RECLAIM, id)) 156453702Swpaul device_printf(sc->dev, "xmit failed\n"); 156546492Swpaul 156646492Swpaul ifp->if_flags |= IFF_OACTIVE; 156746492Swpaul 156846492Swpaul /* 156946492Swpaul * Set a timeout in case the chip goes out to lunch. 157046492Swpaul */ 157146492Swpaul ifp->if_timer = 5; 157246492Swpaul 157367092Swpaul WI_UNLOCK(sc); 157446492Swpaul return; 157546492Swpaul} 157646492Swpaul 157746492Swpaulstatic int wi_mgmt_xmit(sc, data, len) 157846492Swpaul struct wi_softc *sc; 157946492Swpaul caddr_t data; 158046492Swpaul int len; 158146492Swpaul{ 158246492Swpaul struct wi_frame tx_frame; 158346492Swpaul int id; 158446492Swpaul struct wi_80211_hdr *hdr; 158546492Swpaul caddr_t dptr; 158646492Swpaul 158746492Swpaul if (sc->wi_gone) 158846492Swpaul return(ENODEV); 158946492Swpaul 159046492Swpaul hdr = (struct wi_80211_hdr *)data; 159146492Swpaul dptr = data + sizeof(struct wi_80211_hdr); 159246492Swpaul 159346492Swpaul bzero((char *)&tx_frame, sizeof(tx_frame)); 159446492Swpaul id = sc->wi_tx_mgmt_id; 159546492Swpaul 159646492Swpaul bcopy((char *)hdr, (char *)&tx_frame.wi_frame_ctl, 159746492Swpaul sizeof(struct wi_80211_hdr)); 159846492Swpaul 159946492Swpaul tx_frame.wi_dat_len = len - WI_SNAPHDR_LEN; 160046492Swpaul tx_frame.wi_len = htons(len - WI_SNAPHDR_LEN); 160146492Swpaul 160246492Swpaul wi_write_data(sc, id, 0, (caddr_t)&tx_frame, sizeof(struct wi_frame)); 160346492Swpaul wi_write_data(sc, id, WI_802_11_OFFSET_RAW, dptr, 160446492Swpaul (len - sizeof(struct wi_80211_hdr)) + 2); 160546492Swpaul 160646492Swpaul if (wi_cmd(sc, WI_CMD_TX|WI_RECLAIM, id)) { 160753702Swpaul device_printf(sc->dev, "xmit failed\n"); 160846492Swpaul return(EIO); 160946492Swpaul } 161046492Swpaul 161146492Swpaul return(0); 161246492Swpaul} 161346492Swpaul 161446492Swpaulstatic void wi_stop(sc) 161546492Swpaul struct wi_softc *sc; 161646492Swpaul{ 161746492Swpaul struct ifnet *ifp; 161846492Swpaul 161967092Swpaul WI_LOCK(sc); 162067092Swpaul 162167092Swpaul if (sc->wi_gone) { 162267092Swpaul WI_UNLOCK(sc); 162346492Swpaul return; 162467092Swpaul } 162546492Swpaul 162646492Swpaul ifp = &sc->arpcom.ac_if; 162746492Swpaul 162870173Sjhb /* 162970173Sjhb * If the card is gone and the memory port isn't mapped, we will 163070173Sjhb * (hopefully) get 0xffff back from the status read, which is not 163170173Sjhb * a valid status value. 163270173Sjhb */ 163370173Sjhb if (CSR_READ_2(sc, WI_STATUS) != 0xffff) { 163470173Sjhb CSR_WRITE_2(sc, WI_INT_EN, 0); 163570173Sjhb wi_cmd(sc, WI_CMD_DISABLE|sc->wi_portnum, 0); 163670173Sjhb } 163746492Swpaul 163846492Swpaul untimeout(wi_inquire, sc, sc->wi_stat_ch); 163946492Swpaul 164046492Swpaul ifp->if_flags &= ~(IFF_RUNNING|IFF_OACTIVE); 164146492Swpaul 164267092Swpaul WI_UNLOCK(sc); 164346492Swpaul return; 164446492Swpaul} 164546492Swpaul 164646492Swpaulstatic void wi_watchdog(ifp) 164746492Swpaul struct ifnet *ifp; 164846492Swpaul{ 164946492Swpaul struct wi_softc *sc; 165046492Swpaul 165146492Swpaul sc = ifp->if_softc; 165246492Swpaul 165353702Swpaul device_printf(sc->dev,"device timeout\n"); 165446492Swpaul 165546492Swpaul wi_init(sc); 165646492Swpaul 165746492Swpaul ifp->if_oerrors++; 165846492Swpaul 165946492Swpaul return; 166046492Swpaul} 166146492Swpaul 166274906Salfredstatic int 166374906Salfredwi_alloc(dev, io_rid) 166453702Swpaul device_t dev; 166574906Salfred int io_rid; 166646492Swpaul{ 166753702Swpaul struct wi_softc *sc = device_get_softc(dev); 166853702Swpaul 166974906Salfred sc->iobase_rid = io_rid; 167074906Salfred sc->iobase = bus_alloc_resource(dev, SYS_RES_IOPORT, &sc->iobase_rid, 167170073Swpaul 0, ~0, (1 << 6), 167270073Swpaul rman_make_alignment_flags(1 << 6) | RF_ACTIVE); 167353702Swpaul if (!sc->iobase) { 167453702Swpaul device_printf(dev, "No I/O space?!\n"); 167553702Swpaul return (ENXIO); 167653702Swpaul } 167753702Swpaul 167874906Salfred sc->irq_rid = 0; 167974906Salfred sc->irq = bus_alloc_resource(dev, SYS_RES_IRQ, &sc->irq_rid, 168053702Swpaul 0, ~0, 1, RF_ACTIVE); 168153702Swpaul if (!sc->irq) { 168253702Swpaul device_printf(dev, "No irq?!\n"); 168353702Swpaul return (ENXIO); 168453702Swpaul } 168553702Swpaul 168653702Swpaul sc->dev = dev; 168753702Swpaul sc->wi_unit = device_get_unit(dev); 168853702Swpaul sc->wi_io_addr = rman_get_start(sc->iobase); 168953702Swpaul sc->wi_btag = rman_get_bustag(sc->iobase); 169053702Swpaul sc->wi_bhandle = rman_get_bushandle(sc->iobase); 169153702Swpaul 169253702Swpaul return (0); 169353702Swpaul} 169453702Swpaul 169553702Swpaulstatic void wi_free(dev) 169653702Swpaul device_t dev; 169753702Swpaul{ 169853702Swpaul struct wi_softc *sc = device_get_softc(dev); 169953702Swpaul 170053702Swpaul if (sc->iobase != NULL) 170153702Swpaul bus_release_resource(dev, SYS_RES_IOPORT, 0, sc->iobase); 170253702Swpaul if (sc->irq != NULL) 170353702Swpaul bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq); 170474906Salfred if (sc->mem != NULL) 170574906Salfred bus_release_resource(dev, SYS_RES_MEMORY, sc->mem_rid, sc->mem); 170653702Swpaul 170753702Swpaul return; 170853702Swpaul} 170953702Swpaul 171053702Swpaulstatic void wi_shutdown(dev) 171153702Swpaul device_t dev; 171253702Swpaul{ 171346492Swpaul struct wi_softc *sc; 171446492Swpaul 171553702Swpaul sc = device_get_softc(dev); 171646492Swpaul wi_stop(sc); 171746492Swpaul 171846492Swpaul return; 171946492Swpaul} 172053702Swpaul 172153702Swpaul#ifdef WICACHE 172253702Swpaul/* wavelan signal strength cache code. 172353702Swpaul * store signal/noise/quality on per MAC src basis in 172453702Swpaul * a small fixed cache. The cache wraps if > MAX slots 172553702Swpaul * used. The cache may be zeroed out to start over. 172653702Swpaul * Two simple filters exist to reduce computation: 172753702Swpaul * 1. ip only (literally 0x800) which may be used 172853702Swpaul * to ignore some packets. It defaults to ip only. 172953702Swpaul * it could be used to focus on broadcast, non-IP 802.11 beacons. 173053702Swpaul * 2. multicast/broadcast only. This may be used to 173153702Swpaul * ignore unicast packets and only cache signal strength 173253702Swpaul * for multicast/broadcast packets (beacons); e.g., Mobile-IP 173353702Swpaul * beacons and not unicast traffic. 173453702Swpaul * 173553702Swpaul * The cache stores (MAC src(index), IP src (major clue), signal, 173653702Swpaul * quality, noise) 173753702Swpaul * 173853702Swpaul * No apologies for storing IP src here. It's easy and saves much 173953702Swpaul * trouble elsewhere. The cache is assumed to be INET dependent, 174053702Swpaul * although it need not be. 174153702Swpaul */ 174253702Swpaul 174353702Swpaul#ifdef documentation 174453702Swpaul 174553702Swpaulint wi_sigitems; /* number of cached entries */ 174653702Swpaulstruct wi_sigcache wi_sigcache[MAXWICACHE]; /* array of cache entries */ 174753702Swpaulint wi_nextitem; /* index/# of entries */ 174853702Swpaul 174953702Swpaul 175053702Swpaul#endif 175153702Swpaul 175253702Swpaul/* control variables for cache filtering. Basic idea is 175353702Swpaul * to reduce cost (e.g., to only Mobile-IP agent beacons 175453702Swpaul * which are broadcast or multicast). Still you might 175553702Swpaul * want to measure signal strength with unicast ping packets 175653702Swpaul * on a pt. to pt. ant. setup. 175753702Swpaul */ 175853702Swpaul/* set true if you want to limit cache items to broadcast/mcast 175953702Swpaul * only packets (not unicast). Useful for mobile-ip beacons which 176053702Swpaul * are broadcast/multicast at network layer. Default is all packets 176153702Swpaul * so ping/unicast will work say with pt. to pt. antennae setup. 176253702Swpaul */ 176353702Swpaulstatic int wi_cache_mcastonly = 0; 176453702SwpaulSYSCTL_INT(_machdep, OID_AUTO, wi_cache_mcastonly, CTLFLAG_RW, 176553702Swpaul &wi_cache_mcastonly, 0, ""); 176653702Swpaul 176753702Swpaul/* set true if you want to limit cache items to IP packets only 176853702Swpaul*/ 176953702Swpaulstatic int wi_cache_iponly = 1; 177053702SwpaulSYSCTL_INT(_machdep, OID_AUTO, wi_cache_iponly, CTLFLAG_RW, 177153702Swpaul &wi_cache_iponly, 0, ""); 177253702Swpaul 177353702Swpaul/* 177453702Swpaul * Original comments: 177553702Swpaul * ----------------- 177653702Swpaul * wi_cache_store, per rx packet store signal 177753702Swpaul * strength in MAC (src) indexed cache. 177853702Swpaul * 177953702Swpaul * follows linux driver in how signal strength is computed. 178053702Swpaul * In ad hoc mode, we use the rx_quality field. 178153702Swpaul * signal and noise are trimmed to fit in the range from 47..138. 178253702Swpaul * rx_quality field MSB is signal strength. 178353702Swpaul * rx_quality field LSB is noise. 178453702Swpaul * "quality" is (signal - noise) as is log value. 178553702Swpaul * note: quality CAN be negative. 178653702Swpaul * 178753702Swpaul * In BSS mode, we use the RID for communication quality. 178853702Swpaul * TBD: BSS mode is currently untested. 178953702Swpaul * 179053702Swpaul * Bill's comments: 179153702Swpaul * --------------- 179253702Swpaul * Actually, we use the rx_quality field all the time for both "ad-hoc" 179353702Swpaul * and BSS modes. Why? Because reading an RID is really, really expensive: 179453702Swpaul * there's a bunch of PIO operations that have to be done to read a record 179553702Swpaul * from the NIC, and reading the comms quality RID each time a packet is 179653702Swpaul * received can really hurt performance. We don't have to do this anyway: 179753702Swpaul * the comms quality field only reflects the values in the rx_quality field 179853702Swpaul * anyway. The comms quality RID is only meaningful in infrastructure mode, 179953702Swpaul * but the values it contains are updated based on the rx_quality from 180053702Swpaul * frames received from the access point. 180153702Swpaul * 180253702Swpaul * Also, according to Lucent, the signal strength and noise level values 180353702Swpaul * can be converted to dBms by subtracting 149, so I've modified the code 180453702Swpaul * to do that instead of the scaling it did originally. 180553702Swpaul */ 180653702Swpaulstatic 180753702Swpaulvoid wi_cache_store (struct wi_softc *sc, struct ether_header *eh, 180853702Swpaul struct mbuf *m, unsigned short rx_quality) 180953702Swpaul{ 181053702Swpaul struct ip *ip = 0; 181153702Swpaul int i; 181253702Swpaul static int cache_slot = 0; /* use this cache entry */ 181353702Swpaul static int wrapindex = 0; /* next "free" cache entry */ 181453702Swpaul int sig, noise; 181553702Swpaul int sawip=0; 181653702Swpaul 181753702Swpaul /* filters: 181853702Swpaul * 1. ip only 181953702Swpaul * 2. configurable filter to throw out unicast packets, 182053702Swpaul * keep multicast only. 182153702Swpaul */ 182253702Swpaul 182353702Swpaul if ((ntohs(eh->ether_type) == 0x800)) { 182453702Swpaul sawip = 1; 182553702Swpaul } 182653702Swpaul 182753702Swpaul /* filter for ip packets only 182853702Swpaul */ 182953702Swpaul if (wi_cache_iponly && !sawip) { 183053702Swpaul return; 183153702Swpaul } 183253702Swpaul 183353702Swpaul /* filter for broadcast/multicast only 183453702Swpaul */ 183553702Swpaul if (wi_cache_mcastonly && ((eh->ether_dhost[0] & 1) == 0)) { 183653702Swpaul return; 183753702Swpaul } 183853702Swpaul 183953702Swpaul#ifdef SIGDEBUG 184053702Swpaul printf("wi%d: q value %x (MSB=0x%x, LSB=0x%x) \n", sc->wi_unit, 184153702Swpaul rx_quality & 0xffff, rx_quality >> 8, rx_quality & 0xff); 184253702Swpaul#endif 184353702Swpaul 184453702Swpaul /* find the ip header. we want to store the ip_src 184553702Swpaul * address. 184653702Swpaul */ 184753702Swpaul if (sawip) { 184853702Swpaul ip = mtod(m, struct ip *); 184953702Swpaul } 185053702Swpaul 185153702Swpaul /* do a linear search for a matching MAC address 185253702Swpaul * in the cache table 185353702Swpaul * . MAC address is 6 bytes, 185453702Swpaul * . var w_nextitem holds total number of entries already cached 185553702Swpaul */ 185653702Swpaul for(i = 0; i < sc->wi_nextitem; i++) { 185753702Swpaul if (! bcmp(eh->ether_shost , sc->wi_sigcache[i].macsrc, 6 )) { 185853702Swpaul /* Match!, 185953702Swpaul * so we already have this entry, 186053702Swpaul * update the data 186153702Swpaul */ 186253702Swpaul break; 186353702Swpaul } 186453702Swpaul } 186553702Swpaul 186653702Swpaul /* did we find a matching mac address? 186753702Swpaul * if yes, then overwrite a previously existing cache entry 186853702Swpaul */ 186953702Swpaul if (i < sc->wi_nextitem ) { 187053702Swpaul cache_slot = i; 187153702Swpaul } 187253702Swpaul /* else, have a new address entry,so 187353702Swpaul * add this new entry, 187453702Swpaul * if table full, then we need to replace LRU entry 187553702Swpaul */ 187653702Swpaul else { 187753702Swpaul 187853702Swpaul /* check for space in cache table 187953702Swpaul * note: wi_nextitem also holds number of entries 188053702Swpaul * added in the cache table 188153702Swpaul */ 188253702Swpaul if ( sc->wi_nextitem < MAXWICACHE ) { 188353702Swpaul cache_slot = sc->wi_nextitem; 188453702Swpaul sc->wi_nextitem++; 188553702Swpaul sc->wi_sigitems = sc->wi_nextitem; 188653702Swpaul } 188753702Swpaul /* no space found, so simply wrap with wrap index 188853702Swpaul * and "zap" the next entry 188953702Swpaul */ 189053702Swpaul else { 189153702Swpaul if (wrapindex == MAXWICACHE) { 189253702Swpaul wrapindex = 0; 189353702Swpaul } 189453702Swpaul cache_slot = wrapindex++; 189553702Swpaul } 189653702Swpaul } 189753702Swpaul 189853702Swpaul /* invariant: cache_slot now points at some slot 189953702Swpaul * in cache. 190053702Swpaul */ 190153702Swpaul if (cache_slot < 0 || cache_slot >= MAXWICACHE) { 190253702Swpaul log(LOG_ERR, "wi_cache_store, bad index: %d of " 190353702Swpaul "[0..%d], gross cache error\n", 190453702Swpaul cache_slot, MAXWICACHE); 190553702Swpaul return; 190653702Swpaul } 190753702Swpaul 190853702Swpaul /* store items in cache 190953702Swpaul * .ip source address 191053702Swpaul * .mac src 191153702Swpaul * .signal, etc. 191253702Swpaul */ 191353702Swpaul if (sawip) { 191453702Swpaul sc->wi_sigcache[cache_slot].ipsrc = ip->ip_src.s_addr; 191553702Swpaul } 191653702Swpaul bcopy( eh->ether_shost, sc->wi_sigcache[cache_slot].macsrc, 6); 191753702Swpaul 191853702Swpaul sig = (rx_quality >> 8) & 0xFF; 191953702Swpaul noise = rx_quality & 0xFF; 192053702Swpaul sc->wi_sigcache[cache_slot].signal = sig - 149; 192153702Swpaul sc->wi_sigcache[cache_slot].noise = noise - 149; 192253702Swpaul sc->wi_sigcache[cache_slot].quality = sig - noise; 192353702Swpaul 192453702Swpaul return; 192553702Swpaul} 192653702Swpaul#endif 1927