1bb5e3b2fSeh146360 /* 2*0dc2366fSVenugopal Iyer * Copyright 2010 Sun Microsystems, Inc. All rights reserved. 3bb5e3b2fSeh146360 * Use is subject to license terms. 4bb5e3b2fSeh146360 */ 5bb5e3b2fSeh146360 6bb5e3b2fSeh146360 /* 7bb5e3b2fSeh146360 * Copyright (c) 2004, 2005 8bb5e3b2fSeh146360 * Damien Bergamini <damien.bergamini@free.fr>. All rights reserved. 9bb5e3b2fSeh146360 * 10bb5e3b2fSeh146360 * Redistribution and use in source and binary forms, with or without 11bb5e3b2fSeh146360 * modification, are permitted provided that the following conditions 12bb5e3b2fSeh146360 * are met: 13bb5e3b2fSeh146360 * 1. Redistributions of source code must retain the above copyright 14bb5e3b2fSeh146360 * notice unmodified, this list of conditions, and the following 15bb5e3b2fSeh146360 * disclaimer. 16bb5e3b2fSeh146360 * 2. Redistributions in binary form must reproduce the above copyright 17bb5e3b2fSeh146360 * notice, this list of conditions and the following disclaimer in the 18bb5e3b2fSeh146360 * documentation and/or other materials provided with the distribution. 19bb5e3b2fSeh146360 * 20bb5e3b2fSeh146360 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 21bb5e3b2fSeh146360 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22bb5e3b2fSeh146360 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23bb5e3b2fSeh146360 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 24bb5e3b2fSeh146360 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25bb5e3b2fSeh146360 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26bb5e3b2fSeh146360 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27bb5e3b2fSeh146360 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28bb5e3b2fSeh146360 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29bb5e3b2fSeh146360 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30bb5e3b2fSeh146360 * SUCH DAMAGE. 31bb5e3b2fSeh146360 */ 32bb5e3b2fSeh146360 33bb5e3b2fSeh146360 #include <sys/types.h> 34bb5e3b2fSeh146360 #include <sys/byteorder.h> 35bb5e3b2fSeh146360 #include <sys/conf.h> 36bb5e3b2fSeh146360 #include <sys/cmn_err.h> 37bb5e3b2fSeh146360 #include <sys/stat.h> 38bb5e3b2fSeh146360 #include <sys/ddi.h> 39bb5e3b2fSeh146360 #include <sys/sunddi.h> 40bb5e3b2fSeh146360 #include <sys/strsubr.h> 41bb5e3b2fSeh146360 #include <sys/ethernet.h> 42bb5e3b2fSeh146360 #include <inet/common.h> 43bb5e3b2fSeh146360 #include <inet/nd.h> 44bb5e3b2fSeh146360 #include <inet/mi.h> 45bb5e3b2fSeh146360 #include <sys/note.h> 46bb5e3b2fSeh146360 #include <sys/stream.h> 47bb5e3b2fSeh146360 #include <sys/strsun.h> 48bb5e3b2fSeh146360 #include <sys/modctl.h> 49bb5e3b2fSeh146360 #include <sys/devops.h> 50bb5e3b2fSeh146360 #include <sys/dlpi.h> 51da14cebeSEric Cheng #include <sys/mac_provider.h> 52bb5e3b2fSeh146360 #include <sys/mac_wifi.h> 53bb5e3b2fSeh146360 #include <sys/varargs.h> 54bb5e3b2fSeh146360 #include <sys/pci.h> 55bb5e3b2fSeh146360 #include <sys/policy.h> 56bb5e3b2fSeh146360 #include <sys/random.h> 57924f3e72Seh146360 #include <sys/crypto/common.h> 58924f3e72Seh146360 #include <sys/crypto/api.h> 59bb5e3b2fSeh146360 60bb5e3b2fSeh146360 #include "ipw2200.h" 61bb5e3b2fSeh146360 #include "ipw2200_impl.h" 62bb5e3b2fSeh146360 #include <inet/wifi_ioctl.h> 63bb5e3b2fSeh146360 64bb5e3b2fSeh146360 /* 65924f3e72Seh146360 * for net80211 kernel usage 66924f3e72Seh146360 */ 67924f3e72Seh146360 #include <sys/net80211.h> 68924f3e72Seh146360 #include <sys/net80211_proto.h> 69924f3e72Seh146360 70924f3e72Seh146360 /* 71bb5e3b2fSeh146360 * minimal size reserved in tx-ring 72bb5e3b2fSeh146360 */ 73bb5e3b2fSeh146360 #define IPW2200_TX_RING_MIN (8) 74bb5e3b2fSeh146360 #define IPW2200_TXBUF_SIZE (IEEE80211_MAX_LEN) 75bb5e3b2fSeh146360 #define IPW2200_RXBUF_SIZE (4096) 76bb5e3b2fSeh146360 77bb5e3b2fSeh146360 static void *ipw2200_ssp = NULL; 7819397407SSherry Moore static char ipw2200_ident[] = IPW2200_DRV_DESC; 79bb5e3b2fSeh146360 80bb5e3b2fSeh146360 /* 81bb5e3b2fSeh146360 * PIO access attributor for registers 82bb5e3b2fSeh146360 */ 83bb5e3b2fSeh146360 static ddi_device_acc_attr_t ipw2200_csr_accattr = { 84bb5e3b2fSeh146360 DDI_DEVICE_ATTR_V0, 85bb5e3b2fSeh146360 DDI_STRUCTURE_LE_ACC, 86bb5e3b2fSeh146360 DDI_STRICTORDER_ACC 87bb5e3b2fSeh146360 }; 88bb5e3b2fSeh146360 89bb5e3b2fSeh146360 /* 90bb5e3b2fSeh146360 * DMA access attributor for descriptors 91bb5e3b2fSeh146360 */ 92bb5e3b2fSeh146360 static ddi_device_acc_attr_t ipw2200_dma_accattr = { 93bb5e3b2fSeh146360 DDI_DEVICE_ATTR_V0, 94bb5e3b2fSeh146360 DDI_NEVERSWAP_ACC, 95bb5e3b2fSeh146360 DDI_STRICTORDER_ACC 96bb5e3b2fSeh146360 }; 97bb5e3b2fSeh146360 98bb5e3b2fSeh146360 /* 99bb5e3b2fSeh146360 * Describes the chip's DMA engine 100bb5e3b2fSeh146360 */ 101bb5e3b2fSeh146360 static ddi_dma_attr_t ipw2200_dma_attr = { 102bb5e3b2fSeh146360 DMA_ATTR_V0, /* version */ 103bb5e3b2fSeh146360 0x0000000000000000ULL, /* addr_lo */ 104bb5e3b2fSeh146360 0x00000000ffffffffULL, /* addr_hi */ 105bb5e3b2fSeh146360 0x00000000ffffffffULL, /* counter */ 106bb5e3b2fSeh146360 0x0000000000000004ULL, /* alignment */ 107bb5e3b2fSeh146360 0xfff, /* burst */ 108bb5e3b2fSeh146360 1, /* min xfer */ 109bb5e3b2fSeh146360 0x00000000ffffffffULL, /* max xfer */ 110bb5e3b2fSeh146360 0x00000000ffffffffULL, /* seg boud */ 111bb5e3b2fSeh146360 1, /* s/g list */ 112bb5e3b2fSeh146360 1, /* granularity */ 113bb5e3b2fSeh146360 0 /* flags */ 114bb5e3b2fSeh146360 }; 115bb5e3b2fSeh146360 116bb5e3b2fSeh146360 static uint8_t ipw2200_broadcast_addr[] = { 117bb5e3b2fSeh146360 0xff, 0xff, 0xff, 0xff, 0xff, 0xff 118bb5e3b2fSeh146360 }; 119bb5e3b2fSeh146360 static const struct ieee80211_rateset ipw2200_rateset_11a = { 8, 120bb5e3b2fSeh146360 {12, 18, 24, 36, 48, 72, 96, 108} 121bb5e3b2fSeh146360 }; 122bb5e3b2fSeh146360 static const struct ieee80211_rateset ipw2200_rateset_11b = { 4, 123bb5e3b2fSeh146360 {2, 4, 11, 22} 124bb5e3b2fSeh146360 }; 125bb5e3b2fSeh146360 static const struct ieee80211_rateset ipw2200_rateset_11g = { 12, 126bb5e3b2fSeh146360 {2, 4, 11, 22, 12, 18, 24, 36, 48, 72, 96, 108} 127bb5e3b2fSeh146360 }; 128bb5e3b2fSeh146360 129bb5e3b2fSeh146360 /* 130bb5e3b2fSeh146360 * Used by multi function thread 131bb5e3b2fSeh146360 */ 132bb5e3b2fSeh146360 extern pri_t minclsyspri; 133bb5e3b2fSeh146360 134bb5e3b2fSeh146360 /* 135bb5e3b2fSeh146360 * ipw2200 specific hardware operations 136bb5e3b2fSeh146360 */ 137bb5e3b2fSeh146360 static void ipw2200_hwconf_get(struct ipw2200_softc *sc); 138bb5e3b2fSeh146360 static int ipw2200_chip_reset(struct ipw2200_softc *sc); 139bb5e3b2fSeh146360 static void ipw2200_master_stop(struct ipw2200_softc *sc); 140bb5e3b2fSeh146360 static void ipw2200_stop(struct ipw2200_softc *sc); 141bb5e3b2fSeh146360 static int ipw2200_config(struct ipw2200_softc *sc); 142bb5e3b2fSeh146360 static int ipw2200_cmd(struct ipw2200_softc *sc, 143bb5e3b2fSeh146360 uint32_t type, void *buf, size_t len, int async); 144bb5e3b2fSeh146360 static void ipw2200_ring_hwsetup(struct ipw2200_softc *sc); 145bb5e3b2fSeh146360 static int ipw2200_ring_alloc(struct ipw2200_softc *sc); 146bb5e3b2fSeh146360 static void ipw2200_ring_free(struct ipw2200_softc *sc); 147bb5e3b2fSeh146360 static void ipw2200_ring_reset(struct ipw2200_softc *sc); 148bb5e3b2fSeh146360 static int ipw2200_ring_init(struct ipw2200_softc *sc); 149bb5e3b2fSeh146360 150bb5e3b2fSeh146360 /* 151bb5e3b2fSeh146360 * GLD specific operations 152bb5e3b2fSeh146360 */ 153bb5e3b2fSeh146360 static int ipw2200_m_stat(void *arg, uint_t stat, uint64_t *val); 154bb5e3b2fSeh146360 static int ipw2200_m_start(void *arg); 155bb5e3b2fSeh146360 static void ipw2200_m_stop(void *arg); 156bb5e3b2fSeh146360 static int ipw2200_m_unicst(void *arg, const uint8_t *macaddr); 157bb5e3b2fSeh146360 static int ipw2200_m_multicst(void *arg, boolean_t add, const uint8_t *m); 158bb5e3b2fSeh146360 static int ipw2200_m_promisc(void *arg, boolean_t on); 159bb5e3b2fSeh146360 static void ipw2200_m_ioctl(void *arg, queue_t *wq, mblk_t *mp); 160bb5e3b2fSeh146360 static mblk_t *ipw2200_m_tx(void *arg, mblk_t *mp); 1617efa17f5Sfei feng - Sun Microsystems - Beijing China static int ipw2200_m_setprop(void *arg, const char *pr_name, 1627efa17f5Sfei feng - Sun Microsystems - Beijing China mac_prop_id_t wldp_pr_num, uint_t wldp_length, const void *wldp_buf); 1637efa17f5Sfei feng - Sun Microsystems - Beijing China static int ipw2200_m_getprop(void *arg, const char *pr_name, 164*0dc2366fSVenugopal Iyer mac_prop_id_t wldp_pr_num, uint_t wldp_length, void *wldp_buf); 165*0dc2366fSVenugopal Iyer static void ipw2200_m_propinfo(void *arg, const char *pr_name, 166*0dc2366fSVenugopal Iyer mac_prop_id_t wldp_pr_num, mac_prop_info_handle_t mph); 167bb5e3b2fSeh146360 168bb5e3b2fSeh146360 /* 169bb5e3b2fSeh146360 * Interrupt and Data transferring operations 170bb5e3b2fSeh146360 */ 171bb5e3b2fSeh146360 static uint_t ipw2200_intr(caddr_t arg); 172bb5e3b2fSeh146360 static int ipw2200_send(struct ieee80211com *ic, mblk_t *mp, uint8_t type); 173bb5e3b2fSeh146360 static void ipw2200_rcv_frame(struct ipw2200_softc *sc, 174bb5e3b2fSeh146360 struct ipw2200_frame *frame); 175bb5e3b2fSeh146360 static void ipw2200_rcv_notif(struct ipw2200_softc *sc, 176bb5e3b2fSeh146360 struct ipw2200_notif *notif); 177bb5e3b2fSeh146360 178bb5e3b2fSeh146360 /* 179bb5e3b2fSeh146360 * WiFi specific operations 180bb5e3b2fSeh146360 */ 181bb5e3b2fSeh146360 static int ipw2200_newstate(struct ieee80211com *ic, 182bb5e3b2fSeh146360 enum ieee80211_state state, int arg); 183bb5e3b2fSeh146360 static void ipw2200_thread(struct ipw2200_softc *sc); 184bb5e3b2fSeh146360 185bb5e3b2fSeh146360 /* 186bb5e3b2fSeh146360 * IOCTL Handler 187bb5e3b2fSeh146360 */ 188bb5e3b2fSeh146360 static int ipw2200_ioctl(struct ipw2200_softc *sc, queue_t *q, mblk_t *m); 189bb5e3b2fSeh146360 static int ipw2200_getset(struct ipw2200_softc *sc, 190bb5e3b2fSeh146360 mblk_t *m, uint32_t cmd, boolean_t *need_net80211); 191bb5e3b2fSeh146360 static int iwi_wificfg_radio(struct ipw2200_softc *sc, 192bb5e3b2fSeh146360 uint32_t cmd, wldp_t *outfp); 193bb5e3b2fSeh146360 static int iwi_wificfg_desrates(wldp_t *outfp); 194bb5e3b2fSeh146360 195bb5e3b2fSeh146360 /* 196924f3e72Seh146360 * net80211 functions 197924f3e72Seh146360 */ 198924f3e72Seh146360 extern uint8_t ieee80211_crypto_getciphertype(ieee80211com_t *ic); 199924f3e72Seh146360 extern void ieee80211_notify_node_join(ieee80211com_t *ic, 200924f3e72Seh146360 ieee80211_node_t *in); 201924f3e72Seh146360 extern void ieee80211_notify_node_leave(ieee80211com_t *ic, 202924f3e72Seh146360 ieee80211_node_t *in); 203924f3e72Seh146360 204924f3e72Seh146360 /* 205bb5e3b2fSeh146360 * Mac Call Back entries 206bb5e3b2fSeh146360 */ 207bb5e3b2fSeh146360 mac_callbacks_t ipw2200_m_callbacks = { 208*0dc2366fSVenugopal Iyer MC_IOCTL | MC_SETPROP | MC_GETPROP | MC_PROPINFO, 209bb5e3b2fSeh146360 ipw2200_m_stat, 210bb5e3b2fSeh146360 ipw2200_m_start, 211bb5e3b2fSeh146360 ipw2200_m_stop, 212bb5e3b2fSeh146360 ipw2200_m_promisc, 213bb5e3b2fSeh146360 ipw2200_m_multicst, 214bb5e3b2fSeh146360 ipw2200_m_unicst, 215bb5e3b2fSeh146360 ipw2200_m_tx, 216*0dc2366fSVenugopal Iyer NULL, 2177efa17f5Sfei feng - Sun Microsystems - Beijing China ipw2200_m_ioctl, 2187efa17f5Sfei feng - Sun Microsystems - Beijing China NULL, 2197efa17f5Sfei feng - Sun Microsystems - Beijing China NULL, 2207efa17f5Sfei feng - Sun Microsystems - Beijing China NULL, 2217efa17f5Sfei feng - Sun Microsystems - Beijing China ipw2200_m_setprop, 222*0dc2366fSVenugopal Iyer ipw2200_m_getprop, 223*0dc2366fSVenugopal Iyer ipw2200_m_propinfo 224bb5e3b2fSeh146360 }; 225bb5e3b2fSeh146360 226bb5e3b2fSeh146360 /* 227bb5e3b2fSeh146360 * DEBUG Facility 228bb5e3b2fSeh146360 */ 229bb5e3b2fSeh146360 #define MAX_MSG (128) 230bb5e3b2fSeh146360 uint32_t ipw2200_debug = 0; 231bb5e3b2fSeh146360 /* 232bb5e3b2fSeh146360 * supported debug marks are: 233bb5e3b2fSeh146360 * | IPW2200_DBG_CSR 234bb5e3b2fSeh146360 * | IPW2200_DBG_TABLE 235bb5e3b2fSeh146360 * | IPW2200_DBG_HWCAP 236bb5e3b2fSeh146360 * | IPW2200_DBG_TX 237bb5e3b2fSeh146360 * | IPW2200_DBG_INIT 238bb5e3b2fSeh146360 * | IPW2200_DBG_FW 239bb5e3b2fSeh146360 * | IPW2200_DBG_NOTIF 240bb5e3b2fSeh146360 * | IPW2200_DBG_SCAN 241bb5e3b2fSeh146360 * | IPW2200_DBG_IOCTL 242bb5e3b2fSeh146360 * | IPW2200_DBG_RING 243bb5e3b2fSeh146360 * | IPW2200_DBG_INT 244bb5e3b2fSeh146360 * | IPW2200_DBG_RX 245bb5e3b2fSeh146360 * | IPW2200_DBG_DMA 246bb5e3b2fSeh146360 * | IPW2200_DBG_GLD 247bb5e3b2fSeh146360 * | IPW2200_DBG_WIFI 248bb5e3b2fSeh146360 * | IPW2200_DBG_SOFTINT 249922d2c76Seh146360 * | IPW2200_DBG_SUSPEND 2507efa17f5Sfei feng - Sun Microsystems - Beijing China * | IPW2200_DBG_BRUSSELS 251bb5e3b2fSeh146360 */ 252bb5e3b2fSeh146360 253bb5e3b2fSeh146360 /* 254bb5e3b2fSeh146360 * Global tunning parameter to work around unknown hardware issues 255bb5e3b2fSeh146360 */ 256bb5e3b2fSeh146360 static uint32_t delay_config_stable = 100000; /* 100ms */ 257bb5e3b2fSeh146360 static uint32_t delay_fatal_recover = 100000 * 20; /* 2s */ 258bb5e3b2fSeh146360 static uint32_t delay_aux_thread = 100000; /* 100ms */ 259bb5e3b2fSeh146360 260bb5e3b2fSeh146360 #define IEEE80211_IS_CHAN_2GHZ(_c) \ 261bb5e3b2fSeh146360 (((_c)->ich_flags & IEEE80211_CHAN_2GHZ) != 0) 262bb5e3b2fSeh146360 #define IEEE80211_IS_CHAN_5GHZ(_c) \ 263bb5e3b2fSeh146360 (((_c)->ich_flags & IEEE80211_CHAN_5GHZ) != 0) 264bb5e3b2fSeh146360 #define isset(a, i) ((a)[(i)/NBBY] & (1 << ((i)%NBBY))) 265bb5e3b2fSeh146360 266bb5e3b2fSeh146360 void 267bb5e3b2fSeh146360 ipw2200_dbg(dev_info_t *dip, int level, const char *fmt, ...) 268bb5e3b2fSeh146360 { 269bb5e3b2fSeh146360 va_list ap; 270bb5e3b2fSeh146360 char buf[MAX_MSG]; 271bb5e3b2fSeh146360 int instance; 272bb5e3b2fSeh146360 273bb5e3b2fSeh146360 va_start(ap, fmt); 274bb5e3b2fSeh146360 (void) vsnprintf(buf, sizeof (buf), fmt, ap); 275bb5e3b2fSeh146360 va_end(ap); 276bb5e3b2fSeh146360 277bb5e3b2fSeh146360 if (dip) { 278bb5e3b2fSeh146360 instance = ddi_get_instance(dip); 279bb5e3b2fSeh146360 cmn_err(level, "%s%d: %s", IPW2200_DRV_NAME, instance, buf); 280bb5e3b2fSeh146360 } else 281bb5e3b2fSeh146360 cmn_err(level, "%s: %s", IPW2200_DRV_NAME, buf); 282bb5e3b2fSeh146360 283bb5e3b2fSeh146360 } 284bb5e3b2fSeh146360 285bb5e3b2fSeh146360 /* 286922d2c76Seh146360 * Set up pci 287922d2c76Seh146360 */ 288922d2c76Seh146360 int 289922d2c76Seh146360 ipw2200_setup_pci(dev_info_t *dip, struct ipw2200_softc *sc) 290922d2c76Seh146360 { 291922d2c76Seh146360 ddi_acc_handle_t cfgh; 292922d2c76Seh146360 caddr_t regs; 293922d2c76Seh146360 int err; 294922d2c76Seh146360 295922d2c76Seh146360 /* 296922d2c76Seh146360 * Map config spaces register to read the vendor id, device id, sub 297922d2c76Seh146360 * vendor id, and sub device id. 298922d2c76Seh146360 */ 299922d2c76Seh146360 err = ddi_regs_map_setup(dip, IPW2200_PCI_CFG_RNUM, ®s, 300922d2c76Seh146360 0, 0, &ipw2200_csr_accattr, &cfgh); 301922d2c76Seh146360 if (err != DDI_SUCCESS) { 302922d2c76Seh146360 IPW2200_WARN((dip, CE_WARN, 303922d2c76Seh146360 "ipw2200_attach(): unable to map spaces regs\n")); 304922d2c76Seh146360 return (DDI_FAILURE); 305922d2c76Seh146360 } 306922d2c76Seh146360 307922d2c76Seh146360 ddi_put8(cfgh, (uint8_t *)(regs + 0x41), 0); 308922d2c76Seh146360 sc->sc_vendor = ddi_get16(cfgh, 309922d2c76Seh146360 (uint16_t *)((uintptr_t)regs + PCI_CONF_VENID)); 310922d2c76Seh146360 sc->sc_device = ddi_get16(cfgh, 311922d2c76Seh146360 (uint16_t *)((uintptr_t)regs + PCI_CONF_DEVID)); 312922d2c76Seh146360 sc->sc_subven = ddi_get16(cfgh, 313922d2c76Seh146360 (uint16_t *)((uintptr_t)regs + PCI_CONF_SUBVENID)); 314922d2c76Seh146360 sc->sc_subdev = ddi_get16(cfgh, 315922d2c76Seh146360 (uint16_t *)((uintptr_t)regs + PCI_CONF_SUBSYSID)); 316922d2c76Seh146360 IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT, 317922d2c76Seh146360 "ipw2200_setup_pci(): vendor = 0x%04x, devic = 0x%04x," 318922d2c76Seh146360 "subversion = 0x%04x, subdev = 0x%04x", 319922d2c76Seh146360 sc->sc_vendor, sc->sc_device, sc->sc_subven, sc->sc_subdev)); 320922d2c76Seh146360 321922d2c76Seh146360 ddi_regs_map_free(&cfgh); 322922d2c76Seh146360 323922d2c76Seh146360 return (DDI_SUCCESS); 324922d2c76Seh146360 325922d2c76Seh146360 } 326922d2c76Seh146360 327922d2c76Seh146360 /* 328bb5e3b2fSeh146360 * Device operations 329bb5e3b2fSeh146360 */ 330bb5e3b2fSeh146360 int 331bb5e3b2fSeh146360 ipw2200_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 332bb5e3b2fSeh146360 { 333bb5e3b2fSeh146360 struct ipw2200_softc *sc; 334bb5e3b2fSeh146360 struct ieee80211com *ic; 335bb5e3b2fSeh146360 int instance, err, i; 336bb5e3b2fSeh146360 char strbuf[32]; 337bb5e3b2fSeh146360 wifi_data_t wd = { 0 }; 338bb5e3b2fSeh146360 mac_register_t *macp; 339bb5e3b2fSeh146360 340922d2c76Seh146360 switch (cmd) { 341922d2c76Seh146360 case DDI_ATTACH: 342922d2c76Seh146360 break; 343922d2c76Seh146360 case DDI_RESUME: 344922d2c76Seh146360 sc = ddi_get_soft_state(ipw2200_ssp, ddi_get_instance(dip)); 345922d2c76Seh146360 ASSERT(sc != NULL); 346922d2c76Seh146360 347922d2c76Seh146360 /* 348922d2c76Seh146360 * set up pci 349922d2c76Seh146360 */ 350922d2c76Seh146360 err = ipw2200_setup_pci(dip, sc); 351922d2c76Seh146360 if (err != DDI_SUCCESS) { 352922d2c76Seh146360 IPW2200_DBG(IPW2200_DBG_SUSPEND, (sc->sc_dip, CE_CONT, 353922d2c76Seh146360 "ipw2200_attach(): resume failure\n")); 354922d2c76Seh146360 return (DDI_FAILURE); 355922d2c76Seh146360 } 356922d2c76Seh146360 357922d2c76Seh146360 /* 358922d2c76Seh146360 * resume hardware. 359922d2c76Seh146360 * If it was on runnning status, reset to INIT state 360922d2c76Seh146360 */ 361922d2c76Seh146360 sc->sc_flags &= ~IPW2200_FLAG_SUSPEND; 362922d2c76Seh146360 if (sc->sc_flags & IPW2200_FLAG_RUNNING) 363922d2c76Seh146360 (void) ipw2200_init(sc); 364922d2c76Seh146360 365922d2c76Seh146360 IPW2200_DBG(IPW2200_DBG_SUSPEND, (sc->sc_dip, CE_CONT, 366922d2c76Seh146360 "ipw2200_attach(): resume successful\n")); 367922d2c76Seh146360 return (DDI_SUCCESS); 368922d2c76Seh146360 default: 369922d2c76Seh146360 return (DDI_FAILURE); 370bb5e3b2fSeh146360 } 371bb5e3b2fSeh146360 372bb5e3b2fSeh146360 instance = ddi_get_instance(dip); 373bb5e3b2fSeh146360 err = ddi_soft_state_zalloc(ipw2200_ssp, instance); 374bb5e3b2fSeh146360 if (err != DDI_SUCCESS) { 375bb5e3b2fSeh146360 IPW2200_WARN((dip, CE_WARN, 376bb5e3b2fSeh146360 "ipw2200_attach(): unable to allocate soft state\n")); 377bb5e3b2fSeh146360 goto fail1; 378bb5e3b2fSeh146360 } 379bb5e3b2fSeh146360 sc = ddi_get_soft_state(ipw2200_ssp, instance); 380bb5e3b2fSeh146360 sc->sc_dip = dip; 381bb5e3b2fSeh146360 382922d2c76Seh146360 /* set up pci, put reg+0x41 0 */ 383922d2c76Seh146360 err = ipw2200_setup_pci(dip, sc); 384bb5e3b2fSeh146360 if (err != DDI_SUCCESS) { 385bb5e3b2fSeh146360 IPW2200_WARN((dip, CE_WARN, 386922d2c76Seh146360 "ipw2200_attach(): unable to setup pci\n")); 387bb5e3b2fSeh146360 goto fail2; 388bb5e3b2fSeh146360 } 389bb5e3b2fSeh146360 390bb5e3b2fSeh146360 /* 391bb5e3b2fSeh146360 * Map operating registers 392bb5e3b2fSeh146360 */ 393bb5e3b2fSeh146360 err = ddi_regs_map_setup(dip, IPW2200_PCI_CSR_RNUM, &sc->sc_regs, 394bb5e3b2fSeh146360 0, 0, &ipw2200_csr_accattr, &sc->sc_ioh); 395bb5e3b2fSeh146360 if (err != DDI_SUCCESS) { 396bb5e3b2fSeh146360 IPW2200_WARN((dip, CE_WARN, 397bb5e3b2fSeh146360 "ipw2200_attach(): ddi_regs_map_setup() failed\n")); 398bb5e3b2fSeh146360 goto fail2; 399bb5e3b2fSeh146360 } 400bb5e3b2fSeh146360 401bb5e3b2fSeh146360 /* 402bb5e3b2fSeh146360 * Reset the chip 403bb5e3b2fSeh146360 */ 404bb5e3b2fSeh146360 err = ipw2200_chip_reset(sc); 405bb5e3b2fSeh146360 if (err != DDI_SUCCESS) { 406bb5e3b2fSeh146360 IPW2200_WARN((dip, CE_WARN, 407bb5e3b2fSeh146360 "ipw2200_attach(): ipw2200_chip_reset() failed\n")); 408bb5e3b2fSeh146360 goto fail3; 409bb5e3b2fSeh146360 } 410bb5e3b2fSeh146360 411bb5e3b2fSeh146360 /* 412bb5e3b2fSeh146360 * Get the hardware configuration, including the MAC address 413bb5e3b2fSeh146360 * Then, init all the rings needed. 414bb5e3b2fSeh146360 */ 415bb5e3b2fSeh146360 ipw2200_hwconf_get(sc); 416bb5e3b2fSeh146360 err = ipw2200_ring_init(sc); 417bb5e3b2fSeh146360 if (err != DDI_SUCCESS) { 418bb5e3b2fSeh146360 IPW2200_WARN((dip, CE_WARN, 419bb5e3b2fSeh146360 "ipw2200_attach(): ipw2200_ring_init() failed\n")); 420bb5e3b2fSeh146360 goto fail3; 421bb5e3b2fSeh146360 } 422bb5e3b2fSeh146360 423bb5e3b2fSeh146360 /* 424bb5e3b2fSeh146360 * Initialize mutexs and condvars 425bb5e3b2fSeh146360 */ 426bb5e3b2fSeh146360 err = ddi_get_iblock_cookie(dip, 0, &sc->sc_iblk); 427bb5e3b2fSeh146360 if (err != DDI_SUCCESS) { 428bb5e3b2fSeh146360 IPW2200_WARN((dip, CE_WARN, 429bb5e3b2fSeh146360 "ipw2200_attach(): ddi_get_iblock_cookie() failed\n")); 430bb5e3b2fSeh146360 goto fail4; 431bb5e3b2fSeh146360 } 432bb5e3b2fSeh146360 433bb5e3b2fSeh146360 /* 434bb5e3b2fSeh146360 * interrupt lock 435bb5e3b2fSeh146360 */ 436bb5e3b2fSeh146360 mutex_init(&sc->sc_ilock, "intr-lock", MUTEX_DRIVER, 437bb5e3b2fSeh146360 (void *) sc->sc_iblk); 438bb5e3b2fSeh146360 cv_init(&sc->sc_fw_cond, "firmware-ok", CV_DRIVER, NULL); 439bb5e3b2fSeh146360 cv_init(&sc->sc_cmd_status_cond, "cmd-status-ring", CV_DRIVER, NULL); 440bb5e3b2fSeh146360 441bb5e3b2fSeh146360 /* 442bb5e3b2fSeh146360 * command ring lock 443bb5e3b2fSeh146360 */ 444bb5e3b2fSeh146360 mutex_init(&sc->sc_cmd_lock, "cmd-ring", MUTEX_DRIVER, 445bb5e3b2fSeh146360 (void *) sc->sc_iblk); 446bb5e3b2fSeh146360 cv_init(&sc->sc_cmd_cond, "cmd-ring", CV_DRIVER, NULL); 447bb5e3b2fSeh146360 448bb5e3b2fSeh146360 /* 449bb5e3b2fSeh146360 * tx ring lock 450bb5e3b2fSeh146360 */ 451bb5e3b2fSeh146360 mutex_init(&sc->sc_tx_lock, "tx-ring", MUTEX_DRIVER, 452bb5e3b2fSeh146360 (void *) sc->sc_iblk); 453bb5e3b2fSeh146360 454bb5e3b2fSeh146360 /* 455924f3e72Seh146360 * rescheduled lock 456924f3e72Seh146360 */ 457924f3e72Seh146360 mutex_init(&sc->sc_resched_lock, "reschedule-lock", MUTEX_DRIVER, 458924f3e72Seh146360 (void *) sc->sc_iblk); 459924f3e72Seh146360 460924f3e72Seh146360 /* 461bb5e3b2fSeh146360 * multi-function lock, may acquire this during interrupt 462bb5e3b2fSeh146360 */ 463bb5e3b2fSeh146360 mutex_init(&sc->sc_mflock, "function-lock", MUTEX_DRIVER, 464bb5e3b2fSeh146360 (void *) sc->sc_iblk); 465bb5e3b2fSeh146360 cv_init(&sc->sc_mfthread_cv, NULL, CV_DRIVER, NULL); 466bb5e3b2fSeh146360 sc->sc_mf_thread = NULL; 467bb5e3b2fSeh146360 sc->sc_mfthread_switch = 0; 468bb5e3b2fSeh146360 469bb5e3b2fSeh146360 /* 470924f3e72Seh146360 * Initialize the WiFi part 471bb5e3b2fSeh146360 */ 472bb5e3b2fSeh146360 ic = &sc->sc_ic; 473bb5e3b2fSeh146360 ic->ic_phytype = IEEE80211_T_OFDM; 474bb5e3b2fSeh146360 ic->ic_opmode = IEEE80211_M_STA; 475bb5e3b2fSeh146360 ic->ic_state = IEEE80211_S_INIT; 476bb5e3b2fSeh146360 ic->ic_maxrssi = 100; /* experimental number */ 477924f3e72Seh146360 ic->ic_caps = 478924f3e72Seh146360 IEEE80211_C_SHPREAMBLE | 479924f3e72Seh146360 IEEE80211_C_TXPMGT | 480924f3e72Seh146360 IEEE80211_C_PMGT | 481924f3e72Seh146360 IEEE80211_C_WPA; 482bb5e3b2fSeh146360 483bb5e3b2fSeh146360 /* 484bb5e3b2fSeh146360 * set mac addr 485bb5e3b2fSeh146360 */ 486bb5e3b2fSeh146360 IEEE80211_ADDR_COPY(ic->ic_macaddr, sc->sc_macaddr); 487bb5e3b2fSeh146360 488bb5e3b2fSeh146360 /* 489bb5e3b2fSeh146360 * set supported .11a rates and channel - (2915ABG only) 490bb5e3b2fSeh146360 */ 491922d2c76Seh146360 if (sc->sc_device >= 0x4223) { 492bb5e3b2fSeh146360 /* .11a rates */ 493bb5e3b2fSeh146360 ic->ic_sup_rates[IEEE80211_MODE_11A] = ipw2200_rateset_11a; 494bb5e3b2fSeh146360 /* .11a channels */ 495bb5e3b2fSeh146360 for (i = 36; i <= 64; i += 4) { 496bb5e3b2fSeh146360 ic->ic_sup_channels[i].ich_freq = 497bb5e3b2fSeh146360 ieee80211_ieee2mhz(i, IEEE80211_CHAN_5GHZ); 498bb5e3b2fSeh146360 ic->ic_sup_channels[i].ich_flags = /* CHAN_A */ 499bb5e3b2fSeh146360 IEEE80211_CHAN_5GHZ | IEEE80211_CHAN_OFDM; 500bb5e3b2fSeh146360 } 501bb5e3b2fSeh146360 for (i = 149; i <= 165; i += 4) { 502bb5e3b2fSeh146360 ic->ic_sup_channels[i].ich_freq = 503bb5e3b2fSeh146360 ieee80211_ieee2mhz(i, IEEE80211_CHAN_5GHZ); 504bb5e3b2fSeh146360 ic->ic_sup_channels[i].ich_flags = /* CHAN_A */ 505bb5e3b2fSeh146360 IEEE80211_CHAN_5GHZ | IEEE80211_CHAN_OFDM; 506bb5e3b2fSeh146360 } 507bb5e3b2fSeh146360 } 508bb5e3b2fSeh146360 509bb5e3b2fSeh146360 /* 510bb5e3b2fSeh146360 * set supported .11b and .11g rates 511bb5e3b2fSeh146360 */ 512bb5e3b2fSeh146360 ic->ic_sup_rates[IEEE80211_MODE_11B] = ipw2200_rateset_11b; 513bb5e3b2fSeh146360 ic->ic_sup_rates[IEEE80211_MODE_11G] = ipw2200_rateset_11g; 514bb5e3b2fSeh146360 515bb5e3b2fSeh146360 /* 516bb5e3b2fSeh146360 * set supported .11b and .11g channels(1 through 14) 517bb5e3b2fSeh146360 */ 518bb5e3b2fSeh146360 for (i = 1; i < 14; i++) { 519bb5e3b2fSeh146360 ic->ic_sup_channels[i].ich_freq = 520bb5e3b2fSeh146360 ieee80211_ieee2mhz(i, IEEE80211_CHAN_2GHZ); 521bb5e3b2fSeh146360 ic->ic_sup_channels[i].ich_flags = 522bb5e3b2fSeh146360 IEEE80211_CHAN_CCK | IEEE80211_CHAN_OFDM | 523bb5e3b2fSeh146360 IEEE80211_CHAN_DYN | IEEE80211_CHAN_2GHZ; 524bb5e3b2fSeh146360 } 525bb5e3b2fSeh146360 526bb5e3b2fSeh146360 /* 527bb5e3b2fSeh146360 * IBSS channal undefined for now 528bb5e3b2fSeh146360 */ 529bb5e3b2fSeh146360 ic->ic_ibss_chan = &ic->ic_sup_channels[0]; 530bb5e3b2fSeh146360 ic->ic_xmit = ipw2200_send; 531bb5e3b2fSeh146360 532bb5e3b2fSeh146360 /* 533bb5e3b2fSeh146360 * init generic layer, then override state transition machine 534bb5e3b2fSeh146360 */ 535bb5e3b2fSeh146360 ieee80211_attach(ic); 536bb5e3b2fSeh146360 537bb5e3b2fSeh146360 /* 538924f3e72Seh146360 * different instance has different WPA door 539924f3e72Seh146360 */ 540924f3e72Seh146360 ieee80211_register_door(ic, ddi_driver_name(dip), instance); 541924f3e72Seh146360 542924f3e72Seh146360 /* 543bb5e3b2fSeh146360 * Override 80211 default routines 544bb5e3b2fSeh146360 */ 545bb5e3b2fSeh146360 ieee80211_media_init(ic); /* initial the node table and bss */ 546bb5e3b2fSeh146360 sc->sc_newstate = ic->ic_newstate; 547bb5e3b2fSeh146360 ic->ic_newstate = ipw2200_newstate; 548bb5e3b2fSeh146360 ic->ic_def_txkey = 0; 549bb5e3b2fSeh146360 sc->sc_authmode = IEEE80211_AUTH_OPEN; 550bb5e3b2fSeh146360 551bb5e3b2fSeh146360 /* 552bb5e3b2fSeh146360 * Add the interrupt handler 553bb5e3b2fSeh146360 */ 554bb5e3b2fSeh146360 err = ddi_add_intr(dip, 0, &sc->sc_iblk, NULL, 555bb5e3b2fSeh146360 ipw2200_intr, (caddr_t)sc); 556bb5e3b2fSeh146360 if (err != DDI_SUCCESS) { 557bb5e3b2fSeh146360 IPW2200_WARN((dip, CE_WARN, 558bb5e3b2fSeh146360 "ipw2200_attach(): ddi_add_intr() failed\n")); 559bb5e3b2fSeh146360 goto fail5; 560bb5e3b2fSeh146360 } 561bb5e3b2fSeh146360 562bb5e3b2fSeh146360 /* 563bb5e3b2fSeh146360 * Initialize pointer to device specific functions 564bb5e3b2fSeh146360 */ 565bb5e3b2fSeh146360 wd.wd_secalloc = WIFI_SEC_NONE; 566bb5e3b2fSeh146360 wd.wd_opmode = ic->ic_opmode; 567924f3e72Seh146360 IEEE80211_ADDR_COPY(wd.wd_bssid, ic->ic_bss->in_bssid); 568bb5e3b2fSeh146360 569bb5e3b2fSeh146360 macp = mac_alloc(MAC_VERSION); 570bb5e3b2fSeh146360 if (err != 0) { 571bb5e3b2fSeh146360 IPW2200_WARN((dip, CE_WARN, 572bb5e3b2fSeh146360 "ipw2200_attach(): mac_alloc() failed\n")); 573bb5e3b2fSeh146360 goto fail6; 574bb5e3b2fSeh146360 } 575bb5e3b2fSeh146360 576bb5e3b2fSeh146360 macp->m_type_ident = MAC_PLUGIN_IDENT_WIFI; 577bb5e3b2fSeh146360 macp->m_driver = sc; 578bb5e3b2fSeh146360 macp->m_dip = dip; 579bb5e3b2fSeh146360 macp->m_src_addr = ic->ic_macaddr; 580bb5e3b2fSeh146360 macp->m_callbacks = &ipw2200_m_callbacks; 581bb5e3b2fSeh146360 macp->m_min_sdu = 0; 582bb5e3b2fSeh146360 macp->m_max_sdu = IEEE80211_MTU; 583bb5e3b2fSeh146360 macp->m_pdata = &wd; 584bb5e3b2fSeh146360 macp->m_pdata_size = sizeof (wd); 585bb5e3b2fSeh146360 586bb5e3b2fSeh146360 /* 587bb5e3b2fSeh146360 * Register the macp to mac 588bb5e3b2fSeh146360 */ 589bb5e3b2fSeh146360 err = mac_register(macp, &ic->ic_mach); 590bb5e3b2fSeh146360 mac_free(macp); 591bb5e3b2fSeh146360 if (err != DDI_SUCCESS) { 592bb5e3b2fSeh146360 IPW2200_WARN((dip, CE_WARN, 593bb5e3b2fSeh146360 "ipw2200_attach(): mac_register() failed\n")); 594bb5e3b2fSeh146360 goto fail6; 595bb5e3b2fSeh146360 } 596bb5e3b2fSeh146360 597bb5e3b2fSeh146360 /* 598bb5e3b2fSeh146360 * Create minor node of type DDI_NT_NET_WIFI 599bb5e3b2fSeh146360 */ 600bb5e3b2fSeh146360 (void) snprintf(strbuf, sizeof (strbuf), "%s%d", 601bb5e3b2fSeh146360 IPW2200_DRV_NAME, instance); 602bb5e3b2fSeh146360 err = ddi_create_minor_node(dip, strbuf, S_IFCHR, 603bb5e3b2fSeh146360 instance + 1, DDI_NT_NET_WIFI, 0); 604bb5e3b2fSeh146360 if (err != DDI_SUCCESS) 605bb5e3b2fSeh146360 IPW2200_WARN((dip, CE_WARN, 606bb5e3b2fSeh146360 "ipw2200_attach(): ddi_create_minor_node() failed\n")); 607bb5e3b2fSeh146360 608bb5e3b2fSeh146360 /* 609bb5e3b2fSeh146360 * Cache firmware will always be true 610bb5e3b2fSeh146360 */ 611bb5e3b2fSeh146360 (void) ipw2200_cache_firmware(sc); 612bb5e3b2fSeh146360 613bb5e3b2fSeh146360 /* 614bb5e3b2fSeh146360 * Notify link is down now 615bb5e3b2fSeh146360 */ 616bb5e3b2fSeh146360 mac_link_update(ic->ic_mach, LINK_STATE_DOWN); 617bb5e3b2fSeh146360 618bb5e3b2fSeh146360 /* 619bb5e3b2fSeh146360 * Create the mf thread to handle the link status, 620bb5e3b2fSeh146360 * recovery fatal error, etc. 621bb5e3b2fSeh146360 */ 622bb5e3b2fSeh146360 sc->sc_mfthread_switch = 1; 623bb5e3b2fSeh146360 if (sc->sc_mf_thread == NULL) 624bb5e3b2fSeh146360 sc->sc_mf_thread = thread_create((caddr_t)NULL, 0, 625bb5e3b2fSeh146360 ipw2200_thread, sc, 0, &p0, TS_RUN, minclsyspri); 626bb5e3b2fSeh146360 627bb5e3b2fSeh146360 return (DDI_SUCCESS); 628bb5e3b2fSeh146360 629bb5e3b2fSeh146360 fail6: 630bb5e3b2fSeh146360 ddi_remove_intr(dip, 0, sc->sc_iblk); 631bb5e3b2fSeh146360 fail5: 632bb5e3b2fSeh146360 ieee80211_detach(ic); 633bb5e3b2fSeh146360 634bb5e3b2fSeh146360 mutex_destroy(&sc->sc_ilock); 635bb5e3b2fSeh146360 mutex_destroy(&sc->sc_cmd_lock); 636bb5e3b2fSeh146360 mutex_destroy(&sc->sc_tx_lock); 637bb5e3b2fSeh146360 mutex_destroy(&sc->sc_mflock); 638924f3e72Seh146360 mutex_destroy(&sc->sc_resched_lock); 639bb5e3b2fSeh146360 cv_destroy(&sc->sc_fw_cond); 640bb5e3b2fSeh146360 cv_destroy(&sc->sc_cmd_status_cond); 641bb5e3b2fSeh146360 cv_destroy(&sc->sc_cmd_cond); 642bb5e3b2fSeh146360 cv_destroy(&sc->sc_mfthread_cv); 643bb5e3b2fSeh146360 fail4: 644bb5e3b2fSeh146360 ipw2200_ring_free(sc); 645bb5e3b2fSeh146360 fail3: 646bb5e3b2fSeh146360 ddi_regs_map_free(&sc->sc_ioh); 647bb5e3b2fSeh146360 fail2: 648bb5e3b2fSeh146360 ddi_soft_state_free(ipw2200_ssp, instance); 649bb5e3b2fSeh146360 fail1: 650bb5e3b2fSeh146360 return (err); 651bb5e3b2fSeh146360 } 652bb5e3b2fSeh146360 653bb5e3b2fSeh146360 654bb5e3b2fSeh146360 int 655bb5e3b2fSeh146360 ipw2200_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 656bb5e3b2fSeh146360 { 657922d2c76Seh146360 struct ipw2200_softc *sc; 658bb5e3b2fSeh146360 int err; 659922d2c76Seh146360 660922d2c76Seh146360 sc = ddi_get_soft_state(ipw2200_ssp, ddi_get_instance(dip)); 661bb5e3b2fSeh146360 ASSERT(sc != NULL); 662bb5e3b2fSeh146360 663922d2c76Seh146360 switch (cmd) { 664922d2c76Seh146360 case DDI_DETACH: 665922d2c76Seh146360 break; 666922d2c76Seh146360 case DDI_SUSPEND: 667922d2c76Seh146360 if (sc->sc_flags & IPW2200_FLAG_RUNNING) { 668922d2c76Seh146360 ipw2200_stop(sc); 669922d2c76Seh146360 } 670922d2c76Seh146360 sc->sc_flags |= IPW2200_FLAG_SUSPEND; 671922d2c76Seh146360 672922d2c76Seh146360 IPW2200_DBG(IPW2200_DBG_SUSPEND, (sc->sc_dip, CE_CONT, 673922d2c76Seh146360 "ipw2200_detach(): suspend\n")); 674922d2c76Seh146360 return (DDI_SUCCESS); 675922d2c76Seh146360 default: 676bb5e3b2fSeh146360 return (DDI_FAILURE); 677922d2c76Seh146360 } 678bb5e3b2fSeh146360 67942516a0cSxinghua wen - Sun Microsystems - Beijing China err = mac_disable(sc->sc_ic.ic_mach); 68042516a0cSxinghua wen - Sun Microsystems - Beijing China if (err != DDI_SUCCESS) 68142516a0cSxinghua wen - Sun Microsystems - Beijing China return (err); 68242516a0cSxinghua wen - Sun Microsystems - Beijing China 683bb5e3b2fSeh146360 ipw2200_stop(sc); 684bb5e3b2fSeh146360 685bb5e3b2fSeh146360 /* 686bb5e3b2fSeh146360 * Destroy the mf_thread 687bb5e3b2fSeh146360 */ 688bb5e3b2fSeh146360 mutex_enter(&sc->sc_mflock); 689bb5e3b2fSeh146360 sc->sc_mfthread_switch = 0; 690bb5e3b2fSeh146360 while (sc->sc_mf_thread != NULL) { 691bb5e3b2fSeh146360 if (cv_wait_sig(&sc->sc_mfthread_cv, &sc->sc_mflock) == 0) 692bb5e3b2fSeh146360 break; 693bb5e3b2fSeh146360 } 694bb5e3b2fSeh146360 mutex_exit(&sc->sc_mflock); 695bb5e3b2fSeh146360 696bb5e3b2fSeh146360 /* 697bb5e3b2fSeh146360 * Unregister from the MAC layer subsystem 698bb5e3b2fSeh146360 */ 69942516a0cSxinghua wen - Sun Microsystems - Beijing China (void) mac_unregister(sc->sc_ic.ic_mach); 700bb5e3b2fSeh146360 701bb5e3b2fSeh146360 ddi_remove_intr(dip, IPW2200_PCI_INTR_NUM, sc->sc_iblk); 702bb5e3b2fSeh146360 703bb5e3b2fSeh146360 mutex_destroy(&sc->sc_ilock); 704bb5e3b2fSeh146360 mutex_destroy(&sc->sc_cmd_lock); 705bb5e3b2fSeh146360 mutex_destroy(&sc->sc_tx_lock); 706bb5e3b2fSeh146360 mutex_destroy(&sc->sc_mflock); 707924f3e72Seh146360 mutex_destroy(&sc->sc_resched_lock); 708bb5e3b2fSeh146360 cv_destroy(&sc->sc_fw_cond); 709bb5e3b2fSeh146360 cv_destroy(&sc->sc_cmd_status_cond); 710bb5e3b2fSeh146360 cv_destroy(&sc->sc_cmd_cond); 711bb5e3b2fSeh146360 cv_destroy(&sc->sc_mfthread_cv); 712bb5e3b2fSeh146360 713bb5e3b2fSeh146360 /* 714bb5e3b2fSeh146360 * Detach ieee80211 715bb5e3b2fSeh146360 */ 716bb5e3b2fSeh146360 ieee80211_detach(&sc->sc_ic); 717bb5e3b2fSeh146360 718bb5e3b2fSeh146360 (void) ipw2200_free_firmware(sc); 719bb5e3b2fSeh146360 ipw2200_ring_free(sc); 720bb5e3b2fSeh146360 721bb5e3b2fSeh146360 ddi_regs_map_free(&sc->sc_ioh); 722bb5e3b2fSeh146360 ddi_remove_minor_node(dip, NULL); 723bb5e3b2fSeh146360 ddi_soft_state_free(ipw2200_ssp, ddi_get_instance(dip)); 724bb5e3b2fSeh146360 725bb5e3b2fSeh146360 return (DDI_SUCCESS); 726bb5e3b2fSeh146360 } 727bb5e3b2fSeh146360 72814d91224Sfei feng - Sun Microsystems - Beijing China /* 72914d91224Sfei feng - Sun Microsystems - Beijing China * quiesce(9E) entry point. 73014d91224Sfei feng - Sun Microsystems - Beijing China * This function is called when the system is single-threaded at high 73114d91224Sfei feng - Sun Microsystems - Beijing China * PIL with preemption disabled. Therefore, this function must not be 73214d91224Sfei feng - Sun Microsystems - Beijing China * blocked. 73314d91224Sfei feng - Sun Microsystems - Beijing China * This function returns DDI_SUCCESS on success, or DDI_FAILURE on failure. 73414d91224Sfei feng - Sun Microsystems - Beijing China * DDI_FAILURE indicates an error condition and should almost never happen. 73514d91224Sfei feng - Sun Microsystems - Beijing China */ 73614d91224Sfei feng - Sun Microsystems - Beijing China static int 73714d91224Sfei feng - Sun Microsystems - Beijing China ipw2200_quiesce(dev_info_t *dip) 73876939ce0Seh146360 { 73976939ce0Seh146360 struct ipw2200_softc *sc = 74076939ce0Seh146360 ddi_get_soft_state(ipw2200_ssp, ddi_get_instance(dip)); 74114d91224Sfei feng - Sun Microsystems - Beijing China if (sc == NULL) 74214d91224Sfei feng - Sun Microsystems - Beijing China return (DDI_FAILURE); 74314d91224Sfei feng - Sun Microsystems - Beijing China 744faceed93Sfei feng - Sun Microsystems - Beijing China /* by pass any messages, if it's quiesce */ 745faceed93Sfei feng - Sun Microsystems - Beijing China ipw2200_debug = 0; 746faceed93Sfei feng - Sun Microsystems - Beijing China 74714d91224Sfei feng - Sun Microsystems - Beijing China /* 74814d91224Sfei feng - Sun Microsystems - Beijing China * No more blocking is allowed while we are in the 74914d91224Sfei feng - Sun Microsystems - Beijing China * quiesce(9E) entry point. 75014d91224Sfei feng - Sun Microsystems - Beijing China */ 75114d91224Sfei feng - Sun Microsystems - Beijing China sc->sc_flags |= IPW2200_FLAG_QUIESCED; 75214d91224Sfei feng - Sun Microsystems - Beijing China 75314d91224Sfei feng - Sun Microsystems - Beijing China /* 75414d91224Sfei feng - Sun Microsystems - Beijing China * Disable and mask all interrupts. 75514d91224Sfei feng - Sun Microsystems - Beijing China */ 756faceed93Sfei feng - Sun Microsystems - Beijing China ipw2200_master_stop(sc); 757faceed93Sfei feng - Sun Microsystems - Beijing China ipw2200_csr_put32(sc, IPW2200_CSR_RST, IPW2200_RST_SW_RESET); 75876939ce0Seh146360 return (DDI_SUCCESS); 75976939ce0Seh146360 } 76076939ce0Seh146360 761bb5e3b2fSeh146360 static void 762bb5e3b2fSeh146360 ipw2200_stop(struct ipw2200_softc *sc) 763bb5e3b2fSeh146360 { 764bb5e3b2fSeh146360 struct ieee80211com *ic = &sc->sc_ic; 765bb5e3b2fSeh146360 766bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_HWCAP, (sc->sc_dip, CE_CONT, 767bb5e3b2fSeh146360 "ipw2200_stop(): enter\n")); 768bb5e3b2fSeh146360 769bb5e3b2fSeh146360 ipw2200_master_stop(sc); 770bb5e3b2fSeh146360 ipw2200_csr_put32(sc, IPW2200_CSR_RST, IPW2200_RST_SW_RESET); 771bb5e3b2fSeh146360 772bb5e3b2fSeh146360 /* 773bb5e3b2fSeh146360 * Reset ring 774bb5e3b2fSeh146360 */ 775bb5e3b2fSeh146360 ipw2200_ring_reset(sc); 776bb5e3b2fSeh146360 777bb5e3b2fSeh146360 ieee80211_new_state(ic, IEEE80211_S_INIT, -1); 778bb5e3b2fSeh146360 sc->sc_flags &= ~IPW2200_FLAG_SCANNING; 779924f3e72Seh146360 sc->sc_flags &= ~IPW2200_FLAG_ASSOCIATED; 780bb5e3b2fSeh146360 781bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_HWCAP, (sc->sc_dip, CE_CONT, 782bb5e3b2fSeh146360 "ipw2200_stop(): exit\n")); 783bb5e3b2fSeh146360 } 784bb5e3b2fSeh146360 785bb5e3b2fSeh146360 static int 786bb5e3b2fSeh146360 ipw2200_config(struct ipw2200_softc *sc) 787bb5e3b2fSeh146360 { 788bb5e3b2fSeh146360 struct ieee80211com *ic = &sc->sc_ic; 789bb5e3b2fSeh146360 struct ipw2200_configuration cfg; 790bb5e3b2fSeh146360 uint32_t data; 791bb5e3b2fSeh146360 struct ipw2200_txpower pwr; 792bb5e3b2fSeh146360 struct ipw2200_rateset rs; 793bb5e3b2fSeh146360 struct ipw2200_wep_key wkey; 794bb5e3b2fSeh146360 int err, i; 795bb5e3b2fSeh146360 796bb5e3b2fSeh146360 /* 797bb5e3b2fSeh146360 * Set the IBSS mode channel: Tx power 798bb5e3b2fSeh146360 */ 799bb5e3b2fSeh146360 if (ic->ic_opmode == IEEE80211_M_IBSS) { 800bb5e3b2fSeh146360 pwr.mode = IPW2200_MODE_11B; 801bb5e3b2fSeh146360 pwr.nchan = 11; 802bb5e3b2fSeh146360 for (i = 0; i < pwr.nchan; i++) { 803bb5e3b2fSeh146360 pwr.chan[i].chan = i + 1; 804bb5e3b2fSeh146360 pwr.chan[i].power = IPW2200_TXPOWER_MAX; 805bb5e3b2fSeh146360 } 806bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT, 807bb5e3b2fSeh146360 "ipw2200_config(): Setting .11b channels Tx power\n")); 808bb5e3b2fSeh146360 err = ipw2200_cmd(sc, IPW2200_CMD_SET_TX_POWER, 809bb5e3b2fSeh146360 &pwr, sizeof (pwr), 0); 810bb5e3b2fSeh146360 if (err != DDI_SUCCESS) 811bb5e3b2fSeh146360 return (err); 812bb5e3b2fSeh146360 813bb5e3b2fSeh146360 pwr.mode = IPW2200_MODE_11G; 814bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT, 815bb5e3b2fSeh146360 "ipw2200_config(): Setting .11g channels Tx power\n")); 816bb5e3b2fSeh146360 err = ipw2200_cmd(sc, IPW2200_CMD_SET_TX_POWER, 817bb5e3b2fSeh146360 &pwr, sizeof (pwr), 0); 818bb5e3b2fSeh146360 if (err != DDI_SUCCESS) 819bb5e3b2fSeh146360 return (err); 820bb5e3b2fSeh146360 } 821bb5e3b2fSeh146360 822bb5e3b2fSeh146360 /* 823bb5e3b2fSeh146360 * Set MAC address 824bb5e3b2fSeh146360 */ 825bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT, 826bb5e3b2fSeh146360 "ipw2200_config(): Setting MAC address to " 827bb5e3b2fSeh146360 "%02x:%02x:%02x:%02x:%02x:%02x\n", 828bb5e3b2fSeh146360 ic->ic_macaddr[0], ic->ic_macaddr[1], ic->ic_macaddr[2], 829bb5e3b2fSeh146360 ic->ic_macaddr[3], ic->ic_macaddr[4], ic->ic_macaddr[5])); 830bb5e3b2fSeh146360 err = ipw2200_cmd(sc, IPW2200_CMD_SET_MAC_ADDRESS, ic->ic_macaddr, 831bb5e3b2fSeh146360 IEEE80211_ADDR_LEN, 0); 832bb5e3b2fSeh146360 if (err != DDI_SUCCESS) 833bb5e3b2fSeh146360 return (err); 834bb5e3b2fSeh146360 835bb5e3b2fSeh146360 /* 836bb5e3b2fSeh146360 * Set basic system config settings: configuration(capabilities) 837bb5e3b2fSeh146360 */ 838bb5e3b2fSeh146360 (void) memset(&cfg, 0, sizeof (cfg)); 839bb5e3b2fSeh146360 cfg.bluetooth_coexistence = 1; 840bb5e3b2fSeh146360 cfg.multicast_enabled = 1; 841bb5e3b2fSeh146360 cfg.answer_pbreq = 1; 842bb5e3b2fSeh146360 cfg.noise_reported = 1; 843924f3e72Seh146360 cfg.disable_multicast_decryption = 1; /* WPA */ 844924f3e72Seh146360 cfg.disable_unicast_decryption = 1; /* WPA */ 845bb5e3b2fSeh146360 846bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT, 847bb5e3b2fSeh146360 "ipw2200_config(): Configuring adapter\n")); 848bb5e3b2fSeh146360 err = ipw2200_cmd(sc, IPW2200_CMD_SET_CONFIG, 849bb5e3b2fSeh146360 &cfg, sizeof (cfg), 0); 850bb5e3b2fSeh146360 if (err != DDI_SUCCESS) 851bb5e3b2fSeh146360 return (err); 852bb5e3b2fSeh146360 853bb5e3b2fSeh146360 /* 854bb5e3b2fSeh146360 * Set power mode 855bb5e3b2fSeh146360 */ 856bb5e3b2fSeh146360 data = LE_32(IPW2200_POWER_MODE_CAM); 857bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT, 858bb5e3b2fSeh146360 "ipw2200_config(): Setting power mode to %u\n", LE_32(data))); 859bb5e3b2fSeh146360 err = ipw2200_cmd(sc, IPW2200_CMD_SET_POWER_MODE, 860bb5e3b2fSeh146360 &data, sizeof (data), 0); 861bb5e3b2fSeh146360 if (err != DDI_SUCCESS) 862bb5e3b2fSeh146360 return (err); 863bb5e3b2fSeh146360 864bb5e3b2fSeh146360 /* 865bb5e3b2fSeh146360 * Set supported rates 866bb5e3b2fSeh146360 */ 867bb5e3b2fSeh146360 rs.mode = IPW2200_MODE_11G; 868bb5e3b2fSeh146360 rs.type = IPW2200_RATESET_TYPE_SUPPORTED; 869bb5e3b2fSeh146360 rs.nrates = ic->ic_sup_rates[IEEE80211_MODE_11G].ir_nrates; 870bb5e3b2fSeh146360 (void) memcpy(rs.rates, ic->ic_sup_rates[IEEE80211_MODE_11G].ir_rates, 871bb5e3b2fSeh146360 rs.nrates); 872bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT, 873bb5e3b2fSeh146360 "ipw2200_config(): Setting .11g supported rates(%u)\n", rs.nrates)); 874bb5e3b2fSeh146360 err = ipw2200_cmd(sc, IPW2200_CMD_SET_RATES, &rs, sizeof (rs), 0); 875bb5e3b2fSeh146360 if (err != DDI_SUCCESS) 876bb5e3b2fSeh146360 return (err); 877bb5e3b2fSeh146360 878bb5e3b2fSeh146360 rs.mode = IPW2200_MODE_11A; 879bb5e3b2fSeh146360 rs.type = IPW2200_RATESET_TYPE_SUPPORTED; 880bb5e3b2fSeh146360 rs.nrates = ic->ic_sup_rates[IEEE80211_MODE_11A].ir_nrates; 881bb5e3b2fSeh146360 (void) memcpy(rs.rates, ic->ic_sup_rates[IEEE80211_MODE_11A].ir_rates, 882bb5e3b2fSeh146360 rs.nrates); 883bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT, 884bb5e3b2fSeh146360 "ipw2200_config(): Setting .11a supported rates(%u)\n", rs.nrates)); 885bb5e3b2fSeh146360 err = ipw2200_cmd(sc, IPW2200_CMD_SET_RATES, &rs, sizeof (rs), 0); 886bb5e3b2fSeh146360 if (err != DDI_SUCCESS) 887bb5e3b2fSeh146360 return (err); 888bb5e3b2fSeh146360 889bb5e3b2fSeh146360 /* 890bb5e3b2fSeh146360 * Set RTS(request-to-send) threshold 891bb5e3b2fSeh146360 */ 892bb5e3b2fSeh146360 data = LE_32(ic->ic_rtsthreshold); 893bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT, 894bb5e3b2fSeh146360 "ipw2200_config(): Setting RTS threshold to %u\n", LE_32(data))); 895bb5e3b2fSeh146360 err = ipw2200_cmd(sc, IPW2200_CMD_SET_RTS_THRESHOLD, &data, 896bb5e3b2fSeh146360 sizeof (data), 0); 897bb5e3b2fSeh146360 if (err != DDI_SUCCESS) 898bb5e3b2fSeh146360 return (err); 899bb5e3b2fSeh146360 900bb5e3b2fSeh146360 /* 901bb5e3b2fSeh146360 * Set fragmentation threshold 902bb5e3b2fSeh146360 */ 903bb5e3b2fSeh146360 data = LE_32(ic->ic_fragthreshold); 904bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT, 905bb5e3b2fSeh146360 "ipw2200_config(): Setting fragmentation threshold to %u\n", 906bb5e3b2fSeh146360 LE_32(data))); 907bb5e3b2fSeh146360 err = ipw2200_cmd(sc, IPW2200_CMD_SET_FRAG_THRESHOLD, &data, 908bb5e3b2fSeh146360 sizeof (data), 0); 909bb5e3b2fSeh146360 if (err != DDI_SUCCESS) 910bb5e3b2fSeh146360 return (err); 911bb5e3b2fSeh146360 912bb5e3b2fSeh146360 /* 913bb5e3b2fSeh146360 * Set desired ESSID if we have 914bb5e3b2fSeh146360 */ 915bb5e3b2fSeh146360 if (ic->ic_des_esslen != 0) { 916bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT, 917bb5e3b2fSeh146360 "ipw2200_config(): Setting desired ESSID to " 918bb5e3b2fSeh146360 "(%u),%c%c%c%c%c%c%c%c\n", 919bb5e3b2fSeh146360 ic->ic_des_esslen, 920bb5e3b2fSeh146360 ic->ic_des_essid[0], ic->ic_des_essid[1], 921bb5e3b2fSeh146360 ic->ic_des_essid[2], ic->ic_des_essid[3], 922bb5e3b2fSeh146360 ic->ic_des_essid[4], ic->ic_des_essid[5], 923bb5e3b2fSeh146360 ic->ic_des_essid[6], ic->ic_des_essid[7])); 924bb5e3b2fSeh146360 err = ipw2200_cmd(sc, IPW2200_CMD_SET_ESSID, ic->ic_des_essid, 925bb5e3b2fSeh146360 ic->ic_des_esslen, 0); 926bb5e3b2fSeh146360 if (err != DDI_SUCCESS) 927bb5e3b2fSeh146360 return (err); 928bb5e3b2fSeh146360 } 929bb5e3b2fSeh146360 930bb5e3b2fSeh146360 /* 931bb5e3b2fSeh146360 * Set WEP initial vector(random seed) 932bb5e3b2fSeh146360 */ 933bb5e3b2fSeh146360 (void) random_get_pseudo_bytes((uint8_t *)&data, sizeof (data)); 934bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT, 935bb5e3b2fSeh146360 "ipw2200_config(): Setting initialization vector to %u\n", 936bb5e3b2fSeh146360 LE_32(data))); 937bb5e3b2fSeh146360 err = ipw2200_cmd(sc, IPW2200_CMD_SET_IV, &data, sizeof (data), 0); 938bb5e3b2fSeh146360 if (err != DDI_SUCCESS) 939bb5e3b2fSeh146360 return (err); 940bb5e3b2fSeh146360 941bb5e3b2fSeh146360 /* 942bb5e3b2fSeh146360 * Set WEP if any 943bb5e3b2fSeh146360 */ 944bb5e3b2fSeh146360 if (ic->ic_flags & IEEE80211_F_PRIVACY) { 945bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT, 946bb5e3b2fSeh146360 "ipw2200_config(): Setting Wep Key\n", LE_32(data))); 947bb5e3b2fSeh146360 for (i = 0; i < IEEE80211_WEP_NKID; i++) { 948bb5e3b2fSeh146360 wkey.cmd = IPW2200_WEP_KEY_CMD_SETKEY; 949bb5e3b2fSeh146360 wkey.idx = (uint8_t)i; 950bb5e3b2fSeh146360 wkey.len = ic->ic_nw_keys[i].wk_keylen; 951bb5e3b2fSeh146360 (void) memset(wkey.key, 0, sizeof (wkey.key)); 952bb5e3b2fSeh146360 if (ic->ic_nw_keys[i].wk_keylen) 953bb5e3b2fSeh146360 (void) memcpy(wkey.key, 954bb5e3b2fSeh146360 ic->ic_nw_keys[i].wk_key, 955bb5e3b2fSeh146360 ic->ic_nw_keys[i].wk_keylen); 956bb5e3b2fSeh146360 err = ipw2200_cmd(sc, IPW2200_CMD_SET_WEP_KEY, 957bb5e3b2fSeh146360 &wkey, sizeof (wkey), 0); 958bb5e3b2fSeh146360 if (err != DDI_SUCCESS) 959bb5e3b2fSeh146360 return (err); 960bb5e3b2fSeh146360 } 961bb5e3b2fSeh146360 } 962bb5e3b2fSeh146360 963bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT, 964bb5e3b2fSeh146360 "ipw2200_config(): Enabling adapter\n")); 965bb5e3b2fSeh146360 966bb5e3b2fSeh146360 return (ipw2200_cmd(sc, IPW2200_CMD_ENABLE, NULL, 0, 0)); 967bb5e3b2fSeh146360 } 968bb5e3b2fSeh146360 969bb5e3b2fSeh146360 static int 970bb5e3b2fSeh146360 ipw2200_cmd(struct ipw2200_softc *sc, 971bb5e3b2fSeh146360 uint32_t type, void *buf, size_t len, int async) 972bb5e3b2fSeh146360 { 973bb5e3b2fSeh146360 struct ipw2200_cmd_desc *cmd; 974bb5e3b2fSeh146360 clock_t clk; 975bb5e3b2fSeh146360 uint32_t idx; 976bb5e3b2fSeh146360 977bb5e3b2fSeh146360 mutex_enter(&sc->sc_cmd_lock); 978bb5e3b2fSeh146360 while (sc->sc_cmd_free < 1) 979bb5e3b2fSeh146360 cv_wait(&sc->sc_cmd_cond, &sc->sc_cmd_lock); 980bb5e3b2fSeh146360 981bb5e3b2fSeh146360 idx = sc->sc_cmd_cur; 982bb5e3b2fSeh146360 cmd = &sc->sc_cmdsc[idx]; 983bb5e3b2fSeh146360 (void) memset(cmd, 0, sizeof (*cmd)); 984bb5e3b2fSeh146360 985bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_RING, (sc->sc_dip, CE_CONT, 986bb5e3b2fSeh146360 "ipw2200_cmd(): cmd-cur=%d\n", idx)); 987bb5e3b2fSeh146360 988bb5e3b2fSeh146360 cmd->hdr.type = IPW2200_HDR_TYPE_COMMAND; 989bb5e3b2fSeh146360 cmd->hdr.flags = IPW2200_HDR_FLAG_IRQ; 990bb5e3b2fSeh146360 cmd->type = (uint8_t)type; 991bb5e3b2fSeh146360 if (len == 0 || buf == NULL) 992bb5e3b2fSeh146360 cmd->len = 0; 993bb5e3b2fSeh146360 else { 994bb5e3b2fSeh146360 cmd->len = (uint8_t)len; 995bb5e3b2fSeh146360 (void) memcpy(cmd->data, buf, len); 996bb5e3b2fSeh146360 } 997bb5e3b2fSeh146360 sc->sc_done[idx] = 0; 998bb5e3b2fSeh146360 999bb5e3b2fSeh146360 /* 1000bb5e3b2fSeh146360 * DMA sync 1001bb5e3b2fSeh146360 */ 1002bb5e3b2fSeh146360 (void) ddi_dma_sync(sc->sc_dma_cmdsc.dr_hnd, 1003bb5e3b2fSeh146360 idx * sizeof (struct ipw2200_cmd_desc), 1004bb5e3b2fSeh146360 sizeof (struct ipw2200_cmd_desc), DDI_DMA_SYNC_FORDEV); 1005bb5e3b2fSeh146360 1006bb5e3b2fSeh146360 sc->sc_cmd_cur = RING_FORWARD(sc->sc_cmd_cur, 1, IPW2200_CMD_RING_SIZE); 1007bb5e3b2fSeh146360 sc->sc_cmd_free--; 1008bb5e3b2fSeh146360 1009bb5e3b2fSeh146360 ipw2200_csr_put32(sc, IPW2200_CSR_CMD_WRITE_INDEX, sc->sc_cmd_cur); 1010bb5e3b2fSeh146360 1011bb5e3b2fSeh146360 mutex_exit(&sc->sc_cmd_lock); 1012bb5e3b2fSeh146360 1013bb5e3b2fSeh146360 if (async) 1014bb5e3b2fSeh146360 goto out; 1015bb5e3b2fSeh146360 1016bb5e3b2fSeh146360 /* 1017bb5e3b2fSeh146360 * Wait for command done 1018bb5e3b2fSeh146360 */ 1019d3d50737SRafael Vanoni clk = drv_usectohz(5000000); 1020bb5e3b2fSeh146360 mutex_enter(&sc->sc_ilock); 1021bb5e3b2fSeh146360 while (sc->sc_done[idx] == 0) { 1022bb5e3b2fSeh146360 /* pending */ 1023d3d50737SRafael Vanoni if (cv_reltimedwait(&sc->sc_cmd_status_cond, &sc->sc_ilock, 1024d3d50737SRafael Vanoni clk, TR_CLOCK_TICK) < 0) 1025bb5e3b2fSeh146360 break; 1026bb5e3b2fSeh146360 } 1027bb5e3b2fSeh146360 mutex_exit(&sc->sc_ilock); 1028bb5e3b2fSeh146360 1029bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_RING, (sc->sc_dip, CE_CONT, 1030bb5e3b2fSeh146360 "ipw2200_cmd(): cmd-done=%s\n", sc->sc_done[idx] ? "yes" : "no")); 1031bb5e3b2fSeh146360 1032bb5e3b2fSeh146360 if (sc->sc_done[idx] == 0) 1033bb5e3b2fSeh146360 return (DDI_FAILURE); 1034bb5e3b2fSeh146360 1035bb5e3b2fSeh146360 out: 1036bb5e3b2fSeh146360 return (DDI_SUCCESS); 1037bb5e3b2fSeh146360 } 1038bb5e3b2fSeh146360 1039bb5e3b2fSeh146360 /* 1040bb5e3b2fSeh146360 * If init failed, it will call stop internally. Therefore, it's unnecessary 1041bb5e3b2fSeh146360 * to call ipw2200_stop() when this subroutine is failed. Otherwise, it may 1042bb5e3b2fSeh146360 * be called twice. 1043bb5e3b2fSeh146360 */ 1044bb5e3b2fSeh146360 int 1045bb5e3b2fSeh146360 ipw2200_init(struct ipw2200_softc *sc) 1046bb5e3b2fSeh146360 { 1047bb5e3b2fSeh146360 int err; 1048bb5e3b2fSeh146360 1049bb5e3b2fSeh146360 /* 1050bb5e3b2fSeh146360 * No firmware is available, failed 1051bb5e3b2fSeh146360 */ 1052bb5e3b2fSeh146360 if (!(sc->sc_flags & IPW2200_FLAG_FW_CACHED)) { 1053bb5e3b2fSeh146360 IPW2200_WARN((sc->sc_dip, CE_WARN, 1054bb5e3b2fSeh146360 "ipw2200_init(): no firmware is available\n")); 1055bb5e3b2fSeh146360 return (DDI_FAILURE); /* return directly */ 1056bb5e3b2fSeh146360 } 1057bb5e3b2fSeh146360 1058bb5e3b2fSeh146360 ipw2200_stop(sc); 1059bb5e3b2fSeh146360 1060bb5e3b2fSeh146360 err = ipw2200_chip_reset(sc); 1061bb5e3b2fSeh146360 if (err != DDI_SUCCESS) { 1062bb5e3b2fSeh146360 IPW2200_WARN((sc->sc_dip, CE_WARN, 1063bb5e3b2fSeh146360 "ipw2200_init(): could not reset adapter\n")); 1064bb5e3b2fSeh146360 goto fail; 1065bb5e3b2fSeh146360 } 1066bb5e3b2fSeh146360 1067bb5e3b2fSeh146360 /* 1068bb5e3b2fSeh146360 * Load boot code 1069bb5e3b2fSeh146360 */ 1070bb5e3b2fSeh146360 err = ipw2200_load_fw(sc, sc->sc_fw.boot_base, sc->sc_fw.boot_size); 1071bb5e3b2fSeh146360 if (err != DDI_SUCCESS) { 1072bb5e3b2fSeh146360 IPW2200_WARN((sc->sc_dip, CE_WARN, 1073bb5e3b2fSeh146360 "ipw2200_init(): could not load boot code\n")); 1074bb5e3b2fSeh146360 goto fail; 1075bb5e3b2fSeh146360 } 1076bb5e3b2fSeh146360 1077bb5e3b2fSeh146360 /* 1078bb5e3b2fSeh146360 * Load boot microcode 1079bb5e3b2fSeh146360 */ 1080bb5e3b2fSeh146360 err = ipw2200_load_uc(sc, sc->sc_fw.uc_base, sc->sc_fw.uc_size); 1081bb5e3b2fSeh146360 if (err != DDI_SUCCESS) { 1082bb5e3b2fSeh146360 IPW2200_WARN((sc->sc_dip, CE_WARN, 1083bb5e3b2fSeh146360 "ipw2200_init(): could not load microcode\n")); 1084bb5e3b2fSeh146360 goto fail; 1085bb5e3b2fSeh146360 } 1086bb5e3b2fSeh146360 1087bb5e3b2fSeh146360 ipw2200_master_stop(sc); 1088bb5e3b2fSeh146360 ipw2200_ring_hwsetup(sc); 1089bb5e3b2fSeh146360 1090bb5e3b2fSeh146360 /* 1091bb5e3b2fSeh146360 * Load firmware 1092bb5e3b2fSeh146360 */ 1093bb5e3b2fSeh146360 err = ipw2200_load_fw(sc, sc->sc_fw.fw_base, sc->sc_fw.fw_size); 1094bb5e3b2fSeh146360 if (err != DDI_SUCCESS) { 1095bb5e3b2fSeh146360 IPW2200_WARN((sc->sc_dip, CE_WARN, 1096bb5e3b2fSeh146360 "ipw2200_init(): could not load firmware\n")); 1097bb5e3b2fSeh146360 goto fail; 1098bb5e3b2fSeh146360 } 1099bb5e3b2fSeh146360 1100bb5e3b2fSeh146360 sc->sc_flags |= IPW2200_FLAG_FW_INITED; 1101bb5e3b2fSeh146360 1102bb5e3b2fSeh146360 /* 1103bb5e3b2fSeh146360 * Hardware will be enabled after configuration 1104bb5e3b2fSeh146360 */ 1105bb5e3b2fSeh146360 err = ipw2200_config(sc); 1106bb5e3b2fSeh146360 if (err != DDI_SUCCESS) { 1107bb5e3b2fSeh146360 IPW2200_WARN((sc->sc_dip, CE_WARN, 1108bb5e3b2fSeh146360 "ipw2200_init(): device configuration failed\n")); 1109bb5e3b2fSeh146360 goto fail; 1110bb5e3b2fSeh146360 } 1111bb5e3b2fSeh146360 1112bb5e3b2fSeh146360 /* 1113bb5e3b2fSeh146360 * workround to prevent too many h/w error. 1114bb5e3b2fSeh146360 * delay for a while till h/w is stable. 1115bb5e3b2fSeh146360 */ 1116bb5e3b2fSeh146360 delay(drv_usectohz(delay_config_stable)); 1117bb5e3b2fSeh146360 1118bb5e3b2fSeh146360 return (DDI_SUCCESS); /* return successfully */ 1119bb5e3b2fSeh146360 fail: 1120bb5e3b2fSeh146360 ipw2200_stop(sc); 1121bb5e3b2fSeh146360 return (err); 1122bb5e3b2fSeh146360 } 1123bb5e3b2fSeh146360 1124bb5e3b2fSeh146360 /* 1125bb5e3b2fSeh146360 * get hardware configurations from EEPROM embedded within PRO/2200 1126bb5e3b2fSeh146360 */ 1127bb5e3b2fSeh146360 static void 1128bb5e3b2fSeh146360 ipw2200_hwconf_get(struct ipw2200_softc *sc) 1129bb5e3b2fSeh146360 { 1130bb5e3b2fSeh146360 int i; 1131bb5e3b2fSeh146360 uint16_t val; 1132bb5e3b2fSeh146360 1133bb5e3b2fSeh146360 /* 1134bb5e3b2fSeh146360 * Get mac address 1135bb5e3b2fSeh146360 */ 1136bb5e3b2fSeh146360 i = 0; 1137bb5e3b2fSeh146360 val = ipw2200_rom_get16(sc, IPW2200_EEPROM_MAC + 0); 1138bb5e3b2fSeh146360 sc->sc_macaddr[i++] = val >> 8; 1139bb5e3b2fSeh146360 sc->sc_macaddr[i++] = val & 0xff; 1140bb5e3b2fSeh146360 val = ipw2200_rom_get16(sc, IPW2200_EEPROM_MAC + 1); 1141bb5e3b2fSeh146360 sc->sc_macaddr[i++] = val >> 8; 1142bb5e3b2fSeh146360 sc->sc_macaddr[i++] = val & 0xff; 1143bb5e3b2fSeh146360 val = ipw2200_rom_get16(sc, IPW2200_EEPROM_MAC + 2); 1144bb5e3b2fSeh146360 sc->sc_macaddr[i++] = val >> 8; 1145bb5e3b2fSeh146360 sc->sc_macaddr[i++] = val & 0xff; 1146bb5e3b2fSeh146360 1147bb5e3b2fSeh146360 /* 1148bb5e3b2fSeh146360 * formatted MAC address string 1149bb5e3b2fSeh146360 */ 1150bb5e3b2fSeh146360 (void) snprintf(sc->sc_macstr, sizeof (sc->sc_macstr), 1151bb5e3b2fSeh146360 "%02x:%02x:%02x:%02x:%02x:%02x", 1152bb5e3b2fSeh146360 sc->sc_macaddr[0], sc->sc_macaddr[1], 1153bb5e3b2fSeh146360 sc->sc_macaddr[2], sc->sc_macaddr[3], 1154bb5e3b2fSeh146360 sc->sc_macaddr[4], sc->sc_macaddr[5]); 1155bb5e3b2fSeh146360 1156bb5e3b2fSeh146360 } 1157bb5e3b2fSeh146360 1158bb5e3b2fSeh146360 /* 1159bb5e3b2fSeh146360 * all ipw2200 interrupts will be masked by this routine 1160bb5e3b2fSeh146360 */ 1161bb5e3b2fSeh146360 static void 1162bb5e3b2fSeh146360 ipw2200_master_stop(struct ipw2200_softc *sc) 1163bb5e3b2fSeh146360 { 1164bb5e3b2fSeh146360 int ntries; 1165bb5e3b2fSeh146360 1166bb5e3b2fSeh146360 /* 1167bb5e3b2fSeh146360 * disable interrupts 1168bb5e3b2fSeh146360 */ 1169bb5e3b2fSeh146360 ipw2200_csr_put32(sc, IPW2200_CSR_INTR_MASK, 0); 1170bb5e3b2fSeh146360 ipw2200_csr_put32(sc, IPW2200_CSR_RST, IPW2200_RST_STOP_MASTER); 1171bb5e3b2fSeh146360 1172bb5e3b2fSeh146360 /* 1173bb5e3b2fSeh146360 * wait long enough to ensure hardware stop successfully. 1174bb5e3b2fSeh146360 */ 1175bb5e3b2fSeh146360 for (ntries = 0; ntries < 500; ntries++) { 1176bb5e3b2fSeh146360 if (ipw2200_csr_get32(sc, IPW2200_CSR_RST) & 1177bb5e3b2fSeh146360 IPW2200_RST_MASTER_DISABLED) 1178bb5e3b2fSeh146360 break; 1179bb5e3b2fSeh146360 /* wait for a while */ 1180bb5e3b2fSeh146360 drv_usecwait(100); 1181bb5e3b2fSeh146360 } 1182faceed93Sfei feng - Sun Microsystems - Beijing China if (ntries == 500) 1183bb5e3b2fSeh146360 IPW2200_WARN((sc->sc_dip, CE_WARN, 1184bb5e3b2fSeh146360 "ipw2200_master_stop(): timeout\n")); 1185bb5e3b2fSeh146360 1186bb5e3b2fSeh146360 ipw2200_csr_put32(sc, IPW2200_CSR_RST, 1187bb5e3b2fSeh146360 IPW2200_RST_PRINCETON_RESET | 1188bb5e3b2fSeh146360 ipw2200_csr_get32(sc, IPW2200_CSR_RST)); 1189bb5e3b2fSeh146360 1190bb5e3b2fSeh146360 sc->sc_flags &= ~IPW2200_FLAG_FW_INITED; 1191bb5e3b2fSeh146360 } 1192bb5e3b2fSeh146360 1193bb5e3b2fSeh146360 /* 1194bb5e3b2fSeh146360 * all ipw2200 interrupts will be masked by this routine 1195bb5e3b2fSeh146360 */ 1196bb5e3b2fSeh146360 static int 1197bb5e3b2fSeh146360 ipw2200_chip_reset(struct ipw2200_softc *sc) 1198bb5e3b2fSeh146360 { 1199bb5e3b2fSeh146360 uint32_t tmp; 1200bb5e3b2fSeh146360 int ntries, i; 1201bb5e3b2fSeh146360 1202bb5e3b2fSeh146360 ipw2200_master_stop(sc); 1203bb5e3b2fSeh146360 1204bb5e3b2fSeh146360 /* 1205bb5e3b2fSeh146360 * Move adapter to DO state 1206bb5e3b2fSeh146360 */ 1207bb5e3b2fSeh146360 tmp = ipw2200_csr_get32(sc, IPW2200_CSR_CTL); 1208bb5e3b2fSeh146360 ipw2200_csr_put32(sc, IPW2200_CSR_CTL, tmp | IPW2200_CTL_INIT); 1209bb5e3b2fSeh146360 1210bb5e3b2fSeh146360 /* 1211bb5e3b2fSeh146360 * Initialize Phase-Locked Level (PLL) 1212bb5e3b2fSeh146360 */ 1213bb5e3b2fSeh146360 ipw2200_csr_put32(sc, IPW2200_CSR_READ_INT, IPW2200_READ_INT_INIT_HOST); 1214bb5e3b2fSeh146360 1215bb5e3b2fSeh146360 /* 1216bb5e3b2fSeh146360 * Wait for clock stabilization 1217bb5e3b2fSeh146360 */ 1218bb5e3b2fSeh146360 for (ntries = 0; ntries < 1000; ntries++) { 1219bb5e3b2fSeh146360 if (ipw2200_csr_get32(sc, IPW2200_CSR_CTL) & 1220bb5e3b2fSeh146360 IPW2200_CTL_CLOCK_READY) 1221bb5e3b2fSeh146360 break; 1222bb5e3b2fSeh146360 drv_usecwait(200); 1223bb5e3b2fSeh146360 } 1224bb5e3b2fSeh146360 if (ntries == 1000) { 1225bb5e3b2fSeh146360 IPW2200_WARN((sc->sc_dip, CE_WARN, 1226bb5e3b2fSeh146360 "ipw2200_chip_reset(): timeout\n")); 1227bb5e3b2fSeh146360 return (DDI_FAILURE); 1228bb5e3b2fSeh146360 } 1229bb5e3b2fSeh146360 1230bb5e3b2fSeh146360 tmp = ipw2200_csr_get32(sc, IPW2200_CSR_RST); 1231bb5e3b2fSeh146360 ipw2200_csr_put32(sc, IPW2200_CSR_RST, tmp | IPW2200_RST_SW_RESET); 1232bb5e3b2fSeh146360 1233bb5e3b2fSeh146360 drv_usecwait(10); 1234bb5e3b2fSeh146360 1235bb5e3b2fSeh146360 tmp = ipw2200_csr_get32(sc, IPW2200_CSR_CTL); 1236bb5e3b2fSeh146360 ipw2200_csr_put32(sc, IPW2200_CSR_CTL, tmp | IPW2200_CTL_INIT); 1237bb5e3b2fSeh146360 1238bb5e3b2fSeh146360 /* 1239bb5e3b2fSeh146360 * clear NIC memory 1240bb5e3b2fSeh146360 */ 1241bb5e3b2fSeh146360 ipw2200_csr_put32(sc, IPW2200_CSR_AUTOINC_ADDR, 0); 1242bb5e3b2fSeh146360 for (i = 0; i < 0xc000; i++) 1243bb5e3b2fSeh146360 ipw2200_csr_put32(sc, IPW2200_CSR_AUTOINC_DATA, 0); 1244bb5e3b2fSeh146360 1245bb5e3b2fSeh146360 return (DDI_SUCCESS); 1246bb5e3b2fSeh146360 } 1247bb5e3b2fSeh146360 1248bb5e3b2fSeh146360 /* 1249bb5e3b2fSeh146360 * This function is used by wificonfig/dladm to get the current 1250bb5e3b2fSeh146360 * radio status, it is off/on 1251bb5e3b2fSeh146360 */ 1252bb5e3b2fSeh146360 int 1253bb5e3b2fSeh146360 ipw2200_radio_status(struct ipw2200_softc *sc) 1254bb5e3b2fSeh146360 { 1255bb5e3b2fSeh146360 int val; 1256bb5e3b2fSeh146360 1257bb5e3b2fSeh146360 val = (ipw2200_csr_get32(sc, IPW2200_CSR_IO) & 1258bb5e3b2fSeh146360 IPW2200_IO_RADIO_ENABLED) ? 1 : 0; 1259bb5e3b2fSeh146360 1260bb5e3b2fSeh146360 return (val); 1261bb5e3b2fSeh146360 } 1262bb5e3b2fSeh146360 /* 1263bb5e3b2fSeh146360 * This function is used to get the statistic 1264bb5e3b2fSeh146360 */ 1265bb5e3b2fSeh146360 void 1266bb5e3b2fSeh146360 ipw2200_get_statistics(struct ipw2200_softc *sc) 1267bb5e3b2fSeh146360 { 1268bb5e3b2fSeh146360 struct ieee80211com *ic = &sc->sc_ic; 1269bb5e3b2fSeh146360 1270bb5e3b2fSeh146360 uint32_t size, buf[128]; 1271bb5e3b2fSeh146360 1272bb5e3b2fSeh146360 if (!(sc->sc_flags & IPW2200_FLAG_FW_INITED)) { 1273bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_IOCTL, (sc->sc_dip, CE_CONT, 1274bb5e3b2fSeh146360 "ipw2200_get_statistic(): fw doesn't download yet.")); 1275bb5e3b2fSeh146360 return; 1276bb5e3b2fSeh146360 } 1277bb5e3b2fSeh146360 1278bb5e3b2fSeh146360 size = min(ipw2200_csr_get32(sc, IPW2200_CSR_TABLE0_SIZE), 128 - 1); 1279bb5e3b2fSeh146360 ipw2200_csr_getbuf32(sc, IPW2200_CSR_TABLE0_BASE, &buf[1], size); 1280bb5e3b2fSeh146360 1281bb5e3b2fSeh146360 /* 1282bb5e3b2fSeh146360 * To retrieve the statistic information into proper places. There are 1283bb5e3b2fSeh146360 * lot of information. These table will be read once a second. 1284bb5e3b2fSeh146360 * Hopefully, it will not effect the performance. 1285bb5e3b2fSeh146360 */ 1286bb5e3b2fSeh146360 1287bb5e3b2fSeh146360 /* 1288bb5e3b2fSeh146360 * For the tx/crc information, we can get them from chip directly; 1289bb5e3b2fSeh146360 * For the rx/wep error/(rts) related information, leave them net80211. 1290bb5e3b2fSeh146360 */ 1291bb5e3b2fSeh146360 /* WIFI_STAT_TX_FRAGS */ 1292bb5e3b2fSeh146360 ic->ic_stats.is_tx_frags = (uint32_t)buf[5]; 1293bb5e3b2fSeh146360 /* WIFI_STAT_MCAST_TX */ 1294bb5e3b2fSeh146360 ic->ic_stats.is_tx_mcast = (uint32_t)buf[31]; 1295bb5e3b2fSeh146360 /* WIFI_STAT_TX_RETRANS */ 1296bb5e3b2fSeh146360 ic->ic_stats.is_tx_retries = (uint32_t)buf[56]; 1297bb5e3b2fSeh146360 /* WIFI_STAT_TX_FAILED */ 1298bb5e3b2fSeh146360 ic->ic_stats.is_tx_failed = (uint32_t)buf[57]; 1299bb5e3b2fSeh146360 /* MAC_STAT_OBYTES */ 1300bb5e3b2fSeh146360 ic->ic_stats.is_tx_bytes = (uint32_t)buf[64]; 1301bb5e3b2fSeh146360 } 1302bb5e3b2fSeh146360 1303bb5e3b2fSeh146360 /* 1304bb5e3b2fSeh146360 * DMA region alloc subroutine 1305bb5e3b2fSeh146360 */ 1306bb5e3b2fSeh146360 int 1307bb5e3b2fSeh146360 ipw2200_dma_region_alloc(struct ipw2200_softc *sc, struct dma_region *dr, 1308bb5e3b2fSeh146360 size_t size, uint_t dir, uint_t flags) 1309bb5e3b2fSeh146360 { 1310bb5e3b2fSeh146360 dev_info_t *dip = sc->sc_dip; 1311bb5e3b2fSeh146360 int err; 1312bb5e3b2fSeh146360 1313bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_DMA, (sc->sc_dip, CE_CONT, 1314bb5e3b2fSeh146360 "ipw2200_dma_region_alloc(): size =%u\n", size)); 1315bb5e3b2fSeh146360 1316bb5e3b2fSeh146360 err = ddi_dma_alloc_handle(dip, &ipw2200_dma_attr, DDI_DMA_SLEEP, NULL, 1317bb5e3b2fSeh146360 &dr->dr_hnd); 1318bb5e3b2fSeh146360 if (err != DDI_SUCCESS) { 1319bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_DMA, (sc->sc_dip, CE_CONT, 1320bb5e3b2fSeh146360 "ipw2200_dma_region_alloc(): " 1321bb5e3b2fSeh146360 "ddi_dma_alloc_handle() failed\n")); 1322bb5e3b2fSeh146360 goto fail0; 1323bb5e3b2fSeh146360 } 1324bb5e3b2fSeh146360 1325bb5e3b2fSeh146360 err = ddi_dma_mem_alloc(dr->dr_hnd, size, &ipw2200_dma_accattr, 1326bb5e3b2fSeh146360 flags, DDI_DMA_SLEEP, NULL, 1327bb5e3b2fSeh146360 &dr->dr_base, &dr->dr_size, &dr->dr_acc); 1328bb5e3b2fSeh146360 if (err != DDI_SUCCESS) { 1329bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_DMA, (sc->sc_dip, CE_CONT, 1330bb5e3b2fSeh146360 "ipw2200_dma_region_alloc(): " 1331bb5e3b2fSeh146360 "ddi_dma_mem_alloc() failed\n")); 1332bb5e3b2fSeh146360 goto fail1; 1333bb5e3b2fSeh146360 } 1334bb5e3b2fSeh146360 1335bb5e3b2fSeh146360 err = ddi_dma_addr_bind_handle(dr->dr_hnd, NULL, 1336bb5e3b2fSeh146360 dr->dr_base, dr->dr_size, 1337bb5e3b2fSeh146360 dir | flags, DDI_DMA_SLEEP, NULL, 1338bb5e3b2fSeh146360 &dr->dr_cookie, &dr->dr_ccnt); 1339bb5e3b2fSeh146360 if (err != DDI_DMA_MAPPED) { 1340bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_DMA, (sc->sc_dip, CE_CONT, 1341bb5e3b2fSeh146360 "ipw2200_dma_region_alloc(): " 1342bb5e3b2fSeh146360 "ddi_dma_addr_bind_handle() failed\n")); 1343bb5e3b2fSeh146360 goto fail2; 1344bb5e3b2fSeh146360 } 1345bb5e3b2fSeh146360 1346bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_DMA, (sc->sc_dip, CE_CONT, 1347bb5e3b2fSeh146360 "ipw2200_dma_region_alloc(): ccnt=%u\n", dr->dr_ccnt)); 1348bb5e3b2fSeh146360 1349bb5e3b2fSeh146360 if (dr->dr_ccnt != 1) { 1350bb5e3b2fSeh146360 err = DDI_FAILURE; 1351bb5e3b2fSeh146360 goto fail3; 1352bb5e3b2fSeh146360 } 1353bb5e3b2fSeh146360 1354bb5e3b2fSeh146360 dr->dr_pbase = dr->dr_cookie.dmac_address; 1355bb5e3b2fSeh146360 1356bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_DMA, (sc->sc_dip, CE_CONT, 1357bb5e3b2fSeh146360 "ipw2200_dma_region_alloc(): get physical-base=0x%08x\n", 1358bb5e3b2fSeh146360 dr->dr_pbase)); 1359bb5e3b2fSeh146360 1360bb5e3b2fSeh146360 return (DDI_SUCCESS); 1361bb5e3b2fSeh146360 1362bb5e3b2fSeh146360 fail3: 1363bb5e3b2fSeh146360 (void) ddi_dma_unbind_handle(dr->dr_hnd); 1364bb5e3b2fSeh146360 fail2: 1365bb5e3b2fSeh146360 ddi_dma_mem_free(&dr->dr_acc); 1366bb5e3b2fSeh146360 fail1: 1367bb5e3b2fSeh146360 ddi_dma_free_handle(&dr->dr_hnd); 1368bb5e3b2fSeh146360 fail0: 1369bb5e3b2fSeh146360 return (err); 1370bb5e3b2fSeh146360 } 1371bb5e3b2fSeh146360 1372bb5e3b2fSeh146360 void 1373bb5e3b2fSeh146360 ipw2200_dma_region_free(struct dma_region *dr) 1374bb5e3b2fSeh146360 { 1375bb5e3b2fSeh146360 (void) ddi_dma_unbind_handle(dr->dr_hnd); 1376bb5e3b2fSeh146360 ddi_dma_mem_free(&dr->dr_acc); 1377bb5e3b2fSeh146360 ddi_dma_free_handle(&dr->dr_hnd); 1378bb5e3b2fSeh146360 } 1379bb5e3b2fSeh146360 1380bb5e3b2fSeh146360 static int 1381bb5e3b2fSeh146360 ipw2200_ring_alloc(struct ipw2200_softc *sc) 1382bb5e3b2fSeh146360 { 1383bb5e3b2fSeh146360 int err, i; 1384bb5e3b2fSeh146360 1385bb5e3b2fSeh146360 /* 1386bb5e3b2fSeh146360 * tx desc ring 1387bb5e3b2fSeh146360 */ 1388bb5e3b2fSeh146360 sc->sc_dma_txdsc.dr_name = "ipw2200-tx-desc-ring"; 1389bb5e3b2fSeh146360 err = ipw2200_dma_region_alloc(sc, &sc->sc_dma_txdsc, 1390bb5e3b2fSeh146360 IPW2200_TX_RING_SIZE * sizeof (struct ipw2200_tx_desc), 1391bb5e3b2fSeh146360 DDI_DMA_WRITE, DDI_DMA_CONSISTENT); 1392bb5e3b2fSeh146360 if (err != DDI_SUCCESS) 1393bb5e3b2fSeh146360 goto fail0; 1394bb5e3b2fSeh146360 /* 1395bb5e3b2fSeh146360 * tx buffer array 1396bb5e3b2fSeh146360 */ 1397bb5e3b2fSeh146360 for (i = 0; i < IPW2200_TX_RING_SIZE; i++) { 1398bb5e3b2fSeh146360 sc->sc_dma_txbufs[i].dr_name = "ipw2200-tx-buf"; 1399bb5e3b2fSeh146360 err = ipw2200_dma_region_alloc(sc, &sc->sc_dma_txbufs[i], 1400bb5e3b2fSeh146360 IPW2200_TXBUF_SIZE, DDI_DMA_WRITE, DDI_DMA_STREAMING); 1401bb5e3b2fSeh146360 if (err != DDI_SUCCESS) { 1402bb5e3b2fSeh146360 while (i >= 0) { 1403bb5e3b2fSeh146360 ipw2200_dma_region_free(&sc->sc_dma_txbufs[i]); 1404bb5e3b2fSeh146360 i--; 1405bb5e3b2fSeh146360 } 1406bb5e3b2fSeh146360 goto fail1; 1407bb5e3b2fSeh146360 } 1408bb5e3b2fSeh146360 } 1409bb5e3b2fSeh146360 /* 1410bb5e3b2fSeh146360 * rx buffer array 1411bb5e3b2fSeh146360 */ 1412bb5e3b2fSeh146360 for (i = 0; i < IPW2200_RX_RING_SIZE; i++) { 1413bb5e3b2fSeh146360 sc->sc_dma_rxbufs[i].dr_name = "ipw2200-rx-buf"; 1414bb5e3b2fSeh146360 err = ipw2200_dma_region_alloc(sc, &sc->sc_dma_rxbufs[i], 1415bb5e3b2fSeh146360 IPW2200_RXBUF_SIZE, DDI_DMA_READ, DDI_DMA_STREAMING); 1416bb5e3b2fSeh146360 if (err != DDI_SUCCESS) { 1417bb5e3b2fSeh146360 while (i >= 0) { 1418bb5e3b2fSeh146360 ipw2200_dma_region_free(&sc->sc_dma_rxbufs[i]); 1419bb5e3b2fSeh146360 i--; 1420bb5e3b2fSeh146360 } 1421bb5e3b2fSeh146360 goto fail2; 1422bb5e3b2fSeh146360 } 1423bb5e3b2fSeh146360 } 1424bb5e3b2fSeh146360 /* 1425bb5e3b2fSeh146360 * cmd desc ring 1426bb5e3b2fSeh146360 */ 1427bb5e3b2fSeh146360 sc->sc_dma_cmdsc.dr_name = "ipw2200-cmd-desc-ring"; 1428bb5e3b2fSeh146360 err = ipw2200_dma_region_alloc(sc, &sc->sc_dma_cmdsc, 1429bb5e3b2fSeh146360 IPW2200_CMD_RING_SIZE * sizeof (struct ipw2200_cmd_desc), 1430bb5e3b2fSeh146360 DDI_DMA_WRITE, DDI_DMA_CONSISTENT); 1431bb5e3b2fSeh146360 if (err != DDI_SUCCESS) 1432bb5e3b2fSeh146360 goto fail3; 1433bb5e3b2fSeh146360 1434bb5e3b2fSeh146360 return (DDI_SUCCESS); 1435bb5e3b2fSeh146360 1436bb5e3b2fSeh146360 fail3: 1437bb5e3b2fSeh146360 for (i = 0; i < IPW2200_RX_RING_SIZE; i++) 1438bb5e3b2fSeh146360 ipw2200_dma_region_free(&sc->sc_dma_rxbufs[i]); 1439bb5e3b2fSeh146360 fail2: 1440bb5e3b2fSeh146360 for (i = 0; i < IPW2200_TX_RING_SIZE; i++) 1441bb5e3b2fSeh146360 ipw2200_dma_region_free(&sc->sc_dma_txbufs[i]); 1442bb5e3b2fSeh146360 fail1: 1443bb5e3b2fSeh146360 ipw2200_dma_region_free(&sc->sc_dma_txdsc); 1444bb5e3b2fSeh146360 fail0: 1445bb5e3b2fSeh146360 return (err); 1446bb5e3b2fSeh146360 } 1447bb5e3b2fSeh146360 1448bb5e3b2fSeh146360 static void 1449bb5e3b2fSeh146360 ipw2200_ring_free(struct ipw2200_softc *sc) 1450bb5e3b2fSeh146360 { 1451bb5e3b2fSeh146360 int i; 1452bb5e3b2fSeh146360 1453bb5e3b2fSeh146360 /* 1454bb5e3b2fSeh146360 * tx ring desc 1455bb5e3b2fSeh146360 */ 1456bb5e3b2fSeh146360 ipw2200_dma_region_free(&sc->sc_dma_txdsc); 1457bb5e3b2fSeh146360 /* 1458bb5e3b2fSeh146360 * tx buf 1459bb5e3b2fSeh146360 */ 1460bb5e3b2fSeh146360 for (i = 0; i < IPW2200_TX_RING_SIZE; i++) 1461bb5e3b2fSeh146360 ipw2200_dma_region_free(&sc->sc_dma_txbufs[i]); 1462bb5e3b2fSeh146360 /* 1463bb5e3b2fSeh146360 * rx buf 1464bb5e3b2fSeh146360 */ 1465bb5e3b2fSeh146360 for (i = 0; i < IPW2200_RX_RING_SIZE; i++) 1466bb5e3b2fSeh146360 ipw2200_dma_region_free(&sc->sc_dma_rxbufs[i]); 1467bb5e3b2fSeh146360 /* 1468bb5e3b2fSeh146360 * command ring desc 1469bb5e3b2fSeh146360 */ 1470bb5e3b2fSeh146360 ipw2200_dma_region_free(&sc->sc_dma_cmdsc); 1471bb5e3b2fSeh146360 } 1472bb5e3b2fSeh146360 1473bb5e3b2fSeh146360 static void 1474bb5e3b2fSeh146360 ipw2200_ring_reset(struct ipw2200_softc *sc) 1475bb5e3b2fSeh146360 { 1476bb5e3b2fSeh146360 int i; 1477bb5e3b2fSeh146360 1478bb5e3b2fSeh146360 /* 1479bb5e3b2fSeh146360 * tx desc ring & buffer array 1480bb5e3b2fSeh146360 */ 1481bb5e3b2fSeh146360 sc->sc_tx_cur = 0; 1482bb5e3b2fSeh146360 sc->sc_tx_free = IPW2200_TX_RING_SIZE; 1483bb5e3b2fSeh146360 sc->sc_txdsc = (struct ipw2200_tx_desc *)sc->sc_dma_txdsc.dr_base; 1484bb5e3b2fSeh146360 for (i = 0; i < IPW2200_TX_RING_SIZE; i++) 1485bb5e3b2fSeh146360 sc->sc_txbufs[i] = (uint8_t *)sc->sc_dma_txbufs[i].dr_base; 1486bb5e3b2fSeh146360 /* 1487bb5e3b2fSeh146360 * rx buffer array 1488bb5e3b2fSeh146360 */ 1489bb5e3b2fSeh146360 sc->sc_rx_cur = 0; 1490bb5e3b2fSeh146360 sc->sc_rx_free = IPW2200_RX_RING_SIZE; 1491bb5e3b2fSeh146360 for (i = 0; i < IPW2200_RX_RING_SIZE; i++) 1492bb5e3b2fSeh146360 sc->sc_rxbufs[i] = (uint8_t *)sc->sc_dma_rxbufs[i].dr_base; 1493bb5e3b2fSeh146360 1494bb5e3b2fSeh146360 /* 1495bb5e3b2fSeh146360 * command desc ring 1496bb5e3b2fSeh146360 */ 1497bb5e3b2fSeh146360 sc->sc_cmd_cur = 0; 1498bb5e3b2fSeh146360 sc->sc_cmd_free = IPW2200_CMD_RING_SIZE; 1499bb5e3b2fSeh146360 sc->sc_cmdsc = (struct ipw2200_cmd_desc *)sc->sc_dma_cmdsc.dr_base; 1500bb5e3b2fSeh146360 } 1501bb5e3b2fSeh146360 1502bb5e3b2fSeh146360 /* 1503bb5e3b2fSeh146360 * tx, rx rings and command initialization 1504bb5e3b2fSeh146360 */ 1505bb5e3b2fSeh146360 static int 1506bb5e3b2fSeh146360 ipw2200_ring_init(struct ipw2200_softc *sc) 1507bb5e3b2fSeh146360 { 1508bb5e3b2fSeh146360 int err; 1509bb5e3b2fSeh146360 1510bb5e3b2fSeh146360 err = ipw2200_ring_alloc(sc); 1511bb5e3b2fSeh146360 if (err != DDI_SUCCESS) 1512bb5e3b2fSeh146360 return (err); 1513bb5e3b2fSeh146360 1514bb5e3b2fSeh146360 ipw2200_ring_reset(sc); 1515bb5e3b2fSeh146360 1516bb5e3b2fSeh146360 return (DDI_SUCCESS); 1517bb5e3b2fSeh146360 } 1518bb5e3b2fSeh146360 1519bb5e3b2fSeh146360 static void 1520bb5e3b2fSeh146360 ipw2200_ring_hwsetup(struct ipw2200_softc *sc) 1521bb5e3b2fSeh146360 { 1522bb5e3b2fSeh146360 int i; 1523bb5e3b2fSeh146360 1524bb5e3b2fSeh146360 /* 1525bb5e3b2fSeh146360 * command desc ring 1526bb5e3b2fSeh146360 */ 1527bb5e3b2fSeh146360 ipw2200_csr_put32(sc, IPW2200_CSR_CMD_BASE, sc->sc_dma_cmdsc.dr_pbase); 1528bb5e3b2fSeh146360 ipw2200_csr_put32(sc, IPW2200_CSR_CMD_SIZE, IPW2200_CMD_RING_SIZE); 1529bb5e3b2fSeh146360 ipw2200_csr_put32(sc, IPW2200_CSR_CMD_WRITE_INDEX, sc->sc_cmd_cur); 1530bb5e3b2fSeh146360 1531bb5e3b2fSeh146360 /* 1532bb5e3b2fSeh146360 * tx desc ring. only tx1 is used, tx2, tx3, and tx4 are unused 1533bb5e3b2fSeh146360 */ 1534bb5e3b2fSeh146360 ipw2200_csr_put32(sc, IPW2200_CSR_TX1_BASE, sc->sc_dma_txdsc.dr_pbase); 1535bb5e3b2fSeh146360 ipw2200_csr_put32(sc, IPW2200_CSR_TX1_SIZE, IPW2200_TX_RING_SIZE); 1536bb5e3b2fSeh146360 ipw2200_csr_put32(sc, IPW2200_CSR_TX1_WRITE_INDEX, sc->sc_tx_cur); 1537bb5e3b2fSeh146360 1538bb5e3b2fSeh146360 /* 1539bb5e3b2fSeh146360 * tx2, tx3, tx4 is not used 1540bb5e3b2fSeh146360 */ 1541bb5e3b2fSeh146360 ipw2200_csr_put32(sc, IPW2200_CSR_TX2_BASE, sc->sc_dma_txdsc.dr_pbase); 1542bb5e3b2fSeh146360 ipw2200_csr_put32(sc, IPW2200_CSR_TX2_SIZE, IPW2200_TX_RING_SIZE); 1543bb5e3b2fSeh146360 ipw2200_csr_put32(sc, IPW2200_CSR_TX2_READ_INDEX, 0); 1544bb5e3b2fSeh146360 ipw2200_csr_put32(sc, IPW2200_CSR_TX2_WRITE_INDEX, 0); 1545bb5e3b2fSeh146360 ipw2200_csr_put32(sc, IPW2200_CSR_TX3_BASE, sc->sc_dma_txdsc.dr_pbase); 1546bb5e3b2fSeh146360 ipw2200_csr_put32(sc, IPW2200_CSR_TX3_SIZE, IPW2200_TX_RING_SIZE); 1547bb5e3b2fSeh146360 ipw2200_csr_put32(sc, IPW2200_CSR_TX3_READ_INDEX, 0); 1548bb5e3b2fSeh146360 ipw2200_csr_put32(sc, IPW2200_CSR_TX3_WRITE_INDEX, 0); 1549bb5e3b2fSeh146360 ipw2200_csr_put32(sc, IPW2200_CSR_TX4_BASE, sc->sc_dma_txdsc.dr_pbase); 1550bb5e3b2fSeh146360 ipw2200_csr_put32(sc, IPW2200_CSR_TX4_SIZE, IPW2200_TX_RING_SIZE); 1551bb5e3b2fSeh146360 ipw2200_csr_put32(sc, IPW2200_CSR_TX4_READ_INDEX, 0); 1552bb5e3b2fSeh146360 ipw2200_csr_put32(sc, IPW2200_CSR_TX4_WRITE_INDEX, 0); 1553bb5e3b2fSeh146360 1554bb5e3b2fSeh146360 /* 1555bb5e3b2fSeh146360 * rx buffer ring 1556bb5e3b2fSeh146360 */ 1557bb5e3b2fSeh146360 for (i = 0; i < IPW2200_RX_RING_SIZE; i++) 1558bb5e3b2fSeh146360 ipw2200_csr_put32(sc, IPW2200_CSR_RX_BASE + i * 4, 1559bb5e3b2fSeh146360 sc->sc_dma_rxbufs[i].dr_pbase); 1560bb5e3b2fSeh146360 /* 1561bb5e3b2fSeh146360 * all rx buffer are empty, rx-rd-index == 0 && rx-wr-index == N-1 1562bb5e3b2fSeh146360 */ 1563bb5e3b2fSeh146360 ipw2200_csr_put32(sc, IPW2200_CSR_RX_WRITE_INDEX, 1564bb5e3b2fSeh146360 RING_BACKWARD(sc->sc_rx_cur, 1, IPW2200_RX_RING_SIZE)); 1565bb5e3b2fSeh146360 } 1566bb5e3b2fSeh146360 1567bb5e3b2fSeh146360 int 1568bb5e3b2fSeh146360 ipw2200_start_scan(struct ipw2200_softc *sc) 1569bb5e3b2fSeh146360 { 1570bb5e3b2fSeh146360 struct ieee80211com *ic = &sc->sc_ic; 1571bb5e3b2fSeh146360 struct ipw2200_scan scan; 1572bb5e3b2fSeh146360 uint8_t *ch; 1573bb5e3b2fSeh146360 int cnt, i; 1574bb5e3b2fSeh146360 1575bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_SCAN, (sc->sc_dip, CE_CONT, 1576bb5e3b2fSeh146360 "ipw2200_start_scan(): start scanning \n")); 1577bb5e3b2fSeh146360 1578bb5e3b2fSeh146360 /* 1579bb5e3b2fSeh146360 * start scanning 1580bb5e3b2fSeh146360 */ 1581bb5e3b2fSeh146360 sc->sc_flags |= IPW2200_FLAG_SCANNING; 1582bb5e3b2fSeh146360 1583bb5e3b2fSeh146360 (void) memset(&scan, 0, sizeof (scan)); 1584bb5e3b2fSeh146360 scan.type = (ic->ic_des_esslen != 0) ? IPW2200_SCAN_TYPE_BDIRECTED : 1585bb5e3b2fSeh146360 IPW2200_SCAN_TYPE_BROADCAST; 1586bb5e3b2fSeh146360 scan.dwelltime = LE_16(40); /* The interval is set up to 40 */ 1587bb5e3b2fSeh146360 1588bb5e3b2fSeh146360 /* 1589bb5e3b2fSeh146360 * Compact supported channel number(5G) into a single buffer 1590bb5e3b2fSeh146360 */ 1591bb5e3b2fSeh146360 ch = scan.channels; 1592bb5e3b2fSeh146360 cnt = 0; 1593bb5e3b2fSeh146360 for (i = 0; i <= IEEE80211_CHAN_MAX; i++) { 1594bb5e3b2fSeh146360 if (IEEE80211_IS_CHAN_5GHZ(&ic->ic_sup_channels[i]) && 1595bb5e3b2fSeh146360 isset(ic->ic_chan_active, i)) { 1596bb5e3b2fSeh146360 *++ch = (uint8_t)i; 1597bb5e3b2fSeh146360 cnt++; 1598bb5e3b2fSeh146360 } 1599bb5e3b2fSeh146360 } 1600bb5e3b2fSeh146360 *(ch - cnt) = IPW2200_CHAN_5GHZ | (uint8_t)cnt; 16013b608f65Sql147931 ch = (cnt > 0) ? (ch + 1) : (scan.channels); 1602bb5e3b2fSeh146360 1603bb5e3b2fSeh146360 /* 1604bb5e3b2fSeh146360 * Compact supported channel number(2G) into a single buffer 1605bb5e3b2fSeh146360 */ 1606bb5e3b2fSeh146360 cnt = 0; 1607bb5e3b2fSeh146360 for (i = 0; i <= IEEE80211_CHAN_MAX; i++) { 1608bb5e3b2fSeh146360 if (IEEE80211_IS_CHAN_2GHZ(&ic->ic_sup_channels[i]) && 1609bb5e3b2fSeh146360 isset(ic->ic_chan_active, i)) { 1610bb5e3b2fSeh146360 *++ch = (uint8_t)i; 1611bb5e3b2fSeh146360 cnt++; 1612bb5e3b2fSeh146360 } 1613bb5e3b2fSeh146360 } 1614bb5e3b2fSeh146360 *(ch - cnt) = IPW2200_CHAN_2GHZ | cnt; 1615bb5e3b2fSeh146360 1616bb5e3b2fSeh146360 return (ipw2200_cmd(sc, IPW2200_CMD_SCAN, &scan, sizeof (scan), 1)); 1617bb5e3b2fSeh146360 } 1618bb5e3b2fSeh146360 1619bb5e3b2fSeh146360 int 1620bb5e3b2fSeh146360 ipw2200_auth_and_assoc(struct ipw2200_softc *sc) 1621bb5e3b2fSeh146360 { 1622bb5e3b2fSeh146360 struct ieee80211com *ic = &sc->sc_ic; 1623bb5e3b2fSeh146360 struct ieee80211_node *in = ic->ic_bss; 1624bb5e3b2fSeh146360 struct ipw2200_configuration cfg; 1625bb5e3b2fSeh146360 struct ipw2200_rateset rs; 1626bb5e3b2fSeh146360 struct ipw2200_associate assoc; 1627bb5e3b2fSeh146360 uint32_t data; 1628bb5e3b2fSeh146360 int err; 1629924f3e72Seh146360 uint8_t *wpa_level; 1630924f3e72Seh146360 1631924f3e72Seh146360 if (sc->sc_flags & IPW2200_FLAG_ASSOCIATED) { 1632924f3e72Seh146360 /* already associated */ 1633924f3e72Seh146360 return (-1); 1634924f3e72Seh146360 } 1635bb5e3b2fSeh146360 1636bb5e3b2fSeh146360 /* 1637bb5e3b2fSeh146360 * set the confiuration 1638bb5e3b2fSeh146360 */ 1639bb5e3b2fSeh146360 if (IEEE80211_IS_CHAN_2GHZ(in->in_chan)) { 1640bb5e3b2fSeh146360 /* enable b/g auto-detection */ 1641bb5e3b2fSeh146360 (void) memset(&cfg, 0, sizeof (cfg)); 1642bb5e3b2fSeh146360 cfg.bluetooth_coexistence = 1; 1643bb5e3b2fSeh146360 cfg.multicast_enabled = 1; 1644bb5e3b2fSeh146360 cfg.use_protection = 1; 1645bb5e3b2fSeh146360 cfg.answer_pbreq = 1; 1646bb5e3b2fSeh146360 cfg.noise_reported = 1; 1647924f3e72Seh146360 cfg.disable_multicast_decryption = 1; /* WPA */ 1648924f3e72Seh146360 cfg.disable_unicast_decryption = 1; /* WPA */ 1649bb5e3b2fSeh146360 err = ipw2200_cmd(sc, IPW2200_CMD_SET_CONFIG, 1650bb5e3b2fSeh146360 &cfg, sizeof (cfg), 1); 1651bb5e3b2fSeh146360 if (err != DDI_SUCCESS) 1652bb5e3b2fSeh146360 return (err); 1653bb5e3b2fSeh146360 } 1654bb5e3b2fSeh146360 1655bb5e3b2fSeh146360 /* 1656bb5e3b2fSeh146360 * set the essid, may be null/hidden AP 1657bb5e3b2fSeh146360 */ 1658bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT, 1659bb5e3b2fSeh146360 "ipw2200_auth_and_assoc(): " 1660bb5e3b2fSeh146360 "setting ESSID to(%u),%c%c%c%c%c%c%c%c\n", 1661bb5e3b2fSeh146360 in->in_esslen, 1662bb5e3b2fSeh146360 in->in_essid[0], in->in_essid[1], 1663bb5e3b2fSeh146360 in->in_essid[2], in->in_essid[3], 1664bb5e3b2fSeh146360 in->in_essid[4], in->in_essid[5], 1665bb5e3b2fSeh146360 in->in_essid[6], in->in_essid[7])); 1666bb5e3b2fSeh146360 err = ipw2200_cmd(sc, IPW2200_CMD_SET_ESSID, in->in_essid, 1667bb5e3b2fSeh146360 in->in_esslen, 1); 1668bb5e3b2fSeh146360 if (err != DDI_SUCCESS) 1669bb5e3b2fSeh146360 return (err); 1670bb5e3b2fSeh146360 1671bb5e3b2fSeh146360 /* 1672bb5e3b2fSeh146360 * set the rate: the rate set has already been ''negocitated'' 1673bb5e3b2fSeh146360 */ 1674bb5e3b2fSeh146360 rs.mode = IEEE80211_IS_CHAN_5GHZ(in->in_chan) ? 1675bb5e3b2fSeh146360 IPW2200_MODE_11A : IPW2200_MODE_11G; 1676bb5e3b2fSeh146360 rs.type = IPW2200_RATESET_TYPE_NEGOCIATED; 1677bb5e3b2fSeh146360 rs.nrates = in->in_rates.ir_nrates; 1678bb5e3b2fSeh146360 (void) memcpy(rs.rates, in->in_rates.ir_rates, in->in_rates.ir_nrates); 1679bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT, 1680bb5e3b2fSeh146360 "ipw2200_auth_and_assoc(): " 1681bb5e3b2fSeh146360 "setting negotiated rates to(nrates = %u)\n", rs.nrates)); 1682bb5e3b2fSeh146360 err = ipw2200_cmd(sc, IPW2200_CMD_SET_RATES, &rs, sizeof (rs), 1); 1683bb5e3b2fSeh146360 if (err != DDI_SUCCESS) 1684bb5e3b2fSeh146360 return (err); 1685bb5e3b2fSeh146360 1686bb5e3b2fSeh146360 /* 1687924f3e72Seh146360 * invoke command associate 1688924f3e72Seh146360 */ 1689924f3e72Seh146360 (void) memset(&assoc, 0, sizeof (assoc)); 1690924f3e72Seh146360 1691924f3e72Seh146360 /* 1692924f3e72Seh146360 * set opt_ie to h/w if associated is WPA, opt_ie has been verified 1693924f3e72Seh146360 * by net80211 kernel module. 1694924f3e72Seh146360 */ 1695924f3e72Seh146360 if (ic->ic_opt_ie != NULL) { 1696924f3e72Seh146360 1697924f3e72Seh146360 wpa_level = (uint8_t *)ic->ic_opt_ie; 1698924f3e72Seh146360 1699924f3e72Seh146360 IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT, 1700924f3e72Seh146360 "ipw2200_auth_and_assoc(): " 1701924f3e72Seh146360 "set wpa_ie and wpa_ie_len to h/w. " 1702924f3e72Seh146360 "length is %d\n" 1703924f3e72Seh146360 "opt_ie[0] = %02X - element vendor\n" 1704924f3e72Seh146360 "opt_ie[1] = %02X - length\n" 1705924f3e72Seh146360 "opt_ie[2,3,4] = %02X %02X %02X - oui\n" 1706924f3e72Seh146360 "opt_ie[5] = %02X - oui type\n" 1707924f3e72Seh146360 "opt_ie[6,7] = %02X %02X - spec version \n" 1708924f3e72Seh146360 "opt_ie[8,9,10,11] = %02X %02X %02X %02X - gk cipher\n" 1709924f3e72Seh146360 "opt_ie[12,13] = %02X %02X - pairwise key cipher(1)\n" 1710924f3e72Seh146360 "opt_ie[14,15,16,17] = %02X %02X %02X %02X - ciphers\n" 1711924f3e72Seh146360 "opt_ie[18,19] = %02X %02X - authselcont(1) \n" 1712924f3e72Seh146360 "opt_ie[20,21,22,23] = %02X %02X %02X %02X - authsels\n", 1713924f3e72Seh146360 wpa_level[1], wpa_level[0], wpa_level[1], 1714924f3e72Seh146360 wpa_level[2], wpa_level[3], wpa_level[4], 1715924f3e72Seh146360 wpa_level[5], wpa_level[6], wpa_level[7], 1716924f3e72Seh146360 wpa_level[8], wpa_level[9], wpa_level[10], 1717924f3e72Seh146360 wpa_level[11], wpa_level[12], wpa_level[13], 1718924f3e72Seh146360 wpa_level[14], wpa_level[15], wpa_level[16], 1719924f3e72Seh146360 wpa_level[17], wpa_level[18], wpa_level[19], 1720924f3e72Seh146360 wpa_level[20], wpa_level[21], wpa_level[22], 1721924f3e72Seh146360 wpa_level[23])); 1722924f3e72Seh146360 1723924f3e72Seh146360 err = ipw2200_cmd(sc, IPW2200_CMD_SET_OPTIE, 1724924f3e72Seh146360 ic->ic_opt_ie, ic->ic_opt_ie_len, 1); 1725924f3e72Seh146360 if (err != DDI_SUCCESS) 1726924f3e72Seh146360 return (err); 1727924f3e72Seh146360 } 1728924f3e72Seh146360 1729924f3e72Seh146360 /* 1730bb5e3b2fSeh146360 * set the sensitive 1731bb5e3b2fSeh146360 */ 1732bb5e3b2fSeh146360 data = LE_32(in->in_rssi); 1733bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT, 1734bb5e3b2fSeh146360 "ipw2200_auth_and_assoc(): " 1735bb5e3b2fSeh146360 "setting sensitivity to rssi:(%u)\n", (uint8_t)in->in_rssi)); 1736bb5e3b2fSeh146360 err = ipw2200_cmd(sc, IPW2200_CMD_SET_SENSITIVITY, 1737bb5e3b2fSeh146360 &data, sizeof (data), 1); 1738bb5e3b2fSeh146360 if (err != DDI_SUCCESS) 1739bb5e3b2fSeh146360 return (err); 1740bb5e3b2fSeh146360 1741bb5e3b2fSeh146360 /* 1742924f3e72Seh146360 * set mode and channel for assocation command 1743bb5e3b2fSeh146360 */ 1744bb5e3b2fSeh146360 assoc.mode = IEEE80211_IS_CHAN_5GHZ(in->in_chan) ? 1745bb5e3b2fSeh146360 IPW2200_MODE_11A : IPW2200_MODE_11G; 1746bb5e3b2fSeh146360 assoc.chan = ieee80211_chan2ieee(ic, in->in_chan); 1747924f3e72Seh146360 1748bb5e3b2fSeh146360 /* 1749bb5e3b2fSeh146360 * use the value set to ic_bss to retraive current sharedmode 1750bb5e3b2fSeh146360 */ 1751bb5e3b2fSeh146360 if (ic->ic_bss->in_authmode == WL_SHAREDKEY) { 1752bb5e3b2fSeh146360 assoc.auth = (ic->ic_def_txkey << 4) | IPW2200_AUTH_SHARED; 1753bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_IOCTL, (sc->sc_dip, CE_CONT, 1754bb5e3b2fSeh146360 "ipw2200_auth_and_assoc(): " 1755bb5e3b2fSeh146360 "associate to shared key mode, set thru. ioctl")); 1756bb5e3b2fSeh146360 } 1757924f3e72Seh146360 1758924f3e72Seh146360 if (ic->ic_flags & IEEE80211_F_WPA) 1759924f3e72Seh146360 assoc.policy = LE_16(IPW2200_POLICY_WPA); /* RSN/WPA active */ 1760bb5e3b2fSeh146360 (void) memcpy(assoc.tstamp, in->in_tstamp.data, 8); 1761bb5e3b2fSeh146360 assoc.capinfo = LE_16(in->in_capinfo); 1762bb5e3b2fSeh146360 assoc.lintval = LE_16(ic->ic_lintval); 1763bb5e3b2fSeh146360 assoc.intval = LE_16(in->in_intval); 1764bb5e3b2fSeh146360 IEEE80211_ADDR_COPY(assoc.bssid, in->in_bssid); 1765bb5e3b2fSeh146360 if (ic->ic_opmode == IEEE80211_M_IBSS) 1766bb5e3b2fSeh146360 IEEE80211_ADDR_COPY(assoc.dst, ipw2200_broadcast_addr); 1767bb5e3b2fSeh146360 else 1768bb5e3b2fSeh146360 IEEE80211_ADDR_COPY(assoc.dst, in->in_bssid); 1769bb5e3b2fSeh146360 1770bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT, 1771bb5e3b2fSeh146360 "ipw2200_auth_and_assoc(): " 1772bb5e3b2fSeh146360 "associate to bssid(%2x:%2x:%2x:%2x:%2x:%2x:), " 1773bb5e3b2fSeh146360 "chan(%u), auth(%u)\n", 1774bb5e3b2fSeh146360 assoc.bssid[0], assoc.bssid[1], assoc.bssid[2], 1775bb5e3b2fSeh146360 assoc.bssid[3], assoc.bssid[4], assoc.bssid[5], 1776bb5e3b2fSeh146360 assoc.chan, assoc.auth)); 1777bb5e3b2fSeh146360 return (ipw2200_cmd(sc, IPW2200_CMD_ASSOCIATE, 1778bb5e3b2fSeh146360 &assoc, sizeof (assoc), 1)); 1779bb5e3b2fSeh146360 } 1780bb5e3b2fSeh146360 1781924f3e72Seh146360 /* 1782924f3e72Seh146360 * Send the dis-association command to h/w, will receive notification to claim 1783924f3e72Seh146360 * the connection is dis-associated. So, it's not marked as disassociated this 1784924f3e72Seh146360 * moment. 1785924f3e72Seh146360 */ 1786924f3e72Seh146360 static int 1787924f3e72Seh146360 ipw2200_disassoc(struct ipw2200_softc *sc) 1788924f3e72Seh146360 { 1789924f3e72Seh146360 struct ipw2200_associate assoc; 1790924f3e72Seh146360 assoc.type = 2; 1791924f3e72Seh146360 return (ipw2200_cmd(sc, IPW2200_CMD_ASSOCIATE, &assoc, 1792924f3e72Seh146360 sizeof (assoc), 1)); 1793924f3e72Seh146360 } 1794924f3e72Seh146360 1795bb5e3b2fSeh146360 /* ARGSUSED */ 1796bb5e3b2fSeh146360 static int 1797bb5e3b2fSeh146360 ipw2200_newstate(struct ieee80211com *ic, enum ieee80211_state state, int arg) 1798bb5e3b2fSeh146360 { 1799bb5e3b2fSeh146360 struct ipw2200_softc *sc = (struct ipw2200_softc *)ic; 1800bb5e3b2fSeh146360 wifi_data_t wd = { 0 }; 1801bb5e3b2fSeh146360 1802bb5e3b2fSeh146360 switch (state) { 1803bb5e3b2fSeh146360 case IEEE80211_S_SCAN: 1804bb5e3b2fSeh146360 if (!(sc->sc_flags & IPW2200_FLAG_SCANNING)) { 1805bb5e3b2fSeh146360 ic->ic_flags |= IEEE80211_F_SCAN | IEEE80211_F_ASCAN; 1806bb5e3b2fSeh146360 (void) ipw2200_start_scan(sc); 1807bb5e3b2fSeh146360 } 1808bb5e3b2fSeh146360 break; 1809bb5e3b2fSeh146360 case IEEE80211_S_AUTH: 1810924f3e72Seh146360 /* 1811924f3e72Seh146360 * The firmware will fail if we are already associated 1812924f3e72Seh146360 */ 1813924f3e72Seh146360 if (sc->sc_flags & IPW2200_FLAG_ASSOCIATED) 1814924f3e72Seh146360 (void) ipw2200_disassoc(sc); 1815bb5e3b2fSeh146360 (void) ipw2200_auth_and_assoc(sc); 1816bb5e3b2fSeh146360 break; 1817bb5e3b2fSeh146360 case IEEE80211_S_RUN: 1818bb5e3b2fSeh146360 /* 1819bb5e3b2fSeh146360 * We can send data now; update the fastpath with our 1820bb5e3b2fSeh146360 * current associated BSSID and other relevant settings. 1821bb5e3b2fSeh146360 */ 1822924f3e72Seh146360 wd.wd_secalloc = ieee80211_crypto_getciphertype(ic); 1823bb5e3b2fSeh146360 wd.wd_opmode = ic->ic_opmode; 1824bb5e3b2fSeh146360 IEEE80211_ADDR_COPY(wd.wd_bssid, ic->ic_bss->in_bssid); 1825bb5e3b2fSeh146360 (void) mac_pdata_update(ic->ic_mach, &wd, sizeof (wd)); 1826bb5e3b2fSeh146360 break; 1827bb5e3b2fSeh146360 case IEEE80211_S_ASSOC: 1828bb5e3b2fSeh146360 case IEEE80211_S_INIT: 1829bb5e3b2fSeh146360 break; 1830bb5e3b2fSeh146360 } 1831bb5e3b2fSeh146360 1832bb5e3b2fSeh146360 /* 1833924f3e72Seh146360 * notify to update the link, and WPA 1834bb5e3b2fSeh146360 */ 1835bb5e3b2fSeh146360 if ((ic->ic_state != IEEE80211_S_RUN) && (state == IEEE80211_S_RUN)) { 1836924f3e72Seh146360 ieee80211_notify_node_join(ic, ic->ic_bss); 1837bb5e3b2fSeh146360 } else if ((ic->ic_state == IEEE80211_S_RUN) && 1838bb5e3b2fSeh146360 (state != IEEE80211_S_RUN)) { 1839924f3e72Seh146360 ieee80211_notify_node_leave(ic, ic->ic_bss); 1840bb5e3b2fSeh146360 } 1841bb5e3b2fSeh146360 1842bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT, 1843bb5e3b2fSeh146360 "ipw2200_newstat(): %s -> %s\n", 1844bb5e3b2fSeh146360 ieee80211_state_name[ic->ic_state], 1845bb5e3b2fSeh146360 ieee80211_state_name[state])); 1846bb5e3b2fSeh146360 1847bb5e3b2fSeh146360 ic->ic_state = state; 1848bb5e3b2fSeh146360 return (DDI_SUCCESS); 1849bb5e3b2fSeh146360 } 1850bb5e3b2fSeh146360 /* 1851bb5e3b2fSeh146360 * GLD operations 1852bb5e3b2fSeh146360 */ 1853bb5e3b2fSeh146360 /* ARGSUSED */ 1854bb5e3b2fSeh146360 static int 1855bb5e3b2fSeh146360 ipw2200_m_stat(void *arg, uint_t stat, uint64_t *val) 1856bb5e3b2fSeh146360 { 1857bb5e3b2fSeh146360 ieee80211com_t *ic = (ieee80211com_t *)arg; 1858922d2c76Seh146360 struct ipw2200_softc *sc = (struct ipw2200_softc *)ic; 1859bb5e3b2fSeh146360 1860bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_GLD, (((struct ipw2200_softc *)arg)->sc_dip, 1861bb5e3b2fSeh146360 CE_CONT, 1862bb5e3b2fSeh146360 "ipw2200_m_stat(): enter\n")); 1863bb5e3b2fSeh146360 /* 1864bb5e3b2fSeh146360 * Some of below statistic data are from hardware, some from net80211 1865bb5e3b2fSeh146360 */ 1866bb5e3b2fSeh146360 switch (stat) { 1867922d2c76Seh146360 case MAC_STAT_NOXMTBUF: 1868922d2c76Seh146360 *val = ic->ic_stats.is_tx_nobuf; 1869922d2c76Seh146360 break; 1870922d2c76Seh146360 case MAC_STAT_IERRORS: 1871922d2c76Seh146360 *val = sc->sc_stats.sc_rx_len_err; 1872922d2c76Seh146360 break; 1873922d2c76Seh146360 case MAC_STAT_OERRORS: 1874922d2c76Seh146360 *val = sc->sc_stats.sc_tx_discard + 1875922d2c76Seh146360 sc->sc_stats.sc_tx_alloc_fail + 1876922d2c76Seh146360 sc->sc_stats.sc_tx_encap_fail + 1877922d2c76Seh146360 sc->sc_stats.sc_tx_crypto_fail; 1878922d2c76Seh146360 break; 1879bb5e3b2fSeh146360 case MAC_STAT_RBYTES: 1880bb5e3b2fSeh146360 *val = ic->ic_stats.is_rx_bytes; 1881bb5e3b2fSeh146360 break; 1882bb5e3b2fSeh146360 case MAC_STAT_IPACKETS: 1883bb5e3b2fSeh146360 *val = ic->ic_stats.is_rx_frags; 1884bb5e3b2fSeh146360 break; 1885bb5e3b2fSeh146360 case MAC_STAT_OBYTES: 1886bb5e3b2fSeh146360 *val = ic->ic_stats.is_tx_bytes; 1887bb5e3b2fSeh146360 break; 1888bb5e3b2fSeh146360 case MAC_STAT_OPACKETS: 1889bb5e3b2fSeh146360 *val = ic->ic_stats.is_tx_frags; 1890bb5e3b2fSeh146360 break; 1891bb5e3b2fSeh146360 /* 1892bb5e3b2fSeh146360 * Get below from hardware statistic, retraive net80211 value once 1s 1893bb5e3b2fSeh146360 */ 1894bb5e3b2fSeh146360 case WIFI_STAT_TX_FRAGS: 1895bb5e3b2fSeh146360 case WIFI_STAT_MCAST_TX: 1896bb5e3b2fSeh146360 case WIFI_STAT_TX_FAILED: 1897bb5e3b2fSeh146360 case WIFI_STAT_TX_RETRANS: 1898bb5e3b2fSeh146360 /* 1899bb5e3b2fSeh146360 * Get blow information from net80211 1900bb5e3b2fSeh146360 */ 1901bb5e3b2fSeh146360 case WIFI_STAT_RTS_SUCCESS: 1902bb5e3b2fSeh146360 case WIFI_STAT_RTS_FAILURE: 1903bb5e3b2fSeh146360 case WIFI_STAT_ACK_FAILURE: 1904bb5e3b2fSeh146360 case WIFI_STAT_RX_FRAGS: 1905bb5e3b2fSeh146360 case WIFI_STAT_MCAST_RX: 1906bb5e3b2fSeh146360 case WIFI_STAT_RX_DUPS: 1907bb5e3b2fSeh146360 case WIFI_STAT_FCS_ERRORS: 1908bb5e3b2fSeh146360 case WIFI_STAT_WEP_ERRORS: 1909bb5e3b2fSeh146360 return (ieee80211_stat(ic, stat, val)); 1910bb5e3b2fSeh146360 /* 1911bb5e3b2fSeh146360 * Need be supported later 1912bb5e3b2fSeh146360 */ 1913bb5e3b2fSeh146360 case MAC_STAT_IFSPEED: 1914bb5e3b2fSeh146360 default: 1915bb5e3b2fSeh146360 return (ENOTSUP); 1916bb5e3b2fSeh146360 } 1917bb5e3b2fSeh146360 return (0); 1918bb5e3b2fSeh146360 } 1919bb5e3b2fSeh146360 1920bb5e3b2fSeh146360 /* ARGSUSED */ 1921bb5e3b2fSeh146360 static int 1922bb5e3b2fSeh146360 ipw2200_m_multicst(void *arg, boolean_t add, const uint8_t *mca) 1923bb5e3b2fSeh146360 { 1924bb5e3b2fSeh146360 /* not supported */ 1925bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_GLD, (((struct ipw2200_softc *)arg)->sc_dip, 1926bb5e3b2fSeh146360 CE_CONT, 1927bb5e3b2fSeh146360 "ipw2200_m_multicst(): enter\n")); 1928bb5e3b2fSeh146360 1929922d2c76Seh146360 return (0); 1930bb5e3b2fSeh146360 } 1931bb5e3b2fSeh146360 1932bb5e3b2fSeh146360 /* 1933bb5e3b2fSeh146360 * Multithread handler for linkstatus, fatal error recovery, get statistic 1934bb5e3b2fSeh146360 */ 1935bb5e3b2fSeh146360 static void 1936bb5e3b2fSeh146360 ipw2200_thread(struct ipw2200_softc *sc) 1937bb5e3b2fSeh146360 { 1938bb5e3b2fSeh146360 struct ieee80211com *ic = &sc->sc_ic; 1939982d85fcSeh146360 enum ieee80211_state ostate; 1940bb5e3b2fSeh146360 int32_t nlstate; 1941bb5e3b2fSeh146360 int stat_cnt = 0; 1942bb5e3b2fSeh146360 1943bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_SOFTINT, (sc->sc_dip, CE_CONT, 1944bb5e3b2fSeh146360 "ipw2200_thread(): enter, linkstate %d\n", sc->sc_linkstate)); 1945bb5e3b2fSeh146360 1946bb5e3b2fSeh146360 mutex_enter(&sc->sc_mflock); 1947bb5e3b2fSeh146360 1948bb5e3b2fSeh146360 while (sc->sc_mfthread_switch) { 1949bb5e3b2fSeh146360 /* 1950922d2c76Seh146360 * when radio is off or SUSPEND status, nothing to do 1951922d2c76Seh146360 */ 1952922d2c76Seh146360 if ((ipw2200_radio_status(sc) == 0) || 1953922d2c76Seh146360 sc->sc_flags & IPW2200_FLAG_SUSPEND) { 1954922d2c76Seh146360 goto wait_loop; 1955922d2c76Seh146360 } 1956922d2c76Seh146360 1957922d2c76Seh146360 /* 1958bb5e3b2fSeh146360 * notify the link state 1959bb5e3b2fSeh146360 */ 1960bb5e3b2fSeh146360 if (ic->ic_mach && (sc->sc_flags & IPW2200_FLAG_LINK_CHANGE)) { 1961bb5e3b2fSeh146360 1962bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_SOFTINT, (sc->sc_dip, CE_CONT, 1963bb5e3b2fSeh146360 "ipw2200_thread(): link status --> %d\n", 1964bb5e3b2fSeh146360 sc->sc_linkstate)); 1965bb5e3b2fSeh146360 1966bb5e3b2fSeh146360 sc->sc_flags &= ~IPW2200_FLAG_LINK_CHANGE; 1967bb5e3b2fSeh146360 nlstate = sc->sc_linkstate; 1968bb5e3b2fSeh146360 1969bb5e3b2fSeh146360 mutex_exit(&sc->sc_mflock); 1970bb5e3b2fSeh146360 mac_link_update(ic->ic_mach, nlstate); 1971bb5e3b2fSeh146360 mutex_enter(&sc->sc_mflock); 1972bb5e3b2fSeh146360 } 1973bb5e3b2fSeh146360 1974bb5e3b2fSeh146360 /* 1975bb5e3b2fSeh146360 * recovery fatal error 1976bb5e3b2fSeh146360 */ 1977bb5e3b2fSeh146360 if (ic->ic_mach && 1978bb5e3b2fSeh146360 (sc->sc_flags & IPW2200_FLAG_HW_ERR_RECOVER)) { 1979bb5e3b2fSeh146360 1980bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_FATAL, (sc->sc_dip, CE_CONT, 1981bb5e3b2fSeh146360 "ipw2200_thread(): " 1982bb5e3b2fSeh146360 "try to recover fatal hw error\n")); 1983bb5e3b2fSeh146360 1984bb5e3b2fSeh146360 sc->sc_flags &= ~IPW2200_FLAG_HW_ERR_RECOVER; 1985bb5e3b2fSeh146360 mutex_exit(&sc->sc_mflock); 1986982d85fcSeh146360 1987924f3e72Seh146360 /* stop again */ 1988982d85fcSeh146360 ostate = ic->ic_state; 1989bb5e3b2fSeh146360 (void) ipw2200_init(sc); /* Force state machine */ 1990924f3e72Seh146360 1991bb5e3b2fSeh146360 /* 1992bb5e3b2fSeh146360 * workround. Delay for a while after init especially 1993bb5e3b2fSeh146360 * when something wrong happened already. 1994bb5e3b2fSeh146360 */ 1995bb5e3b2fSeh146360 delay(drv_usectohz(delay_fatal_recover)); 1996982d85fcSeh146360 1997982d85fcSeh146360 /* 1998982d85fcSeh146360 * Init scan will recovery the original connection if 1999982d85fcSeh146360 * the original state is run 2000982d85fcSeh146360 */ 2001982d85fcSeh146360 if (ostate != IEEE80211_S_INIT) 2002982d85fcSeh146360 ieee80211_begin_scan(ic, 0); 2003982d85fcSeh146360 2004bb5e3b2fSeh146360 mutex_enter(&sc->sc_mflock); 2005bb5e3b2fSeh146360 } 2006bb5e3b2fSeh146360 2007bb5e3b2fSeh146360 /* 2008bb5e3b2fSeh146360 * get statistic, the value will be retrieved by m_stat 2009bb5e3b2fSeh146360 */ 2010bb5e3b2fSeh146360 if (stat_cnt == 10) { 2011bb5e3b2fSeh146360 2012bb5e3b2fSeh146360 stat_cnt = 0; /* re-start */ 2013bb5e3b2fSeh146360 mutex_exit(&sc->sc_mflock); 2014bb5e3b2fSeh146360 ipw2200_get_statistics(sc); 2015bb5e3b2fSeh146360 mutex_enter(&sc->sc_mflock); 2016bb5e3b2fSeh146360 2017bb5e3b2fSeh146360 } else 2018bb5e3b2fSeh146360 stat_cnt++; /* until 1s */ 2019bb5e3b2fSeh146360 2020922d2c76Seh146360 wait_loop: 2021bb5e3b2fSeh146360 mutex_exit(&sc->sc_mflock); 2022bb5e3b2fSeh146360 delay(drv_usectohz(delay_aux_thread)); 2023bb5e3b2fSeh146360 mutex_enter(&sc->sc_mflock); 2024bb5e3b2fSeh146360 2025bb5e3b2fSeh146360 } 2026bb5e3b2fSeh146360 sc->sc_mf_thread = NULL; 2027bb5e3b2fSeh146360 cv_signal(&sc->sc_mfthread_cv); 2028bb5e3b2fSeh146360 mutex_exit(&sc->sc_mflock); 2029bb5e3b2fSeh146360 } 2030bb5e3b2fSeh146360 2031bb5e3b2fSeh146360 static int 2032bb5e3b2fSeh146360 ipw2200_m_start(void *arg) 2033bb5e3b2fSeh146360 { 2034bb5e3b2fSeh146360 struct ipw2200_softc *sc = (struct ipw2200_softc *)arg; 2035982d85fcSeh146360 struct ieee80211com *ic = &sc->sc_ic; 2036bb5e3b2fSeh146360 2037bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_GLD, (sc->sc_dip, CE_CONT, 2038bb5e3b2fSeh146360 "ipw2200_m_start(): enter\n")); 2039bb5e3b2fSeh146360 /* 2040bb5e3b2fSeh146360 * initialize ipw2200 hardware, everything ok will start scan 2041bb5e3b2fSeh146360 */ 2042bb5e3b2fSeh146360 (void) ipw2200_init(sc); 2043924f3e72Seh146360 2044982d85fcSeh146360 /* 2045982d85fcSeh146360 * set the state machine to INIT 2046982d85fcSeh146360 */ 2047982d85fcSeh146360 ieee80211_new_state(ic, IEEE80211_S_INIT, -1); 2048bb5e3b2fSeh146360 2049bb5e3b2fSeh146360 sc->sc_flags |= IPW2200_FLAG_RUNNING; 2050bb5e3b2fSeh146360 2051924f3e72Seh146360 /* 2052924f3e72Seh146360 * fix KCF bug. - workaround, need to fix it in net80211 2053924f3e72Seh146360 */ 2054924f3e72Seh146360 (void) crypto_mech2id(SUN_CKM_RC4); 2055924f3e72Seh146360 2056922d2c76Seh146360 return (0); 2057bb5e3b2fSeh146360 } 2058bb5e3b2fSeh146360 2059bb5e3b2fSeh146360 static void 2060bb5e3b2fSeh146360 ipw2200_m_stop(void *arg) 2061bb5e3b2fSeh146360 { 2062bb5e3b2fSeh146360 struct ipw2200_softc *sc = (struct ipw2200_softc *)arg; 2063982d85fcSeh146360 struct ieee80211com *ic = &sc->sc_ic; 2064bb5e3b2fSeh146360 2065bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_GLD, (sc->sc_dip, CE_CONT, 2066bb5e3b2fSeh146360 "ipw2200_m_stop(): enter\n")); 2067bb5e3b2fSeh146360 2068bb5e3b2fSeh146360 ipw2200_stop(sc); 2069982d85fcSeh146360 /* 2070982d85fcSeh146360 * set the state machine to INIT 2071982d85fcSeh146360 */ 2072982d85fcSeh146360 ieee80211_new_state(ic, IEEE80211_S_INIT, -1); 2073bb5e3b2fSeh146360 2074bb5e3b2fSeh146360 sc->sc_flags &= ~IPW2200_FLAG_RUNNING; 2075bb5e3b2fSeh146360 } 2076bb5e3b2fSeh146360 2077bb5e3b2fSeh146360 static int 2078bb5e3b2fSeh146360 ipw2200_m_unicst(void *arg, const uint8_t *macaddr) 2079bb5e3b2fSeh146360 { 2080bb5e3b2fSeh146360 struct ipw2200_softc *sc = (struct ipw2200_softc *)arg; 2081bb5e3b2fSeh146360 struct ieee80211com *ic = &sc->sc_ic; 2082bb5e3b2fSeh146360 int err; 2083bb5e3b2fSeh146360 2084bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_GLD, (sc->sc_dip, CE_CONT, 2085bb5e3b2fSeh146360 "ipw2200_m_unicst(): enter\n")); 2086bb5e3b2fSeh146360 2087bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_GLD, (sc->sc_dip, CE_CONT, 2088bb5e3b2fSeh146360 "ipw2200_m_unicst(): GLD setting MAC address to " 2089bb5e3b2fSeh146360 "%02x:%02x:%02x:%02x:%02x:%02x\n", 2090bb5e3b2fSeh146360 macaddr[0], macaddr[1], macaddr[2], 2091bb5e3b2fSeh146360 macaddr[3], macaddr[4], macaddr[5])); 2092bb5e3b2fSeh146360 2093bb5e3b2fSeh146360 if (!IEEE80211_ADDR_EQ(ic->ic_macaddr, macaddr)) { 2094bb5e3b2fSeh146360 2095bb5e3b2fSeh146360 IEEE80211_ADDR_COPY(ic->ic_macaddr, macaddr); 2096bb5e3b2fSeh146360 2097bb5e3b2fSeh146360 if (sc->sc_flags & IPW2200_FLAG_RUNNING) { 2098bb5e3b2fSeh146360 err = ipw2200_config(sc); 2099bb5e3b2fSeh146360 if (err != DDI_SUCCESS) { 2100bb5e3b2fSeh146360 IPW2200_WARN((sc->sc_dip, CE_WARN, 2101bb5e3b2fSeh146360 "ipw2200_m_unicst(): " 2102bb5e3b2fSeh146360 "device configuration failed\n")); 2103bb5e3b2fSeh146360 goto fail; 2104bb5e3b2fSeh146360 } 2105bb5e3b2fSeh146360 } 2106bb5e3b2fSeh146360 } 2107922d2c76Seh146360 return (0); 2108bb5e3b2fSeh146360 fail: 2109922d2c76Seh146360 return (EIO); 2110bb5e3b2fSeh146360 } 2111bb5e3b2fSeh146360 2112bb5e3b2fSeh146360 static int 2113bb5e3b2fSeh146360 ipw2200_m_promisc(void *arg, boolean_t on) 2114bb5e3b2fSeh146360 { 2115bb5e3b2fSeh146360 /* not supported */ 2116bb5e3b2fSeh146360 struct ipw2200_softc *sc = (struct ipw2200_softc *)arg; 2117bb5e3b2fSeh146360 2118bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_GLD, (sc->sc_dip, CE_CONT, 2119bb5e3b2fSeh146360 "ipw2200_m_promisc(): enter. " 2120bb5e3b2fSeh146360 "GLD setting promiscuous mode - %d\n", on)); 2121bb5e3b2fSeh146360 2122922d2c76Seh146360 return (0); 2123bb5e3b2fSeh146360 } 2124bb5e3b2fSeh146360 2125bb5e3b2fSeh146360 static mblk_t * 2126bb5e3b2fSeh146360 ipw2200_m_tx(void *arg, mblk_t *mp) 2127bb5e3b2fSeh146360 { 2128bb5e3b2fSeh146360 struct ipw2200_softc *sc = (struct ipw2200_softc *)arg; 2129bb5e3b2fSeh146360 struct ieee80211com *ic = &sc->sc_ic; 2130bb5e3b2fSeh146360 mblk_t *next; 2131bb5e3b2fSeh146360 2132bb5e3b2fSeh146360 /* 2133922d2c76Seh146360 * when driver in on suspend state, freemsgchain directly 2134922d2c76Seh146360 */ 2135922d2c76Seh146360 if (sc->sc_flags & IPW2200_FLAG_SUSPEND) { 2136922d2c76Seh146360 IPW2200_DBG(IPW2200_DBG_SUSPEND, (sc->sc_dip, CE_CONT, 2137922d2c76Seh146360 "ipw2200_m_tx(): suspend status, discard msg\n")); 2138922d2c76Seh146360 sc->sc_stats.sc_tx_discard++; /* discard data */ 2139922d2c76Seh146360 freemsgchain(mp); 2140922d2c76Seh146360 return (NULL); 2141922d2c76Seh146360 } 2142922d2c76Seh146360 2143922d2c76Seh146360 /* 2144bb5e3b2fSeh146360 * No data frames go out unless we're associated; this 2145bb5e3b2fSeh146360 * should not happen as the 802.11 layer does not enable 2146bb5e3b2fSeh146360 * the xmit queue until we enter the RUN state. 2147bb5e3b2fSeh146360 */ 2148bb5e3b2fSeh146360 if (ic->ic_state != IEEE80211_S_RUN) { 2149bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_GLD, (sc->sc_dip, CE_CONT, 2150bb5e3b2fSeh146360 "ipw2200_m_tx(): discard msg, ic_state = %u\n", 2151bb5e3b2fSeh146360 ic->ic_state)); 2152922d2c76Seh146360 sc->sc_stats.sc_tx_discard++; /* discard data */ 2153bb5e3b2fSeh146360 freemsgchain(mp); 2154bb5e3b2fSeh146360 return (NULL); 2155bb5e3b2fSeh146360 } 2156bb5e3b2fSeh146360 2157bb5e3b2fSeh146360 while (mp != NULL) { 2158bb5e3b2fSeh146360 next = mp->b_next; 2159bb5e3b2fSeh146360 mp->b_next = NULL; 2160bb5e3b2fSeh146360 if (ipw2200_send(ic, mp, IEEE80211_FC0_TYPE_DATA) == 2161924f3e72Seh146360 ENOMEM) { 2162bb5e3b2fSeh146360 mp->b_next = next; 2163bb5e3b2fSeh146360 break; 2164bb5e3b2fSeh146360 } 2165bb5e3b2fSeh146360 mp = next; 2166bb5e3b2fSeh146360 } 2167bb5e3b2fSeh146360 return (mp); 2168bb5e3b2fSeh146360 } 2169bb5e3b2fSeh146360 2170924f3e72Seh146360 /* 2171924f3e72Seh146360 * ipw2200_send(): send data. softway to handle crypto_encap. 2172924f3e72Seh146360 */ 2173bb5e3b2fSeh146360 static int 2174bb5e3b2fSeh146360 ipw2200_send(ieee80211com_t *ic, mblk_t *mp, uint8_t type) 2175bb5e3b2fSeh146360 { 2176bb5e3b2fSeh146360 struct ipw2200_softc *sc = (struct ipw2200_softc *)ic; 2177bb5e3b2fSeh146360 struct ieee80211_node *in; 2178bb5e3b2fSeh146360 struct ieee80211_frame *wh; 2179924f3e72Seh146360 struct ieee80211_key *k; 2180924f3e72Seh146360 mblk_t *m0, *m; 2181bb5e3b2fSeh146360 size_t cnt, off; 2182bb5e3b2fSeh146360 struct ipw2200_tx_desc *txdsc; 2183bb5e3b2fSeh146360 struct dma_region *dr; 2184bb5e3b2fSeh146360 uint32_t idx; 2185924f3e72Seh146360 int err = DDI_SUCCESS; 2186bb5e3b2fSeh146360 /* tmp pointer, used to pack header and payload */ 2187bb5e3b2fSeh146360 uint8_t *p; 2188bb5e3b2fSeh146360 2189bb5e3b2fSeh146360 ASSERT(mp->b_next == NULL); 2190bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_GLD, (sc->sc_dip, CE_CONT, 2191bb5e3b2fSeh146360 "ipw2200_send(): enter\n")); 2192bb5e3b2fSeh146360 2193bb5e3b2fSeh146360 if ((type & IEEE80211_FC0_TYPE_MASK) != IEEE80211_FC0_TYPE_DATA) { 2194bb5e3b2fSeh146360 /* 2195bb5e3b2fSeh146360 * skip all management frames since ipw2200 won't generate any 2196bb5e3b2fSeh146360 * management frames. Therefore, drop this package. 2197bb5e3b2fSeh146360 */ 2198bb5e3b2fSeh146360 freemsg(mp); 2199924f3e72Seh146360 err = DDI_FAILURE; 2200bb5e3b2fSeh146360 goto fail0; 2201bb5e3b2fSeh146360 } 2202bb5e3b2fSeh146360 2203bb5e3b2fSeh146360 mutex_enter(&sc->sc_tx_lock); 2204922d2c76Seh146360 if (sc->sc_flags & IPW2200_FLAG_SUSPEND) { 2205922d2c76Seh146360 /* 2206922d2c76Seh146360 * when sending data, system runs into suspend status, 2207922d2c76Seh146360 * return fail directly 2208922d2c76Seh146360 */ 2209922d2c76Seh146360 err = ENXIO; 2210922d2c76Seh146360 goto fail0; 2211922d2c76Seh146360 } 2212bb5e3b2fSeh146360 2213bb5e3b2fSeh146360 /* 2214bb5e3b2fSeh146360 * need 1 empty descriptor 2215bb5e3b2fSeh146360 */ 2216bb5e3b2fSeh146360 if (sc->sc_tx_free <= IPW2200_TX_RING_MIN) { 2217924f3e72Seh146360 mutex_enter(&sc->sc_resched_lock); 2218bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_RING, (sc->sc_dip, CE_WARN, 2219bb5e3b2fSeh146360 "ipw2200_send(): no enough descriptors(%d)\n", 2220bb5e3b2fSeh146360 sc->sc_tx_free)); 2221bb5e3b2fSeh146360 ic->ic_stats.is_tx_nobuf++; /* no enough buffer */ 2222bb5e3b2fSeh146360 sc->sc_flags |= IPW2200_FLAG_TX_SCHED; 2223924f3e72Seh146360 err = ENOMEM; 2224924f3e72Seh146360 mutex_exit(&sc->sc_resched_lock); 2225bb5e3b2fSeh146360 goto fail1; 2226bb5e3b2fSeh146360 } 2227bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_RING, (sc->sc_dip, CE_CONT, 2228bb5e3b2fSeh146360 "ipw2200_send(): tx-free=%d,tx-curr=%d\n", 2229bb5e3b2fSeh146360 sc->sc_tx_free, sc->sc_tx_cur)); 2230bb5e3b2fSeh146360 2231924f3e72Seh146360 /* 2232924f3e72Seh146360 * put the mp into one blk, and use it to do the crypto_encap 2233924f3e72Seh146360 * if necessaary. 2234924f3e72Seh146360 */ 2235924f3e72Seh146360 m = allocb(msgdsize(mp) + 32, BPRI_MED); 2236924f3e72Seh146360 if (m == NULL) { /* can not alloc buf, drop this package */ 2237924f3e72Seh146360 IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT, 2238924f3e72Seh146360 "ipw2200_send(): msg allocation failed\n")); 2239924f3e72Seh146360 freemsg(mp); 2240922d2c76Seh146360 sc->sc_stats.sc_tx_alloc_fail++; /* alloc fail */ 2241922d2c76Seh146360 ic->ic_stats.is_tx_failed++; /* trans failed */ 2242924f3e72Seh146360 err = DDI_FAILURE; 2243924f3e72Seh146360 goto fail1; 2244924f3e72Seh146360 } 2245924f3e72Seh146360 for (off = 0, m0 = mp; m0 != NULL; m0 = m0->b_cont) { 2246924f3e72Seh146360 cnt = MBLKL(m0); 2247924f3e72Seh146360 (void) memcpy(m->b_rptr + off, m0->b_rptr, cnt); 2248924f3e72Seh146360 off += cnt; 2249924f3e72Seh146360 } 2250924f3e72Seh146360 m->b_wptr += off; 2251924f3e72Seh146360 2252924f3e72Seh146360 /* 2253924f3e72Seh146360 * find tx_node, and encapsulate the data 2254924f3e72Seh146360 */ 2255924f3e72Seh146360 wh = (struct ieee80211_frame *)m->b_rptr; 2256bb5e3b2fSeh146360 in = ieee80211_find_txnode(ic, wh->i_addr1); 2257bb5e3b2fSeh146360 if (in == NULL) { /* can not find the tx node, drop the package */ 2258922d2c76Seh146360 sc->sc_stats.sc_tx_encap_fail++; /* tx encap fail */ 2259922d2c76Seh146360 ic->ic_stats.is_tx_failed++; /* trans failed */ 2260bb5e3b2fSeh146360 freemsg(mp); 2261924f3e72Seh146360 err = DDI_FAILURE; 2262924f3e72Seh146360 goto fail2; 2263bb5e3b2fSeh146360 } 2264bb5e3b2fSeh146360 in->in_inact = 0; 2265924f3e72Seh146360 2266924f3e72Seh146360 (void) ieee80211_encap(ic, m, in); 2267bb5e3b2fSeh146360 ieee80211_free_node(in); 2268bb5e3b2fSeh146360 2269924f3e72Seh146360 if (wh->i_fc[1] & IEEE80211_FC1_WEP) { 2270924f3e72Seh146360 k = ieee80211_crypto_encap(ic, m); 2271924f3e72Seh146360 if (k == NULL) { /* can not get the key, drop packages */ 2272924f3e72Seh146360 IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT, 2273924f3e72Seh146360 "ipw2200_send(): " 2274924f3e72Seh146360 "Encrypting 802.11 frame failed\n")); 2275922d2c76Seh146360 sc->sc_stats.sc_tx_crypto_fail++; /* tx encap fail */ 2276922d2c76Seh146360 ic->ic_stats.is_tx_failed++; /* trans failed */ 2277924f3e72Seh146360 freemsg(mp); 2278924f3e72Seh146360 err = DDI_FAILURE; 2279924f3e72Seh146360 goto fail2; 2280924f3e72Seh146360 } 2281924f3e72Seh146360 wh = (struct ieee80211_frame *)m->b_rptr; 2282924f3e72Seh146360 } 2283924f3e72Seh146360 2284bb5e3b2fSeh146360 /* 2285924f3e72Seh146360 * get txdsc 2286bb5e3b2fSeh146360 */ 2287bb5e3b2fSeh146360 idx = sc->sc_tx_cur; 2288bb5e3b2fSeh146360 txdsc = &sc->sc_txdsc[idx]; 2289bb5e3b2fSeh146360 (void) memset(txdsc, 0, sizeof (*txdsc)); 2290bb5e3b2fSeh146360 /* 2291bb5e3b2fSeh146360 * extract header from message 2292bb5e3b2fSeh146360 */ 2293bb5e3b2fSeh146360 p = (uint8_t *)&txdsc->wh; 2294924f3e72Seh146360 off = sizeof (struct ieee80211_frame); 2295924f3e72Seh146360 (void) memcpy(p, m->b_rptr, off); 2296bb5e3b2fSeh146360 /* 2297bb5e3b2fSeh146360 * extract payload from message 2298bb5e3b2fSeh146360 */ 2299bb5e3b2fSeh146360 dr = &sc->sc_dma_txbufs[idx]; 2300bb5e3b2fSeh146360 p = sc->sc_txbufs[idx]; 2301924f3e72Seh146360 cnt = MBLKL(m); 2302924f3e72Seh146360 (void) memcpy(p, m->b_rptr + off, cnt - off); 2303924f3e72Seh146360 cnt -= off; 2304bb5e3b2fSeh146360 2305bb5e3b2fSeh146360 txdsc->hdr.type = IPW2200_HDR_TYPE_DATA; 2306bb5e3b2fSeh146360 txdsc->hdr.flags = IPW2200_HDR_FLAG_IRQ; 2307bb5e3b2fSeh146360 txdsc->cmd = IPW2200_DATA_CMD_TX; 2308924f3e72Seh146360 txdsc->len = LE_16(cnt); 2309bb5e3b2fSeh146360 txdsc->flags = 0; 2310bb5e3b2fSeh146360 2311bb5e3b2fSeh146360 if (ic->ic_opmode == IEEE80211_M_IBSS) { 2312bb5e3b2fSeh146360 if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) 2313bb5e3b2fSeh146360 txdsc->flags |= IPW2200_DATA_FLAG_NEED_ACK; 2314bb5e3b2fSeh146360 } else if (!IEEE80211_IS_MULTICAST(wh->i_addr3)) 2315bb5e3b2fSeh146360 txdsc->flags |= IPW2200_DATA_FLAG_NEED_ACK; 2316bb5e3b2fSeh146360 2317924f3e72Seh146360 /* always set it to none wep, because it's handled by software */ 2318bb5e3b2fSeh146360 txdsc->flags |= IPW2200_DATA_FLAG_NO_WEP; 2319bb5e3b2fSeh146360 2320bb5e3b2fSeh146360 if (ic->ic_flags & IEEE80211_F_SHPREAMBLE) 2321bb5e3b2fSeh146360 txdsc->flags |= IPW2200_DATA_FLAG_SHPREAMBLE; 2322bb5e3b2fSeh146360 2323bb5e3b2fSeh146360 txdsc->nseg = LE_32(1); 2324bb5e3b2fSeh146360 txdsc->seg_addr[0] = LE_32(dr->dr_pbase); 2325924f3e72Seh146360 txdsc->seg_len[0] = LE_32(cnt); 2326bb5e3b2fSeh146360 2327bb5e3b2fSeh146360 /* 2328bb5e3b2fSeh146360 * DMA sync: buffer and desc 2329bb5e3b2fSeh146360 */ 2330bb5e3b2fSeh146360 (void) ddi_dma_sync(dr->dr_hnd, 0, 2331bb5e3b2fSeh146360 IPW2200_TXBUF_SIZE, DDI_DMA_SYNC_FORDEV); 2332bb5e3b2fSeh146360 (void) ddi_dma_sync(sc->sc_dma_txdsc.dr_hnd, 2333bb5e3b2fSeh146360 idx * sizeof (struct ipw2200_tx_desc), 2334bb5e3b2fSeh146360 sizeof (struct ipw2200_tx_desc), DDI_DMA_SYNC_FORDEV); 2335bb5e3b2fSeh146360 2336bb5e3b2fSeh146360 sc->sc_tx_cur = RING_FORWARD(sc->sc_tx_cur, 1, IPW2200_TX_RING_SIZE); 2337bb5e3b2fSeh146360 sc->sc_tx_free--; 2338bb5e3b2fSeh146360 2339bb5e3b2fSeh146360 /* 2340bb5e3b2fSeh146360 * update txcur 2341bb5e3b2fSeh146360 */ 2342bb5e3b2fSeh146360 ipw2200_csr_put32(sc, IPW2200_CSR_TX1_WRITE_INDEX, sc->sc_tx_cur); 2343bb5e3b2fSeh146360 2344bb5e3b2fSeh146360 /* 2345bb5e3b2fSeh146360 * success, free the original message 2346bb5e3b2fSeh146360 */ 2347bb5e3b2fSeh146360 if (mp) 2348bb5e3b2fSeh146360 freemsg(mp); 2349924f3e72Seh146360 fail2: 2350924f3e72Seh146360 if (m) 2351924f3e72Seh146360 freemsg(m); 2352bb5e3b2fSeh146360 fail1: 2353bb5e3b2fSeh146360 mutex_exit(&sc->sc_tx_lock); 2354bb5e3b2fSeh146360 fail0: 2355bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_GLD, (sc->sc_dip, CE_CONT, 2356bb5e3b2fSeh146360 "ipw2200_send(): exit - err=%d\n", err)); 2357bb5e3b2fSeh146360 2358bb5e3b2fSeh146360 return (err); 2359bb5e3b2fSeh146360 } 2360bb5e3b2fSeh146360 2361bb5e3b2fSeh146360 /* 2362bb5e3b2fSeh146360 * IOCTL handlers 2363bb5e3b2fSeh146360 */ 2364bb5e3b2fSeh146360 #define IEEE80211_IOCTL_REQUIRED (1) 2365bb5e3b2fSeh146360 #define IEEE80211_IOCTL_NOT_REQUIRED (0) 2366bb5e3b2fSeh146360 static void 2367bb5e3b2fSeh146360 ipw2200_m_ioctl(void *arg, queue_t *q, mblk_t *m) 2368bb5e3b2fSeh146360 { 2369bb5e3b2fSeh146360 struct ipw2200_softc *sc = (struct ipw2200_softc *)arg; 2370bb5e3b2fSeh146360 struct ieee80211com *ic = &sc->sc_ic; 2371bb5e3b2fSeh146360 uint32_t err; 2372bb5e3b2fSeh146360 2373bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_GLD, (sc->sc_dip, CE_CONT, 2374bb5e3b2fSeh146360 "ipw2200_m_ioctl(): enter\n")); 2375bb5e3b2fSeh146360 2376bb5e3b2fSeh146360 /* 2377bb5e3b2fSeh146360 * Check whether or not need to handle this in net80211 2378bb5e3b2fSeh146360 * 2379bb5e3b2fSeh146360 */ 2380bb5e3b2fSeh146360 if (ipw2200_ioctl(sc, q, m) == IEEE80211_IOCTL_NOT_REQUIRED) 2381bb5e3b2fSeh146360 return; 2382bb5e3b2fSeh146360 2383bb5e3b2fSeh146360 err = ieee80211_ioctl(ic, q, m); 2384bb5e3b2fSeh146360 if (err == ENETRESET) { 2385bb5e3b2fSeh146360 if (sc->sc_flags & IPW2200_FLAG_RUNNING) { 2386bb5e3b2fSeh146360 (void) ipw2200_m_start(sc); 2387bb5e3b2fSeh146360 (void) ieee80211_new_state(ic, 2388bb5e3b2fSeh146360 IEEE80211_S_SCAN, -1); 2389bb5e3b2fSeh146360 } 2390bb5e3b2fSeh146360 } 2391bb5e3b2fSeh146360 if (err == ERESTART) { 2392bb5e3b2fSeh146360 if (sc->sc_flags & IPW2200_FLAG_RUNNING) 2393bb5e3b2fSeh146360 (void) ipw2200_chip_reset(sc); 2394bb5e3b2fSeh146360 } 2395bb5e3b2fSeh146360 } 2396bb5e3b2fSeh146360 static int 2397bb5e3b2fSeh146360 ipw2200_ioctl(struct ipw2200_softc *sc, queue_t *q, mblk_t *m) 2398bb5e3b2fSeh146360 { 2399bb5e3b2fSeh146360 struct iocblk *iocp; 2400922d2c76Seh146360 uint32_t len, ret, cmd, mblen; 2401bb5e3b2fSeh146360 mblk_t *m0; 2402bb5e3b2fSeh146360 boolean_t need_privilege; 2403bb5e3b2fSeh146360 boolean_t need_net80211; 2404bb5e3b2fSeh146360 2405922d2c76Seh146360 mblen = MBLKL(m); 2406922d2c76Seh146360 if (mblen < sizeof (struct iocblk)) { 2407bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_IOCTL, (sc->sc_dip, CE_CONT, 2408bb5e3b2fSeh146360 "ipw2200_ioctl(): ioctl buffer too short, %u\n", 2409922d2c76Seh146360 mblen)); 2410bb5e3b2fSeh146360 miocnak(q, m, 0, EINVAL); 2411bb5e3b2fSeh146360 /* 2412bb5e3b2fSeh146360 * Buf not enough, do not need net80211 either 2413bb5e3b2fSeh146360 */ 2414bb5e3b2fSeh146360 return (IEEE80211_IOCTL_NOT_REQUIRED); 2415bb5e3b2fSeh146360 } 2416bb5e3b2fSeh146360 2417bb5e3b2fSeh146360 /* 2418bb5e3b2fSeh146360 * Validate the command 2419bb5e3b2fSeh146360 */ 2420922d2c76Seh146360 iocp = (struct iocblk *)(uintptr_t)m->b_rptr; 2421bb5e3b2fSeh146360 iocp->ioc_error = 0; 2422bb5e3b2fSeh146360 cmd = iocp->ioc_cmd; 2423bb5e3b2fSeh146360 need_privilege = B_TRUE; 2424bb5e3b2fSeh146360 switch (cmd) { 2425bb5e3b2fSeh146360 case WLAN_SET_PARAM: 2426bb5e3b2fSeh146360 case WLAN_COMMAND: 2427bb5e3b2fSeh146360 break; 2428bb5e3b2fSeh146360 case WLAN_GET_PARAM: 2429bb5e3b2fSeh146360 need_privilege = B_FALSE; 2430bb5e3b2fSeh146360 break; 2431bb5e3b2fSeh146360 default: 2432bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_IOCTL, (sc->sc_dip, CE_CONT, 2433bb5e3b2fSeh146360 "ipw2200_ioctl(): unknown cmd 0x%x", cmd)); 2434bb5e3b2fSeh146360 miocnak(q, m, 0, EINVAL); 2435bb5e3b2fSeh146360 /* 2436bb5e3b2fSeh146360 * Unknown cmd, do not need net80211 either 2437bb5e3b2fSeh146360 */ 2438bb5e3b2fSeh146360 return (IEEE80211_IOCTL_NOT_REQUIRED); 2439bb5e3b2fSeh146360 } 2440bb5e3b2fSeh146360 2441eae72b5bSSebastien Roy if (need_privilege && (ret = secpolicy_dl_config(iocp->ioc_cr)) != 0) { 2442bb5e3b2fSeh146360 miocnak(q, m, 0, ret); 2443bb5e3b2fSeh146360 /* 2444bb5e3b2fSeh146360 * privilege check fail, do not need net80211 either 2445bb5e3b2fSeh146360 */ 2446bb5e3b2fSeh146360 return (IEEE80211_IOCTL_NOT_REQUIRED); 2447bb5e3b2fSeh146360 } 2448eae72b5bSSebastien Roy 2449bb5e3b2fSeh146360 /* 2450bb5e3b2fSeh146360 * sanity check 2451bb5e3b2fSeh146360 */ 2452bb5e3b2fSeh146360 m0 = m->b_cont; 2453bb5e3b2fSeh146360 if (iocp->ioc_count == 0 || iocp->ioc_count < sizeof (wldp_t) || 2454bb5e3b2fSeh146360 m0 == NULL) { 2455bb5e3b2fSeh146360 miocnak(q, m, 0, EINVAL); 2456bb5e3b2fSeh146360 /* 2457bb5e3b2fSeh146360 * invalid format, do not need net80211 either 2458bb5e3b2fSeh146360 */ 2459bb5e3b2fSeh146360 return (IEEE80211_IOCTL_NOT_REQUIRED); 2460bb5e3b2fSeh146360 } 2461bb5e3b2fSeh146360 /* 2462bb5e3b2fSeh146360 * assuming single data block 2463bb5e3b2fSeh146360 */ 2464bb5e3b2fSeh146360 if (m0->b_cont) { 2465bb5e3b2fSeh146360 freemsg(m0->b_cont); 2466bb5e3b2fSeh146360 m0->b_cont = NULL; 2467bb5e3b2fSeh146360 } 2468bb5e3b2fSeh146360 2469bb5e3b2fSeh146360 need_net80211 = B_FALSE; 2470bb5e3b2fSeh146360 ret = ipw2200_getset(sc, m0, cmd, &need_net80211); 2471bb5e3b2fSeh146360 if (!need_net80211) { 2472bb5e3b2fSeh146360 len = msgdsize(m0); 2473bb5e3b2fSeh146360 2474bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_IOCTL, (sc->sc_dip, CE_CONT, 2475bb5e3b2fSeh146360 "ipw2200_ioctl(): go to call miocack with " 2476bb5e3b2fSeh146360 "ret = %d, len = %d\n", ret, len)); 2477bb5e3b2fSeh146360 miocack(q, m, len, ret); 2478bb5e3b2fSeh146360 return (IEEE80211_IOCTL_NOT_REQUIRED); 2479bb5e3b2fSeh146360 } 2480bb5e3b2fSeh146360 2481bb5e3b2fSeh146360 /* 2482bb5e3b2fSeh146360 * IEEE80211_IOCTL - need net80211 handle 2483bb5e3b2fSeh146360 */ 2484bb5e3b2fSeh146360 return (IEEE80211_IOCTL_REQUIRED); 2485bb5e3b2fSeh146360 } 2486bb5e3b2fSeh146360 2487bb5e3b2fSeh146360 static int 2488bb5e3b2fSeh146360 ipw2200_getset(struct ipw2200_softc *sc, mblk_t *m, uint32_t cmd, 2489bb5e3b2fSeh146360 boolean_t *need_net80211) 2490bb5e3b2fSeh146360 { 2491bb5e3b2fSeh146360 wldp_t *infp, *outfp; 2492bb5e3b2fSeh146360 uint32_t id; 2493bb5e3b2fSeh146360 int ret; 2494bb5e3b2fSeh146360 2495922d2c76Seh146360 infp = (wldp_t *)(uintptr_t)m->b_rptr; 2496922d2c76Seh146360 outfp = (wldp_t *)(uintptr_t)m->b_rptr; 2497bb5e3b2fSeh146360 outfp->wldp_result = WL_NOTSUPPORTED; 2498bb5e3b2fSeh146360 2499bb5e3b2fSeh146360 id = infp->wldp_id; 2500bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_IOCTL, (sc->sc_dip, CE_CONT, 2501bb5e3b2fSeh146360 "ipw2200_getset(): id = 0x%x\n", id)); 2502bb5e3b2fSeh146360 switch (id) { 2503bb5e3b2fSeh146360 case WL_RADIO: /* which is not supported by net80211 */ 2504bb5e3b2fSeh146360 ret = iwi_wificfg_radio(sc, cmd, outfp); 2505bb5e3b2fSeh146360 break; 2506bb5e3b2fSeh146360 case WL_DESIRED_RATES: /* hardware doesn't support fix-rates */ 2507bb5e3b2fSeh146360 ret = iwi_wificfg_desrates(outfp); 2508bb5e3b2fSeh146360 break; 2509bb5e3b2fSeh146360 default: 2510bb5e3b2fSeh146360 /* 2511bb5e3b2fSeh146360 * The wifi IOCTL net80211 supported: 2512bb5e3b2fSeh146360 * case WL_ESSID: 2513bb5e3b2fSeh146360 * case WL_BSSID: 2514bb5e3b2fSeh146360 * case WL_WEP_KEY_TAB: 2515bb5e3b2fSeh146360 * case WL_WEP_KEY_ID: 2516bb5e3b2fSeh146360 * case WL_AUTH_MODE: 2517bb5e3b2fSeh146360 * case WL_ENCRYPTION: 2518bb5e3b2fSeh146360 * case WL_BSS_TYPE: 2519bb5e3b2fSeh146360 * case WL_ESS_LIST: 2520bb5e3b2fSeh146360 * case WL_LINKSTATUS: 2521bb5e3b2fSeh146360 * case WL_RSSI: 2522bb5e3b2fSeh146360 * case WL_SCAN: 2523bb5e3b2fSeh146360 * case WL_LOAD_DEFAULTS: 2524bb5e3b2fSeh146360 * case WL_DISASSOCIATE: 2525bb5e3b2fSeh146360 */ 25269e2cd38cSeh146360 25279e2cd38cSeh146360 /* 25289e2cd38cSeh146360 * When radio is off, need to ignore all ioctl. What need to 25299e2cd38cSeh146360 * do is to check radio status firstly. If radio is ON, pass 25309e2cd38cSeh146360 * it to net80211, otherwise, return to upper layer directly. 25319e2cd38cSeh146360 * 25329e2cd38cSeh146360 * Considering the WL_SUCCESS also means WL_CONNECTED for 25339e2cd38cSeh146360 * checking linkstatus, one exception for WL_LINKSTATUS is to 25349e2cd38cSeh146360 * let net80211 handle it. 25359e2cd38cSeh146360 */ 25369e2cd38cSeh146360 if ((ipw2200_radio_status(sc) == 0) && 25379e2cd38cSeh146360 (id != WL_LINKSTATUS)) { 25389e2cd38cSeh146360 25399e2cd38cSeh146360 IPW2200_REPORT((sc->sc_dip, CE_CONT, 25409e2cd38cSeh146360 "iwi: radio is OFF\n")); 25419e2cd38cSeh146360 25429e2cd38cSeh146360 outfp->wldp_length = WIFI_BUF_OFFSET; 25439e2cd38cSeh146360 outfp->wldp_result = WL_SUCCESS; 25449e2cd38cSeh146360 ret = 0; 25459e2cd38cSeh146360 break; 25469e2cd38cSeh146360 } 25479e2cd38cSeh146360 2548bb5e3b2fSeh146360 *need_net80211 = B_TRUE; /* let net80211 do the rest */ 2549bb5e3b2fSeh146360 return (0); 2550bb5e3b2fSeh146360 } 2551bb5e3b2fSeh146360 /* 2552bb5e3b2fSeh146360 * we will overwrite everything 2553bb5e3b2fSeh146360 */ 2554bb5e3b2fSeh146360 m->b_wptr = m->b_rptr + outfp->wldp_length; 2555bb5e3b2fSeh146360 return (ret); 2556bb5e3b2fSeh146360 } 2557bb5e3b2fSeh146360 25587efa17f5Sfei feng - Sun Microsystems - Beijing China /* 25597efa17f5Sfei feng - Sun Microsystems - Beijing China * Call back functions for get/set proporty 25607efa17f5Sfei feng - Sun Microsystems - Beijing China */ 25617efa17f5Sfei feng - Sun Microsystems - Beijing China static int 25627efa17f5Sfei feng - Sun Microsystems - Beijing China ipw2200_m_getprop(void *arg, const char *pr_name, mac_prop_id_t wldp_pr_num, 2563*0dc2366fSVenugopal Iyer uint_t wldp_length, void *wldp_buf) 25647efa17f5Sfei feng - Sun Microsystems - Beijing China { 25657efa17f5Sfei feng - Sun Microsystems - Beijing China struct ipw2200_softc *sc = (struct ipw2200_softc *)arg; 25667efa17f5Sfei feng - Sun Microsystems - Beijing China struct ieee80211com *ic = &sc->sc_ic; 25677efa17f5Sfei feng - Sun Microsystems - Beijing China int err = 0; 25687efa17f5Sfei feng - Sun Microsystems - Beijing China 25697efa17f5Sfei feng - Sun Microsystems - Beijing China switch (wldp_pr_num) { 25707efa17f5Sfei feng - Sun Microsystems - Beijing China /* mac_prop_id */ 25717efa17f5Sfei feng - Sun Microsystems - Beijing China case MAC_PROP_WL_DESIRED_RATES: 25727efa17f5Sfei feng - Sun Microsystems - Beijing China IPW2200_DBG(IPW2200_DBG_BRUSSELS, (sc->sc_dip, CE_CONT, 25737efa17f5Sfei feng - Sun Microsystems - Beijing China "ipw2200_m_getprop(): Not Support DESIRED_RATES\n")); 25747efa17f5Sfei feng - Sun Microsystems - Beijing China break; 25757efa17f5Sfei feng - Sun Microsystems - Beijing China case MAC_PROP_WL_RADIO: 25767efa17f5Sfei feng - Sun Microsystems - Beijing China *(wl_linkstatus_t *)wldp_buf = ipw2200_radio_status(sc); 25777efa17f5Sfei feng - Sun Microsystems - Beijing China break; 25787efa17f5Sfei feng - Sun Microsystems - Beijing China default: 25797efa17f5Sfei feng - Sun Microsystems - Beijing China /* go through net80211 */ 2580*0dc2366fSVenugopal Iyer err = ieee80211_getprop(ic, pr_name, wldp_pr_num, 2581*0dc2366fSVenugopal Iyer wldp_length, wldp_buf); 25827efa17f5Sfei feng - Sun Microsystems - Beijing China break; 25837efa17f5Sfei feng - Sun Microsystems - Beijing China } 25847efa17f5Sfei feng - Sun Microsystems - Beijing China 25857efa17f5Sfei feng - Sun Microsystems - Beijing China return (err); 25867efa17f5Sfei feng - Sun Microsystems - Beijing China } 25877efa17f5Sfei feng - Sun Microsystems - Beijing China 2588*0dc2366fSVenugopal Iyer static void 2589*0dc2366fSVenugopal Iyer ipw2200_m_propinfo(void *arg, const char *pr_name, 2590*0dc2366fSVenugopal Iyer mac_prop_id_t wlpd_pr_num, mac_prop_info_handle_t mph) 2591*0dc2366fSVenugopal Iyer { 2592*0dc2366fSVenugopal Iyer struct ipw2200_softc *sc = (struct ipw2200_softc *)arg; 2593*0dc2366fSVenugopal Iyer struct ieee80211com *ic = &sc->sc_ic; 2594*0dc2366fSVenugopal Iyer 2595*0dc2366fSVenugopal Iyer ieee80211_propinfo(ic, pr_name, wlpd_pr_num, mph); 2596*0dc2366fSVenugopal Iyer } 2597*0dc2366fSVenugopal Iyer 25987efa17f5Sfei feng - Sun Microsystems - Beijing China static int 25997efa17f5Sfei feng - Sun Microsystems - Beijing China ipw2200_m_setprop(void *arg, const char *pr_name, mac_prop_id_t wldp_pr_num, 26007efa17f5Sfei feng - Sun Microsystems - Beijing China uint_t wldp_length, const void *wldp_buf) 26017efa17f5Sfei feng - Sun Microsystems - Beijing China { 26027efa17f5Sfei feng - Sun Microsystems - Beijing China struct ipw2200_softc *sc = (struct ipw2200_softc *)arg; 26037efa17f5Sfei feng - Sun Microsystems - Beijing China struct ieee80211com *ic = &sc->sc_ic; 26047efa17f5Sfei feng - Sun Microsystems - Beijing China int err; 26057efa17f5Sfei feng - Sun Microsystems - Beijing China 26067efa17f5Sfei feng - Sun Microsystems - Beijing China switch (wldp_pr_num) { 26077efa17f5Sfei feng - Sun Microsystems - Beijing China /* mac_prop_id */ 26087efa17f5Sfei feng - Sun Microsystems - Beijing China case MAC_PROP_WL_DESIRED_RATES: 26097efa17f5Sfei feng - Sun Microsystems - Beijing China IPW2200_DBG(IPW2200_DBG_BRUSSELS, (sc->sc_dip, CE_CONT, 26107efa17f5Sfei feng - Sun Microsystems - Beijing China "ipw2200_m_setprop(): Not Support DESIRED_RATES\n")); 26117efa17f5Sfei feng - Sun Microsystems - Beijing China err = ENOTSUP; 26127efa17f5Sfei feng - Sun Microsystems - Beijing China break; 26137efa17f5Sfei feng - Sun Microsystems - Beijing China case MAC_PROP_WL_RADIO: 26147efa17f5Sfei feng - Sun Microsystems - Beijing China IPW2200_DBG(IPW2200_DBG_BRUSSELS, (sc->sc_dip, CE_CONT, 26157efa17f5Sfei feng - Sun Microsystems - Beijing China "ipw2200_m_setprop(): Not Support RADIO\n")); 26167efa17f5Sfei feng - Sun Microsystems - Beijing China err = ENOTSUP; 26177efa17f5Sfei feng - Sun Microsystems - Beijing China break; 26187efa17f5Sfei feng - Sun Microsystems - Beijing China default: 26197efa17f5Sfei feng - Sun Microsystems - Beijing China /* go through net80211 */ 26207efa17f5Sfei feng - Sun Microsystems - Beijing China err = ieee80211_setprop(ic, pr_name, wldp_pr_num, wldp_length, 26217efa17f5Sfei feng - Sun Microsystems - Beijing China wldp_buf); 26227efa17f5Sfei feng - Sun Microsystems - Beijing China break; 26237efa17f5Sfei feng - Sun Microsystems - Beijing China } 26247efa17f5Sfei feng - Sun Microsystems - Beijing China 26257efa17f5Sfei feng - Sun Microsystems - Beijing China if (err == ENETRESET) { 26267efa17f5Sfei feng - Sun Microsystems - Beijing China if (sc->sc_flags & IPW2200_FLAG_RUNNING) { 26277efa17f5Sfei feng - Sun Microsystems - Beijing China (void) ipw2200_m_start(sc); 26287efa17f5Sfei feng - Sun Microsystems - Beijing China (void) ieee80211_new_state(ic, 26297efa17f5Sfei feng - Sun Microsystems - Beijing China IEEE80211_S_SCAN, -1); 26307efa17f5Sfei feng - Sun Microsystems - Beijing China } 26317efa17f5Sfei feng - Sun Microsystems - Beijing China err = 0; 26327efa17f5Sfei feng - Sun Microsystems - Beijing China } 26337efa17f5Sfei feng - Sun Microsystems - Beijing China 26347efa17f5Sfei feng - Sun Microsystems - Beijing China return (err); 26357efa17f5Sfei feng - Sun Microsystems - Beijing China } 26367efa17f5Sfei feng - Sun Microsystems - Beijing China 2637bb5e3b2fSeh146360 static int 2638bb5e3b2fSeh146360 iwi_wificfg_radio(struct ipw2200_softc *sc, uint32_t cmd, wldp_t *outfp) 2639bb5e3b2fSeh146360 { 2640bb5e3b2fSeh146360 uint32_t ret = ENOTSUP; 2641bb5e3b2fSeh146360 2642bb5e3b2fSeh146360 switch (cmd) { 2643bb5e3b2fSeh146360 case WLAN_GET_PARAM: 2644bb5e3b2fSeh146360 *(wl_linkstatus_t *)(outfp->wldp_buf) = 2645bb5e3b2fSeh146360 ipw2200_radio_status(sc); 2646bb5e3b2fSeh146360 outfp->wldp_length = WIFI_BUF_OFFSET + sizeof (wl_linkstatus_t); 2647bb5e3b2fSeh146360 outfp->wldp_result = WL_SUCCESS; 2648bb5e3b2fSeh146360 ret = 0; /* command success */ 2649bb5e3b2fSeh146360 break; 2650bb5e3b2fSeh146360 case WLAN_SET_PARAM: 2651bb5e3b2fSeh146360 default: 2652bb5e3b2fSeh146360 break; 2653bb5e3b2fSeh146360 } 2654bb5e3b2fSeh146360 return (ret); 2655bb5e3b2fSeh146360 } 2656bb5e3b2fSeh146360 2657bb5e3b2fSeh146360 static int 2658bb5e3b2fSeh146360 iwi_wificfg_desrates(wldp_t *outfp) 2659bb5e3b2fSeh146360 { 2660bb5e3b2fSeh146360 /* return success, but with result NOTSUPPORTED */ 2661bb5e3b2fSeh146360 outfp->wldp_length = WIFI_BUF_OFFSET; 2662bb5e3b2fSeh146360 outfp->wldp_result = WL_NOTSUPPORTED; 2663bb5e3b2fSeh146360 return (0); 2664bb5e3b2fSeh146360 } 2665bb5e3b2fSeh146360 /* End of IOCTL Handlers */ 2666bb5e3b2fSeh146360 2667bb5e3b2fSeh146360 void 2668bb5e3b2fSeh146360 ipw2200_fix_channel(struct ieee80211com *ic, mblk_t *m) 2669bb5e3b2fSeh146360 { 2670bb5e3b2fSeh146360 struct ieee80211_frame *wh; 2671bb5e3b2fSeh146360 uint8_t subtype; 2672bb5e3b2fSeh146360 uint8_t *frm, *efrm; 2673bb5e3b2fSeh146360 2674bb5e3b2fSeh146360 wh = (struct ieee80211_frame *)m->b_rptr; 2675bb5e3b2fSeh146360 2676bb5e3b2fSeh146360 if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) != IEEE80211_FC0_TYPE_MGT) 2677bb5e3b2fSeh146360 return; 2678bb5e3b2fSeh146360 2679bb5e3b2fSeh146360 subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK; 2680bb5e3b2fSeh146360 2681bb5e3b2fSeh146360 if (subtype != IEEE80211_FC0_SUBTYPE_BEACON && 2682bb5e3b2fSeh146360 subtype != IEEE80211_FC0_SUBTYPE_PROBE_RESP) 2683bb5e3b2fSeh146360 return; 2684bb5e3b2fSeh146360 2685bb5e3b2fSeh146360 /* 2686bb5e3b2fSeh146360 * assume the message contains only 1 block 2687bb5e3b2fSeh146360 */ 2688bb5e3b2fSeh146360 frm = (uint8_t *)(wh + 1); 2689bb5e3b2fSeh146360 efrm = (uint8_t *)m->b_wptr; 2690bb5e3b2fSeh146360 frm += 12; /* skip tstamp, bintval and capinfo fields */ 2691bb5e3b2fSeh146360 while (frm < efrm) { 2692bb5e3b2fSeh146360 if (*frm == IEEE80211_ELEMID_DSPARMS) 2693bb5e3b2fSeh146360 #if IEEE80211_CHAN_MAX < 255 2694bb5e3b2fSeh146360 if (frm[2] <= IEEE80211_CHAN_MAX) 2695bb5e3b2fSeh146360 #endif 2696bb5e3b2fSeh146360 ic->ic_curchan = &ic->ic_sup_channels[frm[2]]; 2697bb5e3b2fSeh146360 frm += frm[1] + 2; 2698bb5e3b2fSeh146360 } 2699bb5e3b2fSeh146360 } 2700bb5e3b2fSeh146360 2701bb5e3b2fSeh146360 static void 2702bb5e3b2fSeh146360 ipw2200_rcv_frame(struct ipw2200_softc *sc, struct ipw2200_frame *frame) 2703bb5e3b2fSeh146360 { 2704bb5e3b2fSeh146360 struct ieee80211com *ic = &sc->sc_ic; 2705bb5e3b2fSeh146360 uint8_t *data = (uint8_t *)frame; 2706bb5e3b2fSeh146360 uint32_t len; 2707bb5e3b2fSeh146360 struct ieee80211_frame *wh; 2708bb5e3b2fSeh146360 struct ieee80211_node *in; 2709bb5e3b2fSeh146360 mblk_t *m; 2710bb5e3b2fSeh146360 2711bb5e3b2fSeh146360 len = LE_16(frame->len); 2712bb5e3b2fSeh146360 if ((len < sizeof (struct ieee80211_frame_min)) || 2713bb5e3b2fSeh146360 (len > IPW2200_RXBUF_SIZE)) { 2714bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_RX, (sc->sc_dip, CE_CONT, 2715bb5e3b2fSeh146360 "ipw2200_rcv_frame(): bad frame length=%u\n", 2716bb5e3b2fSeh146360 LE_16(frame->len))); 2717922d2c76Seh146360 sc->sc_stats.sc_rx_len_err++; /* length doesn't work */ 2718bb5e3b2fSeh146360 return; 2719bb5e3b2fSeh146360 } 2720bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_RX, (sc->sc_dip, CE_CONT, 2721bb5e3b2fSeh146360 "ipw2200_rcv_frame(): chan = %d, length = %d\n", frame->chan, len)); 2722bb5e3b2fSeh146360 2723924f3e72Seh146360 /* 2724924f3e72Seh146360 * Skip the frame header, get the real data from the input 2725924f3e72Seh146360 */ 2726924f3e72Seh146360 data += sizeof (struct ipw2200_frame); 2727924f3e72Seh146360 2728bb5e3b2fSeh146360 m = allocb(len, BPRI_MED); 2729bb5e3b2fSeh146360 if (m) { 2730bb5e3b2fSeh146360 (void) memcpy(m->b_wptr, data, len); 2731bb5e3b2fSeh146360 m->b_wptr += len; 2732bb5e3b2fSeh146360 2733bb5e3b2fSeh146360 if (ic->ic_state == IEEE80211_S_SCAN) { 2734bb5e3b2fSeh146360 ic->ic_ibss_chan = &ic->ic_sup_channels[frame->chan]; 2735bb5e3b2fSeh146360 ipw2200_fix_channel(ic, m); 2736bb5e3b2fSeh146360 } 2737924f3e72Seh146360 wh = (struct ieee80211_frame *)m->b_rptr; 2738bb5e3b2fSeh146360 2739bb5e3b2fSeh146360 in = ieee80211_find_rxnode(ic, wh); 2740924f3e72Seh146360 2741bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_RX, (sc->sc_dip, CE_CONT, 2742bb5e3b2fSeh146360 "ipw2200_rcv_frame(): " 2743924f3e72Seh146360 "type = %x, subtype = %x, i_fc[1] = %x, " 2744bb5e3b2fSeh146360 "ni_esslen:%d, ni_essid[0-5]:%c%c%c%c%c%c\n", 2745924f3e72Seh146360 wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK, 2746924f3e72Seh146360 wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK, 2747924f3e72Seh146360 wh->i_fc[1] & IEEE80211_FC1_WEP, 2748bb5e3b2fSeh146360 in->in_esslen, 2749bb5e3b2fSeh146360 in->in_essid[0], in->in_essid[1], in->in_essid[2], 2750bb5e3b2fSeh146360 in->in_essid[3], in->in_essid[4], in->in_essid[5])); 2751bb5e3b2fSeh146360 2752bb5e3b2fSeh146360 (void) ieee80211_input(ic, m, in, frame->rssi_dbm, 0); 2753bb5e3b2fSeh146360 2754bb5e3b2fSeh146360 ieee80211_free_node(in); 2755bb5e3b2fSeh146360 } 2756bb5e3b2fSeh146360 else 2757bb5e3b2fSeh146360 IPW2200_WARN((sc->sc_dip, CE_WARN, 2758bb5e3b2fSeh146360 "ipw2200_rcv_frame(): " 2759bb5e3b2fSeh146360 "cannot allocate receive message(%u)\n", 2760bb5e3b2fSeh146360 LE_16(frame->len))); 2761bb5e3b2fSeh146360 } 2762bb5e3b2fSeh146360 2763bb5e3b2fSeh146360 static void 2764bb5e3b2fSeh146360 ipw2200_rcv_notif(struct ipw2200_softc *sc, struct ipw2200_notif *notif) 2765bb5e3b2fSeh146360 { 2766bb5e3b2fSeh146360 struct ieee80211com *ic = &sc->sc_ic; 2767bb5e3b2fSeh146360 struct ipw2200_notif_association *assoc; 2768bb5e3b2fSeh146360 struct ipw2200_notif_authentication *auth; 2769bb5e3b2fSeh146360 uint8_t *ndata = (uint8_t *)notif; 2770bb5e3b2fSeh146360 2771bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_NOTIF, (sc->sc_dip, CE_CONT, 2772bb5e3b2fSeh146360 "ipw2200_rcv_notif(): type=%u\n", notif->type)); 2773bb5e3b2fSeh146360 2774bb5e3b2fSeh146360 ndata += sizeof (struct ipw2200_notif); 2775bb5e3b2fSeh146360 switch (notif->type) { 2776bb5e3b2fSeh146360 case IPW2200_NOTIF_TYPE_ASSOCIATION: 2777bb5e3b2fSeh146360 assoc = (struct ipw2200_notif_association *)ndata; 2778bb5e3b2fSeh146360 2779bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT, 2780bb5e3b2fSeh146360 "ipw2200_rcv_notif(): association=%u,%u\n", 2781bb5e3b2fSeh146360 assoc->state, assoc->status)); 2782bb5e3b2fSeh146360 2783bb5e3b2fSeh146360 switch (assoc->state) { 2784bb5e3b2fSeh146360 case IPW2200_ASSOC_SUCCESS: 2785924f3e72Seh146360 sc->sc_flags |= IPW2200_FLAG_ASSOCIATED; 2786bb5e3b2fSeh146360 ieee80211_new_state(ic, IEEE80211_S_RUN, -1); 2787bb5e3b2fSeh146360 break; 2788bb5e3b2fSeh146360 case IPW2200_ASSOC_FAIL: 2789924f3e72Seh146360 sc->sc_flags &= ~IPW2200_FLAG_ASSOCIATED; 2790924f3e72Seh146360 ieee80211_begin_scan(ic, 1); 2791bb5e3b2fSeh146360 break; 2792bb5e3b2fSeh146360 default: 2793bb5e3b2fSeh146360 break; 2794bb5e3b2fSeh146360 } 2795bb5e3b2fSeh146360 break; 2796bb5e3b2fSeh146360 2797bb5e3b2fSeh146360 case IPW2200_NOTIF_TYPE_AUTHENTICATION: 2798bb5e3b2fSeh146360 auth = (struct ipw2200_notif_authentication *)ndata; 2799bb5e3b2fSeh146360 2800bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT, 2801bb5e3b2fSeh146360 "ipw2200_rcv_notif(): authentication=%u\n", auth->state)); 2802bb5e3b2fSeh146360 2803bb5e3b2fSeh146360 switch (auth->state) { 2804bb5e3b2fSeh146360 case IPW2200_AUTH_SUCCESS: 2805bb5e3b2fSeh146360 ieee80211_new_state(ic, IEEE80211_S_ASSOC, -1); 2806bb5e3b2fSeh146360 break; 2807bb5e3b2fSeh146360 case IPW2200_AUTH_FAIL: 2808924f3e72Seh146360 sc->sc_flags &= ~IPW2200_FLAG_ASSOCIATED; 2809bb5e3b2fSeh146360 break; 2810bb5e3b2fSeh146360 default: 2811bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_NOTIF, (sc->sc_dip, CE_CONT, 2812bb5e3b2fSeh146360 "ipw2200_rcv_notif(): " 2813bb5e3b2fSeh146360 "unknown authentication state(%u)\n", auth->state)); 2814bb5e3b2fSeh146360 break; 2815bb5e3b2fSeh146360 } 2816bb5e3b2fSeh146360 break; 2817bb5e3b2fSeh146360 2818bb5e3b2fSeh146360 case IPW2200_NOTIF_TYPE_SCAN_CHANNEL: 2819bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_SCAN, (sc->sc_dip, CE_CONT, 2820bb5e3b2fSeh146360 "ipw2200_rcv_notif(): scan-channel=%u\n", 2821bb5e3b2fSeh146360 ((struct ipw2200_notif_scan_channel *)ndata)->nchan)); 2822bb5e3b2fSeh146360 break; 2823bb5e3b2fSeh146360 2824bb5e3b2fSeh146360 case IPW2200_NOTIF_TYPE_SCAN_COMPLETE: 2825bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_SCAN, (sc->sc_dip, CE_CONT, 2826bb5e3b2fSeh146360 "ipw2200_rcv_notif():scan-completed,(%u,%u)\n", 2827bb5e3b2fSeh146360 ((struct ipw2200_notif_scan_complete *)ndata)->nchan, 2828bb5e3b2fSeh146360 ((struct ipw2200_notif_scan_complete *)ndata)->status)); 2829bb5e3b2fSeh146360 2830bb5e3b2fSeh146360 /* 2831bb5e3b2fSeh146360 * scan complete 2832bb5e3b2fSeh146360 */ 2833bb5e3b2fSeh146360 sc->sc_flags &= ~IPW2200_FLAG_SCANNING; 2834bb5e3b2fSeh146360 ieee80211_end_scan(ic); 2835bb5e3b2fSeh146360 break; 2836bb5e3b2fSeh146360 2837bb5e3b2fSeh146360 case IPW2200_NOTIF_TYPE_BEACON: 2838bb5e3b2fSeh146360 case IPW2200_NOTIF_TYPE_CALIBRATION: 2839bb5e3b2fSeh146360 case IPW2200_NOTIF_TYPE_NOISE: 2840bb5e3b2fSeh146360 /* 2841bb5e3b2fSeh146360 * just ignore 2842bb5e3b2fSeh146360 */ 2843bb5e3b2fSeh146360 break; 2844bb5e3b2fSeh146360 default: 2845bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_NOTIF, (sc->sc_dip, CE_CONT, 2846bb5e3b2fSeh146360 "ipw2200_rcv_notif(): unknown notification type(%u)\n", 2847bb5e3b2fSeh146360 notif->type)); 2848bb5e3b2fSeh146360 break; 2849bb5e3b2fSeh146360 } 2850bb5e3b2fSeh146360 } 2851bb5e3b2fSeh146360 2852bb5e3b2fSeh146360 static uint_t 2853bb5e3b2fSeh146360 ipw2200_intr(caddr_t arg) 2854bb5e3b2fSeh146360 { 2855922d2c76Seh146360 struct ipw2200_softc *sc = (struct ipw2200_softc *)(uintptr_t)arg; 2856bb5e3b2fSeh146360 struct ieee80211com *ic = &sc->sc_ic; 2857bb5e3b2fSeh146360 uint32_t ireg, ridx, len, i; 2858bb5e3b2fSeh146360 uint8_t *p, *rxbuf; 2859bb5e3b2fSeh146360 struct dma_region *dr; 2860bb5e3b2fSeh146360 struct ipw2200_hdr *hdr; 2861bb5e3b2fSeh146360 uint32_t widx; 2862bb5e3b2fSeh146360 2863922d2c76Seh146360 /* when it is on suspend, unclaim all interrupt directly */ 2864922d2c76Seh146360 if (sc->sc_flags & IPW2200_FLAG_SUSPEND) 2865bb5e3b2fSeh146360 return (DDI_INTR_UNCLAIMED); 2866bb5e3b2fSeh146360 2867922d2c76Seh146360 /* unclaim interrupt when it is not for iwi */ 2868922d2c76Seh146360 ireg = ipw2200_csr_get32(sc, IPW2200_CSR_INTR); 2869922d2c76Seh146360 if (ireg == 0xffffffff || 2870922d2c76Seh146360 !(ireg & IPW2200_INTR_MASK_ALL)) 2871bb5e3b2fSeh146360 return (DDI_INTR_UNCLAIMED); 2872bb5e3b2fSeh146360 2873bb5e3b2fSeh146360 /* 2874bb5e3b2fSeh146360 * mask all interrupts 2875bb5e3b2fSeh146360 */ 2876bb5e3b2fSeh146360 ipw2200_csr_put32(sc, IPW2200_CSR_INTR_MASK, 0); 2877bb5e3b2fSeh146360 2878bb5e3b2fSeh146360 /* 2879bb5e3b2fSeh146360 * acknowledge all fired interrupts 2880bb5e3b2fSeh146360 */ 2881bb5e3b2fSeh146360 ipw2200_csr_put32(sc, IPW2200_CSR_INTR, ireg); 2882bb5e3b2fSeh146360 2883bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_INT, (sc->sc_dip, CE_CONT, 2884bb5e3b2fSeh146360 "ipw2200_intr(): enter. interrupt fired, int=0x%08x\n", ireg)); 2885bb5e3b2fSeh146360 2886bb5e3b2fSeh146360 if (ireg & IPW2200_INTR_MASK_ERR) { 2887bb5e3b2fSeh146360 2888bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_FATAL, (sc->sc_dip, CE_CONT, 2889bb5e3b2fSeh146360 "ipw2200 interrupt(): int= 0x%08x\n", ireg)); 2890bb5e3b2fSeh146360 2891bb5e3b2fSeh146360 /* 2892bb5e3b2fSeh146360 * inform mfthread to recover hw error by stopping it 2893bb5e3b2fSeh146360 */ 2894bb5e3b2fSeh146360 mutex_enter(&sc->sc_mflock); 2895bb5e3b2fSeh146360 sc->sc_flags |= IPW2200_FLAG_HW_ERR_RECOVER; 2896bb5e3b2fSeh146360 mutex_exit(&sc->sc_mflock); 2897bb5e3b2fSeh146360 2898922d2c76Seh146360 goto enable_interrupt; 2899922d2c76Seh146360 } 2900922d2c76Seh146360 2901924f3e72Seh146360 /* 2902924f3e72Seh146360 * FW intr 2903924f3e72Seh146360 */ 2904bb5e3b2fSeh146360 if (ireg & IPW2200_INTR_FW_INITED) { 2905bb5e3b2fSeh146360 mutex_enter(&sc->sc_ilock); 2906bb5e3b2fSeh146360 sc->sc_fw_ok = 1; 2907bb5e3b2fSeh146360 cv_signal(&sc->sc_fw_cond); 2908bb5e3b2fSeh146360 mutex_exit(&sc->sc_ilock); 2909bb5e3b2fSeh146360 } 2910924f3e72Seh146360 2911924f3e72Seh146360 /* 2912924f3e72Seh146360 * Radio OFF 2913924f3e72Seh146360 */ 2914bb5e3b2fSeh146360 if (ireg & IPW2200_INTR_RADIO_OFF) { 2915bb5e3b2fSeh146360 IPW2200_REPORT((sc->sc_dip, CE_CONT, 2916bb5e3b2fSeh146360 "ipw2200_intr(): radio is OFF\n")); 29179e2cd38cSeh146360 2918bb5e3b2fSeh146360 /* 29199e2cd38cSeh146360 * Stop hardware, will notify LINK is down. 29209e2cd38cSeh146360 * Need a better scan solution to ensure 29219e2cd38cSeh146360 * table has right value. 2922bb5e3b2fSeh146360 */ 2923bb5e3b2fSeh146360 ipw2200_stop(sc); 2924bb5e3b2fSeh146360 } 2925924f3e72Seh146360 2926924f3e72Seh146360 /* 2927924f3e72Seh146360 * CMD intr 2928924f3e72Seh146360 */ 2929bb5e3b2fSeh146360 if (ireg & IPW2200_INTR_CMD_TRANSFER) { 2930bb5e3b2fSeh146360 mutex_enter(&sc->sc_cmd_lock); 2931bb5e3b2fSeh146360 ridx = ipw2200_csr_get32(sc, 2932bb5e3b2fSeh146360 IPW2200_CSR_CMD_READ_INDEX); 2933bb5e3b2fSeh146360 i = RING_FORWARD(sc->sc_cmd_cur, 2934bb5e3b2fSeh146360 sc->sc_cmd_free, IPW2200_CMD_RING_SIZE); 2935bb5e3b2fSeh146360 len = RING_FLEN(i, ridx, IPW2200_CMD_RING_SIZE); 2936bb5e3b2fSeh146360 2937bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_INT, (sc->sc_dip, CE_CONT, 2938bb5e3b2fSeh146360 "ipw2200_intr(): cmd-ring,i=%u,ridx=%u,len=%u\n", 2939bb5e3b2fSeh146360 i, ridx, len)); 2940bb5e3b2fSeh146360 2941bb5e3b2fSeh146360 if (len > 0) { 2942bb5e3b2fSeh146360 sc->sc_cmd_free += len; 2943bb5e3b2fSeh146360 cv_signal(&sc->sc_cmd_cond); 2944bb5e3b2fSeh146360 } 2945bb5e3b2fSeh146360 for (; i != ridx; 2946bb5e3b2fSeh146360 i = RING_FORWARD(i, 1, IPW2200_CMD_RING_SIZE)) 2947bb5e3b2fSeh146360 sc->sc_done[i] = 1; 2948bb5e3b2fSeh146360 mutex_exit(&sc->sc_cmd_lock); 2949bb5e3b2fSeh146360 2950bb5e3b2fSeh146360 mutex_enter(&sc->sc_ilock); 2951bb5e3b2fSeh146360 cv_signal(&sc->sc_cmd_status_cond); 2952bb5e3b2fSeh146360 mutex_exit(&sc->sc_ilock); 2953bb5e3b2fSeh146360 } 2954924f3e72Seh146360 2955924f3e72Seh146360 /* 2956924f3e72Seh146360 * RX intr 2957924f3e72Seh146360 */ 2958bb5e3b2fSeh146360 if (ireg & IPW2200_INTR_RX_TRANSFER) { 2959bb5e3b2fSeh146360 ridx = ipw2200_csr_get32(sc, 2960bb5e3b2fSeh146360 IPW2200_CSR_RX_READ_INDEX); 2961bb5e3b2fSeh146360 widx = ipw2200_csr_get32(sc, 2962bb5e3b2fSeh146360 IPW2200_CSR_RX_WRITE_INDEX); 2963bb5e3b2fSeh146360 2964bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_INT, (sc->sc_dip, CE_CONT, 2965bb5e3b2fSeh146360 "ipw2200_intr(): rx-ring,widx=%u,ridx=%u\n", 2966bb5e3b2fSeh146360 ridx, widx)); 2967bb5e3b2fSeh146360 2968bb5e3b2fSeh146360 for (; sc->sc_rx_cur != ridx; 2969bb5e3b2fSeh146360 sc->sc_rx_cur = RING_FORWARD(sc->sc_rx_cur, 1, 2970bb5e3b2fSeh146360 IPW2200_RX_RING_SIZE)) { 2971bb5e3b2fSeh146360 i = sc->sc_rx_cur; 2972bb5e3b2fSeh146360 rxbuf = sc->sc_rxbufs[i]; 2973bb5e3b2fSeh146360 dr = &sc->sc_dma_rxbufs[i]; 2974bb5e3b2fSeh146360 2975bb5e3b2fSeh146360 /* 2976bb5e3b2fSeh146360 * DMA sync 2977bb5e3b2fSeh146360 */ 2978bb5e3b2fSeh146360 (void) ddi_dma_sync(dr->dr_hnd, 0, 2979bb5e3b2fSeh146360 IPW2200_RXBUF_SIZE, DDI_DMA_SYNC_FORKERNEL); 2980bb5e3b2fSeh146360 /* 2981bb5e3b2fSeh146360 * Get rx header(hdr) and rx data(p) from rxbuf 2982bb5e3b2fSeh146360 */ 2983bb5e3b2fSeh146360 p = rxbuf; 2984bb5e3b2fSeh146360 hdr = (struct ipw2200_hdr *)p; 2985bb5e3b2fSeh146360 p += sizeof (struct ipw2200_hdr); 2986bb5e3b2fSeh146360 2987922d2c76Seh146360 IPW2200_DBG(IPW2200_DBG_INT, (sc->sc_dip, CE_CONT, 2988bb5e3b2fSeh146360 "ipw2200_intr(): Rx hdr type %u\n", 2989bb5e3b2fSeh146360 hdr->type)); 2990bb5e3b2fSeh146360 2991bb5e3b2fSeh146360 switch (hdr->type) { 2992bb5e3b2fSeh146360 case IPW2200_HDR_TYPE_FRAME: 2993bb5e3b2fSeh146360 ipw2200_rcv_frame(sc, 2994bb5e3b2fSeh146360 (struct ipw2200_frame *)p); 2995bb5e3b2fSeh146360 break; 2996bb5e3b2fSeh146360 2997bb5e3b2fSeh146360 case IPW2200_HDR_TYPE_NOTIF: 2998bb5e3b2fSeh146360 ipw2200_rcv_notif(sc, 2999bb5e3b2fSeh146360 (struct ipw2200_notif *)p); 3000bb5e3b2fSeh146360 break; 3001922d2c76Seh146360 3002bb5e3b2fSeh146360 default: 3003922d2c76Seh146360 IPW2200_DBG(IPW2200_DBG_INT, (sc->sc_dip, 3004922d2c76Seh146360 CE_CONT, 3005922d2c76Seh146360 "ipw2200_intr(): unknown Rx hdr type %u\n", 3006bb5e3b2fSeh146360 hdr->type)); 3007bb5e3b2fSeh146360 break; 3008bb5e3b2fSeh146360 } 3009bb5e3b2fSeh146360 } 3010bb5e3b2fSeh146360 /* 3011bb5e3b2fSeh146360 * write sc_rx_cur backward 1 step into RX_WRITE_INDEX 3012bb5e3b2fSeh146360 */ 3013bb5e3b2fSeh146360 ipw2200_csr_put32(sc, IPW2200_CSR_RX_WRITE_INDEX, 3014bb5e3b2fSeh146360 RING_BACKWARD(sc->sc_rx_cur, 1, 3015bb5e3b2fSeh146360 IPW2200_RX_RING_SIZE)); 3016bb5e3b2fSeh146360 } 3017924f3e72Seh146360 3018924f3e72Seh146360 /* 3019924f3e72Seh146360 * TX intr 3020924f3e72Seh146360 */ 3021bb5e3b2fSeh146360 if (ireg & IPW2200_INTR_TX1_TRANSFER) { 3022bb5e3b2fSeh146360 mutex_enter(&sc->sc_tx_lock); 3023bb5e3b2fSeh146360 ridx = ipw2200_csr_get32(sc, 3024bb5e3b2fSeh146360 IPW2200_CSR_TX1_READ_INDEX); 3025bb5e3b2fSeh146360 len = RING_FLEN(RING_FORWARD(sc->sc_tx_cur, 3026bb5e3b2fSeh146360 sc->sc_tx_free, IPW2200_TX_RING_SIZE), 3027bb5e3b2fSeh146360 ridx, IPW2200_TX_RING_SIZE); 3028924f3e72Seh146360 sc->sc_tx_free += len; 3029bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_RING, (sc->sc_dip, CE_CONT, 3030bb5e3b2fSeh146360 "ipw2200_intr(): tx-ring,ridx=%u,len=%u\n", 3031bb5e3b2fSeh146360 ridx, len)); 3032924f3e72Seh146360 mutex_exit(&sc->sc_tx_lock); 3033bb5e3b2fSeh146360 3034924f3e72Seh146360 mutex_enter(&sc->sc_resched_lock); 3035bb5e3b2fSeh146360 if ((sc->sc_tx_free > IPW2200_TX_RING_MIN) && 3036bb5e3b2fSeh146360 (sc->sc_flags & IPW2200_FLAG_TX_SCHED)) { 3037bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_RING, (sc->sc_dip, 3038bb5e3b2fSeh146360 CE_CONT, 3039bb5e3b2fSeh146360 "ipw2200_intr(): Need Reschedule!")); 3040bb5e3b2fSeh146360 sc->sc_flags &= ~IPW2200_FLAG_TX_SCHED; 3041bb5e3b2fSeh146360 mac_tx_update(ic->ic_mach); 3042bb5e3b2fSeh146360 } 3043924f3e72Seh146360 mutex_exit(&sc->sc_resched_lock); 3044924f3e72Seh146360 } 3045bb5e3b2fSeh146360 3046922d2c76Seh146360 enable_interrupt: 3047bb5e3b2fSeh146360 /* 3048bb5e3b2fSeh146360 * enable all interrupts 3049bb5e3b2fSeh146360 */ 3050bb5e3b2fSeh146360 ipw2200_csr_put32(sc, IPW2200_CSR_INTR_MASK, IPW2200_INTR_MASK_ALL); 3051bb5e3b2fSeh146360 3052bb5e3b2fSeh146360 return (DDI_INTR_CLAIMED); 3053bb5e3b2fSeh146360 } 3054bb5e3b2fSeh146360 3055bb5e3b2fSeh146360 3056bb5e3b2fSeh146360 /* 3057bb5e3b2fSeh146360 * Module Loading Data & Entry Points 3058bb5e3b2fSeh146360 */ 3059bb5e3b2fSeh146360 DDI_DEFINE_STREAM_OPS(ipw2200_devops, nulldev, nulldev, ipw2200_attach, 306014d91224Sfei feng - Sun Microsystems - Beijing China ipw2200_detach, nodev, NULL, D_MP, NULL, ipw2200_quiesce); 3061bb5e3b2fSeh146360 3062bb5e3b2fSeh146360 static struct modldrv ipw2200_modldrv = { 3063bb5e3b2fSeh146360 &mod_driverops, 3064bb5e3b2fSeh146360 ipw2200_ident, 3065bb5e3b2fSeh146360 &ipw2200_devops 3066bb5e3b2fSeh146360 }; 3067bb5e3b2fSeh146360 3068bb5e3b2fSeh146360 static struct modlinkage ipw2200_modlinkage = { 3069bb5e3b2fSeh146360 MODREV_1, 3070bb5e3b2fSeh146360 &ipw2200_modldrv, 3071bb5e3b2fSeh146360 NULL 3072bb5e3b2fSeh146360 }; 3073bb5e3b2fSeh146360 3074bb5e3b2fSeh146360 int 3075bb5e3b2fSeh146360 _init(void) 3076bb5e3b2fSeh146360 { 3077bb5e3b2fSeh146360 int status; 3078bb5e3b2fSeh146360 3079bb5e3b2fSeh146360 status = ddi_soft_state_init(&ipw2200_ssp, 3080bb5e3b2fSeh146360 sizeof (struct ipw2200_softc), 1); 3081bb5e3b2fSeh146360 if (status != DDI_SUCCESS) 3082bb5e3b2fSeh146360 return (status); 3083bb5e3b2fSeh146360 3084bb5e3b2fSeh146360 mac_init_ops(&ipw2200_devops, IPW2200_DRV_NAME); 3085bb5e3b2fSeh146360 status = mod_install(&ipw2200_modlinkage); 3086bb5e3b2fSeh146360 if (status != DDI_SUCCESS) { 3087bb5e3b2fSeh146360 mac_fini_ops(&ipw2200_devops); 3088bb5e3b2fSeh146360 ddi_soft_state_fini(&ipw2200_ssp); 3089bb5e3b2fSeh146360 } 3090bb5e3b2fSeh146360 3091bb5e3b2fSeh146360 return (status); 3092bb5e3b2fSeh146360 } 3093bb5e3b2fSeh146360 3094bb5e3b2fSeh146360 int 3095bb5e3b2fSeh146360 _fini(void) 3096bb5e3b2fSeh146360 { 3097bb5e3b2fSeh146360 int status; 3098bb5e3b2fSeh146360 3099bb5e3b2fSeh146360 status = mod_remove(&ipw2200_modlinkage); 3100bb5e3b2fSeh146360 if (status == DDI_SUCCESS) { 3101bb5e3b2fSeh146360 mac_fini_ops(&ipw2200_devops); 3102bb5e3b2fSeh146360 ddi_soft_state_fini(&ipw2200_ssp); 3103bb5e3b2fSeh146360 } 3104bb5e3b2fSeh146360 3105bb5e3b2fSeh146360 return (status); 3106bb5e3b2fSeh146360 } 3107bb5e3b2fSeh146360 3108bb5e3b2fSeh146360 int 3109bb5e3b2fSeh146360 _info(struct modinfo *modinfop) 3110bb5e3b2fSeh146360 { 3111bb5e3b2fSeh146360 return (mod_info(&ipw2200_modlinkage, modinfop)); 3112bb5e3b2fSeh146360 } 3113