if_wi.c revision 70173
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 70173 2000-12-18 23:49:56Z jhb $ 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> 7646492Swpaul#include <sys/socket.h> 7753702Swpaul#include <sys/module.h> 7853702Swpaul#include <sys/bus.h> 7953702Swpaul#include <sys/syslog.h> 8053702Swpaul#include <sys/sysctl.h> 8146492Swpaul 8253702Swpaul#include <machine/bus.h> 8353702Swpaul#include <machine/resource.h> 8453702Swpaul#include <machine/md_var.h> 8553702Swpaul#include <machine/bus_pio.h> 8653702Swpaul#include <sys/rman.h> 8753702Swpaul 8846492Swpaul#include <net/if.h> 8946492Swpaul#include <net/if_arp.h> 9046492Swpaul#include <net/ethernet.h> 9146492Swpaul#include <net/if_dl.h> 9246492Swpaul#include <net/if_media.h> 9346492Swpaul#include <net/if_types.h> 9446492Swpaul 9546492Swpaul#include <netinet/in.h> 9646492Swpaul#include <netinet/in_systm.h> 9746492Swpaul#include <netinet/in_var.h> 9846492Swpaul#include <netinet/ip.h> 9946492Swpaul#include <netinet/if_ether.h> 10046492Swpaul 10146492Swpaul#include <net/bpf.h> 10246492Swpaul 10353702Swpaul#include <machine/if_wavelan_ieee.h> 10446492Swpaul#include <i386/isa/if_wireg.h> 10546492Swpaul 10646492Swpaul#if !defined(lint) 10746492Swpaulstatic const char rcsid[] = 10850477Speter "$FreeBSD: head/sys/dev/wi/if_wi.c 70173 2000-12-18 23:49:56Z jhb $"; 10946492Swpaul#endif 11046492Swpaul 11146492Swpaul#ifdef foo 11247401Swpaulstatic u_int8_t wi_mcast_addr[6] = { 0x01, 0x60, 0x1D, 0x00, 0x01, 0x00 }; 11346492Swpaul#endif 11446492Swpaul 11553702Swpaulstatic void wi_intr __P((void *)); 11646492Swpaulstatic void wi_reset __P((struct wi_softc *)); 11746492Swpaulstatic int wi_ioctl __P((struct ifnet *, u_long, caddr_t)); 11846492Swpaulstatic void wi_init __P((void *)); 11946492Swpaulstatic void wi_start __P((struct ifnet *)); 12046492Swpaulstatic void wi_stop __P((struct wi_softc *)); 12146492Swpaulstatic void wi_watchdog __P((struct ifnet *)); 12246492Swpaulstatic void wi_rxeof __P((struct wi_softc *)); 12346492Swpaulstatic void wi_txeof __P((struct wi_softc *, int)); 12446492Swpaulstatic void wi_update_stats __P((struct wi_softc *)); 12546492Swpaulstatic void wi_setmulti __P((struct wi_softc *)); 12646492Swpaul 12746492Swpaulstatic int wi_cmd __P((struct wi_softc *, int, int)); 12846492Swpaulstatic int wi_read_record __P((struct wi_softc *, struct wi_ltv_gen *)); 12946492Swpaulstatic int wi_write_record __P((struct wi_softc *, struct wi_ltv_gen *)); 13046492Swpaulstatic int wi_read_data __P((struct wi_softc *, int, 13146492Swpaul int, caddr_t, int)); 13246492Swpaulstatic int wi_write_data __P((struct wi_softc *, int, 13346492Swpaul int, caddr_t, int)); 13446492Swpaulstatic int wi_seek __P((struct wi_softc *, int, int, int)); 13546492Swpaulstatic int wi_alloc_nicmem __P((struct wi_softc *, int, int *)); 13646492Swpaulstatic void wi_inquire __P((void *)); 13746492Swpaulstatic void wi_setdef __P((struct wi_softc *, struct wi_req *)); 13846492Swpaulstatic int wi_mgmt_xmit __P((struct wi_softc *, caddr_t, int)); 13946492Swpaul 14053702Swpaul#ifdef WICACHE 14153702Swpaulstatic 14253702Swpaulvoid wi_cache_store __P((struct wi_softc *, struct ether_header *, 14353702Swpaul struct mbuf *, unsigned short)); 14453702Swpaul#endif 14553702Swpaul 14653702Swpaulstatic int wi_pccard_probe __P((device_t)); 14753702Swpaulstatic int wi_pccard_attach __P((device_t)); 14853702Swpaulstatic int wi_pccard_detach __P((device_t)); 14953702Swpaulstatic void wi_shutdown __P((device_t)); 15053702Swpaul 15153702Swpaulstatic int wi_alloc __P((device_t)); 15253702Swpaulstatic void wi_free __P((device_t)); 15353702Swpaul 15453702Swpaulstatic device_method_t wi_pccard_methods[] = { 15553702Swpaul /* Device interface */ 15653702Swpaul DEVMETHOD(device_probe, wi_pccard_probe), 15753702Swpaul DEVMETHOD(device_attach, wi_pccard_attach), 15853702Swpaul DEVMETHOD(device_detach, wi_pccard_detach), 15953702Swpaul DEVMETHOD(device_shutdown, wi_shutdown), 16053702Swpaul 16153702Swpaul { 0, 0 } 16246492Swpaul}; 16346492Swpaul 16453702Swpaulstatic driver_t wi_pccard_driver = { 16546492Swpaul "wi", 16653702Swpaul wi_pccard_methods, 16753702Swpaul sizeof(struct wi_softc) 16846492Swpaul}; 16946492Swpaul 17053702Swpaulstatic devclass_t wi_pccard_devclass; 17146492Swpaul 17253702SwpaulDRIVER_MODULE(if_wi, pccard, wi_pccard_driver, wi_pccard_devclass, 0, 0); 17353702Swpaul 17453702Swpaulstatic int wi_pccard_probe(dev) 17553702Swpaul device_t dev; 17646492Swpaul{ 17753702Swpaul struct wi_softc *sc; 17853702Swpaul int error; 17946492Swpaul 18053702Swpaul sc = device_get_softc(dev); 18146492Swpaul sc->wi_gone = 0; 18246492Swpaul 18353702Swpaul error = wi_alloc(dev); 18453702Swpaul if (error) 18553702Swpaul return (error); 18653702Swpaul 18753702Swpaul device_set_desc(dev, "WaveLAN/IEEE 802.11"); 18853702Swpaul wi_free(dev); 18953702Swpaul 19046492Swpaul /* Make sure interrupts are disabled. */ 19146492Swpaul CSR_WRITE_2(sc, WI_INT_EN, 0); 19246492Swpaul CSR_WRITE_2(sc, WI_EVENT_ACK, 0xFFFF); 19346492Swpaul 19453702Swpaul return (0); 19546492Swpaul} 19646492Swpaul 19753702Swpaulstatic int wi_pccard_detach(dev) 19853702Swpaul device_t dev; 19946492Swpaul{ 20046492Swpaul struct wi_softc *sc; 20146492Swpaul struct ifnet *ifp; 20246492Swpaul 20353702Swpaul sc = device_get_softc(dev); 20467092Swpaul WI_LOCK(sc); 20546492Swpaul ifp = &sc->arpcom.ac_if; 20646492Swpaul 20746492Swpaul if (sc->wi_gone) { 20853702Swpaul device_printf(dev, "already unloaded\n"); 20967092Swpaul WI_UNLOCK(sc); 21053702Swpaul return(ENODEV); 21146492Swpaul } 21246492Swpaul 21353702Swpaul wi_stop(sc); 21458274Srwatson 21563090Sarchie ether_ifdetach(ifp, ETHER_BPF_SUPPORTED); 21654277Swpaul bus_teardown_intr(dev, sc->irq, sc->wi_intrhand); 21753702Swpaul wi_free(dev); 21846492Swpaul sc->wi_gone = 1; 21946492Swpaul 22053702Swpaul device_printf(dev, "unload\n"); 22167092Swpaul WI_UNLOCK(sc); 22267092Swpaul mtx_destroy(&sc->wi_mtx); 22346492Swpaul 22446492Swpaul return(0); 22546492Swpaul} 22646492Swpaul 22753702Swpaulstatic int wi_pccard_attach(device_t dev) 22846492Swpaul{ 22946492Swpaul struct wi_softc *sc; 23046492Swpaul struct wi_ltv_macaddr mac; 23146563Swpaul struct wi_ltv_gen gen; 23246492Swpaul struct ifnet *ifp; 23353702Swpaul int error; 23470073Swpaul u_int32_t flags; 23546492Swpaul 23653702Swpaul sc = device_get_softc(dev); 23746492Swpaul ifp = &sc->arpcom.ac_if; 23846492Swpaul 23970073Swpaul /* 24070073Swpaul * XXX: quick hack to support Prism II chip. 24170073Swpaul * Currently, we need to set a flags in pccard.conf to specify 24270073Swpaul * which type chip is used. 24370073Swpaul * 24470073Swpaul * We need to replace this code in a future. 24570073Swpaul * It is better to use CIS than using a flag. 24670073Swpaul */ 24770073Swpaul flags = device_get_flags(dev); 24870073Swpaul#define WI_FLAGS_PRISM2 0x10000 24970073Swpaul if (flags & WI_FLAGS_PRISM2) { 25070073Swpaul sc->wi_prism2 = 1; 25170073Swpaul if (bootverbose) { 25270073Swpaul device_printf(dev, "found PrismII chip\n"); 25370073Swpaul } 25470073Swpaul } 25570073Swpaul else { 25670073Swpaul sc->wi_prism2 = 0; 25770073Swpaul if (bootverbose) { 25870073Swpaul device_printf(dev, "found Lucent chip\n"); 25970073Swpaul } 26070073Swpaul } 26170073Swpaul 26253702Swpaul error = wi_alloc(dev); 26353702Swpaul if (error) { 26453702Swpaul device_printf(dev, "wi_alloc() failed! (%d)\n", error); 26553702Swpaul return (error); 26653702Swpaul } 26753702Swpaul 26853702Swpaul error = bus_setup_intr(dev, sc->irq, INTR_TYPE_NET, 26953702Swpaul wi_intr, sc, &sc->wi_intrhand); 27053702Swpaul 27153702Swpaul if (error) { 27253702Swpaul device_printf(dev, "bus_setup_intr() failed! (%d)\n", error); 27353702Swpaul wi_free(dev); 27453702Swpaul return (error); 27553702Swpaul } 27653702Swpaul 27767092Swpaul mtx_init(&sc->wi_mtx, device_get_nameunit(dev), MTX_DEF); 27867092Swpaul WI_LOCK(sc); 27967092Swpaul 28046492Swpaul /* Reset the NIC. */ 28146492Swpaul wi_reset(sc); 28246492Swpaul 28346492Swpaul /* Read the station address. */ 28446492Swpaul mac.wi_type = WI_RID_MAC_NODE; 28546492Swpaul mac.wi_len = 4; 28646492Swpaul wi_read_record(sc, (struct wi_ltv_gen *)&mac); 28746492Swpaul bcopy((char *)&mac.wi_mac_addr, 28846492Swpaul (char *)&sc->arpcom.ac_enaddr, ETHER_ADDR_LEN); 28946492Swpaul 29053702Swpaul device_printf(dev, "Ethernet address: %6D\n", 29146492Swpaul sc->arpcom.ac_enaddr, ":"); 29246492Swpaul 29346492Swpaul ifp->if_softc = sc; 29446492Swpaul ifp->if_unit = sc->wi_unit; 29546492Swpaul ifp->if_name = "wi"; 29646492Swpaul ifp->if_mtu = ETHERMTU; 29746492Swpaul ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; 29846492Swpaul ifp->if_ioctl = wi_ioctl; 29946492Swpaul ifp->if_output = ether_output; 30046492Swpaul ifp->if_start = wi_start; 30146492Swpaul ifp->if_watchdog = wi_watchdog; 30246492Swpaul ifp->if_init = wi_init; 30346492Swpaul ifp->if_baudrate = 10000000; 30446492Swpaul ifp->if_snd.ifq_maxlen = IFQ_MAXLEN; 30546492Swpaul 30646492Swpaul bzero(sc->wi_node_name, sizeof(sc->wi_node_name)); 30746492Swpaul bcopy(WI_DEFAULT_NODENAME, sc->wi_node_name, 30846492Swpaul sizeof(WI_DEFAULT_NODENAME) - 1); 30946492Swpaul 31046492Swpaul bzero(sc->wi_net_name, sizeof(sc->wi_net_name)); 31146492Swpaul bcopy(WI_DEFAULT_NETNAME, sc->wi_net_name, 31246492Swpaul sizeof(WI_DEFAULT_NETNAME) - 1); 31346492Swpaul 31446492Swpaul bzero(sc->wi_ibss_name, sizeof(sc->wi_ibss_name)); 31546492Swpaul bcopy(WI_DEFAULT_IBSS, sc->wi_ibss_name, 31646492Swpaul sizeof(WI_DEFAULT_IBSS) - 1); 31746492Swpaul 31846492Swpaul sc->wi_portnum = WI_DEFAULT_PORT; 31946492Swpaul sc->wi_ptype = WI_PORTTYPE_ADHOC; 32046492Swpaul sc->wi_ap_density = WI_DEFAULT_AP_DENSITY; 32146492Swpaul sc->wi_rts_thresh = WI_DEFAULT_RTS_THRESH; 32246492Swpaul sc->wi_tx_rate = WI_DEFAULT_TX_RATE; 32346492Swpaul sc->wi_max_data_len = WI_DEFAULT_DATALEN; 32446492Swpaul sc->wi_create_ibss = WI_DEFAULT_CREATE_IBSS; 32546611Swpaul sc->wi_pm_enabled = WI_DEFAULT_PM_ENABLED; 32646611Swpaul sc->wi_max_sleep = WI_DEFAULT_MAX_SLEEP; 32746492Swpaul 32846563Swpaul /* 32946563Swpaul * Read the default channel from the NIC. This may vary 33046563Swpaul * depending on the country where the NIC was purchased, so 33146563Swpaul * we can't hard-code a default and expect it to work for 33246563Swpaul * everyone. 33346563Swpaul */ 33446563Swpaul gen.wi_type = WI_RID_OWN_CHNL; 33546563Swpaul gen.wi_len = 2; 33646563Swpaul wi_read_record(sc, &gen); 33746563Swpaul sc->wi_channel = gen.wi_val; 33846563Swpaul 33956965Swpaul /* 34056965Swpaul * Find out if we support WEP on this card. 34156965Swpaul */ 34256965Swpaul gen.wi_type = WI_RID_WEP_AVAIL; 34356965Swpaul gen.wi_len = 2; 34456965Swpaul wi_read_record(sc, &gen); 34556965Swpaul sc->wi_has_wep = gen.wi_val; 34656965Swpaul 34770073Swpaul if (bootverbose) { 34870073Swpaul device_printf(sc->dev, 34970073Swpaul __FUNCTION__ ":wi_has_wep = %d\n", 35070073Swpaul sc->wi_has_wep); 35170073Swpaul } 35270073Swpaul 35346492Swpaul bzero((char *)&sc->wi_stats, sizeof(sc->wi_stats)); 35446492Swpaul 35546492Swpaul wi_init(sc); 35646492Swpaul wi_stop(sc); 35746492Swpaul 35846492Swpaul /* 35963090Sarchie * Call MI attach routine. 36046492Swpaul */ 36163090Sarchie ether_ifattach(ifp, ETHER_BPF_SUPPORTED); 36253702Swpaul callout_handle_init(&sc->wi_stat_ch); 36367092Swpaul WI_UNLOCK(sc); 36446492Swpaul 36546492Swpaul return(0); 36646492Swpaul} 36746492Swpaul 36846492Swpaulstatic void wi_rxeof(sc) 36946492Swpaul struct wi_softc *sc; 37046492Swpaul{ 37146492Swpaul struct ifnet *ifp; 37246492Swpaul struct ether_header *eh; 37346492Swpaul struct wi_frame rx_frame; 37446492Swpaul struct mbuf *m; 37546492Swpaul int id; 37646492Swpaul 37746492Swpaul ifp = &sc->arpcom.ac_if; 37846492Swpaul 37946492Swpaul id = CSR_READ_2(sc, WI_RX_FID); 38046492Swpaul 38146492Swpaul /* First read in the frame header */ 38246492Swpaul if (wi_read_data(sc, id, 0, (caddr_t)&rx_frame, sizeof(rx_frame))) { 38346492Swpaul ifp->if_ierrors++; 38446492Swpaul return; 38546492Swpaul } 38646492Swpaul 38746492Swpaul if (rx_frame.wi_status & WI_STAT_ERRSTAT) { 38846492Swpaul ifp->if_ierrors++; 38946492Swpaul return; 39046492Swpaul } 39146492Swpaul 39246492Swpaul MGETHDR(m, M_DONTWAIT, MT_DATA); 39346492Swpaul if (m == NULL) { 39446492Swpaul ifp->if_ierrors++; 39546492Swpaul return; 39646492Swpaul } 39746492Swpaul MCLGET(m, M_DONTWAIT); 39846492Swpaul if (!(m->m_flags & M_EXT)) { 39946492Swpaul m_freem(m); 40046492Swpaul ifp->if_ierrors++; 40146492Swpaul return; 40246492Swpaul } 40346492Swpaul 40446492Swpaul eh = mtod(m, struct ether_header *); 40546492Swpaul m->m_pkthdr.rcvif = ifp; 40646492Swpaul 40746492Swpaul if (rx_frame.wi_status == WI_STAT_1042 || 40846492Swpaul rx_frame.wi_status == WI_STAT_TUNNEL || 40946492Swpaul rx_frame.wi_status == WI_STAT_WMP_MSG) { 41048553Swpaul if((rx_frame.wi_dat_len + WI_SNAPHDR_LEN) > MCLBYTES) { 41153702Swpaul device_printf(sc->dev, "oversized packet received " 41253702Swpaul "(wi_dat_len=%d, wi_status=0x%x)\n", 41348553Swpaul rx_frame.wi_dat_len, rx_frame.wi_status); 41448553Swpaul m_freem(m); 41548553Swpaul ifp->if_ierrors++; 41648553Swpaul return; 41748553Swpaul } 41846492Swpaul m->m_pkthdr.len = m->m_len = 41946492Swpaul rx_frame.wi_dat_len + WI_SNAPHDR_LEN; 42046492Swpaul 42146492Swpaul bcopy((char *)&rx_frame.wi_addr1, 42246492Swpaul (char *)&eh->ether_dhost, ETHER_ADDR_LEN); 42359328Swpaul if (sc->wi_ptype == WI_PORTTYPE_ADHOC) { 42459328Swpaul bcopy((char *)&rx_frame.wi_addr2, 42559328Swpaul (char *)&eh->ether_shost, ETHER_ADDR_LEN); 42659328Swpaul } else { 42759328Swpaul bcopy((char *)&rx_frame.wi_addr3, 42859328Swpaul (char *)&eh->ether_shost, ETHER_ADDR_LEN); 42959328Swpaul } 43046492Swpaul bcopy((char *)&rx_frame.wi_type, 43146492Swpaul (char *)&eh->ether_type, sizeof(u_int16_t)); 43246492Swpaul 43346492Swpaul if (wi_read_data(sc, id, WI_802_11_OFFSET, 43446492Swpaul mtod(m, caddr_t) + sizeof(struct ether_header), 43546492Swpaul m->m_len + 2)) { 43646492Swpaul m_freem(m); 43746492Swpaul ifp->if_ierrors++; 43846492Swpaul return; 43946492Swpaul } 44046492Swpaul } else { 44148553Swpaul if((rx_frame.wi_dat_len + 44248553Swpaul sizeof(struct ether_header)) > MCLBYTES) { 44353702Swpaul device_printf(sc->dev, "oversized packet received " 44453702Swpaul "(wi_dat_len=%d, wi_status=0x%x)\n", 44548553Swpaul rx_frame.wi_dat_len, rx_frame.wi_status); 44648553Swpaul m_freem(m); 44748553Swpaul ifp->if_ierrors++; 44848553Swpaul return; 44948553Swpaul } 45046492Swpaul m->m_pkthdr.len = m->m_len = 45146492Swpaul rx_frame.wi_dat_len + sizeof(struct ether_header); 45246492Swpaul 45346492Swpaul if (wi_read_data(sc, id, WI_802_3_OFFSET, 45446492Swpaul mtod(m, caddr_t), m->m_len + 2)) { 45546492Swpaul m_freem(m); 45646492Swpaul ifp->if_ierrors++; 45746492Swpaul return; 45846492Swpaul } 45946492Swpaul } 46046492Swpaul 46146492Swpaul ifp->if_ipackets++; 46246492Swpaul 46346492Swpaul /* Receive packet. */ 46446492Swpaul m_adj(m, sizeof(struct ether_header)); 46553702Swpaul#ifdef WICACHE 46653702Swpaul wi_cache_store(sc, eh, m, rx_frame.wi_q_info); 46753702Swpaul#endif 46846492Swpaul ether_input(ifp, eh, m); 46946492Swpaul} 47046492Swpaul 47146492Swpaulstatic void wi_txeof(sc, status) 47246492Swpaul struct wi_softc *sc; 47346492Swpaul int status; 47446492Swpaul{ 47546492Swpaul struct ifnet *ifp; 47646492Swpaul 47746492Swpaul ifp = &sc->arpcom.ac_if; 47846492Swpaul 47946492Swpaul ifp->if_timer = 0; 48046492Swpaul ifp->if_flags &= ~IFF_OACTIVE; 48146492Swpaul 48246492Swpaul if (status & WI_EV_TX_EXC) 48346492Swpaul ifp->if_oerrors++; 48446492Swpaul else 48546492Swpaul ifp->if_opackets++; 48646492Swpaul 48746492Swpaul return; 48846492Swpaul} 48946492Swpaul 49046492Swpaulvoid wi_inquire(xsc) 49146492Swpaul void *xsc; 49246492Swpaul{ 49346492Swpaul struct wi_softc *sc; 49446492Swpaul struct ifnet *ifp; 49546492Swpaul 49646492Swpaul sc = xsc; 49746492Swpaul ifp = &sc->arpcom.ac_if; 49846492Swpaul 49946492Swpaul sc->wi_stat_ch = timeout(wi_inquire, sc, hz * 60); 50046492Swpaul 50146492Swpaul /* Don't do this while we're transmitting */ 50246492Swpaul if (ifp->if_flags & IFF_OACTIVE) 50346492Swpaul return; 50446492Swpaul 50546492Swpaul wi_cmd(sc, WI_CMD_INQUIRE, WI_INFO_COUNTERS); 50646492Swpaul 50746492Swpaul return; 50846492Swpaul} 50946492Swpaul 51046492Swpaulvoid wi_update_stats(sc) 51146492Swpaul struct wi_softc *sc; 51246492Swpaul{ 51346492Swpaul struct wi_ltv_gen gen; 51446492Swpaul u_int16_t id; 51546492Swpaul struct ifnet *ifp; 51646492Swpaul u_int32_t *ptr; 51746492Swpaul int i; 51846492Swpaul u_int16_t t; 51946492Swpaul 52046492Swpaul ifp = &sc->arpcom.ac_if; 52146492Swpaul 52246492Swpaul id = CSR_READ_2(sc, WI_INFO_FID); 52346492Swpaul 52446492Swpaul wi_read_data(sc, id, 0, (char *)&gen, 4); 52546492Swpaul 52646492Swpaul if (gen.wi_type != WI_INFO_COUNTERS || 52746492Swpaul gen.wi_len > (sizeof(sc->wi_stats) / 4) + 1) 52846492Swpaul return; 52946492Swpaul 53046492Swpaul ptr = (u_int32_t *)&sc->wi_stats; 53146492Swpaul 53246492Swpaul for (i = 0; i < gen.wi_len - 1; i++) { 53346492Swpaul t = CSR_READ_2(sc, WI_DATA1); 53446492Swpaul#ifdef WI_HERMES_STATS_WAR 53546492Swpaul if (t > 0xF000) 53646492Swpaul t = ~t & 0xFFFF; 53746492Swpaul#endif 53846492Swpaul ptr[i] += t; 53946492Swpaul } 54046492Swpaul 54146492Swpaul ifp->if_collisions = sc->wi_stats.wi_tx_single_retries + 54246492Swpaul sc->wi_stats.wi_tx_multi_retries + 54346492Swpaul sc->wi_stats.wi_tx_retry_limit; 54446492Swpaul 54546492Swpaul return; 54646492Swpaul} 54746492Swpaul 54853702Swpaulstatic void wi_intr(xsc) 54953702Swpaul void *xsc; 55046492Swpaul{ 55153702Swpaul struct wi_softc *sc = xsc; 55246492Swpaul struct ifnet *ifp; 55346492Swpaul u_int16_t status; 55446492Swpaul 55567092Swpaul WI_LOCK(sc); 55667092Swpaul 55746492Swpaul ifp = &sc->arpcom.ac_if; 55846492Swpaul 55946492Swpaul if (!(ifp->if_flags & IFF_UP)) { 56046492Swpaul CSR_WRITE_2(sc, WI_EVENT_ACK, 0xFFFF); 56146492Swpaul CSR_WRITE_2(sc, WI_INT_EN, 0); 56267092Swpaul WI_UNLOCK(sc); 56346492Swpaul return; 56446492Swpaul } 56546492Swpaul 56646492Swpaul /* Disable interrupts. */ 56746492Swpaul CSR_WRITE_2(sc, WI_INT_EN, 0); 56846492Swpaul 56946492Swpaul status = CSR_READ_2(sc, WI_EVENT_STAT); 57046492Swpaul CSR_WRITE_2(sc, WI_EVENT_ACK, ~WI_INTRS); 57146492Swpaul 57246492Swpaul if (status & WI_EV_RX) { 57346492Swpaul wi_rxeof(sc); 57446492Swpaul CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_RX); 57546492Swpaul } 57646492Swpaul 57746492Swpaul if (status & WI_EV_TX) { 57846492Swpaul wi_txeof(sc, status); 57946492Swpaul CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_TX); 58046492Swpaul } 58146492Swpaul 58246492Swpaul if (status & WI_EV_ALLOC) { 58346492Swpaul int id; 58446492Swpaul id = CSR_READ_2(sc, WI_ALLOC_FID); 58546492Swpaul CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_ALLOC); 58646492Swpaul if (id == sc->wi_tx_data_id) 58746492Swpaul wi_txeof(sc, status); 58846492Swpaul } 58946492Swpaul 59046492Swpaul if (status & WI_EV_INFO) { 59146492Swpaul wi_update_stats(sc); 59246492Swpaul CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_INFO); 59346492Swpaul } 59446492Swpaul 59546492Swpaul if (status & WI_EV_TX_EXC) { 59646492Swpaul wi_txeof(sc, status); 59746492Swpaul CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_TX_EXC); 59846492Swpaul } 59946492Swpaul 60046492Swpaul if (status & WI_EV_INFO_DROP) { 60146492Swpaul CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_INFO_DROP); 60246492Swpaul } 60346492Swpaul 60446492Swpaul /* Re-enable interrupts. */ 60546492Swpaul CSR_WRITE_2(sc, WI_INT_EN, WI_INTRS); 60646492Swpaul 60746492Swpaul if (ifp->if_snd.ifq_head != NULL) 60846492Swpaul wi_start(ifp); 60946492Swpaul 61067092Swpaul WI_UNLOCK(sc); 61167092Swpaul 61246492Swpaul return; 61346492Swpaul} 61446492Swpaul 61546492Swpaulstatic int wi_cmd(sc, cmd, val) 61646492Swpaul struct wi_softc *sc; 61746492Swpaul int cmd; 61846492Swpaul int val; 61946492Swpaul{ 62046492Swpaul int i, s = 0; 62146492Swpaul 62270073Swpaul /* wait for the busy bit to clear */ 62370073Swpaul for (i = 0; i < WI_TIMEOUT; i++) { 62470073Swpaul if (!(CSR_READ_2(sc, WI_COMMAND) & WI_CMD_BUSY)) { 62570073Swpaul break; 62670073Swpaul } 62770073Swpaul DELAY(10*1000); /* 10 m sec */ 62870073Swpaul } 62970073Swpaul 63070073Swpaul if (i == WI_TIMEOUT) { 63170073Swpaul return(ETIMEDOUT); 63270073Swpaul } 63370073Swpaul 63446492Swpaul CSR_WRITE_2(sc, WI_PARAM0, val); 63570073Swpaul CSR_WRITE_2(sc, WI_PARAM1, 0); 63670073Swpaul CSR_WRITE_2(sc, WI_PARAM2, 0); 63746492Swpaul CSR_WRITE_2(sc, WI_COMMAND, cmd); 63846492Swpaul 63946492Swpaul for (i = 0; i < WI_TIMEOUT; i++) { 64046492Swpaul /* 64146492Swpaul * Wait for 'command complete' bit to be 64246492Swpaul * set in the event status register. 64346492Swpaul */ 64446492Swpaul s = CSR_READ_2(sc, WI_EVENT_STAT) & WI_EV_CMD; 64546492Swpaul if (s) { 64646492Swpaul /* Ack the event and read result code. */ 64746492Swpaul s = CSR_READ_2(sc, WI_STATUS); 64846492Swpaul CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_CMD); 64946492Swpaul#ifdef foo 65046492Swpaul if ((s & WI_CMD_CODE_MASK) != (cmd & WI_CMD_CODE_MASK)) 65146492Swpaul return(EIO); 65246492Swpaul#endif 65346492Swpaul if (s & WI_STAT_CMD_RESULT) 65446492Swpaul return(EIO); 65546492Swpaul break; 65646492Swpaul } 65746492Swpaul } 65846492Swpaul 65946492Swpaul if (i == WI_TIMEOUT) 66046492Swpaul return(ETIMEDOUT); 66146492Swpaul 66246492Swpaul return(0); 66346492Swpaul} 66446492Swpaul 66546492Swpaulstatic void wi_reset(sc) 66646492Swpaul struct wi_softc *sc; 66746492Swpaul{ 66870073Swpaul#ifdef foo 66953702Swpaul wi_cmd(sc, WI_CMD_INI, 0); 67053702Swpaul DELAY(100000); 67153702Swpaul wi_cmd(sc, WI_CMD_INI, 0); 67270073Swpaul#endif 67353702Swpaul DELAY(100000); 67446492Swpaul if (wi_cmd(sc, WI_CMD_INI, 0)) 67553702Swpaul device_printf(sc->dev, "init failed\n"); 67646492Swpaul CSR_WRITE_2(sc, WI_INT_EN, 0); 67746492Swpaul CSR_WRITE_2(sc, WI_EVENT_ACK, 0xFFFF); 67846492Swpaul 67946492Swpaul /* Calibrate timer. */ 68046492Swpaul WI_SETVAL(WI_RID_TICK_TIME, 8); 68170073Swpaul 68246492Swpaul return; 68346492Swpaul} 68446492Swpaul 68546492Swpaul/* 68646492Swpaul * Read an LTV record from the NIC. 68746492Swpaul */ 68846492Swpaulstatic int wi_read_record(sc, ltv) 68946492Swpaul struct wi_softc *sc; 69046492Swpaul struct wi_ltv_gen *ltv; 69146492Swpaul{ 69246492Swpaul u_int16_t *ptr; 69346492Swpaul int i, len, code; 69470073Swpaul struct wi_ltv_gen *oltv, p2ltv; 69546492Swpaul 69670073Swpaul oltv = ltv; 69770073Swpaul if (sc->wi_prism2) { 69870073Swpaul switch (ltv->wi_type) { 69970073Swpaul case WI_RID_ENCRYPTION: 70070073Swpaul p2ltv.wi_type = WI_RID_P2_ENCRYPTION; 70170073Swpaul p2ltv.wi_len = 2; 70270073Swpaul ltv = &p2ltv; 70370073Swpaul break; 70470073Swpaul case WI_RID_TX_CRYPT_KEY: 70570073Swpaul p2ltv.wi_type = WI_RID_P2_TX_CRYPT_KEY; 70670073Swpaul p2ltv.wi_len = 2; 70770073Swpaul ltv = &p2ltv; 70870073Swpaul break; 70970073Swpaul } 71070073Swpaul } 71170073Swpaul 71246492Swpaul /* Tell the NIC to enter record read mode. */ 71346492Swpaul if (wi_cmd(sc, WI_CMD_ACCESS|WI_ACCESS_READ, ltv->wi_type)) 71446492Swpaul return(EIO); 71546492Swpaul 71647789Swpaul /* Seek to the record. */ 71747789Swpaul if (wi_seek(sc, ltv->wi_type, 0, WI_BAP1)) 71847789Swpaul return(EIO); 71946492Swpaul 72046492Swpaul /* 72146492Swpaul * Read the length and record type and make sure they 72246492Swpaul * match what we expect (this verifies that we have enough 72347401Swpaul * room to hold all of the returned data). 72446492Swpaul */ 72546492Swpaul len = CSR_READ_2(sc, WI_DATA1); 72646492Swpaul if (len > ltv->wi_len) 72746492Swpaul return(ENOSPC); 72846492Swpaul code = CSR_READ_2(sc, WI_DATA1); 72946492Swpaul if (code != ltv->wi_type) 73046492Swpaul return(EIO); 73146492Swpaul 73246492Swpaul ltv->wi_len = len; 73346492Swpaul ltv->wi_type = code; 73446492Swpaul 73546492Swpaul /* Now read the data. */ 73646492Swpaul ptr = <v->wi_val; 73746492Swpaul for (i = 0; i < ltv->wi_len - 1; i++) 73846492Swpaul ptr[i] = CSR_READ_2(sc, WI_DATA1); 73946492Swpaul 74070073Swpaul if (sc->wi_prism2) { 74170073Swpaul switch (oltv->wi_type) { 74270073Swpaul case WI_RID_TX_RATE: 74370073Swpaul case WI_RID_CUR_TX_RATE: 74470073Swpaul switch (ltv->wi_val) { 74570073Swpaul case 1: oltv->wi_val = 1; break; 74670073Swpaul case 2: oltv->wi_val = 2; break; 74770073Swpaul case 3: oltv->wi_val = 6; break; 74870073Swpaul case 4: oltv->wi_val = 5; break; 74970073Swpaul case 7: oltv->wi_val = 7; break; 75070073Swpaul case 8: oltv->wi_val = 11; break; 75170073Swpaul case 15: oltv->wi_val = 3; break; 75270073Swpaul default: oltv->wi_val = 0x100 + ltv->wi_val; break; 75370073Swpaul } 75470073Swpaul break; 75570073Swpaul case WI_RID_ENCRYPTION: 75670073Swpaul oltv->wi_len = 2; 75770073Swpaul if (ltv->wi_val & 0x01) 75870073Swpaul oltv->wi_val = 1; 75970073Swpaul else 76070073Swpaul oltv->wi_val = 0; 76170073Swpaul break; 76270073Swpaul case WI_RID_TX_CRYPT_KEY: 76370073Swpaul oltv->wi_len = 2; 76470073Swpaul oltv->wi_val = ltv->wi_val; 76570073Swpaul break; 76670073Swpaul } 76770073Swpaul } 76870073Swpaul 76946492Swpaul return(0); 77046492Swpaul} 77146492Swpaul 77246492Swpaul/* 77346492Swpaul * Same as read, except we inject data instead of reading it. 77446492Swpaul */ 77546492Swpaulstatic int wi_write_record(sc, ltv) 77646492Swpaul struct wi_softc *sc; 77746492Swpaul struct wi_ltv_gen *ltv; 77846492Swpaul{ 77946492Swpaul u_int16_t *ptr; 78046492Swpaul int i; 78170073Swpaul struct wi_ltv_gen p2ltv; 78246492Swpaul 78370073Swpaul if (sc->wi_prism2) { 78470073Swpaul switch (ltv->wi_type) { 78570073Swpaul case WI_RID_TX_RATE: 78670073Swpaul p2ltv.wi_type = WI_RID_TX_RATE; 78770073Swpaul p2ltv.wi_len = 2; 78870073Swpaul switch (ltv->wi_val) { 78970073Swpaul case 1: p2ltv.wi_val = 1; break; 79070073Swpaul case 2: p2ltv.wi_val = 2; break; 79170073Swpaul case 3: p2ltv.wi_val = 15; break; 79270073Swpaul case 5: p2ltv.wi_val = 4; break; 79370073Swpaul case 6: p2ltv.wi_val = 3; break; 79470073Swpaul case 7: p2ltv.wi_val = 7; break; 79570073Swpaul case 11: p2ltv.wi_val = 8; break; 79670073Swpaul default: return EINVAL; 79770073Swpaul } 79870073Swpaul ltv = &p2ltv; 79970073Swpaul break; 80070073Swpaul case WI_RID_ENCRYPTION: 80170073Swpaul p2ltv.wi_type = WI_RID_P2_ENCRYPTION; 80270073Swpaul p2ltv.wi_len = 2; 80370073Swpaul if (ltv->wi_val) 80470073Swpaul p2ltv.wi_val = 0x03; 80570073Swpaul else 80670073Swpaul p2ltv.wi_val = 0x90; 80770073Swpaul ltv = &p2ltv; 80870073Swpaul break; 80970073Swpaul case WI_RID_TX_CRYPT_KEY: 81070073Swpaul p2ltv.wi_type = WI_RID_P2_TX_CRYPT_KEY; 81170073Swpaul p2ltv.wi_len = 2; 81270073Swpaul p2ltv.wi_val = ltv->wi_val; 81370073Swpaul ltv = &p2ltv; 81470073Swpaul break; 81570073Swpaul case WI_RID_DEFLT_CRYPT_KEYS: 81670073Swpaul { 81770073Swpaul int error; 81870073Swpaul struct wi_ltv_str ws; 81970073Swpaul struct wi_ltv_keys *wk = (struct wi_ltv_keys *)ltv; 82070073Swpaul for (i = 0; i < 4; i++) { 82170073Swpaul ws.wi_len = 4; 82270073Swpaul ws.wi_type = WI_RID_P2_CRYPT_KEY0 + i; 82370073Swpaul memcpy(ws.wi_str, &wk->wi_keys[i].wi_keydat, 5); 82470073Swpaul ws.wi_str[5] = '\0'; 82570073Swpaul error = wi_write_record(sc, 82670073Swpaul (struct wi_ltv_gen *)&ws); 82770073Swpaul if (error) 82870073Swpaul return error; 82970073Swpaul } 83070073Swpaul return 0; 83170073Swpaul } 83270073Swpaul } 83370073Swpaul } 83470073Swpaul 83547789Swpaul if (wi_seek(sc, ltv->wi_type, 0, WI_BAP1)) 83647789Swpaul return(EIO); 83746492Swpaul 83846492Swpaul CSR_WRITE_2(sc, WI_DATA1, ltv->wi_len); 83946492Swpaul CSR_WRITE_2(sc, WI_DATA1, ltv->wi_type); 84046492Swpaul 84146492Swpaul ptr = <v->wi_val; 84246492Swpaul for (i = 0; i < ltv->wi_len - 1; i++) 84346492Swpaul CSR_WRITE_2(sc, WI_DATA1, ptr[i]); 84446492Swpaul 84546492Swpaul if (wi_cmd(sc, WI_CMD_ACCESS|WI_ACCESS_WRITE, ltv->wi_type)) 84646492Swpaul return(EIO); 84746492Swpaul 84846492Swpaul return(0); 84946492Swpaul} 85046492Swpaul 85146492Swpaulstatic int wi_seek(sc, id, off, chan) 85246492Swpaul struct wi_softc *sc; 85346492Swpaul int id, off, chan; 85446492Swpaul{ 85546492Swpaul int i; 85646492Swpaul int selreg, offreg; 85746492Swpaul 85846492Swpaul switch (chan) { 85946492Swpaul case WI_BAP0: 86046492Swpaul selreg = WI_SEL0; 86146492Swpaul offreg = WI_OFF0; 86246492Swpaul break; 86346492Swpaul case WI_BAP1: 86446492Swpaul selreg = WI_SEL1; 86546492Swpaul offreg = WI_OFF1; 86646492Swpaul break; 86746492Swpaul default: 86853702Swpaul device_printf(sc->dev, "invalid data path: %x\n", chan); 86946492Swpaul return(EIO); 87046492Swpaul } 87146492Swpaul 87246492Swpaul CSR_WRITE_2(sc, selreg, id); 87346492Swpaul CSR_WRITE_2(sc, offreg, off); 87446492Swpaul 87546492Swpaul for (i = 0; i < WI_TIMEOUT; i++) { 87646492Swpaul if (!(CSR_READ_2(sc, offreg) & (WI_OFF_BUSY|WI_OFF_ERR))) 87746492Swpaul break; 87846492Swpaul } 87946492Swpaul 88046492Swpaul if (i == WI_TIMEOUT) 88146492Swpaul return(ETIMEDOUT); 88246492Swpaul 88346492Swpaul return(0); 88446492Swpaul} 88546492Swpaul 88646492Swpaulstatic int wi_read_data(sc, id, off, buf, len) 88746492Swpaul struct wi_softc *sc; 88846492Swpaul int id, off; 88946492Swpaul caddr_t buf; 89046492Swpaul int len; 89146492Swpaul{ 89246492Swpaul int i; 89346492Swpaul u_int16_t *ptr; 89446492Swpaul 89546492Swpaul if (wi_seek(sc, id, off, WI_BAP1)) 89646492Swpaul return(EIO); 89746492Swpaul 89846492Swpaul ptr = (u_int16_t *)buf; 89946492Swpaul for (i = 0; i < len / 2; i++) 90046492Swpaul ptr[i] = CSR_READ_2(sc, WI_DATA1); 90146492Swpaul 90246492Swpaul return(0); 90346492Swpaul} 90446492Swpaul 90546492Swpaul/* 90646492Swpaul * According to the comments in the HCF Light code, there is a bug in 90746492Swpaul * the Hermes (or possibly in certain Hermes firmware revisions) where 90846492Swpaul * the chip's internal autoincrement counter gets thrown off during 90946492Swpaul * data writes: the autoincrement is missed, causing one data word to 91046492Swpaul * be overwritten and subsequent words to be written to the wrong memory 91146492Swpaul * locations. The end result is that we could end up transmitting bogus 91246492Swpaul * frames without realizing it. The workaround for this is to write a 91346492Swpaul * couple of extra guard words after the end of the transfer, then 91446492Swpaul * attempt to read then back. If we fail to locate the guard words where 91546492Swpaul * we expect them, we preform the transfer over again. 91646492Swpaul */ 91746492Swpaulstatic int wi_write_data(sc, id, off, buf, len) 91846492Swpaul struct wi_softc *sc; 91946492Swpaul int id, off; 92046492Swpaul caddr_t buf; 92146492Swpaul int len; 92246492Swpaul{ 92346492Swpaul int i; 92446492Swpaul u_int16_t *ptr; 92546492Swpaul 92646492Swpaul#ifdef WI_HERMES_AUTOINC_WAR 92746492Swpaulagain: 92846492Swpaul#endif 92946492Swpaul 93046492Swpaul if (wi_seek(sc, id, off, WI_BAP0)) 93146492Swpaul return(EIO); 93246492Swpaul 93346492Swpaul ptr = (u_int16_t *)buf; 93446492Swpaul for (i = 0; i < (len / 2); i++) 93546492Swpaul CSR_WRITE_2(sc, WI_DATA0, ptr[i]); 93646492Swpaul 93746492Swpaul#ifdef WI_HERMES_AUTOINC_WAR 93846492Swpaul CSR_WRITE_2(sc, WI_DATA0, 0x1234); 93946492Swpaul CSR_WRITE_2(sc, WI_DATA0, 0x5678); 94046492Swpaul 94146492Swpaul if (wi_seek(sc, id, off + len, WI_BAP0)) 94246492Swpaul return(EIO); 94346492Swpaul 94446492Swpaul if (CSR_READ_2(sc, WI_DATA0) != 0x1234 || 94546492Swpaul CSR_READ_2(sc, WI_DATA0) != 0x5678) 94646492Swpaul goto again; 94746492Swpaul#endif 94846492Swpaul 94946492Swpaul return(0); 95046492Swpaul} 95146492Swpaul 95246492Swpaul/* 95346492Swpaul * Allocate a region of memory inside the NIC and zero 95446492Swpaul * it out. 95546492Swpaul */ 95646492Swpaulstatic int wi_alloc_nicmem(sc, len, id) 95746492Swpaul struct wi_softc *sc; 95846492Swpaul int len; 95946492Swpaul int *id; 96046492Swpaul{ 96146492Swpaul int i; 96246492Swpaul 96346492Swpaul if (wi_cmd(sc, WI_CMD_ALLOC_MEM, len)) { 96453702Swpaul device_printf(sc->dev, "failed to allocate %d bytes on NIC\n", len); 96546492Swpaul return(ENOMEM); 96646492Swpaul } 96746492Swpaul 96846492Swpaul for (i = 0; i < WI_TIMEOUT; i++) { 96946492Swpaul if (CSR_READ_2(sc, WI_EVENT_STAT) & WI_EV_ALLOC) 97046492Swpaul break; 97146492Swpaul } 97246492Swpaul 97346492Swpaul if (i == WI_TIMEOUT) 97446492Swpaul return(ETIMEDOUT); 97546492Swpaul 97646492Swpaul CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_ALLOC); 97746492Swpaul *id = CSR_READ_2(sc, WI_ALLOC_FID); 97846492Swpaul 97947789Swpaul if (wi_seek(sc, *id, 0, WI_BAP0)) 98047789Swpaul return(EIO); 98146492Swpaul 98246492Swpaul for (i = 0; i < len / 2; i++) 98346492Swpaul CSR_WRITE_2(sc, WI_DATA0, 0); 98446492Swpaul 98546492Swpaul return(0); 98646492Swpaul} 98746492Swpaul 98846492Swpaulstatic void wi_setmulti(sc) 98946492Swpaul struct wi_softc *sc; 99046492Swpaul{ 99146492Swpaul struct ifnet *ifp; 99246492Swpaul int i = 0; 99346492Swpaul struct ifmultiaddr *ifma; 99446492Swpaul struct wi_ltv_mcast mcast; 99546492Swpaul 99646492Swpaul ifp = &sc->arpcom.ac_if; 99746492Swpaul 99846492Swpaul bzero((char *)&mcast, sizeof(mcast)); 99946492Swpaul 100046492Swpaul mcast.wi_type = WI_RID_MCAST; 100146492Swpaul mcast.wi_len = (3 * 16) + 1; 100246492Swpaul 100346492Swpaul if (ifp->if_flags & IFF_ALLMULTI || ifp->if_flags & IFF_PROMISC) { 100446492Swpaul wi_write_record(sc, (struct wi_ltv_gen *)&mcast); 100546492Swpaul return; 100646492Swpaul } 100746492Swpaul 100846492Swpaul for (ifma = ifp->if_multiaddrs.lh_first; ifma != NULL; 100946492Swpaul ifma = ifma->ifma_link.le_next) { 101046492Swpaul if (ifma->ifma_addr->sa_family != AF_LINK) 101146492Swpaul continue; 101246492Swpaul if (i < 16) { 101346492Swpaul bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr), 101446492Swpaul (char *)&mcast.wi_mcast[i], ETHER_ADDR_LEN); 101546492Swpaul i++; 101646492Swpaul } else { 101746492Swpaul bzero((char *)&mcast, sizeof(mcast)); 101846492Swpaul break; 101946492Swpaul } 102046492Swpaul } 102146492Swpaul 102246492Swpaul mcast.wi_len = (i * 3) + 1; 102346492Swpaul wi_write_record(sc, (struct wi_ltv_gen *)&mcast); 102446492Swpaul 102546492Swpaul return; 102646492Swpaul} 102746492Swpaul 102846492Swpaulstatic void wi_setdef(sc, wreq) 102946492Swpaul struct wi_softc *sc; 103046492Swpaul struct wi_req *wreq; 103146492Swpaul{ 103246492Swpaul struct sockaddr_dl *sdl; 103346492Swpaul struct ifaddr *ifa; 103446492Swpaul struct ifnet *ifp; 103546492Swpaul 103646492Swpaul ifp = &sc->arpcom.ac_if; 103746492Swpaul 103846492Swpaul switch(wreq->wi_type) { 103946492Swpaul case WI_RID_MAC_NODE: 104046492Swpaul ifa = ifnet_addrs[ifp->if_index - 1]; 104146492Swpaul sdl = (struct sockaddr_dl *)ifa->ifa_addr; 104246492Swpaul bcopy((char *)&wreq->wi_val, (char *)&sc->arpcom.ac_enaddr, 104346492Swpaul ETHER_ADDR_LEN); 104446492Swpaul bcopy((char *)&wreq->wi_val, LLADDR(sdl), ETHER_ADDR_LEN); 104546492Swpaul break; 104646492Swpaul case WI_RID_PORTTYPE: 104746492Swpaul sc->wi_ptype = wreq->wi_val[0]; 104846492Swpaul break; 104946492Swpaul case WI_RID_TX_RATE: 105046492Swpaul sc->wi_tx_rate = wreq->wi_val[0]; 105146492Swpaul break; 105246492Swpaul case WI_RID_MAX_DATALEN: 105346492Swpaul sc->wi_max_data_len = wreq->wi_val[0]; 105446492Swpaul break; 105546492Swpaul case WI_RID_RTS_THRESH: 105646492Swpaul sc->wi_rts_thresh = wreq->wi_val[0]; 105746492Swpaul break; 105846492Swpaul case WI_RID_SYSTEM_SCALE: 105946492Swpaul sc->wi_ap_density = wreq->wi_val[0]; 106046492Swpaul break; 106146492Swpaul case WI_RID_CREATE_IBSS: 106246492Swpaul sc->wi_create_ibss = wreq->wi_val[0]; 106346492Swpaul break; 106446563Swpaul case WI_RID_OWN_CHNL: 106546563Swpaul sc->wi_channel = wreq->wi_val[0]; 106646563Swpaul break; 106746492Swpaul case WI_RID_NODENAME: 106846492Swpaul bzero(sc->wi_node_name, sizeof(sc->wi_node_name)); 106946492Swpaul bcopy((char *)&wreq->wi_val[1], sc->wi_node_name, 30); 107046492Swpaul break; 107146492Swpaul case WI_RID_DESIRED_SSID: 107246492Swpaul bzero(sc->wi_net_name, sizeof(sc->wi_net_name)); 107346492Swpaul bcopy((char *)&wreq->wi_val[1], sc->wi_net_name, 30); 107446492Swpaul break; 107546492Swpaul case WI_RID_OWN_SSID: 107646492Swpaul bzero(sc->wi_ibss_name, sizeof(sc->wi_ibss_name)); 107746492Swpaul bcopy((char *)&wreq->wi_val[1], sc->wi_ibss_name, 30); 107846492Swpaul break; 107946611Swpaul case WI_RID_PM_ENABLED: 108046611Swpaul sc->wi_pm_enabled = wreq->wi_val[0]; 108146611Swpaul break; 108246611Swpaul case WI_RID_MAX_SLEEP: 108346611Swpaul sc->wi_max_sleep = wreq->wi_val[0]; 108446611Swpaul break; 108556965Swpaul case WI_RID_ENCRYPTION: 108656965Swpaul sc->wi_use_wep = wreq->wi_val[0]; 108756965Swpaul break; 108856965Swpaul case WI_RID_TX_CRYPT_KEY: 108956965Swpaul sc->wi_tx_key = wreq->wi_val[0]; 109056965Swpaul break; 109156965Swpaul case WI_RID_DEFLT_CRYPT_KEYS: 109256965Swpaul bcopy((char *)wreq, (char *)&sc->wi_keys, 109356965Swpaul sizeof(struct wi_ltv_keys)); 109456965Swpaul break; 109546492Swpaul default: 109646492Swpaul break; 109746492Swpaul } 109846492Swpaul 109946563Swpaul /* Reinitialize WaveLAN. */ 110046563Swpaul wi_init(sc); 110146563Swpaul 110246492Swpaul return; 110346492Swpaul} 110446492Swpaul 110546492Swpaulstatic int wi_ioctl(ifp, command, data) 110646492Swpaul struct ifnet *ifp; 110746492Swpaul u_long command; 110846492Swpaul caddr_t data; 110946492Swpaul{ 111067092Swpaul int error = 0; 111146492Swpaul struct wi_softc *sc; 111246492Swpaul struct wi_req wreq; 111346492Swpaul struct ifreq *ifr; 111461818Sroberto struct proc *p = curproc; 111546492Swpaul 111646492Swpaul sc = ifp->if_softc; 111767092Swpaul WI_LOCK(sc); 111846492Swpaul ifr = (struct ifreq *)data; 111946492Swpaul 112061818Sroberto if (sc->wi_gone) { 112161818Sroberto error = ENODEV; 112261818Sroberto goto out; 112361818Sroberto } 112446492Swpaul 112546492Swpaul switch(command) { 112646492Swpaul case SIOCSIFADDR: 112746492Swpaul case SIOCGIFADDR: 112846492Swpaul case SIOCSIFMTU: 112946492Swpaul error = ether_ioctl(ifp, command, data); 113046492Swpaul break; 113146492Swpaul case SIOCSIFFLAGS: 113246492Swpaul if (ifp->if_flags & IFF_UP) { 113346492Swpaul if (ifp->if_flags & IFF_RUNNING && 113446492Swpaul ifp->if_flags & IFF_PROMISC && 113546492Swpaul !(sc->wi_if_flags & IFF_PROMISC)) { 113646492Swpaul WI_SETVAL(WI_RID_PROMISC, 1); 113746492Swpaul } else if (ifp->if_flags & IFF_RUNNING && 113846492Swpaul !(ifp->if_flags & IFF_PROMISC) && 113946492Swpaul sc->wi_if_flags & IFF_PROMISC) { 114046492Swpaul WI_SETVAL(WI_RID_PROMISC, 0); 114146492Swpaul } else 114246492Swpaul wi_init(sc); 114346492Swpaul } else { 114446492Swpaul if (ifp->if_flags & IFF_RUNNING) { 114546492Swpaul wi_stop(sc); 114646492Swpaul } 114746492Swpaul } 114846492Swpaul sc->wi_if_flags = ifp->if_flags; 114946492Swpaul error = 0; 115046492Swpaul break; 115146492Swpaul case SIOCADDMULTI: 115246492Swpaul case SIOCDELMULTI: 115346492Swpaul wi_setmulti(sc); 115446492Swpaul error = 0; 115546492Swpaul break; 115646492Swpaul case SIOCGWAVELAN: 115746492Swpaul error = copyin(ifr->ifr_data, &wreq, sizeof(wreq)); 115846492Swpaul if (error) 115946492Swpaul break; 116065581Swpaul /* Don't show WEP keys to non-root users. */ 116165581Swpaul if (wreq.wi_type == WI_RID_DEFLT_CRYPT_KEYS && suser(p)) 116265581Swpaul break; 116346492Swpaul if (wreq.wi_type == WI_RID_IFACE_STATS) { 116446492Swpaul bcopy((char *)&sc->wi_stats, (char *)&wreq.wi_val, 116546492Swpaul sizeof(sc->wi_stats)); 116646492Swpaul wreq.wi_len = (sizeof(sc->wi_stats) / 2) + 1; 116756965Swpaul } else if (wreq.wi_type == WI_RID_DEFLT_CRYPT_KEYS) { 116856965Swpaul bcopy((char *)&sc->wi_keys, (char *)&wreq, 116956965Swpaul sizeof(struct wi_ltv_keys)); 117053702Swpaul } 117153702Swpaul#ifdef WICACHE 117253702Swpaul else if (wreq.wi_type == WI_RID_ZERO_CACHE) { 117353702Swpaul sc->wi_sigitems = sc->wi_nextitem = 0; 117453702Swpaul } else if (wreq.wi_type == WI_RID_READ_CACHE) { 117553702Swpaul char *pt = (char *)&wreq.wi_val; 117653702Swpaul bcopy((char *)&sc->wi_sigitems, 117753702Swpaul (char *)pt, sizeof(int)); 117853702Swpaul pt += (sizeof (int)); 117953702Swpaul wreq.wi_len = sizeof(int) / 2; 118053702Swpaul bcopy((char *)&sc->wi_sigcache, (char *)pt, 118153702Swpaul sizeof(struct wi_sigcache) * sc->wi_sigitems); 118253702Swpaul wreq.wi_len += ((sizeof(struct wi_sigcache) * 118353702Swpaul sc->wi_sigitems) / 2) + 1; 118453702Swpaul } 118553702Swpaul#endif 118653702Swpaul else { 118746492Swpaul if (wi_read_record(sc, (struct wi_ltv_gen *)&wreq)) { 118846492Swpaul error = EINVAL; 118946492Swpaul break; 119046492Swpaul } 119146492Swpaul } 119246492Swpaul error = copyout(&wreq, ifr->ifr_data, sizeof(wreq)); 119346492Swpaul break; 119446492Swpaul case SIOCSWAVELAN: 119561818Sroberto if ((error = suser(p))) 119661818Sroberto goto out; 119746492Swpaul error = copyin(ifr->ifr_data, &wreq, sizeof(wreq)); 119846492Swpaul if (error) 119946492Swpaul break; 120046492Swpaul if (wreq.wi_type == WI_RID_IFACE_STATS) { 120146492Swpaul error = EINVAL; 120246492Swpaul break; 120346492Swpaul } else if (wreq.wi_type == WI_RID_MGMT_XMIT) { 120446492Swpaul error = wi_mgmt_xmit(sc, (caddr_t)&wreq.wi_val, 120546492Swpaul wreq.wi_len); 120646492Swpaul } else { 120746492Swpaul error = wi_write_record(sc, (struct wi_ltv_gen *)&wreq); 120846492Swpaul if (!error) 120946492Swpaul wi_setdef(sc, &wreq); 121046492Swpaul } 121146492Swpaul break; 121246492Swpaul default: 121346492Swpaul error = EINVAL; 121446492Swpaul break; 121546492Swpaul } 121661818Srobertoout: 121767092Swpaul WI_UNLOCK(sc); 121846492Swpaul 121946492Swpaul return(error); 122046492Swpaul} 122146492Swpaul 122246492Swpaulstatic void wi_init(xsc) 122346492Swpaul void *xsc; 122446492Swpaul{ 122546492Swpaul struct wi_softc *sc = xsc; 122646492Swpaul struct ifnet *ifp = &sc->arpcom.ac_if; 122746492Swpaul struct wi_ltv_macaddr mac; 122846492Swpaul int id = 0; 122946492Swpaul 123067092Swpaul WI_LOCK(sc); 123167092Swpaul 123267092Swpaul if (sc->wi_gone) { 123367092Swpaul WI_UNLOCK(sc); 123446492Swpaul return; 123567092Swpaul } 123646492Swpaul 123746492Swpaul if (ifp->if_flags & IFF_RUNNING) 123846492Swpaul wi_stop(sc); 123946492Swpaul 124046492Swpaul wi_reset(sc); 124146492Swpaul 124246492Swpaul /* Program max data length. */ 124346492Swpaul WI_SETVAL(WI_RID_MAX_DATALEN, sc->wi_max_data_len); 124446492Swpaul 124547401Swpaul /* Enable/disable IBSS creation. */ 124646492Swpaul WI_SETVAL(WI_RID_CREATE_IBSS, sc->wi_create_ibss); 124746492Swpaul 124846492Swpaul /* Set the port type. */ 124946492Swpaul WI_SETVAL(WI_RID_PORTTYPE, sc->wi_ptype); 125046492Swpaul 125146492Swpaul /* Program the RTS/CTS threshold. */ 125246492Swpaul WI_SETVAL(WI_RID_RTS_THRESH, sc->wi_rts_thresh); 125346492Swpaul 125446492Swpaul /* Program the TX rate */ 125546492Swpaul WI_SETVAL(WI_RID_TX_RATE, sc->wi_tx_rate); 125646492Swpaul 125746492Swpaul /* Access point density */ 125846492Swpaul WI_SETVAL(WI_RID_SYSTEM_SCALE, sc->wi_ap_density); 125946492Swpaul 126046611Swpaul /* Power Management Enabled */ 126146611Swpaul WI_SETVAL(WI_RID_PM_ENABLED, sc->wi_pm_enabled); 126246611Swpaul 126346611Swpaul /* Power Managment Max Sleep */ 126446611Swpaul WI_SETVAL(WI_RID_MAX_SLEEP, sc->wi_max_sleep); 126546611Swpaul 126646492Swpaul /* Specify the IBSS name */ 126746492Swpaul WI_SETSTR(WI_RID_OWN_SSID, sc->wi_ibss_name); 126846492Swpaul 126946492Swpaul /* Specify the network name */ 127046492Swpaul WI_SETSTR(WI_RID_DESIRED_SSID, sc->wi_net_name); 127146492Swpaul 127246563Swpaul /* Specify the frequency to use */ 127346563Swpaul WI_SETVAL(WI_RID_OWN_CHNL, sc->wi_channel); 127446563Swpaul 127546492Swpaul /* Program the nodename. */ 127646492Swpaul WI_SETSTR(WI_RID_NODENAME, sc->wi_node_name); 127746492Swpaul 127846492Swpaul /* Set our MAC address. */ 127946492Swpaul mac.wi_len = 4; 128046492Swpaul mac.wi_type = WI_RID_MAC_NODE; 128146492Swpaul bcopy((char *)&sc->arpcom.ac_enaddr, 128246492Swpaul (char *)&mac.wi_mac_addr, ETHER_ADDR_LEN); 128346492Swpaul wi_write_record(sc, (struct wi_ltv_gen *)&mac); 128446492Swpaul 128556965Swpaul /* Configure WEP. */ 128656965Swpaul if (sc->wi_has_wep) { 128756965Swpaul WI_SETVAL(WI_RID_ENCRYPTION, sc->wi_use_wep); 128856965Swpaul WI_SETVAL(WI_RID_TX_CRYPT_KEY, sc->wi_tx_key); 128956965Swpaul sc->wi_keys.wi_len = (sizeof(struct wi_ltv_keys) / 2) + 1; 129056965Swpaul sc->wi_keys.wi_type = WI_RID_DEFLT_CRYPT_KEYS; 129156965Swpaul wi_write_record(sc, (struct wi_ltv_gen *)&sc->wi_keys); 129256965Swpaul } 129356965Swpaul 129446492Swpaul /* Initialize promisc mode. */ 129546492Swpaul if (ifp->if_flags & IFF_PROMISC) { 129646492Swpaul WI_SETVAL(WI_RID_PROMISC, 1); 129746492Swpaul } else { 129846492Swpaul WI_SETVAL(WI_RID_PROMISC, 0); 129946492Swpaul } 130046492Swpaul 130146492Swpaul /* Set multicast filter. */ 130246492Swpaul wi_setmulti(sc); 130346492Swpaul 130446492Swpaul /* Enable desired port */ 130546492Swpaul wi_cmd(sc, WI_CMD_ENABLE|sc->wi_portnum, 0); 130646492Swpaul 130746492Swpaul if (wi_alloc_nicmem(sc, 1518 + sizeof(struct wi_frame) + 8, &id)) 130853702Swpaul device_printf(sc->dev, "tx buffer allocation failed\n"); 130946492Swpaul sc->wi_tx_data_id = id; 131046492Swpaul 131146492Swpaul if (wi_alloc_nicmem(sc, 1518 + sizeof(struct wi_frame) + 8, &id)) 131253702Swpaul device_printf(sc->dev, "mgmt. buffer allocation failed\n"); 131346492Swpaul sc->wi_tx_mgmt_id = id; 131446492Swpaul 131546492Swpaul /* enable interrupts */ 131646492Swpaul CSR_WRITE_2(sc, WI_INT_EN, WI_INTRS); 131746492Swpaul 131846492Swpaul ifp->if_flags |= IFF_RUNNING; 131946492Swpaul ifp->if_flags &= ~IFF_OACTIVE; 132046492Swpaul 132146492Swpaul sc->wi_stat_ch = timeout(wi_inquire, sc, hz * 60); 132267092Swpaul WI_UNLOCK(sc); 132346492Swpaul 132446492Swpaul return; 132546492Swpaul} 132646492Swpaul 132746492Swpaulstatic void wi_start(ifp) 132846492Swpaul struct ifnet *ifp; 132946492Swpaul{ 133046492Swpaul struct wi_softc *sc; 133146492Swpaul struct mbuf *m0; 133246492Swpaul struct wi_frame tx_frame; 133346492Swpaul struct ether_header *eh; 133446492Swpaul int id; 133546492Swpaul 133646492Swpaul sc = ifp->if_softc; 133767092Swpaul WI_LOCK(sc); 133846492Swpaul 133967092Swpaul if (sc->wi_gone) { 134067092Swpaul WI_UNLOCK(sc); 134146492Swpaul return; 134267092Swpaul } 134346492Swpaul 134467092Swpaul if (ifp->if_flags & IFF_OACTIVE) { 134567092Swpaul WI_UNLOCK(sc); 134646492Swpaul return; 134767092Swpaul } 134846492Swpaul 134946492Swpaul IF_DEQUEUE(&ifp->if_snd, m0); 135067092Swpaul if (m0 == NULL) { 135167092Swpaul WI_UNLOCK(sc); 135246492Swpaul return; 135367092Swpaul } 135446492Swpaul 135546492Swpaul bzero((char *)&tx_frame, sizeof(tx_frame)); 135646492Swpaul id = sc->wi_tx_data_id; 135746492Swpaul eh = mtod(m0, struct ether_header *); 135846492Swpaul 135946492Swpaul /* 136047401Swpaul * Use RFC1042 encoding for IP and ARP datagrams, 136146492Swpaul * 802.3 for anything else. 136246492Swpaul */ 136355831Swpaul if (ntohs(eh->ether_type) > 1518) { 136446492Swpaul bcopy((char *)&eh->ether_dhost, 136546492Swpaul (char *)&tx_frame.wi_addr1, ETHER_ADDR_LEN); 136646492Swpaul bcopy((char *)&eh->ether_shost, 136746492Swpaul (char *)&tx_frame.wi_addr2, ETHER_ADDR_LEN); 136846492Swpaul bcopy((char *)&eh->ether_dhost, 136946492Swpaul (char *)&tx_frame.wi_dst_addr, ETHER_ADDR_LEN); 137046492Swpaul bcopy((char *)&eh->ether_shost, 137146492Swpaul (char *)&tx_frame.wi_src_addr, ETHER_ADDR_LEN); 137246492Swpaul 137346492Swpaul tx_frame.wi_dat_len = m0->m_pkthdr.len - WI_SNAPHDR_LEN; 137446492Swpaul tx_frame.wi_frame_ctl = WI_FTYPE_DATA; 137546492Swpaul tx_frame.wi_dat[0] = htons(WI_SNAP_WORD0); 137646492Swpaul tx_frame.wi_dat[1] = htons(WI_SNAP_WORD1); 137746492Swpaul tx_frame.wi_len = htons(m0->m_pkthdr.len - WI_SNAPHDR_LEN); 137846492Swpaul tx_frame.wi_type = eh->ether_type; 137946492Swpaul 138046492Swpaul m_copydata(m0, sizeof(struct ether_header), 138146492Swpaul m0->m_pkthdr.len - sizeof(struct ether_header), 138246492Swpaul (caddr_t)&sc->wi_txbuf); 138346492Swpaul 138446492Swpaul wi_write_data(sc, id, 0, (caddr_t)&tx_frame, 138546492Swpaul sizeof(struct wi_frame)); 138646492Swpaul wi_write_data(sc, id, WI_802_11_OFFSET, (caddr_t)&sc->wi_txbuf, 138746492Swpaul (m0->m_pkthdr.len - sizeof(struct ether_header)) + 2); 138846492Swpaul } else { 138946492Swpaul tx_frame.wi_dat_len = m0->m_pkthdr.len; 139046492Swpaul 139155831Swpaul eh->ether_type = htons(m0->m_pkthdr.len - WI_SNAPHDR_LEN); 139246492Swpaul m_copydata(m0, 0, m0->m_pkthdr.len, (caddr_t)&sc->wi_txbuf); 139346492Swpaul 139446492Swpaul wi_write_data(sc, id, 0, (caddr_t)&tx_frame, 139546492Swpaul sizeof(struct wi_frame)); 139646492Swpaul wi_write_data(sc, id, WI_802_3_OFFSET, (caddr_t)&sc->wi_txbuf, 139746492Swpaul m0->m_pkthdr.len + 2); 139846492Swpaul } 139946492Swpaul 140046492Swpaul /* 140146492Swpaul * If there's a BPF listner, bounce a copy of 140246492Swpaul * this frame to him. 140346492Swpaul */ 140446492Swpaul if (ifp->if_bpf) 140546492Swpaul bpf_mtap(ifp, m0); 140646492Swpaul 140746492Swpaul m_freem(m0); 140846492Swpaul 140946492Swpaul if (wi_cmd(sc, WI_CMD_TX|WI_RECLAIM, id)) 141053702Swpaul device_printf(sc->dev, "xmit failed\n"); 141146492Swpaul 141246492Swpaul ifp->if_flags |= IFF_OACTIVE; 141346492Swpaul 141446492Swpaul /* 141546492Swpaul * Set a timeout in case the chip goes out to lunch. 141646492Swpaul */ 141746492Swpaul ifp->if_timer = 5; 141846492Swpaul 141967092Swpaul WI_UNLOCK(sc); 142046492Swpaul return; 142146492Swpaul} 142246492Swpaul 142346492Swpaulstatic int wi_mgmt_xmit(sc, data, len) 142446492Swpaul struct wi_softc *sc; 142546492Swpaul caddr_t data; 142646492Swpaul int len; 142746492Swpaul{ 142846492Swpaul struct wi_frame tx_frame; 142946492Swpaul int id; 143046492Swpaul struct wi_80211_hdr *hdr; 143146492Swpaul caddr_t dptr; 143246492Swpaul 143346492Swpaul if (sc->wi_gone) 143446492Swpaul return(ENODEV); 143546492Swpaul 143646492Swpaul hdr = (struct wi_80211_hdr *)data; 143746492Swpaul dptr = data + sizeof(struct wi_80211_hdr); 143846492Swpaul 143946492Swpaul bzero((char *)&tx_frame, sizeof(tx_frame)); 144046492Swpaul id = sc->wi_tx_mgmt_id; 144146492Swpaul 144246492Swpaul bcopy((char *)hdr, (char *)&tx_frame.wi_frame_ctl, 144346492Swpaul sizeof(struct wi_80211_hdr)); 144446492Swpaul 144546492Swpaul tx_frame.wi_dat_len = len - WI_SNAPHDR_LEN; 144646492Swpaul tx_frame.wi_len = htons(len - WI_SNAPHDR_LEN); 144746492Swpaul 144846492Swpaul wi_write_data(sc, id, 0, (caddr_t)&tx_frame, sizeof(struct wi_frame)); 144946492Swpaul wi_write_data(sc, id, WI_802_11_OFFSET_RAW, dptr, 145046492Swpaul (len - sizeof(struct wi_80211_hdr)) + 2); 145146492Swpaul 145246492Swpaul if (wi_cmd(sc, WI_CMD_TX|WI_RECLAIM, id)) { 145353702Swpaul device_printf(sc->dev, "xmit failed\n"); 145446492Swpaul return(EIO); 145546492Swpaul } 145646492Swpaul 145746492Swpaul return(0); 145846492Swpaul} 145946492Swpaul 146046492Swpaulstatic void wi_stop(sc) 146146492Swpaul struct wi_softc *sc; 146246492Swpaul{ 146346492Swpaul struct ifnet *ifp; 146446492Swpaul 146567092Swpaul WI_LOCK(sc); 146667092Swpaul 146767092Swpaul if (sc->wi_gone) { 146867092Swpaul WI_UNLOCK(sc); 146946492Swpaul return; 147067092Swpaul } 147146492Swpaul 147246492Swpaul ifp = &sc->arpcom.ac_if; 147346492Swpaul 147470173Sjhb /* 147570173Sjhb * If the card is gone and the memory port isn't mapped, we will 147670173Sjhb * (hopefully) get 0xffff back from the status read, which is not 147770173Sjhb * a valid status value. 147870173Sjhb */ 147970173Sjhb if (CSR_READ_2(sc, WI_STATUS) != 0xffff) { 148070173Sjhb CSR_WRITE_2(sc, WI_INT_EN, 0); 148170173Sjhb wi_cmd(sc, WI_CMD_DISABLE|sc->wi_portnum, 0); 148270173Sjhb } 148346492Swpaul 148446492Swpaul untimeout(wi_inquire, sc, sc->wi_stat_ch); 148546492Swpaul 148646492Swpaul ifp->if_flags &= ~(IFF_RUNNING|IFF_OACTIVE); 148746492Swpaul 148867092Swpaul WI_UNLOCK(sc); 148946492Swpaul return; 149046492Swpaul} 149146492Swpaul 149246492Swpaulstatic void wi_watchdog(ifp) 149346492Swpaul struct ifnet *ifp; 149446492Swpaul{ 149546492Swpaul struct wi_softc *sc; 149646492Swpaul 149746492Swpaul sc = ifp->if_softc; 149846492Swpaul 149953702Swpaul device_printf(sc->dev,"device timeout\n"); 150046492Swpaul 150146492Swpaul wi_init(sc); 150246492Swpaul 150346492Swpaul ifp->if_oerrors++; 150446492Swpaul 150546492Swpaul return; 150646492Swpaul} 150746492Swpaul 150853702Swpaulstatic int wi_alloc(dev) 150953702Swpaul device_t dev; 151046492Swpaul{ 151153702Swpaul struct wi_softc *sc = device_get_softc(dev); 151253702Swpaul int rid; 151353702Swpaul 151453702Swpaul rid = 0; 151553702Swpaul sc->iobase = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 151670073Swpaul 0, ~0, (1 << 6), 151770073Swpaul rman_make_alignment_flags(1 << 6) | RF_ACTIVE); 151853702Swpaul if (!sc->iobase) { 151953702Swpaul device_printf(dev, "No I/O space?!\n"); 152053702Swpaul return (ENXIO); 152153702Swpaul } 152253702Swpaul 152353702Swpaul rid = 0; 152453702Swpaul sc->irq = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 152553702Swpaul 0, ~0, 1, RF_ACTIVE); 152653702Swpaul if (!sc->irq) { 152753702Swpaul device_printf(dev, "No irq?!\n"); 152853702Swpaul return (ENXIO); 152953702Swpaul } 153053702Swpaul 153153702Swpaul sc->dev = dev; 153253702Swpaul sc->wi_unit = device_get_unit(dev); 153353702Swpaul sc->wi_io_addr = rman_get_start(sc->iobase); 153453702Swpaul sc->wi_btag = rman_get_bustag(sc->iobase); 153553702Swpaul sc->wi_bhandle = rman_get_bushandle(sc->iobase); 153653702Swpaul 153753702Swpaul return (0); 153853702Swpaul} 153953702Swpaul 154053702Swpaulstatic void wi_free(dev) 154153702Swpaul device_t dev; 154253702Swpaul{ 154353702Swpaul struct wi_softc *sc = device_get_softc(dev); 154453702Swpaul 154553702Swpaul if (sc->iobase != NULL) 154653702Swpaul bus_release_resource(dev, SYS_RES_IOPORT, 0, sc->iobase); 154753702Swpaul if (sc->irq != NULL) 154853702Swpaul bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq); 154953702Swpaul 155053702Swpaul return; 155153702Swpaul} 155253702Swpaul 155353702Swpaulstatic void wi_shutdown(dev) 155453702Swpaul device_t dev; 155553702Swpaul{ 155646492Swpaul struct wi_softc *sc; 155746492Swpaul 155853702Swpaul sc = device_get_softc(dev); 155946492Swpaul wi_stop(sc); 156046492Swpaul 156146492Swpaul return; 156246492Swpaul} 156353702Swpaul 156453702Swpaul#ifdef WICACHE 156553702Swpaul/* wavelan signal strength cache code. 156653702Swpaul * store signal/noise/quality on per MAC src basis in 156753702Swpaul * a small fixed cache. The cache wraps if > MAX slots 156853702Swpaul * used. The cache may be zeroed out to start over. 156953702Swpaul * Two simple filters exist to reduce computation: 157053702Swpaul * 1. ip only (literally 0x800) which may be used 157153702Swpaul * to ignore some packets. It defaults to ip only. 157253702Swpaul * it could be used to focus on broadcast, non-IP 802.11 beacons. 157353702Swpaul * 2. multicast/broadcast only. This may be used to 157453702Swpaul * ignore unicast packets and only cache signal strength 157553702Swpaul * for multicast/broadcast packets (beacons); e.g., Mobile-IP 157653702Swpaul * beacons and not unicast traffic. 157753702Swpaul * 157853702Swpaul * The cache stores (MAC src(index), IP src (major clue), signal, 157953702Swpaul * quality, noise) 158053702Swpaul * 158153702Swpaul * No apologies for storing IP src here. It's easy and saves much 158253702Swpaul * trouble elsewhere. The cache is assumed to be INET dependent, 158353702Swpaul * although it need not be. 158453702Swpaul */ 158553702Swpaul 158653702Swpaul#ifdef documentation 158753702Swpaul 158853702Swpaulint wi_sigitems; /* number of cached entries */ 158953702Swpaulstruct wi_sigcache wi_sigcache[MAXWICACHE]; /* array of cache entries */ 159053702Swpaulint wi_nextitem; /* index/# of entries */ 159153702Swpaul 159253702Swpaul 159353702Swpaul#endif 159453702Swpaul 159553702Swpaul/* control variables for cache filtering. Basic idea is 159653702Swpaul * to reduce cost (e.g., to only Mobile-IP agent beacons 159753702Swpaul * which are broadcast or multicast). Still you might 159853702Swpaul * want to measure signal strength with unicast ping packets 159953702Swpaul * on a pt. to pt. ant. setup. 160053702Swpaul */ 160153702Swpaul/* set true if you want to limit cache items to broadcast/mcast 160253702Swpaul * only packets (not unicast). Useful for mobile-ip beacons which 160353702Swpaul * are broadcast/multicast at network layer. Default is all packets 160453702Swpaul * so ping/unicast will work say with pt. to pt. antennae setup. 160553702Swpaul */ 160653702Swpaulstatic int wi_cache_mcastonly = 0; 160753702SwpaulSYSCTL_INT(_machdep, OID_AUTO, wi_cache_mcastonly, CTLFLAG_RW, 160853702Swpaul &wi_cache_mcastonly, 0, ""); 160953702Swpaul 161053702Swpaul/* set true if you want to limit cache items to IP packets only 161153702Swpaul*/ 161253702Swpaulstatic int wi_cache_iponly = 1; 161353702SwpaulSYSCTL_INT(_machdep, OID_AUTO, wi_cache_iponly, CTLFLAG_RW, 161453702Swpaul &wi_cache_iponly, 0, ""); 161553702Swpaul 161653702Swpaul/* 161753702Swpaul * Original comments: 161853702Swpaul * ----------------- 161953702Swpaul * wi_cache_store, per rx packet store signal 162053702Swpaul * strength in MAC (src) indexed cache. 162153702Swpaul * 162253702Swpaul * follows linux driver in how signal strength is computed. 162353702Swpaul * In ad hoc mode, we use the rx_quality field. 162453702Swpaul * signal and noise are trimmed to fit in the range from 47..138. 162553702Swpaul * rx_quality field MSB is signal strength. 162653702Swpaul * rx_quality field LSB is noise. 162753702Swpaul * "quality" is (signal - noise) as is log value. 162853702Swpaul * note: quality CAN be negative. 162953702Swpaul * 163053702Swpaul * In BSS mode, we use the RID for communication quality. 163153702Swpaul * TBD: BSS mode is currently untested. 163253702Swpaul * 163353702Swpaul * Bill's comments: 163453702Swpaul * --------------- 163553702Swpaul * Actually, we use the rx_quality field all the time for both "ad-hoc" 163653702Swpaul * and BSS modes. Why? Because reading an RID is really, really expensive: 163753702Swpaul * there's a bunch of PIO operations that have to be done to read a record 163853702Swpaul * from the NIC, and reading the comms quality RID each time a packet is 163953702Swpaul * received can really hurt performance. We don't have to do this anyway: 164053702Swpaul * the comms quality field only reflects the values in the rx_quality field 164153702Swpaul * anyway. The comms quality RID is only meaningful in infrastructure mode, 164253702Swpaul * but the values it contains are updated based on the rx_quality from 164353702Swpaul * frames received from the access point. 164453702Swpaul * 164553702Swpaul * Also, according to Lucent, the signal strength and noise level values 164653702Swpaul * can be converted to dBms by subtracting 149, so I've modified the code 164753702Swpaul * to do that instead of the scaling it did originally. 164853702Swpaul */ 164953702Swpaulstatic 165053702Swpaulvoid wi_cache_store (struct wi_softc *sc, struct ether_header *eh, 165153702Swpaul struct mbuf *m, unsigned short rx_quality) 165253702Swpaul{ 165353702Swpaul struct ip *ip = 0; 165453702Swpaul int i; 165553702Swpaul static int cache_slot = 0; /* use this cache entry */ 165653702Swpaul static int wrapindex = 0; /* next "free" cache entry */ 165753702Swpaul int sig, noise; 165853702Swpaul int sawip=0; 165953702Swpaul 166053702Swpaul /* filters: 166153702Swpaul * 1. ip only 166253702Swpaul * 2. configurable filter to throw out unicast packets, 166353702Swpaul * keep multicast only. 166453702Swpaul */ 166553702Swpaul 166653702Swpaul if ((ntohs(eh->ether_type) == 0x800)) { 166753702Swpaul sawip = 1; 166853702Swpaul } 166953702Swpaul 167053702Swpaul /* filter for ip packets only 167153702Swpaul */ 167253702Swpaul if (wi_cache_iponly && !sawip) { 167353702Swpaul return; 167453702Swpaul } 167553702Swpaul 167653702Swpaul /* filter for broadcast/multicast only 167753702Swpaul */ 167853702Swpaul if (wi_cache_mcastonly && ((eh->ether_dhost[0] & 1) == 0)) { 167953702Swpaul return; 168053702Swpaul } 168153702Swpaul 168253702Swpaul#ifdef SIGDEBUG 168353702Swpaul printf("wi%d: q value %x (MSB=0x%x, LSB=0x%x) \n", sc->wi_unit, 168453702Swpaul rx_quality & 0xffff, rx_quality >> 8, rx_quality & 0xff); 168553702Swpaul#endif 168653702Swpaul 168753702Swpaul /* find the ip header. we want to store the ip_src 168853702Swpaul * address. 168953702Swpaul */ 169053702Swpaul if (sawip) { 169153702Swpaul ip = mtod(m, struct ip *); 169253702Swpaul } 169353702Swpaul 169453702Swpaul /* do a linear search for a matching MAC address 169553702Swpaul * in the cache table 169653702Swpaul * . MAC address is 6 bytes, 169753702Swpaul * . var w_nextitem holds total number of entries already cached 169853702Swpaul */ 169953702Swpaul for(i = 0; i < sc->wi_nextitem; i++) { 170053702Swpaul if (! bcmp(eh->ether_shost , sc->wi_sigcache[i].macsrc, 6 )) { 170153702Swpaul /* Match!, 170253702Swpaul * so we already have this entry, 170353702Swpaul * update the data 170453702Swpaul */ 170553702Swpaul break; 170653702Swpaul } 170753702Swpaul } 170853702Swpaul 170953702Swpaul /* did we find a matching mac address? 171053702Swpaul * if yes, then overwrite a previously existing cache entry 171153702Swpaul */ 171253702Swpaul if (i < sc->wi_nextitem ) { 171353702Swpaul cache_slot = i; 171453702Swpaul } 171553702Swpaul /* else, have a new address entry,so 171653702Swpaul * add this new entry, 171753702Swpaul * if table full, then we need to replace LRU entry 171853702Swpaul */ 171953702Swpaul else { 172053702Swpaul 172153702Swpaul /* check for space in cache table 172253702Swpaul * note: wi_nextitem also holds number of entries 172353702Swpaul * added in the cache table 172453702Swpaul */ 172553702Swpaul if ( sc->wi_nextitem < MAXWICACHE ) { 172653702Swpaul cache_slot = sc->wi_nextitem; 172753702Swpaul sc->wi_nextitem++; 172853702Swpaul sc->wi_sigitems = sc->wi_nextitem; 172953702Swpaul } 173053702Swpaul /* no space found, so simply wrap with wrap index 173153702Swpaul * and "zap" the next entry 173253702Swpaul */ 173353702Swpaul else { 173453702Swpaul if (wrapindex == MAXWICACHE) { 173553702Swpaul wrapindex = 0; 173653702Swpaul } 173753702Swpaul cache_slot = wrapindex++; 173853702Swpaul } 173953702Swpaul } 174053702Swpaul 174153702Swpaul /* invariant: cache_slot now points at some slot 174253702Swpaul * in cache. 174353702Swpaul */ 174453702Swpaul if (cache_slot < 0 || cache_slot >= MAXWICACHE) { 174553702Swpaul log(LOG_ERR, "wi_cache_store, bad index: %d of " 174653702Swpaul "[0..%d], gross cache error\n", 174753702Swpaul cache_slot, MAXWICACHE); 174853702Swpaul return; 174953702Swpaul } 175053702Swpaul 175153702Swpaul /* store items in cache 175253702Swpaul * .ip source address 175353702Swpaul * .mac src 175453702Swpaul * .signal, etc. 175553702Swpaul */ 175653702Swpaul if (sawip) { 175753702Swpaul sc->wi_sigcache[cache_slot].ipsrc = ip->ip_src.s_addr; 175853702Swpaul } 175953702Swpaul bcopy( eh->ether_shost, sc->wi_sigcache[cache_slot].macsrc, 6); 176053702Swpaul 176153702Swpaul sig = (rx_quality >> 8) & 0xFF; 176253702Swpaul noise = rx_quality & 0xFF; 176353702Swpaul sc->wi_sigcache[cache_slot].signal = sig - 149; 176453702Swpaul sc->wi_sigcache[cache_slot].noise = noise - 149; 176553702Swpaul sc->wi_sigcache[cache_slot].quality = sig - noise; 176653702Swpaul 176753702Swpaul return; 176853702Swpaul} 176953702Swpaul#endif 1770