diff options
Diffstat (limited to 'sys/dev/wi')
-rw-r--r-- | sys/dev/wi/if_wavelan_ieee.h | 42 | ||||
-rw-r--r-- | sys/dev/wi/if_wi.c | 4458 | ||||
-rw-r--r-- | sys/dev/wi/if_wi_pccard.c | 8 | ||||
-rw-r--r-- | sys/dev/wi/if_wi_pci.c | 16 | ||||
-rw-r--r-- | sys/dev/wi/if_wireg.h | 281 | ||||
-rw-r--r-- | sys/dev/wi/if_wivar.h | 223 |
6 files changed, 2348 insertions, 2680 deletions
diff --git a/sys/dev/wi/if_wavelan_ieee.h b/sys/dev/wi/if_wavelan_ieee.h index 8916750..36fbbba 100644 --- a/sys/dev/wi/if_wavelan_ieee.h +++ b/sys/dev/wi/if_wavelan_ieee.h @@ -77,7 +77,8 @@ struct wi_req { #define WI_RID_FWDOWNLOAD 0x0500 #define WI_RID_MONITOR_MODE 0x0600 #define WI_RID_MIF 0x0700 - +#define WI_RID_SCAN_APS 0x0800 +#define WI_RID_READ_APS 0x0900 struct wi_80211_hdr { u_int16_t frame_ctl; @@ -166,7 +167,6 @@ struct wi_fwdownload { caddr_t sec_data; /* Pointer (user) to secondary data */ }; -#ifndef _KERNEL struct wi_counters { u_int32_t wi_tx_unicast_frames; u_int32_t wi_tx_multicast_frames; @@ -190,7 +190,6 @@ struct wi_counters { u_int32_t wi_rx_msg_in_msg_frags; u_int32_t wi_rx_msg_in_bad_msg_frags; }; -#endif /* * Network parameters, static configuration entities. @@ -238,7 +237,9 @@ struct wi_counters { #define WI_RID_WEP_MAPTABLE 0xFC29 #define WI_RID_CNFAUTHMODE 0xFC2A #define WI_RID_ROAMING_MODE 0xFC2D +#define WI_RID_OWN_BEACON_INT 0xFC33 /* beacon xmit time for BSS creation */ #define WI_RID_CNF_DBM_ADJUST 0xFC46 +#define WI_RID_DBM_ADJUST 0xFC46 /* RSSI - WI_RID_DBM_ADJUST ~ dBm */ #define WI_RID_BASIC_RATE 0xFCB3 #define WI_RID_SUPPORT_RATE 0xFCB4 @@ -280,18 +281,17 @@ struct wi_counters { #define WI_RID_TX_CRYPT_KEY 0xFCB1 #define WI_RID_TICK_TIME 0xFCE0 -#ifndef _KERNEL struct wi_key { u_int16_t wi_keylen; u_int8_t wi_keydat[14]; }; +#define WI_NLTV_KEYS 4 struct wi_ltv_keys { u_int16_t wi_len; u_int16_t wi_type; - struct wi_key wi_keys[4]; + struct wi_key wi_keys[WI_NLTV_KEYS]; }; -#endif /* * NIC information @@ -327,7 +327,7 @@ struct wi_ltv_keys { #define WI_RID_CURRENT_BSSID 0xFD42 /* ID of actually connected BSS */ #define WI_RID_COMMS_QUALITY 0xFD43 /* quality of BSS connection */ #define WI_RID_CUR_TX_RATE 0xFD44 /* current TX rate */ -#define WI_RID_OWN_BEACON_INT 0xFD45 /* beacon xmit time for BSS creation */ +#define WI_RID_CUR_BEACON_INT 0xFD45 /* current beacon interval */ #define WI_RID_CUR_SCALE_THRESH 0xFD46 /* actual system scane thresh setting */ #define WI_RID_PROT_RESP_TIME 0xFD47 /* time to wait for resp to req msg */ #define WI_RID_SHORT_RTR_LIM 0xFD48 /* max tx attempts for short frames */ @@ -349,6 +349,34 @@ struct wi_ltv_keys { #define WI_RID_PCI_INFO 0xFD87 /* point coordination func cap */ /* + * Scan Information + */ +#define WI_RID_BCAST_SCAN_REQ 0xFCAB /* Broadcast Scan request (Symbol) */ +#define BSCAN_5SEC 0x01 +#define BSCAN_ONETIME 0x02 +#define BSCAN_PASSIVE 0x40 +#define BSCAN_BCAST 0x80 +#define WI_RID_SCAN_REQ 0xFCE1 /* Scan request (STA only) */ +#define WI_RID_JOIN_REQ 0xFCE2 /* Join request (STA only) */ +#define WI_RID_AUTH_STATION 0xFCE3 /* Authenticates Station (AP) */ +#define WI_RID_CHANNEL_REQ 0xFCE4 /* Channel Information Request (AP) */ +#define WI_RID_SCAN_RESULTS 0xFD88 /* Scan Results Table */ + +struct wi_apinfo { + int scanreason; /* ScanReason */ + char bssid[6]; /* BSSID (mac address) */ + int channel; /* Channel */ + int signal; /* Signal level */ + int noise; /* Average Noise Level*/ + int quality; /* Quality */ + int namelen; /* Length of SSID string */ + char name[32]; /* SSID string */ + int capinfo; /* Capability info. */ + int interval; /* BSS Beacon Interval */ + int rate; /* Data Rate */ +}; + +/* * Modem information */ #define WI_RID_PHY_TYPE 0xFDC0 /* phys layer type indication */ diff --git a/sys/dev/wi/if_wi.c b/sys/dev/wi/if_wi.c index 700c720..f19abe7 100644 --- a/sys/dev/wi/if_wi.c +++ b/sys/dev/wi/if_wi.c @@ -1,3 +1,5 @@ +/* $NetBSD: wi.c,v 1.109 2003/01/09 08:52:19 dyoung Exp $ */ + /* * Copyright (c) 1997, 1998, 1999 * Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved. @@ -31,9 +33,9 @@ */ /* - * Lucent WaveLAN/IEEE 802.11 PCMCIA driver for FreeBSD. + * Lucent WaveLAN/IEEE 802.11 PCMCIA driver. * - * Written by Bill Paul <wpaul@ctr.columbia.edu> + * Original FreeBSD driver written by Bill Paul <wpaul@ctr.columbia.edu> * Electrical Engineering Department * Columbia University, New York City */ @@ -59,6 +61,11 @@ * Prism 2 chipsets with firmware from Intersil and Symbol. */ +#define WI_HERMES_AUTOINC_WAR /* Work around data write autoinc bug. */ +#define WI_HERMES_STATS_WAR /* Work around stats counter bug. */ + +#define NBPFILTER 1 + #include <sys/param.h> #include <sys/systm.h> #if __FreeBSD_version >= 500033 @@ -97,47 +104,54 @@ #include <net/bpf.h> #include <dev/wi/if_wavelan_ieee.h> -#include <dev/wi/wi_hostap.h> #include <dev/wi/if_wivar.h> #include <dev/wi/if_wireg.h> +#define IF_POLL(ifq, m) ((m) = (ifq)->ifq_head) +#define IFQ_POLL(ifq, m) IF_POLL((ifq), (m)) +#define IFQ_DEQUEUE(ifq, m) IF_DEQUEUE((ifq), (m)) + #if !defined(lint) static const char rcsid[] = "$FreeBSD$"; #endif -static void wi_intr(void *); -static void wi_reset(struct wi_softc *); -static int wi_ioctl(struct ifnet *, u_long, caddr_t); -static void wi_init(void *); static void wi_start(struct ifnet *); +static int wi_reset(struct wi_softc *); static void wi_watchdog(struct ifnet *); -static void wi_rxeof(struct wi_softc *); -static void wi_txeof(struct wi_softc *, int); -static void wi_update_stats(struct wi_softc *); -static void wi_setmulti(struct wi_softc *); - -static int wi_cmd(struct wi_softc *, int, int, int, int); -static int wi_read_record(struct wi_softc *, struct wi_ltv_gen *); -static int wi_write_record(struct wi_softc *, struct wi_ltv_gen *); -static int wi_read_data(struct wi_softc *, int, int, caddr_t, int); -static int wi_write_data(struct wi_softc *, int, int, caddr_t, int); -static int wi_seek(struct wi_softc *, int, int, int); -static int wi_alloc_nicmem(struct wi_softc *, int, int *); -static void wi_inquire(void *); -static void wi_setdef(struct wi_softc *, struct wi_req *); - -#ifdef WICACHE -static -void wi_cache_store(struct wi_softc *, struct ether_header *, - struct mbuf *, unsigned short); -#endif - -static int wi_get_cur_ssid(struct wi_softc *, char *, int *); -static void wi_get_id(struct wi_softc *); -static int wi_media_change(struct ifnet *); +static int wi_ioctl(struct ifnet *, u_long, caddr_t); +static int wi_media_change(struct ifnet *); static void wi_media_status(struct ifnet *, struct ifmediareq *); +static void wi_rx_intr(struct wi_softc *); +static void wi_tx_intr(struct wi_softc *); +static void wi_tx_ex_intr(struct wi_softc *); +static void wi_info_intr(struct wi_softc *); + +static int wi_get_cfg(struct ifnet *, u_long, caddr_t); +static int wi_set_cfg(struct ifnet *, u_long, caddr_t); +static int wi_write_txrate(struct wi_softc *); +static int wi_write_wep(struct wi_softc *); +static int wi_write_multi(struct wi_softc *); +static int wi_alloc_fid(struct wi_softc *, int, int *); +static void wi_read_nicid(struct wi_softc *); +static int wi_write_ssid(struct wi_softc *, int, u_int8_t *, int); + +static int wi_cmd(struct wi_softc *, int, int, int, int); +static int wi_seek_bap(struct wi_softc *, int, int); +static int wi_read_bap(struct wi_softc *, int, int, void *, int); +static int wi_write_bap(struct wi_softc *, int, int, void *, int); +static int wi_mwrite_bap(struct wi_softc *, int, int, struct mbuf *, int); +static int wi_read_rid(struct wi_softc *, int, void *, int *); +static int wi_write_rid(struct wi_softc *, int, void *, int); + +static int wi_newstate(void *, enum ieee80211_state); + +static int wi_scan_ap(struct wi_softc *); +static void wi_scan_result(struct wi_softc *, int, int); + +static void wi_dump_pkt(struct wi_frame *, struct ieee80211_node *, int rssi); + static int wi_get_debug(struct wi_softc *, struct wi_req *); static int wi_set_debug(struct wi_softc *, struct wi_req *); @@ -148,7 +162,37 @@ static int wi_symbol_write_firm(struct wi_softc *, const void *, int, static int wi_symbol_set_hcr(struct wi_softc *, int); #endif -devclass_t wi_devclass; +static __inline int +wi_write_val(struct wi_softc *sc, int rid, u_int16_t val) +{ + + val = htole16(val); + return wi_write_rid(sc, rid, &val, sizeof(val)); +} + +static struct timeval lasttxerror; /* time of last tx error msg */ +static int curtxeps; /* current tx error msgs/sec */ +static int wi_txerate = 10; /* tx error rate: max msgs/sec */ +SYSCTL_INT(_kern, OID_AUTO, wi, CTLFLAG_RW, &wi_txerate, + 0, "Wireless driver max tx error msgs/sec; 0 disables msgs"); + +#define WI_DEBUG +#ifdef WI_DEBUG +static int wi_debug = 0; +SYSCTL_INT(_debug, OID_AUTO, wi, CTLFLAG_RW, &wi_debug, + 0, "Wireless driver debugging printfs"); + +#define DPRINTF(X) if (wi_debug) printf X +#define DPRINTF2(X) if (wi_debug > 1) printf X +#define IFF_DUMPPKTS(_ifp) \ + (((_ifp)->if_flags & (IFF_DEBUG|IFF_LINK2)) == (IFF_DEBUG|IFF_LINK2)) +#else +#define DPRINTF(X) +#define DPRINTF2(X) +#define IFF_DUMPPKTS(_ifp) 0 +#endif + +#define WI_INTRS (WI_EV_RX | WI_EV_ALLOC | WI_EV_INFO) struct wi_card_ident wi_card_ident[] = { /* CARD_ID CARD_NAME FIRM_TYPE */ @@ -187,56 +231,27 @@ struct wi_card_ident wi_card_ident[] = { { 0, NULL, 0 }, }; -int -wi_generic_detach(dev) - device_t dev; -{ - struct wi_softc *sc; - struct ifnet *ifp; - int s; - - sc = device_get_softc(dev); - WI_LOCK(sc, s); - ifp = &sc->arpcom.ac_if; - - if (sc->wi_gone) { - device_printf(dev, "already unloaded\n"); - WI_UNLOCK(sc, s); - return(ENODEV); - } - - wi_stop(sc); - - /* Delete all remaining media. */ - ifmedia_removeall(&sc->ifmedia); - - ether_ifdetach(ifp); - bus_teardown_intr(dev, sc->irq, sc->wi_intrhand); - wi_free(dev); - sc->wi_gone = 1; - - WI_UNLOCK(sc, s); -#if __FreeBSD_version >= 500000 - mtx_destroy(&sc->wi_mtx); -#endif - - return(0); -} +devclass_t wi_devclass; int -wi_generic_attach(device_t dev) +wi_attach(device_t dev) { - struct wi_softc *sc; - struct wi_ltv_macaddr mac; - struct wi_ltv_gen gen; - struct ifnet *ifp; - int error; - int s; - - /* XXX maybe we need the splimp stuff here XXX */ - sc = device_get_softc(dev); - ifp = &sc->arpcom.ac_if; + struct wi_softc *sc = device_get_softc(dev); + struct ieee80211com *ic = &sc->sc_ic; + struct ifnet *ifp = &ic->ic_if; + int i, nrate, mword, buflen; + u_int8_t r; + u_int16_t val; + u_int8_t ratebuf[2 + IEEE80211_RATE_SIZE]; + static const u_int8_t empty_macaddr[IEEE80211_ADDR_LEN] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + int error; + /* + * NB: no locking is needed here; don't put it here + * unless you can prove it! + */ error = bus_setup_intr(dev, sc->irq, INTR_TYPE_NET, wi_intr, sc, &sc->wi_intrhand); @@ -247,13 +262,15 @@ wi_generic_attach(device_t dev) } #if __FreeBSD_version >= 500000 - mtx_init(&sc->wi_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK, + mtx_init(&sc->sc_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK, MTX_DEF | MTX_RECURSE); #endif - WI_LOCK(sc, s); /* Reset the NIC. */ - wi_reset(sc); + if (wi_reset(sc) != 0) { + WI_UNLOCK(sc); + return ENXIO; /* XXX */ + } /* * Read the station address. @@ -261,58 +278,51 @@ wi_generic_attach(device_t dev) * an error when trying to read it the first time, which causes * the probe to fail. */ - mac.wi_type = WI_RID_MAC_NODE; - mac.wi_len = 4; - wi_read_record(sc, (struct wi_ltv_gen *)&mac); - if ((error = wi_read_record(sc, (struct wi_ltv_gen *)&mac)) != 0) { - device_printf(dev, "mac read failed %d\n", error); + buflen = IEEE80211_ADDR_LEN; + error = wi_read_rid(sc, WI_RID_MAC_NODE, ic->ic_myaddr, &buflen); + if (error != 0) { + buflen = IEEE80211_ADDR_LEN; + error = wi_read_rid(sc, WI_RID_MAC_NODE, ic->ic_myaddr, &buflen); + } + if (error || IEEE80211_ADDR_EQ(ic->ic_myaddr, empty_macaddr)) { + if (error != 0) + device_printf(dev, "mac read failed %d\n", error); + else + device_printf(dev, "mac read failed (all zeros)\n"); wi_free(dev); return (error); } - bcopy((char *)&mac.wi_mac_addr, - (char *)&sc->arpcom.ac_enaddr, ETHER_ADDR_LEN); + device_printf(dev, "802.11 address: %6D\n", ic->ic_myaddr, ":"); - device_printf(dev, "802.11 address: %6D\n", sc->arpcom.ac_enaddr, ":"); - - wi_get_id(sc); + /* Read NIC identification */ + wi_read_nicid(sc); ifp->if_softc = sc; - ifp->if_unit = sc->wi_unit; + ifp->if_unit = sc->sc_unit; ifp->if_name = "wi"; - ifp->if_mtu = ETHERMTU; ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; ifp->if_ioctl = wi_ioctl; - ifp->if_output = ether_output; ifp->if_start = wi_start; ifp->if_watchdog = wi_watchdog; ifp->if_init = wi_init; - ifp->if_baudrate = 10000000; ifp->if_snd.ifq_maxlen = IFQ_MAXLEN; - bzero(sc->wi_node_name, sizeof(sc->wi_node_name)); - bcopy(WI_DEFAULT_NODENAME, sc->wi_node_name, - sizeof(WI_DEFAULT_NODENAME) - 1); - - bzero(sc->wi_net_name, sizeof(sc->wi_net_name)); - bcopy(WI_DEFAULT_NETNAME, sc->wi_net_name, - sizeof(WI_DEFAULT_NETNAME) - 1); + ic->ic_phytype = IEEE80211_T_DS; + ic->ic_opmode = IEEE80211_M_STA; + ic->ic_flags = IEEE80211_F_HASPMGT | IEEE80211_F_HASAHDEMO; + ic->ic_state = IEEE80211_S_INIT; + ic->ic_newstate = wi_newstate; - bzero(sc->wi_ibss_name, sizeof(sc->wi_ibss_name)); - bcopy(WI_DEFAULT_IBSS, sc->wi_ibss_name, - sizeof(WI_DEFAULT_IBSS) - 1); - - sc->wi_portnum = WI_DEFAULT_PORT; - sc->wi_ptype = WI_PORTTYPE_BSS; - sc->wi_ap_density = WI_DEFAULT_AP_DENSITY; - sc->wi_rts_thresh = WI_DEFAULT_RTS_THRESH; - sc->wi_tx_rate = WI_DEFAULT_TX_RATE; - sc->wi_max_data_len = WI_DEFAULT_DATALEN; - sc->wi_create_ibss = WI_DEFAULT_CREATE_IBSS; - sc->wi_pm_enabled = WI_DEFAULT_PM_ENABLED; - sc->wi_max_sleep = WI_DEFAULT_MAX_SLEEP; - sc->wi_roaming = WI_DEFAULT_ROAMING; - sc->wi_authtype = WI_DEFAULT_AUTHTYPE; - sc->wi_authmode = IEEE80211_AUTH_OPEN; + /* Find available channels */ + buflen = sizeof(val); + if (wi_read_rid(sc, WI_RID_CHANNEL_LIST, &val, &buflen) != 0) + val = htole16(0x1fff); /* assume 1-11 */ + for (i = 0; i < 16; i++) { + if (isset((u_int8_t*)&val, i)) + setbit(ic->ic_chan_avail, i + 1); + } + KASSERT(ic->ic_chan_avail != 0, + ("wi_attach: no available channels listed!")); /* * Read the default channel from the NIC. This may vary @@ -320,1218 +330,1248 @@ wi_generic_attach(device_t dev) * we can't hard-code a default and expect it to work for * everyone. */ - gen.wi_type = WI_RID_OWN_CHNL; - gen.wi_len = 2; - wi_read_record(sc, &gen); - sc->wi_channel = gen.wi_val; + buflen = sizeof(val); + if (wi_read_rid(sc, WI_RID_OWN_CHNL, &val, &buflen) == 0) + ic->ic_ibss_chan = le16toh(val); + else { + /* use lowest available channel */ + for (i = 0; i < 16 && !isset(ic->ic_chan_avail, i); i++) + ; + ic->ic_ibss_chan = i; + } /* * Set flags based on firmware version. */ switch (sc->sc_firmware_type) { case WI_LUCENT: - sc->wi_flags |= WI_FLAGS_HAS_ROAMING; - if (sc->sc_sta_firmware_ver >= 60000) - sc->wi_flags |= WI_FLAGS_HAS_MOR; - if (sc->sc_sta_firmware_ver >= 60006) { - sc->wi_flags |= WI_FLAGS_HAS_IBSS; - sc->wi_flags |= WI_FLAGS_HAS_CREATE_IBSS; + sc->sc_flags |= WI_FLAGS_HAS_SYSSCALE; +#ifdef WI_HERMES_AUTOINC_WAR + /* XXX: not confirmed, but never seen for recent firmware */ + if (sc->sc_sta_firmware_ver < 40000) { + sc->sc_flags |= WI_FLAGS_BUG_AUTOINC; } - sc->wi_ibss_port = htole16(1); +#endif + if (sc->sc_sta_firmware_ver >= 60000) + sc->sc_flags |= WI_FLAGS_HAS_MOR; + if (sc->sc_sta_firmware_ver >= 60006) + ic->ic_flags |= IEEE80211_F_HASIBSS; + sc->sc_ibss_port = htole16(1); break; + case WI_INTERSIL: - sc->wi_flags |= WI_FLAGS_HAS_ROAMING; - if (sc->sc_sta_firmware_ver >= 800) { - sc->wi_flags |= WI_FLAGS_HAS_IBSS; - sc->wi_flags |= WI_FLAGS_HAS_CREATE_IBSS; - } - /* - * version 0.8.3 and newer are the only ones that are known - * to currently work. Earlier versions can be made to work, - * at least according to the Linux driver. - */ + sc->sc_flags |= WI_FLAGS_HAS_FRAGTHR; + sc->sc_flags |= WI_FLAGS_HAS_ROAMING; + sc->sc_flags |= WI_FLAGS_HAS_SYSSCALE; + if (sc->sc_sta_firmware_ver > 10101) + sc->sc_flags |= WI_FLAGS_HAS_DBMADJUST; + if (sc->sc_sta_firmware_ver >= 800) + ic->ic_flags |= IEEE80211_F_HASIBSS; if (sc->sc_sta_firmware_ver >= 803) - sc->wi_flags |= WI_FLAGS_HAS_HOSTAP; - sc->wi_ibss_port = htole16(0); + ic->ic_flags |= IEEE80211_F_HASHOSTAP; + sc->sc_ibss_port = htole16(0); break; + case WI_SYMBOL: - sc->wi_flags |= WI_FLAGS_HAS_DIVERSITY; - if (sc->sc_sta_firmware_ver >= 20000) - sc->wi_flags |= WI_FLAGS_HAS_IBSS; - /* Older Symbol firmware does not support IBSS creation. */ + sc->sc_flags |= WI_FLAGS_HAS_DIVERSITY; if (sc->sc_sta_firmware_ver >= 25000) - sc->wi_flags |= WI_FLAGS_HAS_CREATE_IBSS; - sc->wi_ibss_port = htole16(4); + ic->ic_flags |= IEEE80211_F_HASIBSS; + sc->sc_ibss_port = htole16(4); break; } /* * Find out if we support WEP on this card. */ - gen.wi_type = WI_RID_WEP_AVAIL; - gen.wi_len = 2; - wi_read_record(sc, &gen); - sc->wi_has_wep = gen.wi_val; - - if (bootverbose) - device_printf(sc->dev, "wi_has_wep = %d\n", sc->wi_has_wep); - - /* - * Find supported rates. - */ - gen.wi_type = WI_RID_DATA_RATES; - gen.wi_len = 2; - if (wi_read_record(sc, &gen)) - sc->wi_supprates = WI_SUPPRATES_1M | WI_SUPPRATES_2M | - WI_SUPPRATES_5M | WI_SUPPRATES_11M; - else - sc->wi_supprates = gen.wi_val; - - bzero((char *)&sc->wi_stats, sizeof(sc->wi_stats)); - - wi_init(sc); - wi_stop(sc); - - ifmedia_init(&sc->ifmedia, 0, wi_media_change, wi_media_status); -#define ADD(m, c) ifmedia_add(&sc->ifmedia, (m), (c), NULL) - if (sc->wi_supprates & WI_SUPPRATES_1M) { - ADD(IFM_MAKEWORD(IFM_IEEE80211, IFM_IEEE80211_DS1, 0, 0), 0); - ADD(IFM_MAKEWORD(IFM_IEEE80211, IFM_IEEE80211_DS1, - IFM_IEEE80211_ADHOC, 0), 0); - if (sc->wi_flags & WI_FLAGS_HAS_IBSS) - ADD(IFM_MAKEWORD(IFM_IEEE80211, IFM_IEEE80211_DS1, - IFM_IEEE80211_IBSS, 0), 0); - if (sc->wi_flags & WI_FLAGS_HAS_CREATE_IBSS) - ADD(IFM_MAKEWORD(IFM_IEEE80211, IFM_IEEE80211_DS1, - IFM_IEEE80211_IBSSMASTER, 0), 0); - if (sc->wi_flags & WI_FLAGS_HAS_HOSTAP) - ADD(IFM_MAKEWORD(IFM_IEEE80211, IFM_IEEE80211_DS1, - IFM_IEEE80211_HOSTAP, 0), 0); - } - if (sc->wi_supprates & WI_SUPPRATES_2M) { - ADD(IFM_MAKEWORD(IFM_IEEE80211, IFM_IEEE80211_DS2, 0, 0), 0); - ADD(IFM_MAKEWORD(IFM_IEEE80211, IFM_IEEE80211_DS2, - IFM_IEEE80211_ADHOC, 0), 0); - if (sc->wi_flags & WI_FLAGS_HAS_IBSS) - ADD(IFM_MAKEWORD(IFM_IEEE80211, IFM_IEEE80211_DS2, - IFM_IEEE80211_IBSS, 0), 0); - if (sc->wi_flags & WI_FLAGS_HAS_CREATE_IBSS) - ADD(IFM_MAKEWORD(IFM_IEEE80211, IFM_IEEE80211_DS2, - IFM_IEEE80211_IBSSMASTER, 0), 0); - if (sc->wi_flags & WI_FLAGS_HAS_HOSTAP) - ADD(IFM_MAKEWORD(IFM_IEEE80211, IFM_IEEE80211_DS2, - IFM_IEEE80211_HOSTAP, 0), 0); - } - if (sc->wi_supprates & WI_SUPPRATES_5M) { - ADD(IFM_MAKEWORD(IFM_IEEE80211, IFM_IEEE80211_DS5, 0, 0), 0); - ADD(IFM_MAKEWORD(IFM_IEEE80211, IFM_IEEE80211_DS5, - IFM_IEEE80211_ADHOC, 0), 0); - if (sc->wi_flags & WI_FLAGS_HAS_IBSS) - ADD(IFM_MAKEWORD(IFM_IEEE80211, IFM_IEEE80211_DS5, - IFM_IEEE80211_IBSS, 0), 0); - if (sc->wi_flags & WI_FLAGS_HAS_CREATE_IBSS) - ADD(IFM_MAKEWORD(IFM_IEEE80211, IFM_IEEE80211_DS5, - IFM_IEEE80211_IBSSMASTER, 0), 0); - if (sc->wi_flags & WI_FLAGS_HAS_HOSTAP) - ADD(IFM_MAKEWORD(IFM_IEEE80211, IFM_IEEE80211_DS5, - IFM_IEEE80211_HOSTAP, 0), 0); - } - if (sc->wi_supprates & WI_SUPPRATES_11M) { - ADD(IFM_MAKEWORD(IFM_IEEE80211, IFM_IEEE80211_DS11, 0, 0), 0); - ADD(IFM_MAKEWORD(IFM_IEEE80211, IFM_IEEE80211_DS11, - IFM_IEEE80211_ADHOC, 0), 0); - if (sc->wi_flags & WI_FLAGS_HAS_IBSS) - ADD(IFM_MAKEWORD(IFM_IEEE80211, IFM_IEEE80211_DS11, - IFM_IEEE80211_IBSS, 0), 0); - if (sc->wi_flags & WI_FLAGS_HAS_CREATE_IBSS) - ADD(IFM_MAKEWORD(IFM_IEEE80211, IFM_IEEE80211_DS11, - IFM_IEEE80211_IBSSMASTER, 0), 0); - if (sc->wi_flags & WI_FLAGS_HAS_HOSTAP) - ADD(IFM_MAKEWORD(IFM_IEEE80211, IFM_IEEE80211_DS11, - IFM_IEEE80211_HOSTAP, 0), 0); - ADD(IFM_MAKEWORD(IFM_IEEE80211, IFM_MANUAL, 0, 0), 0); - } - ADD(IFM_MAKEWORD(IFM_IEEE80211, IFM_AUTO, IFM_IEEE80211_ADHOC, 0), 0); - if (sc->wi_flags & WI_FLAGS_HAS_IBSS) - ADD(IFM_MAKEWORD(IFM_IEEE80211, IFM_AUTO, IFM_IEEE80211_IBSS, - 0), 0); - if (sc->wi_flags & WI_FLAGS_HAS_CREATE_IBSS) - ADD(IFM_MAKEWORD(IFM_IEEE80211, IFM_AUTO, - IFM_IEEE80211_IBSSMASTER, 0), 0); - if (sc->wi_flags & WI_FLAGS_HAS_HOSTAP) - ADD(IFM_MAKEWORD(IFM_IEEE80211, IFM_AUTO, - IFM_IEEE80211_HOSTAP, 0), 0); - ADD(IFM_MAKEWORD(IFM_IEEE80211, IFM_AUTO, 0, 0), 0); -#undef ADD - ifmedia_set(&sc->ifmedia, IFM_MAKEWORD(IFM_IEEE80211, IFM_AUTO, 0, 0)); - - /* - * Call MI attach routine. - */ - ether_ifattach(ifp, sc->arpcom.ac_enaddr); - callout_handle_init(&sc->wi_stat_ch); - WI_UNLOCK(sc, s); + buflen = sizeof(val); + if (wi_read_rid(sc, WI_RID_WEP_AVAIL, &val, &buflen) == 0 && + val != htole16(0)) + ic->ic_flags |= IEEE80211_F_HASWEP; + + /* Find supported rates. */ + buflen = sizeof(ratebuf); + if (wi_read_rid(sc, WI_RID_DATA_RATES, ratebuf, &buflen) == 0) { + nrate = le16toh(*(u_int16_t *)ratebuf); + if (nrate > IEEE80211_RATE_SIZE) + nrate = IEEE80211_RATE_SIZE; + memcpy(ic->ic_sup_rates, ratebuf + 2, nrate); + } else { + /* XXX fallback on error? */ + nrate = 0; + } - return(0); -} + buflen = sizeof(val); + if ((sc->sc_flags & WI_FLAGS_HAS_DBMADJUST) && + wi_read_rid(sc, WI_RID_DBM_ADJUST, &val, &buflen) == 0) { + sc->sc_dbm_adjust = le16toh(val); + } else + sc->sc_dbm_adjust = 100; /* default */ -static void -wi_get_id(sc) - struct wi_softc *sc; -{ - struct wi_ltv_ver ver; - struct wi_card_ident *id; + sc->sc_max_datalen = 2304; + sc->sc_rts_thresh = 2347; + sc->sc_frag_thresh = 2346; + sc->sc_system_scale = 1; + sc->sc_cnfauthmode = IEEE80211_AUTH_OPEN; + sc->sc_roaming_mode = 1; - /* getting chip identity */ - memset(&ver, 0, sizeof(ver)); - ver.wi_type = WI_RID_CARD_ID; - ver.wi_len = 5; - wi_read_record(sc, (struct wi_ltv_gen *)&ver); - device_printf(sc->dev, "using "); - sc->sc_firmware_type = WI_NOTYPE; - for (id = wi_card_ident; id->card_name != NULL; id++) { - if (le16toh(ver.wi_ver[0]) == id->card_id) { - printf("%s", id->card_name); - sc->sc_firmware_type = id->firm_type; - break; - } - } - if (sc->sc_firmware_type == WI_NOTYPE) { - if (le16toh(ver.wi_ver[0]) & 0x8000) { - printf("Unknown PRISM2 chip"); - sc->sc_firmware_type = WI_INTERSIL; - } else { - printf("Unknown Lucent chip"); - sc->sc_firmware_type = WI_LUCENT; - } - } + sc->sc_portnum = WI_DEFAULT_PORT; + sc->sc_authtype = WI_DEFAULT_AUTHTYPE; - if (sc->sc_firmware_type != WI_LUCENT) { - /* get primary firmware version */ - memset(&ver, 0, sizeof(ver)); - ver.wi_type = WI_RID_PRI_IDENTITY; - ver.wi_len = 5; - wi_read_record(sc, (struct wi_ltv_gen *)&ver); - ver.wi_ver[1] = le16toh(ver.wi_ver[1]); - ver.wi_ver[2] = le16toh(ver.wi_ver[2]); - ver.wi_ver[3] = le16toh(ver.wi_ver[3]); - sc->sc_pri_firmware_ver = ver.wi_ver[2] * 10000 + - ver.wi_ver[3] * 100 + ver.wi_ver[1]; - } + bzero(sc->sc_nodename, sizeof(sc->sc_nodename)); + sc->sc_nodelen = sizeof(WI_DEFAULT_NODENAME) - 1; + bcopy(WI_DEFAULT_NODENAME, sc->sc_nodename, sc->sc_nodelen); - /* get station firmware version */ - memset(&ver, 0, sizeof(ver)); - ver.wi_type = WI_RID_STA_IDENTITY; - ver.wi_len = 5; - wi_read_record(sc, (struct wi_ltv_gen *)&ver); - ver.wi_ver[1] = le16toh(ver.wi_ver[1]); - ver.wi_ver[2] = le16toh(ver.wi_ver[2]); - ver.wi_ver[3] = le16toh(ver.wi_ver[3]); - sc->sc_sta_firmware_ver = ver.wi_ver[2] * 10000 + - ver.wi_ver[3] * 100 + ver.wi_ver[1]; - if (sc->sc_firmware_type == WI_INTERSIL && - (sc->sc_sta_firmware_ver == 10102 || - sc->sc_sta_firmware_ver == 20102)) { - struct wi_ltv_str sver; - char *p; + bzero(sc->sc_net_name, sizeof(sc->sc_net_name)); + bcopy(WI_DEFAULT_NETNAME, sc->sc_net_name, + sizeof(WI_DEFAULT_NETNAME) - 1); - memset(&sver, 0, sizeof(sver)); - sver.wi_type = WI_RID_SYMBOL_IDENTITY; - sver.wi_len = 7; - /* value should be the format like "V2.00-11" */ - if (wi_read_record(sc, (struct wi_ltv_gen *)&sver) == 0 && - *(p = (char *)sver.wi_str) >= 'A' && - p[2] == '.' && p[5] == '-' && p[8] == '\0') { - sc->sc_firmware_type = WI_SYMBOL; - sc->sc_sta_firmware_ver = (p[1] - '0') * 10000 + - (p[3] - '0') * 1000 + (p[4] - '0') * 100 + - (p[6] - '0') * 10 + (p[7] - '0'); - } + ifmedia_init(&sc->sc_media, 0, wi_media_change, wi_media_status); + if_printf(ifp, "supported rates: "); +#define ADD(s, o) ifmedia_add(&sc->sc_media, \ + IFM_MAKEWORD(IFM_IEEE80211, (s), (o), 0), 0, NULL) + ADD(IFM_AUTO, 0); + if (ic->ic_flags & IEEE80211_F_HASHOSTAP) + ADD(IFM_AUTO, IFM_IEEE80211_HOSTAP); + if (ic->ic_flags & IEEE80211_F_HASIBSS) + ADD(IFM_AUTO, IFM_IEEE80211_ADHOC); + ADD(IFM_AUTO, IFM_IEEE80211_ADHOC | IFM_FLAG0); + for (i = 0; i < nrate; i++) { + r = ic->ic_sup_rates[i]; + mword = ieee80211_rate2media(r, IEEE80211_T_DS); + if (mword == 0) + continue; + printf("%s%d%sMbps", (i != 0 ? " " : ""), + (r & IEEE80211_RATE_VAL) / 2, ((r & 0x1) != 0 ? ".5" : "")); + ADD(mword, 0); + if (ic->ic_flags & IEEE80211_F_HASHOSTAP) + ADD(mword, IFM_IEEE80211_HOSTAP); + if (ic->ic_flags & IEEE80211_F_HASIBSS) + ADD(mword, IFM_IEEE80211_ADHOC); + ADD(mword, IFM_IEEE80211_ADHOC | IFM_FLAG0); } printf("\n"); - device_printf(sc->dev, "%s Firmware: ", - sc->sc_firmware_type == WI_LUCENT ? "Lucent" : - (sc->sc_firmware_type == WI_SYMBOL ? "Symbol" : "Intersil")); + ifmedia_set(&sc->sc_media, IFM_MAKEWORD(IFM_IEEE80211, IFM_AUTO, 0, 0)); +#undef ADD /* - * The primary firmware is only valid on Prism based chipsets - * (INTERSIL or SYMBOL). + * Call MI attach routine. */ - if (sc->sc_firmware_type != WI_LUCENT) - printf("Primary %u.%02u.%02u, ", sc->sc_pri_firmware_ver / 10000, - (sc->sc_pri_firmware_ver % 10000) / 100, - sc->sc_pri_firmware_ver % 100); - printf("Station %u.%02u.%02u\n", - sc->sc_sta_firmware_ver / 10000, (sc->sc_sta_firmware_ver % 10000) / 100, - sc->sc_sta_firmware_ver % 100); - return; + ieee80211_ifattach(ifp); + + return (0); } -static void -wi_rxeof(sc) - struct wi_softc *sc; +int +wi_detach(device_t dev) { - struct ifnet *ifp; - struct ether_header *eh; - struct mbuf *m; - int id; + struct wi_softc *sc = device_get_softc(dev); + struct ifnet *ifp = &sc->sc_ic.ic_if; + WI_LOCK_DECL(); - ifp = &sc->arpcom.ac_if; + WI_LOCK(sc); - id = CSR_READ_2(sc, WI_RX_FID); + /* check if device was removed */ + sc->wi_gone = !bus_child_present(dev); - /* - * if we have the procframe flag set, disregard all this and just - * read the data from the device. - */ - if (sc->wi_procframe || sc->wi_debug.wi_monitor) { - struct wi_frame *rx_frame; - int datlen, hdrlen; - - /* first allocate mbuf for packet storage */ - MGETHDR(m, M_DONTWAIT, MT_DATA); - if (m == NULL) { - ifp->if_ierrors++; - return; - } - MCLGET(m, M_DONTWAIT); - if (!(m->m_flags & M_EXT)) { - m_freem(m); - ifp->if_ierrors++; - return; - } - - m->m_pkthdr.rcvif = ifp; - - /* now read wi_frame first so we know how much data to read */ - if (wi_read_data(sc, id, 0, mtod(m, caddr_t), - sizeof(struct wi_frame))) { - m_freem(m); - ifp->if_ierrors++; - return; - } - - rx_frame = mtod(m, struct wi_frame *); - - switch ((rx_frame->wi_status & WI_STAT_MAC_PORT) >> 8) { - case 7: - switch (rx_frame->wi_frame_ctl & WI_FCTL_FTYPE) { - case WI_FTYPE_DATA: - hdrlen = WI_DATA_HDRLEN; - datlen = rx_frame->wi_dat_len + WI_FCS_LEN; - break; - case WI_FTYPE_MGMT: - hdrlen = WI_MGMT_HDRLEN; - datlen = rx_frame->wi_dat_len + WI_FCS_LEN; - break; - case WI_FTYPE_CTL: - /* - * prism2 cards don't pass control packets - * down properly or consistently, so we'll only - * pass down the header. - */ - hdrlen = WI_CTL_HDRLEN; - datlen = 0; - break; - default: - device_printf(sc->dev, "received packet of " - "unknown type on port 7\n"); - m_freem(m); - ifp->if_ierrors++; - return; - } - break; - case 0: - hdrlen = WI_DATA_HDRLEN; - datlen = rx_frame->wi_dat_len + WI_FCS_LEN; - break; - default: - device_printf(sc->dev, "received packet on invalid " - "port (wi_status=0x%x)\n", rx_frame->wi_status); - m_freem(m); - ifp->if_ierrors++; - return; - } - - if ((hdrlen + datlen + 2) > MCLBYTES) { - device_printf(sc->dev, "oversized packet received " - "(wi_dat_len=%d, wi_status=0x%x)\n", - datlen, rx_frame->wi_status); - m_freem(m); - ifp->if_ierrors++; - return; - } - - if (wi_read_data(sc, id, hdrlen, mtod(m, caddr_t) + hdrlen, - datlen + 2)) { - m_freem(m); - ifp->if_ierrors++; - return; - } - - m->m_pkthdr.len = m->m_len = hdrlen + datlen; - - ifp->if_ipackets++; - - /* Handle BPF listeners. */ - BPF_MTAP(ifp, m); - - m_freem(m); - } else { - struct wi_frame rx_frame; - - /* First read in the frame header */ - if (wi_read_data(sc, id, 0, (caddr_t)&rx_frame, - sizeof(rx_frame))) { - ifp->if_ierrors++; - return; - } + wi_stop(ifp, 0); - if (rx_frame.wi_status & WI_STAT_ERRSTAT) { - ifp->if_ierrors++; - return; - } - - MGETHDR(m, M_DONTWAIT, MT_DATA); - if (m == NULL) { - ifp->if_ierrors++; - return; - } - MCLGET(m, M_DONTWAIT); - if (!(m->m_flags & M_EXT)) { - m_freem(m); - ifp->if_ierrors++; - return; - } - - eh = mtod(m, struct ether_header *); - m->m_pkthdr.rcvif = ifp; - - if (rx_frame.wi_status == WI_STAT_MGMT && - sc->wi_ptype == WI_PORTTYPE_HOSTAP) { - if ((WI_802_11_OFFSET_RAW + rx_frame.wi_dat_len + 2) > - MCLBYTES) { - device_printf(sc->dev, "oversized mgmt packet " - "received in hostap mode " - "(wi_dat_len=%d, wi_status=0x%x)\n", - rx_frame.wi_dat_len, rx_frame.wi_status); - m_freem(m); - ifp->if_ierrors++; - return; - } - - /* Put the whole header in there. */ - bcopy(&rx_frame, mtod(m, void *), - sizeof(struct wi_frame)); - if (wi_read_data(sc, id, WI_802_11_OFFSET_RAW, - mtod(m, caddr_t) + WI_802_11_OFFSET_RAW, - rx_frame.wi_dat_len + 2)) { - m_freem(m); - ifp->if_ierrors++; - return; - } - m->m_pkthdr.len = m->m_len = - WI_802_11_OFFSET_RAW + rx_frame.wi_dat_len; - /* XXX: consider giving packet to bhp? */ - wihap_mgmt_input(sc, &rx_frame, m); - return; - } + /* Delete all remaining media. */ + ifmedia_removeall(&sc->sc_media); - if (rx_frame.wi_status == WI_STAT_1042 || - rx_frame.wi_status == WI_STAT_TUNNEL || - rx_frame.wi_status == WI_STAT_WMP_MSG) { - if((rx_frame.wi_dat_len + WI_SNAPHDR_LEN) > MCLBYTES) { - device_printf(sc->dev, - "oversized packet received " - "(wi_dat_len=%d, wi_status=0x%x)\n", - rx_frame.wi_dat_len, rx_frame.wi_status); - m_freem(m); - ifp->if_ierrors++; - return; - } - m->m_pkthdr.len = m->m_len = - rx_frame.wi_dat_len + WI_SNAPHDR_LEN; + ieee80211_ifdetach(ifp); + bus_teardown_intr(dev, sc->irq, sc->wi_intrhand); + wi_free(dev); -#if 0 - bcopy((char *)&rx_frame.wi_addr1, - (char *)&eh->ether_dhost, ETHER_ADDR_LEN); - if (sc->wi_ptype == WI_PORTTYPE_ADHOC) { - bcopy((char *)&rx_frame.wi_addr2, - (char *)&eh->ether_shost, ETHER_ADDR_LEN); - } else { - bcopy((char *)&rx_frame.wi_addr3, - (char *)&eh->ether_shost, ETHER_ADDR_LEN); - } -#else - bcopy((char *)&rx_frame.wi_dst_addr, - (char *)&eh->ether_dhost, ETHER_ADDR_LEN); - bcopy((char *)&rx_frame.wi_src_addr, - (char *)&eh->ether_shost, ETHER_ADDR_LEN); + WI_UNLOCK(sc); +#if __FreeBSD_version >= 500000 + mtx_destroy(&sc->sc_mtx); #endif + return (0); +} - bcopy((char *)&rx_frame.wi_type, - (char *)&eh->ether_type, ETHER_TYPE_LEN); - - if (wi_read_data(sc, id, WI_802_11_OFFSET, - mtod(m, caddr_t) + sizeof(struct ether_header), - m->m_len + 2)) { - m_freem(m); - ifp->if_ierrors++; - return; - } - } else { - if((rx_frame.wi_dat_len + - sizeof(struct ether_header)) > MCLBYTES) { - device_printf(sc->dev, - "oversized packet received " - "(wi_dat_len=%d, wi_status=0x%x)\n", - rx_frame.wi_dat_len, rx_frame.wi_status); - m_freem(m); - ifp->if_ierrors++; - return; - } - m->m_pkthdr.len = m->m_len = - rx_frame.wi_dat_len + sizeof(struct ether_header); - - if (wi_read_data(sc, id, WI_802_3_OFFSET, - mtod(m, caddr_t), m->m_len + 2)) { - m_freem(m); - ifp->if_ierrors++; - return; - } - } +#ifdef __NetBSD__ +int +wi_activate(struct device *self, enum devact act) +{ + struct wi_softc *sc = (struct wi_softc *)self; + int rv = 0, s; - ifp->if_ipackets++; + s = splnet(); + switch (act) { + case DVACT_ACTIVATE: + rv = EOPNOTSUPP; + break; - if (sc->wi_ptype == WI_PORTTYPE_HOSTAP) { - /* - * Give host AP code first crack at data - * packets. If it decides to handle it (or - * drop it), it will return a non-zero. - * Otherwise, it is destined for this host. - */ - if (wihap_data_input(sc, &rx_frame, m)) - return; - } - /* Receive packet. */ -#ifdef WICACHE - wi_cache_store(sc, eh, m, rx_frame.wi_q_info); -#endif - (*ifp->if_input)(ifp, m); + case DVACT_DEACTIVATE: + if_deactivate(&sc->sc_ic.ic_if); + break; } + splx(s); + return rv; } -static void -wi_txeof(sc, status) - struct wi_softc *sc; - int status; +void +wi_power(struct wi_softc *sc, int why) { - struct ifnet *ifp; - - ifp = &sc->arpcom.ac_if; - - ifp->if_timer = 0; - ifp->if_flags &= ~IFF_OACTIVE; + struct ifnet *ifp = &sc->sc_ic.ic_if; + int s; - if (status & WI_EV_TX_EXC) - ifp->if_oerrors++; - else - ifp->if_opackets++; - - return; + s = splnet(); + switch (why) { + case PWR_SUSPEND: + case PWR_STANDBY: + wi_stop(ifp, 1); + break; + case PWR_RESUME: + if (ifp->if_flags & IFF_UP) { + wi_init(ifp); + (void)wi_intr(sc); + } + break; + case PWR_SOFTSUSPEND: + case PWR_SOFTSTANDBY: + case PWR_SOFTRESUME: + break; + } + splx(s); } +#endif /* __NetBSD__ */ -static void -wi_inquire(xsc) - void *xsc; +void +wi_shutdown(device_t dev) { - struct wi_softc *sc; - struct ifnet *ifp; - int s; - - sc = xsc; - ifp = &sc->arpcom.ac_if; - - sc->wi_stat_ch = timeout(wi_inquire, sc, hz * 60); - - /* Don't do this while we're transmitting */ - if (ifp->if_flags & IFF_OACTIVE) - return; + struct wi_softc *sc = device_get_softc(dev); - WI_LOCK(sc, s); - wi_cmd(sc, WI_CMD_INQUIRE, WI_INFO_COUNTERS, 0, 0); - WI_UNLOCK(sc, s); - - return; + wi_stop(&sc->sc_if, 1); } -static void -wi_update_stats(sc) - struct wi_softc *sc; +void +wi_intr(void *arg) { - struct wi_ltv_gen gen; - u_int16_t id; - struct ifnet *ifp; - u_int32_t *ptr; - int len, i; - u_int16_t t; + int i; + struct wi_softc *sc = arg; + struct ifnet *ifp = &sc->sc_ic.ic_if; + u_int16_t status, raw_status, last_status; + WI_LOCK_DECL(); - ifp = &sc->arpcom.ac_if; + WI_LOCK(sc); - id = CSR_READ_2(sc, WI_INFO_FID); + if (sc->wi_gone || (ifp->if_flags & IFF_UP) == 0) { + CSR_WRITE_2(sc, WI_EVENT_ACK, ~0); + CSR_WRITE_2(sc, WI_INT_EN, 0); + WI_UNLOCK(sc); + return; + } + + /* maximum 10 loops per interrupt */ + last_status = 0; + for (i = 0; i < 10; i++) { + /* + * Only believe a status bit when we enter wi_intr, or when + * the bit was "off" the last time through the loop. This is + * my strategy to avoid racing the hardware/firmware if I + * can re-read the event status register more quickly than + * it is updated. + */ + raw_status = CSR_READ_2(sc, WI_EVENT_STAT); + status = raw_status & ~last_status; + if ((status & WI_INTRS) == 0) + break; + last_status = raw_status; - wi_read_data(sc, id, 0, (char *)&gen, 4); + if (status & WI_EV_RX) + wi_rx_intr(sc); - /* - * if we just got our scan results, copy it over into the scan buffer - * so we can return it to anyone that asks for it. (add a little - * compatibility with the prism2 scanning mechanism) - */ - if (gen.wi_type == WI_INFO_SCAN_RESULTS) - { - sc->wi_scanbuf_len = gen.wi_len; - wi_read_data(sc, id, 4, (char *)sc->wi_scanbuf, - sc->wi_scanbuf_len * 2); + if (status & WI_EV_ALLOC) + wi_tx_intr(sc); - return; - } - else if (gen.wi_type != WI_INFO_COUNTERS) - return; + if (status & WI_EV_TX_EXC) + wi_tx_ex_intr(sc); - len = (gen.wi_len - 1 < sizeof(sc->wi_stats) / 4) ? - gen.wi_len - 1 : sizeof(sc->wi_stats) / 4; - ptr = (u_int32_t *)&sc->wi_stats; + if (status & WI_EV_INFO) + wi_info_intr(sc); - for (i = 0; i < len - 1; i++) { - t = CSR_READ_2(sc, WI_DATA1); -#ifdef WI_HERMES_STATS_WAR - if (t > 0xF000) - t = ~t & 0xFFFF; -#endif - ptr[i] += t; + if ((ifp->if_flags & IFF_OACTIVE) == 0 && + (sc->sc_flags & WI_FLAGS_OUTRANGE) == 0 && + _IF_QLEN(&ifp->if_snd) != 0) + wi_start(ifp); } - ifp->if_collisions = sc->wi_stats.wi_tx_single_retries + - sc->wi_stats.wi_tx_multi_retries + - sc->wi_stats.wi_tx_retry_limit; + WI_UNLOCK(sc); return; } -static void -wi_intr(xsc) - void *xsc; +void +wi_init(void *arg) { - struct wi_softc *sc = xsc; - struct ifnet *ifp; - u_int16_t status; - int s; - - WI_LOCK(sc, s); + struct wi_softc *sc = arg; + struct ifnet *ifp = &sc->sc_if; + struct ieee80211com *ic = &sc->sc_ic; + struct wi_joinreq join; + int i; + int error = 0, wasenabled; + struct ifaddr *ifa; + struct sockaddr_dl *sdl; + WI_LOCK_DECL(); - ifp = &sc->arpcom.ac_if; + WI_LOCK(sc); - if (sc->wi_gone || !(ifp->if_flags & IFF_UP)) { - CSR_WRITE_2(sc, WI_EVENT_ACK, 0xFFFF); - CSR_WRITE_2(sc, WI_INT_EN, 0); - WI_UNLOCK(sc, s); + if (sc->wi_gone) { + WI_UNLOCK(sc); return; } - /* Disable interrupts. */ - CSR_WRITE_2(sc, WI_INT_EN, 0); + wasenabled = sc->sc_enabled; + if (!sc->sc_enabled) { + sc->sc_enabled = 1; + } else + wi_stop(ifp, 0); - status = CSR_READ_2(sc, WI_EVENT_STAT); - CSR_WRITE_2(sc, WI_EVENT_ACK, ~WI_INTRS); + /* Symbol firmware cannot be initialized more than once */ + if (sc->sc_firmware_type != WI_SYMBOL || !wasenabled) + wi_reset(sc); - if (status & WI_EV_RX) { - wi_rxeof(sc); - CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_RX); + /* common 802.11 configuration */ + ic->ic_flags &= ~IEEE80211_F_IBSSON; + sc->sc_flags &= ~WI_FLAGS_OUTRANGE; + switch (ic->ic_opmode) { + case IEEE80211_M_STA: + wi_write_val(sc, WI_RID_PORTTYPE, WI_PORTTYPE_BSS); + break; + case IEEE80211_M_IBSS: + wi_write_val(sc, WI_RID_PORTTYPE, sc->sc_ibss_port); + ic->ic_flags |= IEEE80211_F_IBSSON; + break; + case IEEE80211_M_AHDEMO: + wi_write_val(sc, WI_RID_PORTTYPE, WI_PORTTYPE_ADHOC); + break; + case IEEE80211_M_HOSTAP: + wi_write_val(sc, WI_RID_PORTTYPE, WI_PORTTYPE_HOSTAP); + break; } - if (status & WI_EV_TX) { - wi_txeof(sc, status); - CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_TX); + /* Intersil interprets this RID as joining ESS even in IBSS mode */ + if (sc->sc_firmware_type == WI_LUCENT && + (ic->ic_flags & IEEE80211_F_IBSSON) && ic->ic_des_esslen > 0) + wi_write_val(sc, WI_RID_CREATE_IBSS, 1); + else + wi_write_val(sc, WI_RID_CREATE_IBSS, 0); + wi_write_val(sc, WI_RID_MAX_SLEEP, ic->ic_lintval); + wi_write_ssid(sc, WI_RID_DESIRED_SSID, ic->ic_des_essid, + ic->ic_des_esslen); + wi_write_val(sc, WI_RID_OWN_CHNL, ic->ic_ibss_chan); + wi_write_ssid(sc, WI_RID_OWN_SSID, ic->ic_des_essid, ic->ic_des_esslen); + + ifa = ifaddr_byindex(ifp->if_index); + sdl = (struct sockaddr_dl *) ifa->ifa_addr; + IEEE80211_ADDR_COPY(ic->ic_myaddr, LLADDR(sdl)); + wi_write_rid(sc, WI_RID_MAC_NODE, ic->ic_myaddr, IEEE80211_ADDR_LEN); + + wi_write_val(sc, WI_RID_PM_ENABLED, + (ic->ic_flags & IEEE80211_F_PMGTON) ? 1 : 0); + + /* not yet common 802.11 configuration */ + wi_write_val(sc, WI_RID_MAX_DATALEN, sc->sc_max_datalen); + wi_write_val(sc, WI_RID_RTS_THRESH, sc->sc_rts_thresh); + if (sc->sc_flags & WI_FLAGS_HAS_FRAGTHR) + wi_write_val(sc, WI_RID_FRAG_THRESH, sc->sc_frag_thresh); + + /* driver specific 802.11 configuration */ + if (sc->sc_flags & WI_FLAGS_HAS_SYSSCALE) + wi_write_val(sc, WI_RID_SYSTEM_SCALE, sc->sc_system_scale); + if (sc->sc_flags & WI_FLAGS_HAS_ROAMING) + wi_write_val(sc, WI_RID_ROAMING_MODE, sc->sc_roaming_mode); + if (sc->sc_flags & WI_FLAGS_HAS_MOR) + wi_write_val(sc, WI_RID_MICROWAVE_OVEN, sc->sc_microwave_oven); + wi_write_txrate(sc); + wi_write_ssid(sc, WI_RID_NODENAME, sc->sc_nodename, sc->sc_nodelen); + + if (ic->ic_opmode == IEEE80211_M_HOSTAP && + sc->sc_firmware_type == WI_INTERSIL) { + wi_write_val(sc, WI_RID_OWN_BEACON_INT, ic->ic_lintval); + wi_write_val(sc, WI_RID_BASIC_RATE, 0x03); /* 1, 2 */ + wi_write_val(sc, WI_RID_SUPPORT_RATE, 0x0f); /* 1, 2, 5.5, 11 */ + wi_write_val(sc, WI_RID_DTIM_PERIOD, 1); } - if (status & WI_EV_ALLOC) { - int id; - - id = CSR_READ_2(sc, WI_ALLOC_FID); - CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_ALLOC); - if (id == sc->wi_tx_data_id) - wi_txeof(sc, status); + /* + * Initialize promisc mode. + * Being in the Host-AP mode causes a great + * deal of pain if primisc mode is set. + * Therefore we avoid confusing the firmware + * and always reset promisc mode in Host-AP + * mode. Host-AP sees all the packets anyway. + */ + if (ic->ic_opmode != IEEE80211_M_HOSTAP && + (ifp->if_flags & IFF_PROMISC) != 0) { + wi_write_val(sc, WI_RID_PROMISC, 1); + } else { + wi_write_val(sc, WI_RID_PROMISC, 0); } - if (status & WI_EV_INFO) { - wi_update_stats(sc); - CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_INFO); - } + /* Configure WEP. */ + if (ic->ic_flags & IEEE80211_F_HASWEP) + wi_write_wep(sc); - if (status & WI_EV_TX_EXC) { - wi_txeof(sc, status); - CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_TX_EXC); + /* Set multicast filter. */ + wi_write_multi(sc); + + if (sc->sc_firmware_type != WI_SYMBOL || !wasenabled) { + sc->sc_buflen = IEEE80211_MAX_LEN + sizeof(struct wi_frame); + if (sc->sc_firmware_type == WI_SYMBOL) + sc->sc_buflen = 1585; /* XXX */ + for (i = 0; i < WI_NTXBUF; i++) { + error = wi_alloc_fid(sc, sc->sc_buflen, + &sc->sc_txd[i].d_fid); + if (error) { + device_printf(sc->sc_dev, + "tx buffer allocation failed (error %u)\n", + error); + goto out; + } + sc->sc_txd[i].d_len = 0; + } } + sc->sc_txcur = sc->sc_txnext = 0; - if (status & WI_EV_INFO_DROP) { - CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_INFO_DROP); - } + /* Enable desired port */ + wi_cmd(sc, WI_CMD_ENABLE | sc->sc_portnum, 0, 0, 0); + + ifp->if_flags |= IFF_RUNNING; + ifp->if_flags &= ~IFF_OACTIVE; + if (ic->ic_opmode == IEEE80211_M_AHDEMO || + ic->ic_opmode == IEEE80211_M_HOSTAP) + wi_newstate(sc, IEEE80211_S_RUN); - /* Re-enable interrupts. */ + /* Enable interrupts */ CSR_WRITE_2(sc, WI_INT_EN, WI_INTRS); - if (ifp->if_snd.ifq_head != NULL) { - wi_start(ifp); + if (!wasenabled && + ic->ic_opmode == IEEE80211_M_HOSTAP && + sc->sc_firmware_type == WI_INTERSIL) { + /* XXX: some card need to be re-enabled for hostap */ + wi_cmd(sc, WI_CMD_DISABLE | WI_PORT0, 0, 0, 0); + wi_cmd(sc, WI_CMD_ENABLE | WI_PORT0, 0, 0, 0); } - WI_UNLOCK(sc, s); + if (ic->ic_opmode == IEEE80211_M_STA && + ((ic->ic_flags & IEEE80211_F_DESBSSID) || + ic->ic_des_chan != IEEE80211_CHAN_ANY)) { + memset(&join, 0, sizeof(join)); + if (ic->ic_flags & IEEE80211_F_DESBSSID) + IEEE80211_ADDR_COPY(&join.wi_bssid, ic->ic_des_bssid); + if (ic->ic_des_chan != IEEE80211_CHAN_ANY) + join.wi_chan = htole16(ic->ic_des_chan); + /* Lucent firmware does not support the JOIN RID. */ + if (sc->sc_firmware_type != WI_LUCENT) + wi_write_rid(sc, WI_RID_JOIN_REQ, &join, sizeof(join)); + } + WI_UNLOCK(sc); + return; +out: + if (error) { + if_printf(ifp, "interface not running\n"); + wi_stop(ifp, 0); + } + DPRINTF(("wi_init: return %d\n", error)); return; } -static int -wi_cmd(sc, cmd, val0, val1, val2) - struct wi_softc *sc; - int cmd; - int val0; - int val1; - int val2; +void +wi_stop(struct ifnet *ifp, int disable) { - int i, s = 0; - static volatile int count = 0; - - if (count > 1) - panic("Hey partner, hold on there!"); - count++; + struct wi_softc *sc = ifp->if_softc; + WI_LOCK_DECL(); - /* wait for the busy bit to clear */ - for (i = 500; i > 0; i--) { /* 5s */ - if (!(CSR_READ_2(sc, WI_COMMAND) & WI_CMD_BUSY)) { - break; + WI_LOCK(sc); + + ieee80211_new_state(ifp, IEEE80211_S_INIT, -1); + if (sc->sc_enabled && !sc->wi_gone) { + CSR_WRITE_2(sc, WI_INT_EN, 0); + wi_cmd(sc, WI_CMD_DISABLE | sc->sc_portnum, 0, 0, 0); + if (disable) { +#ifdef __NetBSD__ + if (sc->sc_disable) + (*sc->sc_disable)(sc); +#endif + sc->sc_enabled = 0; } - DELAY(10*1000); /* 10 m sec */ } - if (i == 0) { - device_printf(sc->dev, "wi_cmd: busy bit won't clear.\n" ); - count--; - return(ETIMEDOUT); + + sc->sc_tx_timer = 0; + sc->sc_scan_timer = 0; + sc->sc_syn_timer = 0; + sc->sc_false_syns = 0; + sc->sc_naps = 0; + ifp->if_flags &= ~(IFF_OACTIVE | IFF_RUNNING); + ifp->if_timer = 0; + + WI_UNLOCK(sc); +} + +static void +wi_start(struct ifnet *ifp) +{ + struct wi_softc *sc = ifp->if_softc; + struct ieee80211com *ic = &sc->sc_ic; + struct ieee80211_node *ni = NULL; + struct ieee80211_frame *wh; + struct mbuf *m0; + struct wi_frame frmhdr; + int cur, fid, off; + WI_LOCK_DECL(); + + WI_LOCK(sc); + + if (sc->wi_gone) { + WI_UNLOCK(sc); + return; } + if (sc->sc_flags & WI_FLAGS_OUTRANGE) { + WI_UNLOCK(sc); + return; + } + KASSERT((ifp->if_flags & IFF_OACTIVE) == 0, + ("wi_start: if_flags %x\n", ifp->if_flags)); - CSR_WRITE_2(sc, WI_PARAM0, val0); - CSR_WRITE_2(sc, WI_PARAM1, val1); - CSR_WRITE_2(sc, WI_PARAM2, val2); - CSR_WRITE_2(sc, WI_COMMAND, cmd); + memset(&frmhdr, 0, sizeof(frmhdr)); + cur = sc->sc_txnext; + for (;;) { + IF_POLL(&ic->ic_mgtq, m0); + if (m0 != NULL) { + if (sc->sc_txd[cur].d_len != 0) { + ifp->if_flags |= IFF_OACTIVE; + break; + } + IF_DEQUEUE(&ic->ic_mgtq, m0); + m_copydata(m0, 4, ETHER_ADDR_LEN * 2, + (caddr_t)&frmhdr.wi_ehdr); + frmhdr.wi_ehdr.ether_type = 0; + wh = mtod(m0, struct ieee80211_frame *); + } else { + if (ic->ic_state != IEEE80211_S_RUN) + break; + IFQ_POLL(&ifp->if_snd, m0); + if (m0 == NULL) + break; + if (sc->sc_txd[cur].d_len != 0) { + ifp->if_flags |= IFF_OACTIVE; + break; + } + IFQ_DEQUEUE(&ifp->if_snd, m0); + ifp->if_opackets++; + m_copydata(m0, 0, ETHER_HDR_LEN, + (caddr_t)&frmhdr.wi_ehdr); +#if NBPFILTER > 0 + BPF_MTAP(ifp, m0); +#endif - for (i = 0; i < WI_TIMEOUT; i++) { - /* - * Wait for 'command complete' bit to be - * set in the event status register. - */ - s = CSR_READ_2(sc, WI_EVENT_STAT); - if (s & WI_EV_CMD) { - /* Ack the event and read result code. */ - s = CSR_READ_2(sc, WI_STATUS); - CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_CMD); -#ifdef foo - if ((s & WI_CMD_CODE_MASK) != (cmd & WI_CMD_CODE_MASK)) - return(EIO); + if ((m0 = ieee80211_encap(ifp, m0)) == NULL) { + ifp->if_oerrors++; + continue; + } + wh = mtod(m0, struct ieee80211_frame *); + if (ic->ic_opmode == IEEE80211_M_HOSTAP && + !IEEE80211_IS_MULTICAST(wh->i_addr1) && + (wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) == + IEEE80211_FC0_TYPE_DATA && + ((ni = ieee80211_find_node(ic, wh->i_addr1)) == + NULL || ni->ni_associd == 0)) { + m_freem(m0); + ifp->if_oerrors++; + continue; + } + if (ic->ic_flags & IEEE80211_F_WEPON) + wh->i_fc[1] |= IEEE80211_FC1_WEP; + + } +#if NBPFILTER > 0 + if (ic->ic_rawbpf) + bpf_mtap(ic->ic_rawbpf, m0); #endif - if (s & WI_STAT_CMD_RESULT) { - count--; - return(EIO); + frmhdr.wi_tx_ctl = htole16(WI_ENC_TX_802_11|WI_TXCNTL_TX_EX); + if (ic->ic_opmode == IEEE80211_M_HOSTAP && + (wh->i_fc[1] & IEEE80211_FC1_WEP)) { + if ((m0 = ieee80211_wep_crypt(ifp, m0, 1)) == NULL) { + ifp->if_oerrors++; + continue; } - break; + frmhdr.wi_tx_ctl |= htole16(WI_TXCNTL_NOCRYPT); } - DELAY(WI_DELAY); + m_copydata(m0, 0, sizeof(struct ieee80211_frame), + (caddr_t)&frmhdr.wi_whdr); + m_adj(m0, sizeof(struct ieee80211_frame)); + frmhdr.wi_dat_len = htole16(m0->m_pkthdr.len); +#if NBPFILTER > 0 + if (sc->sc_drvbpf) { + struct mbuf *mb; + + MGETHDR(mb, M_DONTWAIT, m0->m_type); + if (mb != NULL) { + (void) m_dup_pkthdr(mb, m0, M_DONTWAIT); + mb->m_next = m0; + mb->m_data = (caddr_t)&frmhdr; + mb->m_len = sizeof(frmhdr); + mb->m_pkthdr.len += mb->m_len; + bpf_mtap(sc->sc_drvbpf, mb); + m_free(mb); + } + } +#endif + if (IFF_DUMPPKTS(ifp)) + wi_dump_pkt(&frmhdr, ni, -1); + fid = sc->sc_txd[cur].d_fid; + off = sizeof(frmhdr); + if (wi_write_bap(sc, fid, 0, &frmhdr, sizeof(frmhdr)) != 0 || + wi_mwrite_bap(sc, fid, off, m0, m0->m_pkthdr.len) != 0) { + ifp->if_oerrors++; + m_freem(m0); + continue; + } + m_freem(m0); + sc->sc_txd[cur].d_len = off; + if (sc->sc_txcur == cur) { + if (wi_cmd(sc, WI_CMD_TX | WI_RECLAIM, fid, 0, 0)) { + if_printf(ifp, "xmit failed\n"); + sc->sc_txd[cur].d_len = 0; + continue; + } + sc->sc_tx_timer = 5; + ifp->if_timer = 1; + } + sc->sc_txnext = cur = (cur + 1) % WI_NTXBUF; } - count--; - if (i == WI_TIMEOUT) { - device_printf(sc->dev, - "timeout in wi_cmd 0x%04x; event status 0x%04x\n", cmd, s); - return(ETIMEDOUT); - } - return(0); + WI_UNLOCK(sc); } -static void -wi_reset(sc) - struct wi_softc *sc; +static int +wi_reset(struct wi_softc *sc) { -#define WI_INIT_TRIES 3 - int i; - int tries; - - /* Symbol firmware cannot be initialized more than once */ - if (sc->sc_firmware_type == WI_SYMBOL && sc->sc_enabled) - return; - if (sc->sc_firmware_type == WI_SYMBOL) - tries = 1; - else - tries = WI_INIT_TRIES; +#define WI_INIT_TRIES 5 + int i, error; - for (i = 0; i < tries; i++) { - if (wi_cmd(sc, WI_CMD_INI, 0, 0, 0) == 0) + for (i = 0; i < WI_INIT_TRIES; i++) { + if ((error = wi_cmd(sc, WI_CMD_INI, 0, 0, 0)) == 0) break; DELAY(WI_DELAY * 1000); } - sc->sc_enabled = 1; - if (i == tries) { - device_printf(sc->dev, "init failed\n"); - return; + if (error) { + device_printf(sc->sc_dev, "init failed\n"); + return error; } CSR_WRITE_2(sc, WI_INT_EN, 0); - CSR_WRITE_2(sc, WI_EVENT_ACK, 0xFFFF); + CSR_WRITE_2(sc, WI_EVENT_ACK, ~0); /* Calibrate timer. */ - WI_SETVAL(WI_RID_TICK_TIME, 8); - - return; + wi_write_val(sc, WI_RID_TICK_TIME, 0); + return 0; +#undef WI_INIT_TRIES } -/* - * Read an LTV record from the NIC. - */ -static int -wi_read_record(sc, ltv) - struct wi_softc *sc; - struct wi_ltv_gen *ltv; +static void +wi_watchdog(struct ifnet *ifp) { - u_int16_t *ptr; - int i, len, code; - struct wi_ltv_gen *oltv, p2ltv; + struct wi_softc *sc = ifp->if_softc; - oltv = ltv; - if (sc->sc_firmware_type != WI_LUCENT) { - switch (ltv->wi_type) { - case WI_RID_ENCRYPTION: - p2ltv.wi_type = WI_RID_P2_ENCRYPTION; - p2ltv.wi_len = 2; - ltv = &p2ltv; - break; - case WI_RID_TX_CRYPT_KEY: - p2ltv.wi_type = WI_RID_P2_TX_CRYPT_KEY; - p2ltv.wi_len = 2; - ltv = &p2ltv; - break; - case WI_RID_ROAMING_MODE: - if (sc->sc_firmware_type == WI_INTERSIL) - break; - /* not supported */ - ltv->wi_len = 1; - return 0; - case WI_RID_MICROWAVE_OVEN: - /* not supported */ - ltv->wi_len = 1; - return 0; + ifp->if_timer = 0; + if (!sc->sc_enabled) + return; + + if (sc->sc_tx_timer) { + if (--sc->sc_tx_timer == 0) { + if_printf(ifp, "device timeout\n"); + ifp->if_oerrors++; + wi_init(ifp->if_softc); + return; } + ifp->if_timer = 1; } - /* Tell the NIC to enter record read mode. */ - if (wi_cmd(sc, WI_CMD_ACCESS|WI_ACCESS_READ, ltv->wi_type, 0, 0)) - return(EIO); - - /* Seek to the record. */ - if (wi_seek(sc, ltv->wi_type, 0, WI_BAP1)) - return(EIO); + if (sc->sc_scan_timer) { + if (--sc->sc_scan_timer <= WI_SCAN_WAIT - WI_SCAN_INQWAIT && + sc->sc_firmware_type == WI_INTERSIL) { + DPRINTF(("wi_watchdog: inquire scan\n")); + wi_cmd(sc, WI_CMD_INQUIRE, WI_INFO_SCAN_RESULTS, 0, 0); + } + if (sc->sc_scan_timer) + ifp->if_timer = 1; + } - /* - * Read the length and record type and make sure they - * match what we expect (this verifies that we have enough - * room to hold all of the returned data). - */ - len = CSR_READ_2(sc, WI_DATA1); - if (len > ltv->wi_len) - return(ENOSPC); - code = CSR_READ_2(sc, WI_DATA1); - if (code != ltv->wi_type) - return(EIO); - - ltv->wi_len = len; - ltv->wi_type = code; - - /* Now read the data. */ - ptr = <v->wi_val; - for (i = 0; i < ltv->wi_len - 1; i++) - ptr[i] = CSR_READ_2(sc, WI_DATA1); - - if (ltv->wi_type == WI_RID_PORTTYPE && sc->wi_ptype == WI_PORTTYPE_IBSS - && ltv->wi_val == sc->wi_ibss_port) { - /* - * Convert vendor IBSS port type to WI_PORTTYPE_IBSS. - * Since Lucent uses port type 1 for BSS *and* IBSS we - * have to rely on wi_ptype to distinguish this for us. - */ - ltv->wi_val = htole16(WI_PORTTYPE_IBSS); - } else if (sc->sc_firmware_type != WI_LUCENT) { - switch (oltv->wi_type) { - case WI_RID_TX_RATE: - case WI_RID_CUR_TX_RATE: - switch (ltv->wi_val) { - case 1: oltv->wi_val = 1; break; - case 2: oltv->wi_val = 2; break; - case 3: oltv->wi_val = 6; break; - case 4: oltv->wi_val = 5; break; - case 7: oltv->wi_val = 7; break; - case 8: oltv->wi_val = 11; break; - case 15: oltv->wi_val = 3; break; - default: oltv->wi_val = 0x100 + ltv->wi_val; break; - } - break; - case WI_RID_ENCRYPTION: - oltv->wi_len = 2; - if (ltv->wi_val & 0x01) - oltv->wi_val = 1; - else - oltv->wi_val = 0; - break; - case WI_RID_TX_CRYPT_KEY: - oltv->wi_len = 2; - oltv->wi_val = ltv->wi_val; - break; - case WI_RID_CNFAUTHMODE: - oltv->wi_len = 2; - if (le16toh(ltv->wi_val) & 0x01) - oltv->wi_val = htole16(1); - else if (le16toh(ltv->wi_val) & 0x02) - oltv->wi_val = htole16(2); - break; + if (sc->sc_syn_timer) { + if (--sc->sc_syn_timer == 0) { + DPRINTF2(("wi_watchdog: %d false syns\n", + sc->sc_false_syns)); + sc->sc_false_syns = 0; + ieee80211_new_state(ifp, IEEE80211_S_RUN, -1); + sc->sc_syn_timer = 5; } + ifp->if_timer = 1; } - return(0); + /* TODO: rate control */ + ieee80211_watchdog(ifp); } -/* - * Same as read, except we inject data instead of reading it. - */ static int -wi_write_record(sc, ltv) - struct wi_softc *sc; - struct wi_ltv_gen *ltv; +wi_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) { - uint16_t *ptr; - uint16_t val; - int i; - struct wi_ltv_gen p2ltv; - - if (ltv->wi_type == WI_RID_PORTTYPE && - le16toh(ltv->wi_val) == WI_PORTTYPE_IBSS) { - /* Convert WI_PORTTYPE_IBSS to vendor IBSS port type. */ - p2ltv.wi_type = WI_RID_PORTTYPE; - p2ltv.wi_len = 2; - p2ltv.wi_val = sc->wi_ibss_port; - ltv = &p2ltv; - } else if (sc->sc_firmware_type != WI_LUCENT) { - switch (ltv->wi_type) { - case WI_RID_TX_RATE: - p2ltv.wi_type = WI_RID_TX_RATE; - p2ltv.wi_len = 2; - switch (ltv->wi_val) { - case 1: p2ltv.wi_val = 1; break; - case 2: p2ltv.wi_val = 2; break; - case 3: p2ltv.wi_val = 15; break; - case 5: p2ltv.wi_val = 4; break; - case 6: p2ltv.wi_val = 3; break; - case 7: p2ltv.wi_val = 7; break; - case 11: p2ltv.wi_val = 8; break; - default: return EINVAL; + struct wi_softc *sc = ifp->if_softc; + struct ieee80211com *ic = &sc->sc_ic; + struct ifreq *ifr = (struct ifreq *)data; + struct ieee80211req *ireq; + u_int8_t nodename[IEEE80211_NWID_LEN]; + int error = 0; +#if __FreeBSD_version >= 500000 + struct thread *td = curthread; +#else + struct proc *td = curproc; /* Little white lie */ +#endif + struct wi_req wreq; + WI_LOCK_DECL(); + + WI_LOCK(sc); + + if (sc->wi_gone) { + error = ENODEV; + goto out; + } + + switch (cmd) { + case SIOCSIFFLAGS: + /* + * Can't do promisc and hostap at the same time. If all that's + * changing is the promisc flag, try to short-circuit a call to + * wi_init() by just setting PROMISC in the hardware. + */ + if (ifp->if_flags & IFF_UP) { + if (ic->ic_opmode != IEEE80211_M_HOSTAP && + ifp->if_flags & IFF_RUNNING) { + if (ifp->if_flags & IFF_PROMISC && + !(sc->sc_if_flags & IFF_PROMISC)) { + wi_write_val(sc, WI_RID_PROMISC, 1); + } else if (!(ifp->if_flags & IFF_PROMISC) && + sc->sc_if_flags & IFF_PROMISC) { + wi_write_val(sc, WI_RID_PROMISC, 0); + } else { + wi_init(sc); + } + } else { + wi_init(sc); + } + } else { + if (ifp->if_flags & IFF_RUNNING) { + wi_stop(ifp, 0); } - p2ltv.wi_val = htole16(p2ltv.wi_val); - ltv = &p2ltv; + } + sc->sc_if_flags = ifp->if_flags; + error = 0; + break; + case SIOCSIFMEDIA: + case SIOCGIFMEDIA: + error = ifmedia_ioctl(ifp, ifr, &sc->sc_media, cmd); + break; + case SIOCADDMULTI: + case SIOCDELMULTI: + error = wi_write_multi(sc); + break; + case SIOCGIFGENERIC: + error = wi_get_cfg(ifp, cmd, data); + break; + case SIOCSIFGENERIC: + error = suser(td); + if (error) break; - case WI_RID_ENCRYPTION: - p2ltv.wi_type = WI_RID_P2_ENCRYPTION; - p2ltv.wi_len = 2; - if (ltv->wi_val & htole16(0x01)) { - val = PRIVACY_INVOKED; - /* - * If using shared key WEP we must set the - * EXCLUDE_UNENCRYPTED bit. Symbol cards - * need this bit set even when not using - * shared key. We can't just test for - * IEEE80211_AUTH_SHARED since Symbol cards - * have 2 shared key modes. - */ - if (sc->wi_authtype != IEEE80211_AUTH_OPEN || - sc->sc_firmware_type == WI_SYMBOL) - val |= EXCLUDE_UNENCRYPTED; - /* TX encryption is broken in Host AP mode. */ - if (sc->wi_ptype == WI_PORTTYPE_HOSTAP) - val |= HOST_ENCRYPT; - } else - val = HOST_ENCRYPT | HOST_DECRYPT; - p2ltv.wi_val = htole16(val); - ltv = &p2ltv; + error = wi_set_cfg(ifp, cmd, data); + break; + case SIOCGPRISM2DEBUG: + error = copyin(ifr->ifr_data, &wreq, sizeof(wreq)); + if (error) break; - case WI_RID_TX_CRYPT_KEY: - if (ltv->wi_val > WI_NLTV_KEYS) - return (EINVAL); - p2ltv.wi_type = WI_RID_P2_TX_CRYPT_KEY; - p2ltv.wi_len = 2; - p2ltv.wi_val = ltv->wi_val; - ltv = &p2ltv; + if (!(ifp->if_flags & IFF_RUNNING) || + sc->sc_firmware_type == WI_LUCENT) { + error = EIO; break; - case WI_RID_DEFLT_CRYPT_KEYS: - { - int error; - int keylen; - struct wi_ltv_str ws; - struct wi_ltv_keys *wk = - (struct wi_ltv_keys *)ltv; - - keylen = wk->wi_keys[sc->wi_tx_key].wi_keylen; - - for (i = 0; i < 4; i++) { - bzero(&ws, sizeof(ws)); - ws.wi_len = (keylen > 5) ? 8 : 4; - ws.wi_type = WI_RID_P2_CRYPT_KEY0 + i; - memcpy(ws.wi_str, - &wk->wi_keys[i].wi_keydat, keylen); - error = wi_write_record(sc, - (struct wi_ltv_gen *)&ws); - if (error) - return error; - } - return 0; - } - case WI_RID_CNFAUTHMODE: - p2ltv.wi_type = WI_RID_CNFAUTHMODE; - p2ltv.wi_len = 2; - if (le16toh(ltv->wi_val) == 1) - p2ltv.wi_val = htole16(0x01); - else if (le16toh(ltv->wi_val) == 2) - p2ltv.wi_val = htole16(0x02); - ltv = &p2ltv; + } + error = wi_get_debug(sc, &wreq); + if (error == 0) + error = copyout(&wreq, ifr->ifr_data, sizeof(wreq)); + break; + case SIOCSPRISM2DEBUG: + if ((error = suser(td))) + goto out; + error = copyin(ifr->ifr_data, &wreq, sizeof(wreq)); + if (error) + break; + error = wi_set_debug(sc, &wreq); + break; + case SIOCG80211: + ireq = (struct ieee80211req *) data; + switch (ireq->i_type) { + case IEEE80211_IOC_STATIONNAME: + ireq->i_len = sc->sc_nodelen + 1; + error = copyout(sc->sc_nodename, ireq->i_data, + ireq->i_len); + break; + default: + error = ieee80211_ioctl(ifp, cmd, data); break; - case WI_RID_ROAMING_MODE: - if (sc->sc_firmware_type == WI_INTERSIL) - break; - /* not supported */ - return 0; - case WI_RID_MICROWAVE_OVEN: - /* not supported */ - return 0; } - } else { - /* LUCENT */ - switch (ltv->wi_type) { - case WI_RID_TX_RATE: - switch (ltv->wi_val) { - case 1: ltv->wi_val = 1; break; /* 1Mb/s fixed */ - case 2: ltv->wi_val = 2; break; /* 2Mb/s fixed */ - case 3: ltv->wi_val = 3; break; /* 11Mb/s auto */ - case 5: ltv->wi_val = 4; break; /* 5.5Mb/s fixed */ - case 6: ltv->wi_val = 6; break; /* 2Mb/s auto */ - case 7: ltv->wi_val = 7; break; /* 5.5Mb/s auto */ - case 11: ltv->wi_val = 5; break; /* 11Mb/s fixed */ - default: return EINVAL; + break; + case SIOCS80211: + error = suser(td); + if (error) + break; + ireq = (struct ieee80211req *) data; + switch (ireq->i_type) { + case IEEE80211_IOC_STATIONNAME: + if (ireq->i_val != 0 || + ireq->i_len > IEEE80211_NWID_LEN) { + error = EINVAL; + break; } - case WI_RID_TX_CRYPT_KEY: - if (ltv->wi_val > WI_NLTV_KEYS) - return (EINVAL); + memset(nodename, 0, IEEE80211_NWID_LEN); + error = copyin(ireq->i_data, nodename, ireq->i_len); + if (error) + break; + if (sc->sc_enabled) { + error = wi_write_ssid(sc, WI_RID_NODENAME, + nodename, ireq->i_len); + if (error) + break; + } + memcpy(sc->sc_nodename, nodename, IEEE80211_NWID_LEN); + sc->sc_nodelen = ireq->i_len; + break; + default: + error = ieee80211_ioctl(ifp, cmd, data); break; } + break; + default: + error = ieee80211_ioctl(ifp, cmd, data); + break; } + if (error == ENETRESET) { + if (sc->sc_enabled) + wi_init(ifp->if_softc); /* XXX no error return */ + error = 0; + } +out: + WI_UNLOCK(sc); - if (wi_seek(sc, ltv->wi_type, 0, WI_BAP1)) - return(EIO); - - CSR_WRITE_2(sc, WI_DATA1, ltv->wi_len); - CSR_WRITE_2(sc, WI_DATA1, ltv->wi_type); - - ptr = <v->wi_val; - for (i = 0; i < ltv->wi_len - 1; i++) - CSR_WRITE_2(sc, WI_DATA1, ptr[i]); - - if (wi_cmd(sc, WI_CMD_ACCESS|WI_ACCESS_WRITE, ltv->wi_type, 0, 0)) - return(EIO); - - return(0); + return (error); } static int -wi_seek(sc, id, off, chan) - struct wi_softc *sc; - int id, off, chan; +wi_media_change(struct ifnet *ifp) { - int i; - int selreg, offreg; - int status; - - switch (chan) { - case WI_BAP0: - selreg = WI_SEL0; - offreg = WI_OFF0; - break; - case WI_BAP1: - selreg = WI_SEL1; - offreg = WI_OFF1; - break; - default: - device_printf(sc->dev, "invalid data path: %x\n", chan); - return(EIO); + struct wi_softc *sc = ifp->if_softc; + struct ieee80211com *ic = &sc->sc_ic; + struct ifmedia_entry *ime; + enum ieee80211_opmode newmode; + int i, rate, error = 0; + + ime = sc->sc_media.ifm_cur; + if (IFM_SUBTYPE(ime->ifm_media) == IFM_AUTO) { + i = -1; + } else { + rate = ieee80211_media2rate(ime->ifm_media, IEEE80211_T_DS); + if (rate == 0) + return EINVAL; + for (i = 0; i < IEEE80211_RATE_SIZE; i++) { + if ((ic->ic_sup_rates[i] & IEEE80211_RATE_VAL) == rate) + break; + } + if (i == IEEE80211_RATE_SIZE) + return EINVAL; + } + if (ic->ic_fixed_rate != i) { + ic->ic_fixed_rate = i; + error = ENETRESET; } - CSR_WRITE_2(sc, selreg, id); - CSR_WRITE_2(sc, offreg, off); - - for (i = 0; i < WI_TIMEOUT; i++) { - status = CSR_READ_2(sc, offreg); - if (!(status & (WI_OFF_BUSY|WI_OFF_ERR))) - break; - DELAY(WI_DELAY); + if ((ime->ifm_media & IFM_IEEE80211_ADHOC) && + (ime->ifm_media & IFM_FLAG0)) + newmode = IEEE80211_M_AHDEMO; + else if (ime->ifm_media & IFM_IEEE80211_ADHOC) + newmode = IEEE80211_M_IBSS; + else if (ime->ifm_media & IFM_IEEE80211_HOSTAP) + newmode = IEEE80211_M_HOSTAP; + else + newmode = IEEE80211_M_STA; + if (ic->ic_opmode != newmode) { + ic->ic_opmode = newmode; + error = ENETRESET; + } + if (error == ENETRESET) { + if (sc->sc_enabled) + wi_init(ifp->if_softc); /* XXX error code lost */ + error = 0; } +#if 0 + ifp->if_baudrate = ifmedia_baudrate(sc->sc_media.ifm_cur->ifm_media); +#endif + return error; +} - if (i == WI_TIMEOUT) { - device_printf(sc->dev, "timeout in wi_seek to %x/%x; last status %x\n", - id, off, status); - return(ETIMEDOUT); +static void +wi_media_status(struct ifnet *ifp, struct ifmediareq *imr) +{ + struct wi_softc *sc = ifp->if_softc; + struct ieee80211com *ic = &sc->sc_ic; + u_int16_t val; + int rate, len; + + if (sc->wi_gone || !sc->sc_enabled) { + imr->ifm_active = IFM_IEEE80211 | IFM_NONE; + imr->ifm_status = 0; + return; } - return(0); + imr->ifm_status = IFM_AVALID; + imr->ifm_active = IFM_IEEE80211; + if (ic->ic_state == IEEE80211_S_RUN && + (sc->sc_flags & WI_FLAGS_OUTRANGE) == 0) + imr->ifm_status |= IFM_ACTIVE; + len = sizeof(val); + if (wi_read_rid(sc, WI_RID_CUR_TX_RATE, &val, &len) != 0) + rate = 0; + else { + /* convert to 802.11 rate */ + rate = val * 2; + if (sc->sc_firmware_type == WI_LUCENT) { + if (rate == 10) + rate = 11; /* 5.5Mbps */ + } else { + if (rate == 4*2) + rate = 11; /* 5.5Mbps */ + else if (rate == 8*2) + rate = 22; /* 11Mbps */ + } + } + imr->ifm_active |= ieee80211_rate2media(rate, IEEE80211_T_DS); + switch (ic->ic_opmode) { + case IEEE80211_M_STA: + break; + case IEEE80211_M_IBSS: + imr->ifm_active |= IFM_IEEE80211_ADHOC; + break; + case IEEE80211_M_AHDEMO: + imr->ifm_active |= IFM_IEEE80211_ADHOC | IFM_FLAG0; + break; + case IEEE80211_M_HOSTAP: + imr->ifm_active |= IFM_IEEE80211_HOSTAP; + break; + } } -static int -wi_read_data(sc, id, off, buf, len) - struct wi_softc *sc; - int id, off; - caddr_t buf; - int len; +static void +wi_sync_bssid(struct wi_softc *sc, u_int8_t new_bssid[IEEE80211_ADDR_LEN]) { - int i; - u_int16_t *ptr; + struct ieee80211com *ic = &sc->sc_ic; + struct ieee80211_node *ni = &ic->ic_bss; + struct ifnet *ifp = &ic->ic_if; - if (wi_seek(sc, id, off, WI_BAP1)) - return(EIO); + if (IEEE80211_ADDR_EQ(new_bssid, ni->ni_bssid)) + return; - ptr = (u_int16_t *)buf; - for (i = 0; i < len / 2; i++) - ptr[i] = CSR_READ_2(sc, WI_DATA1); + DPRINTF(("wi_sync_bssid: bssid %s -> ", ether_sprintf(ni->ni_bssid))); + DPRINTF(("%s ?\n", ether_sprintf(new_bssid))); - return(0); + /* In promiscuous mode, the BSSID field is not a reliable + * indicator of the firmware's BSSID. Damp spurious + * change-of-BSSID indications. + */ + if ((ifp->if_flags & IFF_PROMISC) != 0 && + sc->sc_false_syns >= WI_MAX_FALSE_SYNS) + return; + + ieee80211_new_state(ifp, IEEE80211_S_RUN, -1); } -/* - * According to the comments in the HCF Light code, there is a bug in - * the Hermes (or possibly in certain Hermes firmware revisions) where - * the chip's internal autoincrement counter gets thrown off during - * data writes: the autoincrement is missed, causing one data word to - * be overwritten and subsequent words to be written to the wrong memory - * locations. The end result is that we could end up transmitting bogus - * frames without realizing it. The workaround for this is to write a - * couple of extra guard words after the end of the transfer, then - * attempt to read then back. If we fail to locate the guard words where - * we expect them, we preform the transfer over again. - */ -static int -wi_write_data(sc, id, off, buf, len) - struct wi_softc *sc; - int id, off; - caddr_t buf; - int len; +static void +wi_rx_intr(struct wi_softc *sc) { - int i; - u_int16_t *ptr; -#ifdef WI_HERMES_AUTOINC_WAR - int retries; - - retries = 512; -again: -#endif + struct ieee80211com *ic = &sc->sc_ic; + struct ifnet *ifp = &ic->ic_if; + struct wi_frame frmhdr; + struct mbuf *m; + struct ieee80211_frame *wh; + int fid, len, off, rssi; + u_int8_t dir; + u_int16_t status; + u_int32_t rstamp; + + fid = CSR_READ_2(sc, WI_RX_FID); + + /* First read in the frame header */ + if (wi_read_bap(sc, fid, 0, &frmhdr, sizeof(frmhdr))) { + CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_RX); + ifp->if_ierrors++; + DPRINTF(("wi_rx_intr: read fid %x failed\n", fid)); + return; + } - if (wi_seek(sc, id, off, WI_BAP0)) - return(EIO); + if (IFF_DUMPPKTS(ifp)) + wi_dump_pkt(&frmhdr, NULL, frmhdr.wi_rx_signal); - ptr = (u_int16_t *)buf; - for (i = 0; i < (len / 2); i++) - CSR_WRITE_2(sc, WI_DATA0, ptr[i]); + /* + * Drop undecryptable or packets with receive errors here + */ + status = le16toh(frmhdr.wi_status); + if (status & WI_STAT_ERRSTAT) { + CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_RX); + ifp->if_ierrors++; + DPRINTF(("wi_rx_intr: fid %x error status %x\n", fid, status)); + return; + } + rssi = frmhdr.wi_rx_signal; + rstamp = (le16toh(frmhdr.wi_rx_tstamp0) << 16) | + le16toh(frmhdr.wi_rx_tstamp1); -#ifdef WI_HERMES_AUTOINC_WAR - CSR_WRITE_2(sc, WI_DATA0, 0x1234); - CSR_WRITE_2(sc, WI_DATA0, 0x5678); + len = le16toh(frmhdr.wi_dat_len); + off = ALIGN(sizeof(struct ieee80211_frame)); - if (wi_seek(sc, id, off + len, WI_BAP0)) - return(EIO); + MGETHDR(m, M_DONTWAIT, MT_DATA); + if (m == NULL) { + CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_RX); + ifp->if_ierrors++; + DPRINTF(("wi_rx_intr: MGET failed\n")); + return; + } + if (off + len > MHLEN) { + MCLGET(m, M_DONTWAIT); + if ((m->m_flags & M_EXT) == 0) { + CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_RX); + m_freem(m); + ifp->if_ierrors++; + DPRINTF(("wi_rx_intr: MCLGET failed\n")); + return; + } + } - if (CSR_READ_2(sc, WI_DATA0) != 0x1234 || - CSR_READ_2(sc, WI_DATA0) != 0x5678) { - if (--retries >= 0) - goto again; - device_printf(sc->dev, "wi_write_data device timeout\n"); - return (EIO); + m->m_data += off - sizeof(struct ieee80211_frame); + memcpy(m->m_data, &frmhdr.wi_whdr, sizeof(struct ieee80211_frame)); + wi_read_bap(sc, fid, sizeof(frmhdr), + m->m_data + sizeof(struct ieee80211_frame), len); + m->m_pkthdr.len = m->m_len = sizeof(struct ieee80211_frame) + len; + m->m_pkthdr.rcvif = ifp; + + CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_RX); + +#if NBPFILTER > 0 + if (sc->sc_drvbpf) { + struct mbuf *mb; + + MGETHDR(mb, M_DONTWAIT, m->m_type); + if (mb != NULL) { + (void) m_dup_pkthdr(mb, m, M_DONTWAIT); + mb->m_next = m; + mb->m_data = (caddr_t)&frmhdr; + mb->m_len = sizeof(frmhdr); + mb->m_pkthdr.len += mb->m_len; + bpf_mtap(sc->sc_drvbpf, mb); + m_free(mb); + } } #endif + wh = mtod(m, struct ieee80211_frame *); + if (wh->i_fc[1] & IEEE80211_FC1_WEP) { + /* + * WEP is decrypted by hardware. Clear WEP bit + * header for ieee80211_input(). + */ + wh->i_fc[1] &= ~IEEE80211_FC1_WEP; + } - return(0); + /* synchronize driver's BSSID with firmware's BSSID */ + dir = wh->i_fc[1] & IEEE80211_FC1_DIR_MASK; + if (ic->ic_opmode == IEEE80211_M_IBSS && dir == IEEE80211_FC1_DIR_NODS) + wi_sync_bssid(sc, wh->i_addr3); + + ieee80211_input(ifp, m, rssi, rstamp); } -/* - * Allocate a region of memory inside the NIC and zero - * it out. - */ -static int -wi_alloc_nicmem(sc, len, id) - struct wi_softc *sc; - int len; - int *id; +static void +wi_tx_ex_intr(struct wi_softc *sc) { - int i; + struct ieee80211com *ic = &sc->sc_ic; + struct ifnet *ifp = &ic->ic_if; + struct wi_frame frmhdr; + int fid; - if (wi_cmd(sc, WI_CMD_ALLOC_MEM, len, 0, 0)) { - device_printf(sc->dev, - "failed to allocate %d bytes on NIC\n", len); - return(ENOMEM); - } + fid = CSR_READ_2(sc, WI_TX_CMP_FID); + /* Read in the frame header */ + if (wi_read_bap(sc, fid, 0, &frmhdr, sizeof(frmhdr)) == 0) { + u_int16_t status = le16toh(frmhdr.wi_status); - for (i = 0; i < WI_TIMEOUT; i++) { - if (CSR_READ_2(sc, WI_EVENT_STAT) & WI_EV_ALLOC) - break; - DELAY(WI_DELAY); - } + /* + * Spontaneous station disconnects appear as xmit + * errors. Don't announce them and/or count them + * as an output error. + */ + if ((status & WI_TXSTAT_DISCONNECT) == 0) { + if (ppsratecheck(&lasttxerror, &curtxeps, wi_txerate)) { + if_printf(ifp, "tx failed"); + if (status & WI_TXSTAT_RET_ERR) + printf(", retry limit exceeded"); + if (status & WI_TXSTAT_AGED_ERR) + printf(", max transmit lifetime exceeded"); + if (status & WI_TXSTAT_DISCONNECT) + printf(", port disconnected"); + if (status & WI_TXSTAT_FORM_ERR) + printf(", invalid format (data len %u src %6D)", + le16toh(frmhdr.wi_dat_len), + frmhdr.wi_ehdr.ether_shost, ":"); + if (status & ~0xf) + printf(", status=0x%x", status); + printf("\n"); + } + ifp->if_oerrors++; + } else { + DPRINTF(("port disconnected\n")); + ifp->if_collisions++; /* XXX */ + } + } else + DPRINTF(("wi_tx_ex_intr: read fid %x failed\n", fid)); + CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_TX_EXC); +} - if (i == WI_TIMEOUT) { - device_printf(sc->dev, "time out allocating memory on card\n"); - return(ETIMEDOUT); - } +static void +wi_tx_intr(struct wi_softc *sc) +{ + struct ieee80211com *ic = &sc->sc_ic; + struct ifnet *ifp = &ic->ic_if; + int fid, cur; + fid = CSR_READ_2(sc, WI_ALLOC_FID); CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_ALLOC); - *id = CSR_READ_2(sc, WI_ALLOC_FID); - if (wi_seek(sc, *id, 0, WI_BAP0)) { - device_printf(sc->dev, "seek failed while allocating memory on card\n"); - return(EIO); + cur = sc->sc_txcur; + if (sc->sc_txd[cur].d_fid != fid) { + if_printf(ifp, "bad alloc %x != %x, cur %d nxt %d\n", + fid, sc->sc_txd[cur].d_fid, cur, sc->sc_txnext); + return; + } + sc->sc_tx_timer = 0; + sc->sc_txd[cur].d_len = 0; + sc->sc_txcur = cur = (cur + 1) % WI_NTXBUF; + if (sc->sc_txd[cur].d_len == 0) + ifp->if_flags &= ~IFF_OACTIVE; + else { + if (wi_cmd(sc, WI_CMD_TX | WI_RECLAIM, sc->sc_txd[cur].d_fid, + 0, 0)) { + if_printf(ifp, "xmit failed\n"); + sc->sc_txd[cur].d_len = 0; + } else { + sc->sc_tx_timer = 5; + ifp->if_timer = 1; + } } - - for (i = 0; i < len / 2; i++) - CSR_WRITE_2(sc, WI_DATA0, 0); - - return(0); } static void -wi_setmulti(sc) - struct wi_softc *sc; +wi_info_intr(struct wi_softc *sc) { - struct ifnet *ifp; - int i = 0; - struct ifmultiaddr *ifma; - struct wi_ltv_mcast mcast; + struct ieee80211com *ic = &sc->sc_ic; + struct ifnet *ifp = &ic->ic_if; + int i, fid, len, off; + u_int16_t ltbuf[2]; + u_int16_t stat; + u_int32_t *ptr; + + fid = CSR_READ_2(sc, WI_INFO_FID); + wi_read_bap(sc, fid, 0, ltbuf, sizeof(ltbuf)); + + switch (le16toh(ltbuf[1])) { + + case WI_INFO_LINK_STAT: + wi_read_bap(sc, fid, sizeof(ltbuf), &stat, sizeof(stat)); + DPRINTF(("wi_info_intr: LINK_STAT 0x%x\n", le16toh(stat))); + switch (le16toh(stat)) { + case WI_INFO_LINK_STAT_CONNECTED: + sc->sc_flags &= ~WI_FLAGS_OUTRANGE; + if (ic->ic_state == IEEE80211_S_RUN && + ic->ic_opmode != IEEE80211_M_IBSS) + break; + /* FALLTHROUGH */ + case WI_INFO_LINK_STAT_AP_CHG: + ieee80211_new_state(ifp, IEEE80211_S_RUN, -1); + break; + case WI_INFO_LINK_STAT_AP_INR: + sc->sc_flags &= ~WI_FLAGS_OUTRANGE; + break; + case WI_INFO_LINK_STAT_AP_OOR: + if (sc->sc_firmware_type == WI_SYMBOL && + sc->sc_scan_timer > 0) { + if (wi_cmd(sc, WI_CMD_INQUIRE, + WI_INFO_HOST_SCAN_RESULTS, 0, 0) != 0) + sc->sc_scan_timer = 0; + break; + } + if (ic->ic_opmode == IEEE80211_M_STA) + sc->sc_flags |= WI_FLAGS_OUTRANGE; + break; + case WI_INFO_LINK_STAT_DISCONNECTED: + case WI_INFO_LINK_STAT_ASSOC_FAILED: + if (ic->ic_opmode == IEEE80211_M_STA) + ieee80211_new_state(ifp, IEEE80211_S_INIT, -1); + break; + } + break; + + case WI_INFO_COUNTERS: + /* some card versions have a larger stats structure */ + len = min(le16toh(ltbuf[0]) - 1, sizeof(sc->sc_stats) / 4); + ptr = (u_int32_t *)&sc->sc_stats; + off = sizeof(ltbuf); + for (i = 0; i < len; i++, off += 2, ptr++) { + wi_read_bap(sc, fid, off, &stat, sizeof(stat)); +#ifdef WI_HERMES_STATS_WAR + if (stat & 0xf000) + stat = ~stat; +#endif + *ptr += stat; + } + ifp->if_collisions = sc->sc_stats.wi_tx_single_retries + + sc->sc_stats.wi_tx_multi_retries + + sc->sc_stats.wi_tx_retry_limit; + break; - ifp = &sc->arpcom.ac_if; + case WI_INFO_SCAN_RESULTS: + case WI_INFO_HOST_SCAN_RESULTS: + wi_scan_result(sc, fid, le16toh(ltbuf[0])); + break; - bzero((char *)&mcast, sizeof(mcast)); + default: + DPRINTF(("wi_info_intr: got fid %x type %x len %d\n", fid, + le16toh(ltbuf[1]), le16toh(ltbuf[0]))); + break; + } + CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_INFO); +} - mcast.wi_type = WI_RID_MCAST_LIST; - mcast.wi_len = (3 * 16) + 1; +static int +wi_write_multi(struct wi_softc *sc) +{ + struct ifnet *ifp = &sc->sc_ic.ic_if; + int n; + struct ifmultiaddr *ifma; + struct wi_mcast mlist; if (ifp->if_flags & IFF_ALLMULTI || ifp->if_flags & IFF_PROMISC) { - wi_write_record(sc, (struct wi_ltv_gen *)&mcast); - return; +allmulti: + memset(&mlist, 0, sizeof(mlist)); + return wi_write_rid(sc, WI_RID_MCAST_LIST, &mlist, + sizeof(mlist)); } + n = 0; #if __FreeBSD_version < 500000 LIST_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { #else @@ -1539,984 +1579,1023 @@ wi_setmulti(sc) #endif if (ifma->ifma_addr->sa_family != AF_LINK) continue; - if (i < 16) { - bcopy(LLADDR((struct sockaddr_dl *)ifma->ifma_addr), - (char *)&mcast.wi_mcast[i], ETHER_ADDR_LEN); - i++; - } else { - bzero((char *)&mcast, sizeof(mcast)); - break; - } - } - - mcast.wi_len = (i * 3) + 1; - wi_write_record(sc, (struct wi_ltv_gen *)&mcast); - - return; + if (n >= 16) + goto allmulti; + IEEE80211_ADDR_COPY(&mlist.wi_mcast[n], + (LLADDR((struct sockaddr_dl *)ifma->ifma_addr))); + n++; + } + return wi_write_rid(sc, WI_RID_MCAST_LIST, &mlist, + IEEE80211_ADDR_LEN * n); } static void -wi_setdef(sc, wreq) - struct wi_softc *sc; - struct wi_req *wreq; +wi_read_nicid(struct wi_softc *sc) { - struct sockaddr_dl *sdl; - struct ifaddr *ifa; - struct ifnet *ifp; + struct wi_card_ident *id; + char *p; + int len; + u_int16_t ver[4]; - ifp = &sc->arpcom.ac_if; + /* getting chip identity */ + memset(ver, 0, sizeof(ver)); + len = sizeof(ver); + wi_read_rid(sc, WI_RID_CARD_ID, ver, &len); + device_printf(sc->sc_dev, "using "); - switch(wreq->wi_type) { - case WI_RID_MAC_NODE: - ifa = ifaddr_byindex(ifp->if_index); - sdl = (struct sockaddr_dl *)ifa->ifa_addr; - bcopy((char *)&wreq->wi_val, (char *)&sc->arpcom.ac_enaddr, - ETHER_ADDR_LEN); - bcopy((char *)&wreq->wi_val, LLADDR(sdl), ETHER_ADDR_LEN); - break; - case WI_RID_PORTTYPE: - sc->wi_ptype = le16toh(wreq->wi_val[0]); - break; - case WI_RID_TX_RATE: - sc->wi_tx_rate = le16toh(wreq->wi_val[0]); - break; - case WI_RID_MAX_DATALEN: - sc->wi_max_data_len = le16toh(wreq->wi_val[0]); - break; - case WI_RID_RTS_THRESH: - sc->wi_rts_thresh = le16toh(wreq->wi_val[0]); - break; - case WI_RID_SYSTEM_SCALE: - sc->wi_ap_density = le16toh(wreq->wi_val[0]); - break; - case WI_RID_CREATE_IBSS: - sc->wi_create_ibss = le16toh(wreq->wi_val[0]); - break; - case WI_RID_OWN_CHNL: - sc->wi_channel = le16toh(wreq->wi_val[0]); - break; - case WI_RID_NODENAME: - bzero(sc->wi_node_name, sizeof(sc->wi_node_name)); - bcopy((char *)&wreq->wi_val[1], sc->wi_node_name, 30); - break; - case WI_RID_DESIRED_SSID: - bzero(sc->wi_net_name, sizeof(sc->wi_net_name)); - bcopy((char *)&wreq->wi_val[1], sc->wi_net_name, 30); - break; - case WI_RID_OWN_SSID: - bzero(sc->wi_ibss_name, sizeof(sc->wi_ibss_name)); - bcopy((char *)&wreq->wi_val[1], sc->wi_ibss_name, 30); - break; - case WI_RID_PM_ENABLED: - sc->wi_pm_enabled = le16toh(wreq->wi_val[0]); - break; - case WI_RID_MICROWAVE_OVEN: - sc->wi_mor_enabled = le16toh(wreq->wi_val[0]); - break; - case WI_RID_MAX_SLEEP: - sc->wi_max_sleep = le16toh(wreq->wi_val[0]); - break; - case WI_RID_CNFAUTHMODE: - sc->wi_authtype = le16toh(wreq->wi_val[0]); - break; - case WI_RID_ROAMING_MODE: - sc->wi_roaming = le16toh(wreq->wi_val[0]); - break; - case WI_RID_ENCRYPTION: - sc->wi_use_wep = le16toh(wreq->wi_val[0]); - break; - case WI_RID_TX_CRYPT_KEY: - sc->wi_tx_key = le16toh(wreq->wi_val[0]); - break; - case WI_RID_DEFLT_CRYPT_KEYS: - bcopy((char *)wreq, (char *)&sc->wi_keys, - sizeof(struct wi_ltv_keys)); - break; - default: - break; + sc->sc_firmware_type = WI_NOTYPE; + for (id = wi_card_ident; id->card_name != NULL; id++) { + if (le16toh(ver[0]) == id->card_id) { + printf("%s", id->card_name); + sc->sc_firmware_type = id->firm_type; + break; + } + } + if (sc->sc_firmware_type == WI_NOTYPE) { + if (le16toh(ver[0]) & 0x8000) { + printf("Unknown PRISM2 chip"); + sc->sc_firmware_type = WI_INTERSIL; + } else { + printf("Unknown Lucent chip"); + sc->sc_firmware_type = WI_LUCENT; + } } - /* Reinitialize WaveLAN. */ - wi_init(sc); + /* get primary firmware version (Only Prism chips) */ + if (sc->sc_firmware_type != WI_LUCENT) { + memset(ver, 0, sizeof(ver)); + len = sizeof(ver); + wi_read_rid(sc, WI_RID_PRI_IDENTITY, ver, &len); + sc->sc_pri_firmware_ver = le16toh(ver[2]) * 10000 + + le16toh(ver[3]) * 100 + le16toh(ver[1]); + } - return; + /* get station firmware version */ + memset(ver, 0, sizeof(ver)); + len = sizeof(ver); + wi_read_rid(sc, WI_RID_STA_IDENTITY, ver, &len); + sc->sc_sta_firmware_ver = le16toh(ver[2]) * 10000 + + le16toh(ver[3]) * 100 + le16toh(ver[1]); + if (sc->sc_firmware_type == WI_INTERSIL && + (sc->sc_sta_firmware_ver == 10102 || + sc->sc_sta_firmware_ver == 20102)) { + char ident[12]; + memset(ident, 0, sizeof(ident)); + len = sizeof(ident); + /* value should be the format like "V2.00-11" */ + if (wi_read_rid(sc, WI_RID_SYMBOL_IDENTITY, ident, &len) == 0 && + *(p = (char *)ident) >= 'A' && + p[2] == '.' && p[5] == '-' && p[8] == '\0') { + sc->sc_firmware_type = WI_SYMBOL; + sc->sc_sta_firmware_ver = (p[1] - '0') * 10000 + + (p[3] - '0') * 1000 + (p[4] - '0') * 100 + + (p[6] - '0') * 10 + (p[7] - '0'); + } + } + printf("\n"); + device_printf(sc->sc_dev, "%s Firmware: ", + sc->sc_firmware_type == WI_LUCENT ? "Lucent" : + (sc->sc_firmware_type == WI_SYMBOL ? "Symbol" : "Intersil")); + if (sc->sc_firmware_type != WI_LUCENT) /* XXX */ + printf("Primary (%u.%u.%u), ", + sc->sc_pri_firmware_ver / 10000, + (sc->sc_pri_firmware_ver % 10000) / 100, + sc->sc_pri_firmware_ver % 100); + printf("Station (%u.%u.%u)\n", + sc->sc_sta_firmware_ver / 10000, + (sc->sc_sta_firmware_ver % 10000) / 100, + sc->sc_sta_firmware_ver % 100); } static int -wi_ioctl(ifp, command, data) - struct ifnet *ifp; - u_long command; - caddr_t data; +wi_write_ssid(struct wi_softc *sc, int rid, u_int8_t *buf, int buflen) { - int error = 0; - int len; - int s; - uint16_t mif; - uint16_t val; - u_int8_t tmpkey[14]; - char tmpssid[IEEE80211_NWID_LEN]; - struct wi_softc *sc; - struct wi_req wreq; - struct ifreq *ifr; - struct ieee80211req *ireq; -#if __FreeBSD_version >= 500000 - struct thread *td = curthread; -#else - struct proc *td = curproc; /* Little white lie */ -#endif + struct wi_ssid ssid; + + if (buflen > IEEE80211_NWID_LEN) + return ENOBUFS; + memset(&ssid, 0, sizeof(ssid)); + ssid.wi_len = htole16(buflen); + memcpy(ssid.wi_ssid, buf, buflen); + return wi_write_rid(sc, rid, &ssid, sizeof(ssid)); +} - sc = ifp->if_softc; - WI_LOCK(sc, s); - ifr = (struct ifreq *)data; - ireq = (struct ieee80211req *)data; +static int +wi_get_cfg(struct ifnet *ifp, u_long cmd, caddr_t data) +{ + struct wi_softc *sc = ifp->if_softc; + struct ieee80211com *ic = &sc->sc_ic; + struct ifreq *ifr = (struct ifreq *)data; + struct wi_req wreq; + int len, n, error, mif, val; - if (sc->wi_gone) { - error = ENODEV; - goto out; - } + error = copyin(ifr->ifr_data, &wreq, sizeof(wreq)); + if (error) + return error; + len = (wreq.wi_len - 1) * 2; + if (len < sizeof(u_int16_t)) + return ENOSPC; + if (len > sizeof(wreq.wi_val)) + len = sizeof(wreq.wi_val); + + switch (wreq.wi_type) { + + case WI_RID_IFACE_STATS: + memcpy(wreq.wi_val, &sc->sc_stats, sizeof(sc->sc_stats)); + if (len < sizeof(sc->sc_stats)) + error = ENOSPC; + else + len = sizeof(sc->sc_stats); + break; - switch(command) { - case SIOCSIFFLAGS: - /* - * Can't do promisc and hostap at the same time. If all that's - * changing is the promisc flag, try to short-circuit a call to - * wi_init() by just setting PROMISC in the hardware. - */ - if (ifp->if_flags & IFF_UP) { - if (sc->wi_ptype != WI_PORTTYPE_HOSTAP && - ifp->if_flags & IFF_RUNNING) { - if (ifp->if_flags & IFF_PROMISC && - !(sc->wi_if_flags & IFF_PROMISC)) { - WI_SETVAL(WI_RID_PROMISC, 1); - } else if (!(ifp->if_flags & IFF_PROMISC) && - sc->wi_if_flags & IFF_PROMISC) { - WI_SETVAL(WI_RID_PROMISC, 0); - } else { - wi_init(sc); - } - } else { - wi_init(sc); - } - } else { - if (ifp->if_flags & IFF_RUNNING) { - wi_stop(sc); - } + case WI_RID_ENCRYPTION: + case WI_RID_TX_CRYPT_KEY: + case WI_RID_DEFLT_CRYPT_KEYS: + case WI_RID_TX_RATE: + return ieee80211_cfgget(ifp, cmd, data); + + case WI_RID_MICROWAVE_OVEN: + if (sc->sc_enabled && (sc->sc_flags & WI_FLAGS_HAS_MOR)) { + error = wi_read_rid(sc, wreq.wi_type, wreq.wi_val, + &len); + break; } - sc->wi_if_flags = ifp->if_flags; - error = 0; - break; - case SIOCSIFMEDIA: - case SIOCGIFMEDIA: - error = ifmedia_ioctl(ifp, ifr, &sc->ifmedia, command); - break; - case SIOCADDMULTI: - case SIOCDELMULTI: - wi_setmulti(sc); - error = 0; + wreq.wi_val[0] = htole16(sc->sc_microwave_oven); + len = sizeof(u_int16_t); break; - case SIOCGWAVELAN: - error = copyin(ifr->ifr_data, &wreq, sizeof(wreq)); - if (error) - break; - if (wreq.wi_len > WI_MAX_DATALEN) { - error = EINVAL; + + case WI_RID_DBM_ADJUST: + if (sc->sc_enabled && (sc->sc_flags & WI_FLAGS_HAS_DBMADJUST)) { + error = wi_read_rid(sc, wreq.wi_type, wreq.wi_val, + &len); break; } - /* Don't show WEP keys to non-root users. */ - if (wreq.wi_type == WI_RID_DEFLT_CRYPT_KEYS && suser(td)) + wreq.wi_val[0] = htole16(sc->sc_dbm_adjust); + len = sizeof(u_int16_t); + break; + + case WI_RID_ROAMING_MODE: + if (sc->sc_enabled && (sc->sc_flags & WI_FLAGS_HAS_ROAMING)) { + error = wi_read_rid(sc, wreq.wi_type, wreq.wi_val, + &len); break; - if (wreq.wi_type == WI_RID_IFACE_STATS) { - bcopy((char *)&sc->wi_stats, (char *)&wreq.wi_val, - sizeof(sc->wi_stats)); - wreq.wi_len = (sizeof(sc->wi_stats) / 2) + 1; - } else if (wreq.wi_type == WI_RID_DEFLT_CRYPT_KEYS) { - bcopy((char *)&sc->wi_keys, (char *)&wreq, - sizeof(struct wi_ltv_keys)); - } -#ifdef WICACHE - else if (wreq.wi_type == WI_RID_ZERO_CACHE) { - error = suser(td); - if (error) - break; - sc->wi_sigitems = sc->wi_nextitem = 0; - } else if (wreq.wi_type == WI_RID_READ_CACHE) { - char *pt = (char *)&wreq.wi_val; - bcopy((char *)&sc->wi_sigitems, - (char *)pt, sizeof(int)); - pt += (sizeof (int)); - wreq.wi_len = sizeof(int) / 2; - bcopy((char *)&sc->wi_sigcache, (char *)pt, - sizeof(struct wi_sigcache) * sc->wi_sigitems); - wreq.wi_len += ((sizeof(struct wi_sigcache) * - sc->wi_sigitems) / 2) + 1; - } -#endif - else if (wreq.wi_type == WI_RID_PROCFRAME) { - wreq.wi_len = 2; - wreq.wi_val[0] = sc->wi_procframe; - } else if (wreq.wi_type == WI_RID_PRISM2) { - wreq.wi_len = 2; - wreq.wi_val[0] = sc->sc_firmware_type != WI_LUCENT; - } else if (wreq.wi_type == WI_RID_SCAN_RES && - sc->sc_firmware_type == WI_LUCENT) { - memcpy((char *)wreq.wi_val, (char *)sc->wi_scanbuf, - sc->wi_scanbuf_len * 2); - wreq.wi_len = sc->wi_scanbuf_len; - } else if (wreq.wi_type == WI_RID_MIF) { - mif = wreq.wi_val[0]; - error = wi_cmd(sc, WI_CMD_READMIF, mif, 0, 0); - val = CSR_READ_2(sc, WI_RESP0); - wreq.wi_len = 2; - wreq.wi_val[0] = val; - } else { - if (wi_read_record(sc, (struct wi_ltv_gen *)&wreq)) { - error = EINVAL; - break; - } } - error = copyout(&wreq, ifr->ifr_data, sizeof(wreq)); + wreq.wi_val[0] = htole16(sc->sc_roaming_mode); + len = sizeof(u_int16_t); break; - case SIOCSWAVELAN: - if ((error = suser(td))) - goto out; - error = copyin(ifr->ifr_data, &wreq, sizeof(wreq)); - if (error) - break; - if (wreq.wi_len > WI_MAX_DATALEN) { - error = EINVAL; + + case WI_RID_SYSTEM_SCALE: + if (sc->sc_enabled && (sc->sc_flags & WI_FLAGS_HAS_SYSSCALE)) { + error = wi_read_rid(sc, wreq.wi_type, wreq.wi_val, + &len); break; } - if (wreq.wi_type == WI_RID_IFACE_STATS) { - error = EINVAL; + wreq.wi_val[0] = htole16(sc->sc_system_scale); + len = sizeof(u_int16_t); + break; + + case WI_RID_FRAG_THRESH: + if (sc->sc_enabled && (sc->sc_flags & WI_FLAGS_HAS_FRAGTHR)) { + error = wi_read_rid(sc, wreq.wi_type, wreq.wi_val, + &len); break; - } else if (wreq.wi_type == WI_RID_MGMT_XMIT) { - error = wi_mgmt_xmit(sc, (caddr_t)&wreq.wi_val, - wreq.wi_len); - } else if (wreq.wi_type == WI_RID_PROCFRAME) { - sc->wi_procframe = wreq.wi_val[0]; - /* - * if we're getting a scan request from a wavelan card - * (non-prism2), send out a cmd_inquire to the card to scan - * results for the scan will be received through the info - * interrupt handler. otherwise the scan request can be - * directly handled by a prism2 card's rid interface. - */ - } else if (wreq.wi_type == WI_RID_SCAN_REQ && - sc->sc_firmware_type == WI_LUCENT) { - wi_cmd(sc, WI_CMD_INQUIRE, WI_INFO_SCAN_RESULTS, 0, 0); - } else if (wreq.wi_type == WI_RID_MIF) { - mif = wreq.wi_val[0]; - val = wreq.wi_val[1]; - error = wi_cmd(sc, WI_CMD_WRITEMIF, mif, val, 0); - } else { - error = wi_write_record(sc, (struct wi_ltv_gen *)&wreq); - if (!error) - wi_setdef(sc, &wreq); } + wreq.wi_val[0] = htole16(sc->sc_frag_thresh); + len = sizeof(u_int16_t); break; - case SIOCGPRISM2DEBUG: - error = copyin(ifr->ifr_data, &wreq, sizeof(wreq)); - if (error) + + case WI_RID_READ_APS: + case WI_RID_SCAN_RES: /* XXX */ + if (ic->ic_opmode == IEEE80211_M_HOSTAP) + return ieee80211_cfgget(ifp, cmd, data); + if (sc->sc_scan_timer > 0) { + error = EINPROGRESS; break; - if (!(ifp->if_flags & IFF_RUNNING) || - sc->sc_firmware_type == WI_LUCENT) { - error = EIO; + } + n = sc->sc_naps; + if (len < sizeof(n)) { + error = ENOSPC; break; } - error = wi_get_debug(sc, &wreq); - if (error == 0) - error = copyout(&wreq, ifr->ifr_data, sizeof(wreq)); + if (len < sizeof(n) + sizeof(struct wi_apinfo) * n) + n = (len - sizeof(n)) / sizeof(struct wi_apinfo); + len = sizeof(n) + sizeof(struct wi_apinfo) * n; + memcpy(wreq.wi_val, &n, sizeof(n)); + memcpy((caddr_t)wreq.wi_val + sizeof(n), sc->sc_aps, + sizeof(struct wi_apinfo) * n); break; - case SIOCSPRISM2DEBUG: - if ((error = suser(td))) - goto out; - error = copyin(ifr->ifr_data, &wreq, sizeof(wreq)); - if (error) - break; - error = wi_set_debug(sc, &wreq); + + case WI_RID_PRISM2: + wreq.wi_val[0] = sc->sc_firmware_type != WI_LUCENT; + len = sizeof(u_int16_t); break; - case SIOCG80211: - switch(ireq->i_type) { - case IEEE80211_IOC_SSID: - if(ireq->i_val == -1) { - bzero(tmpssid, IEEE80211_NWID_LEN); - error = wi_get_cur_ssid(sc, tmpssid, &len); - if (error != 0) - break; - error = copyout(tmpssid, ireq->i_data, - IEEE80211_NWID_LEN); - ireq->i_len = len; - } else if (ireq->i_val == 0) { - error = copyout(sc->wi_net_name, - ireq->i_data, - IEEE80211_NWID_LEN); - ireq->i_len = IEEE80211_NWID_LEN; - } else - error = EINVAL; - break; - case IEEE80211_IOC_NUMSSIDS: - ireq->i_val = 1; - break; - case IEEE80211_IOC_WEP: - if(!sc->wi_has_wep) { - ireq->i_val = IEEE80211_WEP_NOSUP; - } else { - if(sc->wi_use_wep) { - ireq->i_val = - IEEE80211_WEP_MIXED; - } else { - ireq->i_val = - IEEE80211_WEP_OFF; - } - } - break; - case IEEE80211_IOC_WEPKEY: - if(!sc->wi_has_wep || - ireq->i_val < 0 || ireq->i_val > 3) { - error = EINVAL; - break; - } - len = sc->wi_keys.wi_keys[ireq->i_val].wi_keylen; - if (suser(td)) - bcopy(sc->wi_keys.wi_keys[ireq->i_val].wi_keydat, - tmpkey, len); - else - bzero(tmpkey, len); - ireq->i_len = len; - error = copyout(tmpkey, ireq->i_data, len); + case WI_RID_MIF: + mif = wreq.wi_val[0]; + error = wi_cmd(sc, WI_CMD_READMIF, mif, 0, 0); + val = CSR_READ_2(sc, WI_RESP0); + wreq.wi_val[0] = val; + len = sizeof(u_int16_t); + break; + case WI_RID_ZERO_CACHE: + case WI_RID_PROCFRAME: /* ignore for compatibility */ + /* XXX ??? */ + break; + + case WI_RID_READ_CACHE: + return ieee80211_cfgget(ifp, cmd, data); + + default: + if (sc->sc_enabled) { + error = wi_read_rid(sc, wreq.wi_type, wreq.wi_val, + &len); break; - case IEEE80211_IOC_NUMWEPKEYS: - if(!sc->wi_has_wep) - error = EINVAL; - else - ireq->i_val = 4; - break; - case IEEE80211_IOC_WEPTXKEY: - if(!sc->wi_has_wep) - error = EINVAL; - else - ireq->i_val = sc->wi_tx_key; + } + switch (wreq.wi_type) { + case WI_RID_MAX_DATALEN: + wreq.wi_val[0] = htole16(sc->sc_max_datalen); + len = sizeof(u_int16_t); break; - case IEEE80211_IOC_AUTHMODE: - ireq->i_val = sc->wi_authmode; + case WI_RID_RTS_THRESH: + wreq.wi_val[0] = htole16(sc->sc_rts_thresh); + len = sizeof(u_int16_t); break; - case IEEE80211_IOC_STATIONNAME: - error = copyout(sc->wi_node_name, - ireq->i_data, IEEE80211_NWID_LEN); - ireq->i_len = IEEE80211_NWID_LEN; + case WI_RID_CNFAUTHMODE: + wreq.wi_val[0] = htole16(sc->sc_cnfauthmode); + len = sizeof(u_int16_t); break; - case IEEE80211_IOC_CHANNEL: - wreq.wi_type = WI_RID_CURRENT_CHAN; - wreq.wi_len = WI_MAX_DATALEN; - if (wi_read_record(sc, (struct wi_ltv_gen *)&wreq)) - error = EINVAL; - else { - ireq->i_val = wreq.wi_val[0]; + case WI_RID_NODENAME: + if (len < sc->sc_nodelen + sizeof(u_int16_t)) { + error = ENOSPC; + break; } - break; - case IEEE80211_IOC_POWERSAVE: - if(sc->wi_pm_enabled) - ireq->i_val = IEEE80211_POWERSAVE_ON; - else - ireq->i_val = IEEE80211_POWERSAVE_OFF; - break; - case IEEE80211_IOC_POWERSAVESLEEP: - ireq->i_val = sc->wi_max_sleep; + len = sc->sc_nodelen + sizeof(u_int16_t); + wreq.wi_val[0] = htole16((sc->sc_nodelen + 1) / 2); + memcpy(&wreq.wi_val[1], sc->sc_nodename, + sc->sc_nodelen); break; default: - error = EINVAL; + return ieee80211_cfgget(ifp, cmd, data); } break; - case SIOCS80211: - if ((error = suser(td))) - goto out; - switch(ireq->i_type) { - case IEEE80211_IOC_SSID: - if (ireq->i_val != 0 || - ireq->i_len > IEEE80211_NWID_LEN) { - error = EINVAL; + } + if (error) + return error; + wreq.wi_len = (len + 1) / 2 + 1; + return copyout(&wreq, ifr->ifr_data, (wreq.wi_len + 1) * 2); +} + +static int +wi_set_cfg(struct ifnet *ifp, u_long cmd, caddr_t data) +{ + struct wi_softc *sc = ifp->if_softc; + struct ieee80211com *ic = &sc->sc_ic; + struct ifreq *ifr = (struct ifreq *)data; + struct wi_req wreq; + struct mbuf *m; + int i, len, error, mif, val; + + error = copyin(ifr->ifr_data, &wreq, sizeof(wreq)); + if (error) + return error; + len = (wreq.wi_len - 1) * 2; + switch (wreq.wi_type) { + case WI_RID_DBM_ADJUST: + return ENODEV; + + case WI_RID_NODENAME: + if (le16toh(wreq.wi_val[0]) * 2 > len || + le16toh(wreq.wi_val[0]) > sizeof(sc->sc_nodename)) { + error = ENOSPC; + break; + } + if (sc->sc_enabled) { + error = wi_write_rid(sc, wreq.wi_type, wreq.wi_val, + len); + if (error) break; - } - /* We set both of them */ - bzero(sc->wi_net_name, IEEE80211_NWID_LEN); - error = copyin(ireq->i_data, - sc->wi_net_name, ireq->i_len); - bcopy(sc->wi_net_name, sc->wi_ibss_name, IEEE80211_NWID_LEN); + } + sc->sc_nodelen = le16toh(wreq.wi_val[0]) * 2; + memcpy(sc->sc_nodename, &wreq.wi_val[1], sc->sc_nodelen); + break; + + case WI_RID_MICROWAVE_OVEN: + case WI_RID_ROAMING_MODE: + case WI_RID_SYSTEM_SCALE: + case WI_RID_FRAG_THRESH: + if (wreq.wi_type == WI_RID_MICROWAVE_OVEN && + (sc->sc_flags & WI_FLAGS_HAS_MOR) == 0) break; - case IEEE80211_IOC_WEP: - /* - * These cards only support one mode so - * we just turn wep on what ever is - * passed in if it's not OFF. - */ - if (ireq->i_val == IEEE80211_WEP_OFF) { - sc->wi_use_wep = 0; - } else { - sc->wi_use_wep = 1; - } + if (wreq.wi_type == WI_RID_ROAMING_MODE && + (sc->sc_flags & WI_FLAGS_HAS_ROAMING) == 0) break; - case IEEE80211_IOC_WEPKEY: - if (ireq->i_val < 0 || ireq->i_val > 3 || - ireq->i_len > 13) { - error = EINVAL; - break; - } - bzero(sc->wi_keys.wi_keys[ireq->i_val].wi_keydat, 13); - error = copyin(ireq->i_data, - sc->wi_keys.wi_keys[ireq->i_val].wi_keydat, - ireq->i_len); - if(error) - break; - sc->wi_keys.wi_keys[ireq->i_val].wi_keylen = - ireq->i_len; + if (wreq.wi_type == WI_RID_SYSTEM_SCALE && + (sc->sc_flags & WI_FLAGS_HAS_SYSSCALE) == 0) break; - case IEEE80211_IOC_WEPTXKEY: - if (ireq->i_val < 0 || ireq->i_val > 3) { - error = EINVAL; + if (wreq.wi_type == WI_RID_FRAG_THRESH && + (sc->sc_flags & WI_FLAGS_HAS_FRAGTHR) == 0) + break; + /* FALLTHROUGH */ + case WI_RID_RTS_THRESH: + case WI_RID_CNFAUTHMODE: + case WI_RID_MAX_DATALEN: + if (sc->sc_enabled) { + error = wi_write_rid(sc, wreq.wi_type, wreq.wi_val, + sizeof(u_int16_t)); + if (error) break; - } - sc->wi_tx_key = ireq->i_val; + } + switch (wreq.wi_type) { + case WI_RID_FRAG_THRESH: + sc->sc_frag_thresh = le16toh(wreq.wi_val[0]); break; - case IEEE80211_IOC_AUTHMODE: - sc->wi_authmode = ireq->i_val; + case WI_RID_RTS_THRESH: + sc->sc_rts_thresh = le16toh(wreq.wi_val[0]); break; - case IEEE80211_IOC_STATIONNAME: - if (ireq->i_len > 32) { - error = EINVAL; - break; - } - bzero(sc->wi_node_name, 32); - error = copyin(ireq->i_data, - sc->wi_node_name, ireq->i_len); + case WI_RID_MICROWAVE_OVEN: + sc->sc_microwave_oven = le16toh(wreq.wi_val[0]); break; - case IEEE80211_IOC_CHANNEL: - /* - * The actual range is 1-14, but if you - * set it to 0 you get the default. So - * we let that work too. - */ - if (ireq->i_val < 0 || ireq->i_val > 14) { - error = EINVAL; - break; - } - sc->wi_channel = ireq->i_val; + case WI_RID_ROAMING_MODE: + sc->sc_roaming_mode = le16toh(wreq.wi_val[0]); break; - case IEEE80211_IOC_POWERSAVE: - switch (ireq->i_val) { - case IEEE80211_POWERSAVE_OFF: - sc->wi_pm_enabled = 0; - break; - case IEEE80211_POWERSAVE_ON: - sc->wi_pm_enabled = 1; - break; - default: - error = EINVAL; - break; - } + case WI_RID_SYSTEM_SCALE: + sc->sc_system_scale = le16toh(wreq.wi_val[0]); break; - case IEEE80211_IOC_POWERSAVESLEEP: - if (ireq->i_val < 0) { - error = EINVAL; - break; - } - sc->wi_max_sleep = ireq->i_val; + case WI_RID_CNFAUTHMODE: + sc->sc_cnfauthmode = le16toh(wreq.wi_val[0]); break; - default: - error = EINVAL; + case WI_RID_MAX_DATALEN: + sc->sc_max_datalen = le16toh(wreq.wi_val[0]); break; } + break; - /* Reinitialize WaveLAN. */ - wi_init(sc); + case WI_RID_TX_RATE: + switch (le16toh(wreq.wi_val[0])) { + case 3: + ic->ic_fixed_rate = -1; + break; + default: + for (i = 0; i < IEEE80211_RATE_SIZE; i++) { + if ((ic->ic_sup_rates[i] & IEEE80211_RATE_VAL) + / 2 == le16toh(wreq.wi_val[0])) + break; + } + if (i == IEEE80211_RATE_SIZE) + return EINVAL; + ic->ic_fixed_rate = i; + } + if (sc->sc_enabled) + error = wi_write_txrate(sc); + break; - break; - case SIOCHOSTAP_ADD: - case SIOCHOSTAP_DEL: - case SIOCHOSTAP_GET: - case SIOCHOSTAP_GETALL: - case SIOCHOSTAP_GFLAGS: - case SIOCHOSTAP_SFLAGS: - /* Send all Host AP specific ioctl's to Host AP code. */ - error = wihap_ioctl(sc, command, data); + case WI_RID_SCAN_APS: + if (sc->sc_enabled && ic->ic_opmode != IEEE80211_M_HOSTAP) + error = wi_scan_ap(sc); break; - default: - error = ether_ioctl(ifp, command, data); + + case WI_RID_MGMT_XMIT: + if (!sc->sc_enabled) { + error = ENETDOWN; + break; + } + if (ic->ic_mgtq.ifq_len > 5) { + error = EAGAIN; + break; + } + /* XXX wi_len looks in u_int8_t, not in u_int16_t */ + m = m_devget((char *)&wreq.wi_val, wreq.wi_len, 0, ifp, NULL); + if (m == NULL) { + error = ENOMEM; + break; + } + IF_ENQUEUE(&ic->ic_mgtq, m); break; - } -out: - WI_UNLOCK(sc, s); - return(error); -} + case WI_RID_MIF: + mif = wreq.wi_val[0]; + val = wreq.wi_val[1]; + error = wi_cmd(sc, WI_CMD_WRITEMIF, mif, val, 0); + break; -static void -wi_init(xsc) - void *xsc; -{ - struct wi_softc *sc = xsc; - struct ifnet *ifp = &sc->arpcom.ac_if; - struct wi_ltv_macaddr mac; - int id = 0; - int s; + case WI_RID_PROCFRAME: /* ignore for compatibility */ + break; - WI_LOCK(sc, s); + case WI_RID_SCAN_REQ: + if (!sc->sc_enabled) { + error = ENETDOWN; + break; + } + if (sc->sc_firmware_type == WI_LUCENT) - if (sc->wi_gone) { - WI_UNLOCK(sc, s); - return; + default: + if (sc->sc_enabled) { + error = wi_write_rid(sc, wreq.wi_type, wreq.wi_val, + len); + if (error) + break; + } + error = ieee80211_cfgset(ifp, cmd, data); + break; } + return error; +} - if (ifp->if_flags & IFF_RUNNING) - wi_stop(sc); - - wi_reset(sc); - - /* Program max data length. */ - WI_SETVAL(WI_RID_MAX_DATALEN, sc->wi_max_data_len); - - /* Set the port type. */ - WI_SETVAL(WI_RID_PORTTYPE, sc->wi_ptype); - - /* Enable/disable IBSS creation. */ - WI_SETVAL(WI_RID_CREATE_IBSS, sc->wi_create_ibss); - - /* Program the RTS/CTS threshold. */ - WI_SETVAL(WI_RID_RTS_THRESH, sc->wi_rts_thresh); - - /* Program the TX rate */ - WI_SETVAL(WI_RID_TX_RATE, sc->wi_tx_rate); - - /* Access point density */ - WI_SETVAL(WI_RID_SYSTEM_SCALE, sc->wi_ap_density); - - /* Power Management Enabled */ - WI_SETVAL(WI_RID_PM_ENABLED, sc->wi_pm_enabled); - - /* Power Managment Max Sleep */ - WI_SETVAL(WI_RID_MAX_SLEEP, sc->wi_max_sleep); - - /* Roaming type */ - WI_SETVAL(WI_RID_ROAMING_MODE, sc->wi_roaming); - - /* Specify the IBSS name */ - WI_SETSTR(WI_RID_OWN_SSID, sc->wi_ibss_name); - - /* Specify the network name */ - WI_SETSTR(WI_RID_DESIRED_SSID, sc->wi_net_name); +static int +wi_write_txrate(struct wi_softc *sc) +{ + struct ieee80211com *ic = &sc->sc_ic; + int i; + u_int16_t rate; - /* Specify the frequency to use */ - WI_SETVAL(WI_RID_OWN_CHNL, sc->wi_channel); + if (ic->ic_fixed_rate < 0) + rate = 0; /* auto */ + else + rate = (ic->ic_sup_rates[ic->ic_fixed_rate] & + IEEE80211_RATE_VAL) / 2; - /* Program the nodename. */ - WI_SETSTR(WI_RID_NODENAME, sc->wi_node_name); + /* rate: 0, 1, 2, 5, 11 */ - /* Specify the authentication mode. */ - WI_SETVAL(WI_RID_CNFAUTHMODE, sc->wi_authmode); + switch (sc->sc_firmware_type) { + case WI_LUCENT: + if (rate == 0) + rate = 3; /* auto */ + break; + default: + /* Choose a bit according to this table. + * + * bit | data rate + * ----+------------------- + * 0 | 1Mbps + * 1 | 2Mbps + * 2 | 5.5Mbps + * 3 | 11Mbps + */ + for (i = 8; i > 0; i >>= 1) { + if (rate >= i) + break; + } + if (i == 0) + rate = 0xf; /* auto */ + else + rate = i; + break; + } + return wi_write_val(sc, WI_RID_TX_RATE, rate); +} - /* Set our MAC address. */ - mac.wi_len = 4; - mac.wi_type = WI_RID_MAC_NODE; - bcopy((char *)&sc->arpcom.ac_enaddr, - (char *)&mac.wi_mac_addr, ETHER_ADDR_LEN); - wi_write_record(sc, (struct wi_ltv_gen *)&mac); +static int +wi_write_wep(struct wi_softc *sc) +{ + struct ieee80211com *ic = &sc->sc_ic; + int error = 0; + int i, keylen; + u_int16_t val; + struct wi_key wkey[IEEE80211_WEP_NKID]; - /* - * Initialize promisc mode. - * Being in the Host-AP mode causes - * great deal of pain if promisc mode is set. - * Therefore we avoid confusing the firmware - * and always reset promisc mode in Host-AP regime, - * it shows us all the packets anyway. - */ - if (sc->wi_ptype != WI_PORTTYPE_HOSTAP && ifp->if_flags & IFF_PROMISC) - WI_SETVAL(WI_RID_PROMISC, 1); - else - WI_SETVAL(WI_RID_PROMISC, 0); + switch (sc->sc_firmware_type) { + case WI_LUCENT: + val = (ic->ic_flags & IEEE80211_F_WEPON) ? 1 : 0; + error = wi_write_val(sc, WI_RID_ENCRYPTION, val); + if (error) + break; + error = wi_write_val(sc, WI_RID_TX_CRYPT_KEY, ic->ic_wep_txkey); + if (error) + break; + memset(wkey, 0, sizeof(wkey)); + for (i = 0; i < IEEE80211_WEP_NKID; i++) { + keylen = ic->ic_nw_keys[i].wk_len; + wkey[i].wi_keylen = htole16(keylen); + memcpy(wkey[i].wi_keydat, ic->ic_nw_keys[i].wk_key, + keylen); + } + error = wi_write_rid(sc, WI_RID_DEFLT_CRYPT_KEYS, + wkey, sizeof(wkey)); + break; - /* Configure WEP. */ - if (sc->wi_has_wep) { - WI_SETVAL(WI_RID_ENCRYPTION, sc->wi_use_wep); - WI_SETVAL(WI_RID_TX_CRYPT_KEY, sc->wi_tx_key); - sc->wi_keys.wi_len = (sizeof(struct wi_ltv_keys) / 2) + 1; - sc->wi_keys.wi_type = WI_RID_DEFLT_CRYPT_KEYS; - wi_write_record(sc, (struct wi_ltv_gen *)&sc->wi_keys); - if (sc->sc_firmware_type != WI_LUCENT && sc->wi_use_wep) { + case WI_INTERSIL: + case WI_SYMBOL: + if (ic->ic_flags & IEEE80211_F_WEPON) { /* * ONLY HWB3163 EVAL-CARD Firmware version * less than 0.8 variant2 * - * If promiscuous mode disable, Prism2 chip - * does not work with WEP. + * If promiscuous mode disable, Prism2 chip + * does not work with WEP . * It is under investigation for details. * (ichiro@netbsd.org) - * - * And make sure that we don't need to do it - * in hostap mode, since it interferes with - * the above hostap workaround. */ - if (sc->wi_ptype != WI_PORTTYPE_HOSTAP && - sc->sc_firmware_type == WI_INTERSIL && + if (sc->sc_firmware_type == WI_INTERSIL && sc->sc_sta_firmware_ver < 802 ) { /* firm ver < 0.8 variant 2 */ - WI_SETVAL(WI_RID_PROMISC, 1); + wi_write_val(sc, WI_RID_PROMISC, 1); } - WI_SETVAL(WI_RID_CNFAUTHMODE, sc->wi_authtype); + wi_write_val(sc, WI_RID_CNFAUTHMODE, + sc->sc_cnfauthmode); + val = PRIVACY_INVOKED | EXCLUDE_UNENCRYPTED; + /* + * Encryption firmware has a bug for HostAP mode. + */ + if (sc->sc_firmware_type == WI_INTERSIL && + ic->ic_opmode == IEEE80211_M_HOSTAP) + val |= HOST_ENCRYPT; + } else { + wi_write_val(sc, WI_RID_CNFAUTHMODE, + IEEE80211_AUTH_OPEN); + val = HOST_ENCRYPT | HOST_DECRYPT; + } + error = wi_write_val(sc, WI_RID_P2_ENCRYPTION, val); + if (error) + break; + error = wi_write_val(sc, WI_RID_P2_TX_CRYPT_KEY, + ic->ic_wep_txkey); + if (error) + break; + /* + * It seems that the firmware accept 104bit key only if + * all the keys have 104bit length. We get the length of + * the transmit key and use it for all other keys. + * Perhaps we should use software WEP for such situation. + */ + keylen = ic->ic_nw_keys[ic->ic_wep_txkey].wk_len; + if (keylen > IEEE80211_WEP_KEYLEN) + keylen = 13; /* 104bit keys */ + else + keylen = IEEE80211_WEP_KEYLEN; + for (i = 0; i < IEEE80211_WEP_NKID; i++) { + error = wi_write_rid(sc, WI_RID_P2_CRYPT_KEY0 + i, + ic->ic_nw_keys[i].wk_key, keylen); + if (error) + break; } + break; } + return error; +} - /* Set multicast filter. */ - wi_setmulti(sc); - - /* Enable desired port */ - wi_cmd(sc, WI_CMD_ENABLE | sc->wi_portnum, 0, 0, 0); +static int +wi_cmd(struct wi_softc *sc, int cmd, int val0, int val1, int val2) +{ + int i, s = 0; + static volatile int count = 0; + + if (count > 0) + panic("Hey partner, hold on there!"); + count++; - if (wi_alloc_nicmem(sc, ETHER_MAX_LEN + sizeof(struct wi_frame) + 8, &id)) - device_printf(sc->dev, "tx buffer allocation failed\n"); - sc->wi_tx_data_id = id; + /* wait for the busy bit to clear */ + for (i = 500; i > 0; i--) { /* 5s */ + if (!(CSR_READ_2(sc, WI_COMMAND) & WI_CMD_BUSY)) { + break; + } + DELAY(10*1000); /* 10 m sec */ + } + if (i == 0) { + device_printf(sc->sc_dev, "wi_cmd: busy bit won't clear.\n" ); + count--; + return(ETIMEDOUT); + } - if (wi_alloc_nicmem(sc, ETHER_MAX_LEN + sizeof(struct wi_frame) + 8, &id)) - device_printf(sc->dev, "mgmt. buffer allocation failed\n"); - sc->wi_tx_mgmt_id = id; + CSR_WRITE_2(sc, WI_PARAM0, val0); + CSR_WRITE_2(sc, WI_PARAM1, val1); + CSR_WRITE_2(sc, WI_PARAM2, val2); + CSR_WRITE_2(sc, WI_COMMAND, cmd); - /* enable interrupts */ - CSR_WRITE_2(sc, WI_INT_EN, WI_INTRS); + if (cmd == WI_CMD_INI) { + /* XXX: should sleep here. */ + DELAY(100*1000); + } + for (i = 0; i < WI_TIMEOUT; i++) { + /* + * Wait for 'command complete' bit to be + * set in the event status register. + */ + s = CSR_READ_2(sc, WI_EVENT_STAT); + if (s & WI_EV_CMD) { + /* Ack the event and read result code. */ + s = CSR_READ_2(sc, WI_STATUS); + CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_CMD); +#ifdef foo + if ((s & WI_CMD_CODE_MASK) != (cmd & WI_CMD_CODE_MASK)) + return(EIO); +#endif + if (s & WI_STAT_CMD_RESULT) { + count--; + return(EIO); + } + break; + } + DELAY(WI_DELAY); + } - wihap_init(sc); + count--; + if (i == WI_TIMEOUT) { + device_printf(sc->sc_dev, + "timeout in wi_cmd 0x%04x; event status 0x%04x\n", cmd, s); + return(ETIMEDOUT); + } + return (0); +} - ifp->if_flags |= IFF_RUNNING; - ifp->if_flags &= ~IFF_OACTIVE; +static int +wi_seek_bap(struct wi_softc *sc, int id, int off) +{ + int i, status; - sc->wi_stat_ch = timeout(wi_inquire, sc, hz * 60); - WI_UNLOCK(sc, s); + CSR_WRITE_2(sc, WI_SEL0, id); + CSR_WRITE_2(sc, WI_OFF0, off); - return; + for (i = 0; ; i++) { + status = CSR_READ_2(sc, WI_OFF0); + if ((status & WI_OFF_BUSY) == 0) + break; + if (i == WI_TIMEOUT) { + device_printf(sc->sc_dev, "timeout in wi_seek to %x/%x\n", + id, off); + sc->sc_bap_off = WI_OFF_ERR; /* invalidate */ + return ETIMEDOUT; + } + DELAY(1); + } + if (status & WI_OFF_ERR) { + device_printf(sc->sc_dev, "failed in wi_seek to %x/%x\n", id, off); + sc->sc_bap_off = WI_OFF_ERR; /* invalidate */ + return EIO; + } + sc->sc_bap_id = id; + sc->sc_bap_off = off; + return 0; } -#define RC4STATE 256 -#define RC4KEYLEN 16 -#define RC4SWAP(x,y) \ - do { u_int8_t t = state[x]; state[x] = state[y]; state[y] = t; } while(0) - -static void -wi_do_hostencrypt(struct wi_softc *sc, caddr_t buf, int len) +static int +wi_read_bap(struct wi_softc *sc, int id, int off, void *buf, int buflen) { - u_int32_t i, crc, klen; - u_int8_t state[RC4STATE], key[RC4KEYLEN]; - u_int8_t x, y, *dat; - - if (!sc->wi_icv_flag) { - sc->wi_icv = arc4random(); - sc->wi_icv_flag++; - } else - sc->wi_icv++; - /* - * Skip 'bad' IVs from Fluhrer/Mantin/Shamir: - * (B, 255, N) with 3 <= B < 8 - */ - if (sc->wi_icv >= 0x03ff00 && - (sc->wi_icv & 0xf8ff00) == 0x00ff00) - sc->wi_icv += 0x000100; - - /* prepend 24bit IV to tx key, byte order does not matter */ - key[0] = sc->wi_icv >> 16; - key[1] = sc->wi_icv >> 8; - key[2] = sc->wi_icv; - - klen = sc->wi_keys.wi_keys[sc->wi_tx_key].wi_keylen + - IEEE80211_WEP_IVLEN; - klen = (klen >= RC4KEYLEN) ? RC4KEYLEN : RC4KEYLEN/2; - bcopy((char *)&sc->wi_keys.wi_keys[sc->wi_tx_key].wi_keydat, - (char *)key + IEEE80211_WEP_IVLEN, klen - IEEE80211_WEP_IVLEN); - - /* rc4 keysetup */ - x = y = 0; - for (i = 0; i < RC4STATE; i++) - state[i] = i; - for (i = 0; i < RC4STATE; i++) { - y = (key[x] + state[i] + y) % RC4STATE; - RC4SWAP(i, y); - x = (x + 1) % klen; - } - - /* output: IV, tx keyid, rc4(data), rc4(crc32(data)) */ - dat = buf; - dat[0] = key[0]; - dat[1] = key[1]; - dat[2] = key[2]; - dat[3] = sc->wi_tx_key << 6; /* pad and keyid */ - dat += 4; - - /* compute rc4 over data, crc32 over data */ - crc = ~0; - x = y = 0; - for (i = 0; i < len; i++) { - x = (x + 1) % RC4STATE; - y = (state[x] + y) % RC4STATE; - RC4SWAP(x, y); - crc = crc32_tab[(crc ^ dat[i]) & 0xff] ^ (crc >> 8); - dat[i] ^= state[(state[x] + state[y]) % RC4STATE]; - } - crc = ~crc; - dat += len; - - /* append little-endian crc32 and encrypt */ - dat[0] = crc; - dat[1] = crc >> 8; - dat[2] = crc >> 16; - dat[3] = crc >> 24; - for (i = 0; i < IEEE80211_WEP_CRCLEN; i++) { - x = (x + 1) % RC4STATE; - y = (state[x] + y) % RC4STATE; - RC4SWAP(x, y); - dat[i] ^= state[(state[x] + state[y]) % RC4STATE]; + u_int16_t *ptr; + int i, error, cnt; + + if (buflen == 0) + return 0; + if (id != sc->sc_bap_id || off != sc->sc_bap_off) { + if ((error = wi_seek_bap(sc, id, off)) != 0) + return error; } + cnt = (buflen + 1) / 2; + ptr = (u_int16_t *)buf; + for (i = 0; i < cnt; i++) + *ptr++ = CSR_READ_2(sc, WI_DATA0); + sc->sc_bap_off += cnt * 2; + return 0; } -static void -wi_start(ifp) - struct ifnet *ifp; +static int +wi_write_bap(struct wi_softc *sc, int id, int off, void *buf, int buflen) { - struct wi_softc *sc; - struct mbuf *m0; - struct wi_frame tx_frame; - struct ether_header *eh; - int id; - int s; + u_int16_t *ptr; + int i, error, cnt; - sc = ifp->if_softc; - WI_LOCK(sc, s); + if (buflen == 0) + return 0; - if (sc->wi_gone) { - WI_UNLOCK(sc, s); - return; - } - - if (ifp->if_flags & IFF_OACTIVE) { - WI_UNLOCK(sc, s); - return; - } - -nextpkt: - IF_DEQUEUE(&ifp->if_snd, m0); - if (m0 == NULL) { - WI_UNLOCK(sc, s); - return; +#ifdef WI_HERMES_AUTOINC_WAR + again: +#endif + if (id != sc->sc_bap_id || off != sc->sc_bap_off) { + if ((error = wi_seek_bap(sc, id, off)) != 0) + return error; } + cnt = (buflen + 1) / 2; + ptr = (u_int16_t *)buf; + for (i = 0; i < cnt; i++) + CSR_WRITE_2(sc, WI_DATA0, ptr[i]); + sc->sc_bap_off += cnt * 2; - bzero((char *)&tx_frame, sizeof(tx_frame)); - tx_frame.wi_frame_ctl = htole16(WI_FTYPE_DATA); - id = sc->wi_tx_data_id; - eh = mtod(m0, struct ether_header *); - - if (sc->wi_ptype == WI_PORTTYPE_HOSTAP) { - if (!wihap_check_tx(&sc->wi_hostap_info, - eh->ether_dhost, &tx_frame.wi_tx_rate)) { - if (ifp->if_flags & IFF_DEBUG) - printf("wi_start: dropping unassoc " - "dst %6D\n", eh->ether_dhost, ":"); - m_freem(m0); - goto nextpkt; - } - } +#ifdef WI_HERMES_AUTOINC_WAR /* - * Use RFC1042 encoding for IP and ARP datagrams, - * 802.3 for anything else. + * According to the comments in the HCF Light code, there is a bug + * in the Hermes (or possibly in certain Hermes firmware revisions) + * where the chip's internal autoincrement counter gets thrown off + * during data writes: the autoincrement is missed, causing one + * data word to be overwritten and subsequent words to be written to + * the wrong memory locations. The end result is that we could end + * up transmitting bogus frames without realizing it. The workaround + * for this is to write a couple of extra guard words after the end + * of the transfer, then attempt to read then back. If we fail to + * locate the guard words where we expect them, we preform the + * transfer over again. */ - if (ntohs(eh->ether_type) > ETHER_MAX_LEN) { - bcopy((char *)&eh->ether_dhost, - (char *)&tx_frame.wi_addr1, ETHER_ADDR_LEN); - if (sc->wi_ptype == WI_PORTTYPE_HOSTAP) { - tx_frame.wi_tx_ctl = WI_ENC_TX_MGMT; /* XXX */ - tx_frame.wi_frame_ctl |= WI_FCTL_FROMDS; - if (sc->wi_use_wep) - tx_frame.wi_frame_ctl |= WI_FCTL_WEP; - bcopy((char *)&sc->arpcom.ac_enaddr, - (char *)&tx_frame.wi_addr2, ETHER_ADDR_LEN); - bcopy((char *)&eh->ether_shost, - (char *)&tx_frame.wi_addr3, ETHER_ADDR_LEN); - } - else - bcopy((char *)&eh->ether_shost, - (char *)&tx_frame.wi_addr2, ETHER_ADDR_LEN); - bcopy((char *)&eh->ether_dhost, - (char *)&tx_frame.wi_dst_addr, ETHER_ADDR_LEN); - bcopy((char *)&eh->ether_shost, - (char *)&tx_frame.wi_src_addr, ETHER_ADDR_LEN); - - tx_frame.wi_dat_len = m0->m_pkthdr.len - WI_SNAPHDR_LEN; - tx_frame.wi_dat[0] = htons(WI_SNAP_WORD0); - tx_frame.wi_dat[1] = htons(WI_SNAP_WORD1); - tx_frame.wi_len = htons(m0->m_pkthdr.len - WI_SNAPHDR_LEN); - tx_frame.wi_type = eh->ether_type; - - if (sc->wi_ptype == WI_PORTTYPE_HOSTAP && sc->wi_use_wep) { - /* Do host encryption. */ - bcopy(&tx_frame.wi_dat[0], &sc->wi_txbuf[4], 8); - m_copydata(m0, sizeof(struct ether_header), - m0->m_pkthdr.len - sizeof(struct ether_header), - (caddr_t)&sc->wi_txbuf[12]); - wi_do_hostencrypt(sc, &sc->wi_txbuf[0], - tx_frame.wi_dat_len); - tx_frame.wi_dat_len += IEEE80211_WEP_IVLEN + - IEEE80211_WEP_KIDLEN + IEEE80211_WEP_CRCLEN; - wi_write_data(sc, id, 0, (caddr_t)&tx_frame, - sizeof(struct wi_frame)); - wi_write_data(sc, id, WI_802_11_OFFSET_RAW, - (caddr_t)&sc->wi_txbuf, (m0->m_pkthdr.len - - sizeof(struct ether_header)) + 18); - } else { - m_copydata(m0, sizeof(struct ether_header), - m0->m_pkthdr.len - sizeof(struct ether_header), - (caddr_t)&sc->wi_txbuf); - wi_write_data(sc, id, 0, (caddr_t)&tx_frame, - sizeof(struct wi_frame)); - wi_write_data(sc, id, WI_802_11_OFFSET, - (caddr_t)&sc->wi_txbuf, (m0->m_pkthdr.len - - sizeof(struct ether_header)) + 2); - } - } else { - tx_frame.wi_dat_len = m0->m_pkthdr.len; - - if (sc->wi_ptype == WI_PORTTYPE_HOSTAP && sc->wi_use_wep) { - /* Do host encryption. */ - printf( "XXX: host encrypt not implemented for 802.3\n" ); - } else { - eh->ether_type = htons(m0->m_pkthdr.len - - WI_SNAPHDR_LEN); - m_copydata(m0, 0, m0->m_pkthdr.len, - (caddr_t)&sc->wi_txbuf); - - wi_write_data(sc, id, 0, (caddr_t)&tx_frame, - sizeof(struct wi_frame)); - wi_write_data(sc, id, WI_802_3_OFFSET, - (caddr_t)&sc->wi_txbuf, m0->m_pkthdr.len + 2); + if ((sc->sc_flags & WI_FLAGS_BUG_AUTOINC) && (id & 0xf000) == 0) { + CSR_WRITE_2(sc, WI_DATA0, 0x1234); + CSR_WRITE_2(sc, WI_DATA0, 0x5678); + wi_seek_bap(sc, id, sc->sc_bap_off); + sc->sc_bap_off = WI_OFF_ERR; /* invalidate */ + if (CSR_READ_2(sc, WI_DATA0) != 0x1234 || + CSR_READ_2(sc, WI_DATA0) != 0x5678) { + device_printf(sc->sc_dev, + "detect auto increment bug, try again\n"); + goto again; } } +#endif + return 0; +} - /* - * If there's a BPF listner, bounce a copy of - * this frame to him. Also, don't send this to the bpf sniffer - * if we're in procframe or monitor sniffing mode. - */ - if (!(sc->wi_procframe || sc->wi_debug.wi_monitor)) - BPF_MTAP(ifp, m0); +static int +wi_mwrite_bap(struct wi_softc *sc, int id, int off, struct mbuf *m0, int totlen) +{ + int error, len; + struct mbuf *m; - m_freem(m0); + for (m = m0; m != NULL && totlen > 0; m = m->m_next) { + if (m->m_len == 0) + continue; - if (wi_cmd(sc, WI_CMD_TX|WI_RECLAIM, id, 0, 0)) - device_printf(sc->dev, "xmit failed\n"); + len = min(m->m_len, totlen); - ifp->if_flags |= IFF_OACTIVE; + if (((u_long)m->m_data) % 2 != 0 || len % 2 != 0) { + m_copydata(m, 0, totlen, (caddr_t)&sc->sc_txbuf); + return wi_write_bap(sc, id, off, (caddr_t)&sc->sc_txbuf, + totlen); + } - /* - * Set a timeout in case the chip goes out to lunch. - */ - ifp->if_timer = 5; + if ((error = wi_write_bap(sc, id, off, m->m_data, len)) != 0) + return error; - WI_UNLOCK(sc, s); - return; + off += m->m_len; + totlen -= len; + } + return 0; } -int -wi_mgmt_xmit(sc, data, len) - struct wi_softc *sc; - caddr_t data; - int len; +static int +wi_alloc_fid(struct wi_softc *sc, int len, int *idp) { - struct wi_frame tx_frame; - int id; - struct wi_80211_hdr *hdr; - caddr_t dptr; - - if (sc->wi_gone) - return(ENODEV); + int i; - hdr = (struct wi_80211_hdr *)data; - dptr = data + sizeof(struct wi_80211_hdr); + if (wi_cmd(sc, WI_CMD_ALLOC_MEM, len, 0, 0)) { + device_printf(sc->sc_dev, "failed to allocate %d bytes on NIC\n", + len); + return ENOMEM; + } - bzero((char *)&tx_frame, sizeof(tx_frame)); - id = sc->wi_tx_mgmt_id; + for (i = 0; i < WI_TIMEOUT; i++) { + if (CSR_READ_2(sc, WI_EVENT_STAT) & WI_EV_ALLOC) + break; + if (i == WI_TIMEOUT) { + device_printf(sc->sc_dev, "timeout in alloc\n"); + return ETIMEDOUT; + } + DELAY(1); + } + *idp = CSR_READ_2(sc, WI_ALLOC_FID); + CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_ALLOC); + return 0; +} - bcopy((char *)hdr, (char *)&tx_frame.wi_frame_ctl, - sizeof(struct wi_80211_hdr)); +static int +wi_read_rid(struct wi_softc *sc, int rid, void *buf, int *buflenp) +{ + int error, len; + u_int16_t ltbuf[2]; - tx_frame.wi_tx_ctl = WI_ENC_TX_MGMT; - tx_frame.wi_dat_len = len - sizeof(struct wi_80211_hdr); - tx_frame.wi_len = htons(tx_frame.wi_dat_len); + /* Tell the NIC to enter record read mode. */ + error = wi_cmd(sc, WI_CMD_ACCESS | WI_ACCESS_READ, rid, 0, 0); + if (error) + return error; - wi_write_data(sc, id, 0, (caddr_t)&tx_frame, sizeof(struct wi_frame)); - wi_write_data(sc, id, WI_802_11_OFFSET_RAW, dptr, - len - sizeof(struct wi_80211_hdr) + 2); + error = wi_read_bap(sc, rid, 0, ltbuf, sizeof(ltbuf)); + if (error) + return error; - if (wi_cmd(sc, WI_CMD_TX|WI_RECLAIM, id, 0, 0)) { - device_printf(sc->dev, "xmit failed\n"); - return(EIO); + if (le16toh(ltbuf[1]) != rid) { + device_printf(sc->sc_dev, "record read mismatch, rid=%x, got=%x\n", + rid, le16toh(ltbuf[1])); + return EIO; } - - return(0); + len = (le16toh(ltbuf[0]) - 1) * 2; /* already got rid */ + if (*buflenp < len) { + device_printf(sc->sc_dev, "record buffer is too small, " + "rid=%x, size=%d, len=%d\n", + rid, *buflenp, len); + return ENOSPC; + } + *buflenp = len; + return wi_read_bap(sc, rid, sizeof(ltbuf), buf, len); } -void -wi_stop(sc) - struct wi_softc *sc; +static int +wi_write_rid(struct wi_softc *sc, int rid, void *buf, int buflen) { - struct ifnet *ifp; - int s; + int error; + u_int16_t ltbuf[2]; - WI_LOCK(sc, s); + ltbuf[0] = htole16((buflen + 1) / 2 + 1); /* includes rid */ + ltbuf[1] = htole16(rid); - if (sc->wi_gone) { - WI_UNLOCK(sc, s); - return; - } + error = wi_write_bap(sc, rid, 0, ltbuf, sizeof(ltbuf)); + if (error) + return error; + error = wi_write_bap(sc, rid, sizeof(ltbuf), buf, buflen); + if (error) + return error; - wihap_shutdown(sc); + return wi_cmd(sc, WI_CMD_ACCESS | WI_ACCESS_WRITE, rid, 0, 0); +} - ifp = &sc->arpcom.ac_if; +static int +wi_newstate(void *arg, enum ieee80211_state nstate) +{ + struct wi_softc *sc = arg; + struct ieee80211com *ic = &sc->sc_ic; + struct ieee80211_node *ni = &ic->ic_bss; + int i, buflen; + u_int16_t val; + struct wi_ssid ssid; + u_int8_t old_bssid[IEEE80211_ADDR_LEN]; + enum ieee80211_state ostate; +#ifdef WI_DEBUG + static const char *stname[] = + { "INIT", "SCAN", "AUTH", "ASSOC", "RUN" }; +#endif /* WI_DEBUG */ + + ostate = ic->ic_state; + DPRINTF(("wi_newstate: %s -> %s\n", stname[ostate], stname[nstate])); + + ic->ic_state = nstate; + switch (nstate) { + case IEEE80211_S_INIT: + ic->ic_flags &= ~IEEE80211_F_SIBSS; + sc->sc_flags &= ~WI_FLAGS_OUTRANGE; + return 0; + + case IEEE80211_S_RUN: + sc->sc_flags &= ~WI_FLAGS_OUTRANGE; + buflen = IEEE80211_ADDR_LEN; + wi_read_rid(sc, WI_RID_CURRENT_BSSID, ni->ni_bssid, &buflen); + IEEE80211_ADDR_COPY(ni->ni_macaddr, ni->ni_bssid); + buflen = sizeof(val); + wi_read_rid(sc, WI_RID_CURRENT_CHAN, &val, &buflen); + ni->ni_chan = le16toh(val); + + if (IEEE80211_ADDR_EQ(old_bssid, ni->ni_bssid)) + sc->sc_false_syns++; + else + sc->sc_false_syns = 0; + + if (ic->ic_opmode == IEEE80211_M_HOSTAP) { + ni->ni_esslen = ic->ic_des_esslen; + memcpy(ni->ni_essid, ic->ic_des_essid, ni->ni_esslen); + ni->ni_nrate = 0; + for (i = 0; i < IEEE80211_RATE_SIZE; i++) { + if (ic->ic_sup_rates[i]) + ni->ni_rates[ni->ni_nrate++] = + ic->ic_sup_rates[i]; + } + ni->ni_intval = ic->ic_lintval; + ni->ni_capinfo = IEEE80211_CAPINFO_ESS; + if (ic->ic_flags & IEEE80211_F_WEPON) + ni->ni_capinfo |= IEEE80211_CAPINFO_PRIVACY; + } else { + /* XXX check return value */ + buflen = sizeof(ssid); + wi_read_rid(sc, WI_RID_CURRENT_SSID, &ssid, &buflen); + ni->ni_esslen = le16toh(ssid.wi_len); + if (ni->ni_esslen > IEEE80211_NWID_LEN) + ni->ni_esslen = IEEE80211_NWID_LEN; /*XXX*/ + memcpy(ni->ni_essid, ssid.wi_ssid, ni->ni_esslen); + } + break; - /* - * If the card is gone and the memory port isn't mapped, we will - * (hopefully) get 0xffff back from the status read, which is not - * a valid status value. - */ - if (CSR_READ_2(sc, WI_STATUS) != 0xffff) { - CSR_WRITE_2(sc, WI_INT_EN, 0); - wi_cmd(sc, WI_CMD_DISABLE|sc->wi_portnum, 0, 0, 0); + case IEEE80211_S_SCAN: + case IEEE80211_S_AUTH: + case IEEE80211_S_ASSOC: + break; } - untimeout(wi_inquire, sc, sc->wi_stat_ch); + /* skip standard ieee80211 handling */ + return EINPROGRESS; +} - ifp->if_flags &= ~(IFF_RUNNING|IFF_OACTIVE); +static int +wi_scan_ap(struct wi_softc *sc) +{ + int error = 0; + u_int16_t val[2]; - WI_UNLOCK(sc, s); - return; + if (!sc->sc_enabled) + return ENXIO; + switch (sc->sc_firmware_type) { + case WI_LUCENT: + (void)wi_cmd(sc, WI_CMD_INQUIRE, WI_INFO_SCAN_RESULTS, 0, 0); + break; + case WI_INTERSIL: + val[0] = 0x3fff; /* channel */ + val[1] = 0x000f; /* tx rate */ + error = wi_write_rid(sc, WI_RID_SCAN_REQ, val, sizeof(val)); + break; + case WI_SYMBOL: + /* + * XXX only supported on 3.x ? + */ + val[0] = BSCAN_BCAST | BSCAN_ONETIME; + error = wi_write_rid(sc, WI_RID_BCAST_SCAN_REQ, + val, sizeof(val[0])); + break; + } + if (error == 0) { + sc->sc_scan_timer = WI_SCAN_WAIT; + sc->sc_ic.ic_if.if_timer = 1; + DPRINTF(("wi_scan_ap: start scanning\n")); + } + return error; } static void -wi_watchdog(ifp) - struct ifnet *ifp; +wi_scan_result(struct wi_softc *sc, int fid, int cnt) { - struct wi_softc *sc; - - sc = ifp->if_softc; - - device_printf(sc->dev, "watchdog timeout\n"); - - wi_init(sc); - - ifp->if_oerrors++; +#define N(a) (sizeof (a) / sizeof (a[0])) + int i, naps, off, szbuf; + struct wi_scan_header ws_hdr; /* Prism2 header */ + struct wi_scan_data_p2 ws_dat; /* Prism2 scantable*/ + struct wi_apinfo *ap; + + off = sizeof(u_int16_t) * 2; + memset(&ws_hdr, 0, sizeof(ws_hdr)); + switch (sc->sc_firmware_type) { + case WI_INTERSIL: + wi_read_bap(sc, fid, off, &ws_hdr, sizeof(ws_hdr)); + off += sizeof(ws_hdr); + szbuf = sizeof(struct wi_scan_data_p2); + break; + case WI_SYMBOL: + szbuf = sizeof(struct wi_scan_data_p2) + 6; + break; + case WI_LUCENT: + szbuf = sizeof(struct wi_scan_data); + break; + default: + device_printf(sc->sc_dev, + "wi_scan_result: unknown firmware type %u\n", + sc->sc_firmware_type); + naps = 0; + goto done; + } + naps = (cnt * 2 + 2 - off) / szbuf; + if (naps > N(sc->sc_aps)) + naps = N(sc->sc_aps); + sc->sc_naps = naps; + /* Read Data */ + ap = sc->sc_aps; + memset(&ws_dat, 0, sizeof(ws_dat)); + for (i = 0; i < naps; i++, ap++) { + wi_read_bap(sc, fid, off, &ws_dat, + (sizeof(ws_dat) < szbuf ? sizeof(ws_dat) : szbuf)); + DPRINTF2(("wi_scan_result: #%d: off %d bssid %s\n", i, off, + ether_sprintf(ws_dat.wi_bssid))); + off += szbuf; + ap->scanreason = le16toh(ws_hdr.wi_reason); + memcpy(ap->bssid, ws_dat.wi_bssid, sizeof(ap->bssid)); + ap->channel = le16toh(ws_dat.wi_chid); + ap->signal = le16toh(ws_dat.wi_signal); + ap->noise = le16toh(ws_dat.wi_noise); + ap->quality = ap->signal - ap->noise; + ap->capinfo = le16toh(ws_dat.wi_capinfo); + ap->interval = le16toh(ws_dat.wi_interval); + ap->rate = le16toh(ws_dat.wi_rate); + ap->namelen = le16toh(ws_dat.wi_namelen); + if (ap->namelen > sizeof(ap->name)) + ap->namelen = sizeof(ap->name); + memcpy(ap->name, ws_dat.wi_name, ap->namelen); + } +done: + /* Done scanning */ + sc->sc_scan_timer = 0; + DPRINTF(("wi_scan_result: scan complete: ap %d\n", naps)); +#undef N +} - return; +static void +wi_dump_pkt(struct wi_frame *wh, struct ieee80211_node *ni, int rssi) +{ + ieee80211_dump_pkt((u_int8_t *) &wh->wi_whdr, sizeof(wh->wi_whdr), + ni ? ni->ni_rates[ni->ni_txrate] & IEEE80211_RATE_VAL : -1, rssi); + printf(" status 0x%x rx_tstamp1 %u rx_tstamp0 0x%u rx_silence %u\n", + le16toh(wh->wi_status), le16toh(wh->wi_rx_tstamp1), + le16toh(wh->wi_rx_tstamp0), wh->wi_rx_silence); + printf(" rx_signal %u rx_rate %u rx_flow %u\n", + wh->wi_rx_signal, wh->wi_rx_rate, wh->wi_rx_flow); + printf(" tx_rtry %u tx_rate %u tx_ctl 0x%x dat_len %u\n", + wh->wi_tx_rtry, wh->wi_tx_rate, + le16toh(wh->wi_tx_ctl), le16toh(wh->wi_dat_len)); + printf(" ehdr dst %6D src %6D type 0x%x\n", + wh->wi_ehdr.ether_dhost, ":", wh->wi_ehdr.ether_shost, ":", + wh->wi_ehdr.ether_type); } int -wi_alloc(dev, rid) - device_t dev; - int rid; +wi_alloc(device_t dev, int rid) { - struct wi_softc *sc = device_get_softc(dev); + struct wi_softc *sc = device_get_softc(dev); if (sc->wi_bus_type != WI_BUS_PCI_NATIVE) { sc->iobase_rid = rid; @@ -2557,17 +2636,16 @@ wi_alloc(dev, rid) return (ENXIO); } - sc->dev = dev; - sc->wi_unit = device_get_unit(dev); + sc->sc_dev = dev; + sc->sc_unit = device_get_unit(dev); return (0); } void -wi_free(dev) - device_t dev; +wi_free(device_t dev) { - struct wi_softc *sc = device_get_softc(dev); + struct wi_softc *sc = device_get_softc(dev); if (sc->iobase != NULL) { bus_release_resource(dev, SYS_RES_IOPORT, sc->iobase_rid, sc->iobase); @@ -2585,430 +2663,10 @@ wi_free(dev) return; } -void -wi_shutdown(dev) - device_t dev; -{ - struct wi_softc *sc; - - sc = device_get_softc(dev); - wi_stop(sc); - - return; -} - -#ifdef WICACHE -/* wavelan signal strength cache code. - * store signal/noise/quality on per MAC src basis in - * a small fixed cache. The cache wraps if > MAX slots - * used. The cache may be zeroed out to start over. - * Two simple filters exist to reduce computation: - * 1. ip only (literally 0x800) which may be used - * to ignore some packets. It defaults to ip only. - * it could be used to focus on broadcast, non-IP 802.11 beacons. - * 2. multicast/broadcast only. This may be used to - * ignore unicast packets and only cache signal strength - * for multicast/broadcast packets (beacons); e.g., Mobile-IP - * beacons and not unicast traffic. - * - * The cache stores (MAC src(index), IP src (major clue), signal, - * quality, noise) - * - * No apologies for storing IP src here. It's easy and saves much - * trouble elsewhere. The cache is assumed to be INET dependent, - * although it need not be. - */ - -#ifdef documentation - -int wi_sigitems; /* number of cached entries */ -struct wi_sigcache wi_sigcache[MAXWICACHE]; /* array of cache entries */ -int wi_nextitem; /* index/# of entries */ - - -#endif - -/* control variables for cache filtering. Basic idea is - * to reduce cost (e.g., to only Mobile-IP agent beacons - * which are broadcast or multicast). Still you might - * want to measure signal strength with unicast ping packets - * on a pt. to pt. ant. setup. - */ -/* set true if you want to limit cache items to broadcast/mcast - * only packets (not unicast). Useful for mobile-ip beacons which - * are broadcast/multicast at network layer. Default is all packets - * so ping/unicast will work say with pt. to pt. antennae setup. - */ -static int wi_cache_mcastonly = 0; -SYSCTL_INT(_machdep, OID_AUTO, wi_cache_mcastonly, CTLFLAG_RW, - &wi_cache_mcastonly, 0, ""); - -/* set true if you want to limit cache items to IP packets only -*/ -static int wi_cache_iponly = 1; -SYSCTL_INT(_machdep, OID_AUTO, wi_cache_iponly, CTLFLAG_RW, - &wi_cache_iponly, 0, ""); - -/* - * Original comments: - * ----------------- - * wi_cache_store, per rx packet store signal - * strength in MAC (src) indexed cache. - * - * follows linux driver in how signal strength is computed. - * In ad hoc mode, we use the rx_quality field. - * signal and noise are trimmed to fit in the range from 47..138. - * rx_quality field MSB is signal strength. - * rx_quality field LSB is noise. - * "quality" is (signal - noise) as is log value. - * note: quality CAN be negative. - * - * In BSS mode, we use the RID for communication quality. - * TBD: BSS mode is currently untested. - * - * Bill's comments: - * --------------- - * Actually, we use the rx_quality field all the time for both "ad-hoc" - * and BSS modes. Why? Because reading an RID is really, really expensive: - * there's a bunch of PIO operations that have to be done to read a record - * from the NIC, and reading the comms quality RID each time a packet is - * received can really hurt performance. We don't have to do this anyway: - * the comms quality field only reflects the values in the rx_quality field - * anyway. The comms quality RID is only meaningful in infrastructure mode, - * but the values it contains are updated based on the rx_quality from - * frames received from the access point. - * - * Also, according to Lucent, the signal strength and noise level values - * can be converted to dBms by subtracting 149, so I've modified the code - * to do that instead of the scaling it did originally. - */ -static void -wi_cache_store(struct wi_softc *sc, struct ether_header *eh, - struct mbuf *m, unsigned short rx_quality) -{ - struct ip *ip = 0; - int i; - static int cache_slot = 0; /* use this cache entry */ - static int wrapindex = 0; /* next "free" cache entry */ - int sig, noise; - int sawip=0; - - /* - * filters: - * 1. ip only - * 2. configurable filter to throw out unicast packets, - * keep multicast only. - */ - - if ((ntohs(eh->ether_type) == ETHERTYPE_IP)) { - sawip = 1; - } - - /* - * filter for ip packets only - */ - if (wi_cache_iponly && !sawip) { - return; - } - - /* - * filter for broadcast/multicast only - */ - if (wi_cache_mcastonly && ((eh->ether_dhost[0] & 1) == 0)) { - return; - } - -#ifdef SIGDEBUG - printf("wi%d: q value %x (MSB=0x%x, LSB=0x%x) \n", sc->wi_unit, - rx_quality & 0xffff, rx_quality >> 8, rx_quality & 0xff); -#endif - - /* - * find the ip header. we want to store the ip_src - * address. - */ - if (sawip) - ip = mtod(m, struct ip *); - - /* - * do a linear search for a matching MAC address - * in the cache table - * . MAC address is 6 bytes, - * . var w_nextitem holds total number of entries already cached - */ - for(i = 0; i < sc->wi_nextitem; i++) { - if (! bcmp(eh->ether_shost , sc->wi_sigcache[i].macsrc, 6 )) { - /* - * Match!, - * so we already have this entry, - * update the data - */ - break; - } - } - - /* - * did we find a matching mac address? - * if yes, then overwrite a previously existing cache entry - */ - if (i < sc->wi_nextitem ) { - cache_slot = i; - } - /* - * else, have a new address entry,so - * add this new entry, - * if table full, then we need to replace LRU entry - */ - else { - - /* - * check for space in cache table - * note: wi_nextitem also holds number of entries - * added in the cache table - */ - if ( sc->wi_nextitem < MAXWICACHE ) { - cache_slot = sc->wi_nextitem; - sc->wi_nextitem++; - sc->wi_sigitems = sc->wi_nextitem; - } - /* no space found, so simply wrap with wrap index - * and "zap" the next entry - */ - else { - if (wrapindex == MAXWICACHE) { - wrapindex = 0; - } - cache_slot = wrapindex++; - } - } - - /* - * invariant: cache_slot now points at some slot - * in cache. - */ - if (cache_slot < 0 || cache_slot >= MAXWICACHE) { - log(LOG_ERR, "wi_cache_store, bad index: %d of " - "[0..%d], gross cache error\n", - cache_slot, MAXWICACHE); - return; - } - - /* - * store items in cache - * .ip source address - * .mac src - * .signal, etc. - */ - if (sawip) - sc->wi_sigcache[cache_slot].ipsrc = ip->ip_src.s_addr; - bcopy( eh->ether_shost, sc->wi_sigcache[cache_slot].macsrc, 6); - - sig = (rx_quality >> 8) & 0xFF; - noise = rx_quality & 0xFF; - - /* - * -149 is Lucent specific to convert to dBm. Prism2 cards do - * things differently, sometimes don't have a noise measurement, - * and is firmware dependent :-( - */ - sc->wi_sigcache[cache_slot].signal = sig - 149; - sc->wi_sigcache[cache_slot].noise = noise - 149; - sc->wi_sigcache[cache_slot].quality = sig - noise; - - return; -} -#endif - -static int -wi_get_cur_ssid(sc, ssid, len) - struct wi_softc *sc; - char *ssid; - int *len; -{ - int error = 0; - struct wi_req wreq; - - wreq.wi_len = WI_MAX_DATALEN; - switch (sc->wi_ptype) { - case WI_PORTTYPE_HOSTAP: - *len = IEEE80211_NWID_LEN; - bcopy(sc->wi_net_name, ssid, IEEE80211_NWID_LEN); - break; - case WI_PORTTYPE_IBSS: - case WI_PORTTYPE_ADHOC: - wreq.wi_type = WI_RID_CURRENT_SSID; - error = wi_read_record(sc, (struct wi_ltv_gen *)&wreq); - if (error != 0) - break; - if (wreq.wi_val[0] > IEEE80211_NWID_LEN) { - error = EINVAL; - break; - } - *len = wreq.wi_val[0]; - bcopy(&wreq.wi_val[1], ssid, IEEE80211_NWID_LEN); - break; - case WI_PORTTYPE_BSS: - wreq.wi_type = WI_RID_COMMQUAL; - error = wi_read_record(sc, (struct wi_ltv_gen *)&wreq); - if (error != 0) - break; - if (wreq.wi_val[0] != 0) /* associated */ { - wreq.wi_type = WI_RID_CURRENT_SSID; - wreq.wi_len = WI_MAX_DATALEN; - error = wi_read_record(sc, (struct wi_ltv_gen *)&wreq); - if (error != 0) - break; - if (wreq.wi_val[0] > IEEE80211_NWID_LEN) { - error = EINVAL; - break; - } - *len = wreq.wi_val[0]; - bcopy(&wreq.wi_val[1], ssid, IEEE80211_NWID_LEN); - } else { - *len = IEEE80211_NWID_LEN; - bcopy(sc->wi_net_name, ssid, IEEE80211_NWID_LEN); - } - break; - default: - error = EINVAL; - break; - } - - return error; -} - -static int -wi_media_change(ifp) - struct ifnet *ifp; -{ - struct wi_softc *sc = ifp->if_softc; - int otype = sc->wi_ptype; - int orate = sc->wi_tx_rate; - int ocreate_ibss = sc->wi_create_ibss; - - if ((sc->ifmedia.ifm_cur->ifm_media & IFM_IEEE80211_HOSTAP) && - sc->sc_firmware_type != WI_INTERSIL) - return (EINVAL); - - sc->wi_create_ibss = 0; - - switch (sc->ifmedia.ifm_cur->ifm_media & IFM_OMASK) { - case 0: - sc->wi_ptype = WI_PORTTYPE_BSS; - break; - case IFM_IEEE80211_ADHOC: - sc->wi_ptype = WI_PORTTYPE_ADHOC; - break; - case IFM_IEEE80211_HOSTAP: - sc->wi_ptype = WI_PORTTYPE_HOSTAP; - break; - case IFM_IEEE80211_IBSSMASTER: - case IFM_IEEE80211_IBSSMASTER|IFM_IEEE80211_IBSS: - if (!(sc->wi_flags & WI_FLAGS_HAS_CREATE_IBSS)) - return (EINVAL); - sc->wi_create_ibss = 1; - /* FALLTHROUGH */ - case IFM_IEEE80211_IBSS: - sc->wi_ptype = WI_PORTTYPE_IBSS; - break; - default: - /* Invalid combination. */ - return (EINVAL); - } - - switch (IFM_SUBTYPE(sc->ifmedia.ifm_cur->ifm_media)) { - case IFM_IEEE80211_DS1: - sc->wi_tx_rate = 1; - break; - case IFM_IEEE80211_DS2: - sc->wi_tx_rate = 2; - break; - case IFM_IEEE80211_DS5: - sc->wi_tx_rate = 5; - break; - case IFM_IEEE80211_DS11: - sc->wi_tx_rate = 11; - break; - case IFM_AUTO: - sc->wi_tx_rate = 3; - break; - } - - if (ocreate_ibss != sc->wi_create_ibss || otype != sc->wi_ptype || - orate != sc->wi_tx_rate) - wi_init(sc); - - return(0); -} - -static void -wi_media_status(ifp, imr) - struct ifnet *ifp; - struct ifmediareq *imr; -{ - struct wi_req wreq; - struct wi_softc *sc = ifp->if_softc; - - if (sc->wi_tx_rate == 3) { - imr->ifm_active = IFM_IEEE80211|IFM_AUTO; - if (sc->wi_ptype == WI_PORTTYPE_ADHOC) - imr->ifm_active |= IFM_IEEE80211_ADHOC; - else if (sc->wi_ptype == WI_PORTTYPE_HOSTAP) - imr->ifm_active |= IFM_IEEE80211_HOSTAP; - else if (sc->wi_ptype == WI_PORTTYPE_IBSS) { - if (sc->wi_create_ibss) - imr->ifm_active |= IFM_IEEE80211_IBSSMASTER; - else - imr->ifm_active |= IFM_IEEE80211_IBSS; - } - wreq.wi_type = WI_RID_CUR_TX_RATE; - wreq.wi_len = WI_MAX_DATALEN; - if (wi_read_record(sc, (struct wi_ltv_gen *)&wreq) == 0) { - switch(wreq.wi_val[0]) { - case 1: - imr->ifm_active |= IFM_IEEE80211_DS1; - break; - case 2: - imr->ifm_active |= IFM_IEEE80211_DS2; - break; - case 6: - imr->ifm_active |= IFM_IEEE80211_DS5; - break; - case 11: - imr->ifm_active |= IFM_IEEE80211_DS11; - break; - } - } - } else { - imr->ifm_active = sc->ifmedia.ifm_cur->ifm_media; - } - - imr->ifm_status = IFM_AVALID; - if (sc->wi_ptype == WI_PORTTYPE_ADHOC || - sc->wi_ptype == WI_PORTTYPE_IBSS) - /* - * XXX: It would be nice if we could give some actually - * useful status like whether we joined another IBSS or - * created one ourselves. - */ - imr->ifm_status |= IFM_ACTIVE; - else if (sc->wi_ptype == WI_PORTTYPE_HOSTAP) - imr->ifm_status |= IFM_ACTIVE; - else { - wreq.wi_type = WI_RID_COMMQUAL; - wreq.wi_len = WI_MAX_DATALEN; - if (wi_read_record(sc, (struct wi_ltv_gen *)&wreq) == 0 && - wreq.wi_val[0] != 0) - imr->ifm_status |= IFM_ACTIVE; - } -} - static int -wi_get_debug(sc, wreq) - struct wi_softc *sc; - struct wi_req *wreq; +wi_get_debug(struct wi_softc *sc, struct wi_req *wreq) { - int error = 0; + int error = 0; wreq->wi_len = 1; @@ -3063,11 +2721,9 @@ wi_get_debug(sc, wreq) } static int -wi_set_debug(sc, wreq) - struct wi_softc *sc; - struct wi_req *wreq; +wi_set_debug(struct wi_softc *sc, struct wi_req *wreq) { - int error = 0; + int error = 0; u_int16_t cmd, param0 = 0, param1 = 0; switch (wreq->wi_type) { diff --git a/sys/dev/wi/if_wi_pccard.c b/sys/dev/wi/if_wi_pccard.c index 0e21691..504b7b6 100644 --- a/sys/dev/wi/if_wi_pccard.c +++ b/sys/dev/wi/if_wi_pccard.c @@ -65,7 +65,6 @@ #endif #include <dev/wi/if_wavelan_ieee.h> -#include <dev/wi/wi_hostap.h> #include <dev/wi/if_wivar.h> #include <dev/wi/if_wireg.h> #ifdef WI_SYMBOL_FIRMWARE @@ -87,7 +86,7 @@ static device_method_t wi_pccard_methods[] = { /* Device interface */ DEVMETHOD(device_probe, wi_pccard_probe), DEVMETHOD(device_attach, wi_pccard_attach), - DEVMETHOD(device_detach, wi_generic_detach), + DEVMETHOD(device_detach, wi_detach), DEVMETHOD(device_shutdown, wi_shutdown), { 0, 0 } @@ -100,7 +99,7 @@ static device_method_t wi_pccard_methods[] = { /* Device interface */ DEVMETHOD(device_probe, pccard_compat_probe), DEVMETHOD(device_attach, pccard_compat_attach), - DEVMETHOD(device_detach, wi_generic_detach), + DEVMETHOD(device_detach, wi_detach), DEVMETHOD(device_shutdown, wi_shutdown), /* Card interface */ @@ -120,6 +119,7 @@ static driver_t wi_pccard_driver = { }; DRIVER_MODULE(if_wi, pccard, wi_pccard_driver, wi_devclass, 0, 0); +MODULE_DEPEND(if_wi, wlan, 1, 1, 1); #if __FreeBSD_version >= 500000 static const struct pccard_product wi_pccard_products[] = { @@ -245,5 +245,5 @@ wi_pccard_attach(device_t dev) } #endif - return (wi_generic_attach(dev)); + return (wi_attach(dev)); } diff --git a/sys/dev/wi/if_wi_pci.c b/sys/dev/wi/if_wi_pci.c index 7dd2536..ce8df8d 100644 --- a/sys/dev/wi/if_wi_pci.c +++ b/sys/dev/wi/if_wi_pci.c @@ -63,7 +63,6 @@ #include <net/if_ieee80211.h> #include <dev/wi/if_wavelan_ieee.h> -#include <dev/wi/wi_hostap.h> #include <dev/wi/if_wivar.h> #include <dev/wi/if_wireg.h> @@ -76,7 +75,7 @@ static device_method_t wi_pci_methods[] = { /* Device interface */ DEVMETHOD(device_probe, wi_pci_probe), DEVMETHOD(device_attach, wi_pci_attach), - DEVMETHOD(device_detach, wi_generic_detach), + DEVMETHOD(device_detach, wi_detach), DEVMETHOD(device_shutdown, wi_shutdown), DEVMETHOD(device_suspend, wi_pci_suspend), DEVMETHOD(device_resume, wi_pci_resume), @@ -108,6 +107,7 @@ static struct { }; DRIVER_MODULE(if_wi, pci, wi_pci_driver, wi_devclass, 0, 0); +MODULE_DEPEND(if_wi, wlan, 1, 1, 1); static int wi_pci_probe(dev) @@ -231,7 +231,7 @@ wi_pci_attach(device_t dev) return (ENXIO); } - error = wi_generic_attach(dev); + error = wi_attach(dev); if (error != 0) return (error); @@ -239,23 +239,25 @@ wi_pci_attach(device_t dev) } static int -wi_pci_suspend (device_t dev) +wi_pci_suspend(device_t dev) { struct wi_softc *sc; + struct ifnet *ifp; sc = device_get_softc(dev); + ifp = &sc->sc_if; - wi_stop(sc); + wi_stop(ifp, 1); return (0); } static int -wi_pci_resume (device_t dev) +wi_pci_resume(device_t dev) { struct wi_softc *sc; struct ifnet *ifp; sc = device_get_softc(dev); - ifp = &sc->arpcom.ac_if; + ifp = &sc->sc_if; if (sc->wi_bus_type != WI_BUS_PCI_NATIVE) return (0); diff --git a/sys/dev/wi/if_wireg.h b/sys/dev/wi/if_wireg.h index ce7a0e3..a415405 100644 --- a/sys/dev/wi/if_wireg.h +++ b/sys/dev/wi/if_wireg.h @@ -35,12 +35,12 @@ #define WI_DELAY 5 #define WI_TIMEOUT (500000/WI_DELAY) /* 500 ms */ -#define WI_PORT0 0 -#define WI_PORT1 1 -#define WI_PORT2 2 -#define WI_PORT3 3 -#define WI_PORT4 4 -#define WI_PORT5 5 +#define WI_PORT0 (0 << 8) +#define WI_PORT1 (1 << 8) +#define WI_PORT2 (2 << 8) +#define WI_PORT3 (3 << 8) +#define WI_PORT4 (4 << 8) +#define WI_PORT5 (5 << 8) #define WI_PCI_LMEMRES 0x10 /* PCI Memory (native PCI implementations) */ #define WI_PCI_LOCALRES 0x14 /* The PLX chip's local registers */ @@ -54,10 +54,7 @@ #define WI_HFA384X_PCICOR_OFF 0x26 /* Default port: 0 (only 0 exists on stations) */ -#define WI_DEFAULT_PORT (WI_PORT0 << 8) - -/* Default TX rate: 2Mbps, auto fallback */ -#define WI_DEFAULT_TX_RATE 3 +#define WI_DEFAULT_PORT WI_PORT0 /* Default network name: ANY */ /* @@ -97,8 +94,6 @@ #define WI_DEFAULT_IBSS OS_STRING_NAME " IBSS" -#define WI_DEFAULT_CHAN 3 - #define WI_BUS_PCCARD 0 /* pccard device */ #define WI_BUS_PCI_PLX 1 /* PCI card w/ PLX PCI/PCMICA bridge */ #define WI_BUS_PCI_NATIVE 2 /* native PCI device (Prism 2.5) */ @@ -302,9 +297,6 @@ #define WI_EV_TX 0x0002 /* async xmit completed succesfully */ #define WI_EV_RX 0x0001 /* async rx completed */ -#define WI_INTRS \ - (WI_EV_RX|WI_EV_TX|WI_EV_TX_EXC|WI_EV_ALLOC|WI_EV_INFO|WI_EV_INFO_DROP) - /* Host software registers */ #define WI_SW0 0x28 #define WI_SW1 0x2A @@ -347,48 +339,16 @@ * and type are 16 bits and are in native byte order. The value is in * multiples of 16 bits and is in little endian byte order. */ -struct wi_ltv_gen { - u_int16_t wi_len; - u_int16_t wi_type; - u_int16_t wi_val; -}; - -struct wi_ltv_str { +struct wi_lt_hdr { u_int16_t wi_len; u_int16_t wi_type; - u_int16_t wi_str[17]; + /* value is vary depends on resource id */ }; -#define WI_SETVAL(recno, val) \ - do { \ - struct wi_ltv_gen g; \ - \ - g.wi_len = 2; \ - g.wi_type = recno; \ - g.wi_val = htole16(val); \ - wi_write_record(sc, &g); \ - } while (0) - -#define WI_SETSTR(recno, str) \ - do { \ - struct wi_ltv_str s; \ - int l; \ - \ - l = (strlen(str) + 1) & ~0x1; \ - bzero((char *)&s, sizeof(s)); \ - s.wi_len = (l / 2) + 2; \ - s.wi_type = recno; \ - s.wi_str[0] = htole16(strlen(str)); \ - bcopy(str, (char *)&s.wi_str[1], strlen(str)); \ - wi_write_record(sc, (struct wi_ltv_gen *)&s); \ - } while (0) - /* * Download buffer location and length (0xFD01). */ -struct wi_ltv_dnld_buf { - u_int16_t wi_len; - u_int16_t wi_type; +struct wi_dnld_buf { u_int16_t wi_buf_pg; /* page addr of intermediate dl buf*/ u_int16_t wi_buf_off; /* offset of idb */ u_int16_t wi_buf_len; /* len of idb */ @@ -397,9 +357,7 @@ struct wi_ltv_dnld_buf { /* * Mem sizes (0xFD02). */ -struct wi_ltv_memsz { - u_int16_t wi_len; - u_int16_t wi_type; +struct wi_memsz { u_int16_t wi_mem_ram; u_int16_t wi_mem_nvram; }; @@ -407,9 +365,7 @@ struct wi_ltv_memsz { /* * NIC Identification (0xFD0B, 0xFD20) */ -struct wi_ltv_ver { - u_int16_t wi_len; - u_int16_t wi_type; +struct wi_ver { u_int16_t wi_ver[4]; }; @@ -493,18 +449,14 @@ struct wi_ltv_domains { /* * CIS struct (0xFD13). */ -struct wi_ltv_cis { - u_int16_t wi_len; - u_int16_t wi_type; +struct wi_cis { u_int16_t wi_cis[240]; }; /* * Communications quality (0xFD43). */ -struct wi_ltv_commqual { - u_int16_t wi_len; - u_int16_t wi_type; +struct wi_commqual { u_int16_t wi_coms_qual; u_int16_t wi_sig_lvl; u_int16_t wi_noise_lvl; @@ -513,9 +465,7 @@ struct wi_ltv_commqual { /* * Actual system scale thresholds (0xFC06, 0xFD46). */ -struct wi_ltv_scalethresh { - u_int16_t wi_len; - u_int16_t wi_type; +struct wi_scalethresh { u_int16_t wi_energy_detect; u_int16_t wi_carrier_detect; u_int16_t wi_defer; @@ -527,14 +477,10 @@ struct wi_ltv_scalethresh { /* * PCF info struct (0xFD87). */ -struct wi_ltv_pcf { - u_int16_t wi_len; - u_int16_t wi_type; - u_int16_t wi_energy_detect; - u_int16_t wi_carrier_detect; - u_int16_t wi_defer; - u_int16_t wi_cell_search; - u_int16_t wi_range; +struct wi_pcf { + u_int16_t wi_medium_occupancy_limit; + u_int16_t wi_cfp_period; + u_int16_t wi_cfp_max_duration; }; /* @@ -546,50 +492,52 @@ struct wi_ltv_pcf { * (Only PRISM2; not 802.11 compliant mode, testing use only) * 6 == HOST AP (Only PRISM2) */ +#define WI_PORTTYPE_IBSS 0x0 #define WI_PORTTYPE_BSS 0x1 #define WI_PORTTYPE_WDS 0x2 #define WI_PORTTYPE_ADHOC 0x3 -#define WI_PORTTYPE_IBSS 0x4 -#define WI_PORTTYPE_HOSTAP 0x6 +#define WI_PORTTYPE_HOSTAP 0x6 /* * Mac addresses. (0xFC01, 0xFC08) */ -struct wi_ltv_macaddr { - u_int16_t wi_len; - u_int16_t wi_type; - u_int16_t wi_mac_addr[3]; +struct wi_macaddr { + u_int8_t wi_mac_addr[6]; }; /* * Station set identification (SSID). (0xFC02, 0xFC04) */ -struct wi_ltv_ssid { +struct wi_ssid { u_int16_t wi_len; - u_int16_t wi_type; - u_int16_t wi_id[17]; + u_int8_t wi_ssid[32]; }; /* * Set our station name. (0xFC0E) */ -struct wi_ltv_nodename { - u_int16_t wi_len; - u_int16_t wi_type; - u_int16_t wi_nodename[17]; +struct wi_nodename { + u_int16_t wi_nodelen; + u_int8_t wi_nodename[32]; }; /* * Multicast addresses to be put in filter. We're * allowed up to 16 addresses in the filter. (0xFC80) */ -struct wi_ltv_mcast { - u_int16_t wi_len; - u_int16_t wi_type; +struct wi_mcast { struct ether_addr wi_mcast[16]; }; /* + * Join request. (0xFCE2) + */ +struct wi_joinreq { + struct ether_addr wi_bssid; + u_int16_t wi_chan; +}; + +/* * supported rates. (0xFCB4) */ #define WI_SUPPRATES_1M 0x0001 @@ -604,78 +552,127 @@ struct wi_ltv_mcast { #define WI_INFO_NOTIFY 0xF000 /* Handover address */ #define WI_INFO_COUNTERS 0xF100 /* Statistics counters */ #define WI_INFO_SCAN_RESULTS 0xF101 /* Scan results */ +#define WI_INFO_HOST_SCAN_RESULTS 0xF104 /* Scan results */ #define WI_INFO_LINK_STAT 0xF200 /* Link status */ +#define WI_INFO_LINK_STAT_CONNECTED 1 +#define WI_INFO_LINK_STAT_DISCONNECTED 2 +#define WI_INFO_LINK_STAT_AP_CHG 3 /* AP Change */ +#define WI_INFO_LINK_STAT_AP_OOR 4 /* AP Out Of Range */ +#define WI_INFO_LINK_STAT_AP_INR 5 /* AP In Range */ +#define WI_INFO_LINK_STAT_ASSOC_FAILED 6 #define WI_INFO_ASSOC_STAT 0xF201 /* Association status */ +#define WI_INFO_AUTH_REQUEST 0xF202 /* Authentication Request (AP) */ +#define WI_INFO_POWERSAVE_COUNT 0xF203 /* PowerSave User Count (AP) */ + +struct wi_assoc { + u_int16_t wi_assoc_stat; /* Association Status */ +#define ASSOC 1 +#define REASSOC 2 +#define DISASSOC 3 +#define ASSOCFAIL 4 +#define AUTHFAIL 5 + u_int8_t wi_assoc_sta[6]; /* Station Address */ + u_int8_t wi_assoc_osta[6]; /* OLD Station Address */ + u_int16_t wi_assoc_reason; /* Reason */ + u_int16_t wi_assoc_reserve; /* Reserved */ +}; /* - * Hermes transmit/receive frame structure + * Scan Results of Prism2 chip */ -struct wi_frame { - u_int16_t wi_status; /* 0x00 */ - u_int16_t wi_rsvd0; /* 0x02 */ - u_int16_t wi_rsvd1; /* 0x04 */ - u_int16_t wi_q_info; /* 0x06 */ - u_int16_t wi_rsvd2; /* 0x08 */ - u_int8_t wi_tx_rtry; /* 0x0A */ - u_int8_t wi_tx_rate; /* 0x0B */ - u_int16_t wi_tx_ctl; /* 0x0C */ - u_int16_t wi_frame_ctl; /* 0x0E */ - u_int16_t wi_id; /* 0x10 */ - u_int8_t wi_addr1[6]; /* 0x12 */ - u_int8_t wi_addr2[6]; /* 0x18 */ - u_int8_t wi_addr3[6]; /* 0x1E */ - u_int16_t wi_seq_ctl; /* 0x24 */ - u_int8_t wi_addr4[6]; /* 0x26 */ - u_int16_t wi_dat_len; /* 0x2C */ - u_int8_t wi_dst_addr[6]; /* 0x2E */ - u_int8_t wi_src_addr[6]; /* 0x34 */ - u_int16_t wi_len; /* 0x3A */ - u_int16_t wi_dat[3]; /* 0x3C */ /* SNAP header */ - u_int16_t wi_type; /* 0x42 */ + +struct wi_scan_header { + u_int16_t wi_reserve; /* future use */ + u_int16_t wi_reason; /* The reason this scan was initiated + 1: Host initiated + 2: Firmware initiated + 3: Inquiry request from host */ }; -#define WI_802_3_OFFSET 0x2E -#define WI_802_11_OFFSET 0x44 -#define WI_802_11_OFFSET_RAW 0x3C -#define WI_802_11_OFFSET_HDR 0x0E +struct wi_scan_data_p2 { + u_int16_t wi_chid; /* BSS Channel ID from Probe Res.(PR)*/ + u_int16_t wi_noise; /* Average Noise Level of the PR */ + u_int16_t wi_signal; /* Signal Level on the PR */ + u_int8_t wi_bssid[6]; /* MACaddress of BSS responder from PR */ + u_int16_t wi_interval; /* BSS beacon interval */ + u_int16_t wi_capinfo; /* BSS Capability Information + IEEE Std 802.11(1997) ,see 7.3.1.4 */ + u_int16_t wi_namelen; /* Length of SSID strings */ + u_int8_t wi_name[32]; /* SSID strings */ + u_int16_t wi_suprate[5]; /* Supported Rates element from the PR + IEEE Std 802.11(1997) ,see 7.3.2.2 */ + u_int16_t wi_rate; /* Data rate of the PR */ +#define WI_APRATE_1 0x0A /* 1 Mbps */ +#define WI_APRATE_2 0x14 /* 2 Mbps */ +#define WI_APRATE_5 0x37 /* 5.5 Mbps */ +#define WI_APRATE_11 0x6E /* 11 Mbps */ +}; + +/* + * Scan Results of Lucent chip + */ +struct wi_scan_data { + u_int16_t wi_chid; /* BSS Channel ID from PR */ + u_int16_t wi_noise; /* Average Noise Level of the PR */ + u_int16_t wi_signal; /* Signal Level on the PR */ + u_int8_t wi_bssid[6]; /* MACaddress of BSS responder from PR */ + u_int16_t wi_interval; /* BSS beacon interval */ + u_int16_t wi_capinfo; /* BSS Capability Information + IEEE Std 802.11(1997) ,see 7.3.1.4 */ + u_int16_t wi_namelen; /* Length of SSID strings */ + u_int8_t wi_name[32]; /* SSID strings */ +}; +/* + * transmit/receive frame structure + */ +struct wi_frame { + u_int16_t wi_status; /* 0x00 */ + u_int16_t wi_rx_tstamp1; /* 0x02 */ + u_int16_t wi_rx_tstamp0; /* 0x04 */ + u_int8_t wi_rx_silence; /* 0x06 */ + u_int8_t wi_rx_signal; /* 0x07 */ + u_int8_t wi_rx_rate; /* 0x08 */ + u_int8_t wi_rx_flow; /* 0x09 */ + u_int8_t wi_tx_rtry; /* 0x0a */ /* Prism2 AP Only */ + u_int8_t wi_tx_rate; /* 0x0b */ /* Prism2 AP Only */ + u_int16_t wi_tx_ctl; /* 0x0c */ + struct ieee80211_frame_addr4 wi_whdr; /* 0x0e */ + u_int16_t wi_dat_len; /* 0x2c */ + struct ether_header wi_ehdr; /* 0x2e */ +} __attribute__((__packed__)); + +/* Tx Status Field */ +#define WI_TXSTAT_RET_ERR 0x0001 +#define WI_TXSTAT_AGED_ERR 0x0002 +#define WI_TXSTAT_DISCONNECT 0x0004 +#define WI_TXSTAT_FORM_ERR 0x0008 + +/* Rx Status Field */ #define WI_STAT_BADCRC 0x0001 #define WI_STAT_UNDECRYPTABLE 0x0002 #define WI_STAT_ERRSTAT 0x0003 #define WI_STAT_MAC_PORT 0x0700 -#define WI_STAT_1042 0x2000 /* RFC1042 encoded */ -#define WI_STAT_TUNNEL 0x4000 /* Bridge-tunnel encoded */ -#define WI_STAT_WMP_MSG 0x6000 /* WaveLAN-II management protocol */ -#define WI_STAT_MGMT 0x8000 /* 802.11b management frames */ +#define WI_STAT_PCF 0x1000 #define WI_RXSTAT_MSG_TYPE 0xE000 +#define WI_STAT_1042 0x2000 /* RFC1042 encoded */ +#define WI_STAT_TUNNEL 0x4000 /* Bridge-tunnel encoded */ +#define WI_STAT_WMP_MSG 0x6000 /* WaveLAN-II management protocol */ +#define WI_STAT_MGMT 0x8000 /* 802.11b management frames */ -#define WI_ENC_TX_802_3 0x00 -#define WI_ENC_TX_802_11 0x11 -#define WI_ENC_TX_MGMT 0x08 #define WI_ENC_TX_E_II 0x0E #define WI_ENC_TX_1042 0x00 #define WI_ENC_TX_TUNNEL 0xF8 -#define WI_TXCNTL_MACPORT 0x00FF -#define WI_TXCNTL_STRUCTTYPE 0xFF00 -#define WI_TXCNTL_TX_EX 0x0004 -#define WI_TXCNTL_TX_OK 0x0002 -#define WI_TXCNTL_NOCRYPT 0x0080 - -/* - * SNAP (sub-network access protocol) constants for transmission - * of IP datagrams over IEEE 802 networks, taken from RFC1042. - * We need these for the LLC/SNAP header fields in the TX/RX frame - * structure. - */ -#define WI_SNAP_K1 0xaa /* assigned global SAP for SNAP */ -#define WI_SNAP_K2 0x00 -#define WI_SNAP_CONTROL 0x03 /* unnumbered information format */ -#define WI_SNAP_WORD0 (WI_SNAP_K1 | (WI_SNAP_K1 << 8)) -#define WI_SNAP_WORD1 (WI_SNAP_K2 | (WI_SNAP_CONTROL << 8)) -#define WI_SNAPHDR_LEN 0x6 -#define WI_FCS_LEN 0x4 +/* TxControl Field (enhanced) */ +#define WI_TXCNTL_TX_OK 0x0002 +#define WI_TXCNTL_TX_EX 0x0004 +#define WI_TXCNTL_STRUCT_TYPE 0x0018 +#define WI_ENC_TX_802_3 0x00 +#define WI_ENC_TX_802_11 0x08 +#define WI_TXCNTL_ALTRTRY 0x0020 +#define WI_TXCNTL_NOCRYPT 0x0080 /* * HFA3861/3863 (BBP) Control Registers diff --git a/sys/dev/wi/if_wivar.h b/sys/dev/wi/if_wivar.h index fee4460..bfc1d28 100644 --- a/sys/dev/wi/if_wivar.h +++ b/sys/dev/wi/if_wivar.h @@ -34,32 +34,10 @@ * $FreeBSD$ */ +#if 0 #define WICACHE /* turn on signal strength cache code */ #define MAXWICACHE 10 - -struct wi_counters { - u_int32_t wi_tx_unicast_frames; - u_int32_t wi_tx_multicast_frames; - u_int32_t wi_tx_fragments; - u_int32_t wi_tx_unicast_octets; - u_int32_t wi_tx_multicast_octets; - u_int32_t wi_tx_deferred_xmits; - u_int32_t wi_tx_single_retries; - u_int32_t wi_tx_multi_retries; - u_int32_t wi_tx_retry_limit; - u_int32_t wi_tx_discards; - u_int32_t wi_rx_unicast_frames; - u_int32_t wi_rx_multicast_frames; - u_int32_t wi_rx_fragments; - u_int32_t wi_rx_unicast_octets; - u_int32_t wi_rx_multicast_octets; - u_int32_t wi_rx_fcs_errors; - u_int32_t wi_rx_discards_nobuf; - u_int32_t wi_tx_discards_wrong_sa; - u_int32_t wi_rx_WEP_cant_decrypt; - u_int32_t wi_rx_msg_in_msg_frags; - u_int32_t wi_rx_msg_in_bad_msg_frags; -}; +#endif /* * Encryption controls. We can enable or disable encryption as @@ -80,31 +58,33 @@ struct wi_counters { #define WI_RID_P2_ENCRYPTION 0xFC28 #define WI_RID_ROAMING_MODE 0xFC2D #define WI_RID_CUR_TX_RATE 0xFD44 /* current TX rate */ -struct wi_key { - u_int16_t wi_keylen; - u_int8_t wi_keydat[14]; -}; - -#define WI_NLTV_KEYS 4 -struct wi_ltv_keys { - u_int16_t wi_len; - u_int16_t wi_type; - struct wi_key wi_keys[WI_NLTV_KEYS]; -}; struct wi_softc { - struct arpcom arpcom; - struct ifmedia ifmedia; - device_t dev; - int wi_unit; + struct ieee80211com sc_ic; + device_t sc_dev; +#if __FreeBSD_version >= 500000 + struct mtx sc_mtx; +#endif + int sc_unit; + int wi_gone; + int sc_enabled; + int sc_firmware_type; +#define WI_NOTYPE 0 +#define WI_LUCENT 1 +#define WI_INTERSIL 2 +#define WI_SYMBOL 3 + int sc_pri_firmware_ver; /* Primary firmware */ + int sc_sta_firmware_ver; /* Station firmware */ + + int wi_bus_type; /* Bus attachment type */ struct resource * local; - int local_rid; + int local_rid; struct resource * iobase; - int iobase_rid; + int iobase_rid; struct resource * irq; - int irq_rid; + int irq_rid; struct resource * mem; - int mem_rid; + int mem_rid; bus_space_handle_t wi_localhandle; bus_space_tag_t wi_localtag; bus_space_handle_t wi_bhandle; @@ -112,71 +92,51 @@ struct wi_softc { bus_space_handle_t wi_bmemhandle; bus_space_tag_t wi_bmemtag; void * wi_intrhand; - int sc_firmware_type; -#define WI_NOTYPE 0 -#define WI_LUCENT 1 -#define WI_INTERSIL 2 -#define WI_SYMBOL 3 - int sc_pri_firmware_ver; /* Primary firmware */ - int sc_sta_firmware_ver; /* Station firmware */ - int sc_enabled; int wi_io_addr; - int wi_tx_data_id; - int wi_tx_mgmt_id; - int wi_gone; - int wi_flags; -#define WI_FLAGS_ATTACHED 0x01 -#define WI_FLAGS_INITIALIZED 0x02 -#define WI_FLAGS_HAS_WEP 0x04 -#define WI_FLAGS_HAS_IBSS 0x08 -#define WI_FLAGS_HAS_CREATE_IBSS 0x10 -#define WI_FLAGS_HAS_MOR 0x20 -#define WI_FLAGS_HAS_ROAMING 0x30 -#define WI_FLAGS_HAS_DIVERSITY 0x40 -#define WI_FLAGS_HAS_HOSTAP 0x80 - int wi_if_flags; - u_int16_t wi_procframe; - u_int16_t wi_ptype; - u_int16_t wi_portnum; - u_int16_t wi_max_data_len; - u_int16_t wi_rts_thresh; - u_int16_t wi_ap_density; - u_int16_t wi_tx_rate; - u_int16_t wi_create_ibss; - u_int16_t wi_channel; - u_int16_t wi_pm_enabled; - u_int16_t wi_mor_enabled; - u_int16_t wi_max_sleep; - u_int16_t wi_supprates; - u_int16_t wi_authtype; - u_int16_t wi_roaming; - char wi_node_name[32]; - char wi_net_name[32]; - char wi_ibss_name[32]; - u_int8_t wi_txbuf[1596]; - u_int8_t wi_scanbuf[1596]; - int wi_scanbuf_len; - struct wi_counters wi_stats; - int wi_has_wep; - int wi_use_wep; - int wi_authmode; - int wi_tx_key; - struct wi_ltv_keys wi_keys; -#ifdef WICACHE - int wi_sigitems; - struct wi_sigcache wi_sigcache[MAXWICACHE]; - int wi_nextitem; -#endif - struct wihap_info wi_hostap_info; - u_int32_t wi_icv; - int wi_icv_flag; - int wi_ibss_port; - struct callout_handle wi_stat_ch; -#if __FreeBSD_version >= 500000 - struct mtx wi_mtx; -#endif - int wi_nic_type; - int wi_bus_type; /* Bus attachment type */ + + struct ifmedia sc_media; + struct bpf_if *sc_drvbpf; + int sc_flags; + int sc_if_flags; + int sc_bap_id; + int sc_bap_off; + + u_int16_t sc_procframe; + u_int16_t sc_portnum; + + u_int16_t sc_dbm_adjust; + u_int16_t sc_max_datalen; + u_int16_t sc_frag_thresh; + u_int16_t sc_rts_thresh; + u_int16_t sc_system_scale; + u_int16_t sc_cnfauthmode; + u_int16_t sc_roaming_mode; + u_int16_t sc_microwave_oven; + u_int16_t sc_authtype; + + int sc_nodelen; + char sc_nodename[IEEE80211_NWID_LEN]; + char sc_net_name[IEEE80211_NWID_LEN]; + + int sc_buflen; /* TX buffer size */ +#define WI_NTXBUF 3 + struct { + int d_fid; + int d_len; + } sc_txd[WI_NTXBUF]; /* TX buffers */ + int sc_txnext; /* index of next TX */ + int sc_txcur; /* index of current TX*/ + int sc_tx_timer; + int sc_scan_timer; + int sc_syn_timer; + + struct wi_counters sc_stats; + u_int16_t sc_ibss_port; + +#define WI_MAXAPINFO 30 + struct wi_apinfo sc_aps[WI_MAXAPINFO]; + int sc_naps; + struct { u_int16_t wi_sleep; u_int16_t wi_delaysupp; @@ -194,7 +154,28 @@ struct wi_softc { u_int16_t wi_confbits_param0; } wi_debug; + int sc_false_syns; + + u_int16_t sc_txbuf[IEEE80211_MAX_LEN/2]; }; +#define sc_if sc_ic.ic_if + +/* maximum consecutive false change-of-BSSID indications */ +#define WI_MAX_FALSE_SYNS 10 + +#define WI_SCAN_INQWAIT 3 /* wait sec before inquire */ +#define WI_SCAN_WAIT 5 /* maximum scan wait */ + +#define WI_FLAGS_ATTACHED 0x0001 +#define WI_FLAGS_INITIALIZED 0x0002 +#define WI_FLAGS_OUTRANGE 0x0004 +#define WI_FLAGS_HAS_MOR 0x0010 +#define WI_FLAGS_HAS_ROAMING 0x0020 +#define WI_FLAGS_HAS_DIVERSITY 0x0040 +#define WI_FLAGS_HAS_SYSSCALE 0x0080 +#define WI_FLAGS_BUG_AUTOINC 0x0100 +#define WI_FLAGS_HAS_FRAGTHR 0x0200 +#define WI_FLAGS_HAS_DBMADJUST 0x0400 struct wi_card_ident { u_int16_t card_id; @@ -209,19 +190,23 @@ struct wi_card_ident { #define le16toh(x) (x) #define htole16(x) (x) #define ifaddr_byindex(idx) ifnet_addrs[(idx) - 1]; -#define WI_LOCK(_sc, _s) s = splimp() -#define WI_UNLOCK(_sc, _s) splx(s) +#define WI_LOCK_DECL() int s +#define WI_LOCK(_sc) s = splimp() +#define WI_UNLOCK(_sc) splx(s) #else -#define WI_LOCK(_sc, _s) _s = 1 -#define WI_UNLOCK(_sc, _s) +#define WI_LOCK_DECL() +#define WI_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx) +#define WI_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx) #endif -int wi_generic_attach(device_t); -int wi_generic_detach(device_t); -void wi_shutdown(device_t); -int wi_alloc(device_t, int); -void wi_free(device_t); -void wi_stop(struct wi_softc *); +int wi_attach(device_t); +int wi_detach(device_t); +void wi_shutdown(device_t); +int wi_alloc(device_t, int); +void wi_free(device_t); extern devclass_t wi_devclass; -int wi_mgmt_xmit(struct wi_softc *, caddr_t, int); -int wi_symbol_load_firm(struct wi_softc *, const void *, int, const void *, int); +void wi_init(void *); +void wi_intr(void *); +int wi_mgmt_xmit(struct wi_softc *, caddr_t, int); +void wi_stop(struct ifnet *, int); +int wi_symbol_load_firm(struct wi_softc *, const void *, int, const void *, int); |