if_wi.c revision 47401
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 * 3247401Swpaul * $Id: if_wi.c,v 1.54 1999/05/20 04:10:40 wpaul Exp $ 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. */ 6946492Swpaul 7046492Swpaul#include "bpfilter.h" 7146492Swpaul#include "card.h" 7246492Swpaul#include "wi.h" 7346492Swpaul 7446492Swpaul#include <sys/param.h> 7546492Swpaul#include <sys/systm.h> 7646492Swpaul#include <sys/sockio.h> 7746492Swpaul#include <sys/mbuf.h> 7846492Swpaul#include <sys/malloc.h> 7946492Swpaul#include <sys/kernel.h> 8046492Swpaul#include <sys/socket.h> 8146492Swpaul 8246492Swpaul#include <net/if.h> 8346492Swpaul#include <net/if_arp.h> 8446492Swpaul#include <net/ethernet.h> 8546492Swpaul#include <net/if_dl.h> 8646492Swpaul#include <net/if_media.h> 8746492Swpaul#include <net/if_types.h> 8846492Swpaul 8946492Swpaul#ifdef INET 9046492Swpaul#include <netinet/in.h> 9146492Swpaul#include <netinet/in_systm.h> 9246492Swpaul#include <netinet/in_var.h> 9346492Swpaul#include <netinet/ip.h> 9446492Swpaul#include <netinet/if_ether.h> 9546492Swpaul#endif 9646492Swpaul 9746492Swpaul#if NBPFILTER > 0 9846492Swpaul#include <net/bpf.h> 9946492Swpaul#endif 10046492Swpaul 10146492Swpaul#include <machine/clock.h> 10246492Swpaul#include <machine/md_var.h> 10346492Swpaul#include <machine/bus_pio.h> 10446492Swpaul#include <machine/bus.h> 10546492Swpaul 10646492Swpaul#include <i386/isa/isa_device.h> 10746492Swpaul#include <i386/isa/icu.h> 10846492Swpaul#include <i386/isa/if_wireg.h> 10946492Swpaul#include <machine/if_wavelan_ieee.h> 11046492Swpaul 11146492Swpaul#if NCARD > 0 11246492Swpaul#include <sys/select.h> 11346492Swpaul#include <pccard/cardinfo.h> 11446492Swpaul#include <pccard/slot.h> 11546492Swpaul#endif 11646492Swpaul 11746492Swpaul#if !defined(lint) 11846492Swpaulstatic const char rcsid[] = 11947401Swpaul "$Id: if_wi.c,v 1.54 1999/05/20 04:10:40 wpaul Exp $"; 12046492Swpaul#endif 12146492Swpaul 12246492Swpaulstatic struct wi_softc wi_softc[NWI]; 12346492Swpaul 12446492Swpaul#ifdef foo 12547401Swpaulstatic u_int8_t wi_mcast_addr[6] = { 0x01, 0x60, 0x1D, 0x00, 0x01, 0x00 }; 12646492Swpaul#endif 12746492Swpaul 12846492Swpaulstatic int wi_probe __P((struct isa_device *)); 12946492Swpaulstatic int wi_attach __P((struct isa_device *)); 13046492Swpaul#ifdef PCCARD_MODULE 13146492Swpaulstatic ointhand2_t wi_intr; 13246492Swpaul#endif 13346492Swpaulstatic void wi_reset __P((struct wi_softc *)); 13446492Swpaulstatic int wi_ioctl __P((struct ifnet *, u_long, caddr_t)); 13546492Swpaulstatic void wi_init __P((void *)); 13646492Swpaulstatic void wi_start __P((struct ifnet *)); 13746492Swpaulstatic void wi_stop __P((struct wi_softc *)); 13846492Swpaulstatic void wi_watchdog __P((struct ifnet *)); 13946492Swpaulstatic void wi_shutdown __P((int, void *)); 14046492Swpaulstatic void wi_rxeof __P((struct wi_softc *)); 14146492Swpaulstatic void wi_txeof __P((struct wi_softc *, int)); 14246492Swpaulstatic void wi_update_stats __P((struct wi_softc *)); 14346492Swpaulstatic void wi_setmulti __P((struct wi_softc *)); 14446492Swpaul 14546492Swpaulstatic int wi_cmd __P((struct wi_softc *, int, int)); 14646492Swpaulstatic int wi_read_record __P((struct wi_softc *, struct wi_ltv_gen *)); 14746492Swpaulstatic int wi_write_record __P((struct wi_softc *, struct wi_ltv_gen *)); 14846492Swpaulstatic int wi_read_data __P((struct wi_softc *, int, 14946492Swpaul int, caddr_t, int)); 15046492Swpaulstatic int wi_write_data __P((struct wi_softc *, int, 15146492Swpaul int, caddr_t, int)); 15246492Swpaulstatic int wi_seek __P((struct wi_softc *, int, int, int)); 15346492Swpaulstatic int wi_alloc_nicmem __P((struct wi_softc *, int, int *)); 15446492Swpaulstatic void wi_inquire __P((void *)); 15546492Swpaulstatic void wi_setdef __P((struct wi_softc *, struct wi_req *)); 15646492Swpaulstatic int wi_mgmt_xmit __P((struct wi_softc *, caddr_t, int)); 15746492Swpaul 15846492Swpaulstruct isa_driver widriver = { 15946492Swpaul wi_probe, 16046492Swpaul wi_attach, 16146492Swpaul "wi", 16246492Swpaul 1 16346492Swpaul}; 16446492Swpaul 16546492Swpaul#if NCARD > 0 16646492Swpaulstatic int wi_pccard_init __P((struct pccard_devinfo *)); 16746492Swpaulstatic void wi_pccard_unload __P((struct pccard_devinfo *)); 16846492Swpaulstatic int wi_pccard_intr __P((struct pccard_devinfo *)); 16946492Swpaul 17046492Swpaul#ifdef PCCARD_MODULE 17146492SwpaulPCCARD_MODULE(wi, wi_pccard_init, wi_pccard_unload, 17246492Swpaul wi_pccard_intr, 0, net_imask); 17346492Swpaul#else 17446492Swpaulstatic struct pccard_device wi_info = { 17546492Swpaul "wi", 17646492Swpaul wi_pccard_init, 17746492Swpaul wi_pccard_unload, 17846492Swpaul wi_pccard_intr, 17946492Swpaul 0, /* Attributes - presently unused */ 18046492Swpaul &net_imask /* Interrupt mask for device */ 18146492Swpaul /* XXX - Should this also include net_imask? */ 18246492Swpaul}; 18346492Swpaul 18446492SwpaulDATA_SET(pccarddrv_set, wi_info); 18546492Swpaul#endif 18646492Swpaul 18746492Swpaul/* Initialize the PCCARD. */ 18846492Swpaulstatic int wi_pccard_init(sc_p) 18946492Swpaul struct pccard_devinfo *sc_p; 19046492Swpaul{ 19146492Swpaul struct wi_softc *sc; 19246492Swpaul int i; 19346492Swpaul u_int32_t irq; 19446492Swpaul 19546492Swpaul if (sc_p->isahd.id_unit >= NWI) 19646492Swpaul return(ENODEV); 19746492Swpaul 19846492Swpaul sc = &wi_softc[sc_p->isahd.id_unit]; 19946492Swpaul sc->wi_gone = 0; 20046492Swpaul sc->wi_unit = sc_p->isahd.id_unit; 20146492Swpaul sc->wi_bhandle = sc_p->isahd.id_iobase; 20246492Swpaul sc->wi_btag = I386_BUS_SPACE_IO; 20346492Swpaul 20446492Swpaul /* Make sure interrupts are disabled. */ 20546492Swpaul CSR_WRITE_2(sc, WI_INT_EN, 0); 20646492Swpaul CSR_WRITE_2(sc, WI_EVENT_ACK, 0xFFFF); 20746492Swpaul 20846492Swpaul /* Grr. IRQ is encoded as a bitmask. */ 20946492Swpaul irq = sc_p->isahd.id_irq; 21046492Swpaul for (i = 0; i < 32; i++) { 21146492Swpaul if (irq & 0x1) 21246492Swpaul break; 21346492Swpaul irq >>= 1; 21446492Swpaul } 21546492Swpaul 21646492Swpaul /* 21746492Swpaul * Print a nice probe message to let the operator 21846492Swpaul * know something interesting is happening. 21946492Swpaul */ 22046492Swpaul printf("wi%d: <WaveLAN/IEEE 802.11> at 0x%x-0x%x irq %d on isa\n", 22146492Swpaul sc_p->isahd.id_unit, sc_p->isahd.id_iobase, 22246492Swpaul sc_p->isahd.id_iobase + WI_IOSIZ - 1, i); 22346492Swpaul 22446492Swpaul if (wi_attach(&sc_p->isahd)) 22546492Swpaul return(ENXIO); 22646492Swpaul 22746492Swpaul return(0); 22846492Swpaul} 22946492Swpaul 23046492Swpaulstatic void wi_pccard_unload(sc_p) 23146492Swpaul struct pccard_devinfo *sc_p; 23246492Swpaul{ 23346492Swpaul struct wi_softc *sc; 23446492Swpaul struct ifnet *ifp; 23546492Swpaul 23646492Swpaul sc = &wi_softc[sc_p->isahd.id_unit]; 23746492Swpaul ifp = &sc->arpcom.ac_if; 23846492Swpaul 23946492Swpaul if (sc->wi_gone) { 24046492Swpaul printf("wi%d: already unloaded\n", sc_p->isahd.id_unit); 24146492Swpaul return; 24246492Swpaul } 24346492Swpaul 24446492Swpaul ifp->if_flags &= ~IFF_RUNNING; 24546492Swpaul if_down(ifp); 24646492Swpaul sc->wi_gone = 1; 24746492Swpaul printf("wi%d: unloaded\n", sc_p->isahd.id_unit); 24846492Swpaul 24946492Swpaul return; 25046492Swpaul} 25146492Swpaul 25246492Swpaulstatic int wi_pccard_intr(sc_p) 25346492Swpaul struct pccard_devinfo *sc_p; 25446492Swpaul{ 25546492Swpaul wi_intr(sc_p->isahd.id_unit); 25646492Swpaul return(1); 25746492Swpaul} 25846492Swpaul#endif 25946492Swpaul 26046492Swpaulstatic int wi_probe(isa_dev) 26146492Swpaul struct isa_device *isa_dev; 26246492Swpaul{ 26346492Swpaul /* 26446492Swpaul * The ISA WaveLAN/IEEE card is actually not an ISA card: 26547401Swpaul * it's a PCMCIA card plugged into a PCMCIA bridge adapter 26646492Swpaul * that fits into an ISA slot. Consequently, we will always 26746492Swpaul * be using the pccard support to probe and attach these 26846492Swpaul * devices, so we can never actually probe one from here. 26946492Swpaul */ 27046492Swpaul return(0); 27146492Swpaul} 27246492Swpaul 27346492Swpaulstatic int wi_attach(isa_dev) 27446492Swpaul struct isa_device *isa_dev; 27546492Swpaul{ 27646492Swpaul struct wi_softc *sc; 27746492Swpaul struct wi_ltv_macaddr mac; 27846563Swpaul struct wi_ltv_gen gen; 27946492Swpaul struct ifnet *ifp; 28046492Swpaul char ifname[IFNAMSIZ]; 28146492Swpaul 28246492Swpaul#ifdef PCCARD_MODULE 28346492Swpaul isa_dev->id_ointr = wi_intr; 28446492Swpaul#endif 28546492Swpaul sc = &wi_softc[isa_dev->id_unit]; 28646492Swpaul ifp = &sc->arpcom.ac_if; 28746492Swpaul 28846492Swpaul /* Reset the NIC. */ 28946492Swpaul wi_reset(sc); 29046492Swpaul 29146492Swpaul /* Read the station address. */ 29246492Swpaul mac.wi_type = WI_RID_MAC_NODE; 29346492Swpaul mac.wi_len = 4; 29446492Swpaul wi_read_record(sc, (struct wi_ltv_gen *)&mac); 29546492Swpaul bcopy((char *)&mac.wi_mac_addr, 29646492Swpaul (char *)&sc->arpcom.ac_enaddr, ETHER_ADDR_LEN); 29746492Swpaul 29846492Swpaul printf("wi%d: Ethernet address: %6D\n", sc->wi_unit, 29946492Swpaul sc->arpcom.ac_enaddr, ":"); 30046492Swpaul 30146492Swpaul ifp->if_softc = sc; 30246492Swpaul ifp->if_unit = sc->wi_unit; 30346492Swpaul ifp->if_name = "wi"; 30446492Swpaul ifp->if_mtu = ETHERMTU; 30546492Swpaul ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; 30646492Swpaul ifp->if_ioctl = wi_ioctl; 30746492Swpaul ifp->if_output = ether_output; 30846492Swpaul ifp->if_start = wi_start; 30946492Swpaul ifp->if_watchdog = wi_watchdog; 31046492Swpaul ifp->if_init = wi_init; 31146492Swpaul ifp->if_baudrate = 10000000; 31246492Swpaul ifp->if_snd.ifq_maxlen = IFQ_MAXLEN; 31346492Swpaul 31446492Swpaul bzero(sc->wi_node_name, sizeof(sc->wi_node_name)); 31546492Swpaul bcopy(WI_DEFAULT_NODENAME, sc->wi_node_name, 31646492Swpaul sizeof(WI_DEFAULT_NODENAME) - 1); 31746492Swpaul 31846492Swpaul bzero(sc->wi_net_name, sizeof(sc->wi_net_name)); 31946492Swpaul bcopy(WI_DEFAULT_NETNAME, sc->wi_net_name, 32046492Swpaul sizeof(WI_DEFAULT_NETNAME) - 1); 32146492Swpaul 32246492Swpaul bzero(sc->wi_ibss_name, sizeof(sc->wi_ibss_name)); 32346492Swpaul bcopy(WI_DEFAULT_IBSS, sc->wi_ibss_name, 32446492Swpaul sizeof(WI_DEFAULT_IBSS) - 1); 32546492Swpaul 32646492Swpaul sc->wi_portnum = WI_DEFAULT_PORT; 32746492Swpaul sc->wi_ptype = WI_PORTTYPE_ADHOC; 32846492Swpaul sc->wi_ap_density = WI_DEFAULT_AP_DENSITY; 32946492Swpaul sc->wi_rts_thresh = WI_DEFAULT_RTS_THRESH; 33046492Swpaul sc->wi_tx_rate = WI_DEFAULT_TX_RATE; 33146492Swpaul sc->wi_max_data_len = WI_DEFAULT_DATALEN; 33246492Swpaul sc->wi_create_ibss = WI_DEFAULT_CREATE_IBSS; 33346611Swpaul sc->wi_pm_enabled = WI_DEFAULT_PM_ENABLED; 33446611Swpaul sc->wi_max_sleep = WI_DEFAULT_MAX_SLEEP; 33546492Swpaul 33646563Swpaul /* 33746563Swpaul * Read the default channel from the NIC. This may vary 33846563Swpaul * depending on the country where the NIC was purchased, so 33946563Swpaul * we can't hard-code a default and expect it to work for 34046563Swpaul * everyone. 34146563Swpaul */ 34246563Swpaul gen.wi_type = WI_RID_OWN_CHNL; 34346563Swpaul gen.wi_len = 2; 34446563Swpaul wi_read_record(sc, &gen); 34546563Swpaul sc->wi_channel = gen.wi_val; 34646563Swpaul 34746492Swpaul bzero((char *)&sc->wi_stats, sizeof(sc->wi_stats)); 34846492Swpaul 34946492Swpaul wi_init(sc); 35046492Swpaul wi_stop(sc); 35146492Swpaul 35246492Swpaul /* 35346492Swpaul * If this logical interface has already been attached, 35446492Swpaul * don't attach it again or chaos will ensue. 35546492Swpaul */ 35646492Swpaul sprintf(ifname, "wi%d", sc->wi_unit); 35746492Swpaul 35846492Swpaul if (ifunit(ifname) == NULL) { 35946492Swpaul callout_handle_init(&sc->wi_stat_ch); 36046492Swpaul /* 36146492Swpaul * Call MI attach routines. 36246492Swpaul */ 36346492Swpaul if_attach(ifp); 36446492Swpaul ether_ifattach(ifp); 36546492Swpaul 36646492Swpaul#if NBPFILTER > 0 36746492Swpaul bpfattach(ifp, DLT_EN10MB, sizeof(struct ether_header)); 36846492Swpaul#endif 36946492Swpaul 37046492Swpaul at_shutdown(wi_shutdown, sc, SHUTDOWN_POST_SYNC); 37146492Swpaul } 37246492Swpaul 37346492Swpaul return(0); 37446492Swpaul} 37546492Swpaul 37646492Swpaulstatic void wi_rxeof(sc) 37746492Swpaul struct wi_softc *sc; 37846492Swpaul{ 37946492Swpaul struct ifnet *ifp; 38046492Swpaul struct ether_header *eh; 38146492Swpaul struct wi_frame rx_frame; 38246492Swpaul struct mbuf *m; 38346492Swpaul int id; 38446492Swpaul 38546492Swpaul ifp = &sc->arpcom.ac_if; 38646492Swpaul 38746492Swpaul id = CSR_READ_2(sc, WI_RX_FID); 38846492Swpaul 38946492Swpaul /* First read in the frame header */ 39046492Swpaul if (wi_read_data(sc, id, 0, (caddr_t)&rx_frame, sizeof(rx_frame))) { 39146492Swpaul ifp->if_ierrors++; 39246492Swpaul return; 39346492Swpaul } 39446492Swpaul 39546492Swpaul if (rx_frame.wi_status & WI_STAT_ERRSTAT) { 39646492Swpaul ifp->if_ierrors++; 39746492Swpaul return; 39846492Swpaul } 39946492Swpaul 40046492Swpaul MGETHDR(m, M_DONTWAIT, MT_DATA); 40146492Swpaul if (m == NULL) { 40246492Swpaul ifp->if_ierrors++; 40346492Swpaul return; 40446492Swpaul } 40546492Swpaul MCLGET(m, M_DONTWAIT); 40646492Swpaul if (!(m->m_flags & M_EXT)) { 40746492Swpaul m_freem(m); 40846492Swpaul ifp->if_ierrors++; 40946492Swpaul return; 41046492Swpaul } 41146492Swpaul 41246492Swpaul eh = mtod(m, struct ether_header *); 41346492Swpaul m->m_pkthdr.rcvif = ifp; 41446492Swpaul 41546492Swpaul if (rx_frame.wi_status == WI_STAT_1042 || 41646492Swpaul rx_frame.wi_status == WI_STAT_TUNNEL || 41746492Swpaul rx_frame.wi_status == WI_STAT_WMP_MSG) { 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); 42346492Swpaul bcopy((char *)&rx_frame.wi_addr2, 42446492Swpaul (char *)&eh->ether_shost, ETHER_ADDR_LEN); 42546492Swpaul bcopy((char *)&rx_frame.wi_type, 42646492Swpaul (char *)&eh->ether_type, sizeof(u_int16_t)); 42746492Swpaul 42846492Swpaul if (wi_read_data(sc, id, WI_802_11_OFFSET, 42946492Swpaul mtod(m, caddr_t) + sizeof(struct ether_header), 43046492Swpaul m->m_len + 2)) { 43146492Swpaul m_freem(m); 43246492Swpaul ifp->if_ierrors++; 43346492Swpaul return; 43446492Swpaul } 43546492Swpaul } else { 43646492Swpaul m->m_pkthdr.len = m->m_len = 43746492Swpaul rx_frame.wi_dat_len + sizeof(struct ether_header); 43846492Swpaul 43946492Swpaul if (wi_read_data(sc, id, WI_802_3_OFFSET, 44046492Swpaul mtod(m, caddr_t), m->m_len + 2)) { 44146492Swpaul m_freem(m); 44246492Swpaul ifp->if_ierrors++; 44346492Swpaul return; 44446492Swpaul } 44546492Swpaul } 44646492Swpaul 44746492Swpaul ifp->if_ipackets++; 44846492Swpaul 44946492Swpaul#if NBPFILTER > 0 45046492Swpaul /* Handle BPF listeners. */ 45146492Swpaul if (ifp->if_bpf) { 45246492Swpaul bpf_mtap(ifp, m); 45346492Swpaul if (ifp->if_flags & IFF_PROMISC && 45446492Swpaul (bcmp(eh->ether_dhost, sc->arpcom.ac_enaddr, 45546492Swpaul ETHER_ADDR_LEN) && (eh->ether_dhost[0] & 1) == 0)) { 45646492Swpaul m_freem(m); 45746492Swpaul return; 45846492Swpaul } 45946492Swpaul } 46046492Swpaul#endif 46146492Swpaul 46246492Swpaul /* Receive packet. */ 46346492Swpaul m_adj(m, sizeof(struct ether_header)); 46446492Swpaul ether_input(ifp, eh, m); 46546492Swpaul 46646492Swpaul return; 46746492Swpaul} 46846492Swpaul 46946492Swpaulstatic void wi_txeof(sc, status) 47046492Swpaul struct wi_softc *sc; 47146492Swpaul int status; 47246492Swpaul{ 47346492Swpaul struct ifnet *ifp; 47446492Swpaul 47546492Swpaul ifp = &sc->arpcom.ac_if; 47646492Swpaul 47746492Swpaul ifp->if_timer = 0; 47846492Swpaul ifp->if_flags &= ~IFF_OACTIVE; 47946492Swpaul 48046492Swpaul if (status & WI_EV_TX_EXC) 48146492Swpaul ifp->if_oerrors++; 48246492Swpaul else 48346492Swpaul ifp->if_opackets++; 48446492Swpaul 48546492Swpaul return; 48646492Swpaul} 48746492Swpaul 48846492Swpaulvoid wi_inquire(xsc) 48946492Swpaul void *xsc; 49046492Swpaul{ 49146492Swpaul struct wi_softc *sc; 49246492Swpaul struct ifnet *ifp; 49346492Swpaul 49446492Swpaul sc = xsc; 49546492Swpaul ifp = &sc->arpcom.ac_if; 49646492Swpaul 49746492Swpaul sc->wi_stat_ch = timeout(wi_inquire, sc, hz * 60); 49846492Swpaul 49946492Swpaul /* Don't do this while we're transmitting */ 50046492Swpaul if (ifp->if_flags & IFF_OACTIVE) 50146492Swpaul return; 50246492Swpaul 50346492Swpaul wi_cmd(sc, WI_CMD_INQUIRE, WI_INFO_COUNTERS); 50446492Swpaul 50546492Swpaul return; 50646492Swpaul} 50746492Swpaul 50846492Swpaulvoid wi_update_stats(sc) 50946492Swpaul struct wi_softc *sc; 51046492Swpaul{ 51146492Swpaul struct wi_ltv_gen gen; 51246492Swpaul u_int16_t id; 51346492Swpaul struct ifnet *ifp; 51446492Swpaul u_int32_t *ptr; 51546492Swpaul int i; 51646492Swpaul u_int16_t t; 51746492Swpaul 51846492Swpaul ifp = &sc->arpcom.ac_if; 51946492Swpaul 52046492Swpaul id = CSR_READ_2(sc, WI_INFO_FID); 52146492Swpaul 52246492Swpaul wi_read_data(sc, id, 0, (char *)&gen, 4); 52346492Swpaul 52446492Swpaul if (gen.wi_type != WI_INFO_COUNTERS || 52546492Swpaul gen.wi_len > (sizeof(sc->wi_stats) / 4) + 1) 52646492Swpaul return; 52746492Swpaul 52846492Swpaul ptr = (u_int32_t *)&sc->wi_stats; 52946492Swpaul 53046492Swpaul for (i = 0; i < gen.wi_len - 1; i++) { 53146492Swpaul t = CSR_READ_2(sc, WI_DATA1); 53246492Swpaul#ifdef WI_HERMES_STATS_WAR 53346492Swpaul if (t > 0xF000) 53446492Swpaul t = ~t & 0xFFFF; 53546492Swpaul#endif 53646492Swpaul ptr[i] += t; 53746492Swpaul } 53846492Swpaul 53946492Swpaul ifp->if_collisions = sc->wi_stats.wi_tx_single_retries + 54046492Swpaul sc->wi_stats.wi_tx_multi_retries + 54146492Swpaul sc->wi_stats.wi_tx_retry_limit; 54246492Swpaul 54346492Swpaul return; 54446492Swpaul} 54546492Swpaul 54646492Swpaulvoid wi_intr(unit) 54746492Swpaul int unit; 54846492Swpaul{ 54946492Swpaul struct wi_softc *sc; 55046492Swpaul struct ifnet *ifp; 55146492Swpaul u_int16_t status; 55246492Swpaul 55346492Swpaul sc = &wi_softc[unit]; 55446492Swpaul ifp = &sc->arpcom.ac_if; 55546492Swpaul 55646492Swpaul if (!(ifp->if_flags & IFF_UP)) { 55746492Swpaul CSR_WRITE_2(sc, WI_EVENT_ACK, 0xFFFF); 55846492Swpaul CSR_WRITE_2(sc, WI_INT_EN, 0); 55946492Swpaul return; 56046492Swpaul } 56146492Swpaul 56246492Swpaul /* Disable interrupts. */ 56346492Swpaul CSR_WRITE_2(sc, WI_INT_EN, 0); 56446492Swpaul 56546492Swpaul status = CSR_READ_2(sc, WI_EVENT_STAT); 56646492Swpaul CSR_WRITE_2(sc, WI_EVENT_ACK, ~WI_INTRS); 56746492Swpaul 56846492Swpaul if (status & WI_EV_RX) { 56946492Swpaul wi_rxeof(sc); 57046492Swpaul CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_RX); 57146492Swpaul } 57246492Swpaul 57346492Swpaul if (status & WI_EV_TX) { 57446492Swpaul wi_txeof(sc, status); 57546492Swpaul CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_TX); 57646492Swpaul } 57746492Swpaul 57846492Swpaul if (status & WI_EV_ALLOC) { 57946492Swpaul int id; 58046492Swpaul id = CSR_READ_2(sc, WI_ALLOC_FID); 58146492Swpaul CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_ALLOC); 58246492Swpaul if (id == sc->wi_tx_data_id) 58346492Swpaul wi_txeof(sc, status); 58446492Swpaul } 58546492Swpaul 58646492Swpaul if (status & WI_EV_INFO) { 58746492Swpaul wi_update_stats(sc); 58846492Swpaul CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_INFO); 58946492Swpaul } 59046492Swpaul 59146492Swpaul if (status & WI_EV_TX_EXC) { 59246492Swpaul wi_txeof(sc, status); 59346492Swpaul CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_TX_EXC); 59446492Swpaul } 59546492Swpaul 59646492Swpaul if (status & WI_EV_INFO_DROP) { 59746492Swpaul CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_INFO_DROP); 59846492Swpaul } 59946492Swpaul 60046492Swpaul /* Re-enable interrupts. */ 60146492Swpaul CSR_WRITE_2(sc, WI_INT_EN, WI_INTRS); 60246492Swpaul 60346492Swpaul if (ifp->if_snd.ifq_head != NULL) 60446492Swpaul wi_start(ifp); 60546492Swpaul 60646492Swpaul return; 60746492Swpaul} 60846492Swpaul 60946492Swpaulstatic int wi_cmd(sc, cmd, val) 61046492Swpaul struct wi_softc *sc; 61146492Swpaul int cmd; 61246492Swpaul int val; 61346492Swpaul{ 61446492Swpaul int i, s = 0; 61546492Swpaul 61646492Swpaul CSR_WRITE_2(sc, WI_PARAM0, val); 61746492Swpaul CSR_WRITE_2(sc, WI_COMMAND, cmd); 61846492Swpaul 61946492Swpaul for (i = 0; i < WI_TIMEOUT; i++) { 62046492Swpaul /* 62146492Swpaul * Wait for 'command complete' bit to be 62246492Swpaul * set in the event status register. 62346492Swpaul */ 62446492Swpaul s = CSR_READ_2(sc, WI_EVENT_STAT) & WI_EV_CMD; 62546492Swpaul if (s) { 62646492Swpaul /* Ack the event and read result code. */ 62746492Swpaul s = CSR_READ_2(sc, WI_STATUS); 62846492Swpaul CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_CMD); 62946492Swpaul#ifdef foo 63046492Swpaul if ((s & WI_CMD_CODE_MASK) != (cmd & WI_CMD_CODE_MASK)) 63146492Swpaul return(EIO); 63246492Swpaul#endif 63346492Swpaul if (s & WI_STAT_CMD_RESULT) 63446492Swpaul return(EIO); 63546492Swpaul break; 63646492Swpaul } 63746492Swpaul } 63846492Swpaul 63946492Swpaul if (i == WI_TIMEOUT) 64046492Swpaul return(ETIMEDOUT); 64146492Swpaul 64246492Swpaul return(0); 64346492Swpaul} 64446492Swpaul 64546492Swpaulstatic void wi_reset(sc) 64646492Swpaul struct wi_softc *sc; 64746492Swpaul{ 64846492Swpaul if (wi_cmd(sc, WI_CMD_INI, 0)) 64946492Swpaul printf("wi%d: init failed\n", sc->wi_unit); 65046492Swpaul CSR_WRITE_2(sc, WI_INT_EN, 0); 65146492Swpaul CSR_WRITE_2(sc, WI_EVENT_ACK, 0xFFFF); 65246492Swpaul 65346492Swpaul /* Calibrate timer. */ 65446492Swpaul WI_SETVAL(WI_RID_TICK_TIME, 8); 65546492Swpaul 65646492Swpaul return; 65746492Swpaul} 65846492Swpaul 65946492Swpaul/* 66046492Swpaul * Read an LTV record from the NIC. 66146492Swpaul */ 66246492Swpaulstatic int wi_read_record(sc, ltv) 66346492Swpaul struct wi_softc *sc; 66446492Swpaul struct wi_ltv_gen *ltv; 66546492Swpaul{ 66646492Swpaul u_int16_t *ptr; 66746492Swpaul int i, len, code; 66846492Swpaul 66946492Swpaul /* Tell the NIC to enter record read mode. */ 67046492Swpaul if (wi_cmd(sc, WI_CMD_ACCESS|WI_ACCESS_READ, ltv->wi_type)) 67146492Swpaul return(EIO); 67246492Swpaul 67346492Swpaul /* Select the record we want to read. */ 67446492Swpaul CSR_WRITE_2(sc, WI_SEL1, ltv->wi_type); 67546492Swpaul 67646492Swpaul /* Specify offset -- we always read the whole record. */ 67746492Swpaul CSR_WRITE_2(sc, WI_OFF1, 0); 67846492Swpaul 67946492Swpaul /* Wait for NIC to acknowledge */ 68046492Swpaul for (i = 0; i < WI_TIMEOUT; i++) { 68146492Swpaul if (!(CSR_READ_2(sc, WI_OFF1) & (WI_OFF_BUSY|WI_OFF_ERR))) 68246492Swpaul break; 68346492Swpaul } 68446492Swpaul 68546492Swpaul if (i == WI_TIMEOUT) 68646492Swpaul return(ETIMEDOUT); 68746492Swpaul 68846492Swpaul /* 68946492Swpaul * Read the length and record type and make sure they 69046492Swpaul * match what we expect (this verifies that we have enough 69147401Swpaul * room to hold all of the returned data). 69246492Swpaul */ 69346492Swpaul len = CSR_READ_2(sc, WI_DATA1); 69446492Swpaul if (len > ltv->wi_len) 69546492Swpaul return(ENOSPC); 69646492Swpaul code = CSR_READ_2(sc, WI_DATA1); 69746492Swpaul if (code != ltv->wi_type) 69846492Swpaul return(EIO); 69946492Swpaul 70046492Swpaul ltv->wi_len = len; 70146492Swpaul ltv->wi_type = code; 70246492Swpaul 70346492Swpaul /* Now read the data. */ 70446492Swpaul ptr = <v->wi_val; 70546492Swpaul for (i = 0; i < ltv->wi_len - 1; i++) 70646492Swpaul ptr[i] = CSR_READ_2(sc, WI_DATA1); 70746492Swpaul 70846492Swpaul return(0); 70946492Swpaul} 71046492Swpaul 71146492Swpaul/* 71246492Swpaul * Same as read, except we inject data instead of reading it. 71346492Swpaul */ 71446492Swpaulstatic int wi_write_record(sc, ltv) 71546492Swpaul struct wi_softc *sc; 71646492Swpaul struct wi_ltv_gen *ltv; 71746492Swpaul{ 71846492Swpaul u_int16_t *ptr; 71946492Swpaul int i; 72046492Swpaul 72146492Swpaul CSR_WRITE_2(sc, WI_SEL1, ltv->wi_type); 72246492Swpaul CSR_WRITE_2(sc, WI_OFF1, 0); 72346492Swpaul 72446492Swpaul for (i = 0; i < WI_TIMEOUT; i++) { 72546492Swpaul if (!(CSR_READ_2(sc, WI_OFF1) & (WI_OFF_BUSY|WI_OFF_ERR))) 72646492Swpaul break; 72746492Swpaul } 72846492Swpaul 72946492Swpaul if (i == WI_TIMEOUT) 73046492Swpaul return(ETIMEDOUT); 73146492Swpaul 73246492Swpaul CSR_WRITE_2(sc, WI_DATA1, ltv->wi_len); 73346492Swpaul CSR_WRITE_2(sc, WI_DATA1, ltv->wi_type); 73446492Swpaul 73546492Swpaul ptr = <v->wi_val; 73646492Swpaul for (i = 0; i < ltv->wi_len - 1; i++) 73746492Swpaul CSR_WRITE_2(sc, WI_DATA1, ptr[i]); 73846492Swpaul 73946492Swpaul if (wi_cmd(sc, WI_CMD_ACCESS|WI_ACCESS_WRITE, ltv->wi_type)) 74046492Swpaul return(EIO); 74146492Swpaul 74246492Swpaul return(0); 74346492Swpaul} 74446492Swpaul 74546492Swpaulstatic int wi_seek(sc, id, off, chan) 74646492Swpaul struct wi_softc *sc; 74746492Swpaul int id, off, chan; 74846492Swpaul{ 74946492Swpaul int i; 75046492Swpaul int selreg, offreg; 75146492Swpaul 75246492Swpaul switch (chan) { 75346492Swpaul case WI_BAP0: 75446492Swpaul selreg = WI_SEL0; 75546492Swpaul offreg = WI_OFF0; 75646492Swpaul break; 75746492Swpaul case WI_BAP1: 75846492Swpaul selreg = WI_SEL1; 75946492Swpaul offreg = WI_OFF1; 76046492Swpaul break; 76146492Swpaul default: 76246492Swpaul printf("wi%d: invalid data path: %x\n", sc->wi_unit, chan); 76346492Swpaul return(EIO); 76446492Swpaul } 76546492Swpaul 76646492Swpaul CSR_WRITE_2(sc, selreg, id); 76746492Swpaul CSR_WRITE_2(sc, offreg, off); 76846492Swpaul 76946492Swpaul for (i = 0; i < WI_TIMEOUT; i++) { 77046492Swpaul if (!(CSR_READ_2(sc, offreg) & (WI_OFF_BUSY|WI_OFF_ERR))) 77146492Swpaul break; 77246492Swpaul } 77346492Swpaul 77446492Swpaul if (i == WI_TIMEOUT) 77546492Swpaul return(ETIMEDOUT); 77646492Swpaul 77746492Swpaul return(0); 77846492Swpaul} 77946492Swpaul 78046492Swpaulstatic int wi_read_data(sc, id, off, buf, len) 78146492Swpaul struct wi_softc *sc; 78246492Swpaul int id, off; 78346492Swpaul caddr_t buf; 78446492Swpaul int len; 78546492Swpaul{ 78646492Swpaul int i; 78746492Swpaul u_int16_t *ptr; 78846492Swpaul 78946492Swpaul if (wi_seek(sc, id, off, WI_BAP1)) 79046492Swpaul return(EIO); 79146492Swpaul 79246492Swpaul ptr = (u_int16_t *)buf; 79346492Swpaul for (i = 0; i < len / 2; i++) 79446492Swpaul ptr[i] = CSR_READ_2(sc, WI_DATA1); 79546492Swpaul 79646492Swpaul return(0); 79746492Swpaul} 79846492Swpaul 79946492Swpaul/* 80046492Swpaul * According to the comments in the HCF Light code, there is a bug in 80146492Swpaul * the Hermes (or possibly in certain Hermes firmware revisions) where 80246492Swpaul * the chip's internal autoincrement counter gets thrown off during 80346492Swpaul * data writes: the autoincrement is missed, causing one data word to 80446492Swpaul * be overwritten and subsequent words to be written to the wrong memory 80546492Swpaul * locations. The end result is that we could end up transmitting bogus 80646492Swpaul * frames without realizing it. The workaround for this is to write a 80746492Swpaul * couple of extra guard words after the end of the transfer, then 80846492Swpaul * attempt to read then back. If we fail to locate the guard words where 80946492Swpaul * we expect them, we preform the transfer over again. 81046492Swpaul */ 81146492Swpaulstatic int wi_write_data(sc, id, off, buf, len) 81246492Swpaul struct wi_softc *sc; 81346492Swpaul int id, off; 81446492Swpaul caddr_t buf; 81546492Swpaul int len; 81646492Swpaul{ 81746492Swpaul int i; 81846492Swpaul u_int16_t *ptr; 81946492Swpaul 82046492Swpaul#ifdef WI_HERMES_AUTOINC_WAR 82146492Swpaulagain: 82246492Swpaul#endif 82346492Swpaul 82446492Swpaul if (wi_seek(sc, id, off, WI_BAP0)) 82546492Swpaul return(EIO); 82646492Swpaul 82746492Swpaul ptr = (u_int16_t *)buf; 82846492Swpaul for (i = 0; i < (len / 2); i++) 82946492Swpaul CSR_WRITE_2(sc, WI_DATA0, ptr[i]); 83046492Swpaul 83146492Swpaul#ifdef WI_HERMES_AUTOINC_WAR 83246492Swpaul CSR_WRITE_2(sc, WI_DATA0, 0x1234); 83346492Swpaul CSR_WRITE_2(sc, WI_DATA0, 0x5678); 83446492Swpaul 83546492Swpaul if (wi_seek(sc, id, off + len, WI_BAP0)) 83646492Swpaul return(EIO); 83746492Swpaul 83846492Swpaul if (CSR_READ_2(sc, WI_DATA0) != 0x1234 || 83946492Swpaul CSR_READ_2(sc, WI_DATA0) != 0x5678) 84046492Swpaul goto again; 84146492Swpaul#endif 84246492Swpaul 84346492Swpaul return(0); 84446492Swpaul} 84546492Swpaul 84646492Swpaul/* 84746492Swpaul * Allocate a region of memory inside the NIC and zero 84846492Swpaul * it out. 84946492Swpaul */ 85046492Swpaulstatic int wi_alloc_nicmem(sc, len, id) 85146492Swpaul struct wi_softc *sc; 85246492Swpaul int len; 85346492Swpaul int *id; 85446492Swpaul{ 85546492Swpaul int i; 85646492Swpaul 85746492Swpaul if (wi_cmd(sc, WI_CMD_ALLOC_MEM, len)) { 85846492Swpaul printf("wi%d: failed to allocate %d bytes on NIC\n", 85946492Swpaul sc->wi_unit, len); 86046492Swpaul return(ENOMEM); 86146492Swpaul } 86246492Swpaul 86346492Swpaul for (i = 0; i < WI_TIMEOUT; i++) { 86446492Swpaul if (CSR_READ_2(sc, WI_EVENT_STAT) & WI_EV_ALLOC) 86546492Swpaul break; 86646492Swpaul } 86746492Swpaul 86846492Swpaul if (i == WI_TIMEOUT) 86946492Swpaul return(ETIMEDOUT); 87046492Swpaul 87146492Swpaul CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_ALLOC); 87246492Swpaul *id = CSR_READ_2(sc, WI_ALLOC_FID); 87346492Swpaul 87446492Swpaul wi_seek(sc, *id, 0, WI_BAP0); 87546492Swpaul 87646492Swpaul for (i = 0; i < len / 2; i++) 87746492Swpaul CSR_WRITE_2(sc, WI_DATA0, 0); 87846492Swpaul 87946492Swpaul return(0); 88046492Swpaul} 88146492Swpaul 88246492Swpaulstatic void wi_setmulti(sc) 88346492Swpaul struct wi_softc *sc; 88446492Swpaul{ 88546492Swpaul struct ifnet *ifp; 88646492Swpaul int i = 0; 88746492Swpaul struct ifmultiaddr *ifma; 88846492Swpaul struct wi_ltv_mcast mcast; 88946492Swpaul 89046492Swpaul ifp = &sc->arpcom.ac_if; 89146492Swpaul 89246492Swpaul bzero((char *)&mcast, sizeof(mcast)); 89346492Swpaul 89446492Swpaul mcast.wi_type = WI_RID_MCAST; 89546492Swpaul mcast.wi_len = (3 * 16) + 1; 89646492Swpaul 89746492Swpaul if (ifp->if_flags & IFF_ALLMULTI || ifp->if_flags & IFF_PROMISC) { 89846492Swpaul wi_write_record(sc, (struct wi_ltv_gen *)&mcast); 89946492Swpaul return; 90046492Swpaul } 90146492Swpaul 90246492Swpaul for (ifma = ifp->if_multiaddrs.lh_first; ifma != NULL; 90346492Swpaul ifma = ifma->ifma_link.le_next) { 90446492Swpaul if (ifma->ifma_addr->sa_family != AF_LINK) 90546492Swpaul continue; 90646492Swpaul if (i < 16) { 90746492Swpaul bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr), 90846492Swpaul (char *)&mcast.wi_mcast[i], ETHER_ADDR_LEN); 90946492Swpaul i++; 91046492Swpaul } else { 91146492Swpaul bzero((char *)&mcast, sizeof(mcast)); 91246492Swpaul break; 91346492Swpaul } 91446492Swpaul } 91546492Swpaul 91646492Swpaul mcast.wi_len = (i * 3) + 1; 91746492Swpaul wi_write_record(sc, (struct wi_ltv_gen *)&mcast); 91846492Swpaul 91946492Swpaul return; 92046492Swpaul} 92146492Swpaul 92246492Swpaulstatic void wi_setdef(sc, wreq) 92346492Swpaul struct wi_softc *sc; 92446492Swpaul struct wi_req *wreq; 92546492Swpaul{ 92646492Swpaul struct sockaddr_dl *sdl; 92746492Swpaul struct ifaddr *ifa; 92846492Swpaul struct ifnet *ifp; 92946492Swpaul 93046492Swpaul ifp = &sc->arpcom.ac_if; 93146492Swpaul 93246492Swpaul switch(wreq->wi_type) { 93346492Swpaul case WI_RID_MAC_NODE: 93446492Swpaul ifa = ifnet_addrs[ifp->if_index - 1]; 93546492Swpaul sdl = (struct sockaddr_dl *)ifa->ifa_addr; 93646492Swpaul bcopy((char *)&wreq->wi_val, (char *)&sc->arpcom.ac_enaddr, 93746492Swpaul ETHER_ADDR_LEN); 93846492Swpaul bcopy((char *)&wreq->wi_val, LLADDR(sdl), ETHER_ADDR_LEN); 93946492Swpaul break; 94046492Swpaul case WI_RID_PORTTYPE: 94146492Swpaul sc->wi_ptype = wreq->wi_val[0]; 94246492Swpaul break; 94346492Swpaul case WI_RID_TX_RATE: 94446492Swpaul sc->wi_tx_rate = wreq->wi_val[0]; 94546492Swpaul break; 94646492Swpaul case WI_RID_MAX_DATALEN: 94746492Swpaul sc->wi_max_data_len = wreq->wi_val[0]; 94846492Swpaul break; 94946492Swpaul case WI_RID_RTS_THRESH: 95046492Swpaul sc->wi_rts_thresh = wreq->wi_val[0]; 95146492Swpaul break; 95246492Swpaul case WI_RID_SYSTEM_SCALE: 95346492Swpaul sc->wi_ap_density = wreq->wi_val[0]; 95446492Swpaul break; 95546492Swpaul case WI_RID_CREATE_IBSS: 95646492Swpaul sc->wi_create_ibss = wreq->wi_val[0]; 95746492Swpaul break; 95846563Swpaul case WI_RID_OWN_CHNL: 95946563Swpaul sc->wi_channel = wreq->wi_val[0]; 96046563Swpaul break; 96146492Swpaul case WI_RID_NODENAME: 96246492Swpaul bzero(sc->wi_node_name, sizeof(sc->wi_node_name)); 96346492Swpaul bcopy((char *)&wreq->wi_val[1], sc->wi_node_name, 30); 96446492Swpaul break; 96546492Swpaul case WI_RID_DESIRED_SSID: 96646492Swpaul bzero(sc->wi_net_name, sizeof(sc->wi_net_name)); 96746492Swpaul bcopy((char *)&wreq->wi_val[1], sc->wi_net_name, 30); 96846492Swpaul break; 96946492Swpaul case WI_RID_OWN_SSID: 97046492Swpaul bzero(sc->wi_ibss_name, sizeof(sc->wi_ibss_name)); 97146492Swpaul bcopy((char *)&wreq->wi_val[1], sc->wi_ibss_name, 30); 97246492Swpaul break; 97346611Swpaul case WI_RID_PM_ENABLED: 97446611Swpaul sc->wi_pm_enabled = wreq->wi_val[0]; 97546611Swpaul break; 97646611Swpaul case WI_RID_MAX_SLEEP: 97746611Swpaul sc->wi_max_sleep = wreq->wi_val[0]; 97846611Swpaul break; 97946492Swpaul default: 98046492Swpaul break; 98146492Swpaul } 98246492Swpaul 98346563Swpaul /* Reinitialize WaveLAN. */ 98446563Swpaul wi_init(sc); 98546563Swpaul 98646492Swpaul return; 98746492Swpaul} 98846492Swpaul 98946492Swpaulstatic int wi_ioctl(ifp, command, data) 99046492Swpaul struct ifnet *ifp; 99146492Swpaul u_long command; 99246492Swpaul caddr_t data; 99346492Swpaul{ 99446492Swpaul int s, error = 0; 99546492Swpaul struct wi_softc *sc; 99646492Swpaul struct wi_req wreq; 99746492Swpaul struct ifreq *ifr; 99846492Swpaul 99946492Swpaul s = splimp(); 100046492Swpaul 100146492Swpaul sc = ifp->if_softc; 100246492Swpaul ifr = (struct ifreq *)data; 100346492Swpaul 100446492Swpaul if (sc->wi_gone) 100546492Swpaul return(ENODEV); 100646492Swpaul 100746492Swpaul switch(command) { 100846492Swpaul case SIOCSIFADDR: 100946492Swpaul case SIOCGIFADDR: 101046492Swpaul case SIOCSIFMTU: 101146492Swpaul error = ether_ioctl(ifp, command, data); 101246492Swpaul break; 101346492Swpaul case SIOCSIFFLAGS: 101446492Swpaul if (ifp->if_flags & IFF_UP) { 101546492Swpaul if (ifp->if_flags & IFF_RUNNING && 101646492Swpaul ifp->if_flags & IFF_PROMISC && 101746492Swpaul !(sc->wi_if_flags & IFF_PROMISC)) { 101846492Swpaul WI_SETVAL(WI_RID_PROMISC, 1); 101946492Swpaul } else if (ifp->if_flags & IFF_RUNNING && 102046492Swpaul !(ifp->if_flags & IFF_PROMISC) && 102146492Swpaul sc->wi_if_flags & IFF_PROMISC) { 102246492Swpaul WI_SETVAL(WI_RID_PROMISC, 0); 102346492Swpaul } else 102446492Swpaul wi_init(sc); 102546492Swpaul } else { 102646492Swpaul if (ifp->if_flags & IFF_RUNNING) { 102746492Swpaul wi_stop(sc); 102846492Swpaul } 102946492Swpaul } 103046492Swpaul sc->wi_if_flags = ifp->if_flags; 103146492Swpaul error = 0; 103246492Swpaul break; 103346492Swpaul case SIOCADDMULTI: 103446492Swpaul case SIOCDELMULTI: 103546492Swpaul wi_setmulti(sc); 103646492Swpaul error = 0; 103746492Swpaul break; 103846492Swpaul case SIOCGWAVELAN: 103946492Swpaul error = copyin(ifr->ifr_data, &wreq, sizeof(wreq)); 104046492Swpaul if (error) 104146492Swpaul break; 104246492Swpaul if (wreq.wi_type == WI_RID_IFACE_STATS) { 104346492Swpaul bcopy((char *)&sc->wi_stats, (char *)&wreq.wi_val, 104446492Swpaul sizeof(sc->wi_stats)); 104546492Swpaul wreq.wi_len = (sizeof(sc->wi_stats) / 2) + 1; 104646492Swpaul } else { 104746492Swpaul if (wi_read_record(sc, (struct wi_ltv_gen *)&wreq)) { 104846492Swpaul error = EINVAL; 104946492Swpaul break; 105046492Swpaul } 105146492Swpaul } 105246492Swpaul error = copyout(&wreq, ifr->ifr_data, sizeof(wreq)); 105346492Swpaul break; 105446492Swpaul case SIOCSWAVELAN: 105546492Swpaul error = copyin(ifr->ifr_data, &wreq, sizeof(wreq)); 105646492Swpaul if (error) 105746492Swpaul break; 105846492Swpaul if (wreq.wi_type == WI_RID_IFACE_STATS) { 105946492Swpaul error = EINVAL; 106046492Swpaul break; 106146492Swpaul } else if (wreq.wi_type == WI_RID_MGMT_XMIT) { 106246492Swpaul error = wi_mgmt_xmit(sc, (caddr_t)&wreq.wi_val, 106346492Swpaul wreq.wi_len); 106446492Swpaul } else { 106546492Swpaul error = wi_write_record(sc, (struct wi_ltv_gen *)&wreq); 106646492Swpaul if (!error) 106746492Swpaul wi_setdef(sc, &wreq); 106846492Swpaul } 106946492Swpaul break; 107046492Swpaul default: 107146492Swpaul error = EINVAL; 107246492Swpaul break; 107346492Swpaul } 107446492Swpaul 107546492Swpaul splx(s); 107646492Swpaul 107746492Swpaul return(error); 107846492Swpaul} 107946492Swpaul 108046492Swpaulstatic void wi_init(xsc) 108146492Swpaul void *xsc; 108246492Swpaul{ 108346492Swpaul struct wi_softc *sc = xsc; 108446492Swpaul struct ifnet *ifp = &sc->arpcom.ac_if; 108546492Swpaul int s; 108646492Swpaul struct wi_ltv_macaddr mac; 108746492Swpaul int id = 0; 108846492Swpaul 108946492Swpaul if (sc->wi_gone) 109046492Swpaul return; 109146492Swpaul 109246492Swpaul s = splimp(); 109346492Swpaul 109446492Swpaul if (ifp->if_flags & IFF_RUNNING) 109546492Swpaul wi_stop(sc); 109646492Swpaul 109746492Swpaul wi_reset(sc); 109846492Swpaul 109946492Swpaul /* Program max data length. */ 110046492Swpaul WI_SETVAL(WI_RID_MAX_DATALEN, sc->wi_max_data_len); 110146492Swpaul 110247401Swpaul /* Enable/disable IBSS creation. */ 110346492Swpaul WI_SETVAL(WI_RID_CREATE_IBSS, sc->wi_create_ibss); 110446492Swpaul 110546492Swpaul /* Set the port type. */ 110646492Swpaul WI_SETVAL(WI_RID_PORTTYPE, sc->wi_ptype); 110746492Swpaul 110846492Swpaul /* Program the RTS/CTS threshold. */ 110946492Swpaul WI_SETVAL(WI_RID_RTS_THRESH, sc->wi_rts_thresh); 111046492Swpaul 111146492Swpaul /* Program the TX rate */ 111246492Swpaul WI_SETVAL(WI_RID_TX_RATE, sc->wi_tx_rate); 111346492Swpaul 111446492Swpaul /* Access point density */ 111546492Swpaul WI_SETVAL(WI_RID_SYSTEM_SCALE, sc->wi_ap_density); 111646492Swpaul 111746611Swpaul /* Power Management Enabled */ 111846611Swpaul WI_SETVAL(WI_RID_PM_ENABLED, sc->wi_pm_enabled); 111946611Swpaul 112046611Swpaul /* Power Managment Max Sleep */ 112146611Swpaul WI_SETVAL(WI_RID_MAX_SLEEP, sc->wi_max_sleep); 112246611Swpaul 112346492Swpaul /* Specify the IBSS name */ 112446492Swpaul WI_SETSTR(WI_RID_OWN_SSID, sc->wi_ibss_name); 112546492Swpaul 112646492Swpaul /* Specify the network name */ 112746492Swpaul WI_SETSTR(WI_RID_DESIRED_SSID, sc->wi_net_name); 112846492Swpaul 112946563Swpaul /* Specify the frequency to use */ 113046563Swpaul WI_SETVAL(WI_RID_OWN_CHNL, sc->wi_channel); 113146563Swpaul 113246492Swpaul /* Program the nodename. */ 113346492Swpaul WI_SETSTR(WI_RID_NODENAME, sc->wi_node_name); 113446492Swpaul 113546492Swpaul /* Set our MAC address. */ 113646492Swpaul mac.wi_len = 4; 113746492Swpaul mac.wi_type = WI_RID_MAC_NODE; 113846492Swpaul bcopy((char *)&sc->arpcom.ac_enaddr, 113946492Swpaul (char *)&mac.wi_mac_addr, ETHER_ADDR_LEN); 114046492Swpaul wi_write_record(sc, (struct wi_ltv_gen *)&mac); 114146492Swpaul 114246492Swpaul /* Initialize promisc mode. */ 114346492Swpaul if (ifp->if_flags & IFF_PROMISC) { 114446492Swpaul WI_SETVAL(WI_RID_PROMISC, 1); 114546492Swpaul } else { 114646492Swpaul WI_SETVAL(WI_RID_PROMISC, 0); 114746492Swpaul } 114846492Swpaul 114946492Swpaul /* Set multicast filter. */ 115046492Swpaul wi_setmulti(sc); 115146492Swpaul 115246492Swpaul /* Enable desired port */ 115346492Swpaul wi_cmd(sc, WI_CMD_ENABLE|sc->wi_portnum, 0); 115446492Swpaul 115546492Swpaul if (wi_alloc_nicmem(sc, 1518 + sizeof(struct wi_frame) + 8, &id)) 115646495Swpaul printf("wi%d: tx buffer allocation failed\n", sc->wi_unit); 115746492Swpaul sc->wi_tx_data_id = id; 115846492Swpaul 115946492Swpaul if (wi_alloc_nicmem(sc, 1518 + sizeof(struct wi_frame) + 8, &id)) 116046495Swpaul printf("wi%d: mgmt. buffer allocation failed\n", sc->wi_unit); 116146492Swpaul sc->wi_tx_mgmt_id = id; 116246492Swpaul 116346492Swpaul /* enable interrupts */ 116446492Swpaul CSR_WRITE_2(sc, WI_INT_EN, WI_INTRS); 116546492Swpaul 116646492Swpaul splx(s); 116746492Swpaul 116846492Swpaul ifp->if_flags |= IFF_RUNNING; 116946492Swpaul ifp->if_flags &= ~IFF_OACTIVE; 117046492Swpaul 117146492Swpaul sc->wi_stat_ch = timeout(wi_inquire, sc, hz * 60); 117246492Swpaul 117346492Swpaul return; 117446492Swpaul} 117546492Swpaul 117646492Swpaulstatic void wi_start(ifp) 117746492Swpaul struct ifnet *ifp; 117846492Swpaul{ 117946492Swpaul struct wi_softc *sc; 118046492Swpaul struct mbuf *m0; 118146492Swpaul struct wi_frame tx_frame; 118246492Swpaul struct ether_header *eh; 118346492Swpaul int id; 118446492Swpaul 118546492Swpaul sc = ifp->if_softc; 118646492Swpaul 118746492Swpaul if (sc->wi_gone) 118846492Swpaul return; 118946492Swpaul 119046492Swpaul if (ifp->if_flags & IFF_OACTIVE) 119146492Swpaul return; 119246492Swpaul 119346492Swpaul IF_DEQUEUE(&ifp->if_snd, m0); 119446492Swpaul if (m0 == NULL) 119546492Swpaul return; 119646492Swpaul 119746492Swpaul bzero((char *)&tx_frame, sizeof(tx_frame)); 119846492Swpaul id = sc->wi_tx_data_id; 119946492Swpaul eh = mtod(m0, struct ether_header *); 120046492Swpaul 120146492Swpaul /* 120247401Swpaul * Use RFC1042 encoding for IP and ARP datagrams, 120346492Swpaul * 802.3 for anything else. 120446492Swpaul */ 120546492Swpaul if (ntohs(eh->ether_type) == ETHERTYPE_IP || 120646492Swpaul ntohs(eh->ether_type) == ETHERTYPE_ARP || 120746492Swpaul ntohs(eh->ether_type) == ETHERTYPE_REVARP) { 120846492Swpaul bcopy((char *)&eh->ether_dhost, 120946492Swpaul (char *)&tx_frame.wi_addr1, ETHER_ADDR_LEN); 121046492Swpaul bcopy((char *)&eh->ether_shost, 121146492Swpaul (char *)&tx_frame.wi_addr2, ETHER_ADDR_LEN); 121246492Swpaul bcopy((char *)&eh->ether_dhost, 121346492Swpaul (char *)&tx_frame.wi_dst_addr, ETHER_ADDR_LEN); 121446492Swpaul bcopy((char *)&eh->ether_shost, 121546492Swpaul (char *)&tx_frame.wi_src_addr, ETHER_ADDR_LEN); 121646492Swpaul 121746492Swpaul tx_frame.wi_dat_len = m0->m_pkthdr.len - WI_SNAPHDR_LEN; 121846492Swpaul tx_frame.wi_frame_ctl = WI_FTYPE_DATA; 121946492Swpaul tx_frame.wi_dat[0] = htons(WI_SNAP_WORD0); 122046492Swpaul tx_frame.wi_dat[1] = htons(WI_SNAP_WORD1); 122146492Swpaul tx_frame.wi_len = htons(m0->m_pkthdr.len - WI_SNAPHDR_LEN); 122246492Swpaul tx_frame.wi_type = eh->ether_type; 122346492Swpaul 122446492Swpaul m_copydata(m0, sizeof(struct ether_header), 122546492Swpaul m0->m_pkthdr.len - sizeof(struct ether_header), 122646492Swpaul (caddr_t)&sc->wi_txbuf); 122746492Swpaul 122846492Swpaul wi_write_data(sc, id, 0, (caddr_t)&tx_frame, 122946492Swpaul sizeof(struct wi_frame)); 123046492Swpaul wi_write_data(sc, id, WI_802_11_OFFSET, (caddr_t)&sc->wi_txbuf, 123146492Swpaul (m0->m_pkthdr.len - sizeof(struct ether_header)) + 2); 123246492Swpaul } else { 123346492Swpaul tx_frame.wi_dat_len = m0->m_pkthdr.len; 123446492Swpaul 123546492Swpaul m_copydata(m0, 0, m0->m_pkthdr.len, (caddr_t)&sc->wi_txbuf); 123646492Swpaul 123746492Swpaul wi_write_data(sc, id, 0, (caddr_t)&tx_frame, 123846492Swpaul sizeof(struct wi_frame)); 123946492Swpaul wi_write_data(sc, id, WI_802_3_OFFSET, (caddr_t)&sc->wi_txbuf, 124046492Swpaul m0->m_pkthdr.len + 2); 124146492Swpaul } 124246492Swpaul 124346492Swpaul#if NBPFILTER > 0 124446492Swpaul /* 124546492Swpaul * If there's a BPF listner, bounce a copy of 124646492Swpaul * this frame to him. 124746492Swpaul */ 124846492Swpaul if (ifp->if_bpf) 124946492Swpaul bpf_mtap(ifp, m0); 125046492Swpaul#endif 125146492Swpaul 125246492Swpaul m_freem(m0); 125346492Swpaul 125446492Swpaul if (wi_cmd(sc, WI_CMD_TX|WI_RECLAIM, id)) 125546492Swpaul printf("wi%d: xmit failed\n", sc->wi_unit); 125646492Swpaul 125746492Swpaul ifp->if_flags |= IFF_OACTIVE; 125846492Swpaul 125946492Swpaul /* 126046492Swpaul * Set a timeout in case the chip goes out to lunch. 126146492Swpaul */ 126246492Swpaul ifp->if_timer = 5; 126346492Swpaul 126446492Swpaul return; 126546492Swpaul} 126646492Swpaul 126746492Swpaulstatic int wi_mgmt_xmit(sc, data, len) 126846492Swpaul struct wi_softc *sc; 126946492Swpaul caddr_t data; 127046492Swpaul int len; 127146492Swpaul{ 127246492Swpaul struct wi_frame tx_frame; 127346492Swpaul int id; 127446492Swpaul struct wi_80211_hdr *hdr; 127546492Swpaul caddr_t dptr; 127646492Swpaul 127746492Swpaul if (sc->wi_gone) 127846492Swpaul return(ENODEV); 127946492Swpaul 128046492Swpaul hdr = (struct wi_80211_hdr *)data; 128146492Swpaul dptr = data + sizeof(struct wi_80211_hdr); 128246492Swpaul 128346492Swpaul bzero((char *)&tx_frame, sizeof(tx_frame)); 128446492Swpaul id = sc->wi_tx_mgmt_id; 128546492Swpaul 128646492Swpaul bcopy((char *)hdr, (char *)&tx_frame.wi_frame_ctl, 128746492Swpaul sizeof(struct wi_80211_hdr)); 128846492Swpaul 128946492Swpaul tx_frame.wi_dat_len = len - WI_SNAPHDR_LEN; 129046492Swpaul tx_frame.wi_len = htons(len - WI_SNAPHDR_LEN); 129146492Swpaul 129246492Swpaul wi_write_data(sc, id, 0, (caddr_t)&tx_frame, sizeof(struct wi_frame)); 129346492Swpaul wi_write_data(sc, id, WI_802_11_OFFSET_RAW, dptr, 129446492Swpaul (len - sizeof(struct wi_80211_hdr)) + 2); 129546492Swpaul 129646492Swpaul if (wi_cmd(sc, WI_CMD_TX|WI_RECLAIM, id)) { 129746492Swpaul printf("wi%d: xmit failed\n", sc->wi_unit); 129846492Swpaul return(EIO); 129946492Swpaul } 130046492Swpaul 130146492Swpaul return(0); 130246492Swpaul} 130346492Swpaul 130446492Swpaulstatic void wi_stop(sc) 130546492Swpaul struct wi_softc *sc; 130646492Swpaul{ 130746492Swpaul struct ifnet *ifp; 130846492Swpaul 130946492Swpaul if (sc->wi_gone) 131046492Swpaul return; 131146492Swpaul 131246492Swpaul ifp = &sc->arpcom.ac_if; 131346492Swpaul 131446492Swpaul CSR_WRITE_2(sc, WI_INT_EN, 0); 131546492Swpaul wi_cmd(sc, WI_CMD_DISABLE|sc->wi_portnum, 0); 131646492Swpaul 131746492Swpaul untimeout(wi_inquire, sc, sc->wi_stat_ch); 131846492Swpaul 131946492Swpaul ifp->if_flags &= ~(IFF_RUNNING|IFF_OACTIVE); 132046492Swpaul 132146492Swpaul return; 132246492Swpaul} 132346492Swpaul 132446492Swpaulstatic void wi_watchdog(ifp) 132546492Swpaul struct ifnet *ifp; 132646492Swpaul{ 132746492Swpaul struct wi_softc *sc; 132846492Swpaul 132946492Swpaul sc = ifp->if_softc; 133046492Swpaul 133146492Swpaul printf("wi%d: device timeout\n", sc->wi_unit); 133246492Swpaul 133346492Swpaul wi_init(sc); 133446492Swpaul 133546492Swpaul ifp->if_oerrors++; 133646492Swpaul 133746492Swpaul return; 133846492Swpaul} 133946492Swpaul 134046492Swpaulstatic void wi_shutdown(howto, arg) 134146492Swpaul int howto; 134246492Swpaul void *arg; 134346492Swpaul{ 134446492Swpaul struct wi_softc *sc; 134546492Swpaul 134646492Swpaul sc = arg; 134746492Swpaul wi_stop(sc); 134846492Swpaul 134946492Swpaul return; 135046492Swpaul} 1351