if_wi.c revision 94472
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 3346492Swpaul/* 3446492Swpaul * Lucent WaveLAN/IEEE 802.11 PCMCIA driver for FreeBSD. 3546492Swpaul * 3646492Swpaul * Written by Bill Paul <wpaul@ctr.columbia.edu> 3746492Swpaul * Electrical Engineering Department 3846492Swpaul * Columbia University, New York City 3946492Swpaul */ 4046492Swpaul 4146492Swpaul/* 4247401Swpaul * The WaveLAN/IEEE adapter is the second generation of the WaveLAN 4346492Swpaul * from Lucent. Unlike the older cards, the new ones are programmed 4446492Swpaul * entirely via a firmware-driven controller called the Hermes. 4546492Swpaul * Unfortunately, Lucent will not release the Hermes programming manual 4646492Swpaul * without an NDA (if at all). What they do release is an API library 4746492Swpaul * called the HCF (Hardware Control Functions) which is supposed to 4846492Swpaul * do the device-specific operations of a device driver for you. The 4946492Swpaul * publically available version of the HCF library (the 'HCF Light') is 5047401Swpaul * a) extremely gross, b) lacks certain features, particularly support 5146492Swpaul * for 802.11 frames, and c) is contaminated by the GNU Public License. 5246492Swpaul * 5346492Swpaul * This driver does not use the HCF or HCF Light at all. Instead, it 5446492Swpaul * programs the Hermes controller directly, using information gleaned 5546492Swpaul * from the HCF Light code and corresponding documentation. 5646492Swpaul * 5746492Swpaul * This driver supports both the PCMCIA and ISA versions of the 5846492Swpaul * WaveLAN/IEEE cards. Note however that the ISA card isn't really 5946492Swpaul * anything of the sort: it's actually a PCMCIA bridge adapter 6046492Swpaul * that fits into an ISA slot, into which a PCMCIA WaveLAN card is 6146492Swpaul * inserted. Consequently, you need to use the pccard support for 6246492Swpaul * both the ISA and PCMCIA adapters. 6346492Swpaul */ 6446492Swpaul 6546492Swpaul#include <sys/param.h> 6646492Swpaul#include <sys/systm.h> 6746492Swpaul#include <sys/sockio.h> 6846492Swpaul#include <sys/mbuf.h> 6983366Sjulian#include <sys/proc.h> 7046492Swpaul#include <sys/kernel.h> 7146492Swpaul#include <sys/socket.h> 7253702Swpaul#include <sys/module.h> 7353702Swpaul#include <sys/bus.h> 7453702Swpaul#include <sys/syslog.h> 7553702Swpaul#include <sys/sysctl.h> 7646492Swpaul 7753702Swpaul#include <machine/bus.h> 7853702Swpaul#include <machine/resource.h> 7953702Swpaul#include <sys/rman.h> 8053702Swpaul 8146492Swpaul#include <net/if.h> 8246492Swpaul#include <net/if_arp.h> 8346492Swpaul#include <net/ethernet.h> 8446492Swpaul#include <net/if_dl.h> 8546492Swpaul#include <net/if_media.h> 8646492Swpaul#include <net/if_types.h> 8777217Sphk#include <net/if_ieee80211.h> 8846492Swpaul 8946492Swpaul#include <netinet/in.h> 9046492Swpaul#include <netinet/in_systm.h> 9146492Swpaul#include <netinet/in_var.h> 9246492Swpaul#include <netinet/ip.h> 9346492Swpaul#include <netinet/if_ether.h> 9446492Swpaul 9546492Swpaul#include <net/bpf.h> 9646492Swpaul 9770808Speter#include <dev/wi/if_wavelan_ieee.h> 9894405Simp#ifdef WI_HOSTAP 9994405Simp#include <dev/wi/wi_hostap.h> 10094405Simp#include <sys/random.h> 10194405Simp#endif 10293611Simp#include <dev/wi/if_wivar.h> 10370808Speter#include <dev/wi/if_wireg.h> 10446492Swpaul 10546492Swpaul#if !defined(lint) 10646492Swpaulstatic const char rcsid[] = 10750477Speter "$FreeBSD: head/sys/dev/wi/if_wi.c 94472 2002-04-12 03:42:37Z imp $"; 10846492Swpaul#endif 10946492Swpaul 11091693Simpstatic void wi_intr(void *); 11191693Simpstatic void wi_reset(struct wi_softc *); 11291693Simpstatic int wi_ioctl(struct ifnet *, u_long, caddr_t); 11391693Simpstatic void wi_init(void *); 11491693Simpstatic void wi_start(struct ifnet *); 11591693Simpstatic void wi_stop(struct wi_softc *); 11691693Simpstatic void wi_watchdog(struct ifnet *); 11791693Simpstatic void wi_rxeof(struct wi_softc *); 11891693Simpstatic void wi_txeof(struct wi_softc *, int); 11991693Simpstatic void wi_update_stats(struct wi_softc *); 12091693Simpstatic void wi_setmulti(struct wi_softc *); 12146492Swpaul 12292457Simpstatic int wi_cmd(struct wi_softc *, int, int, int, int); 12391693Simpstatic int wi_read_record(struct wi_softc *, struct wi_ltv_gen *); 12491693Simpstatic int wi_write_record(struct wi_softc *, struct wi_ltv_gen *); 12591693Simpstatic int wi_read_data(struct wi_softc *, int, int, caddr_t, int); 12691693Simpstatic int wi_write_data(struct wi_softc *, int, int, caddr_t, int); 12791693Simpstatic int wi_seek(struct wi_softc *, int, int, int); 12891693Simpstatic int wi_alloc_nicmem(struct wi_softc *, int, int *); 12991693Simpstatic void wi_inquire(void *); 13091693Simpstatic void wi_setdef(struct wi_softc *, struct wi_req *); 13146492Swpaul 13253702Swpaul#ifdef WICACHE 13353702Swpaulstatic 13491693Simpvoid wi_cache_store(struct wi_softc *, struct ether_header *, 13591693Simp struct mbuf *, unsigned short); 13653702Swpaul#endif 13753702Swpaul 13891693Simpstatic int wi_get_cur_ssid(struct wi_softc *, char *, int *); 13993825Simpstatic void wi_get_id(struct wi_softc *); 14091693Simpstatic int wi_media_change(struct ifnet *); 14191693Simpstatic void wi_media_status(struct ifnet *, struct ifmediareq *); 14277217Sphk 14393359Simpstatic int wi_get_debug(struct wi_softc *, struct wi_req *); 14493359Simpstatic int wi_set_debug(struct wi_softc *, struct wi_req *); 14593359Simp 14693611Simpdevclass_t wi_devclass; 14753702Swpaul 14893825Simpstruct wi_card_ident wi_card_ident[] = { 14993825Simp /* CARD_ID CARD_NAME FIRM_TYPE */ 15093825Simp { WI_NIC_LUCENT_ID, WI_NIC_LUCENT_STR, WI_LUCENT }, 15193825Simp { WI_NIC_SONY_ID, WI_NIC_SONY_STR, WI_LUCENT }, 15293825Simp { WI_NIC_LUCENT_EMB_ID, WI_NIC_LUCENT_EMB_STR, WI_LUCENT }, 15393825Simp { WI_NIC_EVB2_ID, WI_NIC_EVB2_STR, WI_INTERSIL }, 15493825Simp { WI_NIC_HWB3763_ID, WI_NIC_HWB3763_STR, WI_INTERSIL }, 15593825Simp { WI_NIC_HWB3163_ID, WI_NIC_HWB3163_STR, WI_INTERSIL }, 15693825Simp { WI_NIC_HWB3163B_ID, WI_NIC_HWB3163B_STR, WI_INTERSIL }, 15793825Simp { WI_NIC_EVB3_ID, WI_NIC_EVB3_STR, WI_INTERSIL }, 15893825Simp { WI_NIC_HWB1153_ID, WI_NIC_HWB1153_STR, WI_INTERSIL }, 15993825Simp { WI_NIC_P2_SST_ID, WI_NIC_P2_SST_STR, WI_INTERSIL }, 16093825Simp { WI_NIC_EVB2_SST_ID, WI_NIC_EVB2_SST_STR, WI_INTERSIL }, 16193825Simp { WI_NIC_3842_EVA_ID, WI_NIC_3842_EVA_STR, WI_INTERSIL }, 16293825Simp { WI_NIC_3842_PCMCIA_AMD_ID, WI_NIC_3842_PCMCIA_STR, WI_INTERSIL }, 16393825Simp { WI_NIC_3842_PCMCIA_SST_ID, WI_NIC_3842_PCMCIA_STR, WI_INTERSIL }, 16493825Simp { WI_NIC_3842_PCMCIA_ATM_ID, WI_NIC_3842_PCMCIA_STR, WI_INTERSIL }, 16593825Simp { WI_NIC_3842_MINI_AMD_ID, WI_NIC_3842_MINI_STR, WI_INTERSIL }, 16693825Simp { WI_NIC_3842_MINI_SST_ID, WI_NIC_3842_MINI_STR, WI_INTERSIL }, 16793825Simp { WI_NIC_3842_MINI_ATM_ID, WI_NIC_3842_MINI_STR, WI_INTERSIL }, 16893825Simp { WI_NIC_3842_PCI_AMD_ID, WI_NIC_3842_PCI_STR, WI_INTERSIL }, 16993825Simp { WI_NIC_3842_PCI_SST_ID, WI_NIC_3842_PCI_STR, WI_INTERSIL }, 17093825Simp { WI_NIC_3842_PCI_ATM_ID, WI_NIC_3842_PCI_STR, WI_INTERSIL }, 17193825Simp { WI_NIC_P3_PCMCIA_AMD_ID, WI_NIC_P3_PCMCIA_STR, WI_INTERSIL }, 17293825Simp { WI_NIC_P3_PCMCIA_SST_ID, WI_NIC_P3_PCMCIA_STR, WI_INTERSIL }, 17393825Simp { WI_NIC_P3_MINI_AMD_ID, WI_NIC_P3_MINI_STR, WI_INTERSIL }, 17493825Simp { WI_NIC_P3_MINI_SST_ID, WI_NIC_P3_MINI_STR, WI_INTERSIL }, 17593825Simp { 0, NULL, 0 }, 17693825Simp}; 17793825Simp 17893611Simpint 17993611Simpwi_generic_detach(dev) 18053702Swpaul device_t dev; 18146492Swpaul{ 18246492Swpaul struct wi_softc *sc; 18346492Swpaul struct ifnet *ifp; 18446492Swpaul 18553702Swpaul sc = device_get_softc(dev); 18667092Swpaul WI_LOCK(sc); 18746492Swpaul ifp = &sc->arpcom.ac_if; 18846492Swpaul 18946492Swpaul if (sc->wi_gone) { 19053702Swpaul device_printf(dev, "already unloaded\n"); 19167092Swpaul WI_UNLOCK(sc); 19253702Swpaul return(ENODEV); 19346492Swpaul } 19446492Swpaul 19553702Swpaul wi_stop(sc); 19658274Srwatson 19777217Sphk /* Delete all remaining media. */ 19877217Sphk ifmedia_removeall(&sc->ifmedia); 19977217Sphk 20063090Sarchie ether_ifdetach(ifp, ETHER_BPF_SUPPORTED); 20154277Swpaul bus_teardown_intr(dev, sc->irq, sc->wi_intrhand); 20253702Swpaul wi_free(dev); 20346492Swpaul sc->wi_gone = 1; 20446492Swpaul 20567092Swpaul WI_UNLOCK(sc); 20667092Swpaul mtx_destroy(&sc->wi_mtx); 20746492Swpaul 20846492Swpaul return(0); 20946492Swpaul} 21046492Swpaul 21193611Simpint 21274906Salfredwi_generic_attach(device_t dev) 21374906Salfred{ 21474906Salfred struct wi_softc *sc; 21574906Salfred struct wi_ltv_macaddr mac; 21674906Salfred struct wi_ltv_gen gen; 21774906Salfred struct ifnet *ifp; 21874906Salfred int error; 21974906Salfred 22094405Simp /* XXX maybe we need the splimp stuff here XXX */ 22174906Salfred sc = device_get_softc(dev); 22274906Salfred ifp = &sc->arpcom.ac_if; 22374906Salfred 22453702Swpaul error = bus_setup_intr(dev, sc->irq, INTR_TYPE_NET, 22574998Swpaul wi_intr, sc, &sc->wi_intrhand); 22653702Swpaul 22753702Swpaul if (error) { 22853702Swpaul device_printf(dev, "bus_setup_intr() failed! (%d)\n", error); 22953702Swpaul wi_free(dev); 23053702Swpaul return (error); 23153702Swpaul } 23253702Swpaul 23393818Sjhb mtx_init(&sc->wi_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK, 23493818Sjhb MTX_DEF | MTX_RECURSE); 23567092Swpaul WI_LOCK(sc); 23667092Swpaul 23746492Swpaul /* Reset the NIC. */ 23846492Swpaul wi_reset(sc); 23946492Swpaul 24076438Swpaul /* 24176438Swpaul * Read the station address. 24276438Swpaul * And do it twice. I've seen PRISM-based cards that return 24376438Swpaul * an error when trying to read it the first time, which causes 24476438Swpaul * the probe to fail. 24576438Swpaul */ 24646492Swpaul mac.wi_type = WI_RID_MAC_NODE; 24746492Swpaul mac.wi_len = 4; 24876457Sgrog wi_read_record(sc, (struct wi_ltv_gen *)&mac); 24975150Simp if ((error = wi_read_record(sc, (struct wi_ltv_gen *)&mac)) != 0) { 25075149Simp device_printf(dev, "mac read failed %d\n", error); 25175149Simp wi_free(dev); 25275149Simp return (error); 25375149Simp } 25446492Swpaul bcopy((char *)&mac.wi_mac_addr, 25546492Swpaul (char *)&sc->arpcom.ac_enaddr, ETHER_ADDR_LEN); 25646492Swpaul 25787383Simp device_printf(dev, "802.11 address: %6D\n", sc->arpcom.ac_enaddr, ":"); 25846492Swpaul 25993825Simp wi_get_id(sc); 26087383Simp 26146492Swpaul ifp->if_softc = sc; 26246492Swpaul ifp->if_unit = sc->wi_unit; 26346492Swpaul ifp->if_name = "wi"; 26446492Swpaul ifp->if_mtu = ETHERMTU; 26546492Swpaul ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; 26646492Swpaul ifp->if_ioctl = wi_ioctl; 26746492Swpaul ifp->if_output = ether_output; 26846492Swpaul ifp->if_start = wi_start; 26946492Swpaul ifp->if_watchdog = wi_watchdog; 27046492Swpaul ifp->if_init = wi_init; 27146492Swpaul ifp->if_baudrate = 10000000; 27246492Swpaul ifp->if_snd.ifq_maxlen = IFQ_MAXLEN; 27346492Swpaul 27446492Swpaul bzero(sc->wi_node_name, sizeof(sc->wi_node_name)); 27546492Swpaul bcopy(WI_DEFAULT_NODENAME, sc->wi_node_name, 27646492Swpaul sizeof(WI_DEFAULT_NODENAME) - 1); 27746492Swpaul 27846492Swpaul bzero(sc->wi_net_name, sizeof(sc->wi_net_name)); 27946492Swpaul bcopy(WI_DEFAULT_NETNAME, sc->wi_net_name, 28046492Swpaul sizeof(WI_DEFAULT_NETNAME) - 1); 28146492Swpaul 28246492Swpaul bzero(sc->wi_ibss_name, sizeof(sc->wi_ibss_name)); 28346492Swpaul bcopy(WI_DEFAULT_IBSS, sc->wi_ibss_name, 28446492Swpaul sizeof(WI_DEFAULT_IBSS) - 1); 28546492Swpaul 28646492Swpaul sc->wi_portnum = WI_DEFAULT_PORT; 28774139Sassar sc->wi_ptype = WI_PORTTYPE_BSS; 28846492Swpaul sc->wi_ap_density = WI_DEFAULT_AP_DENSITY; 28946492Swpaul sc->wi_rts_thresh = WI_DEFAULT_RTS_THRESH; 29046492Swpaul sc->wi_tx_rate = WI_DEFAULT_TX_RATE; 29146492Swpaul sc->wi_max_data_len = WI_DEFAULT_DATALEN; 29246492Swpaul sc->wi_create_ibss = WI_DEFAULT_CREATE_IBSS; 29346611Swpaul sc->wi_pm_enabled = WI_DEFAULT_PM_ENABLED; 29446611Swpaul sc->wi_max_sleep = WI_DEFAULT_MAX_SLEEP; 29591695Simp sc->wi_roaming = WI_DEFAULT_ROAMING; 29691695Simp sc->wi_authtype = WI_DEFAULT_AUTHTYPE; 29794405Simp sc->wi_authmode = IEEE80211_AUTH_OPEN; 29846492Swpaul 29946563Swpaul /* 30046563Swpaul * Read the default channel from the NIC. This may vary 30146563Swpaul * depending on the country where the NIC was purchased, so 30246563Swpaul * we can't hard-code a default and expect it to work for 30346563Swpaul * everyone. 30446563Swpaul */ 30546563Swpaul gen.wi_type = WI_RID_OWN_CHNL; 30646563Swpaul gen.wi_len = 2; 30746563Swpaul wi_read_record(sc, &gen); 30846563Swpaul sc->wi_channel = gen.wi_val; 30946563Swpaul 31056965Swpaul /* 31156965Swpaul * Find out if we support WEP on this card. 31256965Swpaul */ 31356965Swpaul gen.wi_type = WI_RID_WEP_AVAIL; 31456965Swpaul gen.wi_len = 2; 31556965Swpaul wi_read_record(sc, &gen); 31656965Swpaul sc->wi_has_wep = gen.wi_val; 31756965Swpaul 31870073Swpaul if (bootverbose) { 31994405Simp device_printf(sc->dev, "%s:wi_has_wep = %d\n", 32094405Simp __func__, sc->wi_has_wep); 32170073Swpaul } 32270073Swpaul 32394405Simp /* 32494405Simp * Find supported rates. 32594405Simp */ 32694405Simp gen.wi_type = WI_RID_TX_RATE; 32794405Simp gen.wi_len = 2; 32894405Simp wi_read_record(sc, &gen); 32994405Simp sc->wi_supprates = gen.wi_val; 33094405Simp 33146492Swpaul bzero((char *)&sc->wi_stats, sizeof(sc->wi_stats)); 33246492Swpaul 33346492Swpaul wi_init(sc); 33446492Swpaul wi_stop(sc); 33546492Swpaul 33677217Sphk ifmedia_init(&sc->ifmedia, 0, wi_media_change, wi_media_status); 33777217Sphk /* XXX: Should read from card capabilities */ 33877217Sphk#define ADD(m, c) ifmedia_add(&sc->ifmedia, (m), (c), NULL) 33977217Sphk ADD(IFM_MAKEWORD(IFM_IEEE80211, IFM_IEEE80211_DS1, 34077217Sphk IFM_IEEE80211_ADHOC, 0), 0); 34177217Sphk ADD(IFM_MAKEWORD(IFM_IEEE80211, IFM_IEEE80211_DS1, 0, 0), 0); 34277217Sphk ADD(IFM_MAKEWORD(IFM_IEEE80211, IFM_IEEE80211_DS2, 34377217Sphk IFM_IEEE80211_ADHOC, 0), 0); 34477217Sphk ADD(IFM_MAKEWORD(IFM_IEEE80211, IFM_IEEE80211_DS2, 0, 0), 0); 34577217Sphk ADD(IFM_MAKEWORD(IFM_IEEE80211, IFM_IEEE80211_DS5, 34677217Sphk IFM_IEEE80211_ADHOC, 0), 0); 34777217Sphk ADD(IFM_MAKEWORD(IFM_IEEE80211, IFM_IEEE80211_DS5, 0, 0), 0); 34877217Sphk ADD(IFM_MAKEWORD(IFM_IEEE80211, IFM_IEEE80211_DS11, 34977217Sphk IFM_IEEE80211_ADHOC, 0), 0); 35077217Sphk ADD(IFM_MAKEWORD(IFM_IEEE80211, IFM_IEEE80211_DS11, 0, 0), 0); 35177217Sphk ADD(IFM_MAKEWORD(IFM_IEEE80211, IFM_AUTO, 35277217Sphk IFM_IEEE80211_ADHOC, 0), 0); 35377217Sphk ADD(IFM_MAKEWORD(IFM_IEEE80211, IFM_AUTO, 0, 0), 0); 35494405Simp#ifdef IFM_IEEE80211_HOSTAP 35594405Simp if (sc->sc_firmware_type == WI_INTERSIL) { 35694405Simp ADD(IFM_MAKEWORD(IFM_IEEE80211, IFM_IEEE80211_DS1, 35794405Simp IFM_IEEE80211_HOSTAP, 0), 0); 35894405Simp ADD(IFM_MAKEWORD(IFM_IEEE80211, IFM_IEEE80211_DS2, 35994405Simp IFM_IEEE80211_HOSTAP, 0), 0); 36094405Simp ADD(IFM_MAKEWORD(IFM_IEEE80211, IFM_IEEE80211_DS5, 36194405Simp IFM_IEEE80211_HOSTAP, 0), 0); 36294405Simp ADD(IFM_MAKEWORD(IFM_IEEE80211, IFM_IEEE80211_DS11, 36394405Simp IFM_IEEE80211_HOSTAP, 0), 0); 36494405Simp ADD(IFM_MAKEWORD(IFM_IEEE80211, IFM_AUTO, 36594405Simp IFM_IEEE80211_HOSTAP, 0), 0); 36694405Simp } 36794405Simp#endif 36877217Sphk#undef ADD 36977217Sphk ifmedia_set(&sc->ifmedia, IFM_MAKEWORD(IFM_IEEE80211, IFM_AUTO, 37077217Sphk 0, 0)); 37177217Sphk 37277217Sphk 37346492Swpaul /* 37463090Sarchie * Call MI attach routine. 37546492Swpaul */ 37663090Sarchie ether_ifattach(ifp, ETHER_BPF_SUPPORTED); 37753702Swpaul callout_handle_init(&sc->wi_stat_ch); 37867092Swpaul WI_UNLOCK(sc); 37946492Swpaul 38046492Swpaul return(0); 38146492Swpaul} 38246492Swpaul 38387383Simpstatic void 38493825Simpwi_get_id(sc) 38587383Simp struct wi_softc *sc; 38687383Simp{ 38787383Simp struct wi_ltv_ver ver; 38893825Simp struct wi_card_ident *id; 38987383Simp 39087383Simp /* getting chip identity */ 39187383Simp memset(&ver, 0, sizeof(ver)); 39293567Simp ver.wi_type = WI_RID_CARD_ID; 39387383Simp ver.wi_len = 5; 39487383Simp wi_read_record(sc, (struct wi_ltv_gen *)&ver); 39593733Simp device_printf(sc->dev, "using "); 39693825Simp sc->sc_firmware_type = WI_NOTYPE; 39793825Simp for (id = wi_card_ident; id->card_name != NULL; id++) { 39893825Simp if (le16toh(ver.wi_ver[0]) == id->card_id) { 39993825Simp printf("%s", id->card_name); 40093825Simp sc->sc_firmware_type = id->firm_type; 40193825Simp break; 40293825Simp } 40393825Simp } 40493825Simp if (sc->sc_firmware_type == WI_NOTYPE) { 40593825Simp if (le16toh(ver.wi_ver[0]) & 0x8000) { 40693733Simp printf("Unknown PRISM2 chip"); 40793825Simp sc->sc_firmware_type = WI_INTERSIL; 40893825Simp } else { 40993733Simp printf("Unknown Lucent chip"); 41093825Simp sc->sc_firmware_type = WI_LUCENT; 41193825Simp } 41287383Simp } 41387383Simp 41493733Simp if (sc->sc_firmware_type != WI_LUCENT) { 41593733Simp /* get primary firmware version */ 41693733Simp memset(&ver, 0, sizeof(ver)); 41793733Simp ver.wi_type = WI_RID_PRI_IDENTITY; 41893733Simp ver.wi_len = 5; 41993733Simp wi_read_record(sc, (struct wi_ltv_gen *)&ver); 42093733Simp ver.wi_ver[1] = le16toh(ver.wi_ver[1]); 42193733Simp ver.wi_ver[2] = le16toh(ver.wi_ver[2]); 42293733Simp ver.wi_ver[3] = le16toh(ver.wi_ver[3]); 42393733Simp sc->sc_pri_firmware_ver = ver.wi_ver[2] * 10000 + 42493733Simp ver.wi_ver[3] * 100 + ver.wi_ver[1]; 42593733Simp } 42693733Simp 42793733Simp /* get station firmware version */ 42892457Simp memset(&ver, 0, sizeof(ver)); 42992457Simp ver.wi_type = WI_RID_STA_IDENTITY; 43092457Simp ver.wi_len = 5; 43192457Simp wi_read_record(sc, (struct wi_ltv_gen *)&ver); 43292457Simp ver.wi_ver[1] = le16toh(ver.wi_ver[1]); 43392457Simp ver.wi_ver[2] = le16toh(ver.wi_ver[2]); 43492457Simp ver.wi_ver[3] = le16toh(ver.wi_ver[3]); 43593733Simp sc->sc_sta_firmware_ver = ver.wi_ver[2] * 10000 + 43693733Simp ver.wi_ver[3] * 100 + ver.wi_ver[1]; 43793733Simp if (sc->sc_firmware_type == WI_INTERSIL && 43893733Simp (sc->sc_sta_firmware_ver == 10102 || 43993733Simp sc->sc_sta_firmware_ver == 20102)) { 44093733Simp struct wi_ltv_str sver; 44193733Simp char *p; 44287383Simp 44393733Simp memset(&sver, 0, sizeof(sver)); 44493733Simp sver.wi_type = WI_RID_SYMBOL_IDENTITY; 44593733Simp sver.wi_len = 7; 44693733Simp /* value should be "V2.00-11" */ 44793733Simp if (wi_read_record(sc, (struct wi_ltv_gen *)&sver) == 0 && 44893733Simp *(p = (char *)sver.wi_str) == 'V' && 44993733Simp p[2] == '.' && p[5] == '-' && p[8] == '\0') { 45093733Simp sc->sc_firmware_type = WI_SYMBOL; 45193733Simp sc->sc_sta_firmware_ver = (p[1] - '0') * 10000 + 45293733Simp (p[3] - '0') * 1000 + (p[4] - '0') * 100 + 45393733Simp (p[6] - '0') * 10 + (p[7] - '0'); 45493733Simp } 45593733Simp } 45693733Simp printf("\n"); 45793733Simp device_printf(sc->dev, "%s Firmware: ", 45893733Simp sc->sc_firmware_type == WI_LUCENT ? "Lucent" : 45993733Simp (sc->sc_firmware_type == WI_SYMBOL ? "Symbol" : "Intersil")); 46093733Simp 46193733Simp /* 46293733Simp * The primary firmware is only valid on Prism based chipsets 46393733Simp * (INTERSIL or SYMBOL). 46493733Simp */ 46593733Simp if (sc->sc_firmware_type != WI_LUCENT) 46693733Simp printf("Primary %u.%02u.%02u, ", sc->sc_pri_firmware_ver / 10000, 46793733Simp (sc->sc_pri_firmware_ver % 10000) / 100, 46893733Simp sc->sc_pri_firmware_ver % 100); 46993733Simp printf("Station %u.%02u.%02u\n", 47093733Simp sc->sc_sta_firmware_ver / 10000, (sc->sc_sta_firmware_ver % 10000) / 100, 47193733Simp sc->sc_sta_firmware_ver % 100); 47287383Simp return; 47387383Simp} 47487383Simp 47588546Salfredstatic void 47688546Salfredwi_rxeof(sc) 47746492Swpaul struct wi_softc *sc; 47846492Swpaul{ 47946492Swpaul struct ifnet *ifp; 48046492Swpaul struct ether_header *eh; 48146492Swpaul struct mbuf *m; 48246492Swpaul int id; 48346492Swpaul 48446492Swpaul ifp = &sc->arpcom.ac_if; 48546492Swpaul 48646492Swpaul id = CSR_READ_2(sc, WI_RX_FID); 48746492Swpaul 48893359Simp /* 48993359Simp * if we have the procframe flag set, disregard all this and just 49093359Simp * read the data from the device. 49193359Simp */ 49293359Simp if (sc->wi_procframe || sc->wi_debug.wi_monitor) { 49393359Simp struct wi_frame *rx_frame; 49493359Simp int datlen, hdrlen; 49546492Swpaul 49693359Simp /* first allocate mbuf for packet storage */ 49793359Simp MGETHDR(m, M_DONTWAIT, MT_DATA); 49893359Simp if (m == NULL) { 49993359Simp ifp->if_ierrors++; 50093359Simp return; 50193359Simp } 50293359Simp MCLGET(m, M_DONTWAIT); 50393359Simp if (!(m->m_flags & M_EXT)) { 50493359Simp m_freem(m); 50593359Simp ifp->if_ierrors++; 50693359Simp return; 50793359Simp } 50846492Swpaul 50993359Simp m->m_pkthdr.rcvif = ifp; 51046492Swpaul 51193359Simp /* now read wi_frame first so we know how much data to read */ 51293359Simp if (wi_read_data(sc, id, 0, mtod(m, caddr_t), 51393359Simp sizeof(struct wi_frame))) { 51493359Simp m_freem(m); 51593359Simp ifp->if_ierrors++; 51693359Simp return; 51793359Simp } 51846492Swpaul 51993359Simp rx_frame = mtod(m, struct wi_frame *); 52093359Simp 52193359Simp switch ((rx_frame->wi_status & WI_STAT_MAC_PORT) >> 8) { 52293359Simp case 7: 52393359Simp switch (rx_frame->wi_frame_ctl & WI_FCTL_FTYPE) { 52493359Simp case WI_FTYPE_DATA: 52593359Simp hdrlen = WI_DATA_HDRLEN; 52693359Simp datlen = rx_frame->wi_dat_len + WI_FCS_LEN; 52793359Simp break; 52893359Simp case WI_FTYPE_MGMT: 52993359Simp hdrlen = WI_MGMT_HDRLEN; 53093359Simp datlen = rx_frame->wi_dat_len + WI_FCS_LEN; 53193359Simp break; 53293359Simp case WI_FTYPE_CTL: 53393359Simp /* 53493359Simp * prism2 cards don't pass control packets 53593359Simp * down properly or consistently, so we'll only 53693359Simp * pass down the header. 53793359Simp */ 53893359Simp hdrlen = WI_CTL_HDRLEN; 53993359Simp datlen = 0; 54093359Simp break; 54193359Simp default: 54293359Simp device_printf(sc->dev, "received packet of " 54393359Simp "unknown type on port 7\n"); 54493359Simp m_freem(m); 54593359Simp ifp->if_ierrors++; 54693359Simp return; 54793359Simp } 54893359Simp break; 54993359Simp case 0: 55093359Simp hdrlen = WI_DATA_HDRLEN; 55193359Simp datlen = rx_frame->wi_dat_len + WI_FCS_LEN; 55293359Simp break; 55393359Simp default: 55493359Simp device_printf(sc->dev, "received packet on invalid " 55593359Simp "port (wi_status=0x%x)\n", rx_frame->wi_status); 55693359Simp m_freem(m); 55793359Simp ifp->if_ierrors++; 55893359Simp return; 55993359Simp } 56093359Simp 56193359Simp if ((hdrlen + datlen + 2) > MCLBYTES) { 56253702Swpaul device_printf(sc->dev, "oversized packet received " 56353702Swpaul "(wi_dat_len=%d, wi_status=0x%x)\n", 56493359Simp datlen, rx_frame->wi_status); 56548553Swpaul m_freem(m); 56648553Swpaul ifp->if_ierrors++; 56748553Swpaul return; 56848553Swpaul } 56946492Swpaul 57093359Simp if (wi_read_data(sc, id, hdrlen, mtod(m, caddr_t) + hdrlen, 57193359Simp datlen + 2)) { 57293359Simp m_freem(m); 57393359Simp ifp->if_ierrors++; 57493359Simp return; 57559328Swpaul } 57675373Salfred 57793359Simp m->m_pkthdr.len = m->m_len = hdrlen + datlen; 57846492Swpaul 57993359Simp ifp->if_ipackets++; 58093359Simp 58193359Simp /* Handle BPF listeners. */ 58293359Simp if (ifp->if_bpf) 58393359Simp bpf_mtap(ifp, m); 58493359Simp 58593359Simp m_freem(m); 58693359Simp } else { 58793359Simp struct wi_frame rx_frame; 58893359Simp 58993359Simp /* First read in the frame header */ 59093359Simp if (wi_read_data(sc, id, 0, (caddr_t)&rx_frame, 59193359Simp sizeof(rx_frame))) { 59246492Swpaul ifp->if_ierrors++; 59346492Swpaul return; 59446492Swpaul } 59593359Simp 59693359Simp if (rx_frame.wi_status & WI_STAT_ERRSTAT) { 59748553Swpaul ifp->if_ierrors++; 59848553Swpaul return; 59948553Swpaul } 60046492Swpaul 60193359Simp MGETHDR(m, M_DONTWAIT, MT_DATA); 60293359Simp if (m == NULL) { 60393359Simp ifp->if_ierrors++; 60493359Simp return; 60593359Simp } 60693359Simp MCLGET(m, M_DONTWAIT); 60793359Simp if (!(m->m_flags & M_EXT)) { 60846492Swpaul m_freem(m); 60946492Swpaul ifp->if_ierrors++; 61046492Swpaul return; 61146492Swpaul } 61246492Swpaul 61393359Simp eh = mtod(m, struct ether_header *); 61493359Simp m->m_pkthdr.rcvif = ifp; 61546492Swpaul 61694405Simp#ifdef WI_HOSTAP 61794405Simp if (rx_frame.wi_status == WI_STAT_MGMT && 61894405Simp sc->wi_ptype == WI_PORTTYPE_AP) { 61994405Simp if ((WI_802_11_OFFSET_RAW + rx_frame.wi_dat_len + 2) > 62094405Simp MCLBYTES) { 62194405Simp device_printf(sc->dev, "oversized mgmt packet " 62294405Simp "received in hostap mode " 62394405Simp "(wi_dat_len=%d, wi_status=0x%x)\n", 62494405Simp rx_frame.wi_dat_len, rx_frame.wi_status); 62594405Simp m_freem(m); 62694405Simp ifp->if_ierrors++; 62794405Simp return; 62894405Simp } 62994405Simp 63094405Simp /* Put the whole header in there. */ 63194405Simp bcopy(&rx_frame, mtod(m, void *), 63294405Simp sizeof(struct wi_frame)); 63394405Simp if (wi_read_data(sc, id, WI_802_11_OFFSET_RAW, 63494405Simp mtod(m, caddr_t) + WI_802_11_OFFSET_RAW, 63594405Simp rx_frame.wi_dat_len + 2)) { 63694405Simp m_freem(m); 63794405Simp ifp->if_ierrors++; 63894405Simp return; 63994405Simp } 64094405Simp m->m_pkthdr.len = m->m_len = 64194405Simp WI_802_11_OFFSET_RAW + rx_frame.wi_dat_len; 64294405Simp /* XXX: consider giving packet to bhp? */ 64394405Simp wihap_mgmt_input(sc, &rx_frame, m); 64494405Simp return; 64594405Simp } 64694405Simp#endif /* WI_HOSTAP */ 64794405Simp 64893359Simp if (rx_frame.wi_status == WI_STAT_1042 || 64993359Simp rx_frame.wi_status == WI_STAT_TUNNEL || 65093359Simp rx_frame.wi_status == WI_STAT_WMP_MSG) { 65193359Simp if((rx_frame.wi_dat_len + WI_SNAPHDR_LEN) > MCLBYTES) { 65293359Simp device_printf(sc->dev, 65393359Simp "oversized packet received " 65493359Simp "(wi_dat_len=%d, wi_status=0x%x)\n", 65593359Simp rx_frame.wi_dat_len, rx_frame.wi_status); 65693359Simp m_freem(m); 65793359Simp ifp->if_ierrors++; 65893359Simp return; 65993359Simp } 66093359Simp m->m_pkthdr.len = m->m_len = 66193359Simp rx_frame.wi_dat_len + WI_SNAPHDR_LEN; 66293359Simp 66393359Simp#if 0 66493359Simp bcopy((char *)&rx_frame.wi_addr1, 66593359Simp (char *)&eh->ether_dhost, ETHER_ADDR_LEN); 66693359Simp if (sc->wi_ptype == WI_PORTTYPE_ADHOC) { 66793359Simp bcopy((char *)&rx_frame.wi_addr2, 66893359Simp (char *)&eh->ether_shost, ETHER_ADDR_LEN); 66993359Simp } else { 67093359Simp bcopy((char *)&rx_frame.wi_addr3, 67193359Simp (char *)&eh->ether_shost, ETHER_ADDR_LEN); 67293359Simp } 67393359Simp#else 67493359Simp bcopy((char *)&rx_frame.wi_dst_addr, 67593359Simp (char *)&eh->ether_dhost, ETHER_ADDR_LEN); 67693359Simp bcopy((char *)&rx_frame.wi_src_addr, 67793359Simp (char *)&eh->ether_shost, ETHER_ADDR_LEN); 67893359Simp#endif 67993359Simp 68093359Simp bcopy((char *)&rx_frame.wi_type, 68193359Simp (char *)&eh->ether_type, ETHER_TYPE_LEN); 68293359Simp 68393359Simp if (wi_read_data(sc, id, WI_802_11_OFFSET, 68493359Simp mtod(m, caddr_t) + sizeof(struct ether_header), 68593359Simp m->m_len + 2)) { 68693359Simp m_freem(m); 68793359Simp ifp->if_ierrors++; 68893359Simp return; 68993359Simp } 69093359Simp } else { 69193359Simp if((rx_frame.wi_dat_len + 69293359Simp sizeof(struct ether_header)) > MCLBYTES) { 69393359Simp device_printf(sc->dev, 69493359Simp "oversized packet received " 69593359Simp "(wi_dat_len=%d, wi_status=0x%x)\n", 69693359Simp rx_frame.wi_dat_len, rx_frame.wi_status); 69793359Simp m_freem(m); 69893359Simp ifp->if_ierrors++; 69993359Simp return; 70093359Simp } 70193359Simp m->m_pkthdr.len = m->m_len = 70293359Simp rx_frame.wi_dat_len + sizeof(struct ether_header); 70393359Simp 70493359Simp if (wi_read_data(sc, id, WI_802_3_OFFSET, 70593359Simp mtod(m, caddr_t), m->m_len + 2)) { 70693359Simp m_freem(m); 70793359Simp ifp->if_ierrors++; 70893359Simp return; 70993359Simp } 71093359Simp } 71193359Simp 71293359Simp ifp->if_ipackets++; 71393359Simp 71494405Simp#ifdef WI_HOSTAP 71594405Simp if (sc->wi_ptype == WI_PORTTYPE_AP) { 71694405Simp /* 71794405Simp * Give host AP code first crack at data 71894405Simp * packets. If it decides to handle it (or 71994405Simp * drop it), it will return a non-zero. 72094405Simp * Otherwise, it is destined for this host. 72194405Simp */ 72294405Simp if (wihap_data_input(sc, &rx_frame, m)) 72394405Simp return; 72494405Simp } 72594405Simp#endif 72693359Simp /* Receive packet. */ 72793359Simp m_adj(m, sizeof(struct ether_header)); 72853702Swpaul#ifdef WICACHE 72993359Simp wi_cache_store(sc, eh, m, rx_frame.wi_q_info); 73053702Swpaul#endif 73193359Simp ether_input(ifp, eh, m); 73293359Simp } 73346492Swpaul} 73446492Swpaul 73588546Salfredstatic void 73688546Salfredwi_txeof(sc, status) 73746492Swpaul struct wi_softc *sc; 73846492Swpaul int status; 73946492Swpaul{ 74046492Swpaul struct ifnet *ifp; 74146492Swpaul 74246492Swpaul ifp = &sc->arpcom.ac_if; 74346492Swpaul 74446492Swpaul ifp->if_timer = 0; 74546492Swpaul ifp->if_flags &= ~IFF_OACTIVE; 74646492Swpaul 74746492Swpaul if (status & WI_EV_TX_EXC) 74846492Swpaul ifp->if_oerrors++; 74946492Swpaul else 75046492Swpaul ifp->if_opackets++; 75146492Swpaul 75246492Swpaul return; 75346492Swpaul} 75446492Swpaul 75588546Salfredvoid 75688546Salfredwi_inquire(xsc) 75746492Swpaul void *xsc; 75846492Swpaul{ 75946492Swpaul struct wi_softc *sc; 76046492Swpaul struct ifnet *ifp; 76194405Simp int s; 76246492Swpaul 76346492Swpaul sc = xsc; 76446492Swpaul ifp = &sc->arpcom.ac_if; 76546492Swpaul 76646492Swpaul sc->wi_stat_ch = timeout(wi_inquire, sc, hz * 60); 76746492Swpaul 76846492Swpaul /* Don't do this while we're transmitting */ 76946492Swpaul if (ifp->if_flags & IFF_OACTIVE) 77046492Swpaul return; 77146492Swpaul 77294405Simp s = splimp(); 77392457Simp wi_cmd(sc, WI_CMD_INQUIRE, WI_INFO_COUNTERS, 0, 0); 77494405Simp splx(s); 77546492Swpaul 77646492Swpaul return; 77746492Swpaul} 77846492Swpaul 77988546Salfredvoid 78088546Salfredwi_update_stats(sc) 78146492Swpaul struct wi_softc *sc; 78246492Swpaul{ 78346492Swpaul struct wi_ltv_gen gen; 78446492Swpaul u_int16_t id; 78546492Swpaul struct ifnet *ifp; 78646492Swpaul u_int32_t *ptr; 78775373Salfred int len, i; 78846492Swpaul u_int16_t t; 78946492Swpaul 79046492Swpaul ifp = &sc->arpcom.ac_if; 79146492Swpaul 79246492Swpaul id = CSR_READ_2(sc, WI_INFO_FID); 79346492Swpaul 79446492Swpaul wi_read_data(sc, id, 0, (char *)&gen, 4); 79546492Swpaul 79693359Simp /* 79793359Simp * if we just got our scan results, copy it over into the scan buffer 79893359Simp * so we can return it to anyone that asks for it. (add a little 79993359Simp * compatibility with the prism2 scanning mechanism) 80093359Simp */ 80193359Simp if (gen.wi_type == WI_INFO_SCAN_RESULTS) 80293359Simp { 80393359Simp sc->wi_scanbuf_len = gen.wi_len; 80493359Simp wi_read_data(sc, id, 4, (char *)sc->wi_scanbuf, 80593359Simp sc->wi_scanbuf_len * 2); 80693359Simp 80746492Swpaul return; 80893359Simp } 80993359Simp else if (gen.wi_type != WI_INFO_COUNTERS) 81093359Simp return; 81146492Swpaul 81275373Salfred len = (gen.wi_len - 1 < sizeof(sc->wi_stats) / 4) ? 81375373Salfred gen.wi_len - 1 : sizeof(sc->wi_stats) / 4; 81446492Swpaul ptr = (u_int32_t *)&sc->wi_stats; 81546492Swpaul 81675373Salfred for (i = 0; i < len - 1; i++) { 81746492Swpaul t = CSR_READ_2(sc, WI_DATA1); 81846492Swpaul#ifdef WI_HERMES_STATS_WAR 81946492Swpaul if (t > 0xF000) 82046492Swpaul t = ~t & 0xFFFF; 82146492Swpaul#endif 82246492Swpaul ptr[i] += t; 82346492Swpaul } 82446492Swpaul 82546492Swpaul ifp->if_collisions = sc->wi_stats.wi_tx_single_retries + 82646492Swpaul sc->wi_stats.wi_tx_multi_retries + 82746492Swpaul sc->wi_stats.wi_tx_retry_limit; 82846492Swpaul 82946492Swpaul return; 83046492Swpaul} 83146492Swpaul 83288546Salfredstatic void 83388546Salfredwi_intr(xsc) 83453702Swpaul void *xsc; 83546492Swpaul{ 83653702Swpaul struct wi_softc *sc = xsc; 83746492Swpaul struct ifnet *ifp; 83846492Swpaul u_int16_t status; 83946492Swpaul 84067092Swpaul WI_LOCK(sc); 84167092Swpaul 84246492Swpaul ifp = &sc->arpcom.ac_if; 84346492Swpaul 84475373Salfred if (sc->wi_gone || !(ifp->if_flags & IFF_UP)) { 84546492Swpaul CSR_WRITE_2(sc, WI_EVENT_ACK, 0xFFFF); 84646492Swpaul CSR_WRITE_2(sc, WI_INT_EN, 0); 84767092Swpaul WI_UNLOCK(sc); 84846492Swpaul return; 84946492Swpaul } 85046492Swpaul 85146492Swpaul /* Disable interrupts. */ 85246492Swpaul CSR_WRITE_2(sc, WI_INT_EN, 0); 85346492Swpaul 85446492Swpaul status = CSR_READ_2(sc, WI_EVENT_STAT); 85546492Swpaul CSR_WRITE_2(sc, WI_EVENT_ACK, ~WI_INTRS); 85646492Swpaul 85746492Swpaul if (status & WI_EV_RX) { 85846492Swpaul wi_rxeof(sc); 85946492Swpaul CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_RX); 86046492Swpaul } 86146492Swpaul 86246492Swpaul if (status & WI_EV_TX) { 86346492Swpaul wi_txeof(sc, status); 86446492Swpaul CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_TX); 86546492Swpaul } 86646492Swpaul 86746492Swpaul if (status & WI_EV_ALLOC) { 86846492Swpaul int id; 86975373Salfred 87046492Swpaul id = CSR_READ_2(sc, WI_ALLOC_FID); 87146492Swpaul CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_ALLOC); 87246492Swpaul if (id == sc->wi_tx_data_id) 87346492Swpaul wi_txeof(sc, status); 87446492Swpaul } 87546492Swpaul 87646492Swpaul if (status & WI_EV_INFO) { 87746492Swpaul wi_update_stats(sc); 87846492Swpaul CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_INFO); 87946492Swpaul } 88046492Swpaul 88146492Swpaul if (status & WI_EV_TX_EXC) { 88246492Swpaul wi_txeof(sc, status); 88346492Swpaul CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_TX_EXC); 88446492Swpaul } 88546492Swpaul 88646492Swpaul if (status & WI_EV_INFO_DROP) { 88746492Swpaul CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_INFO_DROP); 88846492Swpaul } 88946492Swpaul 89046492Swpaul /* Re-enable interrupts. */ 89146492Swpaul CSR_WRITE_2(sc, WI_INT_EN, WI_INTRS); 89246492Swpaul 89375373Salfred if (ifp->if_snd.ifq_head != NULL) { 89446492Swpaul wi_start(ifp); 89575373Salfred } 89646492Swpaul 89767092Swpaul WI_UNLOCK(sc); 89867092Swpaul 89946492Swpaul return; 90046492Swpaul} 90146492Swpaul 90288546Salfredstatic int 90392457Simpwi_cmd(sc, cmd, val0, val1, val2) 90446492Swpaul struct wi_softc *sc; 90546492Swpaul int cmd; 90692457Simp int val0; 90792457Simp int val1; 90892457Simp int val2; 90946492Swpaul{ 91046492Swpaul int i, s = 0; 91146492Swpaul 91270073Swpaul /* wait for the busy bit to clear */ 91375331Simp for (i = 500; i > 0; i--) { /* 5s */ 91470073Swpaul if (!(CSR_READ_2(sc, WI_COMMAND) & WI_CMD_BUSY)) { 91570073Swpaul break; 91670073Swpaul } 91770073Swpaul DELAY(10*1000); /* 10 m sec */ 91870073Swpaul } 91975229Salfred if (i == 0) { 92090580Sbrooks device_printf(sc->dev, "wi_cmd: busy bit won't clear.\n" ); 92170073Swpaul return(ETIMEDOUT); 92270073Swpaul } 92370073Swpaul 92492457Simp CSR_WRITE_2(sc, WI_PARAM0, val0); 92592457Simp CSR_WRITE_2(sc, WI_PARAM1, val1); 92692457Simp CSR_WRITE_2(sc, WI_PARAM2, val2); 92746492Swpaul CSR_WRITE_2(sc, WI_COMMAND, cmd); 92846492Swpaul 92946492Swpaul for (i = 0; i < WI_TIMEOUT; i++) { 93046492Swpaul /* 93146492Swpaul * Wait for 'command complete' bit to be 93246492Swpaul * set in the event status register. 93346492Swpaul */ 93490580Sbrooks s = CSR_READ_2(sc, WI_EVENT_STAT); 93590580Sbrooks if (s & WI_EV_CMD) { 93646492Swpaul /* Ack the event and read result code. */ 93746492Swpaul s = CSR_READ_2(sc, WI_STATUS); 93846492Swpaul CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_CMD); 93946492Swpaul#ifdef foo 94046492Swpaul if ((s & WI_CMD_CODE_MASK) != (cmd & WI_CMD_CODE_MASK)) 94146492Swpaul return(EIO); 94246492Swpaul#endif 94346492Swpaul if (s & WI_STAT_CMD_RESULT) 94446492Swpaul return(EIO); 94546492Swpaul break; 94646492Swpaul } 94790580Sbrooks DELAY(WI_DELAY); 94846492Swpaul } 94946492Swpaul 95090580Sbrooks if (i == WI_TIMEOUT) { 95190580Sbrooks device_printf(sc->dev, 95292457Simp "timeout in wi_cmd 0x%04x; event status 0x%04x\n", cmd, s); 95346492Swpaul return(ETIMEDOUT); 95490580Sbrooks } 95546492Swpaul 95646492Swpaul return(0); 95746492Swpaul} 95846492Swpaul 95988546Salfredstatic void 96088546Salfredwi_reset(sc) 96146492Swpaul struct wi_softc *sc; 96246492Swpaul{ 96394397Simp#define WI_INIT_TRIES 3 96475150Simp int i; 96593733Simp int tries; 96675149Simp 96793733Simp /* Symbol firmware cannot be initialized more than once */ 96893733Simp if (sc->sc_firmware_type == WI_SYMBOL && sc->sc_enabled) 96993733Simp return; 97093733Simp if (sc->sc_firmware_type == WI_SYMBOL) 97193733Simp tries = 1; 97293733Simp else 97393733Simp tries = WI_INIT_TRIES; 97493733Simp 97593733Simp for (i = 0; i < tries; i++) { 97692457Simp if (wi_cmd(sc, WI_CMD_INI, 0, 0, 0) == 0) 97775149Simp break; 97890580Sbrooks DELAY(WI_DELAY * 1000); 97975149Simp } 98094397Simp sc->sc_enabled = 1; 98194397Simp 98294397Simp if (i == tries) { 98353702Swpaul device_printf(sc->dev, "init failed\n"); 98494397Simp return; 98594397Simp } 98675373Salfred 98746492Swpaul CSR_WRITE_2(sc, WI_INT_EN, 0); 98846492Swpaul CSR_WRITE_2(sc, WI_EVENT_ACK, 0xFFFF); 98946492Swpaul 99046492Swpaul /* Calibrate timer. */ 99146492Swpaul WI_SETVAL(WI_RID_TICK_TIME, 8); 99270073Swpaul 99346492Swpaul return; 99446492Swpaul} 99546492Swpaul 99646492Swpaul/* 99746492Swpaul * Read an LTV record from the NIC. 99846492Swpaul */ 99988546Salfredstatic int 100088546Salfredwi_read_record(sc, ltv) 100146492Swpaul struct wi_softc *sc; 100246492Swpaul struct wi_ltv_gen *ltv; 100346492Swpaul{ 100446492Swpaul u_int16_t *ptr; 100546492Swpaul int i, len, code; 100670073Swpaul struct wi_ltv_gen *oltv, p2ltv; 100746492Swpaul 100870073Swpaul oltv = ltv; 100993733Simp if (sc->sc_firmware_type != WI_LUCENT) { 101070073Swpaul switch (ltv->wi_type) { 101170073Swpaul case WI_RID_ENCRYPTION: 101270073Swpaul p2ltv.wi_type = WI_RID_P2_ENCRYPTION; 101370073Swpaul p2ltv.wi_len = 2; 101470073Swpaul ltv = &p2ltv; 101570073Swpaul break; 101670073Swpaul case WI_RID_TX_CRYPT_KEY: 101770073Swpaul p2ltv.wi_type = WI_RID_P2_TX_CRYPT_KEY; 101870073Swpaul p2ltv.wi_len = 2; 101970073Swpaul ltv = &p2ltv; 102070073Swpaul break; 102170073Swpaul } 102270073Swpaul } 102370073Swpaul 102446492Swpaul /* Tell the NIC to enter record read mode. */ 102592457Simp if (wi_cmd(sc, WI_CMD_ACCESS|WI_ACCESS_READ, ltv->wi_type, 0, 0)) 102646492Swpaul return(EIO); 102746492Swpaul 102847789Swpaul /* Seek to the record. */ 102947789Swpaul if (wi_seek(sc, ltv->wi_type, 0, WI_BAP1)) 103047789Swpaul return(EIO); 103146492Swpaul 103246492Swpaul /* 103346492Swpaul * Read the length and record type and make sure they 103446492Swpaul * match what we expect (this verifies that we have enough 103547401Swpaul * room to hold all of the returned data). 103646492Swpaul */ 103746492Swpaul len = CSR_READ_2(sc, WI_DATA1); 103846492Swpaul if (len > ltv->wi_len) 103946492Swpaul return(ENOSPC); 104046492Swpaul code = CSR_READ_2(sc, WI_DATA1); 104146492Swpaul if (code != ltv->wi_type) 104246492Swpaul return(EIO); 104346492Swpaul 104446492Swpaul ltv->wi_len = len; 104546492Swpaul ltv->wi_type = code; 104646492Swpaul 104746492Swpaul /* Now read the data. */ 104846492Swpaul ptr = <v->wi_val; 104946492Swpaul for (i = 0; i < ltv->wi_len - 1; i++) 105046492Swpaul ptr[i] = CSR_READ_2(sc, WI_DATA1); 105146492Swpaul 105293733Simp if (sc->sc_firmware_type != WI_LUCENT) { 105370073Swpaul switch (oltv->wi_type) { 105470073Swpaul case WI_RID_TX_RATE: 105570073Swpaul case WI_RID_CUR_TX_RATE: 105670073Swpaul switch (ltv->wi_val) { 105770073Swpaul case 1: oltv->wi_val = 1; break; 105870073Swpaul case 2: oltv->wi_val = 2; break; 105970073Swpaul case 3: oltv->wi_val = 6; break; 106070073Swpaul case 4: oltv->wi_val = 5; break; 106170073Swpaul case 7: oltv->wi_val = 7; break; 106270073Swpaul case 8: oltv->wi_val = 11; break; 106370073Swpaul case 15: oltv->wi_val = 3; break; 106470073Swpaul default: oltv->wi_val = 0x100 + ltv->wi_val; break; 106570073Swpaul } 106670073Swpaul break; 106770073Swpaul case WI_RID_ENCRYPTION: 106870073Swpaul oltv->wi_len = 2; 106970073Swpaul if (ltv->wi_val & 0x01) 107070073Swpaul oltv->wi_val = 1; 107170073Swpaul else 107270073Swpaul oltv->wi_val = 0; 107370073Swpaul break; 107470073Swpaul case WI_RID_TX_CRYPT_KEY: 107570073Swpaul oltv->wi_len = 2; 107670073Swpaul oltv->wi_val = ltv->wi_val; 107770073Swpaul break; 107894405Simp case WI_RID_CNFAUTHMODE: 107991695Simp oltv->wi_len = 2; 108091695Simp if (le16toh(ltv->wi_val) & 0x01) 108191695Simp oltv->wi_val = htole16(1); 108291695Simp else if (le16toh(ltv->wi_val) & 0x02) 108391695Simp oltv->wi_val = htole16(2); 108491695Simp break; 108570073Swpaul } 108670073Swpaul } 108770073Swpaul 108846492Swpaul return(0); 108946492Swpaul} 109046492Swpaul 109146492Swpaul/* 109246492Swpaul * Same as read, except we inject data instead of reading it. 109346492Swpaul */ 109488546Salfredstatic int 109588546Salfredwi_write_record(sc, ltv) 109646492Swpaul struct wi_softc *sc; 109746492Swpaul struct wi_ltv_gen *ltv; 109846492Swpaul{ 109946492Swpaul u_int16_t *ptr; 110046492Swpaul int i; 110170073Swpaul struct wi_ltv_gen p2ltv; 110246492Swpaul 110393733Simp if (sc->sc_firmware_type != WI_LUCENT) { 110470073Swpaul switch (ltv->wi_type) { 110570073Swpaul case WI_RID_TX_RATE: 110670073Swpaul p2ltv.wi_type = WI_RID_TX_RATE; 110770073Swpaul p2ltv.wi_len = 2; 110870073Swpaul switch (ltv->wi_val) { 110970073Swpaul case 1: p2ltv.wi_val = 1; break; 111070073Swpaul case 2: p2ltv.wi_val = 2; break; 111170073Swpaul case 3: p2ltv.wi_val = 15; break; 111270073Swpaul case 5: p2ltv.wi_val = 4; break; 111370073Swpaul case 6: p2ltv.wi_val = 3; break; 111470073Swpaul case 7: p2ltv.wi_val = 7; break; 111570073Swpaul case 11: p2ltv.wi_val = 8; break; 111670073Swpaul default: return EINVAL; 111770073Swpaul } 111870073Swpaul ltv = &p2ltv; 111970073Swpaul break; 112070073Swpaul case WI_RID_ENCRYPTION: 112170073Swpaul p2ltv.wi_type = WI_RID_P2_ENCRYPTION; 112270073Swpaul p2ltv.wi_len = 2; 112394405Simp if (le16toh(ltv->wi_val)) { 112493825Simp p2ltv.wi_val =htole16(PRIVACY_INVOKED | 112593825Simp EXCLUDE_UNENCRYPTED); 112694405Simp#ifdef WI_HOSTAP 112794405Simp if (sc->wi_ptype == WI_PORTTYPE_AP) 112894405Simp /* 112994405Simp * Disable tx encryption... 113094405Simp * it's broken. 113194405Simp */ 113294405Simp p2ltv.wi_val |= htole16(HOST_ENCRYPT); 113394405Simp#endif 113494405Simp } else 113593825Simp p2ltv.wi_val = 113693825Simp htole16(HOST_ENCRYPT | HOST_DECRYPT); 113770073Swpaul ltv = &p2ltv; 113870073Swpaul break; 113970073Swpaul case WI_RID_TX_CRYPT_KEY: 114070073Swpaul p2ltv.wi_type = WI_RID_P2_TX_CRYPT_KEY; 114170073Swpaul p2ltv.wi_len = 2; 114270073Swpaul p2ltv.wi_val = ltv->wi_val; 114370073Swpaul ltv = &p2ltv; 114470073Swpaul break; 114570073Swpaul case WI_RID_DEFLT_CRYPT_KEYS: 114670073Swpaul { 114770073Swpaul int error; 114891548Sbrooks int keylen; 114970073Swpaul struct wi_ltv_str ws; 115074998Swpaul struct wi_ltv_keys *wk = 115174998Swpaul (struct wi_ltv_keys *)ltv; 115274998Swpaul 115391548Sbrooks keylen = wk->wi_keys[sc->wi_tx_key].wi_keylen; 115491548Sbrooks 115570073Swpaul for (i = 0; i < 4; i++) { 115691548Sbrooks bzero(&ws, sizeof(ws)); 115791548Sbrooks ws.wi_len = (keylen > 5) ? 8 : 4; 115870073Swpaul ws.wi_type = WI_RID_P2_CRYPT_KEY0 + i; 115974998Swpaul memcpy(ws.wi_str, 116091548Sbrooks &wk->wi_keys[i].wi_keydat, keylen); 116170073Swpaul error = wi_write_record(sc, 116270073Swpaul (struct wi_ltv_gen *)&ws); 116370073Swpaul if (error) 116470073Swpaul return error; 116570073Swpaul } 116670073Swpaul return 0; 116770073Swpaul } 116894405Simp case WI_RID_CNFAUTHMODE: 116994405Simp p2ltv.wi_type = WI_RID_CNFAUTHMODE; 117091695Simp p2ltv.wi_len = 2; 117191695Simp if (le16toh(ltv->wi_val) == 1) 117291695Simp p2ltv.wi_val = htole16(0x01); 117391695Simp else if (le16toh(ltv->wi_val) == 2) 117491695Simp p2ltv.wi_val = htole16(0x02); 117591695Simp ltv = &p2ltv; 117691695Simp break; 117770073Swpaul } 117870073Swpaul } 117970073Swpaul 118047789Swpaul if (wi_seek(sc, ltv->wi_type, 0, WI_BAP1)) 118147789Swpaul return(EIO); 118246492Swpaul 118346492Swpaul CSR_WRITE_2(sc, WI_DATA1, ltv->wi_len); 118446492Swpaul CSR_WRITE_2(sc, WI_DATA1, ltv->wi_type); 118546492Swpaul 118646492Swpaul ptr = <v->wi_val; 118746492Swpaul for (i = 0; i < ltv->wi_len - 1; i++) 118846492Swpaul CSR_WRITE_2(sc, WI_DATA1, ptr[i]); 118946492Swpaul 119092457Simp if (wi_cmd(sc, WI_CMD_ACCESS|WI_ACCESS_WRITE, ltv->wi_type, 0, 0)) 119146492Swpaul return(EIO); 119246492Swpaul 119346492Swpaul return(0); 119446492Swpaul} 119546492Swpaul 119688546Salfredstatic int 119788546Salfredwi_seek(sc, id, off, chan) 119846492Swpaul struct wi_softc *sc; 119946492Swpaul int id, off, chan; 120046492Swpaul{ 120146492Swpaul int i; 120246492Swpaul int selreg, offreg; 120375373Salfred int status; 120446492Swpaul 120546492Swpaul switch (chan) { 120646492Swpaul case WI_BAP0: 120746492Swpaul selreg = WI_SEL0; 120846492Swpaul offreg = WI_OFF0; 120946492Swpaul break; 121046492Swpaul case WI_BAP1: 121146492Swpaul selreg = WI_SEL1; 121246492Swpaul offreg = WI_OFF1; 121346492Swpaul break; 121446492Swpaul default: 121553702Swpaul device_printf(sc->dev, "invalid data path: %x\n", chan); 121646492Swpaul return(EIO); 121746492Swpaul } 121846492Swpaul 121946492Swpaul CSR_WRITE_2(sc, selreg, id); 122046492Swpaul CSR_WRITE_2(sc, offreg, off); 122146492Swpaul 122246492Swpaul for (i = 0; i < WI_TIMEOUT; i++) { 122375373Salfred status = CSR_READ_2(sc, offreg); 122475373Salfred if (!(status & (WI_OFF_BUSY|WI_OFF_ERR))) 122546492Swpaul break; 122690580Sbrooks DELAY(WI_DELAY); 122746492Swpaul } 122846492Swpaul 122975373Salfred if (i == WI_TIMEOUT) { 123075373Salfred device_printf(sc->dev, "timeout in wi_seek to %x/%x; last status %x\n", 123175373Salfred id, off, status); 123246492Swpaul return(ETIMEDOUT); 123375373Salfred } 123446492Swpaul 123546492Swpaul return(0); 123646492Swpaul} 123746492Swpaul 123888546Salfredstatic int 123988546Salfredwi_read_data(sc, id, off, buf, len) 124046492Swpaul struct wi_softc *sc; 124146492Swpaul int id, off; 124246492Swpaul caddr_t buf; 124346492Swpaul int len; 124446492Swpaul{ 124546492Swpaul int i; 124646492Swpaul u_int16_t *ptr; 124746492Swpaul 124846492Swpaul if (wi_seek(sc, id, off, WI_BAP1)) 124946492Swpaul return(EIO); 125046492Swpaul 125146492Swpaul ptr = (u_int16_t *)buf; 125246492Swpaul for (i = 0; i < len / 2; i++) 125346492Swpaul ptr[i] = CSR_READ_2(sc, WI_DATA1); 125446492Swpaul 125546492Swpaul return(0); 125646492Swpaul} 125746492Swpaul 125846492Swpaul/* 125946492Swpaul * According to the comments in the HCF Light code, there is a bug in 126046492Swpaul * the Hermes (or possibly in certain Hermes firmware revisions) where 126146492Swpaul * the chip's internal autoincrement counter gets thrown off during 126246492Swpaul * data writes: the autoincrement is missed, causing one data word to 126346492Swpaul * be overwritten and subsequent words to be written to the wrong memory 126446492Swpaul * locations. The end result is that we could end up transmitting bogus 126546492Swpaul * frames without realizing it. The workaround for this is to write a 126646492Swpaul * couple of extra guard words after the end of the transfer, then 126746492Swpaul * attempt to read then back. If we fail to locate the guard words where 126846492Swpaul * we expect them, we preform the transfer over again. 126946492Swpaul */ 127088546Salfredstatic int 127188546Salfredwi_write_data(sc, id, off, buf, len) 127246492Swpaul struct wi_softc *sc; 127346492Swpaul int id, off; 127446492Swpaul caddr_t buf; 127546492Swpaul int len; 127646492Swpaul{ 127746492Swpaul int i; 127846492Swpaul u_int16_t *ptr; 127974838Salfred#ifdef WI_HERMES_AUTOINC_WAR 128074838Salfred int retries; 128146492Swpaul 128275373Salfred retries = 512; 128346492Swpaulagain: 128446492Swpaul#endif 128546492Swpaul 128646492Swpaul if (wi_seek(sc, id, off, WI_BAP0)) 128746492Swpaul return(EIO); 128846492Swpaul 128946492Swpaul ptr = (u_int16_t *)buf; 129046492Swpaul for (i = 0; i < (len / 2); i++) 129146492Swpaul CSR_WRITE_2(sc, WI_DATA0, ptr[i]); 129246492Swpaul 129346492Swpaul#ifdef WI_HERMES_AUTOINC_WAR 129446492Swpaul CSR_WRITE_2(sc, WI_DATA0, 0x1234); 129546492Swpaul CSR_WRITE_2(sc, WI_DATA0, 0x5678); 129646492Swpaul 129746492Swpaul if (wi_seek(sc, id, off + len, WI_BAP0)) 129846492Swpaul return(EIO); 129946492Swpaul 130046492Swpaul if (CSR_READ_2(sc, WI_DATA0) != 0x1234 || 130174998Swpaul CSR_READ_2(sc, WI_DATA0) != 0x5678) { 130274838Salfred if (--retries >= 0) 130374838Salfred goto again; 130474838Salfred device_printf(sc->dev, "wi_write_data device timeout\n"); 130574838Salfred return (EIO); 130674838Salfred } 130746492Swpaul#endif 130846492Swpaul 130946492Swpaul return(0); 131046492Swpaul} 131146492Swpaul 131246492Swpaul/* 131346492Swpaul * Allocate a region of memory inside the NIC and zero 131446492Swpaul * it out. 131546492Swpaul */ 131688546Salfredstatic int 131788546Salfredwi_alloc_nicmem(sc, len, id) 131846492Swpaul struct wi_softc *sc; 131946492Swpaul int len; 132046492Swpaul int *id; 132146492Swpaul{ 132246492Swpaul int i; 132346492Swpaul 132492457Simp if (wi_cmd(sc, WI_CMD_ALLOC_MEM, len, 0, 0)) { 132574998Swpaul device_printf(sc->dev, 132674998Swpaul "failed to allocate %d bytes on NIC\n", len); 132746492Swpaul return(ENOMEM); 132846492Swpaul } 132946492Swpaul 133046492Swpaul for (i = 0; i < WI_TIMEOUT; i++) { 133146492Swpaul if (CSR_READ_2(sc, WI_EVENT_STAT) & WI_EV_ALLOC) 133246492Swpaul break; 133390580Sbrooks DELAY(WI_DELAY); 133446492Swpaul } 133546492Swpaul 133675373Salfred if (i == WI_TIMEOUT) { 133775373Salfred device_printf(sc->dev, "time out allocating memory on card\n"); 133846492Swpaul return(ETIMEDOUT); 133975373Salfred } 134046492Swpaul 134146492Swpaul CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_ALLOC); 134246492Swpaul *id = CSR_READ_2(sc, WI_ALLOC_FID); 134346492Swpaul 134475373Salfred if (wi_seek(sc, *id, 0, WI_BAP0)) { 134575373Salfred device_printf(sc->dev, "seek failed while allocating memory on card\n"); 134647789Swpaul return(EIO); 134775373Salfred } 134846492Swpaul 134946492Swpaul for (i = 0; i < len / 2; i++) 135046492Swpaul CSR_WRITE_2(sc, WI_DATA0, 0); 135146492Swpaul 135246492Swpaul return(0); 135346492Swpaul} 135446492Swpaul 135588546Salfredstatic void 135688546Salfredwi_setmulti(sc) 135746492Swpaul struct wi_softc *sc; 135846492Swpaul{ 135946492Swpaul struct ifnet *ifp; 136046492Swpaul int i = 0; 136146492Swpaul struct ifmultiaddr *ifma; 136246492Swpaul struct wi_ltv_mcast mcast; 136346492Swpaul 136446492Swpaul ifp = &sc->arpcom.ac_if; 136546492Swpaul 136646492Swpaul bzero((char *)&mcast, sizeof(mcast)); 136746492Swpaul 136893756Simp mcast.wi_type = WI_RID_MCAST_LIST; 136946492Swpaul mcast.wi_len = (3 * 16) + 1; 137046492Swpaul 137146492Swpaul if (ifp->if_flags & IFF_ALLMULTI || ifp->if_flags & IFF_PROMISC) { 137246492Swpaul wi_write_record(sc, (struct wi_ltv_gen *)&mcast); 137346492Swpaul return; 137446492Swpaul } 137546492Swpaul 137672084Sphk TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { 137746492Swpaul if (ifma->ifma_addr->sa_family != AF_LINK) 137846492Swpaul continue; 137946492Swpaul if (i < 16) { 138046492Swpaul bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr), 138146492Swpaul (char *)&mcast.wi_mcast[i], ETHER_ADDR_LEN); 138246492Swpaul i++; 138346492Swpaul } else { 138446492Swpaul bzero((char *)&mcast, sizeof(mcast)); 138546492Swpaul break; 138646492Swpaul } 138746492Swpaul } 138846492Swpaul 138946492Swpaul mcast.wi_len = (i * 3) + 1; 139046492Swpaul wi_write_record(sc, (struct wi_ltv_gen *)&mcast); 139146492Swpaul 139246492Swpaul return; 139346492Swpaul} 139446492Swpaul 139588546Salfredstatic void 139688546Salfredwi_setdef(sc, wreq) 139746492Swpaul struct wi_softc *sc; 139846492Swpaul struct wi_req *wreq; 139946492Swpaul{ 140046492Swpaul struct sockaddr_dl *sdl; 140146492Swpaul struct ifaddr *ifa; 140246492Swpaul struct ifnet *ifp; 140346492Swpaul 140446492Swpaul ifp = &sc->arpcom.ac_if; 140546492Swpaul 140646492Swpaul switch(wreq->wi_type) { 140746492Swpaul case WI_RID_MAC_NODE: 140883130Sjlemon ifa = ifaddr_byindex(ifp->if_index); 140946492Swpaul sdl = (struct sockaddr_dl *)ifa->ifa_addr; 141046492Swpaul bcopy((char *)&wreq->wi_val, (char *)&sc->arpcom.ac_enaddr, 141146492Swpaul ETHER_ADDR_LEN); 141246492Swpaul bcopy((char *)&wreq->wi_val, LLADDR(sdl), ETHER_ADDR_LEN); 141346492Swpaul break; 141446492Swpaul case WI_RID_PORTTYPE: 141591695Simp sc->wi_ptype = le16toh(wreq->wi_val[0]); 141646492Swpaul break; 141746492Swpaul case WI_RID_TX_RATE: 141891695Simp sc->wi_tx_rate = le16toh(wreq->wi_val[0]); 141946492Swpaul break; 142046492Swpaul case WI_RID_MAX_DATALEN: 142191695Simp sc->wi_max_data_len = le16toh(wreq->wi_val[0]); 142246492Swpaul break; 142346492Swpaul case WI_RID_RTS_THRESH: 142491695Simp sc->wi_rts_thresh = le16toh(wreq->wi_val[0]); 142546492Swpaul break; 142646492Swpaul case WI_RID_SYSTEM_SCALE: 142791695Simp sc->wi_ap_density = le16toh(wreq->wi_val[0]); 142846492Swpaul break; 142946492Swpaul case WI_RID_CREATE_IBSS: 143091695Simp sc->wi_create_ibss = le16toh(wreq->wi_val[0]); 143146492Swpaul break; 143246563Swpaul case WI_RID_OWN_CHNL: 143391695Simp sc->wi_channel = le16toh(wreq->wi_val[0]); 143446563Swpaul break; 143546492Swpaul case WI_RID_NODENAME: 143646492Swpaul bzero(sc->wi_node_name, sizeof(sc->wi_node_name)); 143746492Swpaul bcopy((char *)&wreq->wi_val[1], sc->wi_node_name, 30); 143846492Swpaul break; 143946492Swpaul case WI_RID_DESIRED_SSID: 144046492Swpaul bzero(sc->wi_net_name, sizeof(sc->wi_net_name)); 144146492Swpaul bcopy((char *)&wreq->wi_val[1], sc->wi_net_name, 30); 144246492Swpaul break; 144346492Swpaul case WI_RID_OWN_SSID: 144446492Swpaul bzero(sc->wi_ibss_name, sizeof(sc->wi_ibss_name)); 144546492Swpaul bcopy((char *)&wreq->wi_val[1], sc->wi_ibss_name, 30); 144646492Swpaul break; 144746611Swpaul case WI_RID_PM_ENABLED: 144891695Simp sc->wi_pm_enabled = le16toh(wreq->wi_val[0]); 144946611Swpaul break; 145091695Simp case WI_RID_MICROWAVE_OVEN: 145191695Simp sc->wi_mor_enabled = le16toh(wreq->wi_val[0]); 145291695Simp break; 145346611Swpaul case WI_RID_MAX_SLEEP: 145491695Simp sc->wi_max_sleep = le16toh(wreq->wi_val[0]); 145546611Swpaul break; 145694405Simp case WI_RID_CNFAUTHMODE: 145791695Simp sc->wi_authtype = le16toh(wreq->wi_val[0]); 145891695Simp break; 145991695Simp case WI_RID_ROAMING_MODE: 146091695Simp sc->wi_roaming = le16toh(wreq->wi_val[0]); 146191695Simp break; 146256965Swpaul case WI_RID_ENCRYPTION: 146391695Simp sc->wi_use_wep = le16toh(wreq->wi_val[0]); 146456965Swpaul break; 146556965Swpaul case WI_RID_TX_CRYPT_KEY: 146691695Simp sc->wi_tx_key = le16toh(wreq->wi_val[0]); 146756965Swpaul break; 146856965Swpaul case WI_RID_DEFLT_CRYPT_KEYS: 146956965Swpaul bcopy((char *)wreq, (char *)&sc->wi_keys, 147056965Swpaul sizeof(struct wi_ltv_keys)); 147156965Swpaul break; 147246492Swpaul default: 147346492Swpaul break; 147446492Swpaul } 147546492Swpaul 147646563Swpaul /* Reinitialize WaveLAN. */ 147746563Swpaul wi_init(sc); 147846563Swpaul 147946492Swpaul return; 148046492Swpaul} 148146492Swpaul 148288546Salfredstatic int 148388546Salfredwi_ioctl(ifp, command, data) 148446492Swpaul struct ifnet *ifp; 148546492Swpaul u_long command; 148646492Swpaul caddr_t data; 148746492Swpaul{ 148867092Swpaul int error = 0; 148977217Sphk int len; 149077217Sphk u_int8_t tmpkey[14]; 149177217Sphk char tmpssid[IEEE80211_NWID_LEN]; 149246492Swpaul struct wi_softc *sc; 149346492Swpaul struct wi_req wreq; 149446492Swpaul struct ifreq *ifr; 149577217Sphk struct ieee80211req *ireq; 149693593Sjhb struct thread *td = curthread; 149746492Swpaul 149846492Swpaul sc = ifp->if_softc; 149967092Swpaul WI_LOCK(sc); 150046492Swpaul ifr = (struct ifreq *)data; 150177217Sphk ireq = (struct ieee80211req *)data; 150246492Swpaul 150361818Sroberto if (sc->wi_gone) { 150461818Sroberto error = ENODEV; 150561818Sroberto goto out; 150661818Sroberto } 150746492Swpaul 150846492Swpaul switch(command) { 150946492Swpaul case SIOCSIFADDR: 151046492Swpaul case SIOCGIFADDR: 151146492Swpaul case SIOCSIFMTU: 151246492Swpaul error = ether_ioctl(ifp, command, data); 151346492Swpaul break; 151446492Swpaul case SIOCSIFFLAGS: 151546492Swpaul if (ifp->if_flags & IFF_UP) { 151646492Swpaul if (ifp->if_flags & IFF_RUNNING && 151746492Swpaul ifp->if_flags & IFF_PROMISC && 151846492Swpaul !(sc->wi_if_flags & IFF_PROMISC)) { 151946492Swpaul WI_SETVAL(WI_RID_PROMISC, 1); 152046492Swpaul } else if (ifp->if_flags & IFF_RUNNING && 152146492Swpaul !(ifp->if_flags & IFF_PROMISC) && 152246492Swpaul sc->wi_if_flags & IFF_PROMISC) { 152346492Swpaul WI_SETVAL(WI_RID_PROMISC, 0); 152446492Swpaul } else 152546492Swpaul wi_init(sc); 152646492Swpaul } else { 152746492Swpaul if (ifp->if_flags & IFF_RUNNING) { 152846492Swpaul wi_stop(sc); 152946492Swpaul } 153046492Swpaul } 153146492Swpaul sc->wi_if_flags = ifp->if_flags; 153246492Swpaul error = 0; 153346492Swpaul break; 153477217Sphk case SIOCSIFMEDIA: 153577217Sphk case SIOCGIFMEDIA: 153677217Sphk error = ifmedia_ioctl(ifp, ifr, &sc->ifmedia, command); 153777217Sphk break; 153846492Swpaul case SIOCADDMULTI: 153946492Swpaul case SIOCDELMULTI: 154046492Swpaul wi_setmulti(sc); 154146492Swpaul error = 0; 154246492Swpaul break; 154346492Swpaul case SIOCGWAVELAN: 154446492Swpaul error = copyin(ifr->ifr_data, &wreq, sizeof(wreq)); 154546492Swpaul if (error) 154646492Swpaul break; 154793833Simp if (wreq.wi_len > WI_MAX_DATALEN) { 154893833Simp error = EINVAL; 154993833Simp break; 155093833Simp } 155165581Swpaul /* Don't show WEP keys to non-root users. */ 155293593Sjhb if (wreq.wi_type == WI_RID_DEFLT_CRYPT_KEYS && suser(td)) 155365581Swpaul break; 155446492Swpaul if (wreq.wi_type == WI_RID_IFACE_STATS) { 155546492Swpaul bcopy((char *)&sc->wi_stats, (char *)&wreq.wi_val, 155646492Swpaul sizeof(sc->wi_stats)); 155746492Swpaul wreq.wi_len = (sizeof(sc->wi_stats) / 2) + 1; 155856965Swpaul } else if (wreq.wi_type == WI_RID_DEFLT_CRYPT_KEYS) { 155956965Swpaul bcopy((char *)&sc->wi_keys, (char *)&wreq, 156056965Swpaul sizeof(struct wi_ltv_keys)); 156153702Swpaul } 156253702Swpaul#ifdef WICACHE 156353702Swpaul else if (wreq.wi_type == WI_RID_ZERO_CACHE) { 156453702Swpaul sc->wi_sigitems = sc->wi_nextitem = 0; 156553702Swpaul } else if (wreq.wi_type == WI_RID_READ_CACHE) { 156653702Swpaul char *pt = (char *)&wreq.wi_val; 156753702Swpaul bcopy((char *)&sc->wi_sigitems, 156853702Swpaul (char *)pt, sizeof(int)); 156953702Swpaul pt += (sizeof (int)); 157053702Swpaul wreq.wi_len = sizeof(int) / 2; 157153702Swpaul bcopy((char *)&sc->wi_sigcache, (char *)pt, 157253702Swpaul sizeof(struct wi_sigcache) * sc->wi_sigitems); 157353702Swpaul wreq.wi_len += ((sizeof(struct wi_sigcache) * 157453702Swpaul sc->wi_sigitems) / 2) + 1; 157553702Swpaul } 157653702Swpaul#endif 157793359Simp else if (wreq.wi_type == WI_RID_PROCFRAME) { 157893359Simp wreq.wi_len = 2; 157993359Simp wreq.wi_val[0] = sc->wi_procframe; 158093359Simp } else if (wreq.wi_type == WI_RID_PRISM2) { 158193359Simp wreq.wi_len = 2; 158293733Simp wreq.wi_val[0] = sc->sc_firmware_type != WI_LUCENT; 158393733Simp } else if (wreq.wi_type == WI_RID_SCAN_RES && 158493733Simp sc->sc_firmware_type == WI_LUCENT) { 158593359Simp memcpy((char *)wreq.wi_val, (char *)sc->wi_scanbuf, 158693359Simp sc->wi_scanbuf_len * 2); 158793359Simp wreq.wi_len = sc->wi_scanbuf_len; 158893359Simp } else { 158946492Swpaul if (wi_read_record(sc, (struct wi_ltv_gen *)&wreq)) { 159046492Swpaul error = EINVAL; 159146492Swpaul break; 159246492Swpaul } 159346492Swpaul } 159446492Swpaul error = copyout(&wreq, ifr->ifr_data, sizeof(wreq)); 159546492Swpaul break; 159646492Swpaul case SIOCSWAVELAN: 159793593Sjhb if ((error = suser(td))) 159861818Sroberto goto out; 159946492Swpaul error = copyin(ifr->ifr_data, &wreq, sizeof(wreq)); 160046492Swpaul if (error) 160146492Swpaul break; 160293833Simp if (wreq.wi_len > WI_MAX_DATALEN) { 160393833Simp error = EINVAL; 160493833Simp break; 160593833Simp } 160646492Swpaul if (wreq.wi_type == WI_RID_IFACE_STATS) { 160746492Swpaul error = EINVAL; 160846492Swpaul break; 160946492Swpaul } else if (wreq.wi_type == WI_RID_MGMT_XMIT) { 161046492Swpaul error = wi_mgmt_xmit(sc, (caddr_t)&wreq.wi_val, 161146492Swpaul wreq.wi_len); 161293359Simp } else if (wreq.wi_type == WI_RID_PROCFRAME) { 161393359Simp sc->wi_procframe = wreq.wi_val[0]; 161493359Simp /* 161593359Simp * if we're getting a scan request from a wavelan card 161693359Simp * (non-prism2), send out a cmd_inquire to the card to scan 161793359Simp * results for the scan will be received through the info 161893359Simp * interrupt handler. otherwise the scan request can be 161993359Simp * directly handled by a prism2 card's rid interface. 162093359Simp */ 162193733Simp } else if (wreq.wi_type == WI_RID_SCAN_REQ && 162293733Simp sc->sc_firmware_type == WI_LUCENT) { 162393359Simp wi_cmd(sc, WI_CMD_INQUIRE, WI_INFO_SCAN_RESULTS, 0, 0); 162446492Swpaul } else { 162546492Swpaul error = wi_write_record(sc, (struct wi_ltv_gen *)&wreq); 162646492Swpaul if (!error) 162746492Swpaul wi_setdef(sc, &wreq); 162846492Swpaul } 162946492Swpaul break; 163093359Simp case SIOCGPRISM2DEBUG: 163193359Simp error = copyin(ifr->ifr_data, &wreq, sizeof(wreq)); 163293359Simp if (error) 163393359Simp break; 163493733Simp if (!(ifp->if_flags & IFF_RUNNING) || 163593733Simp sc->sc_firmware_type == WI_LUCENT) { 163693359Simp error = EIO; 163793359Simp break; 163893359Simp } 163993359Simp error = wi_get_debug(sc, &wreq); 164093359Simp if (error == 0) 164193359Simp error = copyout(&wreq, ifr->ifr_data, sizeof(wreq)); 164293359Simp break; 164393359Simp case SIOCSPRISM2DEBUG: 164493593Sjhb if ((error = suser(td))) 164593359Simp goto out; 164693359Simp error = copyin(ifr->ifr_data, &wreq, sizeof(wreq)); 164793359Simp if (error) 164893359Simp break; 164993359Simp error = wi_set_debug(sc, &wreq); 165093359Simp break; 165177217Sphk case SIOCG80211: 165277217Sphk switch(ireq->i_type) { 165377217Sphk case IEEE80211_IOC_SSID: 165477217Sphk if(ireq->i_val == -1) { 165577217Sphk bzero(tmpssid, IEEE80211_NWID_LEN); 165677217Sphk error = wi_get_cur_ssid(sc, tmpssid, &len); 165777217Sphk if (error != 0) 165877217Sphk break; 165977217Sphk error = copyout(tmpssid, ireq->i_data, 166077217Sphk IEEE80211_NWID_LEN); 166177217Sphk ireq->i_len = len; 166277217Sphk } else if (ireq->i_val == 0) { 166377217Sphk error = copyout(sc->wi_net_name, 166477217Sphk ireq->i_data, 166577217Sphk IEEE80211_NWID_LEN); 166677217Sphk ireq->i_len = IEEE80211_NWID_LEN; 166777217Sphk } else 166877217Sphk error = EINVAL; 166977217Sphk break; 167077217Sphk case IEEE80211_IOC_NUMSSIDS: 167177217Sphk ireq->i_val = 1; 167277217Sphk break; 167377217Sphk case IEEE80211_IOC_WEP: 167477217Sphk if(!sc->wi_has_wep) { 167577217Sphk ireq->i_val = IEEE80211_WEP_NOSUP; 167677217Sphk } else { 167777217Sphk if(sc->wi_use_wep) { 167877217Sphk ireq->i_val = 167977217Sphk IEEE80211_WEP_MIXED; 168077217Sphk } else { 168177217Sphk ireq->i_val = 168277217Sphk IEEE80211_WEP_OFF; 168377217Sphk } 168477217Sphk } 168577217Sphk break; 168677217Sphk case IEEE80211_IOC_WEPKEY: 168777217Sphk if(!sc->wi_has_wep || 168877217Sphk ireq->i_val < 0 || ireq->i_val > 3) { 168977217Sphk error = EINVAL; 169077217Sphk break; 169177217Sphk } 169277217Sphk len = sc->wi_keys.wi_keys[ireq->i_val].wi_keylen; 169393593Sjhb if (suser(td)) 169477217Sphk bcopy(sc->wi_keys.wi_keys[ireq->i_val].wi_keydat, 169577217Sphk tmpkey, len); 169677217Sphk else 169777217Sphk bzero(tmpkey, len); 169877217Sphk 169977217Sphk ireq->i_len = len; 170077217Sphk error = copyout(tmpkey, ireq->i_data, len); 170177217Sphk 170277217Sphk break; 170377217Sphk case IEEE80211_IOC_NUMWEPKEYS: 170477217Sphk if(!sc->wi_has_wep) 170577217Sphk error = EINVAL; 170677217Sphk else 170777217Sphk ireq->i_val = 4; 170877217Sphk break; 170977217Sphk case IEEE80211_IOC_WEPTXKEY: 171077217Sphk if(!sc->wi_has_wep) 171177217Sphk error = EINVAL; 171277217Sphk else 171377217Sphk ireq->i_val = sc->wi_tx_key; 171477217Sphk break; 171577217Sphk case IEEE80211_IOC_AUTHMODE: 171694405Simp ireq->i_val = sc->wi_authmode; 171777217Sphk break; 171877217Sphk case IEEE80211_IOC_STATIONNAME: 171977217Sphk error = copyout(sc->wi_node_name, 172077217Sphk ireq->i_data, IEEE80211_NWID_LEN); 172177217Sphk ireq->i_len = IEEE80211_NWID_LEN; 172277217Sphk break; 172377217Sphk case IEEE80211_IOC_CHANNEL: 172477217Sphk wreq.wi_type = WI_RID_CURRENT_CHAN; 172577217Sphk wreq.wi_len = WI_MAX_DATALEN; 172677217Sphk if (wi_read_record(sc, (struct wi_ltv_gen *)&wreq)) 172777217Sphk error = EINVAL; 172877217Sphk else { 172977217Sphk ireq->i_val = wreq.wi_val[0]; 173077217Sphk } 173177217Sphk break; 173277217Sphk case IEEE80211_IOC_POWERSAVE: 173377217Sphk if(sc->wi_pm_enabled) 173477217Sphk ireq->i_val = IEEE80211_POWERSAVE_ON; 173577217Sphk else 173677217Sphk ireq->i_val = IEEE80211_POWERSAVE_OFF; 173777217Sphk break; 173877217Sphk case IEEE80211_IOC_POWERSAVESLEEP: 173977217Sphk ireq->i_val = sc->wi_max_sleep; 174077217Sphk break; 174177217Sphk default: 174277217Sphk error = EINVAL; 174377217Sphk } 174477217Sphk break; 174577217Sphk case SIOCS80211: 174693593Sjhb if ((error = suser(td))) 174777217Sphk goto out; 174877217Sphk switch(ireq->i_type) { 174977217Sphk case IEEE80211_IOC_SSID: 175077217Sphk if (ireq->i_val != 0 || 175177217Sphk ireq->i_len > IEEE80211_NWID_LEN) { 175277217Sphk error = EINVAL; 175377217Sphk break; 175477217Sphk } 175577217Sphk /* We set both of them */ 175677217Sphk bzero(sc->wi_net_name, IEEE80211_NWID_LEN); 175777217Sphk error = copyin(ireq->i_data, 175877217Sphk sc->wi_net_name, ireq->i_len); 175977217Sphk bcopy(sc->wi_net_name, sc->wi_ibss_name, IEEE80211_NWID_LEN); 176077217Sphk break; 176177217Sphk case IEEE80211_IOC_WEP: 176277217Sphk /* 176377217Sphk * These cards only support one mode so 176477217Sphk * we just turn wep on what ever is 176577217Sphk * passed in if it's not OFF. 176677217Sphk */ 176777217Sphk if (ireq->i_val == IEEE80211_WEP_OFF) { 176877217Sphk sc->wi_use_wep = 0; 176977217Sphk } else { 177077217Sphk sc->wi_use_wep = 1; 177177217Sphk } 177277217Sphk break; 177377217Sphk case IEEE80211_IOC_WEPKEY: 177477217Sphk if (ireq->i_val < 0 || ireq->i_val > 3 || 177577217Sphk ireq->i_len > 13) { 177677217Sphk error = EINVAL; 177777217Sphk break; 177877217Sphk } 177977217Sphk bzero(sc->wi_keys.wi_keys[ireq->i_val].wi_keydat, 13); 178077217Sphk error = copyin(ireq->i_data, 178177217Sphk sc->wi_keys.wi_keys[ireq->i_val].wi_keydat, 178277217Sphk ireq->i_len); 178377217Sphk if(error) 178477217Sphk break; 178577217Sphk sc->wi_keys.wi_keys[ireq->i_val].wi_keylen = 178677217Sphk ireq->i_len; 178777217Sphk break; 178877217Sphk case IEEE80211_IOC_WEPTXKEY: 178977217Sphk if (ireq->i_val < 0 || ireq->i_val > 3) { 179077217Sphk error = EINVAL; 179177217Sphk break; 179277217Sphk } 179377217Sphk sc->wi_tx_key = ireq->i_val; 179477217Sphk break; 179577217Sphk case IEEE80211_IOC_AUTHMODE: 179694405Simp sc->wi_authmode = ireq->i_val; 179777217Sphk break; 179877217Sphk case IEEE80211_IOC_STATIONNAME: 179977217Sphk if (ireq->i_len > 32) { 180077217Sphk error = EINVAL; 180177217Sphk break; 180277217Sphk } 180377217Sphk bzero(sc->wi_node_name, 32); 180477217Sphk error = copyin(ireq->i_data, 180577217Sphk sc->wi_node_name, ireq->i_len); 180677217Sphk break; 180777217Sphk case IEEE80211_IOC_CHANNEL: 180877217Sphk /* 180977217Sphk * The actual range is 1-14, but if you 181077217Sphk * set it to 0 you get the default. So 181177217Sphk * we let that work too. 181277217Sphk */ 181377217Sphk if (ireq->i_val < 0 || ireq->i_val > 14) { 181477217Sphk error = EINVAL; 181577217Sphk break; 181677217Sphk } 181777217Sphk sc->wi_channel = ireq->i_val; 181877217Sphk break; 181977217Sphk case IEEE80211_IOC_POWERSAVE: 182077217Sphk switch (ireq->i_val) { 182177217Sphk case IEEE80211_POWERSAVE_OFF: 182277217Sphk sc->wi_pm_enabled = 0; 182377217Sphk break; 182477217Sphk case IEEE80211_POWERSAVE_ON: 182577217Sphk sc->wi_pm_enabled = 1; 182677217Sphk break; 182777217Sphk default: 182877217Sphk error = EINVAL; 182977217Sphk break; 183077217Sphk } 183177217Sphk break; 183277217Sphk case IEEE80211_IOC_POWERSAVESLEEP: 183377217Sphk if (ireq->i_val < 0) { 183477217Sphk error = EINVAL; 183577217Sphk break; 183677217Sphk } 183777217Sphk sc->wi_max_sleep = ireq->i_val; 183877217Sphk break; 183977217Sphk default: 184077217Sphk error = EINVAL; 184177217Sphk break; 184277217Sphk } 184377217Sphk 184477217Sphk /* Reinitialize WaveLAN. */ 184577217Sphk wi_init(sc); 184677217Sphk 184794405Simp break; 184894405Simp#ifdef WI_HOSTAP 184994405Simp case SIOCHOSTAP_ADD: 185094405Simp case SIOCHOSTAP_DEL: 185194405Simp case SIOCHOSTAP_GET: 185294405Simp case SIOCHOSTAP_GETALL: 185394405Simp case SIOCHOSTAP_GFLAGS: 185494405Simp case SIOCHOSTAP_SFLAGS: 185594405Simp /* Send all Host AP specific ioctl's to Host AP code. */ 185694405Simp error = wihap_ioctl(sc, command, data); 185777217Sphk break; 185894405Simp#endif 185946492Swpaul default: 186046492Swpaul error = EINVAL; 186146492Swpaul break; 186246492Swpaul } 186361818Srobertoout: 186467092Swpaul WI_UNLOCK(sc); 186546492Swpaul 186646492Swpaul return(error); 186746492Swpaul} 186846492Swpaul 186988546Salfredstatic void 187088546Salfredwi_init(xsc) 187146492Swpaul void *xsc; 187246492Swpaul{ 187346492Swpaul struct wi_softc *sc = xsc; 187446492Swpaul struct ifnet *ifp = &sc->arpcom.ac_if; 187546492Swpaul struct wi_ltv_macaddr mac; 187646492Swpaul int id = 0; 187746492Swpaul 187867092Swpaul WI_LOCK(sc); 187967092Swpaul 188067092Swpaul if (sc->wi_gone) { 188167092Swpaul WI_UNLOCK(sc); 188246492Swpaul return; 188367092Swpaul } 188446492Swpaul 188546492Swpaul if (ifp->if_flags & IFF_RUNNING) 188646492Swpaul wi_stop(sc); 188746492Swpaul 188846492Swpaul wi_reset(sc); 188946492Swpaul 189046492Swpaul /* Program max data length. */ 189146492Swpaul WI_SETVAL(WI_RID_MAX_DATALEN, sc->wi_max_data_len); 189246492Swpaul 189347401Swpaul /* Enable/disable IBSS creation. */ 189446492Swpaul WI_SETVAL(WI_RID_CREATE_IBSS, sc->wi_create_ibss); 189546492Swpaul 189646492Swpaul /* Set the port type. */ 189746492Swpaul WI_SETVAL(WI_RID_PORTTYPE, sc->wi_ptype); 189846492Swpaul 189946492Swpaul /* Program the RTS/CTS threshold. */ 190046492Swpaul WI_SETVAL(WI_RID_RTS_THRESH, sc->wi_rts_thresh); 190146492Swpaul 190246492Swpaul /* Program the TX rate */ 190346492Swpaul WI_SETVAL(WI_RID_TX_RATE, sc->wi_tx_rate); 190446492Swpaul 190546492Swpaul /* Access point density */ 190646492Swpaul WI_SETVAL(WI_RID_SYSTEM_SCALE, sc->wi_ap_density); 190746492Swpaul 190846611Swpaul /* Power Management Enabled */ 190946611Swpaul WI_SETVAL(WI_RID_PM_ENABLED, sc->wi_pm_enabled); 191046611Swpaul 191146611Swpaul /* Power Managment Max Sleep */ 191246611Swpaul WI_SETVAL(WI_RID_MAX_SLEEP, sc->wi_max_sleep); 191346611Swpaul 191491695Simp /* Roaming type */ 191591695Simp WI_SETVAL(WI_RID_ROAMING_MODE, sc->wi_roaming); 191691695Simp 191746492Swpaul /* Specify the IBSS name */ 191846492Swpaul WI_SETSTR(WI_RID_OWN_SSID, sc->wi_ibss_name); 191946492Swpaul 192046492Swpaul /* Specify the network name */ 192146492Swpaul WI_SETSTR(WI_RID_DESIRED_SSID, sc->wi_net_name); 192246492Swpaul 192346563Swpaul /* Specify the frequency to use */ 192446563Swpaul WI_SETVAL(WI_RID_OWN_CHNL, sc->wi_channel); 192546563Swpaul 192646492Swpaul /* Program the nodename. */ 192746492Swpaul WI_SETSTR(WI_RID_NODENAME, sc->wi_node_name); 192846492Swpaul 192994405Simp /* Specify the authentication mode. */ 193094405Simp WI_SETVAL(WI_RID_CNFAUTHMODE, sc->wi_authmode); 193194405Simp 193246492Swpaul /* Set our MAC address. */ 193346492Swpaul mac.wi_len = 4; 193446492Swpaul mac.wi_type = WI_RID_MAC_NODE; 193546492Swpaul bcopy((char *)&sc->arpcom.ac_enaddr, 193646492Swpaul (char *)&mac.wi_mac_addr, ETHER_ADDR_LEN); 193746492Swpaul wi_write_record(sc, (struct wi_ltv_gen *)&mac); 193846492Swpaul 193956965Swpaul /* Configure WEP. */ 194056965Swpaul if (sc->wi_has_wep) { 194156965Swpaul WI_SETVAL(WI_RID_ENCRYPTION, sc->wi_use_wep); 194256965Swpaul WI_SETVAL(WI_RID_TX_CRYPT_KEY, sc->wi_tx_key); 194356965Swpaul sc->wi_keys.wi_len = (sizeof(struct wi_ltv_keys) / 2) + 1; 194456965Swpaul sc->wi_keys.wi_type = WI_RID_DEFLT_CRYPT_KEYS; 194556965Swpaul wi_write_record(sc, (struct wi_ltv_gen *)&sc->wi_keys); 194693733Simp if (sc->sc_firmware_type != WI_LUCENT && sc->wi_use_wep) { 194791695Simp /* 194891695Simp * ONLY HWB3163 EVAL-CARD Firmware version 194993733Simp * less than 0.8 variant2 195091695Simp * 195191695Simp * If promiscuous mode disable, Prism2 chip 195291695Simp * does not work with WEP . 195391695Simp * It is under investigation for details. 195491695Simp * (ichiro@netbsd.org) 195591695Simp */ 195693733Simp if (sc->sc_firmware_type == WI_INTERSIL && 195793733Simp sc->sc_sta_firmware_ver < 802 ) { 195893733Simp /* firm ver < 0.8 variant 2 */ 195991695Simp WI_SETVAL(WI_RID_PROMISC, 1); 196091695Simp } 196194405Simp WI_SETVAL(WI_RID_CNFAUTHMODE, sc->wi_authtype); 196291695Simp } 196356965Swpaul } 196456965Swpaul 196546492Swpaul /* Initialize promisc mode. */ 196646492Swpaul if (ifp->if_flags & IFF_PROMISC) { 196746492Swpaul WI_SETVAL(WI_RID_PROMISC, 1); 196846492Swpaul } else { 196946492Swpaul WI_SETVAL(WI_RID_PROMISC, 0); 197046492Swpaul } 197146492Swpaul 197246492Swpaul /* Set multicast filter. */ 197346492Swpaul wi_setmulti(sc); 197446492Swpaul 197546492Swpaul /* Enable desired port */ 197692457Simp wi_cmd(sc, WI_CMD_ENABLE | sc->wi_portnum, 0, 0, 0); 197746492Swpaul 197875373Salfred if (wi_alloc_nicmem(sc, ETHER_MAX_LEN + sizeof(struct wi_frame) + 8, &id)) 197953702Swpaul device_printf(sc->dev, "tx buffer allocation failed\n"); 198046492Swpaul sc->wi_tx_data_id = id; 198146492Swpaul 198275373Salfred if (wi_alloc_nicmem(sc, ETHER_MAX_LEN + sizeof(struct wi_frame) + 8, &id)) 198353702Swpaul device_printf(sc->dev, "mgmt. buffer allocation failed\n"); 198446492Swpaul sc->wi_tx_mgmt_id = id; 198546492Swpaul 198646492Swpaul /* enable interrupts */ 198746492Swpaul CSR_WRITE_2(sc, WI_INT_EN, WI_INTRS); 198846492Swpaul 198994405Simp#ifdef WI_HOSTAP 199094405Simp wihap_init(sc); 199194405Simp 199294405Simp /* 199394405Simp * Initialize ICV to something random. XXX: this doesn't work 199494405Simp * if init happens in early boot-up. Fix later. 199594405Simp */ 199694405Simp read_random(&sc->wi_icv, sizeof(sc->wi_icv)); 199794405Simp#endif 199846492Swpaul ifp->if_flags |= IFF_RUNNING; 199946492Swpaul ifp->if_flags &= ~IFF_OACTIVE; 200046492Swpaul 200146492Swpaul sc->wi_stat_ch = timeout(wi_inquire, sc, hz * 60); 200267092Swpaul WI_UNLOCK(sc); 200346492Swpaul 200446492Swpaul return; 200546492Swpaul} 200646492Swpaul 200794472Simpstatic u_int32_t crc32_tab[] = { 200894472Simp 0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L, 200994472Simp 0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L, 201094472Simp 0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L, 201194472Simp 0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL, 201294472Simp 0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L, 201394472Simp 0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L, 201494472Simp 0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L, 201594472Simp 0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL, 201694472Simp 0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L, 201794472Simp 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL, 201894472Simp 0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L, 201994472Simp 0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L, 202094472Simp 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L, 202194472Simp 0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL, 202294472Simp 0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL, 202394472Simp 0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L, 202494472Simp 0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL, 202594472Simp 0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L, 202694472Simp 0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L, 202794472Simp 0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L, 202894472Simp 0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL, 202994472Simp 0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L, 203094472Simp 0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L, 203194472Simp 0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL, 203294472Simp 0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L, 203394472Simp 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L, 203494472Simp 0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L, 203594472Simp 0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L, 203694472Simp 0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L, 203794472Simp 0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL, 203894472Simp 0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL, 203994472Simp 0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L, 204094472Simp 0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L, 204194472Simp 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL, 204294472Simp 0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL, 204394472Simp 0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L, 204494472Simp 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL, 204594472Simp 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L, 204694472Simp 0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL, 204794472Simp 0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L, 204894472Simp 0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL, 204994472Simp 0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L, 205094472Simp 0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L, 205194472Simp 0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL, 205294472Simp 0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L, 205394472Simp 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L, 205494472Simp 0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L, 205594472Simp 0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L, 205694472Simp 0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L, 205794472Simp 0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L, 205894472Simp 0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL, 205994472Simp 0x2d02ef8dL 206094472Simp}; 206194405Simp 206294472Simp#define RC4STATE 256 206394472Simp#define RC4KEYLEN 16 206494472Simp#define RC4SWAP(x,y) \ 206594472Simp do { u_int8_t t = state[x]; state[x] = state[y]; state[y] = t; } while(0) 206694405Simp 206788546Salfredstatic void 206894405Simpwi_do_hostencrypt(struct wi_softc *sc, caddr_t buf, int len) 206994405Simp{ 207094472Simp u_int32_t i, crc, klen; 207194472Simp u_int8_t state[RC4STATE], key[RC4KEYLEN]; 207294472Simp u_int8_t x, y, *dat; 207394405Simp 207494472Simp if (!sc->wi_icv_flag) { 207594472Simp sc->wi_icv = arc4random(); 207694472Simp sc->wi_icv_flag++; 207794472Simp } else 207894472Simp sc->wi_icv++; 207994472Simp /* 208094472Simp * Skip 'bad' IVs from Fluhrer/Mantin/Shamir: 208194472Simp * (B, 255, N) with 3 <= B < 8 208294472Simp */ 208394472Simp if (sc->wi_icv >= 0x03ff00 && 208494472Simp (sc->wi_icv & 0xf8ff00) == 0x00ff00) 208594472Simp sc->wi_icv += 0x000100; 208694405Simp 208794472Simp /* prepend 24bit IV to tx key, byte order does not matter */ 208894472Simp key[0] = sc->wi_icv >> 16; 208994472Simp key[1] = sc->wi_icv >> 8; 209094472Simp key[2] = sc->wi_icv; 209194405Simp 209294472Simp klen = sc->wi_keys.wi_keys[sc->wi_tx_key].wi_keylen + 209394472Simp IEEE80211_WEP_IVLEN; 209494472Simp klen = (klen >= RC4KEYLEN) ? RC4KEYLEN : RC4KEYLEN/2; 209594472Simp bcopy((char *)&sc->wi_keys.wi_keys[sc->wi_tx_key].wi_keydat, 209694472Simp (char *)key + IEEE80211_WEP_IVLEN, klen - IEEE80211_WEP_IVLEN); 209794405Simp 209894472Simp /* rc4 keysetup */ 209994472Simp x = y = 0; 210094472Simp for (i = 0; i < RC4STATE; i++) 210194472Simp state[i] = i; 210294472Simp for (i = 0; i < RC4STATE; i++) { 210394472Simp y = (key[x] + state[i] + y) % RC4STATE; 210494472Simp RC4SWAP(i, y); 210594472Simp x = (x + 1) % klen; 210694405Simp } 210794405Simp 210894472Simp /* output: IV, tx keyid, rc4(data), rc4(crc32(data)) */ 210994472Simp dat = buf; 211094472Simp dat[0] = key[0]; 211194472Simp dat[1] = key[1]; 211294472Simp dat[2] = key[2]; 211394472Simp dat[3] = sc->wi_tx_key << 6; /* pad and keyid */ 211494472Simp dat += 4; 211594472Simp 211694472Simp /* compute rc4 over data, crc32 over data */ 211794472Simp crc = ~0; 211894472Simp x = y = 0; 211994472Simp for (i = 0; i < len; i++) { 212094472Simp x = (x + 1) % RC4STATE; 212194472Simp y = (state[x] + y) % RC4STATE; 212294472Simp RC4SWAP(x, y); 212394472Simp crc = crc32_tab[(crc ^ dat[i]) & 0xff] ^ (crc >> 8); 212494472Simp dat[i] ^= state[(state[x] + state[y]) % RC4STATE]; 212594405Simp } 212694472Simp crc = ~crc; 212794472Simp dat += len; 212894405Simp 212994472Simp /* append little-endian crc32 and encrypt */ 213094472Simp dat[0] = crc; 213194472Simp dat[1] = crc >> 8; 213294472Simp dat[2] = crc >> 16; 213394472Simp dat[3] = crc >> 24; 213494472Simp for (i = 0; i < IEEE80211_WEP_CRCLEN; i++) { 213594472Simp x = (x + 1) % RC4STATE; 213694472Simp y = (state[x] + y) % RC4STATE; 213794472Simp RC4SWAP(x, y); 213894472Simp dat[i] ^= state[(state[x] + state[y]) % RC4STATE]; 213994405Simp } 214094405Simp} 214194405Simp 214294405Simpstatic void 214388546Salfredwi_start(ifp) 214446492Swpaul struct ifnet *ifp; 214546492Swpaul{ 214646492Swpaul struct wi_softc *sc; 214746492Swpaul struct mbuf *m0; 214846492Swpaul struct wi_frame tx_frame; 214946492Swpaul struct ether_header *eh; 215046492Swpaul int id; 215146492Swpaul 215246492Swpaul sc = ifp->if_softc; 215367092Swpaul WI_LOCK(sc); 215446492Swpaul 215567092Swpaul if (sc->wi_gone) { 215667092Swpaul WI_UNLOCK(sc); 215746492Swpaul return; 215867092Swpaul } 215946492Swpaul 216067092Swpaul if (ifp->if_flags & IFF_OACTIVE) { 216167092Swpaul WI_UNLOCK(sc); 216246492Swpaul return; 216367092Swpaul } 216446492Swpaul 216594405Simp#ifdef WI_HOSTAP 216694405Simpnextpkt: 216794405Simp#endif 216846492Swpaul IF_DEQUEUE(&ifp->if_snd, m0); 216967092Swpaul if (m0 == NULL) { 217067092Swpaul WI_UNLOCK(sc); 217146492Swpaul return; 217267092Swpaul } 217346492Swpaul 217446492Swpaul bzero((char *)&tx_frame, sizeof(tx_frame)); 217594405Simp tx_frame.wi_frame_ctl = htole16(WI_FTYPE_DATA); 217646492Swpaul id = sc->wi_tx_data_id; 217746492Swpaul eh = mtod(m0, struct ether_header *); 217846492Swpaul 217994405Simp#ifdef WI_HOSTAP 218094405Simp if (sc->wi_ptype == WI_PORTTYPE_AP) { 218194405Simp if (!wihap_check_tx(&sc->wi_hostap_info, 218294405Simp eh->ether_dhost, &tx_frame.wi_tx_rate)) { 218394405Simp if (ifp->if_flags & IFF_DEBUG) 218494405Simp printf("wi_start: dropping unassoc " 218594405Simp "dst %6D\n", eh->ether_dhost, ":"); 218694405Simp m_freem(m0); 218794405Simp goto nextpkt; 218894405Simp } 218994405Simp } 219094405Simp#endif 219146492Swpaul /* 219247401Swpaul * Use RFC1042 encoding for IP and ARP datagrams, 219346492Swpaul * 802.3 for anything else. 219446492Swpaul */ 219575275Salfred if (ntohs(eh->ether_type) > ETHER_MAX_LEN) { 219646492Swpaul bcopy((char *)&eh->ether_dhost, 219746492Swpaul (char *)&tx_frame.wi_addr1, ETHER_ADDR_LEN); 219894405Simp#ifdef WI_HOSTAP 219994405Simp if (sc->wi_ptype == WI_PORTTYPE_AP) { 220094405Simp tx_frame.wi_tx_ctl = WI_ENC_TX_MGMT; /* XXX */ 220194405Simp tx_frame.wi_frame_ctl |= WI_FCTL_FROMDS; 220294405Simp if (sc->wi_use_wep) 220394405Simp tx_frame.wi_frame_ctl |= WI_FCTL_WEP; 220494405Simp bcopy((char *)&sc->arpcom.ac_enaddr, 220594405Simp (char *)&tx_frame.wi_addr2, ETHER_ADDR_LEN); 220694405Simp bcopy((char *)&eh->ether_shost, 220794405Simp (char *)&tx_frame.wi_addr3, ETHER_ADDR_LEN); 220894405Simp } 220994405Simp else 221094405Simp#endif 221194405Simp bcopy((char *)&eh->ether_shost, 221294405Simp (char *)&tx_frame.wi_addr2, ETHER_ADDR_LEN); 221346492Swpaul bcopy((char *)&eh->ether_dhost, 221446492Swpaul (char *)&tx_frame.wi_dst_addr, ETHER_ADDR_LEN); 221546492Swpaul bcopy((char *)&eh->ether_shost, 221646492Swpaul (char *)&tx_frame.wi_src_addr, ETHER_ADDR_LEN); 221746492Swpaul 221846492Swpaul tx_frame.wi_dat_len = m0->m_pkthdr.len - WI_SNAPHDR_LEN; 221946492Swpaul tx_frame.wi_dat[0] = htons(WI_SNAP_WORD0); 222046492Swpaul tx_frame.wi_dat[1] = htons(WI_SNAP_WORD1); 222146492Swpaul tx_frame.wi_len = htons(m0->m_pkthdr.len - WI_SNAPHDR_LEN); 222246492Swpaul tx_frame.wi_type = eh->ether_type; 222346492Swpaul 222494405Simp#ifdef WI_HOSTAP 222594405Simp if (sc->wi_ptype == WI_PORTTYPE_AP && sc->wi_use_wep) { 222694405Simp /* Do host encryption. */ 222794405Simp bcopy(&tx_frame.wi_dat[0], &sc->wi_txbuf[4], 8); 222894405Simp m_copydata(m0, sizeof(struct ether_header), 222994405Simp m0->m_pkthdr.len - sizeof(struct ether_header), 223094405Simp (caddr_t)&sc->wi_txbuf[12]); 223194405Simp wi_do_hostencrypt(sc, &sc->wi_txbuf[0], 223294405Simp tx_frame.wi_dat_len); 223394472Simp tx_frame.wi_dat_len += IEEE80211_WEP_IVLEN + 223494472Simp IEEE80211_WEP_KIDLEN + IEEE80211_WEP_CRCLEN; 223594405Simp wi_write_data(sc, id, 0, (caddr_t)&tx_frame, 223694405Simp sizeof(struct wi_frame)); 223794405Simp wi_write_data(sc, id, WI_802_11_OFFSET_RAW, 223894405Simp (caddr_t)&sc->wi_txbuf, (m0->m_pkthdr.len - 223994405Simp sizeof(struct ether_header)) + 18); 224094405Simp } 224194405Simp else 224294405Simp#endif 224394405Simp { 224494405Simp m_copydata(m0, sizeof(struct ether_header), 224594405Simp m0->m_pkthdr.len - sizeof(struct ether_header), 224694405Simp (caddr_t)&sc->wi_txbuf); 224794405Simp wi_write_data(sc, id, 0, (caddr_t)&tx_frame, 224894405Simp sizeof(struct wi_frame)); 224994405Simp wi_write_data(sc, id, WI_802_11_OFFSET, 225094405Simp (caddr_t)&sc->wi_txbuf, (m0->m_pkthdr.len - 225194405Simp sizeof(struct ether_header)) + 2); 225294405Simp } 225346492Swpaul } else { 225446492Swpaul tx_frame.wi_dat_len = m0->m_pkthdr.len; 225546492Swpaul 225694405Simp#ifdef WI_HOSTAP 225794405Simp if (sc->wi_ptype == WI_PORTTYPE_AP && sc->wi_use_wep) { 225894405Simp /* Do host encryption. */ 225994405Simp printf( "XXX: host encrypt not implemented for 802.3\n" ); 226094405Simp } 226194405Simp else 226294405Simp#endif 226394405Simp { 226494405Simp eh->ether_type = htons(m0->m_pkthdr.len - 226594405Simp WI_SNAPHDR_LEN); 226694405Simp m_copydata(m0, 0, m0->m_pkthdr.len, 226794405Simp (caddr_t)&sc->wi_txbuf); 226846492Swpaul 226994405Simp wi_write_data(sc, id, 0, (caddr_t)&tx_frame, 227094405Simp sizeof(struct wi_frame)); 227194405Simp wi_write_data(sc, id, WI_802_3_OFFSET, 227294405Simp (caddr_t)&sc->wi_txbuf, m0->m_pkthdr.len + 2); 227394405Simp } 227446492Swpaul } 227546492Swpaul 227646492Swpaul /* 227746492Swpaul * If there's a BPF listner, bounce a copy of 227893359Simp * this frame to him. Also, don't send this to the bpf sniffer 227993359Simp * if we're in procframe or monitor sniffing mode. 228046492Swpaul */ 228193359Simp if (!(sc->wi_procframe || sc->wi_debug.wi_monitor) && ifp->if_bpf) 228246492Swpaul bpf_mtap(ifp, m0); 228346492Swpaul 228446492Swpaul m_freem(m0); 228546492Swpaul 228692457Simp if (wi_cmd(sc, WI_CMD_TX|WI_RECLAIM, id, 0, 0)) 228753702Swpaul device_printf(sc->dev, "xmit failed\n"); 228846492Swpaul 228946492Swpaul ifp->if_flags |= IFF_OACTIVE; 229046492Swpaul 229146492Swpaul /* 229246492Swpaul * Set a timeout in case the chip goes out to lunch. 229346492Swpaul */ 229446492Swpaul ifp->if_timer = 5; 229546492Swpaul 229667092Swpaul WI_UNLOCK(sc); 229746492Swpaul return; 229846492Swpaul} 229946492Swpaul 230094405Simpint 230188546Salfredwi_mgmt_xmit(sc, data, len) 230246492Swpaul struct wi_softc *sc; 230346492Swpaul caddr_t data; 230446492Swpaul int len; 230546492Swpaul{ 230646492Swpaul struct wi_frame tx_frame; 230746492Swpaul int id; 230846492Swpaul struct wi_80211_hdr *hdr; 230946492Swpaul caddr_t dptr; 231046492Swpaul 231146492Swpaul if (sc->wi_gone) 231246492Swpaul return(ENODEV); 231346492Swpaul 231446492Swpaul hdr = (struct wi_80211_hdr *)data; 231546492Swpaul dptr = data + sizeof(struct wi_80211_hdr); 231646492Swpaul 231746492Swpaul bzero((char *)&tx_frame, sizeof(tx_frame)); 231846492Swpaul id = sc->wi_tx_mgmt_id; 231946492Swpaul 232046492Swpaul bcopy((char *)hdr, (char *)&tx_frame.wi_frame_ctl, 232146492Swpaul sizeof(struct wi_80211_hdr)); 232246492Swpaul 232394405Simp tx_frame.wi_tx_ctl = WI_ENC_TX_MGMT; 232494405Simp tx_frame.wi_dat_len = len - sizeof(struct wi_80211_hdr); 232594405Simp tx_frame.wi_len = htons(tx_frame.wi_dat_len); 232646492Swpaul 232746492Swpaul wi_write_data(sc, id, 0, (caddr_t)&tx_frame, sizeof(struct wi_frame)); 232846492Swpaul wi_write_data(sc, id, WI_802_11_OFFSET_RAW, dptr, 232994405Simp len - sizeof(struct wi_80211_hdr) + 2); 233046492Swpaul 233192457Simp if (wi_cmd(sc, WI_CMD_TX|WI_RECLAIM, id, 0, 0)) { 233253702Swpaul device_printf(sc->dev, "xmit failed\n"); 233346492Swpaul return(EIO); 233446492Swpaul } 233546492Swpaul 233646492Swpaul return(0); 233746492Swpaul} 233846492Swpaul 233988546Salfredstatic void 234088546Salfredwi_stop(sc) 234146492Swpaul struct wi_softc *sc; 234246492Swpaul{ 234346492Swpaul struct ifnet *ifp; 234446492Swpaul 234567092Swpaul WI_LOCK(sc); 234667092Swpaul 234767092Swpaul if (sc->wi_gone) { 234867092Swpaul WI_UNLOCK(sc); 234946492Swpaul return; 235067092Swpaul } 235146492Swpaul 235294405Simp#ifdef WI_HOSTAP 235394405Simp wihap_shutdown(sc); 235494405Simp#endif 235594405Simp 235646492Swpaul ifp = &sc->arpcom.ac_if; 235746492Swpaul 235870173Sjhb /* 235970173Sjhb * If the card is gone and the memory port isn't mapped, we will 236070173Sjhb * (hopefully) get 0xffff back from the status read, which is not 236170173Sjhb * a valid status value. 236270173Sjhb */ 236370173Sjhb if (CSR_READ_2(sc, WI_STATUS) != 0xffff) { 236470173Sjhb CSR_WRITE_2(sc, WI_INT_EN, 0); 236592457Simp wi_cmd(sc, WI_CMD_DISABLE|sc->wi_portnum, 0, 0, 0); 236670173Sjhb } 236746492Swpaul 236846492Swpaul untimeout(wi_inquire, sc, sc->wi_stat_ch); 236946492Swpaul 237046492Swpaul ifp->if_flags &= ~(IFF_RUNNING|IFF_OACTIVE); 237146492Swpaul 237267092Swpaul WI_UNLOCK(sc); 237346492Swpaul return; 237446492Swpaul} 237546492Swpaul 237688546Salfredstatic void 237788546Salfredwi_watchdog(ifp) 237846492Swpaul struct ifnet *ifp; 237946492Swpaul{ 238046492Swpaul struct wi_softc *sc; 238146492Swpaul 238246492Swpaul sc = ifp->if_softc; 238346492Swpaul 238475199Salfred device_printf(sc->dev, "watchdog timeout\n"); 238546492Swpaul 238646492Swpaul wi_init(sc); 238746492Swpaul 238846492Swpaul ifp->if_oerrors++; 238946492Swpaul 239046492Swpaul return; 239146492Swpaul} 239246492Swpaul 239393611Simpint 239490580Sbrookswi_alloc(dev, rid) 239553702Swpaul device_t dev; 239690580Sbrooks int rid; 239746492Swpaul{ 239853702Swpaul struct wi_softc *sc = device_get_softc(dev); 239953702Swpaul 240090580Sbrooks if (sc->wi_bus_type != WI_BUS_PCI_NATIVE) { 240190580Sbrooks sc->iobase_rid = rid; 240290580Sbrooks sc->iobase = bus_alloc_resource(dev, SYS_RES_IOPORT, 240390580Sbrooks &sc->iobase_rid, 0, ~0, (1 << 6), 240490580Sbrooks rman_make_alignment_flags(1 << 6) | RF_ACTIVE); 240590580Sbrooks if (!sc->iobase) { 240690580Sbrooks device_printf(dev, "No I/O space?!\n"); 240790580Sbrooks return (ENXIO); 240890580Sbrooks } 240990580Sbrooks 241090580Sbrooks sc->wi_io_addr = rman_get_start(sc->iobase); 241190580Sbrooks sc->wi_btag = rman_get_bustag(sc->iobase); 241290580Sbrooks sc->wi_bhandle = rman_get_bushandle(sc->iobase); 241390580Sbrooks } else { 241490580Sbrooks sc->mem_rid = rid; 241590580Sbrooks sc->mem = bus_alloc_resource(dev, SYS_RES_MEMORY, 241690580Sbrooks &sc->mem_rid, 0, ~0, 1, RF_ACTIVE); 241790580Sbrooks 241890580Sbrooks if (!sc->mem) { 241990580Sbrooks device_printf(dev, "No Mem space on prism2.5?\n"); 242090580Sbrooks return (ENXIO); 242190580Sbrooks } 242290580Sbrooks 242390580Sbrooks sc->wi_btag = rman_get_bustag(sc->mem); 242490580Sbrooks sc->wi_bhandle = rman_get_bushandle(sc->mem); 242553702Swpaul } 242653702Swpaul 242790580Sbrooks 242874906Salfred sc->irq_rid = 0; 242974906Salfred sc->irq = bus_alloc_resource(dev, SYS_RES_IRQ, &sc->irq_rid, 243090580Sbrooks 0, ~0, 1, RF_ACTIVE | 243190580Sbrooks ((sc->wi_bus_type == WI_BUS_PCCARD) ? 0 : RF_SHAREABLE)); 243290580Sbrooks 243353702Swpaul if (!sc->irq) { 243475219Salfred wi_free(dev); 243553702Swpaul device_printf(dev, "No irq?!\n"); 243653702Swpaul return (ENXIO); 243753702Swpaul } 243853702Swpaul 243953702Swpaul sc->dev = dev; 244053702Swpaul sc->wi_unit = device_get_unit(dev); 244153702Swpaul 244253702Swpaul return (0); 244353702Swpaul} 244453702Swpaul 244593611Simpvoid 244688546Salfredwi_free(dev) 244753702Swpaul device_t dev; 244853702Swpaul{ 244953702Swpaul struct wi_softc *sc = device_get_softc(dev); 245053702Swpaul 245175219Salfred if (sc->iobase != NULL) { 245275219Salfred bus_release_resource(dev, SYS_RES_IOPORT, sc->iobase_rid, sc->iobase); 245375219Salfred sc->iobase = NULL; 245475219Salfred } 245575219Salfred if (sc->irq != NULL) { 245675219Salfred bus_release_resource(dev, SYS_RES_IRQ, sc->irq_rid, sc->irq); 245775219Salfred sc->irq = NULL; 245875219Salfred } 245975219Salfred if (sc->mem != NULL) { 246074906Salfred bus_release_resource(dev, SYS_RES_MEMORY, sc->mem_rid, sc->mem); 246175219Salfred sc->mem = NULL; 246275219Salfred } 246353702Swpaul 246453702Swpaul return; 246553702Swpaul} 246653702Swpaul 246793611Simpvoid 246888546Salfredwi_shutdown(dev) 246953702Swpaul device_t dev; 247053702Swpaul{ 247146492Swpaul struct wi_softc *sc; 247246492Swpaul 247353702Swpaul sc = device_get_softc(dev); 247446492Swpaul wi_stop(sc); 247546492Swpaul 247646492Swpaul return; 247746492Swpaul} 247853702Swpaul 247953702Swpaul#ifdef WICACHE 248053702Swpaul/* wavelan signal strength cache code. 248153702Swpaul * store signal/noise/quality on per MAC src basis in 248253702Swpaul * a small fixed cache. The cache wraps if > MAX slots 248353702Swpaul * used. The cache may be zeroed out to start over. 248453702Swpaul * Two simple filters exist to reduce computation: 248553702Swpaul * 1. ip only (literally 0x800) which may be used 248653702Swpaul * to ignore some packets. It defaults to ip only. 248753702Swpaul * it could be used to focus on broadcast, non-IP 802.11 beacons. 248853702Swpaul * 2. multicast/broadcast only. This may be used to 248953702Swpaul * ignore unicast packets and only cache signal strength 249053702Swpaul * for multicast/broadcast packets (beacons); e.g., Mobile-IP 249153702Swpaul * beacons and not unicast traffic. 249253702Swpaul * 249353702Swpaul * The cache stores (MAC src(index), IP src (major clue), signal, 249453702Swpaul * quality, noise) 249553702Swpaul * 249653702Swpaul * No apologies for storing IP src here. It's easy and saves much 249753702Swpaul * trouble elsewhere. The cache is assumed to be INET dependent, 249853702Swpaul * although it need not be. 249953702Swpaul */ 250053702Swpaul 250153702Swpaul#ifdef documentation 250253702Swpaul 250353702Swpaulint wi_sigitems; /* number of cached entries */ 250453702Swpaulstruct wi_sigcache wi_sigcache[MAXWICACHE]; /* array of cache entries */ 250553702Swpaulint wi_nextitem; /* index/# of entries */ 250653702Swpaul 250753702Swpaul 250853702Swpaul#endif 250953702Swpaul 251053702Swpaul/* control variables for cache filtering. Basic idea is 251153702Swpaul * to reduce cost (e.g., to only Mobile-IP agent beacons 251253702Swpaul * which are broadcast or multicast). Still you might 251353702Swpaul * want to measure signal strength with unicast ping packets 251453702Swpaul * on a pt. to pt. ant. setup. 251553702Swpaul */ 251653702Swpaul/* set true if you want to limit cache items to broadcast/mcast 251753702Swpaul * only packets (not unicast). Useful for mobile-ip beacons which 251853702Swpaul * are broadcast/multicast at network layer. Default is all packets 251953702Swpaul * so ping/unicast will work say with pt. to pt. antennae setup. 252053702Swpaul */ 252153702Swpaulstatic int wi_cache_mcastonly = 0; 252253702SwpaulSYSCTL_INT(_machdep, OID_AUTO, wi_cache_mcastonly, CTLFLAG_RW, 252353702Swpaul &wi_cache_mcastonly, 0, ""); 252453702Swpaul 252553702Swpaul/* set true if you want to limit cache items to IP packets only 252653702Swpaul*/ 252753702Swpaulstatic int wi_cache_iponly = 1; 252853702SwpaulSYSCTL_INT(_machdep, OID_AUTO, wi_cache_iponly, CTLFLAG_RW, 252953702Swpaul &wi_cache_iponly, 0, ""); 253053702Swpaul 253153702Swpaul/* 253253702Swpaul * Original comments: 253353702Swpaul * ----------------- 253453702Swpaul * wi_cache_store, per rx packet store signal 253553702Swpaul * strength in MAC (src) indexed cache. 253653702Swpaul * 253753702Swpaul * follows linux driver in how signal strength is computed. 253853702Swpaul * In ad hoc mode, we use the rx_quality field. 253953702Swpaul * signal and noise are trimmed to fit in the range from 47..138. 254053702Swpaul * rx_quality field MSB is signal strength. 254153702Swpaul * rx_quality field LSB is noise. 254253702Swpaul * "quality" is (signal - noise) as is log value. 254353702Swpaul * note: quality CAN be negative. 254453702Swpaul * 254553702Swpaul * In BSS mode, we use the RID for communication quality. 254653702Swpaul * TBD: BSS mode is currently untested. 254753702Swpaul * 254853702Swpaul * Bill's comments: 254953702Swpaul * --------------- 255053702Swpaul * Actually, we use the rx_quality field all the time for both "ad-hoc" 255153702Swpaul * and BSS modes. Why? Because reading an RID is really, really expensive: 255253702Swpaul * there's a bunch of PIO operations that have to be done to read a record 255353702Swpaul * from the NIC, and reading the comms quality RID each time a packet is 255453702Swpaul * received can really hurt performance. We don't have to do this anyway: 255553702Swpaul * the comms quality field only reflects the values in the rx_quality field 255653702Swpaul * anyway. The comms quality RID is only meaningful in infrastructure mode, 255753702Swpaul * but the values it contains are updated based on the rx_quality from 255853702Swpaul * frames received from the access point. 255953702Swpaul * 256053702Swpaul * Also, according to Lucent, the signal strength and noise level values 256153702Swpaul * can be converted to dBms by subtracting 149, so I've modified the code 256253702Swpaul * to do that instead of the scaling it did originally. 256353702Swpaul */ 256488546Salfredstatic void 256588546Salfredwi_cache_store(struct wi_softc *sc, struct ether_header *eh, 256653702Swpaul struct mbuf *m, unsigned short rx_quality) 256753702Swpaul{ 256853702Swpaul struct ip *ip = 0; 256953702Swpaul int i; 257053702Swpaul static int cache_slot = 0; /* use this cache entry */ 257153702Swpaul static int wrapindex = 0; /* next "free" cache entry */ 257253702Swpaul int sig, noise; 257353702Swpaul int sawip=0; 257453702Swpaul 257594405Simp /* 257694405Simp * filters: 257753702Swpaul * 1. ip only 257853702Swpaul * 2. configurable filter to throw out unicast packets, 257953702Swpaul * keep multicast only. 258053702Swpaul */ 258153702Swpaul 258275276Salfred if ((ntohs(eh->ether_type) == ETHERTYPE_IP)) { 258353702Swpaul sawip = 1; 258453702Swpaul } 258553702Swpaul 258694405Simp /* 258794405Simp * filter for ip packets only 258853702Swpaul */ 258953702Swpaul if (wi_cache_iponly && !sawip) { 259053702Swpaul return; 259153702Swpaul } 259253702Swpaul 259394405Simp /* 259494405Simp * filter for broadcast/multicast only 259553702Swpaul */ 259653702Swpaul if (wi_cache_mcastonly && ((eh->ether_dhost[0] & 1) == 0)) { 259753702Swpaul return; 259853702Swpaul } 259953702Swpaul 260053702Swpaul#ifdef SIGDEBUG 260153702Swpaul printf("wi%d: q value %x (MSB=0x%x, LSB=0x%x) \n", sc->wi_unit, 260253702Swpaul rx_quality & 0xffff, rx_quality >> 8, rx_quality & 0xff); 260353702Swpaul#endif 260453702Swpaul 260594405Simp /* 260694405Simp * find the ip header. we want to store the ip_src 260753702Swpaul * address. 260853702Swpaul */ 260994405Simp if (sawip) 261053702Swpaul ip = mtod(m, struct ip *); 261153702Swpaul 261294405Simp /* 261394405Simp * do a linear search for a matching MAC address 261453702Swpaul * in the cache table 261553702Swpaul * . MAC address is 6 bytes, 261653702Swpaul * . var w_nextitem holds total number of entries already cached 261753702Swpaul */ 261853702Swpaul for(i = 0; i < sc->wi_nextitem; i++) { 261953702Swpaul if (! bcmp(eh->ether_shost , sc->wi_sigcache[i].macsrc, 6 )) { 262094405Simp /* 262194405Simp * Match!, 262253702Swpaul * so we already have this entry, 262353702Swpaul * update the data 262453702Swpaul */ 262553702Swpaul break; 262653702Swpaul } 262753702Swpaul } 262853702Swpaul 262994405Simp /* 263094405Simp * did we find a matching mac address? 263153702Swpaul * if yes, then overwrite a previously existing cache entry 263253702Swpaul */ 263353702Swpaul if (i < sc->wi_nextitem ) { 263453702Swpaul cache_slot = i; 263553702Swpaul } 263694405Simp /* 263794405Simp * else, have a new address entry,so 263853702Swpaul * add this new entry, 263953702Swpaul * if table full, then we need to replace LRU entry 264053702Swpaul */ 264153702Swpaul else { 264253702Swpaul 264394405Simp /* 264494405Simp * check for space in cache table 264553702Swpaul * note: wi_nextitem also holds number of entries 264653702Swpaul * added in the cache table 264753702Swpaul */ 264853702Swpaul if ( sc->wi_nextitem < MAXWICACHE ) { 264953702Swpaul cache_slot = sc->wi_nextitem; 265053702Swpaul sc->wi_nextitem++; 265153702Swpaul sc->wi_sigitems = sc->wi_nextitem; 265253702Swpaul } 265353702Swpaul /* no space found, so simply wrap with wrap index 265453702Swpaul * and "zap" the next entry 265553702Swpaul */ 265653702Swpaul else { 265753702Swpaul if (wrapindex == MAXWICACHE) { 265853702Swpaul wrapindex = 0; 265953702Swpaul } 266053702Swpaul cache_slot = wrapindex++; 266153702Swpaul } 266253702Swpaul } 266353702Swpaul 266494405Simp /* 266594405Simp * invariant: cache_slot now points at some slot 266653702Swpaul * in cache. 266753702Swpaul */ 266853702Swpaul if (cache_slot < 0 || cache_slot >= MAXWICACHE) { 266953702Swpaul log(LOG_ERR, "wi_cache_store, bad index: %d of " 267053702Swpaul "[0..%d], gross cache error\n", 267153702Swpaul cache_slot, MAXWICACHE); 267253702Swpaul return; 267353702Swpaul } 267453702Swpaul 267594405Simp /* 267694405Simp * store items in cache 267753702Swpaul * .ip source address 267853702Swpaul * .mac src 267953702Swpaul * .signal, etc. 268053702Swpaul */ 268194405Simp if (sawip) 268253702Swpaul sc->wi_sigcache[cache_slot].ipsrc = ip->ip_src.s_addr; 268353702Swpaul bcopy( eh->ether_shost, sc->wi_sigcache[cache_slot].macsrc, 6); 268453702Swpaul 268553702Swpaul sig = (rx_quality >> 8) & 0xFF; 268653702Swpaul noise = rx_quality & 0xFF; 268753702Swpaul sc->wi_sigcache[cache_slot].signal = sig - 149; 268853702Swpaul sc->wi_sigcache[cache_slot].noise = noise - 149; 268953702Swpaul sc->wi_sigcache[cache_slot].quality = sig - noise; 269053702Swpaul 269153702Swpaul return; 269253702Swpaul} 269353702Swpaul#endif 269477217Sphk 269588546Salfredstatic int 269688546Salfredwi_get_cur_ssid(sc, ssid, len) 269777217Sphk struct wi_softc *sc; 269877217Sphk char *ssid; 269977217Sphk int *len; 270077217Sphk{ 270177217Sphk int error = 0; 270277217Sphk struct wi_req wreq; 270377217Sphk 270477217Sphk wreq.wi_len = WI_MAX_DATALEN; 270577217Sphk switch (sc->wi_ptype) { 270694405Simp#ifdef WI_HOSTAP 270794405Simp case WI_PORTTYPE_AP: 270894405Simp *len = IEEE80211_NWID_LEN; 270994405Simp bcopy(sc->wi_net_name, ssid, IEEE80211_NWID_LEN); 271094405Simp break; 271194405Simp#endif 271277217Sphk case WI_PORTTYPE_ADHOC: 271377217Sphk wreq.wi_type = WI_RID_CURRENT_SSID; 271477217Sphk error = wi_read_record(sc, (struct wi_ltv_gen *)&wreq); 271577217Sphk if (error != 0) 271677217Sphk break; 271777217Sphk if (wreq.wi_val[0] > IEEE80211_NWID_LEN) { 271877217Sphk error = EINVAL; 271977217Sphk break; 272077217Sphk } 272177217Sphk *len = wreq.wi_val[0]; 272277217Sphk bcopy(&wreq.wi_val[1], ssid, IEEE80211_NWID_LEN); 272377217Sphk break; 272477217Sphk case WI_PORTTYPE_BSS: 272577217Sphk wreq.wi_type = WI_RID_COMMQUAL; 272677217Sphk error = wi_read_record(sc, (struct wi_ltv_gen *)&wreq); 272777217Sphk if (error != 0) 272877217Sphk break; 272977217Sphk if (wreq.wi_val[0] != 0) /* associated */ { 273077217Sphk wreq.wi_type = WI_RID_CURRENT_SSID; 273177217Sphk wreq.wi_len = WI_MAX_DATALEN; 273277217Sphk error = wi_read_record(sc, (struct wi_ltv_gen *)&wreq); 273377217Sphk if (error != 0) 273477217Sphk break; 273577217Sphk if (wreq.wi_val[0] > IEEE80211_NWID_LEN) { 273677217Sphk error = EINVAL; 273777217Sphk break; 273877217Sphk } 273977217Sphk *len = wreq.wi_val[0]; 274077217Sphk bcopy(&wreq.wi_val[1], ssid, IEEE80211_NWID_LEN); 274177217Sphk } else { 274277217Sphk *len = IEEE80211_NWID_LEN; 274377217Sphk bcopy(sc->wi_net_name, ssid, IEEE80211_NWID_LEN); 274477217Sphk } 274577217Sphk break; 274677217Sphk default: 274777217Sphk error = EINVAL; 274877217Sphk break; 274977217Sphk } 275077217Sphk 275177217Sphk return error; 275277217Sphk} 275377217Sphk 275488546Salfredstatic int 275588546Salfredwi_media_change(ifp) 275677217Sphk struct ifnet *ifp; 275777217Sphk{ 275877217Sphk struct wi_softc *sc = ifp->if_softc; 275977217Sphk int otype = sc->wi_ptype; 276077217Sphk int orate = sc->wi_tx_rate; 276177217Sphk 276277217Sphk if ((sc->ifmedia.ifm_cur->ifm_media & IFM_IEEE80211_ADHOC) != 0) 276377217Sphk sc->wi_ptype = WI_PORTTYPE_ADHOC; 276494405Simp#if defined(WI_HOSTAP) && defined(IFM_IEEE80211_HOSTAP) 276594405Simp else if ((sc->ifmedia.ifm_cur->ifm_media & IFM_IEEE80211_HOSTAP) != 0) 276694405Simp sc->wi_ptype = WI_PORTTYPE_AP; 276794405Simp#endif 276877217Sphk else 276977217Sphk sc->wi_ptype = WI_PORTTYPE_BSS; 277077217Sphk 277177217Sphk switch (IFM_SUBTYPE(sc->ifmedia.ifm_cur->ifm_media)) { 277277217Sphk case IFM_IEEE80211_DS1: 277377217Sphk sc->wi_tx_rate = 1; 277477217Sphk break; 277577217Sphk case IFM_IEEE80211_DS2: 277677217Sphk sc->wi_tx_rate = 2; 277777217Sphk break; 277877217Sphk case IFM_IEEE80211_DS5: 277977217Sphk sc->wi_tx_rate = 5; 278077217Sphk break; 278177217Sphk case IFM_IEEE80211_DS11: 278277217Sphk sc->wi_tx_rate = 11; 278377217Sphk break; 278477217Sphk case IFM_AUTO: 278577217Sphk sc->wi_tx_rate = 3; 278677217Sphk break; 278777217Sphk } 278877217Sphk 278977217Sphk if (otype != sc->wi_ptype || 279077217Sphk orate != sc->wi_tx_rate) 279177217Sphk wi_init(sc); 279277217Sphk 279377217Sphk return(0); 279477217Sphk} 279577217Sphk 279688546Salfredstatic void 279788546Salfredwi_media_status(ifp, imr) 279877217Sphk struct ifnet *ifp; 279977217Sphk struct ifmediareq *imr; 280077217Sphk{ 280177217Sphk struct wi_req wreq; 280277217Sphk struct wi_softc *sc = ifp->if_softc; 280377217Sphk 280477217Sphk if (sc->wi_tx_rate == 3) { 280577217Sphk imr->ifm_active = IFM_IEEE80211|IFM_AUTO; 280677217Sphk if (sc->wi_ptype == WI_PORTTYPE_ADHOC) 280777217Sphk imr->ifm_active |= IFM_IEEE80211_ADHOC; 280894405Simp#if defined(WI_HOSTAP) && defined(IFM_IEEE80211_HOSTAP) 280994405Simp else if (sc->wi_ptype == WI_PORTTYPE_AP) 281094405Simp imr->ifm_active |= IFM_IEEE80211_HOSTAP; 281194405Simp#endif 281277217Sphk wreq.wi_type = WI_RID_CUR_TX_RATE; 281377217Sphk wreq.wi_len = WI_MAX_DATALEN; 281477217Sphk if (wi_read_record(sc, (struct wi_ltv_gen *)&wreq) == 0) { 281577217Sphk switch(wreq.wi_val[0]) { 281677217Sphk case 1: 281777217Sphk imr->ifm_active |= IFM_IEEE80211_DS1; 281877217Sphk break; 281977217Sphk case 2: 282077217Sphk imr->ifm_active |= IFM_IEEE80211_DS2; 282177217Sphk break; 282277217Sphk case 6: 282377217Sphk imr->ifm_active |= IFM_IEEE80211_DS5; 282477217Sphk break; 282577217Sphk case 11: 282677217Sphk imr->ifm_active |= IFM_IEEE80211_DS11; 282777217Sphk break; 282877217Sphk } 282977217Sphk } 283077217Sphk } else { 283177217Sphk imr->ifm_active = sc->ifmedia.ifm_cur->ifm_media; 283277217Sphk } 283377217Sphk 283477217Sphk imr->ifm_status = IFM_AVALID; 283577217Sphk if (sc->wi_ptype == WI_PORTTYPE_ADHOC) 283677217Sphk /* 283777217Sphk * XXX: It would be nice if we could give some actually 283877217Sphk * useful status like whether we joined another IBSS or 283977217Sphk * created one ourselves. 284077217Sphk */ 284177217Sphk imr->ifm_status |= IFM_ACTIVE; 284294405Simp#ifdef WI_HOSTAP 284394405Simp else if (sc->wi_ptype == WI_PORTTYPE_AP) 284494405Simp imr->ifm_status |= IFM_ACTIVE; 284594405Simp#endif 284677217Sphk else { 284777217Sphk wreq.wi_type = WI_RID_COMMQUAL; 284877217Sphk wreq.wi_len = WI_MAX_DATALEN; 284977217Sphk if (wi_read_record(sc, (struct wi_ltv_gen *)&wreq) == 0 && 285077217Sphk wreq.wi_val[0] != 0) 285177217Sphk imr->ifm_status |= IFM_ACTIVE; 285277217Sphk } 285377217Sphk} 285493359Simp 285593359Simpstatic int 285693359Simpwi_get_debug(sc, wreq) 285793359Simp struct wi_softc *sc; 285893359Simp struct wi_req *wreq; 285993359Simp{ 286093359Simp int error = 0; 286193359Simp 286293359Simp wreq->wi_len = 1; 286393359Simp 286493359Simp switch (wreq->wi_type) { 286593359Simp case WI_DEBUG_SLEEP: 286693359Simp wreq->wi_len++; 286793359Simp wreq->wi_val[0] = sc->wi_debug.wi_sleep; 286893359Simp break; 286993359Simp case WI_DEBUG_DELAYSUPP: 287093359Simp wreq->wi_len++; 287193359Simp wreq->wi_val[0] = sc->wi_debug.wi_delaysupp; 287293359Simp break; 287393359Simp case WI_DEBUG_TXSUPP: 287493359Simp wreq->wi_len++; 287593359Simp wreq->wi_val[0] = sc->wi_debug.wi_txsupp; 287693359Simp break; 287793359Simp case WI_DEBUG_MONITOR: 287893359Simp wreq->wi_len++; 287993359Simp wreq->wi_val[0] = sc->wi_debug.wi_monitor; 288093359Simp break; 288193359Simp case WI_DEBUG_LEDTEST: 288293359Simp wreq->wi_len += 3; 288393359Simp wreq->wi_val[0] = sc->wi_debug.wi_ledtest; 288493359Simp wreq->wi_val[1] = sc->wi_debug.wi_ledtest_param0; 288593359Simp wreq->wi_val[2] = sc->wi_debug.wi_ledtest_param1; 288693359Simp break; 288793359Simp case WI_DEBUG_CONTTX: 288893359Simp wreq->wi_len += 2; 288993359Simp wreq->wi_val[0] = sc->wi_debug.wi_conttx; 289093359Simp wreq->wi_val[1] = sc->wi_debug.wi_conttx_param0; 289193359Simp break; 289293359Simp case WI_DEBUG_CONTRX: 289393359Simp wreq->wi_len++; 289493359Simp wreq->wi_val[0] = sc->wi_debug.wi_contrx; 289593359Simp break; 289693359Simp case WI_DEBUG_SIGSTATE: 289793359Simp wreq->wi_len += 2; 289893359Simp wreq->wi_val[0] = sc->wi_debug.wi_sigstate; 289993359Simp wreq->wi_val[1] = sc->wi_debug.wi_sigstate_param0; 290093359Simp break; 290193359Simp case WI_DEBUG_CONFBITS: 290293359Simp wreq->wi_len += 2; 290393359Simp wreq->wi_val[0] = sc->wi_debug.wi_confbits; 290493359Simp wreq->wi_val[1] = sc->wi_debug.wi_confbits_param0; 290593359Simp break; 290693359Simp default: 290793359Simp error = EIO; 290893359Simp break; 290993359Simp } 291093359Simp 291193359Simp return (error); 291293359Simp} 291393359Simp 291493359Simpstatic int 291593359Simpwi_set_debug(sc, wreq) 291693359Simp struct wi_softc *sc; 291793359Simp struct wi_req *wreq; 291893359Simp{ 291993359Simp int error = 0; 292093359Simp u_int16_t cmd, param0 = 0, param1 = 0; 292193359Simp 292293359Simp switch (wreq->wi_type) { 292393359Simp case WI_DEBUG_RESET: 292493359Simp case WI_DEBUG_INIT: 292593359Simp case WI_DEBUG_CALENABLE: 292693359Simp break; 292793359Simp case WI_DEBUG_SLEEP: 292893359Simp sc->wi_debug.wi_sleep = 1; 292993359Simp break; 293093359Simp case WI_DEBUG_WAKE: 293193359Simp sc->wi_debug.wi_sleep = 0; 293293359Simp break; 293393359Simp case WI_DEBUG_CHAN: 293493359Simp param0 = wreq->wi_val[0]; 293593359Simp break; 293693359Simp case WI_DEBUG_DELAYSUPP: 293793359Simp sc->wi_debug.wi_delaysupp = 1; 293893359Simp break; 293993359Simp case WI_DEBUG_TXSUPP: 294093359Simp sc->wi_debug.wi_txsupp = 1; 294193359Simp break; 294293359Simp case WI_DEBUG_MONITOR: 294393359Simp sc->wi_debug.wi_monitor = 1; 294493359Simp break; 294593359Simp case WI_DEBUG_LEDTEST: 294693359Simp param0 = wreq->wi_val[0]; 294793359Simp param1 = wreq->wi_val[1]; 294893359Simp sc->wi_debug.wi_ledtest = 1; 294993359Simp sc->wi_debug.wi_ledtest_param0 = param0; 295093359Simp sc->wi_debug.wi_ledtest_param1 = param1; 295193359Simp break; 295293359Simp case WI_DEBUG_CONTTX: 295393359Simp param0 = wreq->wi_val[0]; 295493359Simp sc->wi_debug.wi_conttx = 1; 295593359Simp sc->wi_debug.wi_conttx_param0 = param0; 295693359Simp break; 295793359Simp case WI_DEBUG_STOPTEST: 295893359Simp sc->wi_debug.wi_delaysupp = 0; 295993359Simp sc->wi_debug.wi_txsupp = 0; 296093359Simp sc->wi_debug.wi_monitor = 0; 296193359Simp sc->wi_debug.wi_ledtest = 0; 296293359Simp sc->wi_debug.wi_ledtest_param0 = 0; 296393359Simp sc->wi_debug.wi_ledtest_param1 = 0; 296493359Simp sc->wi_debug.wi_conttx = 0; 296593359Simp sc->wi_debug.wi_conttx_param0 = 0; 296693359Simp sc->wi_debug.wi_contrx = 0; 296793359Simp sc->wi_debug.wi_sigstate = 0; 296893359Simp sc->wi_debug.wi_sigstate_param0 = 0; 296993359Simp break; 297093359Simp case WI_DEBUG_CONTRX: 297193359Simp sc->wi_debug.wi_contrx = 1; 297293359Simp break; 297393359Simp case WI_DEBUG_SIGSTATE: 297493359Simp param0 = wreq->wi_val[0]; 297593359Simp sc->wi_debug.wi_sigstate = 1; 297693359Simp sc->wi_debug.wi_sigstate_param0 = param0; 297793359Simp break; 297893359Simp case WI_DEBUG_CONFBITS: 297993359Simp param0 = wreq->wi_val[0]; 298093359Simp param1 = wreq->wi_val[1]; 298193359Simp sc->wi_debug.wi_confbits = param0; 298293359Simp sc->wi_debug.wi_confbits_param0 = param1; 298393359Simp break; 298493359Simp default: 298593359Simp error = EIO; 298693359Simp break; 298793359Simp } 298893359Simp 298993359Simp if (error) 299093359Simp return (error); 299193359Simp 299293359Simp cmd = WI_CMD_DEBUG | (wreq->wi_type << 8); 299393359Simp error = wi_cmd(sc, cmd, param0, param1, 0); 299493359Simp 299593359Simp return (error); 299693359Simp} 2997