if_wi.c revision 46492
146492Swpaul/* 246492Swpaul * Copyright (c) 1997, 1998, 1999 346492Swpaul * Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved. 446492Swpaul * 546492Swpaul * Redistribution and use in source and binary forms, with or without 646492Swpaul * modification, are permitted provided that the following conditions 746492Swpaul * are met: 846492Swpaul * 1. Redistributions of source code must retain the above copyright 946492Swpaul * notice, this list of conditions and the following disclaimer. 1046492Swpaul * 2. Redistributions in binary form must reproduce the above copyright 1146492Swpaul * notice, this list of conditions and the following disclaimer in the 1246492Swpaul * documentation and/or other materials provided with the distribution. 1346492Swpaul * 3. All advertising materials mentioning features or use of this software 1446492Swpaul * must display the following acknowledgement: 1546492Swpaul * This product includes software developed by Bill Paul. 1646492Swpaul * 4. Neither the name of the author nor the names of any co-contributors 1746492Swpaul * may be used to endorse or promote products derived from this software 1846492Swpaul * without specific prior written permission. 1946492Swpaul * 2046492Swpaul * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND 2146492Swpaul * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2246492Swpaul * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2346492Swpaul * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD 2446492Swpaul * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 2546492Swpaul * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 2646492Swpaul * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 2746492Swpaul * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 2846492Swpaul * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 2946492Swpaul * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 3046492Swpaul * THE POSSIBILITY OF SUCH DAMAGE. 3146492Swpaul * 3246492Swpaul * $Id: if_wi.c,v 1.48 1999/05/05 00:32:13 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[] = 11946492Swpaul "$Id: if_wi.c,v 1.48 1999/05/05 00:32:13 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; 27846492Swpaul struct ifnet *ifp; 27946492Swpaul char ifname[IFNAMSIZ]; 28046492Swpaul 28146492Swpaul#ifdef PCCARD_MODULE 28246492Swpaul isa_dev->id_ointr = wi_intr; 28346492Swpaul#endif 28446492Swpaul sc = &wi_softc[isa_dev->id_unit]; 28546492Swpaul ifp = &sc->arpcom.ac_if; 28646492Swpaul 28746492Swpaul /* Reset the NIC. */ 28846492Swpaul wi_reset(sc); 28946492Swpaul 29046492Swpaul /* Read the station address. */ 29146492Swpaul mac.wi_type = WI_RID_MAC_NODE; 29246492Swpaul mac.wi_len = 4; 29346492Swpaul wi_read_record(sc, (struct wi_ltv_gen *)&mac); 29446492Swpaul bcopy((char *)&mac.wi_mac_addr, 29546492Swpaul (char *)&sc->arpcom.ac_enaddr, ETHER_ADDR_LEN); 29646492Swpaul 29746492Swpaul printf("wi%d: Ethernet address: %6D\n", sc->wi_unit, 29846492Swpaul sc->arpcom.ac_enaddr, ":"); 29946492Swpaul 30046492Swpaul ifp->if_softc = sc; 30146492Swpaul ifp->if_unit = sc->wi_unit; 30246492Swpaul ifp->if_name = "wi"; 30346492Swpaul ifp->if_mtu = ETHERMTU; 30446492Swpaul ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; 30546492Swpaul ifp->if_ioctl = wi_ioctl; 30646492Swpaul ifp->if_output = ether_output; 30746492Swpaul ifp->if_start = wi_start; 30846492Swpaul ifp->if_watchdog = wi_watchdog; 30946492Swpaul ifp->if_init = wi_init; 31046492Swpaul ifp->if_baudrate = 10000000; 31146492Swpaul ifp->if_snd.ifq_maxlen = IFQ_MAXLEN; 31246492Swpaul 31346492Swpaul bzero(sc->wi_node_name, sizeof(sc->wi_node_name)); 31446492Swpaul bcopy(WI_DEFAULT_NODENAME, sc->wi_node_name, 31546492Swpaul sizeof(WI_DEFAULT_NODENAME) - 1); 31646492Swpaul 31746492Swpaul bzero(sc->wi_net_name, sizeof(sc->wi_net_name)); 31846492Swpaul bcopy(WI_DEFAULT_NETNAME, sc->wi_net_name, 31946492Swpaul sizeof(WI_DEFAULT_NETNAME) - 1); 32046492Swpaul 32146492Swpaul bzero(sc->wi_ibss_name, sizeof(sc->wi_ibss_name)); 32246492Swpaul bcopy(WI_DEFAULT_IBSS, sc->wi_ibss_name, 32346492Swpaul sizeof(WI_DEFAULT_IBSS) - 1); 32446492Swpaul 32546492Swpaul sc->wi_portnum = WI_DEFAULT_PORT; 32646492Swpaul sc->wi_ptype = WI_PORTTYPE_ADHOC; 32746492Swpaul sc->wi_ap_density = WI_DEFAULT_AP_DENSITY; 32846492Swpaul sc->wi_rts_thresh = WI_DEFAULT_RTS_THRESH; 32946492Swpaul sc->wi_tx_rate = WI_DEFAULT_TX_RATE; 33046492Swpaul sc->wi_max_data_len = WI_DEFAULT_DATALEN; 33146492Swpaul sc->wi_create_ibss = WI_DEFAULT_CREATE_IBSS; 33246492Swpaul 33346492Swpaul bzero((char *)&sc->wi_stats, sizeof(sc->wi_stats)); 33446492Swpaul 33546492Swpaul wi_init(sc); 33646492Swpaul wi_stop(sc); 33746492Swpaul 33846492Swpaul /* 33946492Swpaul * If this logical interface has already been attached, 34046492Swpaul * don't attach it again or chaos will ensue. 34146492Swpaul */ 34246492Swpaul sprintf(ifname, "wi%d", sc->wi_unit); 34346492Swpaul 34446492Swpaul if (ifunit(ifname) == NULL) { 34546492Swpaul callout_handle_init(&sc->wi_stat_ch); 34646492Swpaul /* 34746492Swpaul * Call MI attach routines. 34846492Swpaul */ 34946492Swpaul if_attach(ifp); 35046492Swpaul ether_ifattach(ifp); 35146492Swpaul 35246492Swpaul#if NBPFILTER > 0 35346492Swpaul bpfattach(ifp, DLT_EN10MB, sizeof(struct ether_header)); 35446492Swpaul#endif 35546492Swpaul 35646492Swpaul at_shutdown(wi_shutdown, sc, SHUTDOWN_POST_SYNC); 35746492Swpaul } 35846492Swpaul 35946492Swpaul return(0); 36046492Swpaul} 36146492Swpaul 36246492Swpaulstatic void wi_rxeof(sc) 36346492Swpaul struct wi_softc *sc; 36446492Swpaul{ 36546492Swpaul struct ifnet *ifp; 36646492Swpaul struct ether_header *eh; 36746492Swpaul struct wi_frame rx_frame; 36846492Swpaul struct mbuf *m; 36946492Swpaul int id; 37046492Swpaul 37146492Swpaul ifp = &sc->arpcom.ac_if; 37246492Swpaul 37346492Swpaul id = CSR_READ_2(sc, WI_RX_FID); 37446492Swpaul 37546492Swpaul /* First read in the frame header */ 37646492Swpaul if (wi_read_data(sc, id, 0, (caddr_t)&rx_frame, sizeof(rx_frame))) { 37746492Swpaul ifp->if_ierrors++; 37846492Swpaul return; 37946492Swpaul } 38046492Swpaul 38146492Swpaul if (rx_frame.wi_status & WI_STAT_ERRSTAT) { 38246492Swpaul ifp->if_ierrors++; 38346492Swpaul return; 38446492Swpaul } 38546492Swpaul 38646492Swpaul MGETHDR(m, M_DONTWAIT, MT_DATA); 38746492Swpaul if (m == NULL) { 38846492Swpaul ifp->if_ierrors++; 38946492Swpaul return; 39046492Swpaul } 39146492Swpaul MCLGET(m, M_DONTWAIT); 39246492Swpaul if (!(m->m_flags & M_EXT)) { 39346492Swpaul m_freem(m); 39446492Swpaul ifp->if_ierrors++; 39546492Swpaul return; 39646492Swpaul } 39746492Swpaul 39846492Swpaul eh = mtod(m, struct ether_header *); 39946492Swpaul m->m_pkthdr.rcvif = ifp; 40046492Swpaul 40146492Swpaul if (rx_frame.wi_status == WI_STAT_1042 || 40246492Swpaul rx_frame.wi_status == WI_STAT_TUNNEL || 40346492Swpaul rx_frame.wi_status == WI_STAT_WMP_MSG) { 40446492Swpaul m->m_pkthdr.len = m->m_len = 40546492Swpaul rx_frame.wi_dat_len + WI_SNAPHDR_LEN; 40646492Swpaul 40746492Swpaul bcopy((char *)&rx_frame.wi_addr1, 40846492Swpaul (char *)&eh->ether_dhost, ETHER_ADDR_LEN); 40946492Swpaul bcopy((char *)&rx_frame.wi_addr2, 41046492Swpaul (char *)&eh->ether_shost, ETHER_ADDR_LEN); 41146492Swpaul bcopy((char *)&rx_frame.wi_type, 41246492Swpaul (char *)&eh->ether_type, sizeof(u_int16_t)); 41346492Swpaul 41446492Swpaul if (wi_read_data(sc, id, WI_802_11_OFFSET, 41546492Swpaul mtod(m, caddr_t) + sizeof(struct ether_header), 41646492Swpaul m->m_len + 2)) { 41746492Swpaul m_freem(m); 41846492Swpaul ifp->if_ierrors++; 41946492Swpaul return; 42046492Swpaul } 42146492Swpaul } else { 42246492Swpaul m->m_pkthdr.len = m->m_len = 42346492Swpaul rx_frame.wi_dat_len + sizeof(struct ether_header); 42446492Swpaul 42546492Swpaul if (wi_read_data(sc, id, WI_802_3_OFFSET, 42646492Swpaul mtod(m, caddr_t), m->m_len + 2)) { 42746492Swpaul m_freem(m); 42846492Swpaul ifp->if_ierrors++; 42946492Swpaul return; 43046492Swpaul } 43146492Swpaul } 43246492Swpaul 43346492Swpaul ifp->if_ipackets++; 43446492Swpaul 43546492Swpaul#if NBPFILTER > 0 43646492Swpaul /* Handle BPF listeners. */ 43746492Swpaul if (ifp->if_bpf) { 43846492Swpaul bpf_mtap(ifp, m); 43946492Swpaul if (ifp->if_flags & IFF_PROMISC && 44046492Swpaul (bcmp(eh->ether_dhost, sc->arpcom.ac_enaddr, 44146492Swpaul ETHER_ADDR_LEN) && (eh->ether_dhost[0] & 1) == 0)) { 44246492Swpaul m_freem(m); 44346492Swpaul return; 44446492Swpaul } 44546492Swpaul } 44646492Swpaul#endif 44746492Swpaul 44846492Swpaul /* Receive packet. */ 44946492Swpaul m_adj(m, sizeof(struct ether_header)); 45046492Swpaul ether_input(ifp, eh, m); 45146492Swpaul 45246492Swpaul return; 45346492Swpaul} 45446492Swpaul 45546492Swpaulstatic void wi_txeof(sc, status) 45646492Swpaul struct wi_softc *sc; 45746492Swpaul int status; 45846492Swpaul{ 45946492Swpaul struct ifnet *ifp; 46046492Swpaul 46146492Swpaul ifp = &sc->arpcom.ac_if; 46246492Swpaul 46346492Swpaul ifp->if_timer = 0; 46446492Swpaul ifp->if_flags &= ~IFF_OACTIVE; 46546492Swpaul 46646492Swpaul if (status & WI_EV_TX_EXC) 46746492Swpaul ifp->if_oerrors++; 46846492Swpaul else 46946492Swpaul ifp->if_opackets++; 47046492Swpaul 47146492Swpaul return; 47246492Swpaul} 47346492Swpaul 47446492Swpaulvoid wi_inquire(xsc) 47546492Swpaul void *xsc; 47646492Swpaul{ 47746492Swpaul struct wi_softc *sc; 47846492Swpaul struct ifnet *ifp; 47946492Swpaul 48046492Swpaul sc = xsc; 48146492Swpaul ifp = &sc->arpcom.ac_if; 48246492Swpaul 48346492Swpaul sc->wi_stat_ch = timeout(wi_inquire, sc, hz * 60); 48446492Swpaul 48546492Swpaul /* Don't do this while we're transmitting */ 48646492Swpaul if (ifp->if_flags & IFF_OACTIVE) 48746492Swpaul return; 48846492Swpaul 48946492Swpaul wi_cmd(sc, WI_CMD_INQUIRE, WI_INFO_COUNTERS); 49046492Swpaul 49146492Swpaul return; 49246492Swpaul} 49346492Swpaul 49446492Swpaulvoid wi_update_stats(sc) 49546492Swpaul struct wi_softc *sc; 49646492Swpaul{ 49746492Swpaul struct wi_ltv_gen gen; 49846492Swpaul u_int16_t id; 49946492Swpaul struct ifnet *ifp; 50046492Swpaul u_int32_t *ptr; 50146492Swpaul int i; 50246492Swpaul u_int16_t t; 50346492Swpaul 50446492Swpaul ifp = &sc->arpcom.ac_if; 50546492Swpaul 50646492Swpaul id = CSR_READ_2(sc, WI_INFO_FID); 50746492Swpaul 50846492Swpaul wi_read_data(sc, id, 0, (char *)&gen, 4); 50946492Swpaul 51046492Swpaul if (gen.wi_type != WI_INFO_COUNTERS || 51146492Swpaul gen.wi_len > (sizeof(sc->wi_stats) / 4) + 1) 51246492Swpaul return; 51346492Swpaul 51446492Swpaul ptr = (u_int32_t *)&sc->wi_stats; 51546492Swpaul 51646492Swpaul for (i = 0; i < gen.wi_len - 1; i++) { 51746492Swpaul t = CSR_READ_2(sc, WI_DATA1); 51846492Swpaul#ifdef WI_HERMES_STATS_WAR 51946492Swpaul if (t > 0xF000) 52046492Swpaul t = ~t & 0xFFFF; 52146492Swpaul#endif 52246492Swpaul ptr[i] += t; 52346492Swpaul } 52446492Swpaul 52546492Swpaul ifp->if_collisions = sc->wi_stats.wi_tx_single_retries + 52646492Swpaul sc->wi_stats.wi_tx_multi_retries + 52746492Swpaul sc->wi_stats.wi_tx_retry_limit; 52846492Swpaul 52946492Swpaul return; 53046492Swpaul} 53146492Swpaul 53246492Swpaulvoid wi_intr(unit) 53346492Swpaul int unit; 53446492Swpaul{ 53546492Swpaul struct wi_softc *sc; 53646492Swpaul struct ifnet *ifp; 53746492Swpaul u_int16_t status; 53846492Swpaul 53946492Swpaul sc = &wi_softc[unit]; 54046492Swpaul ifp = &sc->arpcom.ac_if; 54146492Swpaul 54246492Swpaul if (!(ifp->if_flags & IFF_UP)) { 54346492Swpaul CSR_WRITE_2(sc, WI_EVENT_ACK, 0xFFFF); 54446492Swpaul CSR_WRITE_2(sc, WI_INT_EN, 0); 54546492Swpaul return; 54646492Swpaul } 54746492Swpaul 54846492Swpaul /* Disable interrupts. */ 54946492Swpaul CSR_WRITE_2(sc, WI_INT_EN, 0); 55046492Swpaul 55146492Swpaul status = CSR_READ_2(sc, WI_EVENT_STAT); 55246492Swpaul CSR_WRITE_2(sc, WI_EVENT_ACK, ~WI_INTRS); 55346492Swpaul 55446492Swpaul if (status & WI_EV_RX) { 55546492Swpaul wi_rxeof(sc); 55646492Swpaul CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_RX); 55746492Swpaul } 55846492Swpaul 55946492Swpaul if (status & WI_EV_TX) { 56046492Swpaul wi_txeof(sc, status); 56146492Swpaul CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_TX); 56246492Swpaul } 56346492Swpaul 56446492Swpaul if (status & WI_EV_ALLOC) { 56546492Swpaul int id; 56646492Swpaul id = CSR_READ_2(sc, WI_ALLOC_FID); 56746492Swpaul CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_ALLOC); 56846492Swpaul if (id == sc->wi_tx_data_id) 56946492Swpaul wi_txeof(sc, status); 57046492Swpaul } 57146492Swpaul 57246492Swpaul if (status & WI_EV_INFO) { 57346492Swpaul wi_update_stats(sc); 57446492Swpaul CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_INFO); 57546492Swpaul } 57646492Swpaul 57746492Swpaul if (status & WI_EV_TX_EXC) { 57846492Swpaul wi_txeof(sc, status); 57946492Swpaul CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_TX_EXC); 58046492Swpaul } 58146492Swpaul 58246492Swpaul if (status & WI_EV_INFO_DROP) { 58346492Swpaul CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_INFO_DROP); 58446492Swpaul } 58546492Swpaul 58646492Swpaul /* Re-enable interrupts. */ 58746492Swpaul CSR_WRITE_2(sc, WI_INT_EN, WI_INTRS); 58846492Swpaul 58946492Swpaul if (ifp->if_snd.ifq_head != NULL) 59046492Swpaul wi_start(ifp); 59146492Swpaul 59246492Swpaul return; 59346492Swpaul} 59446492Swpaul 59546492Swpaulstatic int wi_cmd(sc, cmd, val) 59646492Swpaul struct wi_softc *sc; 59746492Swpaul int cmd; 59846492Swpaul int val; 59946492Swpaul{ 60046492Swpaul int i, s = 0; 60146492Swpaul 60246492Swpaul CSR_WRITE_2(sc, WI_PARAM0, val); 60346492Swpaul CSR_WRITE_2(sc, WI_COMMAND, cmd); 60446492Swpaul 60546492Swpaul for (i = 0; i < WI_TIMEOUT; i++) { 60646492Swpaul /* 60746492Swpaul * Wait for 'command complete' bit to be 60846492Swpaul * set in the event status register. 60946492Swpaul */ 61046492Swpaul s = CSR_READ_2(sc, WI_EVENT_STAT) & WI_EV_CMD; 61146492Swpaul if (s) { 61246492Swpaul /* Ack the event and read result code. */ 61346492Swpaul s = CSR_READ_2(sc, WI_STATUS); 61446492Swpaul CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_CMD); 61546492Swpaul#ifdef foo 61646492Swpaul if ((s & WI_CMD_CODE_MASK) != (cmd & WI_CMD_CODE_MASK)) 61746492Swpaul return(EIO); 61846492Swpaul#endif 61946492Swpaul if (s & WI_STAT_CMD_RESULT) 62046492Swpaul return(EIO); 62146492Swpaul break; 62246492Swpaul } 62346492Swpaul } 62446492Swpaul 62546492Swpaul if (i == WI_TIMEOUT) 62646492Swpaul return(ETIMEDOUT); 62746492Swpaul 62846492Swpaul return(0); 62946492Swpaul} 63046492Swpaul 63146492Swpaulstatic void wi_reset(sc) 63246492Swpaul struct wi_softc *sc; 63346492Swpaul{ 63446492Swpaul if (wi_cmd(sc, WI_CMD_INI, 0)) 63546492Swpaul printf("wi%d: init failed\n", sc->wi_unit); 63646492Swpaul CSR_WRITE_2(sc, WI_INT_EN, 0); 63746492Swpaul CSR_WRITE_2(sc, WI_EVENT_ACK, 0xFFFF); 63846492Swpaul 63946492Swpaul /* Calibrate timer. */ 64046492Swpaul WI_SETVAL(WI_RID_TICK_TIME, 8); 64146492Swpaul 64246492Swpaul return; 64346492Swpaul} 64446492Swpaul 64546492Swpaul/* 64646492Swpaul * Read an LTV record from the NIC. 64746492Swpaul */ 64846492Swpaulstatic int wi_read_record(sc, ltv) 64946492Swpaul struct wi_softc *sc; 65046492Swpaul struct wi_ltv_gen *ltv; 65146492Swpaul{ 65246492Swpaul u_int16_t *ptr; 65346492Swpaul int i, len, code; 65446492Swpaul 65546492Swpaul /* Tell the NIC to enter record read mode. */ 65646492Swpaul if (wi_cmd(sc, WI_CMD_ACCESS|WI_ACCESS_READ, ltv->wi_type)) 65746492Swpaul return(EIO); 65846492Swpaul 65946492Swpaul /* Select the record we want to read. */ 66046492Swpaul CSR_WRITE_2(sc, WI_SEL1, ltv->wi_type); 66146492Swpaul 66246492Swpaul /* Specify offset -- we always read the whole record. */ 66346492Swpaul CSR_WRITE_2(sc, WI_OFF1, 0); 66446492Swpaul 66546492Swpaul /* Wait for NIC to acknowledge */ 66646492Swpaul for (i = 0; i < WI_TIMEOUT; i++) { 66746492Swpaul if (!(CSR_READ_2(sc, WI_OFF1) & (WI_OFF_BUSY|WI_OFF_ERR))) 66846492Swpaul break; 66946492Swpaul } 67046492Swpaul 67146492Swpaul if (i == WI_TIMEOUT) 67246492Swpaul return(ETIMEDOUT); 67346492Swpaul 67446492Swpaul /* 67546492Swpaul * Read the length and record type and make sure they 67646492Swpaul * match what we expect (this verifies that we have enough 67746492Swpaul * room to hold all of the returned data. 67846492Swpaul */ 67946492Swpaul len = CSR_READ_2(sc, WI_DATA1); 68046492Swpaul if (len > ltv->wi_len) 68146492Swpaul return(ENOSPC); 68246492Swpaul code = CSR_READ_2(sc, WI_DATA1); 68346492Swpaul if (code != ltv->wi_type) 68446492Swpaul return(EIO); 68546492Swpaul 68646492Swpaul ltv->wi_len = len; 68746492Swpaul ltv->wi_type = code; 68846492Swpaul 68946492Swpaul /* Now read the data. */ 69046492Swpaul ptr = <v->wi_val; 69146492Swpaul for (i = 0; i < ltv->wi_len - 1; i++) 69246492Swpaul ptr[i] = CSR_READ_2(sc, WI_DATA1); 69346492Swpaul 69446492Swpaul return(0); 69546492Swpaul} 69646492Swpaul 69746492Swpaul/* 69846492Swpaul * Same as read, except we inject data instead of reading it. 69946492Swpaul */ 70046492Swpaulstatic int wi_write_record(sc, ltv) 70146492Swpaul struct wi_softc *sc; 70246492Swpaul struct wi_ltv_gen *ltv; 70346492Swpaul{ 70446492Swpaul u_int16_t *ptr; 70546492Swpaul int i; 70646492Swpaul 70746492Swpaul CSR_WRITE_2(sc, WI_SEL1, ltv->wi_type); 70846492Swpaul CSR_WRITE_2(sc, WI_OFF1, 0); 70946492Swpaul 71046492Swpaul for (i = 0; i < WI_TIMEOUT; i++) { 71146492Swpaul if (!(CSR_READ_2(sc, WI_OFF1) & (WI_OFF_BUSY|WI_OFF_ERR))) 71246492Swpaul break; 71346492Swpaul } 71446492Swpaul 71546492Swpaul if (i == WI_TIMEOUT) 71646492Swpaul return(ETIMEDOUT); 71746492Swpaul 71846492Swpaul CSR_WRITE_2(sc, WI_DATA1, ltv->wi_len); 71946492Swpaul CSR_WRITE_2(sc, WI_DATA1, ltv->wi_type); 72046492Swpaul 72146492Swpaul ptr = <v->wi_val; 72246492Swpaul for (i = 0; i < ltv->wi_len - 1; i++) 72346492Swpaul CSR_WRITE_2(sc, WI_DATA1, ptr[i]); 72446492Swpaul 72546492Swpaul if (wi_cmd(sc, WI_CMD_ACCESS|WI_ACCESS_WRITE, ltv->wi_type)) 72646492Swpaul return(EIO); 72746492Swpaul 72846492Swpaul return(0); 72946492Swpaul} 73046492Swpaul 73146492Swpaulstatic int wi_seek(sc, id, off, chan) 73246492Swpaul struct wi_softc *sc; 73346492Swpaul int id, off, chan; 73446492Swpaul{ 73546492Swpaul int i; 73646492Swpaul int selreg, offreg; 73746492Swpaul 73846492Swpaul switch (chan) { 73946492Swpaul case WI_BAP0: 74046492Swpaul selreg = WI_SEL0; 74146492Swpaul offreg = WI_OFF0; 74246492Swpaul break; 74346492Swpaul case WI_BAP1: 74446492Swpaul selreg = WI_SEL1; 74546492Swpaul offreg = WI_OFF1; 74646492Swpaul break; 74746492Swpaul default: 74846492Swpaul printf("wi%d: invalid data path: %x\n", sc->wi_unit, chan); 74946492Swpaul return(EIO); 75046492Swpaul } 75146492Swpaul 75246492Swpaul CSR_WRITE_2(sc, selreg, id); 75346492Swpaul CSR_WRITE_2(sc, offreg, off); 75446492Swpaul 75546492Swpaul for (i = 0; i < WI_TIMEOUT; i++) { 75646492Swpaul if (!(CSR_READ_2(sc, offreg) & (WI_OFF_BUSY|WI_OFF_ERR))) 75746492Swpaul break; 75846492Swpaul } 75946492Swpaul 76046492Swpaul if (i == WI_TIMEOUT) 76146492Swpaul return(ETIMEDOUT); 76246492Swpaul 76346492Swpaul return(0); 76446492Swpaul} 76546492Swpaul 76646492Swpaulstatic int wi_read_data(sc, id, off, buf, len) 76746492Swpaul struct wi_softc *sc; 76846492Swpaul int id, off; 76946492Swpaul caddr_t buf; 77046492Swpaul int len; 77146492Swpaul{ 77246492Swpaul int i; 77346492Swpaul u_int16_t *ptr; 77446492Swpaul 77546492Swpaul if (wi_seek(sc, id, off, WI_BAP1)) 77646492Swpaul return(EIO); 77746492Swpaul 77846492Swpaul ptr = (u_int16_t *)buf; 77946492Swpaul for (i = 0; i < len / 2; i++) 78046492Swpaul ptr[i] = CSR_READ_2(sc, WI_DATA1); 78146492Swpaul 78246492Swpaul return(0); 78346492Swpaul} 78446492Swpaul 78546492Swpaul/* 78646492Swpaul * According to the comments in the HCF Light code, there is a bug in 78746492Swpaul * the Hermes (or possibly in certain Hermes firmware revisions) where 78846492Swpaul * the chip's internal autoincrement counter gets thrown off during 78946492Swpaul * data writes: the autoincrement is missed, causing one data word to 79046492Swpaul * be overwritten and subsequent words to be written to the wrong memory 79146492Swpaul * locations. The end result is that we could end up transmitting bogus 79246492Swpaul * frames without realizing it. The workaround for this is to write a 79346492Swpaul * couple of extra guard words after the end of the transfer, then 79446492Swpaul * attempt to read then back. If we fail to locate the guard words where 79546492Swpaul * we expect them, we preform the transfer over again. 79646492Swpaul */ 79746492Swpaulstatic int wi_write_data(sc, id, off, buf, len) 79846492Swpaul struct wi_softc *sc; 79946492Swpaul int id, off; 80046492Swpaul caddr_t buf; 80146492Swpaul int len; 80246492Swpaul{ 80346492Swpaul int i; 80446492Swpaul u_int16_t *ptr; 80546492Swpaul 80646492Swpaul#ifdef WI_HERMES_AUTOINC_WAR 80746492Swpaulagain: 80846492Swpaul#endif 80946492Swpaul 81046492Swpaul if (wi_seek(sc, id, off, WI_BAP0)) 81146492Swpaul return(EIO); 81246492Swpaul 81346492Swpaul ptr = (u_int16_t *)buf; 81446492Swpaul for (i = 0; i < (len / 2); i++) 81546492Swpaul CSR_WRITE_2(sc, WI_DATA0, ptr[i]); 81646492Swpaul 81746492Swpaul#ifdef WI_HERMES_AUTOINC_WAR 81846492Swpaul CSR_WRITE_2(sc, WI_DATA0, 0x1234); 81946492Swpaul CSR_WRITE_2(sc, WI_DATA0, 0x5678); 82046492Swpaul 82146492Swpaul if (wi_seek(sc, id, off + len, WI_BAP0)) 82246492Swpaul return(EIO); 82346492Swpaul 82446492Swpaul if (CSR_READ_2(sc, WI_DATA0) != 0x1234 || 82546492Swpaul CSR_READ_2(sc, WI_DATA0) != 0x5678) 82646492Swpaul goto again; 82746492Swpaul#endif 82846492Swpaul 82946492Swpaul return(0); 83046492Swpaul} 83146492Swpaul 83246492Swpaul/* 83346492Swpaul * Allocate a region of memory inside the NIC and zero 83446492Swpaul * it out. 83546492Swpaul */ 83646492Swpaulstatic int wi_alloc_nicmem(sc, len, id) 83746492Swpaul struct wi_softc *sc; 83846492Swpaul int len; 83946492Swpaul int *id; 84046492Swpaul{ 84146492Swpaul int i; 84246492Swpaul 84346492Swpaul if (wi_cmd(sc, WI_CMD_ALLOC_MEM, len)) { 84446492Swpaul printf("wi%d: failed to allocate %d bytes on NIC\n", 84546492Swpaul sc->wi_unit, len); 84646492Swpaul return(ENOMEM); 84746492Swpaul } 84846492Swpaul 84946492Swpaul for (i = 0; i < WI_TIMEOUT; i++) { 85046492Swpaul if (CSR_READ_2(sc, WI_EVENT_STAT) & WI_EV_ALLOC) 85146492Swpaul break; 85246492Swpaul } 85346492Swpaul 85446492Swpaul if (i == WI_TIMEOUT) 85546492Swpaul return(ETIMEDOUT); 85646492Swpaul 85746492Swpaul CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_ALLOC); 85846492Swpaul *id = CSR_READ_2(sc, WI_ALLOC_FID); 85946492Swpaul 86046492Swpaul wi_seek(sc, *id, 0, WI_BAP0); 86146492Swpaul 86246492Swpaul for (i = 0; i < len / 2; i++) 86346492Swpaul CSR_WRITE_2(sc, WI_DATA0, 0); 86446492Swpaul 86546492Swpaul return(0); 86646492Swpaul} 86746492Swpaul 86846492Swpaulstatic void wi_setmulti(sc) 86946492Swpaul struct wi_softc *sc; 87046492Swpaul{ 87146492Swpaul struct ifnet *ifp; 87246492Swpaul int i = 0; 87346492Swpaul struct ifmultiaddr *ifma; 87446492Swpaul struct wi_ltv_mcast mcast; 87546492Swpaul 87646492Swpaul ifp = &sc->arpcom.ac_if; 87746492Swpaul 87846492Swpaul bzero((char *)&mcast, sizeof(mcast)); 87946492Swpaul 88046492Swpaul mcast.wi_type = WI_RID_MCAST; 88146492Swpaul mcast.wi_len = (3 * 16) + 1; 88246492Swpaul 88346492Swpaul if (ifp->if_flags & IFF_ALLMULTI || ifp->if_flags & IFF_PROMISC) { 88446492Swpaul wi_write_record(sc, (struct wi_ltv_gen *)&mcast); 88546492Swpaul return; 88646492Swpaul } 88746492Swpaul 88846492Swpaul for (ifma = ifp->if_multiaddrs.lh_first; ifma != NULL; 88946492Swpaul ifma = ifma->ifma_link.le_next) { 89046492Swpaul if (ifma->ifma_addr->sa_family != AF_LINK) 89146492Swpaul continue; 89246492Swpaul if (i < 16) { 89346492Swpaul bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr), 89446492Swpaul (char *)&mcast.wi_mcast[i], ETHER_ADDR_LEN); 89546492Swpaul i++; 89646492Swpaul } else { 89746492Swpaul bzero((char *)&mcast, sizeof(mcast)); 89846492Swpaul break; 89946492Swpaul } 90046492Swpaul } 90146492Swpaul 90246492Swpaul mcast.wi_len = (i * 3) + 1; 90346492Swpaul wi_write_record(sc, (struct wi_ltv_gen *)&mcast); 90446492Swpaul 90546492Swpaul return; 90646492Swpaul} 90746492Swpaul 90846492Swpaulstatic void wi_setdef(sc, wreq) 90946492Swpaul struct wi_softc *sc; 91046492Swpaul struct wi_req *wreq; 91146492Swpaul{ 91246492Swpaul struct sockaddr_dl *sdl; 91346492Swpaul struct ifaddr *ifa; 91446492Swpaul struct ifnet *ifp; 91546492Swpaul 91646492Swpaul ifp = &sc->arpcom.ac_if; 91746492Swpaul 91846492Swpaul switch(wreq->wi_type) { 91946492Swpaul case WI_RID_MAC_NODE: 92046492Swpaul ifa = ifnet_addrs[ifp->if_index - 1]; 92146492Swpaul sdl = (struct sockaddr_dl *)ifa->ifa_addr; 92246492Swpaul bcopy((char *)&wreq->wi_val, (char *)&sc->arpcom.ac_enaddr, 92346492Swpaul ETHER_ADDR_LEN); 92446492Swpaul bcopy((char *)&wreq->wi_val, LLADDR(sdl), ETHER_ADDR_LEN); 92546492Swpaul break; 92646492Swpaul case WI_RID_PORTTYPE: 92746492Swpaul sc->wi_ptype = wreq->wi_val[0]; 92846492Swpaul break; 92946492Swpaul case WI_RID_TX_RATE: 93046492Swpaul sc->wi_tx_rate = wreq->wi_val[0]; 93146492Swpaul break; 93246492Swpaul case WI_RID_MAX_DATALEN: 93346492Swpaul sc->wi_max_data_len = wreq->wi_val[0]; 93446492Swpaul break; 93546492Swpaul case WI_RID_RTS_THRESH: 93646492Swpaul sc->wi_rts_thresh = wreq->wi_val[0]; 93746492Swpaul break; 93846492Swpaul case WI_RID_SYSTEM_SCALE: 93946492Swpaul sc->wi_ap_density = wreq->wi_val[0]; 94046492Swpaul break; 94146492Swpaul case WI_RID_CREATE_IBSS: 94246492Swpaul sc->wi_create_ibss = wreq->wi_val[0]; 94346492Swpaul break; 94446492Swpaul case WI_RID_NODENAME: 94546492Swpaul bzero(sc->wi_node_name, sizeof(sc->wi_node_name)); 94646492Swpaul bcopy((char *)&wreq->wi_val[1], sc->wi_node_name, 30); 94746492Swpaul break; 94846492Swpaul case WI_RID_DESIRED_SSID: 94946492Swpaul bzero(sc->wi_net_name, sizeof(sc->wi_net_name)); 95046492Swpaul bcopy((char *)&wreq->wi_val[1], sc->wi_net_name, 30); 95146492Swpaul break; 95246492Swpaul case WI_RID_OWN_SSID: 95346492Swpaul bzero(sc->wi_ibss_name, sizeof(sc->wi_ibss_name)); 95446492Swpaul bcopy((char *)&wreq->wi_val[1], sc->wi_ibss_name, 30); 95546492Swpaul break; 95646492Swpaul default: 95746492Swpaul break; 95846492Swpaul } 95946492Swpaul 96046492Swpaul return; 96146492Swpaul} 96246492Swpaul 96346492Swpaulstatic int wi_ioctl(ifp, command, data) 96446492Swpaul struct ifnet *ifp; 96546492Swpaul u_long command; 96646492Swpaul caddr_t data; 96746492Swpaul{ 96846492Swpaul int s, error = 0; 96946492Swpaul struct wi_softc *sc; 97046492Swpaul struct wi_req wreq; 97146492Swpaul struct ifreq *ifr; 97246492Swpaul 97346492Swpaul s = splimp(); 97446492Swpaul 97546492Swpaul sc = ifp->if_softc; 97646492Swpaul ifr = (struct ifreq *)data; 97746492Swpaul 97846492Swpaul if (sc->wi_gone) 97946492Swpaul return(ENODEV); 98046492Swpaul 98146492Swpaul switch(command) { 98246492Swpaul case SIOCSIFADDR: 98346492Swpaul case SIOCGIFADDR: 98446492Swpaul case SIOCSIFMTU: 98546492Swpaul error = ether_ioctl(ifp, command, data); 98646492Swpaul break; 98746492Swpaul case SIOCSIFFLAGS: 98846492Swpaul if (ifp->if_flags & IFF_UP) { 98946492Swpaul if (ifp->if_flags & IFF_RUNNING && 99046492Swpaul ifp->if_flags & IFF_PROMISC && 99146492Swpaul !(sc->wi_if_flags & IFF_PROMISC)) { 99246492Swpaul WI_SETVAL(WI_RID_PROMISC, 1); 99346492Swpaul } else if (ifp->if_flags & IFF_RUNNING && 99446492Swpaul !(ifp->if_flags & IFF_PROMISC) && 99546492Swpaul sc->wi_if_flags & IFF_PROMISC) { 99646492Swpaul WI_SETVAL(WI_RID_PROMISC, 0); 99746492Swpaul } else 99846492Swpaul wi_init(sc); 99946492Swpaul } else { 100046492Swpaul if (ifp->if_flags & IFF_RUNNING) { 100146492Swpaul wi_stop(sc); 100246492Swpaul } 100346492Swpaul } 100446492Swpaul sc->wi_if_flags = ifp->if_flags; 100546492Swpaul error = 0; 100646492Swpaul break; 100746492Swpaul case SIOCADDMULTI: 100846492Swpaul case SIOCDELMULTI: 100946492Swpaul wi_setmulti(sc); 101046492Swpaul error = 0; 101146492Swpaul break; 101246492Swpaul case SIOCGWAVELAN: 101346492Swpaul error = copyin(ifr->ifr_data, &wreq, sizeof(wreq)); 101446492Swpaul if (error) 101546492Swpaul break; 101646492Swpaul if (wreq.wi_type == WI_RID_IFACE_STATS) { 101746492Swpaul bcopy((char *)&sc->wi_stats, (char *)&wreq.wi_val, 101846492Swpaul sizeof(sc->wi_stats)); 101946492Swpaul wreq.wi_len = (sizeof(sc->wi_stats) / 2) + 1; 102046492Swpaul } else { 102146492Swpaul if (wi_read_record(sc, (struct wi_ltv_gen *)&wreq)) { 102246492Swpaul error = EINVAL; 102346492Swpaul break; 102446492Swpaul } 102546492Swpaul } 102646492Swpaul error = copyout(&wreq, ifr->ifr_data, sizeof(wreq)); 102746492Swpaul break; 102846492Swpaul case SIOCSWAVELAN: 102946492Swpaul error = copyin(ifr->ifr_data, &wreq, sizeof(wreq)); 103046492Swpaul if (error) 103146492Swpaul break; 103246492Swpaul if (wreq.wi_type == WI_RID_IFACE_STATS) { 103346492Swpaul error = EINVAL; 103446492Swpaul break; 103546492Swpaul } else if (wreq.wi_type == WI_RID_MGMT_XMIT) { 103646492Swpaul error = wi_mgmt_xmit(sc, (caddr_t)&wreq.wi_val, 103746492Swpaul wreq.wi_len); 103846492Swpaul } else { 103946492Swpaul error = wi_write_record(sc, (struct wi_ltv_gen *)&wreq); 104046492Swpaul if (!error) 104146492Swpaul wi_setdef(sc, &wreq); 104246492Swpaul } 104346492Swpaul break; 104446492Swpaul default: 104546492Swpaul error = EINVAL; 104646492Swpaul break; 104746492Swpaul } 104846492Swpaul 104946492Swpaul splx(s); 105046492Swpaul 105146492Swpaul return(error); 105246492Swpaul} 105346492Swpaul 105446492Swpaulstatic void wi_init(xsc) 105546492Swpaul void *xsc; 105646492Swpaul{ 105746492Swpaul struct wi_softc *sc = xsc; 105846492Swpaul struct ifnet *ifp = &sc->arpcom.ac_if; 105946492Swpaul int s; 106046492Swpaul struct wi_ltv_macaddr mac; 106146492Swpaul int id = 0; 106246492Swpaul 106346492Swpaul if (sc->wi_gone) 106446492Swpaul return; 106546492Swpaul 106646492Swpaul s = splimp(); 106746492Swpaul 106846492Swpaul if (ifp->if_flags & IFF_RUNNING) 106946492Swpaul wi_stop(sc); 107046492Swpaul 107146492Swpaul wi_reset(sc); 107246492Swpaul 107346492Swpaul /* Program max data length. */ 107446492Swpaul WI_SETVAL(WI_RID_MAX_DATALEN, sc->wi_max_data_len); 107546492Swpaul 107646492Swpaul /* Enable/disable IBSS ctration. */ 107746492Swpaul WI_SETVAL(WI_RID_CREATE_IBSS, sc->wi_create_ibss); 107846492Swpaul 107946492Swpaul /* Set the port type. */ 108046492Swpaul WI_SETVAL(WI_RID_PORTTYPE, sc->wi_ptype); 108146492Swpaul 108246492Swpaul /* Program the RTS/CTS threshold. */ 108346492Swpaul WI_SETVAL(WI_RID_RTS_THRESH, sc->wi_rts_thresh); 108446492Swpaul 108546492Swpaul /* Program the TX rate */ 108646492Swpaul WI_SETVAL(WI_RID_TX_RATE, sc->wi_tx_rate); 108746492Swpaul 108846492Swpaul /* Access point density */ 108946492Swpaul WI_SETVAL(WI_RID_SYSTEM_SCALE, sc->wi_ap_density); 109046492Swpaul 109146492Swpaul /* Specify the IBSS name */ 109246492Swpaul WI_SETSTR(WI_RID_OWN_SSID, sc->wi_ibss_name); 109346492Swpaul 109446492Swpaul /* Specify the network name */ 109546492Swpaul WI_SETSTR(WI_RID_DESIRED_SSID, sc->wi_net_name); 109646492Swpaul 109746492Swpaul /* Program the nodename. */ 109846492Swpaul WI_SETSTR(WI_RID_NODENAME, sc->wi_node_name); 109946492Swpaul 110046492Swpaul /* Set our MAC address. */ 110146492Swpaul mac.wi_len = 4; 110246492Swpaul mac.wi_type = WI_RID_MAC_NODE; 110346492Swpaul bcopy((char *)&sc->arpcom.ac_enaddr, 110446492Swpaul (char *)&mac.wi_mac_addr, ETHER_ADDR_LEN); 110546492Swpaul wi_write_record(sc, (struct wi_ltv_gen *)&mac); 110646492Swpaul 110746492Swpaul /* Initialize promisc mode. */ 110846492Swpaul if (ifp->if_flags & IFF_PROMISC) { 110946492Swpaul WI_SETVAL(WI_RID_PROMISC, 1); 111046492Swpaul } else { 111146492Swpaul WI_SETVAL(WI_RID_PROMISC, 0); 111246492Swpaul } 111346492Swpaul 111446492Swpaul /* Set multicast filter. */ 111546492Swpaul wi_setmulti(sc); 111646492Swpaul 111746492Swpaul /* Enable desired port */ 111846492Swpaul wi_cmd(sc, WI_CMD_ENABLE|sc->wi_portnum, 0); 111946492Swpaul 112046492Swpaul if (wi_alloc_nicmem(sc, 1518 + sizeof(struct wi_frame) + 8, &id)) 112146492Swpaul printf("wi%d: mem allocation failed...\n", sc->wi_unit); 112246492Swpaul sc->wi_tx_data_id = id; 112346492Swpaul 112446492Swpaul if (wi_alloc_nicmem(sc, 1518 + sizeof(struct wi_frame) + 8, &id)) 112546492Swpaul printf("wi%d: mem allocation failed...\n", sc->wi_unit); 112646492Swpaul sc->wi_tx_mgmt_id = id; 112746492Swpaul 112846492Swpaul /* enable interrupts */ 112946492Swpaul CSR_WRITE_2(sc, WI_INT_EN, WI_INTRS); 113046492Swpaul 113146492Swpaul splx(s); 113246492Swpaul 113346492Swpaul ifp->if_flags |= IFF_RUNNING; 113446492Swpaul ifp->if_flags &= ~IFF_OACTIVE; 113546492Swpaul 113646492Swpaul sc->wi_stat_ch = timeout(wi_inquire, sc, hz * 60); 113746492Swpaul 113846492Swpaul return; 113946492Swpaul} 114046492Swpaul 114146492Swpaulstatic void wi_start(ifp) 114246492Swpaul struct ifnet *ifp; 114346492Swpaul{ 114446492Swpaul struct wi_softc *sc; 114546492Swpaul struct mbuf *m0; 114646492Swpaul struct wi_frame tx_frame; 114746492Swpaul struct ether_header *eh; 114846492Swpaul int id; 114946492Swpaul 115046492Swpaul sc = ifp->if_softc; 115146492Swpaul 115246492Swpaul if (sc->wi_gone) 115346492Swpaul return; 115446492Swpaul 115546492Swpaul if (ifp->if_flags & IFF_OACTIVE) 115646492Swpaul return; 115746492Swpaul 115846492Swpaul IF_DEQUEUE(&ifp->if_snd, m0); 115946492Swpaul if (m0 == NULL) 116046492Swpaul return; 116146492Swpaul 116246492Swpaul bzero((char *)&tx_frame, sizeof(tx_frame)); 116346492Swpaul id = sc->wi_tx_data_id; 116446492Swpaul eh = mtod(m0, struct ether_header *); 116546492Swpaul 116646492Swpaul /* 116746492Swpaul * Use RFC1042 encoding for IP and ARP datagrames, 116846492Swpaul * 802.3 for anything else. 116946492Swpaul */ 117046492Swpaul if (ntohs(eh->ether_type) == ETHERTYPE_IP || 117146492Swpaul ntohs(eh->ether_type) == ETHERTYPE_ARP || 117246492Swpaul ntohs(eh->ether_type) == ETHERTYPE_REVARP) { 117346492Swpaul bcopy((char *)&eh->ether_dhost, 117446492Swpaul (char *)&tx_frame.wi_addr1, ETHER_ADDR_LEN); 117546492Swpaul bcopy((char *)&eh->ether_shost, 117646492Swpaul (char *)&tx_frame.wi_addr2, ETHER_ADDR_LEN); 117746492Swpaul bcopy((char *)&eh->ether_dhost, 117846492Swpaul (char *)&tx_frame.wi_dst_addr, ETHER_ADDR_LEN); 117946492Swpaul bcopy((char *)&eh->ether_shost, 118046492Swpaul (char *)&tx_frame.wi_src_addr, ETHER_ADDR_LEN); 118146492Swpaul 118246492Swpaul tx_frame.wi_dat_len = m0->m_pkthdr.len - WI_SNAPHDR_LEN; 118346492Swpaul tx_frame.wi_frame_ctl = WI_FTYPE_DATA; 118446492Swpaul tx_frame.wi_dat[0] = htons(WI_SNAP_WORD0); 118546492Swpaul tx_frame.wi_dat[1] = htons(WI_SNAP_WORD1); 118646492Swpaul tx_frame.wi_len = htons(m0->m_pkthdr.len - WI_SNAPHDR_LEN); 118746492Swpaul tx_frame.wi_type = eh->ether_type; 118846492Swpaul 118946492Swpaul m_copydata(m0, sizeof(struct ether_header), 119046492Swpaul m0->m_pkthdr.len - sizeof(struct ether_header), 119146492Swpaul (caddr_t)&sc->wi_txbuf); 119246492Swpaul 119346492Swpaul wi_write_data(sc, id, 0, (caddr_t)&tx_frame, 119446492Swpaul sizeof(struct wi_frame)); 119546492Swpaul wi_write_data(sc, id, WI_802_11_OFFSET, (caddr_t)&sc->wi_txbuf, 119646492Swpaul (m0->m_pkthdr.len - sizeof(struct ether_header)) + 2); 119746492Swpaul } else { 119846492Swpaul tx_frame.wi_dat_len = m0->m_pkthdr.len; 119946492Swpaul 120046492Swpaul m_copydata(m0, 0, m0->m_pkthdr.len, (caddr_t)&sc->wi_txbuf); 120146492Swpaul 120246492Swpaul wi_write_data(sc, id, 0, (caddr_t)&tx_frame, 120346492Swpaul sizeof(struct wi_frame)); 120446492Swpaul wi_write_data(sc, id, WI_802_3_OFFSET, (caddr_t)&sc->wi_txbuf, 120546492Swpaul m0->m_pkthdr.len + 2); 120646492Swpaul } 120746492Swpaul 120846492Swpaul#if NBPFILTER > 0 120946492Swpaul /* 121046492Swpaul * If there's a BPF listner, bounce a copy of 121146492Swpaul * this frame to him. 121246492Swpaul */ 121346492Swpaul if (ifp->if_bpf) 121446492Swpaul bpf_mtap(ifp, m0); 121546492Swpaul#endif 121646492Swpaul 121746492Swpaul m_freem(m0); 121846492Swpaul 121946492Swpaul if (wi_cmd(sc, WI_CMD_TX|WI_RECLAIM, id)) 122046492Swpaul printf("wi%d: xmit failed\n", sc->wi_unit); 122146492Swpaul 122246492Swpaul ifp->if_flags |= IFF_OACTIVE; 122346492Swpaul 122446492Swpaul /* 122546492Swpaul * Set a timeout in case the chip goes out to lunch. 122646492Swpaul */ 122746492Swpaul ifp->if_timer = 5; 122846492Swpaul 122946492Swpaul return; 123046492Swpaul} 123146492Swpaul 123246492Swpaulstatic int wi_mgmt_xmit(sc, data, len) 123346492Swpaul struct wi_softc *sc; 123446492Swpaul caddr_t data; 123546492Swpaul int len; 123646492Swpaul{ 123746492Swpaul struct wi_frame tx_frame; 123846492Swpaul int id; 123946492Swpaul struct wi_80211_hdr *hdr; 124046492Swpaul caddr_t dptr; 124146492Swpaul 124246492Swpaul if (sc->wi_gone) 124346492Swpaul return(ENODEV); 124446492Swpaul 124546492Swpaul hdr = (struct wi_80211_hdr *)data; 124646492Swpaul dptr = data + sizeof(struct wi_80211_hdr); 124746492Swpaul 124846492Swpaul bzero((char *)&tx_frame, sizeof(tx_frame)); 124946492Swpaul id = sc->wi_tx_mgmt_id; 125046492Swpaul 125146492Swpaul bcopy((char *)hdr, (char *)&tx_frame.wi_frame_ctl, 125246492Swpaul sizeof(struct wi_80211_hdr)); 125346492Swpaul 125446492Swpaul tx_frame.wi_dat_len = len - WI_SNAPHDR_LEN; 125546492Swpaul tx_frame.wi_len = htons(len - WI_SNAPHDR_LEN); 125646492Swpaul 125746492Swpaul wi_write_data(sc, id, 0, (caddr_t)&tx_frame, sizeof(struct wi_frame)); 125846492Swpaul wi_write_data(sc, id, WI_802_11_OFFSET_RAW, dptr, 125946492Swpaul (len - sizeof(struct wi_80211_hdr)) + 2); 126046492Swpaul 126146492Swpaul if (wi_cmd(sc, WI_CMD_TX|WI_RECLAIM, id)) { 126246492Swpaul printf("wi%d: xmit failed\n", sc->wi_unit); 126346492Swpaul return(EIO); 126446492Swpaul } 126546492Swpaul 126646492Swpaul return(0); 126746492Swpaul} 126846492Swpaul 126946492Swpaulstatic void wi_stop(sc) 127046492Swpaul struct wi_softc *sc; 127146492Swpaul{ 127246492Swpaul struct ifnet *ifp; 127346492Swpaul 127446492Swpaul if (sc->wi_gone) 127546492Swpaul return; 127646492Swpaul 127746492Swpaul ifp = &sc->arpcom.ac_if; 127846492Swpaul 127946492Swpaul CSR_WRITE_2(sc, WI_INT_EN, 0); 128046492Swpaul wi_cmd(sc, WI_CMD_DISABLE|sc->wi_portnum, 0); 128146492Swpaul 128246492Swpaul untimeout(wi_inquire, sc, sc->wi_stat_ch); 128346492Swpaul 128446492Swpaul ifp->if_flags &= ~(IFF_RUNNING|IFF_OACTIVE); 128546492Swpaul 128646492Swpaul return; 128746492Swpaul} 128846492Swpaul 128946492Swpaulstatic void wi_watchdog(ifp) 129046492Swpaul struct ifnet *ifp; 129146492Swpaul{ 129246492Swpaul struct wi_softc *sc; 129346492Swpaul 129446492Swpaul sc = ifp->if_softc; 129546492Swpaul 129646492Swpaul printf("wi%d: device timeout\n", sc->wi_unit); 129746492Swpaul 129846492Swpaul wi_init(sc); 129946492Swpaul 130046492Swpaul ifp->if_oerrors++; 130146492Swpaul 130246492Swpaul return; 130346492Swpaul} 130446492Swpaul 130546492Swpaulstatic void wi_shutdown(howto, arg) 130646492Swpaul int howto; 130746492Swpaul void *arg; 130846492Swpaul{ 130946492Swpaul struct wi_softc *sc; 131046492Swpaul 131146492Swpaul sc = arg; 131246492Swpaul wi_stop(sc); 131346492Swpaul 131446492Swpaul return; 131546492Swpaul} 1316