if_wi.c revision 46563
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 * 3246563Swpaul * $Id: if_wi.c,v 1.52 1999/05/06 16:28:02 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/* 4446492Swpaul * 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 5246492Swpaul * a) extremely gross, b) lacks certain fearures, 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[] = 11946563Swpaul "$Id: if_wi.c,v 1.52 1999/05/06 16:28:02 wpaul Exp $"; 12046492Swpaul#endif 12146492Swpaul 12246492Swpaulstatic struct wi_softc wi_softc[NWI]; 12346492Swpaul 12446492Swpaul#ifdef foo 12546492Swpaulstatic u_int8_t wi_mcast_addr[6] = { 0x00, 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: 26546492Swpaul * it's a PCMCIA card plugged into a PCMCIA expander card 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; 33346492Swpaul 33446563Swpaul /* 33546563Swpaul * Read the default channel from the NIC. This may vary 33646563Swpaul * depending on the country where the NIC was purchased, so 33746563Swpaul * we can't hard-code a default and expect it to work for 33846563Swpaul * everyone. 33946563Swpaul */ 34046563Swpaul gen.wi_type = WI_RID_OWN_CHNL; 34146563Swpaul gen.wi_len = 2; 34246563Swpaul wi_read_record(sc, &gen); 34346563Swpaul sc->wi_channel = gen.wi_val; 34446563Swpaul 34546492Swpaul bzero((char *)&sc->wi_stats, sizeof(sc->wi_stats)); 34646492Swpaul 34746492Swpaul wi_init(sc); 34846492Swpaul wi_stop(sc); 34946492Swpaul 35046492Swpaul /* 35146492Swpaul * If this logical interface has already been attached, 35246492Swpaul * don't attach it again or chaos will ensue. 35346492Swpaul */ 35446492Swpaul sprintf(ifname, "wi%d", sc->wi_unit); 35546492Swpaul 35646492Swpaul if (ifunit(ifname) == NULL) { 35746492Swpaul callout_handle_init(&sc->wi_stat_ch); 35846492Swpaul /* 35946492Swpaul * Call MI attach routines. 36046492Swpaul */ 36146492Swpaul if_attach(ifp); 36246492Swpaul ether_ifattach(ifp); 36346492Swpaul 36446492Swpaul#if NBPFILTER > 0 36546492Swpaul bpfattach(ifp, DLT_EN10MB, sizeof(struct ether_header)); 36646492Swpaul#endif 36746492Swpaul 36846492Swpaul at_shutdown(wi_shutdown, sc, SHUTDOWN_POST_SYNC); 36946492Swpaul } 37046492Swpaul 37146492Swpaul return(0); 37246492Swpaul} 37346492Swpaul 37446492Swpaulstatic void wi_rxeof(sc) 37546492Swpaul struct wi_softc *sc; 37646492Swpaul{ 37746492Swpaul struct ifnet *ifp; 37846492Swpaul struct ether_header *eh; 37946492Swpaul struct wi_frame rx_frame; 38046492Swpaul struct mbuf *m; 38146492Swpaul int id; 38246492Swpaul 38346492Swpaul ifp = &sc->arpcom.ac_if; 38446492Swpaul 38546492Swpaul id = CSR_READ_2(sc, WI_RX_FID); 38646492Swpaul 38746492Swpaul /* First read in the frame header */ 38846492Swpaul if (wi_read_data(sc, id, 0, (caddr_t)&rx_frame, sizeof(rx_frame))) { 38946492Swpaul ifp->if_ierrors++; 39046492Swpaul return; 39146492Swpaul } 39246492Swpaul 39346492Swpaul if (rx_frame.wi_status & WI_STAT_ERRSTAT) { 39446492Swpaul ifp->if_ierrors++; 39546492Swpaul return; 39646492Swpaul } 39746492Swpaul 39846492Swpaul MGETHDR(m, M_DONTWAIT, MT_DATA); 39946492Swpaul if (m == NULL) { 40046492Swpaul ifp->if_ierrors++; 40146492Swpaul return; 40246492Swpaul } 40346492Swpaul MCLGET(m, M_DONTWAIT); 40446492Swpaul if (!(m->m_flags & M_EXT)) { 40546492Swpaul m_freem(m); 40646492Swpaul ifp->if_ierrors++; 40746492Swpaul return; 40846492Swpaul } 40946492Swpaul 41046492Swpaul eh = mtod(m, struct ether_header *); 41146492Swpaul m->m_pkthdr.rcvif = ifp; 41246492Swpaul 41346492Swpaul if (rx_frame.wi_status == WI_STAT_1042 || 41446492Swpaul rx_frame.wi_status == WI_STAT_TUNNEL || 41546492Swpaul rx_frame.wi_status == WI_STAT_WMP_MSG) { 41646492Swpaul m->m_pkthdr.len = m->m_len = 41746492Swpaul rx_frame.wi_dat_len + WI_SNAPHDR_LEN; 41846492Swpaul 41946492Swpaul bcopy((char *)&rx_frame.wi_addr1, 42046492Swpaul (char *)&eh->ether_dhost, ETHER_ADDR_LEN); 42146492Swpaul bcopy((char *)&rx_frame.wi_addr2, 42246492Swpaul (char *)&eh->ether_shost, ETHER_ADDR_LEN); 42346492Swpaul bcopy((char *)&rx_frame.wi_type, 42446492Swpaul (char *)&eh->ether_type, sizeof(u_int16_t)); 42546492Swpaul 42646492Swpaul if (wi_read_data(sc, id, WI_802_11_OFFSET, 42746492Swpaul mtod(m, caddr_t) + sizeof(struct ether_header), 42846492Swpaul m->m_len + 2)) { 42946492Swpaul m_freem(m); 43046492Swpaul ifp->if_ierrors++; 43146492Swpaul return; 43246492Swpaul } 43346492Swpaul } else { 43446492Swpaul m->m_pkthdr.len = m->m_len = 43546492Swpaul rx_frame.wi_dat_len + sizeof(struct ether_header); 43646492Swpaul 43746492Swpaul if (wi_read_data(sc, id, WI_802_3_OFFSET, 43846492Swpaul mtod(m, caddr_t), m->m_len + 2)) { 43946492Swpaul m_freem(m); 44046492Swpaul ifp->if_ierrors++; 44146492Swpaul return; 44246492Swpaul } 44346492Swpaul } 44446492Swpaul 44546492Swpaul ifp->if_ipackets++; 44646492Swpaul 44746492Swpaul#if NBPFILTER > 0 44846492Swpaul /* Handle BPF listeners. */ 44946492Swpaul if (ifp->if_bpf) { 45046492Swpaul bpf_mtap(ifp, m); 45146492Swpaul if (ifp->if_flags & IFF_PROMISC && 45246492Swpaul (bcmp(eh->ether_dhost, sc->arpcom.ac_enaddr, 45346492Swpaul ETHER_ADDR_LEN) && (eh->ether_dhost[0] & 1) == 0)) { 45446492Swpaul m_freem(m); 45546492Swpaul return; 45646492Swpaul } 45746492Swpaul } 45846492Swpaul#endif 45946492Swpaul 46046492Swpaul /* Receive packet. */ 46146492Swpaul m_adj(m, sizeof(struct ether_header)); 46246492Swpaul ether_input(ifp, eh, m); 46346492Swpaul 46446492Swpaul return; 46546492Swpaul} 46646492Swpaul 46746492Swpaulstatic void wi_txeof(sc, status) 46846492Swpaul struct wi_softc *sc; 46946492Swpaul int status; 47046492Swpaul{ 47146492Swpaul struct ifnet *ifp; 47246492Swpaul 47346492Swpaul ifp = &sc->arpcom.ac_if; 47446492Swpaul 47546492Swpaul ifp->if_timer = 0; 47646492Swpaul ifp->if_flags &= ~IFF_OACTIVE; 47746492Swpaul 47846492Swpaul if (status & WI_EV_TX_EXC) 47946492Swpaul ifp->if_oerrors++; 48046492Swpaul else 48146492Swpaul ifp->if_opackets++; 48246492Swpaul 48346492Swpaul return; 48446492Swpaul} 48546492Swpaul 48646492Swpaulvoid wi_inquire(xsc) 48746492Swpaul void *xsc; 48846492Swpaul{ 48946492Swpaul struct wi_softc *sc; 49046492Swpaul struct ifnet *ifp; 49146492Swpaul 49246492Swpaul sc = xsc; 49346492Swpaul ifp = &sc->arpcom.ac_if; 49446492Swpaul 49546492Swpaul sc->wi_stat_ch = timeout(wi_inquire, sc, hz * 60); 49646492Swpaul 49746492Swpaul /* Don't do this while we're transmitting */ 49846492Swpaul if (ifp->if_flags & IFF_OACTIVE) 49946492Swpaul return; 50046492Swpaul 50146492Swpaul wi_cmd(sc, WI_CMD_INQUIRE, WI_INFO_COUNTERS); 50246492Swpaul 50346492Swpaul return; 50446492Swpaul} 50546492Swpaul 50646492Swpaulvoid wi_update_stats(sc) 50746492Swpaul struct wi_softc *sc; 50846492Swpaul{ 50946492Swpaul struct wi_ltv_gen gen; 51046492Swpaul u_int16_t id; 51146492Swpaul struct ifnet *ifp; 51246492Swpaul u_int32_t *ptr; 51346492Swpaul int i; 51446492Swpaul u_int16_t t; 51546492Swpaul 51646492Swpaul ifp = &sc->arpcom.ac_if; 51746492Swpaul 51846492Swpaul id = CSR_READ_2(sc, WI_INFO_FID); 51946492Swpaul 52046492Swpaul wi_read_data(sc, id, 0, (char *)&gen, 4); 52146492Swpaul 52246492Swpaul if (gen.wi_type != WI_INFO_COUNTERS || 52346492Swpaul gen.wi_len > (sizeof(sc->wi_stats) / 4) + 1) 52446492Swpaul return; 52546492Swpaul 52646492Swpaul ptr = (u_int32_t *)&sc->wi_stats; 52746492Swpaul 52846492Swpaul for (i = 0; i < gen.wi_len - 1; i++) { 52946492Swpaul t = CSR_READ_2(sc, WI_DATA1); 53046492Swpaul#ifdef WI_HERMES_STATS_WAR 53146492Swpaul if (t > 0xF000) 53246492Swpaul t = ~t & 0xFFFF; 53346492Swpaul#endif 53446492Swpaul ptr[i] += t; 53546492Swpaul } 53646492Swpaul 53746492Swpaul ifp->if_collisions = sc->wi_stats.wi_tx_single_retries + 53846492Swpaul sc->wi_stats.wi_tx_multi_retries + 53946492Swpaul sc->wi_stats.wi_tx_retry_limit; 54046492Swpaul 54146492Swpaul return; 54246492Swpaul} 54346492Swpaul 54446492Swpaulvoid wi_intr(unit) 54546492Swpaul int unit; 54646492Swpaul{ 54746492Swpaul struct wi_softc *sc; 54846492Swpaul struct ifnet *ifp; 54946492Swpaul u_int16_t status; 55046492Swpaul 55146492Swpaul sc = &wi_softc[unit]; 55246492Swpaul ifp = &sc->arpcom.ac_if; 55346492Swpaul 55446492Swpaul if (!(ifp->if_flags & IFF_UP)) { 55546492Swpaul CSR_WRITE_2(sc, WI_EVENT_ACK, 0xFFFF); 55646492Swpaul CSR_WRITE_2(sc, WI_INT_EN, 0); 55746492Swpaul return; 55846492Swpaul } 55946492Swpaul 56046492Swpaul /* Disable interrupts. */ 56146492Swpaul CSR_WRITE_2(sc, WI_INT_EN, 0); 56246492Swpaul 56346492Swpaul status = CSR_READ_2(sc, WI_EVENT_STAT); 56446492Swpaul CSR_WRITE_2(sc, WI_EVENT_ACK, ~WI_INTRS); 56546492Swpaul 56646492Swpaul if (status & WI_EV_RX) { 56746492Swpaul wi_rxeof(sc); 56846492Swpaul CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_RX); 56946492Swpaul } 57046492Swpaul 57146492Swpaul if (status & WI_EV_TX) { 57246492Swpaul wi_txeof(sc, status); 57346492Swpaul CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_TX); 57446492Swpaul } 57546492Swpaul 57646492Swpaul if (status & WI_EV_ALLOC) { 57746492Swpaul int id; 57846492Swpaul id = CSR_READ_2(sc, WI_ALLOC_FID); 57946492Swpaul CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_ALLOC); 58046492Swpaul if (id == sc->wi_tx_data_id) 58146492Swpaul wi_txeof(sc, status); 58246492Swpaul } 58346492Swpaul 58446492Swpaul if (status & WI_EV_INFO) { 58546492Swpaul wi_update_stats(sc); 58646492Swpaul CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_INFO); 58746492Swpaul } 58846492Swpaul 58946492Swpaul if (status & WI_EV_TX_EXC) { 59046492Swpaul wi_txeof(sc, status); 59146492Swpaul CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_TX_EXC); 59246492Swpaul } 59346492Swpaul 59446492Swpaul if (status & WI_EV_INFO_DROP) { 59546492Swpaul CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_INFO_DROP); 59646492Swpaul } 59746492Swpaul 59846492Swpaul /* Re-enable interrupts. */ 59946492Swpaul CSR_WRITE_2(sc, WI_INT_EN, WI_INTRS); 60046492Swpaul 60146492Swpaul if (ifp->if_snd.ifq_head != NULL) 60246492Swpaul wi_start(ifp); 60346492Swpaul 60446492Swpaul return; 60546492Swpaul} 60646492Swpaul 60746492Swpaulstatic int wi_cmd(sc, cmd, val) 60846492Swpaul struct wi_softc *sc; 60946492Swpaul int cmd; 61046492Swpaul int val; 61146492Swpaul{ 61246492Swpaul int i, s = 0; 61346492Swpaul 61446492Swpaul CSR_WRITE_2(sc, WI_PARAM0, val); 61546492Swpaul CSR_WRITE_2(sc, WI_COMMAND, cmd); 61646492Swpaul 61746492Swpaul for (i = 0; i < WI_TIMEOUT; i++) { 61846492Swpaul /* 61946492Swpaul * Wait for 'command complete' bit to be 62046492Swpaul * set in the event status register. 62146492Swpaul */ 62246492Swpaul s = CSR_READ_2(sc, WI_EVENT_STAT) & WI_EV_CMD; 62346492Swpaul if (s) { 62446492Swpaul /* Ack the event and read result code. */ 62546492Swpaul s = CSR_READ_2(sc, WI_STATUS); 62646492Swpaul CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_CMD); 62746492Swpaul#ifdef foo 62846492Swpaul if ((s & WI_CMD_CODE_MASK) != (cmd & WI_CMD_CODE_MASK)) 62946492Swpaul return(EIO); 63046492Swpaul#endif 63146492Swpaul if (s & WI_STAT_CMD_RESULT) 63246492Swpaul return(EIO); 63346492Swpaul break; 63446492Swpaul } 63546492Swpaul } 63646492Swpaul 63746492Swpaul if (i == WI_TIMEOUT) 63846492Swpaul return(ETIMEDOUT); 63946492Swpaul 64046492Swpaul return(0); 64146492Swpaul} 64246492Swpaul 64346492Swpaulstatic void wi_reset(sc) 64446492Swpaul struct wi_softc *sc; 64546492Swpaul{ 64646492Swpaul if (wi_cmd(sc, WI_CMD_INI, 0)) 64746492Swpaul printf("wi%d: init failed\n", sc->wi_unit); 64846492Swpaul CSR_WRITE_2(sc, WI_INT_EN, 0); 64946492Swpaul CSR_WRITE_2(sc, WI_EVENT_ACK, 0xFFFF); 65046492Swpaul 65146492Swpaul /* Calibrate timer. */ 65246492Swpaul WI_SETVAL(WI_RID_TICK_TIME, 8); 65346492Swpaul 65446492Swpaul return; 65546492Swpaul} 65646492Swpaul 65746492Swpaul/* 65846492Swpaul * Read an LTV record from the NIC. 65946492Swpaul */ 66046492Swpaulstatic int wi_read_record(sc, ltv) 66146492Swpaul struct wi_softc *sc; 66246492Swpaul struct wi_ltv_gen *ltv; 66346492Swpaul{ 66446492Swpaul u_int16_t *ptr; 66546492Swpaul int i, len, code; 66646492Swpaul 66746492Swpaul /* Tell the NIC to enter record read mode. */ 66846492Swpaul if (wi_cmd(sc, WI_CMD_ACCESS|WI_ACCESS_READ, ltv->wi_type)) 66946492Swpaul return(EIO); 67046492Swpaul 67146492Swpaul /* Select the record we want to read. */ 67246492Swpaul CSR_WRITE_2(sc, WI_SEL1, ltv->wi_type); 67346492Swpaul 67446492Swpaul /* Specify offset -- we always read the whole record. */ 67546492Swpaul CSR_WRITE_2(sc, WI_OFF1, 0); 67646492Swpaul 67746492Swpaul /* Wait for NIC to acknowledge */ 67846492Swpaul for (i = 0; i < WI_TIMEOUT; i++) { 67946492Swpaul if (!(CSR_READ_2(sc, WI_OFF1) & (WI_OFF_BUSY|WI_OFF_ERR))) 68046492Swpaul break; 68146492Swpaul } 68246492Swpaul 68346492Swpaul if (i == WI_TIMEOUT) 68446492Swpaul return(ETIMEDOUT); 68546492Swpaul 68646492Swpaul /* 68746492Swpaul * Read the length and record type and make sure they 68846492Swpaul * match what we expect (this verifies that we have enough 68946492Swpaul * room to hold all of the returned data. 69046492Swpaul */ 69146492Swpaul len = CSR_READ_2(sc, WI_DATA1); 69246492Swpaul if (len > ltv->wi_len) 69346492Swpaul return(ENOSPC); 69446492Swpaul code = CSR_READ_2(sc, WI_DATA1); 69546492Swpaul if (code != ltv->wi_type) 69646492Swpaul return(EIO); 69746492Swpaul 69846492Swpaul ltv->wi_len = len; 69946492Swpaul ltv->wi_type = code; 70046492Swpaul 70146492Swpaul /* Now read the data. */ 70246492Swpaul ptr = <v->wi_val; 70346492Swpaul for (i = 0; i < ltv->wi_len - 1; i++) 70446492Swpaul ptr[i] = CSR_READ_2(sc, WI_DATA1); 70546492Swpaul 70646492Swpaul return(0); 70746492Swpaul} 70846492Swpaul 70946492Swpaul/* 71046492Swpaul * Same as read, except we inject data instead of reading it. 71146492Swpaul */ 71246492Swpaulstatic int wi_write_record(sc, ltv) 71346492Swpaul struct wi_softc *sc; 71446492Swpaul struct wi_ltv_gen *ltv; 71546492Swpaul{ 71646492Swpaul u_int16_t *ptr; 71746492Swpaul int i; 71846492Swpaul 71946492Swpaul CSR_WRITE_2(sc, WI_SEL1, ltv->wi_type); 72046492Swpaul CSR_WRITE_2(sc, WI_OFF1, 0); 72146492Swpaul 72246492Swpaul for (i = 0; i < WI_TIMEOUT; i++) { 72346492Swpaul if (!(CSR_READ_2(sc, WI_OFF1) & (WI_OFF_BUSY|WI_OFF_ERR))) 72446492Swpaul break; 72546492Swpaul } 72646492Swpaul 72746492Swpaul if (i == WI_TIMEOUT) 72846492Swpaul return(ETIMEDOUT); 72946492Swpaul 73046492Swpaul CSR_WRITE_2(sc, WI_DATA1, ltv->wi_len); 73146492Swpaul CSR_WRITE_2(sc, WI_DATA1, ltv->wi_type); 73246492Swpaul 73346492Swpaul ptr = <v->wi_val; 73446492Swpaul for (i = 0; i < ltv->wi_len - 1; i++) 73546492Swpaul CSR_WRITE_2(sc, WI_DATA1, ptr[i]); 73646492Swpaul 73746492Swpaul if (wi_cmd(sc, WI_CMD_ACCESS|WI_ACCESS_WRITE, ltv->wi_type)) 73846492Swpaul return(EIO); 73946492Swpaul 74046492Swpaul return(0); 74146492Swpaul} 74246492Swpaul 74346492Swpaulstatic int wi_seek(sc, id, off, chan) 74446492Swpaul struct wi_softc *sc; 74546492Swpaul int id, off, chan; 74646492Swpaul{ 74746492Swpaul int i; 74846492Swpaul int selreg, offreg; 74946492Swpaul 75046492Swpaul switch (chan) { 75146492Swpaul case WI_BAP0: 75246492Swpaul selreg = WI_SEL0; 75346492Swpaul offreg = WI_OFF0; 75446492Swpaul break; 75546492Swpaul case WI_BAP1: 75646492Swpaul selreg = WI_SEL1; 75746492Swpaul offreg = WI_OFF1; 75846492Swpaul break; 75946492Swpaul default: 76046492Swpaul printf("wi%d: invalid data path: %x\n", sc->wi_unit, chan); 76146492Swpaul return(EIO); 76246492Swpaul } 76346492Swpaul 76446492Swpaul CSR_WRITE_2(sc, selreg, id); 76546492Swpaul CSR_WRITE_2(sc, offreg, off); 76646492Swpaul 76746492Swpaul for (i = 0; i < WI_TIMEOUT; i++) { 76846492Swpaul if (!(CSR_READ_2(sc, offreg) & (WI_OFF_BUSY|WI_OFF_ERR))) 76946492Swpaul break; 77046492Swpaul } 77146492Swpaul 77246492Swpaul if (i == WI_TIMEOUT) 77346492Swpaul return(ETIMEDOUT); 77446492Swpaul 77546492Swpaul return(0); 77646492Swpaul} 77746492Swpaul 77846492Swpaulstatic int wi_read_data(sc, id, off, buf, len) 77946492Swpaul struct wi_softc *sc; 78046492Swpaul int id, off; 78146492Swpaul caddr_t buf; 78246492Swpaul int len; 78346492Swpaul{ 78446492Swpaul int i; 78546492Swpaul u_int16_t *ptr; 78646492Swpaul 78746492Swpaul if (wi_seek(sc, id, off, WI_BAP1)) 78846492Swpaul return(EIO); 78946492Swpaul 79046492Swpaul ptr = (u_int16_t *)buf; 79146492Swpaul for (i = 0; i < len / 2; i++) 79246492Swpaul ptr[i] = CSR_READ_2(sc, WI_DATA1); 79346492Swpaul 79446492Swpaul return(0); 79546492Swpaul} 79646492Swpaul 79746492Swpaul/* 79846492Swpaul * According to the comments in the HCF Light code, there is a bug in 79946492Swpaul * the Hermes (or possibly in certain Hermes firmware revisions) where 80046492Swpaul * the chip's internal autoincrement counter gets thrown off during 80146492Swpaul * data writes: the autoincrement is missed, causing one data word to 80246492Swpaul * be overwritten and subsequent words to be written to the wrong memory 80346492Swpaul * locations. The end result is that we could end up transmitting bogus 80446492Swpaul * frames without realizing it. The workaround for this is to write a 80546492Swpaul * couple of extra guard words after the end of the transfer, then 80646492Swpaul * attempt to read then back. If we fail to locate the guard words where 80746492Swpaul * we expect them, we preform the transfer over again. 80846492Swpaul */ 80946492Swpaulstatic int wi_write_data(sc, id, off, buf, len) 81046492Swpaul struct wi_softc *sc; 81146492Swpaul int id, off; 81246492Swpaul caddr_t buf; 81346492Swpaul int len; 81446492Swpaul{ 81546492Swpaul int i; 81646492Swpaul u_int16_t *ptr; 81746492Swpaul 81846492Swpaul#ifdef WI_HERMES_AUTOINC_WAR 81946492Swpaulagain: 82046492Swpaul#endif 82146492Swpaul 82246492Swpaul if (wi_seek(sc, id, off, WI_BAP0)) 82346492Swpaul return(EIO); 82446492Swpaul 82546492Swpaul ptr = (u_int16_t *)buf; 82646492Swpaul for (i = 0; i < (len / 2); i++) 82746492Swpaul CSR_WRITE_2(sc, WI_DATA0, ptr[i]); 82846492Swpaul 82946492Swpaul#ifdef WI_HERMES_AUTOINC_WAR 83046492Swpaul CSR_WRITE_2(sc, WI_DATA0, 0x1234); 83146492Swpaul CSR_WRITE_2(sc, WI_DATA0, 0x5678); 83246492Swpaul 83346492Swpaul if (wi_seek(sc, id, off + len, WI_BAP0)) 83446492Swpaul return(EIO); 83546492Swpaul 83646492Swpaul if (CSR_READ_2(sc, WI_DATA0) != 0x1234 || 83746492Swpaul CSR_READ_2(sc, WI_DATA0) != 0x5678) 83846492Swpaul goto again; 83946492Swpaul#endif 84046492Swpaul 84146492Swpaul return(0); 84246492Swpaul} 84346492Swpaul 84446492Swpaul/* 84546492Swpaul * Allocate a region of memory inside the NIC and zero 84646492Swpaul * it out. 84746492Swpaul */ 84846492Swpaulstatic int wi_alloc_nicmem(sc, len, id) 84946492Swpaul struct wi_softc *sc; 85046492Swpaul int len; 85146492Swpaul int *id; 85246492Swpaul{ 85346492Swpaul int i; 85446492Swpaul 85546492Swpaul if (wi_cmd(sc, WI_CMD_ALLOC_MEM, len)) { 85646492Swpaul printf("wi%d: failed to allocate %d bytes on NIC\n", 85746492Swpaul sc->wi_unit, len); 85846492Swpaul return(ENOMEM); 85946492Swpaul } 86046492Swpaul 86146492Swpaul for (i = 0; i < WI_TIMEOUT; i++) { 86246492Swpaul if (CSR_READ_2(sc, WI_EVENT_STAT) & WI_EV_ALLOC) 86346492Swpaul break; 86446492Swpaul } 86546492Swpaul 86646492Swpaul if (i == WI_TIMEOUT) 86746492Swpaul return(ETIMEDOUT); 86846492Swpaul 86946492Swpaul CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_ALLOC); 87046492Swpaul *id = CSR_READ_2(sc, WI_ALLOC_FID); 87146492Swpaul 87246492Swpaul wi_seek(sc, *id, 0, WI_BAP0); 87346492Swpaul 87446492Swpaul for (i = 0; i < len / 2; i++) 87546492Swpaul CSR_WRITE_2(sc, WI_DATA0, 0); 87646492Swpaul 87746492Swpaul return(0); 87846492Swpaul} 87946492Swpaul 88046492Swpaulstatic void wi_setmulti(sc) 88146492Swpaul struct wi_softc *sc; 88246492Swpaul{ 88346492Swpaul struct ifnet *ifp; 88446492Swpaul int i = 0; 88546492Swpaul struct ifmultiaddr *ifma; 88646492Swpaul struct wi_ltv_mcast mcast; 88746492Swpaul 88846492Swpaul ifp = &sc->arpcom.ac_if; 88946492Swpaul 89046492Swpaul bzero((char *)&mcast, sizeof(mcast)); 89146492Swpaul 89246492Swpaul mcast.wi_type = WI_RID_MCAST; 89346492Swpaul mcast.wi_len = (3 * 16) + 1; 89446492Swpaul 89546492Swpaul if (ifp->if_flags & IFF_ALLMULTI || ifp->if_flags & IFF_PROMISC) { 89646492Swpaul wi_write_record(sc, (struct wi_ltv_gen *)&mcast); 89746492Swpaul return; 89846492Swpaul } 89946492Swpaul 90046492Swpaul for (ifma = ifp->if_multiaddrs.lh_first; ifma != NULL; 90146492Swpaul ifma = ifma->ifma_link.le_next) { 90246492Swpaul if (ifma->ifma_addr->sa_family != AF_LINK) 90346492Swpaul continue; 90446492Swpaul if (i < 16) { 90546492Swpaul bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr), 90646492Swpaul (char *)&mcast.wi_mcast[i], ETHER_ADDR_LEN); 90746492Swpaul i++; 90846492Swpaul } else { 90946492Swpaul bzero((char *)&mcast, sizeof(mcast)); 91046492Swpaul break; 91146492Swpaul } 91246492Swpaul } 91346492Swpaul 91446492Swpaul mcast.wi_len = (i * 3) + 1; 91546492Swpaul wi_write_record(sc, (struct wi_ltv_gen *)&mcast); 91646492Swpaul 91746492Swpaul return; 91846492Swpaul} 91946492Swpaul 92046492Swpaulstatic void wi_setdef(sc, wreq) 92146492Swpaul struct wi_softc *sc; 92246492Swpaul struct wi_req *wreq; 92346492Swpaul{ 92446492Swpaul struct sockaddr_dl *sdl; 92546492Swpaul struct ifaddr *ifa; 92646492Swpaul struct ifnet *ifp; 92746492Swpaul 92846492Swpaul ifp = &sc->arpcom.ac_if; 92946492Swpaul 93046492Swpaul switch(wreq->wi_type) { 93146492Swpaul case WI_RID_MAC_NODE: 93246492Swpaul ifa = ifnet_addrs[ifp->if_index - 1]; 93346492Swpaul sdl = (struct sockaddr_dl *)ifa->ifa_addr; 93446492Swpaul bcopy((char *)&wreq->wi_val, (char *)&sc->arpcom.ac_enaddr, 93546492Swpaul ETHER_ADDR_LEN); 93646492Swpaul bcopy((char *)&wreq->wi_val, LLADDR(sdl), ETHER_ADDR_LEN); 93746492Swpaul break; 93846492Swpaul case WI_RID_PORTTYPE: 93946492Swpaul sc->wi_ptype = wreq->wi_val[0]; 94046492Swpaul break; 94146492Swpaul case WI_RID_TX_RATE: 94246492Swpaul sc->wi_tx_rate = wreq->wi_val[0]; 94346492Swpaul break; 94446492Swpaul case WI_RID_MAX_DATALEN: 94546492Swpaul sc->wi_max_data_len = wreq->wi_val[0]; 94646492Swpaul break; 94746492Swpaul case WI_RID_RTS_THRESH: 94846492Swpaul sc->wi_rts_thresh = wreq->wi_val[0]; 94946492Swpaul break; 95046492Swpaul case WI_RID_SYSTEM_SCALE: 95146492Swpaul sc->wi_ap_density = wreq->wi_val[0]; 95246492Swpaul break; 95346492Swpaul case WI_RID_CREATE_IBSS: 95446492Swpaul sc->wi_create_ibss = wreq->wi_val[0]; 95546492Swpaul break; 95646563Swpaul case WI_RID_OWN_CHNL: 95746563Swpaul sc->wi_channel = wreq->wi_val[0]; 95846563Swpaul break; 95946492Swpaul case WI_RID_NODENAME: 96046492Swpaul bzero(sc->wi_node_name, sizeof(sc->wi_node_name)); 96146492Swpaul bcopy((char *)&wreq->wi_val[1], sc->wi_node_name, 30); 96246492Swpaul break; 96346492Swpaul case WI_RID_DESIRED_SSID: 96446492Swpaul bzero(sc->wi_net_name, sizeof(sc->wi_net_name)); 96546492Swpaul bcopy((char *)&wreq->wi_val[1], sc->wi_net_name, 30); 96646492Swpaul break; 96746492Swpaul case WI_RID_OWN_SSID: 96846492Swpaul bzero(sc->wi_ibss_name, sizeof(sc->wi_ibss_name)); 96946492Swpaul bcopy((char *)&wreq->wi_val[1], sc->wi_ibss_name, 30); 97046492Swpaul break; 97146492Swpaul default: 97246492Swpaul break; 97346492Swpaul } 97446492Swpaul 97546563Swpaul /* Reinitialize WaveLAN. */ 97646563Swpaul wi_init(sc); 97746563Swpaul 97846492Swpaul return; 97946492Swpaul} 98046492Swpaul 98146492Swpaulstatic int wi_ioctl(ifp, command, data) 98246492Swpaul struct ifnet *ifp; 98346492Swpaul u_long command; 98446492Swpaul caddr_t data; 98546492Swpaul{ 98646492Swpaul int s, error = 0; 98746492Swpaul struct wi_softc *sc; 98846492Swpaul struct wi_req wreq; 98946492Swpaul struct ifreq *ifr; 99046492Swpaul 99146492Swpaul s = splimp(); 99246492Swpaul 99346492Swpaul sc = ifp->if_softc; 99446492Swpaul ifr = (struct ifreq *)data; 99546492Swpaul 99646492Swpaul if (sc->wi_gone) 99746492Swpaul return(ENODEV); 99846492Swpaul 99946492Swpaul switch(command) { 100046492Swpaul case SIOCSIFADDR: 100146492Swpaul case SIOCGIFADDR: 100246492Swpaul case SIOCSIFMTU: 100346492Swpaul error = ether_ioctl(ifp, command, data); 100446492Swpaul break; 100546492Swpaul case SIOCSIFFLAGS: 100646492Swpaul if (ifp->if_flags & IFF_UP) { 100746492Swpaul if (ifp->if_flags & IFF_RUNNING && 100846492Swpaul ifp->if_flags & IFF_PROMISC && 100946492Swpaul !(sc->wi_if_flags & IFF_PROMISC)) { 101046492Swpaul WI_SETVAL(WI_RID_PROMISC, 1); 101146492Swpaul } else if (ifp->if_flags & IFF_RUNNING && 101246492Swpaul !(ifp->if_flags & IFF_PROMISC) && 101346492Swpaul sc->wi_if_flags & IFF_PROMISC) { 101446492Swpaul WI_SETVAL(WI_RID_PROMISC, 0); 101546492Swpaul } else 101646492Swpaul wi_init(sc); 101746492Swpaul } else { 101846492Swpaul if (ifp->if_flags & IFF_RUNNING) { 101946492Swpaul wi_stop(sc); 102046492Swpaul } 102146492Swpaul } 102246492Swpaul sc->wi_if_flags = ifp->if_flags; 102346492Swpaul error = 0; 102446492Swpaul break; 102546492Swpaul case SIOCADDMULTI: 102646492Swpaul case SIOCDELMULTI: 102746492Swpaul wi_setmulti(sc); 102846492Swpaul error = 0; 102946492Swpaul break; 103046492Swpaul case SIOCGWAVELAN: 103146492Swpaul error = copyin(ifr->ifr_data, &wreq, sizeof(wreq)); 103246492Swpaul if (error) 103346492Swpaul break; 103446492Swpaul if (wreq.wi_type == WI_RID_IFACE_STATS) { 103546492Swpaul bcopy((char *)&sc->wi_stats, (char *)&wreq.wi_val, 103646492Swpaul sizeof(sc->wi_stats)); 103746492Swpaul wreq.wi_len = (sizeof(sc->wi_stats) / 2) + 1; 103846492Swpaul } else { 103946492Swpaul if (wi_read_record(sc, (struct wi_ltv_gen *)&wreq)) { 104046492Swpaul error = EINVAL; 104146492Swpaul break; 104246492Swpaul } 104346492Swpaul } 104446492Swpaul error = copyout(&wreq, ifr->ifr_data, sizeof(wreq)); 104546492Swpaul break; 104646492Swpaul case SIOCSWAVELAN: 104746492Swpaul error = copyin(ifr->ifr_data, &wreq, sizeof(wreq)); 104846492Swpaul if (error) 104946492Swpaul break; 105046492Swpaul if (wreq.wi_type == WI_RID_IFACE_STATS) { 105146492Swpaul error = EINVAL; 105246492Swpaul break; 105346492Swpaul } else if (wreq.wi_type == WI_RID_MGMT_XMIT) { 105446492Swpaul error = wi_mgmt_xmit(sc, (caddr_t)&wreq.wi_val, 105546492Swpaul wreq.wi_len); 105646492Swpaul } else { 105746492Swpaul error = wi_write_record(sc, (struct wi_ltv_gen *)&wreq); 105846492Swpaul if (!error) 105946492Swpaul wi_setdef(sc, &wreq); 106046492Swpaul } 106146492Swpaul break; 106246492Swpaul default: 106346492Swpaul error = EINVAL; 106446492Swpaul break; 106546492Swpaul } 106646492Swpaul 106746492Swpaul splx(s); 106846492Swpaul 106946492Swpaul return(error); 107046492Swpaul} 107146492Swpaul 107246492Swpaulstatic void wi_init(xsc) 107346492Swpaul void *xsc; 107446492Swpaul{ 107546492Swpaul struct wi_softc *sc = xsc; 107646492Swpaul struct ifnet *ifp = &sc->arpcom.ac_if; 107746492Swpaul int s; 107846492Swpaul struct wi_ltv_macaddr mac; 107946492Swpaul int id = 0; 108046492Swpaul 108146492Swpaul if (sc->wi_gone) 108246492Swpaul return; 108346492Swpaul 108446492Swpaul s = splimp(); 108546492Swpaul 108646492Swpaul if (ifp->if_flags & IFF_RUNNING) 108746492Swpaul wi_stop(sc); 108846492Swpaul 108946492Swpaul wi_reset(sc); 109046492Swpaul 109146492Swpaul /* Program max data length. */ 109246492Swpaul WI_SETVAL(WI_RID_MAX_DATALEN, sc->wi_max_data_len); 109346492Swpaul 109446492Swpaul /* Enable/disable IBSS ctration. */ 109546492Swpaul WI_SETVAL(WI_RID_CREATE_IBSS, sc->wi_create_ibss); 109646492Swpaul 109746492Swpaul /* Set the port type. */ 109846492Swpaul WI_SETVAL(WI_RID_PORTTYPE, sc->wi_ptype); 109946492Swpaul 110046492Swpaul /* Program the RTS/CTS threshold. */ 110146492Swpaul WI_SETVAL(WI_RID_RTS_THRESH, sc->wi_rts_thresh); 110246492Swpaul 110346492Swpaul /* Program the TX rate */ 110446492Swpaul WI_SETVAL(WI_RID_TX_RATE, sc->wi_tx_rate); 110546492Swpaul 110646492Swpaul /* Access point density */ 110746492Swpaul WI_SETVAL(WI_RID_SYSTEM_SCALE, sc->wi_ap_density); 110846492Swpaul 110946492Swpaul /* Specify the IBSS name */ 111046492Swpaul WI_SETSTR(WI_RID_OWN_SSID, sc->wi_ibss_name); 111146492Swpaul 111246492Swpaul /* Specify the network name */ 111346492Swpaul WI_SETSTR(WI_RID_DESIRED_SSID, sc->wi_net_name); 111446492Swpaul 111546563Swpaul /* Specify the frequency to use */ 111646563Swpaul WI_SETVAL(WI_RID_OWN_CHNL, sc->wi_channel); 111746563Swpaul 111846492Swpaul /* Program the nodename. */ 111946492Swpaul WI_SETSTR(WI_RID_NODENAME, sc->wi_node_name); 112046492Swpaul 112146492Swpaul /* Set our MAC address. */ 112246492Swpaul mac.wi_len = 4; 112346492Swpaul mac.wi_type = WI_RID_MAC_NODE; 112446492Swpaul bcopy((char *)&sc->arpcom.ac_enaddr, 112546492Swpaul (char *)&mac.wi_mac_addr, ETHER_ADDR_LEN); 112646492Swpaul wi_write_record(sc, (struct wi_ltv_gen *)&mac); 112746492Swpaul 112846492Swpaul /* Initialize promisc mode. */ 112946492Swpaul if (ifp->if_flags & IFF_PROMISC) { 113046492Swpaul WI_SETVAL(WI_RID_PROMISC, 1); 113146492Swpaul } else { 113246492Swpaul WI_SETVAL(WI_RID_PROMISC, 0); 113346492Swpaul } 113446492Swpaul 113546492Swpaul /* Set multicast filter. */ 113646492Swpaul wi_setmulti(sc); 113746492Swpaul 113846492Swpaul /* Enable desired port */ 113946492Swpaul wi_cmd(sc, WI_CMD_ENABLE|sc->wi_portnum, 0); 114046492Swpaul 114146492Swpaul if (wi_alloc_nicmem(sc, 1518 + sizeof(struct wi_frame) + 8, &id)) 114246495Swpaul printf("wi%d: tx buffer allocation failed\n", sc->wi_unit); 114346492Swpaul sc->wi_tx_data_id = id; 114446492Swpaul 114546492Swpaul if (wi_alloc_nicmem(sc, 1518 + sizeof(struct wi_frame) + 8, &id)) 114646495Swpaul printf("wi%d: mgmt. buffer allocation failed\n", sc->wi_unit); 114746492Swpaul sc->wi_tx_mgmt_id = id; 114846492Swpaul 114946492Swpaul /* enable interrupts */ 115046492Swpaul CSR_WRITE_2(sc, WI_INT_EN, WI_INTRS); 115146492Swpaul 115246492Swpaul splx(s); 115346492Swpaul 115446492Swpaul ifp->if_flags |= IFF_RUNNING; 115546492Swpaul ifp->if_flags &= ~IFF_OACTIVE; 115646492Swpaul 115746492Swpaul sc->wi_stat_ch = timeout(wi_inquire, sc, hz * 60); 115846492Swpaul 115946492Swpaul return; 116046492Swpaul} 116146492Swpaul 116246492Swpaulstatic void wi_start(ifp) 116346492Swpaul struct ifnet *ifp; 116446492Swpaul{ 116546492Swpaul struct wi_softc *sc; 116646492Swpaul struct mbuf *m0; 116746492Swpaul struct wi_frame tx_frame; 116846492Swpaul struct ether_header *eh; 116946492Swpaul int id; 117046492Swpaul 117146492Swpaul sc = ifp->if_softc; 117246492Swpaul 117346492Swpaul if (sc->wi_gone) 117446492Swpaul return; 117546492Swpaul 117646492Swpaul if (ifp->if_flags & IFF_OACTIVE) 117746492Swpaul return; 117846492Swpaul 117946492Swpaul IF_DEQUEUE(&ifp->if_snd, m0); 118046492Swpaul if (m0 == NULL) 118146492Swpaul return; 118246492Swpaul 118346492Swpaul bzero((char *)&tx_frame, sizeof(tx_frame)); 118446492Swpaul id = sc->wi_tx_data_id; 118546492Swpaul eh = mtod(m0, struct ether_header *); 118646492Swpaul 118746492Swpaul /* 118846492Swpaul * Use RFC1042 encoding for IP and ARP datagrames, 118946492Swpaul * 802.3 for anything else. 119046492Swpaul */ 119146492Swpaul if (ntohs(eh->ether_type) == ETHERTYPE_IP || 119246492Swpaul ntohs(eh->ether_type) == ETHERTYPE_ARP || 119346492Swpaul ntohs(eh->ether_type) == ETHERTYPE_REVARP) { 119446492Swpaul bcopy((char *)&eh->ether_dhost, 119546492Swpaul (char *)&tx_frame.wi_addr1, ETHER_ADDR_LEN); 119646492Swpaul bcopy((char *)&eh->ether_shost, 119746492Swpaul (char *)&tx_frame.wi_addr2, ETHER_ADDR_LEN); 119846492Swpaul bcopy((char *)&eh->ether_dhost, 119946492Swpaul (char *)&tx_frame.wi_dst_addr, ETHER_ADDR_LEN); 120046492Swpaul bcopy((char *)&eh->ether_shost, 120146492Swpaul (char *)&tx_frame.wi_src_addr, ETHER_ADDR_LEN); 120246492Swpaul 120346492Swpaul tx_frame.wi_dat_len = m0->m_pkthdr.len - WI_SNAPHDR_LEN; 120446492Swpaul tx_frame.wi_frame_ctl = WI_FTYPE_DATA; 120546492Swpaul tx_frame.wi_dat[0] = htons(WI_SNAP_WORD0); 120646492Swpaul tx_frame.wi_dat[1] = htons(WI_SNAP_WORD1); 120746492Swpaul tx_frame.wi_len = htons(m0->m_pkthdr.len - WI_SNAPHDR_LEN); 120846492Swpaul tx_frame.wi_type = eh->ether_type; 120946492Swpaul 121046492Swpaul m_copydata(m0, sizeof(struct ether_header), 121146492Swpaul m0->m_pkthdr.len - sizeof(struct ether_header), 121246492Swpaul (caddr_t)&sc->wi_txbuf); 121346492Swpaul 121446492Swpaul wi_write_data(sc, id, 0, (caddr_t)&tx_frame, 121546492Swpaul sizeof(struct wi_frame)); 121646492Swpaul wi_write_data(sc, id, WI_802_11_OFFSET, (caddr_t)&sc->wi_txbuf, 121746492Swpaul (m0->m_pkthdr.len - sizeof(struct ether_header)) + 2); 121846492Swpaul } else { 121946492Swpaul tx_frame.wi_dat_len = m0->m_pkthdr.len; 122046492Swpaul 122146492Swpaul m_copydata(m0, 0, m0->m_pkthdr.len, (caddr_t)&sc->wi_txbuf); 122246492Swpaul 122346492Swpaul wi_write_data(sc, id, 0, (caddr_t)&tx_frame, 122446492Swpaul sizeof(struct wi_frame)); 122546492Swpaul wi_write_data(sc, id, WI_802_3_OFFSET, (caddr_t)&sc->wi_txbuf, 122646492Swpaul m0->m_pkthdr.len + 2); 122746492Swpaul } 122846492Swpaul 122946492Swpaul#if NBPFILTER > 0 123046492Swpaul /* 123146492Swpaul * If there's a BPF listner, bounce a copy of 123246492Swpaul * this frame to him. 123346492Swpaul */ 123446492Swpaul if (ifp->if_bpf) 123546492Swpaul bpf_mtap(ifp, m0); 123646492Swpaul#endif 123746492Swpaul 123846492Swpaul m_freem(m0); 123946492Swpaul 124046492Swpaul if (wi_cmd(sc, WI_CMD_TX|WI_RECLAIM, id)) 124146492Swpaul printf("wi%d: xmit failed\n", sc->wi_unit); 124246492Swpaul 124346492Swpaul ifp->if_flags |= IFF_OACTIVE; 124446492Swpaul 124546492Swpaul /* 124646492Swpaul * Set a timeout in case the chip goes out to lunch. 124746492Swpaul */ 124846492Swpaul ifp->if_timer = 5; 124946492Swpaul 125046492Swpaul return; 125146492Swpaul} 125246492Swpaul 125346492Swpaulstatic int wi_mgmt_xmit(sc, data, len) 125446492Swpaul struct wi_softc *sc; 125546492Swpaul caddr_t data; 125646492Swpaul int len; 125746492Swpaul{ 125846492Swpaul struct wi_frame tx_frame; 125946492Swpaul int id; 126046492Swpaul struct wi_80211_hdr *hdr; 126146492Swpaul caddr_t dptr; 126246492Swpaul 126346492Swpaul if (sc->wi_gone) 126446492Swpaul return(ENODEV); 126546492Swpaul 126646492Swpaul hdr = (struct wi_80211_hdr *)data; 126746492Swpaul dptr = data + sizeof(struct wi_80211_hdr); 126846492Swpaul 126946492Swpaul bzero((char *)&tx_frame, sizeof(tx_frame)); 127046492Swpaul id = sc->wi_tx_mgmt_id; 127146492Swpaul 127246492Swpaul bcopy((char *)hdr, (char *)&tx_frame.wi_frame_ctl, 127346492Swpaul sizeof(struct wi_80211_hdr)); 127446492Swpaul 127546492Swpaul tx_frame.wi_dat_len = len - WI_SNAPHDR_LEN; 127646492Swpaul tx_frame.wi_len = htons(len - WI_SNAPHDR_LEN); 127746492Swpaul 127846492Swpaul wi_write_data(sc, id, 0, (caddr_t)&tx_frame, sizeof(struct wi_frame)); 127946492Swpaul wi_write_data(sc, id, WI_802_11_OFFSET_RAW, dptr, 128046492Swpaul (len - sizeof(struct wi_80211_hdr)) + 2); 128146492Swpaul 128246492Swpaul if (wi_cmd(sc, WI_CMD_TX|WI_RECLAIM, id)) { 128346492Swpaul printf("wi%d: xmit failed\n", sc->wi_unit); 128446492Swpaul return(EIO); 128546492Swpaul } 128646492Swpaul 128746492Swpaul return(0); 128846492Swpaul} 128946492Swpaul 129046492Swpaulstatic void wi_stop(sc) 129146492Swpaul struct wi_softc *sc; 129246492Swpaul{ 129346492Swpaul struct ifnet *ifp; 129446492Swpaul 129546492Swpaul if (sc->wi_gone) 129646492Swpaul return; 129746492Swpaul 129846492Swpaul ifp = &sc->arpcom.ac_if; 129946492Swpaul 130046492Swpaul CSR_WRITE_2(sc, WI_INT_EN, 0); 130146492Swpaul wi_cmd(sc, WI_CMD_DISABLE|sc->wi_portnum, 0); 130246492Swpaul 130346492Swpaul untimeout(wi_inquire, sc, sc->wi_stat_ch); 130446492Swpaul 130546492Swpaul ifp->if_flags &= ~(IFF_RUNNING|IFF_OACTIVE); 130646492Swpaul 130746492Swpaul return; 130846492Swpaul} 130946492Swpaul 131046492Swpaulstatic void wi_watchdog(ifp) 131146492Swpaul struct ifnet *ifp; 131246492Swpaul{ 131346492Swpaul struct wi_softc *sc; 131446492Swpaul 131546492Swpaul sc = ifp->if_softc; 131646492Swpaul 131746492Swpaul printf("wi%d: device timeout\n", sc->wi_unit); 131846492Swpaul 131946492Swpaul wi_init(sc); 132046492Swpaul 132146492Swpaul ifp->if_oerrors++; 132246492Swpaul 132346492Swpaul return; 132446492Swpaul} 132546492Swpaul 132646492Swpaulstatic void wi_shutdown(howto, arg) 132746492Swpaul int howto; 132846492Swpaul void *arg; 132946492Swpaul{ 133046492Swpaul struct wi_softc *sc; 133146492Swpaul 133246492Swpaul sc = arg; 133346492Swpaul wi_stop(sc); 133446492Swpaul 133546492Swpaul return; 133646492Swpaul} 1337