if_wi.c revision 67092
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 * 3250477Speter * $FreeBSD: head/sys/dev/wi/if_wi.c 67092 2000-10-13 20:33:24Z wpaul $ 3346492Swpaul */ 3446492Swpaul 3546492Swpaul/* 3646492Swpaul * Lucent WaveLAN/IEEE 802.11 PCMCIA driver for FreeBSD. 3746492Swpaul * 3846492Swpaul * Written by Bill Paul <wpaul@ctr.columbia.edu> 3946492Swpaul * Electrical Engineering Department 4046492Swpaul * Columbia University, New York City 4146492Swpaul */ 4246492Swpaul 4346492Swpaul/* 4447401Swpaul * The WaveLAN/IEEE adapter is the second generation of the WaveLAN 4546492Swpaul * from Lucent. Unlike the older cards, the new ones are programmed 4646492Swpaul * entirely via a firmware-driven controller called the Hermes. 4746492Swpaul * Unfortunately, Lucent will not release the Hermes programming manual 4846492Swpaul * without an NDA (if at all). What they do release is an API library 4946492Swpaul * called the HCF (Hardware Control Functions) which is supposed to 5046492Swpaul * do the device-specific operations of a device driver for you. The 5146492Swpaul * publically available version of the HCF library (the 'HCF Light') is 5247401Swpaul * a) extremely gross, b) lacks certain features, particularly support 5346492Swpaul * for 802.11 frames, and c) is contaminated by the GNU Public License. 5446492Swpaul * 5546492Swpaul * This driver does not use the HCF or HCF Light at all. Instead, it 5646492Swpaul * programs the Hermes controller directly, using information gleaned 5746492Swpaul * from the HCF Light code and corresponding documentation. 5846492Swpaul * 5946492Swpaul * This driver supports both the PCMCIA and ISA versions of the 6046492Swpaul * WaveLAN/IEEE cards. Note however that the ISA card isn't really 6146492Swpaul * anything of the sort: it's actually a PCMCIA bridge adapter 6246492Swpaul * that fits into an ISA slot, into which a PCMCIA WaveLAN card is 6346492Swpaul * inserted. Consequently, you need to use the pccard support for 6446492Swpaul * both the ISA and PCMCIA adapters. 6546492Swpaul */ 6646492Swpaul 6746492Swpaul#define WI_HERMES_AUTOINC_WAR /* Work around data write autoinc bug. */ 6846492Swpaul#define WI_HERMES_STATS_WAR /* Work around stats counter bug. */ 6953702Swpaul#define WICACHE /* turn on signal strength cache code */ 7046492Swpaul 7146492Swpaul#include <sys/param.h> 7246492Swpaul#include <sys/systm.h> 7346492Swpaul#include <sys/sockio.h> 7446492Swpaul#include <sys/mbuf.h> 7546492Swpaul#include <sys/kernel.h> 7661818Sroberto#include <sys/proc.h> 7761818Sroberto#include <sys/ucred.h> 7846492Swpaul#include <sys/socket.h> 7953702Swpaul#include <sys/module.h> 8053702Swpaul#include <sys/bus.h> 8153702Swpaul#include <sys/syslog.h> 8253702Swpaul#include <sys/sysctl.h> 8346492Swpaul 8453702Swpaul#include <machine/bus.h> 8553702Swpaul#include <machine/resource.h> 8653702Swpaul#include <machine/clock.h> 8753702Swpaul#include <machine/md_var.h> 8853702Swpaul#include <machine/bus_pio.h> 8953702Swpaul#include <sys/rman.h> 9053702Swpaul 9146492Swpaul#include <net/if.h> 9246492Swpaul#include <net/if_arp.h> 9346492Swpaul#include <net/ethernet.h> 9446492Swpaul#include <net/if_dl.h> 9546492Swpaul#include <net/if_media.h> 9646492Swpaul#include <net/if_types.h> 9746492Swpaul 9846492Swpaul#include <netinet/in.h> 9946492Swpaul#include <netinet/in_systm.h> 10046492Swpaul#include <netinet/in_var.h> 10146492Swpaul#include <netinet/ip.h> 10246492Swpaul#include <netinet/if_ether.h> 10346492Swpaul 10446492Swpaul#include <net/bpf.h> 10546492Swpaul 10653702Swpaul#include <machine/if_wavelan_ieee.h> 10746492Swpaul#include <i386/isa/if_wireg.h> 10846492Swpaul 10946492Swpaul#if !defined(lint) 11046492Swpaulstatic const char rcsid[] = 11150477Speter "$FreeBSD: head/sys/dev/wi/if_wi.c 67092 2000-10-13 20:33:24Z wpaul $"; 11246492Swpaul#endif 11346492Swpaul 11446492Swpaul#ifdef foo 11547401Swpaulstatic u_int8_t wi_mcast_addr[6] = { 0x01, 0x60, 0x1D, 0x00, 0x01, 0x00 }; 11646492Swpaul#endif 11746492Swpaul 11853702Swpaulstatic void wi_intr __P((void *)); 11946492Swpaulstatic void wi_reset __P((struct wi_softc *)); 12046492Swpaulstatic int wi_ioctl __P((struct ifnet *, u_long, caddr_t)); 12146492Swpaulstatic void wi_init __P((void *)); 12246492Swpaulstatic void wi_start __P((struct ifnet *)); 12346492Swpaulstatic void wi_stop __P((struct wi_softc *)); 12446492Swpaulstatic void wi_watchdog __P((struct ifnet *)); 12546492Swpaulstatic void wi_rxeof __P((struct wi_softc *)); 12646492Swpaulstatic void wi_txeof __P((struct wi_softc *, int)); 12746492Swpaulstatic void wi_update_stats __P((struct wi_softc *)); 12846492Swpaulstatic void wi_setmulti __P((struct wi_softc *)); 12946492Swpaul 13046492Swpaulstatic int wi_cmd __P((struct wi_softc *, int, int)); 13146492Swpaulstatic int wi_read_record __P((struct wi_softc *, struct wi_ltv_gen *)); 13246492Swpaulstatic int wi_write_record __P((struct wi_softc *, struct wi_ltv_gen *)); 13346492Swpaulstatic int wi_read_data __P((struct wi_softc *, int, 13446492Swpaul int, caddr_t, int)); 13546492Swpaulstatic int wi_write_data __P((struct wi_softc *, int, 13646492Swpaul int, caddr_t, int)); 13746492Swpaulstatic int wi_seek __P((struct wi_softc *, int, int, int)); 13846492Swpaulstatic int wi_alloc_nicmem __P((struct wi_softc *, int, int *)); 13946492Swpaulstatic void wi_inquire __P((void *)); 14046492Swpaulstatic void wi_setdef __P((struct wi_softc *, struct wi_req *)); 14146492Swpaulstatic int wi_mgmt_xmit __P((struct wi_softc *, caddr_t, int)); 14246492Swpaul 14353702Swpaul#ifdef WICACHE 14453702Swpaulstatic 14553702Swpaulvoid wi_cache_store __P((struct wi_softc *, struct ether_header *, 14653702Swpaul struct mbuf *, unsigned short)); 14753702Swpaul#endif 14853702Swpaul 14953702Swpaulstatic int wi_pccard_probe __P((device_t)); 15053702Swpaulstatic int wi_pccard_attach __P((device_t)); 15153702Swpaulstatic int wi_pccard_detach __P((device_t)); 15253702Swpaulstatic void wi_shutdown __P((device_t)); 15353702Swpaul 15453702Swpaulstatic int wi_alloc __P((device_t)); 15553702Swpaulstatic void wi_free __P((device_t)); 15653702Swpaul 15753702Swpaulstatic device_method_t wi_pccard_methods[] = { 15853702Swpaul /* Device interface */ 15953702Swpaul DEVMETHOD(device_probe, wi_pccard_probe), 16053702Swpaul DEVMETHOD(device_attach, wi_pccard_attach), 16153702Swpaul DEVMETHOD(device_detach, wi_pccard_detach), 16253702Swpaul DEVMETHOD(device_shutdown, wi_shutdown), 16353702Swpaul 16453702Swpaul { 0, 0 } 16546492Swpaul}; 16646492Swpaul 16753702Swpaulstatic driver_t wi_pccard_driver = { 16846492Swpaul "wi", 16953702Swpaul wi_pccard_methods, 17053702Swpaul sizeof(struct wi_softc) 17146492Swpaul}; 17246492Swpaul 17353702Swpaulstatic devclass_t wi_pccard_devclass; 17446492Swpaul 17553702SwpaulDRIVER_MODULE(if_wi, pccard, wi_pccard_driver, wi_pccard_devclass, 0, 0); 17653702Swpaul 17753702Swpaulstatic int wi_pccard_probe(dev) 17853702Swpaul device_t dev; 17946492Swpaul{ 18053702Swpaul struct wi_softc *sc; 18153702Swpaul int error; 18246492Swpaul 18353702Swpaul sc = device_get_softc(dev); 18446492Swpaul sc->wi_gone = 0; 18546492Swpaul 18653702Swpaul error = wi_alloc(dev); 18753702Swpaul if (error) 18853702Swpaul return (error); 18953702Swpaul 19053702Swpaul device_set_desc(dev, "WaveLAN/IEEE 802.11"); 19153702Swpaul wi_free(dev); 19253702Swpaul 19346492Swpaul /* Make sure interrupts are disabled. */ 19446492Swpaul CSR_WRITE_2(sc, WI_INT_EN, 0); 19546492Swpaul CSR_WRITE_2(sc, WI_EVENT_ACK, 0xFFFF); 19646492Swpaul 19753702Swpaul return (0); 19846492Swpaul} 19946492Swpaul 20053702Swpaulstatic int wi_pccard_detach(dev) 20153702Swpaul device_t dev; 20246492Swpaul{ 20346492Swpaul struct wi_softc *sc; 20446492Swpaul struct ifnet *ifp; 20546492Swpaul 20653702Swpaul sc = device_get_softc(dev); 20767092Swpaul WI_LOCK(sc); 20846492Swpaul ifp = &sc->arpcom.ac_if; 20946492Swpaul 21046492Swpaul if (sc->wi_gone) { 21153702Swpaul device_printf(dev, "already unloaded\n"); 21267092Swpaul WI_UNLOCK(sc); 21353702Swpaul return(ENODEV); 21446492Swpaul } 21546492Swpaul 21653702Swpaul wi_stop(sc); 21758274Srwatson 21863090Sarchie ether_ifdetach(ifp, ETHER_BPF_SUPPORTED); 21954277Swpaul bus_teardown_intr(dev, sc->irq, sc->wi_intrhand); 22053702Swpaul wi_free(dev); 22146492Swpaul sc->wi_gone = 1; 22246492Swpaul 22353702Swpaul device_printf(dev, "unload\n"); 22467092Swpaul WI_UNLOCK(sc); 22567092Swpaul mtx_destroy(&sc->wi_mtx); 22646492Swpaul 22746492Swpaul return(0); 22846492Swpaul} 22946492Swpaul 23053702Swpaulstatic int wi_pccard_attach(device_t dev) 23146492Swpaul{ 23246492Swpaul struct wi_softc *sc; 23346492Swpaul struct wi_ltv_macaddr mac; 23446563Swpaul struct wi_ltv_gen gen; 23546492Swpaul struct ifnet *ifp; 23653702Swpaul int error; 23746492Swpaul 23853702Swpaul sc = device_get_softc(dev); 23946492Swpaul ifp = &sc->arpcom.ac_if; 24046492Swpaul 24153702Swpaul error = wi_alloc(dev); 24253702Swpaul if (error) { 24353702Swpaul device_printf(dev, "wi_alloc() failed! (%d)\n", error); 24453702Swpaul return (error); 24553702Swpaul } 24653702Swpaul 24753702Swpaul error = bus_setup_intr(dev, sc->irq, INTR_TYPE_NET, 24853702Swpaul wi_intr, sc, &sc->wi_intrhand); 24953702Swpaul 25053702Swpaul if (error) { 25153702Swpaul device_printf(dev, "bus_setup_intr() failed! (%d)\n", error); 25253702Swpaul wi_free(dev); 25353702Swpaul return (error); 25453702Swpaul } 25553702Swpaul 25667092Swpaul mtx_init(&sc->wi_mtx, device_get_nameunit(dev), MTX_DEF); 25767092Swpaul WI_LOCK(sc); 25867092Swpaul 25946492Swpaul /* Reset the NIC. */ 26046492Swpaul wi_reset(sc); 26146492Swpaul 26246492Swpaul /* Read the station address. */ 26346492Swpaul mac.wi_type = WI_RID_MAC_NODE; 26446492Swpaul mac.wi_len = 4; 26546492Swpaul wi_read_record(sc, (struct wi_ltv_gen *)&mac); 26646492Swpaul bcopy((char *)&mac.wi_mac_addr, 26746492Swpaul (char *)&sc->arpcom.ac_enaddr, ETHER_ADDR_LEN); 26846492Swpaul 26953702Swpaul device_printf(dev, "Ethernet address: %6D\n", 27046492Swpaul sc->arpcom.ac_enaddr, ":"); 27146492Swpaul 27246492Swpaul ifp->if_softc = sc; 27346492Swpaul ifp->if_unit = sc->wi_unit; 27446492Swpaul ifp->if_name = "wi"; 27546492Swpaul ifp->if_mtu = ETHERMTU; 27646492Swpaul ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; 27746492Swpaul ifp->if_ioctl = wi_ioctl; 27846492Swpaul ifp->if_output = ether_output; 27946492Swpaul ifp->if_start = wi_start; 28046492Swpaul ifp->if_watchdog = wi_watchdog; 28146492Swpaul ifp->if_init = wi_init; 28246492Swpaul ifp->if_baudrate = 10000000; 28346492Swpaul ifp->if_snd.ifq_maxlen = IFQ_MAXLEN; 28446492Swpaul 28546492Swpaul bzero(sc->wi_node_name, sizeof(sc->wi_node_name)); 28646492Swpaul bcopy(WI_DEFAULT_NODENAME, sc->wi_node_name, 28746492Swpaul sizeof(WI_DEFAULT_NODENAME) - 1); 28846492Swpaul 28946492Swpaul bzero(sc->wi_net_name, sizeof(sc->wi_net_name)); 29046492Swpaul bcopy(WI_DEFAULT_NETNAME, sc->wi_net_name, 29146492Swpaul sizeof(WI_DEFAULT_NETNAME) - 1); 29246492Swpaul 29346492Swpaul bzero(sc->wi_ibss_name, sizeof(sc->wi_ibss_name)); 29446492Swpaul bcopy(WI_DEFAULT_IBSS, sc->wi_ibss_name, 29546492Swpaul sizeof(WI_DEFAULT_IBSS) - 1); 29646492Swpaul 29746492Swpaul sc->wi_portnum = WI_DEFAULT_PORT; 29846492Swpaul sc->wi_ptype = WI_PORTTYPE_ADHOC; 29946492Swpaul sc->wi_ap_density = WI_DEFAULT_AP_DENSITY; 30046492Swpaul sc->wi_rts_thresh = WI_DEFAULT_RTS_THRESH; 30146492Swpaul sc->wi_tx_rate = WI_DEFAULT_TX_RATE; 30246492Swpaul sc->wi_max_data_len = WI_DEFAULT_DATALEN; 30346492Swpaul sc->wi_create_ibss = WI_DEFAULT_CREATE_IBSS; 30446611Swpaul sc->wi_pm_enabled = WI_DEFAULT_PM_ENABLED; 30546611Swpaul sc->wi_max_sleep = WI_DEFAULT_MAX_SLEEP; 30646492Swpaul 30746563Swpaul /* 30846563Swpaul * Read the default channel from the NIC. This may vary 30946563Swpaul * depending on the country where the NIC was purchased, so 31046563Swpaul * we can't hard-code a default and expect it to work for 31146563Swpaul * everyone. 31246563Swpaul */ 31346563Swpaul gen.wi_type = WI_RID_OWN_CHNL; 31446563Swpaul gen.wi_len = 2; 31546563Swpaul wi_read_record(sc, &gen); 31646563Swpaul sc->wi_channel = gen.wi_val; 31746563Swpaul 31856965Swpaul /* 31956965Swpaul * Find out if we support WEP on this card. 32056965Swpaul */ 32156965Swpaul gen.wi_type = WI_RID_WEP_AVAIL; 32256965Swpaul gen.wi_len = 2; 32356965Swpaul wi_read_record(sc, &gen); 32456965Swpaul sc->wi_has_wep = gen.wi_val; 32556965Swpaul 32646492Swpaul bzero((char *)&sc->wi_stats, sizeof(sc->wi_stats)); 32746492Swpaul 32846492Swpaul wi_init(sc); 32946492Swpaul wi_stop(sc); 33046492Swpaul 33146492Swpaul /* 33263090Sarchie * Call MI attach routine. 33346492Swpaul */ 33463090Sarchie ether_ifattach(ifp, ETHER_BPF_SUPPORTED); 33553702Swpaul callout_handle_init(&sc->wi_stat_ch); 33667092Swpaul WI_UNLOCK(sc); 33746492Swpaul 33846492Swpaul return(0); 33946492Swpaul} 34046492Swpaul 34146492Swpaulstatic void wi_rxeof(sc) 34246492Swpaul struct wi_softc *sc; 34346492Swpaul{ 34446492Swpaul struct ifnet *ifp; 34546492Swpaul struct ether_header *eh; 34646492Swpaul struct wi_frame rx_frame; 34746492Swpaul struct mbuf *m; 34846492Swpaul int id; 34946492Swpaul 35046492Swpaul ifp = &sc->arpcom.ac_if; 35146492Swpaul 35246492Swpaul id = CSR_READ_2(sc, WI_RX_FID); 35346492Swpaul 35446492Swpaul /* First read in the frame header */ 35546492Swpaul if (wi_read_data(sc, id, 0, (caddr_t)&rx_frame, sizeof(rx_frame))) { 35646492Swpaul ifp->if_ierrors++; 35746492Swpaul return; 35846492Swpaul } 35946492Swpaul 36046492Swpaul if (rx_frame.wi_status & WI_STAT_ERRSTAT) { 36146492Swpaul ifp->if_ierrors++; 36246492Swpaul return; 36346492Swpaul } 36446492Swpaul 36546492Swpaul MGETHDR(m, M_DONTWAIT, MT_DATA); 36646492Swpaul if (m == NULL) { 36746492Swpaul ifp->if_ierrors++; 36846492Swpaul return; 36946492Swpaul } 37046492Swpaul MCLGET(m, M_DONTWAIT); 37146492Swpaul if (!(m->m_flags & M_EXT)) { 37246492Swpaul m_freem(m); 37346492Swpaul ifp->if_ierrors++; 37446492Swpaul return; 37546492Swpaul } 37646492Swpaul 37746492Swpaul eh = mtod(m, struct ether_header *); 37846492Swpaul m->m_pkthdr.rcvif = ifp; 37946492Swpaul 38046492Swpaul if (rx_frame.wi_status == WI_STAT_1042 || 38146492Swpaul rx_frame.wi_status == WI_STAT_TUNNEL || 38246492Swpaul rx_frame.wi_status == WI_STAT_WMP_MSG) { 38348553Swpaul if((rx_frame.wi_dat_len + WI_SNAPHDR_LEN) > MCLBYTES) { 38453702Swpaul device_printf(sc->dev, "oversized packet received " 38553702Swpaul "(wi_dat_len=%d, wi_status=0x%x)\n", 38648553Swpaul rx_frame.wi_dat_len, rx_frame.wi_status); 38748553Swpaul m_freem(m); 38848553Swpaul ifp->if_ierrors++; 38948553Swpaul return; 39048553Swpaul } 39146492Swpaul m->m_pkthdr.len = m->m_len = 39246492Swpaul rx_frame.wi_dat_len + WI_SNAPHDR_LEN; 39346492Swpaul 39446492Swpaul bcopy((char *)&rx_frame.wi_addr1, 39546492Swpaul (char *)&eh->ether_dhost, ETHER_ADDR_LEN); 39659328Swpaul if (sc->wi_ptype == WI_PORTTYPE_ADHOC) { 39759328Swpaul bcopy((char *)&rx_frame.wi_addr2, 39859328Swpaul (char *)&eh->ether_shost, ETHER_ADDR_LEN); 39959328Swpaul } else { 40059328Swpaul bcopy((char *)&rx_frame.wi_addr3, 40159328Swpaul (char *)&eh->ether_shost, ETHER_ADDR_LEN); 40259328Swpaul } 40346492Swpaul bcopy((char *)&rx_frame.wi_type, 40446492Swpaul (char *)&eh->ether_type, sizeof(u_int16_t)); 40546492Swpaul 40646492Swpaul if (wi_read_data(sc, id, WI_802_11_OFFSET, 40746492Swpaul mtod(m, caddr_t) + sizeof(struct ether_header), 40846492Swpaul m->m_len + 2)) { 40946492Swpaul m_freem(m); 41046492Swpaul ifp->if_ierrors++; 41146492Swpaul return; 41246492Swpaul } 41346492Swpaul } else { 41448553Swpaul if((rx_frame.wi_dat_len + 41548553Swpaul sizeof(struct ether_header)) > MCLBYTES) { 41653702Swpaul device_printf(sc->dev, "oversized packet received " 41753702Swpaul "(wi_dat_len=%d, wi_status=0x%x)\n", 41848553Swpaul rx_frame.wi_dat_len, rx_frame.wi_status); 41948553Swpaul m_freem(m); 42048553Swpaul ifp->if_ierrors++; 42148553Swpaul return; 42248553Swpaul } 42346492Swpaul m->m_pkthdr.len = m->m_len = 42446492Swpaul rx_frame.wi_dat_len + sizeof(struct ether_header); 42546492Swpaul 42646492Swpaul if (wi_read_data(sc, id, WI_802_3_OFFSET, 42746492Swpaul mtod(m, caddr_t), m->m_len + 2)) { 42846492Swpaul m_freem(m); 42946492Swpaul ifp->if_ierrors++; 43046492Swpaul return; 43146492Swpaul } 43246492Swpaul } 43346492Swpaul 43446492Swpaul ifp->if_ipackets++; 43546492Swpaul 43646492Swpaul /* Receive packet. */ 43746492Swpaul m_adj(m, sizeof(struct ether_header)); 43853702Swpaul#ifdef WICACHE 43953702Swpaul wi_cache_store(sc, eh, m, rx_frame.wi_q_info); 44053702Swpaul#endif 44146492Swpaul ether_input(ifp, eh, m); 44246492Swpaul} 44346492Swpaul 44446492Swpaulstatic void wi_txeof(sc, status) 44546492Swpaul struct wi_softc *sc; 44646492Swpaul int status; 44746492Swpaul{ 44846492Swpaul struct ifnet *ifp; 44946492Swpaul 45046492Swpaul ifp = &sc->arpcom.ac_if; 45146492Swpaul 45246492Swpaul ifp->if_timer = 0; 45346492Swpaul ifp->if_flags &= ~IFF_OACTIVE; 45446492Swpaul 45546492Swpaul if (status & WI_EV_TX_EXC) 45646492Swpaul ifp->if_oerrors++; 45746492Swpaul else 45846492Swpaul ifp->if_opackets++; 45946492Swpaul 46046492Swpaul return; 46146492Swpaul} 46246492Swpaul 46346492Swpaulvoid wi_inquire(xsc) 46446492Swpaul void *xsc; 46546492Swpaul{ 46646492Swpaul struct wi_softc *sc; 46746492Swpaul struct ifnet *ifp; 46846492Swpaul 46946492Swpaul sc = xsc; 47046492Swpaul ifp = &sc->arpcom.ac_if; 47146492Swpaul 47246492Swpaul sc->wi_stat_ch = timeout(wi_inquire, sc, hz * 60); 47346492Swpaul 47446492Swpaul /* Don't do this while we're transmitting */ 47546492Swpaul if (ifp->if_flags & IFF_OACTIVE) 47646492Swpaul return; 47746492Swpaul 47846492Swpaul wi_cmd(sc, WI_CMD_INQUIRE, WI_INFO_COUNTERS); 47946492Swpaul 48046492Swpaul return; 48146492Swpaul} 48246492Swpaul 48346492Swpaulvoid wi_update_stats(sc) 48446492Swpaul struct wi_softc *sc; 48546492Swpaul{ 48646492Swpaul struct wi_ltv_gen gen; 48746492Swpaul u_int16_t id; 48846492Swpaul struct ifnet *ifp; 48946492Swpaul u_int32_t *ptr; 49046492Swpaul int i; 49146492Swpaul u_int16_t t; 49246492Swpaul 49346492Swpaul ifp = &sc->arpcom.ac_if; 49446492Swpaul 49546492Swpaul id = CSR_READ_2(sc, WI_INFO_FID); 49646492Swpaul 49746492Swpaul wi_read_data(sc, id, 0, (char *)&gen, 4); 49846492Swpaul 49946492Swpaul if (gen.wi_type != WI_INFO_COUNTERS || 50046492Swpaul gen.wi_len > (sizeof(sc->wi_stats) / 4) + 1) 50146492Swpaul return; 50246492Swpaul 50346492Swpaul ptr = (u_int32_t *)&sc->wi_stats; 50446492Swpaul 50546492Swpaul for (i = 0; i < gen.wi_len - 1; i++) { 50646492Swpaul t = CSR_READ_2(sc, WI_DATA1); 50746492Swpaul#ifdef WI_HERMES_STATS_WAR 50846492Swpaul if (t > 0xF000) 50946492Swpaul t = ~t & 0xFFFF; 51046492Swpaul#endif 51146492Swpaul ptr[i] += t; 51246492Swpaul } 51346492Swpaul 51446492Swpaul ifp->if_collisions = sc->wi_stats.wi_tx_single_retries + 51546492Swpaul sc->wi_stats.wi_tx_multi_retries + 51646492Swpaul sc->wi_stats.wi_tx_retry_limit; 51746492Swpaul 51846492Swpaul return; 51946492Swpaul} 52046492Swpaul 52153702Swpaulstatic void wi_intr(xsc) 52253702Swpaul void *xsc; 52346492Swpaul{ 52453702Swpaul struct wi_softc *sc = xsc; 52546492Swpaul struct ifnet *ifp; 52646492Swpaul u_int16_t status; 52746492Swpaul 52867092Swpaul WI_LOCK(sc); 52967092Swpaul 53046492Swpaul ifp = &sc->arpcom.ac_if; 53146492Swpaul 53246492Swpaul if (!(ifp->if_flags & IFF_UP)) { 53346492Swpaul CSR_WRITE_2(sc, WI_EVENT_ACK, 0xFFFF); 53446492Swpaul CSR_WRITE_2(sc, WI_INT_EN, 0); 53567092Swpaul WI_UNLOCK(sc); 53646492Swpaul return; 53746492Swpaul } 53846492Swpaul 53946492Swpaul /* Disable interrupts. */ 54046492Swpaul CSR_WRITE_2(sc, WI_INT_EN, 0); 54146492Swpaul 54246492Swpaul status = CSR_READ_2(sc, WI_EVENT_STAT); 54346492Swpaul CSR_WRITE_2(sc, WI_EVENT_ACK, ~WI_INTRS); 54446492Swpaul 54546492Swpaul if (status & WI_EV_RX) { 54646492Swpaul wi_rxeof(sc); 54746492Swpaul CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_RX); 54846492Swpaul } 54946492Swpaul 55046492Swpaul if (status & WI_EV_TX) { 55146492Swpaul wi_txeof(sc, status); 55246492Swpaul CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_TX); 55346492Swpaul } 55446492Swpaul 55546492Swpaul if (status & WI_EV_ALLOC) { 55646492Swpaul int id; 55746492Swpaul id = CSR_READ_2(sc, WI_ALLOC_FID); 55846492Swpaul CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_ALLOC); 55946492Swpaul if (id == sc->wi_tx_data_id) 56046492Swpaul wi_txeof(sc, status); 56146492Swpaul } 56246492Swpaul 56346492Swpaul if (status & WI_EV_INFO) { 56446492Swpaul wi_update_stats(sc); 56546492Swpaul CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_INFO); 56646492Swpaul } 56746492Swpaul 56846492Swpaul if (status & WI_EV_TX_EXC) { 56946492Swpaul wi_txeof(sc, status); 57046492Swpaul CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_TX_EXC); 57146492Swpaul } 57246492Swpaul 57346492Swpaul if (status & WI_EV_INFO_DROP) { 57446492Swpaul CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_INFO_DROP); 57546492Swpaul } 57646492Swpaul 57746492Swpaul /* Re-enable interrupts. */ 57846492Swpaul CSR_WRITE_2(sc, WI_INT_EN, WI_INTRS); 57946492Swpaul 58046492Swpaul if (ifp->if_snd.ifq_head != NULL) 58146492Swpaul wi_start(ifp); 58246492Swpaul 58367092Swpaul WI_UNLOCK(sc); 58467092Swpaul 58546492Swpaul return; 58646492Swpaul} 58746492Swpaul 58846492Swpaulstatic int wi_cmd(sc, cmd, val) 58946492Swpaul struct wi_softc *sc; 59046492Swpaul int cmd; 59146492Swpaul int val; 59246492Swpaul{ 59346492Swpaul int i, s = 0; 59446492Swpaul 59546492Swpaul CSR_WRITE_2(sc, WI_PARAM0, val); 59646492Swpaul CSR_WRITE_2(sc, WI_COMMAND, cmd); 59746492Swpaul 59846492Swpaul for (i = 0; i < WI_TIMEOUT; i++) { 59946492Swpaul /* 60046492Swpaul * Wait for 'command complete' bit to be 60146492Swpaul * set in the event status register. 60246492Swpaul */ 60346492Swpaul s = CSR_READ_2(sc, WI_EVENT_STAT) & WI_EV_CMD; 60446492Swpaul if (s) { 60546492Swpaul /* Ack the event and read result code. */ 60646492Swpaul s = CSR_READ_2(sc, WI_STATUS); 60746492Swpaul CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_CMD); 60846492Swpaul#ifdef foo 60946492Swpaul if ((s & WI_CMD_CODE_MASK) != (cmd & WI_CMD_CODE_MASK)) 61046492Swpaul return(EIO); 61146492Swpaul#endif 61246492Swpaul if (s & WI_STAT_CMD_RESULT) 61346492Swpaul return(EIO); 61446492Swpaul break; 61546492Swpaul } 61646492Swpaul } 61746492Swpaul 61846492Swpaul if (i == WI_TIMEOUT) 61946492Swpaul return(ETIMEDOUT); 62046492Swpaul 62146492Swpaul return(0); 62246492Swpaul} 62346492Swpaul 62446492Swpaulstatic void wi_reset(sc) 62546492Swpaul struct wi_softc *sc; 62646492Swpaul{ 62753702Swpaul wi_cmd(sc, WI_CMD_INI, 0); 62853702Swpaul DELAY(100000); 62953702Swpaul wi_cmd(sc, WI_CMD_INI, 0); 63053702Swpaul DELAY(100000); 63153702Swpaul#ifdef foo 63246492Swpaul if (wi_cmd(sc, WI_CMD_INI, 0)) 63353702Swpaul device_printf(sc->dev, "init failed\n"); 63446492Swpaul CSR_WRITE_2(sc, WI_INT_EN, 0); 63546492Swpaul CSR_WRITE_2(sc, WI_EVENT_ACK, 0xFFFF); 63646492Swpaul 63746492Swpaul /* Calibrate timer. */ 63846492Swpaul WI_SETVAL(WI_RID_TICK_TIME, 8); 63953702Swpaul#endif 64046492Swpaul return; 64146492Swpaul} 64246492Swpaul 64346492Swpaul/* 64446492Swpaul * Read an LTV record from the NIC. 64546492Swpaul */ 64646492Swpaulstatic int wi_read_record(sc, ltv) 64746492Swpaul struct wi_softc *sc; 64846492Swpaul struct wi_ltv_gen *ltv; 64946492Swpaul{ 65046492Swpaul u_int16_t *ptr; 65146492Swpaul int i, len, code; 65246492Swpaul 65346492Swpaul /* Tell the NIC to enter record read mode. */ 65446492Swpaul if (wi_cmd(sc, WI_CMD_ACCESS|WI_ACCESS_READ, ltv->wi_type)) 65546492Swpaul return(EIO); 65646492Swpaul 65747789Swpaul /* Seek to the record. */ 65847789Swpaul if (wi_seek(sc, ltv->wi_type, 0, WI_BAP1)) 65947789Swpaul return(EIO); 66046492Swpaul 66146492Swpaul /* 66246492Swpaul * Read the length and record type and make sure they 66346492Swpaul * match what we expect (this verifies that we have enough 66447401Swpaul * room to hold all of the returned data). 66546492Swpaul */ 66646492Swpaul len = CSR_READ_2(sc, WI_DATA1); 66746492Swpaul if (len > ltv->wi_len) 66846492Swpaul return(ENOSPC); 66946492Swpaul code = CSR_READ_2(sc, WI_DATA1); 67046492Swpaul if (code != ltv->wi_type) 67146492Swpaul return(EIO); 67246492Swpaul 67346492Swpaul ltv->wi_len = len; 67446492Swpaul ltv->wi_type = code; 67546492Swpaul 67646492Swpaul /* Now read the data. */ 67746492Swpaul ptr = <v->wi_val; 67846492Swpaul for (i = 0; i < ltv->wi_len - 1; i++) 67946492Swpaul ptr[i] = CSR_READ_2(sc, WI_DATA1); 68046492Swpaul 68146492Swpaul return(0); 68246492Swpaul} 68346492Swpaul 68446492Swpaul/* 68546492Swpaul * Same as read, except we inject data instead of reading it. 68646492Swpaul */ 68746492Swpaulstatic int wi_write_record(sc, ltv) 68846492Swpaul struct wi_softc *sc; 68946492Swpaul struct wi_ltv_gen *ltv; 69046492Swpaul{ 69146492Swpaul u_int16_t *ptr; 69246492Swpaul int i; 69346492Swpaul 69447789Swpaul if (wi_seek(sc, ltv->wi_type, 0, WI_BAP1)) 69547789Swpaul return(EIO); 69646492Swpaul 69746492Swpaul CSR_WRITE_2(sc, WI_DATA1, ltv->wi_len); 69846492Swpaul CSR_WRITE_2(sc, WI_DATA1, ltv->wi_type); 69946492Swpaul 70046492Swpaul ptr = <v->wi_val; 70146492Swpaul for (i = 0; i < ltv->wi_len - 1; i++) 70246492Swpaul CSR_WRITE_2(sc, WI_DATA1, ptr[i]); 70346492Swpaul 70446492Swpaul if (wi_cmd(sc, WI_CMD_ACCESS|WI_ACCESS_WRITE, ltv->wi_type)) 70546492Swpaul return(EIO); 70646492Swpaul 70746492Swpaul return(0); 70846492Swpaul} 70946492Swpaul 71046492Swpaulstatic int wi_seek(sc, id, off, chan) 71146492Swpaul struct wi_softc *sc; 71246492Swpaul int id, off, chan; 71346492Swpaul{ 71446492Swpaul int i; 71546492Swpaul int selreg, offreg; 71646492Swpaul 71746492Swpaul switch (chan) { 71846492Swpaul case WI_BAP0: 71946492Swpaul selreg = WI_SEL0; 72046492Swpaul offreg = WI_OFF0; 72146492Swpaul break; 72246492Swpaul case WI_BAP1: 72346492Swpaul selreg = WI_SEL1; 72446492Swpaul offreg = WI_OFF1; 72546492Swpaul break; 72646492Swpaul default: 72753702Swpaul device_printf(sc->dev, "invalid data path: %x\n", chan); 72846492Swpaul return(EIO); 72946492Swpaul } 73046492Swpaul 73146492Swpaul CSR_WRITE_2(sc, selreg, id); 73246492Swpaul CSR_WRITE_2(sc, offreg, off); 73346492Swpaul 73446492Swpaul for (i = 0; i < WI_TIMEOUT; i++) { 73546492Swpaul if (!(CSR_READ_2(sc, offreg) & (WI_OFF_BUSY|WI_OFF_ERR))) 73646492Swpaul break; 73746492Swpaul } 73846492Swpaul 73946492Swpaul if (i == WI_TIMEOUT) 74046492Swpaul return(ETIMEDOUT); 74146492Swpaul 74246492Swpaul return(0); 74346492Swpaul} 74446492Swpaul 74546492Swpaulstatic int wi_read_data(sc, id, off, buf, len) 74646492Swpaul struct wi_softc *sc; 74746492Swpaul int id, off; 74846492Swpaul caddr_t buf; 74946492Swpaul int len; 75046492Swpaul{ 75146492Swpaul int i; 75246492Swpaul u_int16_t *ptr; 75346492Swpaul 75446492Swpaul if (wi_seek(sc, id, off, WI_BAP1)) 75546492Swpaul return(EIO); 75646492Swpaul 75746492Swpaul ptr = (u_int16_t *)buf; 75846492Swpaul for (i = 0; i < len / 2; i++) 75946492Swpaul ptr[i] = CSR_READ_2(sc, WI_DATA1); 76046492Swpaul 76146492Swpaul return(0); 76246492Swpaul} 76346492Swpaul 76446492Swpaul/* 76546492Swpaul * According to the comments in the HCF Light code, there is a bug in 76646492Swpaul * the Hermes (or possibly in certain Hermes firmware revisions) where 76746492Swpaul * the chip's internal autoincrement counter gets thrown off during 76846492Swpaul * data writes: the autoincrement is missed, causing one data word to 76946492Swpaul * be overwritten and subsequent words to be written to the wrong memory 77046492Swpaul * locations. The end result is that we could end up transmitting bogus 77146492Swpaul * frames without realizing it. The workaround for this is to write a 77246492Swpaul * couple of extra guard words after the end of the transfer, then 77346492Swpaul * attempt to read then back. If we fail to locate the guard words where 77446492Swpaul * we expect them, we preform the transfer over again. 77546492Swpaul */ 77646492Swpaulstatic int wi_write_data(sc, id, off, buf, len) 77746492Swpaul struct wi_softc *sc; 77846492Swpaul int id, off; 77946492Swpaul caddr_t buf; 78046492Swpaul int len; 78146492Swpaul{ 78246492Swpaul int i; 78346492Swpaul u_int16_t *ptr; 78446492Swpaul 78546492Swpaul#ifdef WI_HERMES_AUTOINC_WAR 78646492Swpaulagain: 78746492Swpaul#endif 78846492Swpaul 78946492Swpaul if (wi_seek(sc, id, off, WI_BAP0)) 79046492Swpaul return(EIO); 79146492Swpaul 79246492Swpaul ptr = (u_int16_t *)buf; 79346492Swpaul for (i = 0; i < (len / 2); i++) 79446492Swpaul CSR_WRITE_2(sc, WI_DATA0, ptr[i]); 79546492Swpaul 79646492Swpaul#ifdef WI_HERMES_AUTOINC_WAR 79746492Swpaul CSR_WRITE_2(sc, WI_DATA0, 0x1234); 79846492Swpaul CSR_WRITE_2(sc, WI_DATA0, 0x5678); 79946492Swpaul 80046492Swpaul if (wi_seek(sc, id, off + len, WI_BAP0)) 80146492Swpaul return(EIO); 80246492Swpaul 80346492Swpaul if (CSR_READ_2(sc, WI_DATA0) != 0x1234 || 80446492Swpaul CSR_READ_2(sc, WI_DATA0) != 0x5678) 80546492Swpaul goto again; 80646492Swpaul#endif 80746492Swpaul 80846492Swpaul return(0); 80946492Swpaul} 81046492Swpaul 81146492Swpaul/* 81246492Swpaul * Allocate a region of memory inside the NIC and zero 81346492Swpaul * it out. 81446492Swpaul */ 81546492Swpaulstatic int wi_alloc_nicmem(sc, len, id) 81646492Swpaul struct wi_softc *sc; 81746492Swpaul int len; 81846492Swpaul int *id; 81946492Swpaul{ 82046492Swpaul int i; 82146492Swpaul 82246492Swpaul if (wi_cmd(sc, WI_CMD_ALLOC_MEM, len)) { 82353702Swpaul device_printf(sc->dev, "failed to allocate %d bytes on NIC\n", len); 82446492Swpaul return(ENOMEM); 82546492Swpaul } 82646492Swpaul 82746492Swpaul for (i = 0; i < WI_TIMEOUT; i++) { 82846492Swpaul if (CSR_READ_2(sc, WI_EVENT_STAT) & WI_EV_ALLOC) 82946492Swpaul break; 83046492Swpaul } 83146492Swpaul 83246492Swpaul if (i == WI_TIMEOUT) 83346492Swpaul return(ETIMEDOUT); 83446492Swpaul 83546492Swpaul CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_ALLOC); 83646492Swpaul *id = CSR_READ_2(sc, WI_ALLOC_FID); 83746492Swpaul 83847789Swpaul if (wi_seek(sc, *id, 0, WI_BAP0)) 83947789Swpaul return(EIO); 84046492Swpaul 84146492Swpaul for (i = 0; i < len / 2; i++) 84246492Swpaul CSR_WRITE_2(sc, WI_DATA0, 0); 84346492Swpaul 84446492Swpaul return(0); 84546492Swpaul} 84646492Swpaul 84746492Swpaulstatic void wi_setmulti(sc) 84846492Swpaul struct wi_softc *sc; 84946492Swpaul{ 85046492Swpaul struct ifnet *ifp; 85146492Swpaul int i = 0; 85246492Swpaul struct ifmultiaddr *ifma; 85346492Swpaul struct wi_ltv_mcast mcast; 85446492Swpaul 85546492Swpaul ifp = &sc->arpcom.ac_if; 85646492Swpaul 85746492Swpaul bzero((char *)&mcast, sizeof(mcast)); 85846492Swpaul 85946492Swpaul mcast.wi_type = WI_RID_MCAST; 86046492Swpaul mcast.wi_len = (3 * 16) + 1; 86146492Swpaul 86246492Swpaul if (ifp->if_flags & IFF_ALLMULTI || ifp->if_flags & IFF_PROMISC) { 86346492Swpaul wi_write_record(sc, (struct wi_ltv_gen *)&mcast); 86446492Swpaul return; 86546492Swpaul } 86646492Swpaul 86746492Swpaul for (ifma = ifp->if_multiaddrs.lh_first; ifma != NULL; 86846492Swpaul ifma = ifma->ifma_link.le_next) { 86946492Swpaul if (ifma->ifma_addr->sa_family != AF_LINK) 87046492Swpaul continue; 87146492Swpaul if (i < 16) { 87246492Swpaul bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr), 87346492Swpaul (char *)&mcast.wi_mcast[i], ETHER_ADDR_LEN); 87446492Swpaul i++; 87546492Swpaul } else { 87646492Swpaul bzero((char *)&mcast, sizeof(mcast)); 87746492Swpaul break; 87846492Swpaul } 87946492Swpaul } 88046492Swpaul 88146492Swpaul mcast.wi_len = (i * 3) + 1; 88246492Swpaul wi_write_record(sc, (struct wi_ltv_gen *)&mcast); 88346492Swpaul 88446492Swpaul return; 88546492Swpaul} 88646492Swpaul 88746492Swpaulstatic void wi_setdef(sc, wreq) 88846492Swpaul struct wi_softc *sc; 88946492Swpaul struct wi_req *wreq; 89046492Swpaul{ 89146492Swpaul struct sockaddr_dl *sdl; 89246492Swpaul struct ifaddr *ifa; 89346492Swpaul struct ifnet *ifp; 89446492Swpaul 89546492Swpaul ifp = &sc->arpcom.ac_if; 89646492Swpaul 89746492Swpaul switch(wreq->wi_type) { 89846492Swpaul case WI_RID_MAC_NODE: 89946492Swpaul ifa = ifnet_addrs[ifp->if_index - 1]; 90046492Swpaul sdl = (struct sockaddr_dl *)ifa->ifa_addr; 90146492Swpaul bcopy((char *)&wreq->wi_val, (char *)&sc->arpcom.ac_enaddr, 90246492Swpaul ETHER_ADDR_LEN); 90346492Swpaul bcopy((char *)&wreq->wi_val, LLADDR(sdl), ETHER_ADDR_LEN); 90446492Swpaul break; 90546492Swpaul case WI_RID_PORTTYPE: 90646492Swpaul sc->wi_ptype = wreq->wi_val[0]; 90746492Swpaul break; 90846492Swpaul case WI_RID_TX_RATE: 90946492Swpaul sc->wi_tx_rate = wreq->wi_val[0]; 91046492Swpaul break; 91146492Swpaul case WI_RID_MAX_DATALEN: 91246492Swpaul sc->wi_max_data_len = wreq->wi_val[0]; 91346492Swpaul break; 91446492Swpaul case WI_RID_RTS_THRESH: 91546492Swpaul sc->wi_rts_thresh = wreq->wi_val[0]; 91646492Swpaul break; 91746492Swpaul case WI_RID_SYSTEM_SCALE: 91846492Swpaul sc->wi_ap_density = wreq->wi_val[0]; 91946492Swpaul break; 92046492Swpaul case WI_RID_CREATE_IBSS: 92146492Swpaul sc->wi_create_ibss = wreq->wi_val[0]; 92246492Swpaul break; 92346563Swpaul case WI_RID_OWN_CHNL: 92446563Swpaul sc->wi_channel = wreq->wi_val[0]; 92546563Swpaul break; 92646492Swpaul case WI_RID_NODENAME: 92746492Swpaul bzero(sc->wi_node_name, sizeof(sc->wi_node_name)); 92846492Swpaul bcopy((char *)&wreq->wi_val[1], sc->wi_node_name, 30); 92946492Swpaul break; 93046492Swpaul case WI_RID_DESIRED_SSID: 93146492Swpaul bzero(sc->wi_net_name, sizeof(sc->wi_net_name)); 93246492Swpaul bcopy((char *)&wreq->wi_val[1], sc->wi_net_name, 30); 93346492Swpaul break; 93446492Swpaul case WI_RID_OWN_SSID: 93546492Swpaul bzero(sc->wi_ibss_name, sizeof(sc->wi_ibss_name)); 93646492Swpaul bcopy((char *)&wreq->wi_val[1], sc->wi_ibss_name, 30); 93746492Swpaul break; 93846611Swpaul case WI_RID_PM_ENABLED: 93946611Swpaul sc->wi_pm_enabled = wreq->wi_val[0]; 94046611Swpaul break; 94146611Swpaul case WI_RID_MAX_SLEEP: 94246611Swpaul sc->wi_max_sleep = wreq->wi_val[0]; 94346611Swpaul break; 94456965Swpaul case WI_RID_ENCRYPTION: 94556965Swpaul sc->wi_use_wep = wreq->wi_val[0]; 94656965Swpaul break; 94756965Swpaul case WI_RID_TX_CRYPT_KEY: 94856965Swpaul sc->wi_tx_key = wreq->wi_val[0]; 94956965Swpaul break; 95056965Swpaul case WI_RID_DEFLT_CRYPT_KEYS: 95156965Swpaul bcopy((char *)wreq, (char *)&sc->wi_keys, 95256965Swpaul sizeof(struct wi_ltv_keys)); 95356965Swpaul break; 95446492Swpaul default: 95546492Swpaul break; 95646492Swpaul } 95746492Swpaul 95846563Swpaul /* Reinitialize WaveLAN. */ 95946563Swpaul wi_init(sc); 96046563Swpaul 96146492Swpaul return; 96246492Swpaul} 96346492Swpaul 96446492Swpaulstatic int wi_ioctl(ifp, command, data) 96546492Swpaul struct ifnet *ifp; 96646492Swpaul u_long command; 96746492Swpaul caddr_t data; 96846492Swpaul{ 96967092Swpaul int error = 0; 97046492Swpaul struct wi_softc *sc; 97146492Swpaul struct wi_req wreq; 97246492Swpaul struct ifreq *ifr; 97361818Sroberto struct proc *p = curproc; 97446492Swpaul 97546492Swpaul sc = ifp->if_softc; 97667092Swpaul WI_LOCK(sc); 97746492Swpaul ifr = (struct ifreq *)data; 97846492Swpaul 97961818Sroberto if (sc->wi_gone) { 98061818Sroberto error = ENODEV; 98161818Sroberto goto out; 98261818Sroberto } 98346492Swpaul 98446492Swpaul switch(command) { 98546492Swpaul case SIOCSIFADDR: 98646492Swpaul case SIOCGIFADDR: 98746492Swpaul case SIOCSIFMTU: 98846492Swpaul error = ether_ioctl(ifp, command, data); 98946492Swpaul break; 99046492Swpaul case SIOCSIFFLAGS: 99146492Swpaul if (ifp->if_flags & IFF_UP) { 99246492Swpaul if (ifp->if_flags & IFF_RUNNING && 99346492Swpaul ifp->if_flags & IFF_PROMISC && 99446492Swpaul !(sc->wi_if_flags & IFF_PROMISC)) { 99546492Swpaul WI_SETVAL(WI_RID_PROMISC, 1); 99646492Swpaul } else if (ifp->if_flags & IFF_RUNNING && 99746492Swpaul !(ifp->if_flags & IFF_PROMISC) && 99846492Swpaul sc->wi_if_flags & IFF_PROMISC) { 99946492Swpaul WI_SETVAL(WI_RID_PROMISC, 0); 100046492Swpaul } else 100146492Swpaul wi_init(sc); 100246492Swpaul } else { 100346492Swpaul if (ifp->if_flags & IFF_RUNNING) { 100446492Swpaul wi_stop(sc); 100546492Swpaul } 100646492Swpaul } 100746492Swpaul sc->wi_if_flags = ifp->if_flags; 100846492Swpaul error = 0; 100946492Swpaul break; 101046492Swpaul case SIOCADDMULTI: 101146492Swpaul case SIOCDELMULTI: 101246492Swpaul wi_setmulti(sc); 101346492Swpaul error = 0; 101446492Swpaul break; 101546492Swpaul case SIOCGWAVELAN: 101646492Swpaul error = copyin(ifr->ifr_data, &wreq, sizeof(wreq)); 101746492Swpaul if (error) 101846492Swpaul break; 101965581Swpaul /* Don't show WEP keys to non-root users. */ 102065581Swpaul if (wreq.wi_type == WI_RID_DEFLT_CRYPT_KEYS && suser(p)) 102165581Swpaul break; 102246492Swpaul if (wreq.wi_type == WI_RID_IFACE_STATS) { 102346492Swpaul bcopy((char *)&sc->wi_stats, (char *)&wreq.wi_val, 102446492Swpaul sizeof(sc->wi_stats)); 102546492Swpaul wreq.wi_len = (sizeof(sc->wi_stats) / 2) + 1; 102656965Swpaul } else if (wreq.wi_type == WI_RID_DEFLT_CRYPT_KEYS) { 102756965Swpaul bcopy((char *)&sc->wi_keys, (char *)&wreq, 102856965Swpaul sizeof(struct wi_ltv_keys)); 102953702Swpaul } 103053702Swpaul#ifdef WICACHE 103153702Swpaul else if (wreq.wi_type == WI_RID_ZERO_CACHE) { 103253702Swpaul sc->wi_sigitems = sc->wi_nextitem = 0; 103353702Swpaul } else if (wreq.wi_type == WI_RID_READ_CACHE) { 103453702Swpaul char *pt = (char *)&wreq.wi_val; 103553702Swpaul bcopy((char *)&sc->wi_sigitems, 103653702Swpaul (char *)pt, sizeof(int)); 103753702Swpaul pt += (sizeof (int)); 103853702Swpaul wreq.wi_len = sizeof(int) / 2; 103953702Swpaul bcopy((char *)&sc->wi_sigcache, (char *)pt, 104053702Swpaul sizeof(struct wi_sigcache) * sc->wi_sigitems); 104153702Swpaul wreq.wi_len += ((sizeof(struct wi_sigcache) * 104253702Swpaul sc->wi_sigitems) / 2) + 1; 104353702Swpaul } 104453702Swpaul#endif 104553702Swpaul else { 104646492Swpaul if (wi_read_record(sc, (struct wi_ltv_gen *)&wreq)) { 104746492Swpaul error = EINVAL; 104846492Swpaul break; 104946492Swpaul } 105046492Swpaul } 105146492Swpaul error = copyout(&wreq, ifr->ifr_data, sizeof(wreq)); 105246492Swpaul break; 105346492Swpaul case SIOCSWAVELAN: 105461818Sroberto if ((error = suser(p))) 105561818Sroberto goto out; 105646492Swpaul error = copyin(ifr->ifr_data, &wreq, sizeof(wreq)); 105746492Swpaul if (error) 105846492Swpaul break; 105946492Swpaul if (wreq.wi_type == WI_RID_IFACE_STATS) { 106046492Swpaul error = EINVAL; 106146492Swpaul break; 106246492Swpaul } else if (wreq.wi_type == WI_RID_MGMT_XMIT) { 106346492Swpaul error = wi_mgmt_xmit(sc, (caddr_t)&wreq.wi_val, 106446492Swpaul wreq.wi_len); 106546492Swpaul } else { 106646492Swpaul error = wi_write_record(sc, (struct wi_ltv_gen *)&wreq); 106746492Swpaul if (!error) 106846492Swpaul wi_setdef(sc, &wreq); 106946492Swpaul } 107046492Swpaul break; 107146492Swpaul default: 107246492Swpaul error = EINVAL; 107346492Swpaul break; 107446492Swpaul } 107561818Srobertoout: 107667092Swpaul WI_UNLOCK(sc); 107746492Swpaul 107846492Swpaul return(error); 107946492Swpaul} 108046492Swpaul 108146492Swpaulstatic void wi_init(xsc) 108246492Swpaul void *xsc; 108346492Swpaul{ 108446492Swpaul struct wi_softc *sc = xsc; 108546492Swpaul struct ifnet *ifp = &sc->arpcom.ac_if; 108646492Swpaul struct wi_ltv_macaddr mac; 108746492Swpaul int id = 0; 108846492Swpaul 108967092Swpaul WI_LOCK(sc); 109067092Swpaul 109167092Swpaul if (sc->wi_gone) { 109267092Swpaul WI_UNLOCK(sc); 109346492Swpaul return; 109467092Swpaul } 109546492Swpaul 109646492Swpaul if (ifp->if_flags & IFF_RUNNING) 109746492Swpaul wi_stop(sc); 109846492Swpaul 109946492Swpaul wi_reset(sc); 110046492Swpaul 110146492Swpaul /* Program max data length. */ 110246492Swpaul WI_SETVAL(WI_RID_MAX_DATALEN, sc->wi_max_data_len); 110346492Swpaul 110447401Swpaul /* Enable/disable IBSS creation. */ 110546492Swpaul WI_SETVAL(WI_RID_CREATE_IBSS, sc->wi_create_ibss); 110646492Swpaul 110746492Swpaul /* Set the port type. */ 110846492Swpaul WI_SETVAL(WI_RID_PORTTYPE, sc->wi_ptype); 110946492Swpaul 111046492Swpaul /* Program the RTS/CTS threshold. */ 111146492Swpaul WI_SETVAL(WI_RID_RTS_THRESH, sc->wi_rts_thresh); 111246492Swpaul 111346492Swpaul /* Program the TX rate */ 111446492Swpaul WI_SETVAL(WI_RID_TX_RATE, sc->wi_tx_rate); 111546492Swpaul 111646492Swpaul /* Access point density */ 111746492Swpaul WI_SETVAL(WI_RID_SYSTEM_SCALE, sc->wi_ap_density); 111846492Swpaul 111946611Swpaul /* Power Management Enabled */ 112046611Swpaul WI_SETVAL(WI_RID_PM_ENABLED, sc->wi_pm_enabled); 112146611Swpaul 112246611Swpaul /* Power Managment Max Sleep */ 112346611Swpaul WI_SETVAL(WI_RID_MAX_SLEEP, sc->wi_max_sleep); 112446611Swpaul 112546492Swpaul /* Specify the IBSS name */ 112646492Swpaul WI_SETSTR(WI_RID_OWN_SSID, sc->wi_ibss_name); 112746492Swpaul 112846492Swpaul /* Specify the network name */ 112946492Swpaul WI_SETSTR(WI_RID_DESIRED_SSID, sc->wi_net_name); 113046492Swpaul 113146563Swpaul /* Specify the frequency to use */ 113246563Swpaul WI_SETVAL(WI_RID_OWN_CHNL, sc->wi_channel); 113346563Swpaul 113446492Swpaul /* Program the nodename. */ 113546492Swpaul WI_SETSTR(WI_RID_NODENAME, sc->wi_node_name); 113646492Swpaul 113746492Swpaul /* Set our MAC address. */ 113846492Swpaul mac.wi_len = 4; 113946492Swpaul mac.wi_type = WI_RID_MAC_NODE; 114046492Swpaul bcopy((char *)&sc->arpcom.ac_enaddr, 114146492Swpaul (char *)&mac.wi_mac_addr, ETHER_ADDR_LEN); 114246492Swpaul wi_write_record(sc, (struct wi_ltv_gen *)&mac); 114346492Swpaul 114456965Swpaul /* Configure WEP. */ 114556965Swpaul if (sc->wi_has_wep) { 114656965Swpaul WI_SETVAL(WI_RID_ENCRYPTION, sc->wi_use_wep); 114756965Swpaul WI_SETVAL(WI_RID_TX_CRYPT_KEY, sc->wi_tx_key); 114856965Swpaul sc->wi_keys.wi_len = (sizeof(struct wi_ltv_keys) / 2) + 1; 114956965Swpaul sc->wi_keys.wi_type = WI_RID_DEFLT_CRYPT_KEYS; 115056965Swpaul wi_write_record(sc, (struct wi_ltv_gen *)&sc->wi_keys); 115156965Swpaul } 115256965Swpaul 115346492Swpaul /* Initialize promisc mode. */ 115446492Swpaul if (ifp->if_flags & IFF_PROMISC) { 115546492Swpaul WI_SETVAL(WI_RID_PROMISC, 1); 115646492Swpaul } else { 115746492Swpaul WI_SETVAL(WI_RID_PROMISC, 0); 115846492Swpaul } 115946492Swpaul 116046492Swpaul /* Set multicast filter. */ 116146492Swpaul wi_setmulti(sc); 116246492Swpaul 116346492Swpaul /* Enable desired port */ 116446492Swpaul wi_cmd(sc, WI_CMD_ENABLE|sc->wi_portnum, 0); 116546492Swpaul 116646492Swpaul if (wi_alloc_nicmem(sc, 1518 + sizeof(struct wi_frame) + 8, &id)) 116753702Swpaul device_printf(sc->dev, "tx buffer allocation failed\n"); 116846492Swpaul sc->wi_tx_data_id = id; 116946492Swpaul 117046492Swpaul if (wi_alloc_nicmem(sc, 1518 + sizeof(struct wi_frame) + 8, &id)) 117153702Swpaul device_printf(sc->dev, "mgmt. buffer allocation failed\n"); 117246492Swpaul sc->wi_tx_mgmt_id = id; 117346492Swpaul 117446492Swpaul /* enable interrupts */ 117546492Swpaul CSR_WRITE_2(sc, WI_INT_EN, WI_INTRS); 117646492Swpaul 117746492Swpaul ifp->if_flags |= IFF_RUNNING; 117846492Swpaul ifp->if_flags &= ~IFF_OACTIVE; 117946492Swpaul 118046492Swpaul sc->wi_stat_ch = timeout(wi_inquire, sc, hz * 60); 118167092Swpaul WI_UNLOCK(sc); 118246492Swpaul 118346492Swpaul return; 118446492Swpaul} 118546492Swpaul 118646492Swpaulstatic void wi_start(ifp) 118746492Swpaul struct ifnet *ifp; 118846492Swpaul{ 118946492Swpaul struct wi_softc *sc; 119046492Swpaul struct mbuf *m0; 119146492Swpaul struct wi_frame tx_frame; 119246492Swpaul struct ether_header *eh; 119346492Swpaul int id; 119446492Swpaul 119546492Swpaul sc = ifp->if_softc; 119667092Swpaul WI_LOCK(sc); 119746492Swpaul 119867092Swpaul if (sc->wi_gone) { 119967092Swpaul WI_UNLOCK(sc); 120046492Swpaul return; 120167092Swpaul } 120246492Swpaul 120367092Swpaul if (ifp->if_flags & IFF_OACTIVE) { 120467092Swpaul WI_UNLOCK(sc); 120546492Swpaul return; 120667092Swpaul } 120746492Swpaul 120846492Swpaul IF_DEQUEUE(&ifp->if_snd, m0); 120967092Swpaul if (m0 == NULL) { 121067092Swpaul WI_UNLOCK(sc); 121146492Swpaul return; 121267092Swpaul } 121346492Swpaul 121446492Swpaul bzero((char *)&tx_frame, sizeof(tx_frame)); 121546492Swpaul id = sc->wi_tx_data_id; 121646492Swpaul eh = mtod(m0, struct ether_header *); 121746492Swpaul 121846492Swpaul /* 121947401Swpaul * Use RFC1042 encoding for IP and ARP datagrams, 122046492Swpaul * 802.3 for anything else. 122146492Swpaul */ 122255831Swpaul if (ntohs(eh->ether_type) > 1518) { 122346492Swpaul bcopy((char *)&eh->ether_dhost, 122446492Swpaul (char *)&tx_frame.wi_addr1, ETHER_ADDR_LEN); 122546492Swpaul bcopy((char *)&eh->ether_shost, 122646492Swpaul (char *)&tx_frame.wi_addr2, ETHER_ADDR_LEN); 122746492Swpaul bcopy((char *)&eh->ether_dhost, 122846492Swpaul (char *)&tx_frame.wi_dst_addr, ETHER_ADDR_LEN); 122946492Swpaul bcopy((char *)&eh->ether_shost, 123046492Swpaul (char *)&tx_frame.wi_src_addr, ETHER_ADDR_LEN); 123146492Swpaul 123246492Swpaul tx_frame.wi_dat_len = m0->m_pkthdr.len - WI_SNAPHDR_LEN; 123346492Swpaul tx_frame.wi_frame_ctl = WI_FTYPE_DATA; 123446492Swpaul tx_frame.wi_dat[0] = htons(WI_SNAP_WORD0); 123546492Swpaul tx_frame.wi_dat[1] = htons(WI_SNAP_WORD1); 123646492Swpaul tx_frame.wi_len = htons(m0->m_pkthdr.len - WI_SNAPHDR_LEN); 123746492Swpaul tx_frame.wi_type = eh->ether_type; 123846492Swpaul 123946492Swpaul m_copydata(m0, sizeof(struct ether_header), 124046492Swpaul m0->m_pkthdr.len - sizeof(struct ether_header), 124146492Swpaul (caddr_t)&sc->wi_txbuf); 124246492Swpaul 124346492Swpaul wi_write_data(sc, id, 0, (caddr_t)&tx_frame, 124446492Swpaul sizeof(struct wi_frame)); 124546492Swpaul wi_write_data(sc, id, WI_802_11_OFFSET, (caddr_t)&sc->wi_txbuf, 124646492Swpaul (m0->m_pkthdr.len - sizeof(struct ether_header)) + 2); 124746492Swpaul } else { 124846492Swpaul tx_frame.wi_dat_len = m0->m_pkthdr.len; 124946492Swpaul 125055831Swpaul eh->ether_type = htons(m0->m_pkthdr.len - WI_SNAPHDR_LEN); 125146492Swpaul m_copydata(m0, 0, m0->m_pkthdr.len, (caddr_t)&sc->wi_txbuf); 125246492Swpaul 125346492Swpaul wi_write_data(sc, id, 0, (caddr_t)&tx_frame, 125446492Swpaul sizeof(struct wi_frame)); 125546492Swpaul wi_write_data(sc, id, WI_802_3_OFFSET, (caddr_t)&sc->wi_txbuf, 125646492Swpaul m0->m_pkthdr.len + 2); 125746492Swpaul } 125846492Swpaul 125946492Swpaul /* 126046492Swpaul * If there's a BPF listner, bounce a copy of 126146492Swpaul * this frame to him. 126246492Swpaul */ 126346492Swpaul if (ifp->if_bpf) 126446492Swpaul bpf_mtap(ifp, m0); 126546492Swpaul 126646492Swpaul m_freem(m0); 126746492Swpaul 126846492Swpaul if (wi_cmd(sc, WI_CMD_TX|WI_RECLAIM, id)) 126953702Swpaul device_printf(sc->dev, "xmit failed\n"); 127046492Swpaul 127146492Swpaul ifp->if_flags |= IFF_OACTIVE; 127246492Swpaul 127346492Swpaul /* 127446492Swpaul * Set a timeout in case the chip goes out to lunch. 127546492Swpaul */ 127646492Swpaul ifp->if_timer = 5; 127746492Swpaul 127867092Swpaul WI_UNLOCK(sc); 127946492Swpaul return; 128046492Swpaul} 128146492Swpaul 128246492Swpaulstatic int wi_mgmt_xmit(sc, data, len) 128346492Swpaul struct wi_softc *sc; 128446492Swpaul caddr_t data; 128546492Swpaul int len; 128646492Swpaul{ 128746492Swpaul struct wi_frame tx_frame; 128846492Swpaul int id; 128946492Swpaul struct wi_80211_hdr *hdr; 129046492Swpaul caddr_t dptr; 129146492Swpaul 129246492Swpaul if (sc->wi_gone) 129346492Swpaul return(ENODEV); 129446492Swpaul 129546492Swpaul hdr = (struct wi_80211_hdr *)data; 129646492Swpaul dptr = data + sizeof(struct wi_80211_hdr); 129746492Swpaul 129846492Swpaul bzero((char *)&tx_frame, sizeof(tx_frame)); 129946492Swpaul id = sc->wi_tx_mgmt_id; 130046492Swpaul 130146492Swpaul bcopy((char *)hdr, (char *)&tx_frame.wi_frame_ctl, 130246492Swpaul sizeof(struct wi_80211_hdr)); 130346492Swpaul 130446492Swpaul tx_frame.wi_dat_len = len - WI_SNAPHDR_LEN; 130546492Swpaul tx_frame.wi_len = htons(len - WI_SNAPHDR_LEN); 130646492Swpaul 130746492Swpaul wi_write_data(sc, id, 0, (caddr_t)&tx_frame, sizeof(struct wi_frame)); 130846492Swpaul wi_write_data(sc, id, WI_802_11_OFFSET_RAW, dptr, 130946492Swpaul (len - sizeof(struct wi_80211_hdr)) + 2); 131046492Swpaul 131146492Swpaul if (wi_cmd(sc, WI_CMD_TX|WI_RECLAIM, id)) { 131253702Swpaul device_printf(sc->dev, "xmit failed\n"); 131346492Swpaul return(EIO); 131446492Swpaul } 131546492Swpaul 131646492Swpaul return(0); 131746492Swpaul} 131846492Swpaul 131946492Swpaulstatic void wi_stop(sc) 132046492Swpaul struct wi_softc *sc; 132146492Swpaul{ 132246492Swpaul struct ifnet *ifp; 132346492Swpaul 132467092Swpaul WI_LOCK(sc); 132567092Swpaul 132667092Swpaul if (sc->wi_gone) { 132767092Swpaul WI_UNLOCK(sc); 132846492Swpaul return; 132967092Swpaul } 133046492Swpaul 133146492Swpaul ifp = &sc->arpcom.ac_if; 133246492Swpaul 133346492Swpaul CSR_WRITE_2(sc, WI_INT_EN, 0); 133446492Swpaul wi_cmd(sc, WI_CMD_DISABLE|sc->wi_portnum, 0); 133546492Swpaul 133646492Swpaul untimeout(wi_inquire, sc, sc->wi_stat_ch); 133746492Swpaul 133846492Swpaul ifp->if_flags &= ~(IFF_RUNNING|IFF_OACTIVE); 133946492Swpaul 134067092Swpaul WI_UNLOCK(sc); 134146492Swpaul return; 134246492Swpaul} 134346492Swpaul 134446492Swpaulstatic void wi_watchdog(ifp) 134546492Swpaul struct ifnet *ifp; 134646492Swpaul{ 134746492Swpaul struct wi_softc *sc; 134846492Swpaul 134946492Swpaul sc = ifp->if_softc; 135046492Swpaul 135153702Swpaul device_printf(sc->dev,"device timeout\n"); 135246492Swpaul 135346492Swpaul wi_init(sc); 135446492Swpaul 135546492Swpaul ifp->if_oerrors++; 135646492Swpaul 135746492Swpaul return; 135846492Swpaul} 135946492Swpaul 136053702Swpaulstatic int wi_alloc(dev) 136153702Swpaul device_t dev; 136246492Swpaul{ 136353702Swpaul struct wi_softc *sc = device_get_softc(dev); 136453702Swpaul int rid; 136553702Swpaul 136653702Swpaul rid = 0; 136753702Swpaul sc->iobase = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 136853702Swpaul 0, ~0, 1, RF_ACTIVE); 136953702Swpaul if (!sc->iobase) { 137053702Swpaul device_printf(dev, "No I/O space?!\n"); 137153702Swpaul return (ENXIO); 137253702Swpaul } 137353702Swpaul 137453702Swpaul rid = 0; 137553702Swpaul sc->irq = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 137653702Swpaul 0, ~0, 1, RF_ACTIVE); 137753702Swpaul if (!sc->irq) { 137853702Swpaul device_printf(dev, "No irq?!\n"); 137953702Swpaul return (ENXIO); 138053702Swpaul } 138153702Swpaul 138253702Swpaul sc->dev = dev; 138353702Swpaul sc->wi_unit = device_get_unit(dev); 138453702Swpaul sc->wi_io_addr = rman_get_start(sc->iobase); 138553702Swpaul sc->wi_btag = rman_get_bustag(sc->iobase); 138653702Swpaul sc->wi_bhandle = rman_get_bushandle(sc->iobase); 138753702Swpaul 138853702Swpaul return (0); 138953702Swpaul} 139053702Swpaul 139153702Swpaulstatic void wi_free(dev) 139253702Swpaul device_t dev; 139353702Swpaul{ 139453702Swpaul struct wi_softc *sc = device_get_softc(dev); 139553702Swpaul 139653702Swpaul if (sc->iobase != NULL) 139753702Swpaul bus_release_resource(dev, SYS_RES_IOPORT, 0, sc->iobase); 139853702Swpaul if (sc->irq != NULL) 139953702Swpaul bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq); 140053702Swpaul 140153702Swpaul return; 140253702Swpaul} 140353702Swpaul 140453702Swpaulstatic void wi_shutdown(dev) 140553702Swpaul device_t dev; 140653702Swpaul{ 140746492Swpaul struct wi_softc *sc; 140846492Swpaul 140953702Swpaul sc = device_get_softc(dev); 141046492Swpaul wi_stop(sc); 141146492Swpaul 141246492Swpaul return; 141346492Swpaul} 141453702Swpaul 141553702Swpaul#ifdef WICACHE 141653702Swpaul/* wavelan signal strength cache code. 141753702Swpaul * store signal/noise/quality on per MAC src basis in 141853702Swpaul * a small fixed cache. The cache wraps if > MAX slots 141953702Swpaul * used. The cache may be zeroed out to start over. 142053702Swpaul * Two simple filters exist to reduce computation: 142153702Swpaul * 1. ip only (literally 0x800) which may be used 142253702Swpaul * to ignore some packets. It defaults to ip only. 142353702Swpaul * it could be used to focus on broadcast, non-IP 802.11 beacons. 142453702Swpaul * 2. multicast/broadcast only. This may be used to 142553702Swpaul * ignore unicast packets and only cache signal strength 142653702Swpaul * for multicast/broadcast packets (beacons); e.g., Mobile-IP 142753702Swpaul * beacons and not unicast traffic. 142853702Swpaul * 142953702Swpaul * The cache stores (MAC src(index), IP src (major clue), signal, 143053702Swpaul * quality, noise) 143153702Swpaul * 143253702Swpaul * No apologies for storing IP src here. It's easy and saves much 143353702Swpaul * trouble elsewhere. The cache is assumed to be INET dependent, 143453702Swpaul * although it need not be. 143553702Swpaul */ 143653702Swpaul 143753702Swpaul#ifdef documentation 143853702Swpaul 143953702Swpaulint wi_sigitems; /* number of cached entries */ 144053702Swpaulstruct wi_sigcache wi_sigcache[MAXWICACHE]; /* array of cache entries */ 144153702Swpaulint wi_nextitem; /* index/# of entries */ 144253702Swpaul 144353702Swpaul 144453702Swpaul#endif 144553702Swpaul 144653702Swpaul/* control variables for cache filtering. Basic idea is 144753702Swpaul * to reduce cost (e.g., to only Mobile-IP agent beacons 144853702Swpaul * which are broadcast or multicast). Still you might 144953702Swpaul * want to measure signal strength with unicast ping packets 145053702Swpaul * on a pt. to pt. ant. setup. 145153702Swpaul */ 145253702Swpaul/* set true if you want to limit cache items to broadcast/mcast 145353702Swpaul * only packets (not unicast). Useful for mobile-ip beacons which 145453702Swpaul * are broadcast/multicast at network layer. Default is all packets 145553702Swpaul * so ping/unicast will work say with pt. to pt. antennae setup. 145653702Swpaul */ 145753702Swpaulstatic int wi_cache_mcastonly = 0; 145853702SwpaulSYSCTL_INT(_machdep, OID_AUTO, wi_cache_mcastonly, CTLFLAG_RW, 145953702Swpaul &wi_cache_mcastonly, 0, ""); 146053702Swpaul 146153702Swpaul/* set true if you want to limit cache items to IP packets only 146253702Swpaul*/ 146353702Swpaulstatic int wi_cache_iponly = 1; 146453702SwpaulSYSCTL_INT(_machdep, OID_AUTO, wi_cache_iponly, CTLFLAG_RW, 146553702Swpaul &wi_cache_iponly, 0, ""); 146653702Swpaul 146753702Swpaul/* 146853702Swpaul * Original comments: 146953702Swpaul * ----------------- 147053702Swpaul * wi_cache_store, per rx packet store signal 147153702Swpaul * strength in MAC (src) indexed cache. 147253702Swpaul * 147353702Swpaul * follows linux driver in how signal strength is computed. 147453702Swpaul * In ad hoc mode, we use the rx_quality field. 147553702Swpaul * signal and noise are trimmed to fit in the range from 47..138. 147653702Swpaul * rx_quality field MSB is signal strength. 147753702Swpaul * rx_quality field LSB is noise. 147853702Swpaul * "quality" is (signal - noise) as is log value. 147953702Swpaul * note: quality CAN be negative. 148053702Swpaul * 148153702Swpaul * In BSS mode, we use the RID for communication quality. 148253702Swpaul * TBD: BSS mode is currently untested. 148353702Swpaul * 148453702Swpaul * Bill's comments: 148553702Swpaul * --------------- 148653702Swpaul * Actually, we use the rx_quality field all the time for both "ad-hoc" 148753702Swpaul * and BSS modes. Why? Because reading an RID is really, really expensive: 148853702Swpaul * there's a bunch of PIO operations that have to be done to read a record 148953702Swpaul * from the NIC, and reading the comms quality RID each time a packet is 149053702Swpaul * received can really hurt performance. We don't have to do this anyway: 149153702Swpaul * the comms quality field only reflects the values in the rx_quality field 149253702Swpaul * anyway. The comms quality RID is only meaningful in infrastructure mode, 149353702Swpaul * but the values it contains are updated based on the rx_quality from 149453702Swpaul * frames received from the access point. 149553702Swpaul * 149653702Swpaul * Also, according to Lucent, the signal strength and noise level values 149753702Swpaul * can be converted to dBms by subtracting 149, so I've modified the code 149853702Swpaul * to do that instead of the scaling it did originally. 149953702Swpaul */ 150053702Swpaulstatic 150153702Swpaulvoid wi_cache_store (struct wi_softc *sc, struct ether_header *eh, 150253702Swpaul struct mbuf *m, unsigned short rx_quality) 150353702Swpaul{ 150453702Swpaul struct ip *ip = 0; 150553702Swpaul int i; 150653702Swpaul static int cache_slot = 0; /* use this cache entry */ 150753702Swpaul static int wrapindex = 0; /* next "free" cache entry */ 150853702Swpaul int sig, noise; 150953702Swpaul int sawip=0; 151053702Swpaul 151153702Swpaul /* filters: 151253702Swpaul * 1. ip only 151353702Swpaul * 2. configurable filter to throw out unicast packets, 151453702Swpaul * keep multicast only. 151553702Swpaul */ 151653702Swpaul 151753702Swpaul if ((ntohs(eh->ether_type) == 0x800)) { 151853702Swpaul sawip = 1; 151953702Swpaul } 152053702Swpaul 152153702Swpaul /* filter for ip packets only 152253702Swpaul */ 152353702Swpaul if (wi_cache_iponly && !sawip) { 152453702Swpaul return; 152553702Swpaul } 152653702Swpaul 152753702Swpaul /* filter for broadcast/multicast only 152853702Swpaul */ 152953702Swpaul if (wi_cache_mcastonly && ((eh->ether_dhost[0] & 1) == 0)) { 153053702Swpaul return; 153153702Swpaul } 153253702Swpaul 153353702Swpaul#ifdef SIGDEBUG 153453702Swpaul printf("wi%d: q value %x (MSB=0x%x, LSB=0x%x) \n", sc->wi_unit, 153553702Swpaul rx_quality & 0xffff, rx_quality >> 8, rx_quality & 0xff); 153653702Swpaul#endif 153753702Swpaul 153853702Swpaul /* find the ip header. we want to store the ip_src 153953702Swpaul * address. 154053702Swpaul */ 154153702Swpaul if (sawip) { 154253702Swpaul ip = mtod(m, struct ip *); 154353702Swpaul } 154453702Swpaul 154553702Swpaul /* do a linear search for a matching MAC address 154653702Swpaul * in the cache table 154753702Swpaul * . MAC address is 6 bytes, 154853702Swpaul * . var w_nextitem holds total number of entries already cached 154953702Swpaul */ 155053702Swpaul for(i = 0; i < sc->wi_nextitem; i++) { 155153702Swpaul if (! bcmp(eh->ether_shost , sc->wi_sigcache[i].macsrc, 6 )) { 155253702Swpaul /* Match!, 155353702Swpaul * so we already have this entry, 155453702Swpaul * update the data 155553702Swpaul */ 155653702Swpaul break; 155753702Swpaul } 155853702Swpaul } 155953702Swpaul 156053702Swpaul /* did we find a matching mac address? 156153702Swpaul * if yes, then overwrite a previously existing cache entry 156253702Swpaul */ 156353702Swpaul if (i < sc->wi_nextitem ) { 156453702Swpaul cache_slot = i; 156553702Swpaul } 156653702Swpaul /* else, have a new address entry,so 156753702Swpaul * add this new entry, 156853702Swpaul * if table full, then we need to replace LRU entry 156953702Swpaul */ 157053702Swpaul else { 157153702Swpaul 157253702Swpaul /* check for space in cache table 157353702Swpaul * note: wi_nextitem also holds number of entries 157453702Swpaul * added in the cache table 157553702Swpaul */ 157653702Swpaul if ( sc->wi_nextitem < MAXWICACHE ) { 157753702Swpaul cache_slot = sc->wi_nextitem; 157853702Swpaul sc->wi_nextitem++; 157953702Swpaul sc->wi_sigitems = sc->wi_nextitem; 158053702Swpaul } 158153702Swpaul /* no space found, so simply wrap with wrap index 158253702Swpaul * and "zap" the next entry 158353702Swpaul */ 158453702Swpaul else { 158553702Swpaul if (wrapindex == MAXWICACHE) { 158653702Swpaul wrapindex = 0; 158753702Swpaul } 158853702Swpaul cache_slot = wrapindex++; 158953702Swpaul } 159053702Swpaul } 159153702Swpaul 159253702Swpaul /* invariant: cache_slot now points at some slot 159353702Swpaul * in cache. 159453702Swpaul */ 159553702Swpaul if (cache_slot < 0 || cache_slot >= MAXWICACHE) { 159653702Swpaul log(LOG_ERR, "wi_cache_store, bad index: %d of " 159753702Swpaul "[0..%d], gross cache error\n", 159853702Swpaul cache_slot, MAXWICACHE); 159953702Swpaul return; 160053702Swpaul } 160153702Swpaul 160253702Swpaul /* store items in cache 160353702Swpaul * .ip source address 160453702Swpaul * .mac src 160553702Swpaul * .signal, etc. 160653702Swpaul */ 160753702Swpaul if (sawip) { 160853702Swpaul sc->wi_sigcache[cache_slot].ipsrc = ip->ip_src.s_addr; 160953702Swpaul } 161053702Swpaul bcopy( eh->ether_shost, sc->wi_sigcache[cache_slot].macsrc, 6); 161153702Swpaul 161253702Swpaul sig = (rx_quality >> 8) & 0xFF; 161353702Swpaul noise = rx_quality & 0xFF; 161453702Swpaul sc->wi_sigcache[cache_slot].signal = sig - 149; 161553702Swpaul sc->wi_sigcache[cache_slot].noise = noise - 149; 161653702Swpaul sc->wi_sigcache[cache_slot].quality = sig - noise; 161753702Swpaul 161853702Swpaul return; 161953702Swpaul} 162053702Swpaul#endif 1621