1bb5e3b2fSeh146360 /* 20dc2366fSVenugopal Iyer * Copyright 2010 Sun Microsystems, Inc. All rights reserved. 3bb5e3b2fSeh146360 * Use is subject to license terms. 4bb5e3b2fSeh146360 */ 5bb5e3b2fSeh146360 6bb5e3b2fSeh146360 /* 7bb5e3b2fSeh146360 * Copyright(c) 2004 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> 52e7801d59Ssowmini #include <net/if.h> 53bb5e3b2fSeh146360 #include <sys/mac_wifi.h> 54bb5e3b2fSeh146360 #include <sys/varargs.h> 55bb5e3b2fSeh146360 #include <sys/policy.h> 56bb5e3b2fSeh146360 57bb5e3b2fSeh146360 #include "ipw2100.h" 58bb5e3b2fSeh146360 #include "ipw2100_impl.h" 59bb5e3b2fSeh146360 #include <inet/wifi_ioctl.h> 60bb5e3b2fSeh146360 61bb5e3b2fSeh146360 /* 62bb5e3b2fSeh146360 * kCF framework include files 63bb5e3b2fSeh146360 */ 64bb5e3b2fSeh146360 #include <sys/crypto/common.h> 65bb5e3b2fSeh146360 #include <sys/crypto/api.h> 66bb5e3b2fSeh146360 67bb5e3b2fSeh146360 static void *ipw2100_ssp = NULL; 6819397407SSherry Moore static char ipw2100_ident[] = IPW2100_DRV_DESC; 69bb5e3b2fSeh146360 70bb5e3b2fSeh146360 /* 71bb5e3b2fSeh146360 * PIO access attribute for register 72bb5e3b2fSeh146360 */ 73bb5e3b2fSeh146360 static ddi_device_acc_attr_t ipw2100_csr_accattr = { 74bb5e3b2fSeh146360 DDI_DEVICE_ATTR_V0, 75bb5e3b2fSeh146360 DDI_STRUCTURE_LE_ACC, 76bb5e3b2fSeh146360 DDI_STRICTORDER_ACC 77bb5e3b2fSeh146360 }; 78bb5e3b2fSeh146360 79bb5e3b2fSeh146360 static ddi_device_acc_attr_t ipw2100_dma_accattr = { 80bb5e3b2fSeh146360 DDI_DEVICE_ATTR_V0, 81bb5e3b2fSeh146360 DDI_NEVERSWAP_ACC, 82bb5e3b2fSeh146360 DDI_STRICTORDER_ACC 83bb5e3b2fSeh146360 }; 84bb5e3b2fSeh146360 85bb5e3b2fSeh146360 static ddi_dma_attr_t ipw2100_dma_attr = { 86bb5e3b2fSeh146360 DMA_ATTR_V0, 87bb5e3b2fSeh146360 0x0000000000000000ULL, 88bb5e3b2fSeh146360 0x00000000ffffffffULL, 89bb5e3b2fSeh146360 0x00000000ffffffffULL, 90bb5e3b2fSeh146360 0x0000000000000004ULL, 91bb5e3b2fSeh146360 0xfff, 92bb5e3b2fSeh146360 1, 93bb5e3b2fSeh146360 0x00000000ffffffffULL, 94bb5e3b2fSeh146360 0x00000000ffffffffULL, 95bb5e3b2fSeh146360 1, 96bb5e3b2fSeh146360 1, 97bb5e3b2fSeh146360 0 98bb5e3b2fSeh146360 }; 99bb5e3b2fSeh146360 100bb5e3b2fSeh146360 static const struct ieee80211_rateset ipw2100_rateset_11b = { 4, 101bb5e3b2fSeh146360 {2, 4, 11, 22} 102bb5e3b2fSeh146360 }; 103bb5e3b2fSeh146360 104bb5e3b2fSeh146360 /* 105bb5e3b2fSeh146360 * For mfthread only 106bb5e3b2fSeh146360 */ 107bb5e3b2fSeh146360 extern pri_t minclsyspri; 108bb5e3b2fSeh146360 109bb5e3b2fSeh146360 /* 110bb5e3b2fSeh146360 * ipw2100 specific hardware operations 111bb5e3b2fSeh146360 */ 112bb5e3b2fSeh146360 static void ipw2100_hwconf_get(struct ipw2100_softc *sc); 113bb5e3b2fSeh146360 static int ipw2100_chip_reset(struct ipw2100_softc *sc); 114bb5e3b2fSeh146360 static void ipw2100_master_stop(struct ipw2100_softc *sc); 115bb5e3b2fSeh146360 static void ipw2100_stop(struct ipw2100_softc *sc); 116bb5e3b2fSeh146360 static int ipw2100_config(struct ipw2100_softc *sc); 117bb5e3b2fSeh146360 static int ipw2100_cmd(struct ipw2100_softc *sc, uint32_t type, 118bb5e3b2fSeh146360 void *buf, size_t len); 119bb5e3b2fSeh146360 static int ipw2100_dma_region_alloc(struct ipw2100_softc *sc, 120bb5e3b2fSeh146360 struct dma_region *dr, size_t size, uint_t dir, uint_t flags); 121bb5e3b2fSeh146360 static void ipw2100_dma_region_free(struct dma_region *dr); 122bb5e3b2fSeh146360 static void ipw2100_tables_init(struct ipw2100_softc *sc); 123bb5e3b2fSeh146360 static void ipw2100_ring_hwsetup(struct ipw2100_softc *sc); 124bb5e3b2fSeh146360 static int ipw2100_ring_alloc(struct ipw2100_softc *sc); 125bb5e3b2fSeh146360 static void ipw2100_ring_free(struct ipw2100_softc *sc); 126bb5e3b2fSeh146360 static void ipw2100_ring_reset(struct ipw2100_softc *sc); 127bb5e3b2fSeh146360 static int ipw2100_ring_init(struct ipw2100_softc *sc); 128bb5e3b2fSeh146360 129bb5e3b2fSeh146360 /* 130bb5e3b2fSeh146360 * GLD specific operations 131bb5e3b2fSeh146360 */ 132bb5e3b2fSeh146360 static int ipw2100_m_stat(void *arg, uint_t stat, uint64_t *val); 133bb5e3b2fSeh146360 static int ipw2100_m_start(void *arg); 134bb5e3b2fSeh146360 static void ipw2100_m_stop(void *arg); 135bb5e3b2fSeh146360 static int ipw2100_m_unicst(void *arg, const uint8_t *macaddr); 136bb5e3b2fSeh146360 static int ipw2100_m_multicst(void *arg, boolean_t add, const uint8_t *m); 137bb5e3b2fSeh146360 static int ipw2100_m_promisc(void *arg, boolean_t on); 138bb5e3b2fSeh146360 static mblk_t *ipw2100_m_tx(void *arg, mblk_t *mp); 139bb5e3b2fSeh146360 static void ipw2100_m_ioctl(void *arg, queue_t *wq, mblk_t *mp); 1407efa17f5Sfei feng - Sun Microsystems - Beijing China static int ipw2100_m_setprop(void *arg, const char *pr_name, 1417efa17f5Sfei feng - Sun Microsystems - Beijing China mac_prop_id_t wldp_pr_num, uint_t wldp_length, const void *wldp_buf); 1427efa17f5Sfei feng - Sun Microsystems - Beijing China static int ipw2100_m_getprop(void *arg, const char *pr_name, 1430dc2366fSVenugopal Iyer mac_prop_id_t wldp_pr_num, uint_t wldp_length, void *wldp_buf); 1440dc2366fSVenugopal Iyer static void ipw2100_m_propinfo(void *, const char *, mac_prop_id_t, 1450dc2366fSVenugopal Iyer mac_prop_info_handle_t); 146bb5e3b2fSeh146360 147bb5e3b2fSeh146360 /* 148bb5e3b2fSeh146360 * Interrupt and Data transferring operations 149bb5e3b2fSeh146360 */ 150bb5e3b2fSeh146360 static uint_t ipw2100_intr(caddr_t arg); 151bb5e3b2fSeh146360 static int ipw2100_send(struct ieee80211com *ic, mblk_t *mp, uint8_t type); 152bb5e3b2fSeh146360 static void ipw2100_rcvpkt(struct ipw2100_softc *sc, 153bb5e3b2fSeh146360 struct ipw2100_status *status, uint8_t *rxbuf); 154bb5e3b2fSeh146360 155bb5e3b2fSeh146360 /* 156bb5e3b2fSeh146360 * WiFi specific operations 157bb5e3b2fSeh146360 */ 158bb5e3b2fSeh146360 static int ipw2100_newstate(struct ieee80211com *ic, 159bb5e3b2fSeh146360 enum ieee80211_state state, int arg); 160bb5e3b2fSeh146360 static void ipw2100_thread(struct ipw2100_softc *sc); 161bb5e3b2fSeh146360 162bb5e3b2fSeh146360 /* 163bb5e3b2fSeh146360 * IOCTL Handler 164bb5e3b2fSeh146360 */ 165bb5e3b2fSeh146360 static int ipw2100_ioctl(struct ipw2100_softc *sc, queue_t *q, mblk_t *m); 166bb5e3b2fSeh146360 static int ipw2100_getset(struct ipw2100_softc *sc, 167bb5e3b2fSeh146360 mblk_t *m, uint32_t cmd, boolean_t *need_net80211); 168bb5e3b2fSeh146360 static int ipw_wificfg_radio(struct ipw2100_softc *sc, 169bb5e3b2fSeh146360 uint32_t cmd, wldp_t *outfp); 170bb5e3b2fSeh146360 static int ipw_wificfg_desrates(wldp_t *outfp); 171bb5e3b2fSeh146360 static int ipw_wificfg_disassoc(struct ipw2100_softc *sc, 172bb5e3b2fSeh146360 wldp_t *outfp); 173bb5e3b2fSeh146360 174bb5e3b2fSeh146360 /* 1750f1b305eSSeth Goldberg * Suspend / Resume operations 1760f1b305eSSeth Goldberg */ 1770f1b305eSSeth Goldberg static int ipw2100_cpr_suspend(struct ipw2100_softc *sc); 1780f1b305eSSeth Goldberg static int ipw2100_cpr_resume(struct ipw2100_softc *sc); 1790f1b305eSSeth Goldberg 1800f1b305eSSeth Goldberg /* 181bb5e3b2fSeh146360 * Mac Call Back entries 182bb5e3b2fSeh146360 */ 183bb5e3b2fSeh146360 mac_callbacks_t ipw2100_m_callbacks = { 1840dc2366fSVenugopal Iyer MC_IOCTL | MC_SETPROP | MC_GETPROP | MC_PROPINFO, 185bb5e3b2fSeh146360 ipw2100_m_stat, 186bb5e3b2fSeh146360 ipw2100_m_start, 187bb5e3b2fSeh146360 ipw2100_m_stop, 188bb5e3b2fSeh146360 ipw2100_m_promisc, 189bb5e3b2fSeh146360 ipw2100_m_multicst, 190bb5e3b2fSeh146360 ipw2100_m_unicst, 191bb5e3b2fSeh146360 ipw2100_m_tx, 1920dc2366fSVenugopal Iyer NULL, 1937efa17f5Sfei feng - Sun Microsystems - Beijing China ipw2100_m_ioctl, 1947efa17f5Sfei feng - Sun Microsystems - Beijing China NULL, 1957efa17f5Sfei feng - Sun Microsystems - Beijing China NULL, 1967efa17f5Sfei feng - Sun Microsystems - Beijing China NULL, 1977efa17f5Sfei feng - Sun Microsystems - Beijing China ipw2100_m_setprop, 1980dc2366fSVenugopal Iyer ipw2100_m_getprop, 1990dc2366fSVenugopal Iyer ipw2100_m_propinfo 200bb5e3b2fSeh146360 }; 201bb5e3b2fSeh146360 202bb5e3b2fSeh146360 203bb5e3b2fSeh146360 /* 204bb5e3b2fSeh146360 * DEBUG Facility 205bb5e3b2fSeh146360 */ 206bb5e3b2fSeh146360 #define MAX_MSG (128) 207bb5e3b2fSeh146360 uint32_t ipw2100_debug = 0; 208bb5e3b2fSeh146360 /* 209bb5e3b2fSeh146360 * supported debug marsks: 210bb5e3b2fSeh146360 * | IPW2100_DBG_INIT 211bb5e3b2fSeh146360 * | IPW2100_DBG_GLD 212bb5e3b2fSeh146360 * | IPW2100_DBG_TABLE 213bb5e3b2fSeh146360 * | IPW2100_DBG_SOFTINT 214bb5e3b2fSeh146360 * | IPW2100_DBG_CSR 215bb5e3b2fSeh146360 * | IPW2100_DBG_INT 216bb5e3b2fSeh146360 * | IPW2100_DBG_FW 217bb5e3b2fSeh146360 * | IPW2100_DBG_IOCTL 218bb5e3b2fSeh146360 * | IPW2100_DBG_HWCAP 219bb5e3b2fSeh146360 * | IPW2100_DBG_STATISTIC 220bb5e3b2fSeh146360 * | IPW2100_DBG_RING 221bb5e3b2fSeh146360 * | IPW2100_DBG_WIFI 2227efa17f5Sfei feng - Sun Microsystems - Beijing China * | IPW2100_DBG_BRUSSELS 223bb5e3b2fSeh146360 */ 224bb5e3b2fSeh146360 225bb5e3b2fSeh146360 /* 226bb5e3b2fSeh146360 * global tuning parameters to work around unknown hardware issues 227bb5e3b2fSeh146360 */ 228bb5e3b2fSeh146360 static uint32_t delay_config_stable = 100000; /* 100ms */ 229bb5e3b2fSeh146360 static uint32_t delay_fatal_recover = 100000 * 20; /* 2s */ 230bb5e3b2fSeh146360 static uint32_t delay_aux_thread = 100000; /* 100ms */ 231bb5e3b2fSeh146360 232bb5e3b2fSeh146360 void 233bb5e3b2fSeh146360 ipw2100_dbg(dev_info_t *dip, int level, const char *fmt, ...) 234bb5e3b2fSeh146360 { 235bb5e3b2fSeh146360 va_list ap; 236bb5e3b2fSeh146360 char buf[MAX_MSG]; 237bb5e3b2fSeh146360 int instance; 238bb5e3b2fSeh146360 239bb5e3b2fSeh146360 va_start(ap, fmt); 240bb5e3b2fSeh146360 (void) vsnprintf(buf, sizeof (buf), fmt, ap); 241bb5e3b2fSeh146360 va_end(ap); 242bb5e3b2fSeh146360 243bb5e3b2fSeh146360 if (dip) { 244bb5e3b2fSeh146360 instance = ddi_get_instance(dip); 245bb5e3b2fSeh146360 cmn_err(level, "%s%d: %s", IPW2100_DRV_NAME, instance, buf); 246bb5e3b2fSeh146360 } else 247bb5e3b2fSeh146360 cmn_err(level, "%s: %s", IPW2100_DRV_NAME, buf); 248bb5e3b2fSeh146360 } 249bb5e3b2fSeh146360 250bb5e3b2fSeh146360 /* 251bb5e3b2fSeh146360 * device operations 252bb5e3b2fSeh146360 */ 253bb5e3b2fSeh146360 int 254bb5e3b2fSeh146360 ipw2100_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 255bb5e3b2fSeh146360 { 256bb5e3b2fSeh146360 struct ipw2100_softc *sc; 257bb5e3b2fSeh146360 ddi_acc_handle_t cfgh; 258bb5e3b2fSeh146360 caddr_t regs; 259bb5e3b2fSeh146360 struct ieee80211com *ic; 260bb5e3b2fSeh146360 int instance, err, i; 261bb5e3b2fSeh146360 char strbuf[32]; 262bb5e3b2fSeh146360 wifi_data_t wd = { 0 }; 263bb5e3b2fSeh146360 mac_register_t *macp; 264bb5e3b2fSeh146360 2650f1b305eSSeth Goldberg switch (cmd) { 2660f1b305eSSeth Goldberg case DDI_ATTACH: 2670f1b305eSSeth Goldberg break; 2680f1b305eSSeth Goldberg case DDI_RESUME: 2690f1b305eSSeth Goldberg sc = ddi_get_soft_state(ipw2100_ssp, ddi_get_instance(dip)); 2700f1b305eSSeth Goldberg if (sc == NULL) { 2710f1b305eSSeth Goldberg err = DDI_FAILURE; 2720f1b305eSSeth Goldberg goto fail1; 2730f1b305eSSeth Goldberg } 2740f1b305eSSeth Goldberg return (ipw2100_cpr_resume(sc)); 2750f1b305eSSeth Goldberg default: 276bb5e3b2fSeh146360 err = DDI_FAILURE; 277bb5e3b2fSeh146360 goto fail1; 278bb5e3b2fSeh146360 } 279bb5e3b2fSeh146360 280bb5e3b2fSeh146360 instance = ddi_get_instance(dip); 281bb5e3b2fSeh146360 err = ddi_soft_state_zalloc(ipw2100_ssp, instance); 282bb5e3b2fSeh146360 if (err != DDI_SUCCESS) { 283bb5e3b2fSeh146360 IPW2100_WARN((dip, CE_WARN, 284bb5e3b2fSeh146360 "ipw2100_attach(): unable to allocate soft state\n")); 285bb5e3b2fSeh146360 goto fail1; 286bb5e3b2fSeh146360 } 287bb5e3b2fSeh146360 sc = ddi_get_soft_state(ipw2100_ssp, instance); 288bb5e3b2fSeh146360 sc->sc_dip = dip; 289bb5e3b2fSeh146360 290bb5e3b2fSeh146360 /* 291bb5e3b2fSeh146360 * Map config spaces register 292bb5e3b2fSeh146360 */ 293bb5e3b2fSeh146360 err = ddi_regs_map_setup(dip, IPW2100_PCI_CFG_RNUM, ®s, 294bb5e3b2fSeh146360 0, 0, &ipw2100_csr_accattr, &cfgh); 295bb5e3b2fSeh146360 if (err != DDI_SUCCESS) { 296bb5e3b2fSeh146360 IPW2100_WARN((dip, CE_WARN, 297bb5e3b2fSeh146360 "ipw2100_attach(): unable to map spaces regs\n")); 298bb5e3b2fSeh146360 goto fail2; 299bb5e3b2fSeh146360 } 300bb5e3b2fSeh146360 ddi_put8(cfgh, (uint8_t *)(regs + 0x41), 0); 301bb5e3b2fSeh146360 ddi_regs_map_free(&cfgh); 302bb5e3b2fSeh146360 303bb5e3b2fSeh146360 /* 304bb5e3b2fSeh146360 * Map operating registers 305bb5e3b2fSeh146360 */ 306bb5e3b2fSeh146360 err = ddi_regs_map_setup(dip, IPW2100_PCI_CSR_RNUM, &sc->sc_regs, 307bb5e3b2fSeh146360 0, 0, &ipw2100_csr_accattr, &sc->sc_ioh); 308bb5e3b2fSeh146360 if (err != DDI_SUCCESS) { 309bb5e3b2fSeh146360 IPW2100_WARN((dip, CE_WARN, 310bb5e3b2fSeh146360 "ipw2100_attach(): unable to map device regs\n")); 311bb5e3b2fSeh146360 goto fail2; 312bb5e3b2fSeh146360 } 313bb5e3b2fSeh146360 314bb5e3b2fSeh146360 /* 315bb5e3b2fSeh146360 * Reset the chip 316bb5e3b2fSeh146360 */ 317bb5e3b2fSeh146360 err = ipw2100_chip_reset(sc); 318bb5e3b2fSeh146360 if (err != DDI_SUCCESS) { 319bb5e3b2fSeh146360 IPW2100_WARN((dip, CE_WARN, 320bb5e3b2fSeh146360 "ipw2100_attach(): reset failed\n")); 321bb5e3b2fSeh146360 goto fail3; 322bb5e3b2fSeh146360 } 323bb5e3b2fSeh146360 324bb5e3b2fSeh146360 /* 325bb5e3b2fSeh146360 * Get the hw conf, including MAC address, then init all rings. 326bb5e3b2fSeh146360 */ 327bb5e3b2fSeh146360 ipw2100_hwconf_get(sc); 328bb5e3b2fSeh146360 err = ipw2100_ring_init(sc); 329bb5e3b2fSeh146360 if (err != DDI_SUCCESS) { 330bb5e3b2fSeh146360 IPW2100_WARN((dip, CE_WARN, 331bb5e3b2fSeh146360 "ipw2100_attach(): " 332bb5e3b2fSeh146360 "unable to allocate and initialize rings\n")); 333bb5e3b2fSeh146360 goto fail3; 334bb5e3b2fSeh146360 } 335bb5e3b2fSeh146360 336bb5e3b2fSeh146360 /* 337bb5e3b2fSeh146360 * Initialize mutexs and condvars 338bb5e3b2fSeh146360 */ 339bb5e3b2fSeh146360 err = ddi_get_iblock_cookie(dip, 0, &sc->sc_iblk); 340bb5e3b2fSeh146360 if (err != DDI_SUCCESS) { 341bb5e3b2fSeh146360 IPW2100_WARN((dip, CE_WARN, 342bb5e3b2fSeh146360 "ipw2100_attach(): ddi_get_iblock_cookie() failed\n")); 343bb5e3b2fSeh146360 goto fail4; 344bb5e3b2fSeh146360 } 345bb5e3b2fSeh146360 /* 346bb5e3b2fSeh146360 * interrupt lock 347bb5e3b2fSeh146360 */ 348bb5e3b2fSeh146360 mutex_init(&sc->sc_ilock, "interrupt-lock", MUTEX_DRIVER, 349bb5e3b2fSeh146360 (void *) sc->sc_iblk); 350bb5e3b2fSeh146360 cv_init(&sc->sc_fw_cond, "firmware", CV_DRIVER, NULL); 351bb5e3b2fSeh146360 cv_init(&sc->sc_cmd_cond, "command", CV_DRIVER, NULL); 352bb5e3b2fSeh146360 /* 353bb5e3b2fSeh146360 * tx ring lock 354bb5e3b2fSeh146360 */ 355bb5e3b2fSeh146360 mutex_init(&sc->sc_tx_lock, "tx-ring", MUTEX_DRIVER, 356bb5e3b2fSeh146360 (void *) sc->sc_iblk); 357bb5e3b2fSeh146360 cv_init(&sc->sc_tx_cond, "tx-ring", CV_DRIVER, NULL); 358bb5e3b2fSeh146360 /* 359bb5e3b2fSeh146360 * rescheuled lock 360bb5e3b2fSeh146360 */ 361bb5e3b2fSeh146360 mutex_init(&sc->sc_resched_lock, "reschedule-lock", MUTEX_DRIVER, 362bb5e3b2fSeh146360 (void *) sc->sc_iblk); 363bb5e3b2fSeh146360 /* 364bb5e3b2fSeh146360 * initialize the mfthread 365bb5e3b2fSeh146360 */ 366bb5e3b2fSeh146360 mutex_init(&sc->sc_mflock, "function-lock", MUTEX_DRIVER, 367bb5e3b2fSeh146360 (void *) sc->sc_iblk); 368bb5e3b2fSeh146360 cv_init(&sc->sc_mfthread_cv, NULL, CV_DRIVER, NULL); 369bb5e3b2fSeh146360 sc->sc_mf_thread = NULL; 370bb5e3b2fSeh146360 sc->sc_mfthread_switch = 0; 371bb5e3b2fSeh146360 /* 372bb5e3b2fSeh146360 * Initialize the wifi part, which will be used by 373bb5e3b2fSeh146360 * generic layer 374bb5e3b2fSeh146360 */ 375bb5e3b2fSeh146360 ic = &sc->sc_ic; 376bb5e3b2fSeh146360 ic->ic_phytype = IEEE80211_T_DS; 377bb5e3b2fSeh146360 ic->ic_opmode = IEEE80211_M_STA; 378bb5e3b2fSeh146360 ic->ic_state = IEEE80211_S_INIT; 379bb5e3b2fSeh146360 ic->ic_maxrssi = 49; 380bb5e3b2fSeh146360 /* 381bb5e3b2fSeh146360 * Future, could use s/w to handle encryption: IEEE80211_C_WEP 382bb5e3b2fSeh146360 * and need to add support for IEEE80211_C_IBSS 383bb5e3b2fSeh146360 */ 384bb5e3b2fSeh146360 ic->ic_caps = IEEE80211_C_SHPREAMBLE | IEEE80211_C_TXPMGT | 385bb5e3b2fSeh146360 IEEE80211_C_PMGT; 386bb5e3b2fSeh146360 ic->ic_sup_rates[IEEE80211_MODE_11B] = ipw2100_rateset_11b; 387bb5e3b2fSeh146360 IEEE80211_ADDR_COPY(ic->ic_macaddr, sc->sc_macaddr); 388bb5e3b2fSeh146360 for (i = 1; i < 16; i++) { 389bb5e3b2fSeh146360 if (sc->sc_chmask &(1 << i)) { 390bb5e3b2fSeh146360 /* IEEE80211_CHAN_B */ 391bb5e3b2fSeh146360 ic->ic_sup_channels[i].ich_freq = ieee80211_ieee2mhz(i, 392bb5e3b2fSeh146360 IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_CCK); 393bb5e3b2fSeh146360 ic->ic_sup_channels[i].ich_flags = 394bb5e3b2fSeh146360 IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_CCK; 395bb5e3b2fSeh146360 } 396bb5e3b2fSeh146360 } 397bb5e3b2fSeh146360 ic->ic_ibss_chan = &ic->ic_sup_channels[0]; 398bb5e3b2fSeh146360 ic->ic_xmit = ipw2100_send; 399bb5e3b2fSeh146360 /* 400bb5e3b2fSeh146360 * init Wifi layer 401bb5e3b2fSeh146360 */ 402bb5e3b2fSeh146360 ieee80211_attach(ic); 403bb5e3b2fSeh146360 404bb5e3b2fSeh146360 /* 405bb5e3b2fSeh146360 * Override 80211 default routines 406bb5e3b2fSeh146360 */ 407bb5e3b2fSeh146360 ieee80211_media_init(ic); 408bb5e3b2fSeh146360 sc->sc_newstate = ic->ic_newstate; 409bb5e3b2fSeh146360 ic->ic_newstate = ipw2100_newstate; 410bb5e3b2fSeh146360 /* 411bb5e3b2fSeh146360 * initialize default tx key 412bb5e3b2fSeh146360 */ 413bb5e3b2fSeh146360 ic->ic_def_txkey = 0; 414bb5e3b2fSeh146360 /* 415bb5e3b2fSeh146360 * Set the Authentication to AUTH_Open only. 416bb5e3b2fSeh146360 */ 417bb5e3b2fSeh146360 sc->sc_authmode = IEEE80211_AUTH_OPEN; 418bb5e3b2fSeh146360 419bb5e3b2fSeh146360 /* 420bb5e3b2fSeh146360 * Add the interrupt handler 421bb5e3b2fSeh146360 */ 422bb5e3b2fSeh146360 err = ddi_add_intr(dip, 0, &sc->sc_iblk, NULL, 423bb5e3b2fSeh146360 ipw2100_intr, (caddr_t)sc); 424bb5e3b2fSeh146360 if (err != DDI_SUCCESS) { 425bb5e3b2fSeh146360 IPW2100_WARN((dip, CE_WARN, 426bb5e3b2fSeh146360 "ipw2100_attach(): ddi_add_intr() failed\n")); 427bb5e3b2fSeh146360 goto fail5; 428bb5e3b2fSeh146360 } 429bb5e3b2fSeh146360 430bb5e3b2fSeh146360 /* 431bb5e3b2fSeh146360 * Initialize pointer to device specific functions 432bb5e3b2fSeh146360 */ 433bb5e3b2fSeh146360 wd.wd_secalloc = WIFI_SEC_NONE; 434bb5e3b2fSeh146360 wd.wd_opmode = ic->ic_opmode; 435922d2c76Seh146360 IEEE80211_ADDR_COPY(wd.wd_bssid, ic->ic_bss->in_bssid); 436bb5e3b2fSeh146360 437bb5e3b2fSeh146360 macp = mac_alloc(MAC_VERSION); 438bb5e3b2fSeh146360 if (err != 0) { 439bb5e3b2fSeh146360 IPW2100_WARN((dip, CE_WARN, 440bb5e3b2fSeh146360 "ipw2100_attach(): mac_alloc() failed\n")); 441bb5e3b2fSeh146360 goto fail6; 442bb5e3b2fSeh146360 } 443bb5e3b2fSeh146360 444bb5e3b2fSeh146360 macp->m_type_ident = MAC_PLUGIN_IDENT_WIFI; 445bb5e3b2fSeh146360 macp->m_driver = sc; 446bb5e3b2fSeh146360 macp->m_dip = dip; 447bb5e3b2fSeh146360 macp->m_src_addr = ic->ic_macaddr; 448bb5e3b2fSeh146360 macp->m_callbacks = &ipw2100_m_callbacks; 449bb5e3b2fSeh146360 macp->m_min_sdu = 0; 450bb5e3b2fSeh146360 macp->m_max_sdu = IEEE80211_MTU; 451bb5e3b2fSeh146360 macp->m_pdata = &wd; 452bb5e3b2fSeh146360 macp->m_pdata_size = sizeof (wd); 453bb5e3b2fSeh146360 454bb5e3b2fSeh146360 /* 455bb5e3b2fSeh146360 * Register the macp to mac 456bb5e3b2fSeh146360 */ 457bb5e3b2fSeh146360 err = mac_register(macp, &ic->ic_mach); 458bb5e3b2fSeh146360 mac_free(macp); 459bb5e3b2fSeh146360 if (err != DDI_SUCCESS) { 460bb5e3b2fSeh146360 IPW2100_WARN((dip, CE_WARN, 461bb5e3b2fSeh146360 "ipw2100_attach(): mac_register() failed\n")); 462bb5e3b2fSeh146360 goto fail6; 463bb5e3b2fSeh146360 } 464bb5e3b2fSeh146360 465bb5e3b2fSeh146360 /* 466bb5e3b2fSeh146360 * Create minor node of type DDI_NT_NET_WIFI 467bb5e3b2fSeh146360 */ 468bb5e3b2fSeh146360 (void) snprintf(strbuf, sizeof (strbuf), "%s%d", 469bb5e3b2fSeh146360 IPW2100_DRV_NAME, instance); 470bb5e3b2fSeh146360 err = ddi_create_minor_node(dip, strbuf, S_IFCHR, 471bb5e3b2fSeh146360 instance + 1, DDI_NT_NET_WIFI, 0); 472bb5e3b2fSeh146360 if (err != DDI_SUCCESS) 473bb5e3b2fSeh146360 IPW2100_WARN((dip, CE_WARN, 474bb5e3b2fSeh146360 "ipw2100_attach(): ddi_create_minor_node() failed\n")); 475bb5e3b2fSeh146360 476bb5e3b2fSeh146360 /* 477bb5e3b2fSeh146360 * Cache firmware, always return true 478bb5e3b2fSeh146360 */ 479bb5e3b2fSeh146360 (void) ipw2100_cache_firmware(sc); 480bb5e3b2fSeh146360 481bb5e3b2fSeh146360 /* 482bb5e3b2fSeh146360 * Notify link is down now 483bb5e3b2fSeh146360 */ 484bb5e3b2fSeh146360 mac_link_update(ic->ic_mach, LINK_STATE_DOWN); 485bb5e3b2fSeh146360 486bb5e3b2fSeh146360 /* 487bb5e3b2fSeh146360 * create the mf thread to handle the link status, 488bb5e3b2fSeh146360 * recovery fatal error, etc. 489bb5e3b2fSeh146360 */ 490bb5e3b2fSeh146360 sc->sc_mfthread_switch = 1; 491bb5e3b2fSeh146360 if (sc->sc_mf_thread == NULL) 492bb5e3b2fSeh146360 sc->sc_mf_thread = thread_create((caddr_t)NULL, 0, 493bb5e3b2fSeh146360 ipw2100_thread, sc, 0, &p0, TS_RUN, minclsyspri); 494bb5e3b2fSeh146360 495bb5e3b2fSeh146360 return (DDI_SUCCESS); 496bb5e3b2fSeh146360 497bb5e3b2fSeh146360 fail6: 498bb5e3b2fSeh146360 ddi_remove_intr(dip, 0, sc->sc_iblk); 499bb5e3b2fSeh146360 fail5: 500bb5e3b2fSeh146360 ieee80211_detach(ic); 501bb5e3b2fSeh146360 502bb5e3b2fSeh146360 mutex_destroy(&sc->sc_ilock); 503bb5e3b2fSeh146360 mutex_destroy(&sc->sc_tx_lock); 504bb5e3b2fSeh146360 mutex_destroy(&sc->sc_mflock); 505bb5e3b2fSeh146360 mutex_destroy(&sc->sc_resched_lock); 506bb5e3b2fSeh146360 cv_destroy(&sc->sc_mfthread_cv); 507bb5e3b2fSeh146360 cv_destroy(&sc->sc_tx_cond); 508bb5e3b2fSeh146360 cv_destroy(&sc->sc_cmd_cond); 509bb5e3b2fSeh146360 cv_destroy(&sc->sc_fw_cond); 510bb5e3b2fSeh146360 fail4: 511bb5e3b2fSeh146360 ipw2100_ring_free(sc); 512bb5e3b2fSeh146360 fail3: 513bb5e3b2fSeh146360 ddi_regs_map_free(&sc->sc_ioh); 514bb5e3b2fSeh146360 fail2: 515bb5e3b2fSeh146360 ddi_soft_state_free(ipw2100_ssp, instance); 516bb5e3b2fSeh146360 fail1: 517bb5e3b2fSeh146360 return (err); 518bb5e3b2fSeh146360 } 519bb5e3b2fSeh146360 520bb5e3b2fSeh146360 int 521bb5e3b2fSeh146360 ipw2100_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 522bb5e3b2fSeh146360 { 523bb5e3b2fSeh146360 struct ipw2100_softc *sc = 524bb5e3b2fSeh146360 ddi_get_soft_state(ipw2100_ssp, ddi_get_instance(dip)); 525bb5e3b2fSeh146360 int err; 526bb5e3b2fSeh146360 527bb5e3b2fSeh146360 ASSERT(sc != NULL); 528bb5e3b2fSeh146360 5290f1b305eSSeth Goldberg switch (cmd) { 5300f1b305eSSeth Goldberg case DDI_DETACH: 5310f1b305eSSeth Goldberg break; 5320f1b305eSSeth Goldberg case DDI_SUSPEND: 5330f1b305eSSeth Goldberg return (ipw2100_cpr_suspend(sc)); 5340f1b305eSSeth Goldberg default: 535bb5e3b2fSeh146360 return (DDI_FAILURE); 5360f1b305eSSeth Goldberg } 537bb5e3b2fSeh146360 538bb5e3b2fSeh146360 /* 539bb5e3b2fSeh146360 * Destroy the mf_thread 540bb5e3b2fSeh146360 */ 541bb5e3b2fSeh146360 mutex_enter(&sc->sc_mflock); 542bb5e3b2fSeh146360 sc->sc_mfthread_switch = 0; 543bb5e3b2fSeh146360 while (sc->sc_mf_thread != NULL) { 544bb5e3b2fSeh146360 if (cv_wait_sig(&sc->sc_mfthread_cv, &sc->sc_mflock) == 0) 545bb5e3b2fSeh146360 break; 546bb5e3b2fSeh146360 } 547bb5e3b2fSeh146360 mutex_exit(&sc->sc_mflock); 548bb5e3b2fSeh146360 549bb5e3b2fSeh146360 /* 5500f1b305eSSeth Goldberg * Unregister from the MAC layer subsystem 551bb5e3b2fSeh146360 */ 552bb5e3b2fSeh146360 err = mac_unregister(sc->sc_ic.ic_mach); 553bb5e3b2fSeh146360 if (err != DDI_SUCCESS) 554bb5e3b2fSeh146360 return (err); 555bb5e3b2fSeh146360 556bb5e3b2fSeh146360 ddi_remove_intr(dip, 0, sc->sc_iblk); 557bb5e3b2fSeh146360 558bb5e3b2fSeh146360 /* 559bb5e3b2fSeh146360 * destroy the cv 560bb5e3b2fSeh146360 */ 561bb5e3b2fSeh146360 mutex_destroy(&sc->sc_ilock); 562bb5e3b2fSeh146360 mutex_destroy(&sc->sc_tx_lock); 563bb5e3b2fSeh146360 mutex_destroy(&sc->sc_mflock); 564bb5e3b2fSeh146360 mutex_destroy(&sc->sc_resched_lock); 565bb5e3b2fSeh146360 cv_destroy(&sc->sc_mfthread_cv); 566bb5e3b2fSeh146360 cv_destroy(&sc->sc_tx_cond); 567bb5e3b2fSeh146360 cv_destroy(&sc->sc_cmd_cond); 568bb5e3b2fSeh146360 cv_destroy(&sc->sc_fw_cond); 569bb5e3b2fSeh146360 570bb5e3b2fSeh146360 /* 571bb5e3b2fSeh146360 * detach ieee80211 572bb5e3b2fSeh146360 */ 573bb5e3b2fSeh146360 ieee80211_detach(&sc->sc_ic); 574bb5e3b2fSeh146360 575bb5e3b2fSeh146360 (void) ipw2100_free_firmware(sc); 576bb5e3b2fSeh146360 ipw2100_ring_free(sc); 577bb5e3b2fSeh146360 578bb5e3b2fSeh146360 ddi_regs_map_free(&sc->sc_ioh); 579bb5e3b2fSeh146360 ddi_remove_minor_node(dip, NULL); 580bb5e3b2fSeh146360 ddi_soft_state_free(ipw2100_ssp, ddi_get_instance(dip)); 581bb5e3b2fSeh146360 582bb5e3b2fSeh146360 return (DDI_SUCCESS); 583bb5e3b2fSeh146360 } 584bb5e3b2fSeh146360 5850f1b305eSSeth Goldberg int 5860f1b305eSSeth Goldberg ipw2100_cpr_suspend(struct ipw2100_softc *sc) 5870f1b305eSSeth Goldberg { 5880f1b305eSSeth Goldberg IPW2100_DBG(IPW2100_DBG_INIT, (sc->sc_dip, CE_CONT, 5890f1b305eSSeth Goldberg "ipw2100_cpr_suspend(): enter\n")); 5900f1b305eSSeth Goldberg 5910f1b305eSSeth Goldberg /* 5920f1b305eSSeth Goldberg * Destroy the mf_thread 5930f1b305eSSeth Goldberg */ 5940f1b305eSSeth Goldberg mutex_enter(&sc->sc_mflock); 5950f1b305eSSeth Goldberg sc->sc_mfthread_switch = 0; 5960f1b305eSSeth Goldberg while (sc->sc_mf_thread != NULL) { 5970f1b305eSSeth Goldberg if (cv_wait_sig(&sc->sc_mfthread_cv, &sc->sc_mflock) == 0) 5980f1b305eSSeth Goldberg break; 5990f1b305eSSeth Goldberg } 6000f1b305eSSeth Goldberg mutex_exit(&sc->sc_mflock); 6010f1b305eSSeth Goldberg 6020f1b305eSSeth Goldberg /* 6030f1b305eSSeth Goldberg * stop the hardware; this mask all interrupts 6040f1b305eSSeth Goldberg */ 6050f1b305eSSeth Goldberg ipw2100_stop(sc); 6060f1b305eSSeth Goldberg sc->sc_flags &= ~IPW2100_FLAG_RUNNING; 6070f1b305eSSeth Goldberg sc->sc_suspended = 1; 6080f1b305eSSeth Goldberg 6090f1b305eSSeth Goldberg (void) ipw2100_free_firmware(sc); 6100f1b305eSSeth Goldberg ipw2100_ring_free(sc); 6110f1b305eSSeth Goldberg 6120f1b305eSSeth Goldberg return (DDI_SUCCESS); 6130f1b305eSSeth Goldberg } 6140f1b305eSSeth Goldberg 6150f1b305eSSeth Goldberg int 6160f1b305eSSeth Goldberg ipw2100_cpr_resume(struct ipw2100_softc *sc) 6170f1b305eSSeth Goldberg { 6180f1b305eSSeth Goldberg struct ieee80211com *ic = &sc->sc_ic; 6190f1b305eSSeth Goldberg dev_info_t *dip = sc->sc_dip; 6200f1b305eSSeth Goldberg int err; 6210f1b305eSSeth Goldberg 6220f1b305eSSeth Goldberg IPW2100_DBG(IPW2100_DBG_INIT, (sc->sc_dip, CE_CONT, 6230f1b305eSSeth Goldberg "ipw2100_cpr_resume(): enter\n")); 6240f1b305eSSeth Goldberg 6250f1b305eSSeth Goldberg /* 6260f1b305eSSeth Goldberg * Reset the chip 6270f1b305eSSeth Goldberg */ 6280f1b305eSSeth Goldberg err = ipw2100_chip_reset(sc); 6290f1b305eSSeth Goldberg if (err != DDI_SUCCESS) { 6300f1b305eSSeth Goldberg IPW2100_WARN((dip, CE_WARN, 6310f1b305eSSeth Goldberg "ipw2100_attach(): reset failed\n")); 6320f1b305eSSeth Goldberg return (DDI_FAILURE); 6330f1b305eSSeth Goldberg } 6340f1b305eSSeth Goldberg 6350f1b305eSSeth Goldberg /* 6360f1b305eSSeth Goldberg * Get the hw conf, including MAC address, then init all rings. 6370f1b305eSSeth Goldberg */ 6380f1b305eSSeth Goldberg /* ipw2100_hwconf_get(sc); */ 6390f1b305eSSeth Goldberg err = ipw2100_ring_init(sc); 6400f1b305eSSeth Goldberg if (err != DDI_SUCCESS) { 6410f1b305eSSeth Goldberg IPW2100_WARN((dip, CE_WARN, 6420f1b305eSSeth Goldberg "ipw2100_attach(): " 6430f1b305eSSeth Goldberg "unable to allocate and initialize rings\n")); 6440f1b305eSSeth Goldberg return (DDI_FAILURE); 6450f1b305eSSeth Goldberg } 6460f1b305eSSeth Goldberg 6470f1b305eSSeth Goldberg /* 6480f1b305eSSeth Goldberg * Cache firmware, always return true 6490f1b305eSSeth Goldberg */ 6500f1b305eSSeth Goldberg (void) ipw2100_cache_firmware(sc); 6510f1b305eSSeth Goldberg 6520f1b305eSSeth Goldberg /* 6530f1b305eSSeth Goldberg * Notify link is down now 6540f1b305eSSeth Goldberg */ 6550f1b305eSSeth Goldberg mac_link_update(ic->ic_mach, LINK_STATE_DOWN); 6560f1b305eSSeth Goldberg 6570f1b305eSSeth Goldberg /* 6580f1b305eSSeth Goldberg * create the mf thread to handle the link status, 6590f1b305eSSeth Goldberg * recovery fatal error, etc. 6600f1b305eSSeth Goldberg */ 6610f1b305eSSeth Goldberg sc->sc_mfthread_switch = 1; 6620f1b305eSSeth Goldberg if (sc->sc_mf_thread == NULL) 6630f1b305eSSeth Goldberg sc->sc_mf_thread = thread_create((caddr_t)NULL, 0, 6640f1b305eSSeth Goldberg ipw2100_thread, sc, 0, &p0, TS_RUN, minclsyspri); 6650f1b305eSSeth Goldberg 6660f1b305eSSeth Goldberg /* 6670f1b305eSSeth Goldberg * enable all interrupts 6680f1b305eSSeth Goldberg */ 6690f1b305eSSeth Goldberg sc->sc_suspended = 0; 6700f1b305eSSeth Goldberg ipw2100_csr_put32(sc, IPW2100_CSR_INTR_MASK, IPW2100_INTR_MASK_ALL); 6710f1b305eSSeth Goldberg 6720f1b305eSSeth Goldberg /* 6730f1b305eSSeth Goldberg * initialize ipw2100 hardware 6740f1b305eSSeth Goldberg */ 6750f1b305eSSeth Goldberg (void) ipw2100_init(sc); 6760f1b305eSSeth Goldberg 6770f1b305eSSeth Goldberg sc->sc_flags |= IPW2100_FLAG_RUNNING; 6780f1b305eSSeth Goldberg 6790f1b305eSSeth Goldberg return (DDI_SUCCESS); 6800f1b305eSSeth Goldberg } 6810f1b305eSSeth Goldberg 682799aa485SKonstantin Ananyev /* 683799aa485SKonstantin Ananyev * quiesce(9E) entry point. 684799aa485SKonstantin Ananyev * This function is called when the system is single-threaded at high 685799aa485SKonstantin Ananyev * PIL with preemption disabled. Therefore, this function must not be 686799aa485SKonstantin Ananyev * blocked. 687799aa485SKonstantin Ananyev * This function returns DDI_SUCCESS on success, or DDI_FAILURE on failure. 688799aa485SKonstantin Ananyev * DDI_FAILURE indicates an error condition and should almost never happen. 689799aa485SKonstantin Ananyev * Contributed by Juergen Keil, <jk@tools.de>. 690799aa485SKonstantin Ananyev */ 691799aa485SKonstantin Ananyev static int 692799aa485SKonstantin Ananyev ipw2100_quiesce(dev_info_t *dip) 693e9f896cfSeh146360 { 694e9f896cfSeh146360 struct ipw2100_softc *sc = 695e9f896cfSeh146360 ddi_get_soft_state(ipw2100_ssp, ddi_get_instance(dip)); 696e9f896cfSeh146360 697799aa485SKonstantin Ananyev if (sc == NULL) 698799aa485SKonstantin Ananyev return (DDI_FAILURE); 699799aa485SKonstantin Ananyev 700799aa485SKonstantin Ananyev /* 701799aa485SKonstantin Ananyev * No more blocking is allowed while we are in the 702799aa485SKonstantin Ananyev * quiesce(9E) entry point. 703799aa485SKonstantin Ananyev */ 704799aa485SKonstantin Ananyev sc->sc_flags |= IPW2100_FLAG_QUIESCED; 705799aa485SKonstantin Ananyev 706799aa485SKonstantin Ananyev /* 707799aa485SKonstantin Ananyev * Disable and mask all interrupts. 708799aa485SKonstantin Ananyev */ 709e9f896cfSeh146360 ipw2100_stop(sc); 710e9f896cfSeh146360 return (DDI_SUCCESS); 711e9f896cfSeh146360 } 712e9f896cfSeh146360 713bb5e3b2fSeh146360 static void 714bb5e3b2fSeh146360 ipw2100_tables_init(struct ipw2100_softc *sc) 715bb5e3b2fSeh146360 { 716bb5e3b2fSeh146360 sc->sc_table1_base = ipw2100_csr_get32(sc, IPW2100_CSR_TABLE1_BASE); 717bb5e3b2fSeh146360 sc->sc_table2_base = ipw2100_csr_get32(sc, IPW2100_CSR_TABLE2_BASE); 718bb5e3b2fSeh146360 } 719bb5e3b2fSeh146360 720bb5e3b2fSeh146360 static void 721bb5e3b2fSeh146360 ipw2100_stop(struct ipw2100_softc *sc) 722bb5e3b2fSeh146360 { 723bb5e3b2fSeh146360 struct ieee80211com *ic = &sc->sc_ic; 724bb5e3b2fSeh146360 725bb5e3b2fSeh146360 ipw2100_master_stop(sc); 726bb5e3b2fSeh146360 ipw2100_csr_put32(sc, IPW2100_CSR_RST, IPW2100_RST_SW_RESET); 727bb5e3b2fSeh146360 sc->sc_flags &= ~IPW2100_FLAG_FW_INITED; 728bb5e3b2fSeh146360 729799aa485SKonstantin Ananyev if (!(sc->sc_flags & IPW2100_FLAG_QUIESCED)) 730bb5e3b2fSeh146360 ieee80211_new_state(ic, IEEE80211_S_INIT, -1); 731bb5e3b2fSeh146360 } 732bb5e3b2fSeh146360 733bb5e3b2fSeh146360 static int 734bb5e3b2fSeh146360 ipw2100_config(struct ipw2100_softc *sc) 735bb5e3b2fSeh146360 { 736bb5e3b2fSeh146360 struct ieee80211com *ic = &sc->sc_ic; 737bb5e3b2fSeh146360 struct ipw2100_security sec; 738bb5e3b2fSeh146360 struct ipw2100_wep_key wkey; 739bb5e3b2fSeh146360 struct ipw2100_scan_options sopt; 740bb5e3b2fSeh146360 struct ipw2100_configuration cfg; 741bb5e3b2fSeh146360 uint32_t data; 742bb5e3b2fSeh146360 int err, i; 743bb5e3b2fSeh146360 744bb5e3b2fSeh146360 /* 745bb5e3b2fSeh146360 * operation mode 746bb5e3b2fSeh146360 */ 747bb5e3b2fSeh146360 switch (ic->ic_opmode) { 748bb5e3b2fSeh146360 case IEEE80211_M_STA: 749bb5e3b2fSeh146360 case IEEE80211_M_HOSTAP: 750bb5e3b2fSeh146360 data = LE_32(IPW2100_MODE_BSS); 751bb5e3b2fSeh146360 break; 752bb5e3b2fSeh146360 753bb5e3b2fSeh146360 case IEEE80211_M_IBSS: 754bb5e3b2fSeh146360 case IEEE80211_M_AHDEMO: 755bb5e3b2fSeh146360 data = LE_32(IPW2100_MODE_IBSS); 756bb5e3b2fSeh146360 break; 757bb5e3b2fSeh146360 } 758bb5e3b2fSeh146360 759bb5e3b2fSeh146360 IPW2100_DBG(IPW2100_DBG_WIFI, (sc->sc_dip, CE_CONT, 760bb5e3b2fSeh146360 "ipw2100_config(): Setting mode to %u\n", LE_32(data))); 761bb5e3b2fSeh146360 762bb5e3b2fSeh146360 err = ipw2100_cmd(sc, IPW2100_CMD_SET_MODE, 763bb5e3b2fSeh146360 &data, sizeof (data)); 764bb5e3b2fSeh146360 if (err != DDI_SUCCESS) 765bb5e3b2fSeh146360 return (err); 766bb5e3b2fSeh146360 767bb5e3b2fSeh146360 /* 768bb5e3b2fSeh146360 * operation channel if IBSS or MONITOR 769bb5e3b2fSeh146360 */ 770bb5e3b2fSeh146360 if (ic->ic_opmode == IEEE80211_M_IBSS) { 771bb5e3b2fSeh146360 772bb5e3b2fSeh146360 data = LE_32(ieee80211_chan2ieee(ic, ic->ic_ibss_chan)); 773bb5e3b2fSeh146360 774bb5e3b2fSeh146360 IPW2100_DBG(IPW2100_DBG_WIFI, (sc->sc_dip, CE_CONT, 775bb5e3b2fSeh146360 "ipw2100_config(): Setting channel to %u\n", LE_32(data))); 776bb5e3b2fSeh146360 777bb5e3b2fSeh146360 err = ipw2100_cmd(sc, IPW2100_CMD_SET_CHANNEL, 778bb5e3b2fSeh146360 &data, sizeof (data)); 779bb5e3b2fSeh146360 if (err != DDI_SUCCESS) 780bb5e3b2fSeh146360 return (err); 781bb5e3b2fSeh146360 } 782bb5e3b2fSeh146360 783bb5e3b2fSeh146360 /* 784bb5e3b2fSeh146360 * set MAC address 785bb5e3b2fSeh146360 */ 786bb5e3b2fSeh146360 IPW2100_DBG(IPW2100_DBG_WIFI, (sc->sc_dip, CE_CONT, 787bb5e3b2fSeh146360 "ipw2100_config(): Setting MAC address to " 788bb5e3b2fSeh146360 "%02x:%02x:%02x:%02x:%02x:%02x\n", 789bb5e3b2fSeh146360 ic->ic_macaddr[0], ic->ic_macaddr[1], ic->ic_macaddr[2], 790bb5e3b2fSeh146360 ic->ic_macaddr[3], ic->ic_macaddr[4], ic->ic_macaddr[5])); 791bb5e3b2fSeh146360 err = ipw2100_cmd(sc, IPW2100_CMD_SET_MAC_ADDRESS, ic->ic_macaddr, 792bb5e3b2fSeh146360 IEEE80211_ADDR_LEN); 793bb5e3b2fSeh146360 if (err != DDI_SUCCESS) 794bb5e3b2fSeh146360 return (err); 795bb5e3b2fSeh146360 796bb5e3b2fSeh146360 /* 797bb5e3b2fSeh146360 * configuration capabilities 798bb5e3b2fSeh146360 */ 799bb5e3b2fSeh146360 cfg.flags = IPW2100_CFG_BSS_MASK | IPW2100_CFG_IBSS_MASK | 800bb5e3b2fSeh146360 IPW2100_CFG_PREAMBLE_AUTO | IPW2100_CFG_802_1x_ENABLE; 801bb5e3b2fSeh146360 if (ic->ic_opmode == IEEE80211_M_IBSS) 802bb5e3b2fSeh146360 cfg.flags |= IPW2100_CFG_IBSS_AUTO_START; 803bb5e3b2fSeh146360 if (sc->if_flags & IFF_PROMISC) 804bb5e3b2fSeh146360 cfg.flags |= IPW2100_CFG_PROMISCUOUS; 805bb5e3b2fSeh146360 cfg.flags = LE_32(cfg.flags); 806bb5e3b2fSeh146360 cfg.bss_chan = LE_32(sc->sc_chmask >> 1); 807bb5e3b2fSeh146360 cfg.ibss_chan = LE_32(sc->sc_chmask >> 1); 808bb5e3b2fSeh146360 809bb5e3b2fSeh146360 IPW2100_DBG(IPW2100_DBG_WIFI, (sc->sc_dip, CE_CONT, 810bb5e3b2fSeh146360 "ipw2100_config(): Setting configuration to 0x%x\n", 811bb5e3b2fSeh146360 LE_32(cfg.flags))); 812bb5e3b2fSeh146360 813bb5e3b2fSeh146360 err = ipw2100_cmd(sc, IPW2100_CMD_SET_CONFIGURATION, 814bb5e3b2fSeh146360 &cfg, sizeof (cfg)); 815bb5e3b2fSeh146360 816bb5e3b2fSeh146360 if (err != DDI_SUCCESS) 817bb5e3b2fSeh146360 return (err); 818bb5e3b2fSeh146360 819bb5e3b2fSeh146360 /* 820bb5e3b2fSeh146360 * set 802.11 Tx rates 821bb5e3b2fSeh146360 */ 822bb5e3b2fSeh146360 data = LE_32(0x3); /* 1, 2 */ 823bb5e3b2fSeh146360 IPW2100_DBG(IPW2100_DBG_WIFI, (sc->sc_dip, CE_CONT, 824bb5e3b2fSeh146360 "ipw2100_config(): Setting 802.11 Tx rates to 0x%x\n", 825bb5e3b2fSeh146360 LE_32(data))); 826bb5e3b2fSeh146360 err = ipw2100_cmd(sc, IPW2100_CMD_SET_BASIC_TX_RATES, 827bb5e3b2fSeh146360 &data, sizeof (data)); 828bb5e3b2fSeh146360 if (err != DDI_SUCCESS) 829bb5e3b2fSeh146360 return (err); 830bb5e3b2fSeh146360 831bb5e3b2fSeh146360 /* 832bb5e3b2fSeh146360 * set 802.11b Tx rates 833bb5e3b2fSeh146360 */ 834bb5e3b2fSeh146360 data = LE_32(0xf); /* 1, 2, 5.5, 11 */ 835bb5e3b2fSeh146360 IPW2100_DBG(IPW2100_DBG_WIFI, (sc->sc_dip, CE_CONT, 836bb5e3b2fSeh146360 "ipw2100_config(): Setting 802.11b Tx rates to 0x%x\n", 837bb5e3b2fSeh146360 LE_32(data))); 838bb5e3b2fSeh146360 err = ipw2100_cmd(sc, IPW2100_CMD_SET_TX_RATES, &data, sizeof (data)); 839bb5e3b2fSeh146360 if (err != DDI_SUCCESS) 840bb5e3b2fSeh146360 return (err); 841bb5e3b2fSeh146360 842bb5e3b2fSeh146360 /* 843bb5e3b2fSeh146360 * set power mode 844bb5e3b2fSeh146360 */ 845bb5e3b2fSeh146360 data = LE_32(IPW2100_POWER_MODE_CAM); 846bb5e3b2fSeh146360 IPW2100_DBG(IPW2100_DBG_WIFI, (sc->sc_dip, CE_CONT, 847bb5e3b2fSeh146360 "ipw2100_config(): Setting power mode to %u\n", LE_32(data))); 848bb5e3b2fSeh146360 err = ipw2100_cmd(sc, IPW2100_CMD_SET_POWER_MODE, &data, sizeof (data)); 849bb5e3b2fSeh146360 if (err != DDI_SUCCESS) 850bb5e3b2fSeh146360 return (err); 851bb5e3b2fSeh146360 852bb5e3b2fSeh146360 /* 853bb5e3b2fSeh146360 * set power index 854bb5e3b2fSeh146360 */ 855bb5e3b2fSeh146360 if (ic->ic_opmode == IEEE80211_M_IBSS) { 856bb5e3b2fSeh146360 data = LE_32(32); 857bb5e3b2fSeh146360 IPW2100_DBG(IPW2100_DBG_WIFI, (sc->sc_dip, CE_CONT, 858bb5e3b2fSeh146360 "ipw2100_config(): Setting Tx power index to %u\n", 859bb5e3b2fSeh146360 LE_32(data))); 860bb5e3b2fSeh146360 err = ipw2100_cmd(sc, IPW2100_CMD_SET_TX_POWER_INDEX, 861bb5e3b2fSeh146360 &data, sizeof (data)); 862bb5e3b2fSeh146360 if (err != DDI_SUCCESS) 863bb5e3b2fSeh146360 return (err); 864bb5e3b2fSeh146360 } 865bb5e3b2fSeh146360 866bb5e3b2fSeh146360 /* 867bb5e3b2fSeh146360 * set RTS threshold 868bb5e3b2fSeh146360 */ 869bb5e3b2fSeh146360 ic->ic_rtsthreshold = 2346; 870bb5e3b2fSeh146360 data = LE_32(ic->ic_rtsthreshold); 871bb5e3b2fSeh146360 IPW2100_DBG(IPW2100_DBG_WIFI, (sc->sc_dip, CE_CONT, 872bb5e3b2fSeh146360 "ipw2100_config(): Setting RTS threshold to %u\n", LE_32(data))); 873bb5e3b2fSeh146360 err = ipw2100_cmd(sc, IPW2100_CMD_SET_RTS_THRESHOLD, 874bb5e3b2fSeh146360 &data, sizeof (data)); 875bb5e3b2fSeh146360 if (err != DDI_SUCCESS) 876bb5e3b2fSeh146360 return (err); 877bb5e3b2fSeh146360 878bb5e3b2fSeh146360 /* 879bb5e3b2fSeh146360 * set frag threshold 880bb5e3b2fSeh146360 */ 881bb5e3b2fSeh146360 ic->ic_fragthreshold = 2346; 882bb5e3b2fSeh146360 data = LE_32(ic->ic_fragthreshold); 883bb5e3b2fSeh146360 IPW2100_DBG(IPW2100_DBG_WIFI, (sc->sc_dip, CE_CONT, 884bb5e3b2fSeh146360 "ipw2100_config(): Setting frag threshold to %u\n", LE_32(data))); 885bb5e3b2fSeh146360 err = ipw2100_cmd(sc, IPW2100_CMD_SET_FRAG_THRESHOLD, 886bb5e3b2fSeh146360 &data, sizeof (data)); 887bb5e3b2fSeh146360 if (err != DDI_SUCCESS) 888bb5e3b2fSeh146360 return (err); 889bb5e3b2fSeh146360 890bb5e3b2fSeh146360 /* 891bb5e3b2fSeh146360 * set ESSID 892bb5e3b2fSeh146360 */ 893bb5e3b2fSeh146360 IPW2100_DBG(IPW2100_DBG_WIFI, (sc->sc_dip, CE_CONT, 894bb5e3b2fSeh146360 "ipw2100_config(): Setting ESSID to %u, ESSID[0]%c\n", 895bb5e3b2fSeh146360 ic->ic_des_esslen, ic->ic_des_essid[0])); 896bb5e3b2fSeh146360 err = ipw2100_cmd(sc, IPW2100_CMD_SET_ESSID, 897bb5e3b2fSeh146360 ic->ic_des_essid, ic->ic_des_esslen); 898bb5e3b2fSeh146360 if (err != DDI_SUCCESS) 899bb5e3b2fSeh146360 return (err); 900bb5e3b2fSeh146360 901bb5e3b2fSeh146360 /* 902bb5e3b2fSeh146360 * no mandatory BSSID 903bb5e3b2fSeh146360 */ 904bb5e3b2fSeh146360 err = ipw2100_cmd(sc, IPW2100_CMD_SET_MANDATORY_BSSID, NULL, 0); 905bb5e3b2fSeh146360 if (err != DDI_SUCCESS) 906bb5e3b2fSeh146360 return (err); 907bb5e3b2fSeh146360 908bb5e3b2fSeh146360 /* 909bb5e3b2fSeh146360 * set BSSID, if any 910bb5e3b2fSeh146360 */ 911bb5e3b2fSeh146360 if (ic->ic_flags & IEEE80211_F_DESBSSID) { 912bb5e3b2fSeh146360 IPW2100_DBG(IPW2100_DBG_WIFI, (sc->sc_dip, CE_CONT, 913bb5e3b2fSeh146360 "ipw2100_config(): Setting BSSID to %u\n", 914bb5e3b2fSeh146360 IEEE80211_ADDR_LEN)); 915bb5e3b2fSeh146360 err = ipw2100_cmd(sc, IPW2100_CMD_SET_DESIRED_BSSID, 916bb5e3b2fSeh146360 ic->ic_des_bssid, IEEE80211_ADDR_LEN); 917bb5e3b2fSeh146360 if (err != DDI_SUCCESS) 918bb5e3b2fSeh146360 return (err); 919bb5e3b2fSeh146360 } 920bb5e3b2fSeh146360 921bb5e3b2fSeh146360 /* 922bb5e3b2fSeh146360 * set security information 923bb5e3b2fSeh146360 */ 924bb5e3b2fSeh146360 (void) memset(&sec, 0, sizeof (sec)); 925bb5e3b2fSeh146360 /* 926bb5e3b2fSeh146360 * use the value set to ic_bss to retrieve current sharedmode 927bb5e3b2fSeh146360 */ 928bb5e3b2fSeh146360 sec.authmode = (ic->ic_bss->in_authmode == WL_SHAREDKEY) ? 929bb5e3b2fSeh146360 IPW2100_AUTH_SHARED : IPW2100_AUTH_OPEN; 930bb5e3b2fSeh146360 sec.ciphers = LE_32(IPW2100_CIPHER_NONE); 931bb5e3b2fSeh146360 IPW2100_DBG(IPW2100_DBG_WIFI, (sc->sc_dip, CE_CONT, 932bb5e3b2fSeh146360 "ipw2100_config(): Setting authmode to %u\n", sec.authmode)); 933bb5e3b2fSeh146360 err = ipw2100_cmd(sc, IPW2100_CMD_SET_SECURITY_INFORMATION, 934bb5e3b2fSeh146360 &sec, sizeof (sec)); 935bb5e3b2fSeh146360 if (err != DDI_SUCCESS) 936bb5e3b2fSeh146360 return (err); 937bb5e3b2fSeh146360 938bb5e3b2fSeh146360 /* 939bb5e3b2fSeh146360 * set WEP if any 940bb5e3b2fSeh146360 */ 941bb5e3b2fSeh146360 if (ic->ic_flags & IEEE80211_F_PRIVACY) { 942bb5e3b2fSeh146360 for (i = 0; i < IEEE80211_WEP_NKID; i++) { 943bb5e3b2fSeh146360 if (ic->ic_nw_keys[i].wk_keylen == 0) 944bb5e3b2fSeh146360 continue; 945bb5e3b2fSeh146360 wkey.idx = (uint8_t)i; 946bb5e3b2fSeh146360 wkey.len = ic->ic_nw_keys[i].wk_keylen; 947bb5e3b2fSeh146360 (void) memset(wkey.key, 0, sizeof (wkey.key)); 948bb5e3b2fSeh146360 if (ic->ic_nw_keys[i].wk_keylen) 949bb5e3b2fSeh146360 (void) memcpy(wkey.key, 950bb5e3b2fSeh146360 ic->ic_nw_keys[i].wk_key, 951bb5e3b2fSeh146360 ic->ic_nw_keys[i].wk_keylen); 952bb5e3b2fSeh146360 err = ipw2100_cmd(sc, IPW2100_CMD_SET_WEP_KEY, 953bb5e3b2fSeh146360 &wkey, sizeof (wkey)); 954bb5e3b2fSeh146360 if (err != DDI_SUCCESS) 955bb5e3b2fSeh146360 return (err); 956bb5e3b2fSeh146360 } 957bb5e3b2fSeh146360 data = LE_32(ic->ic_def_txkey); 958bb5e3b2fSeh146360 err = ipw2100_cmd(sc, IPW2100_CMD_SET_WEP_KEY_INDEX, 959bb5e3b2fSeh146360 &data, sizeof (data)); 960bb5e3b2fSeh146360 if (err != DDI_SUCCESS) 961bb5e3b2fSeh146360 return (err); 962bb5e3b2fSeh146360 } 963bb5e3b2fSeh146360 964bb5e3b2fSeh146360 /* 965bb5e3b2fSeh146360 * turn on WEP 966bb5e3b2fSeh146360 */ 967bb5e3b2fSeh146360 data = LE_32((ic->ic_flags & IEEE80211_F_PRIVACY) ? 0x8 : 0); 968bb5e3b2fSeh146360 IPW2100_DBG(IPW2100_DBG_WIFI, (sc->sc_dip, CE_CONT, 969bb5e3b2fSeh146360 "ipw2100_config(): Setting WEP flags to %u\n", LE_32(data))); 970bb5e3b2fSeh146360 err = ipw2100_cmd(sc, IPW2100_CMD_SET_WEP_FLAGS, &data, sizeof (data)); 971bb5e3b2fSeh146360 if (err != DDI_SUCCESS) 972bb5e3b2fSeh146360 return (err); 973bb5e3b2fSeh146360 974bb5e3b2fSeh146360 /* 975bb5e3b2fSeh146360 * set beacon interval if IBSS or HostAP 976bb5e3b2fSeh146360 */ 977bb5e3b2fSeh146360 if (ic->ic_opmode == IEEE80211_M_IBSS || 978bb5e3b2fSeh146360 ic->ic_opmode == IEEE80211_M_HOSTAP) { 979bb5e3b2fSeh146360 980bb5e3b2fSeh146360 data = LE_32(ic->ic_lintval); 981bb5e3b2fSeh146360 IPW2100_DBG(IPW2100_DBG_WIFI, (sc->sc_dip, CE_CONT, 982bb5e3b2fSeh146360 "ipw2100_config(): Setting beacon interval to %u\n", 983bb5e3b2fSeh146360 LE_32(data))); 984bb5e3b2fSeh146360 err = ipw2100_cmd(sc, IPW2100_CMD_SET_BEACON_INTERVAL, 985bb5e3b2fSeh146360 &data, sizeof (data)); 986bb5e3b2fSeh146360 if (err != DDI_SUCCESS) 987bb5e3b2fSeh146360 return (err); 988bb5e3b2fSeh146360 } 989bb5e3b2fSeh146360 990bb5e3b2fSeh146360 /* 991bb5e3b2fSeh146360 * set scan options 992bb5e3b2fSeh146360 */ 993bb5e3b2fSeh146360 sopt.flags = LE_32(0); 994bb5e3b2fSeh146360 sopt.channels = LE_32(sc->sc_chmask >> 1); 995bb5e3b2fSeh146360 err = ipw2100_cmd(sc, IPW2100_CMD_SET_SCAN_OPTIONS, 996bb5e3b2fSeh146360 &sopt, sizeof (sopt)); 997bb5e3b2fSeh146360 if (err != DDI_SUCCESS) 998bb5e3b2fSeh146360 return (err); 999bb5e3b2fSeh146360 1000bb5e3b2fSeh146360 en_adapter: 1001bb5e3b2fSeh146360 1002bb5e3b2fSeh146360 IPW2100_DBG(IPW2100_DBG_WIFI, (sc->sc_dip, CE_CONT, 1003bb5e3b2fSeh146360 "ipw2100_config(): Enabling adapter\n")); 1004bb5e3b2fSeh146360 1005bb5e3b2fSeh146360 return (ipw2100_cmd(sc, IPW2100_CMD_ENABLE, NULL, 0)); 1006bb5e3b2fSeh146360 } 1007bb5e3b2fSeh146360 1008bb5e3b2fSeh146360 static int 1009bb5e3b2fSeh146360 ipw2100_cmd(struct ipw2100_softc *sc, uint32_t type, void *buf, size_t len) 1010bb5e3b2fSeh146360 { 1011bb5e3b2fSeh146360 struct ipw2100_bd *txbd; 1012bb5e3b2fSeh146360 clock_t clk; 1013bb5e3b2fSeh146360 uint32_t idx; 1014bb5e3b2fSeh146360 1015bb5e3b2fSeh146360 /* 1016bb5e3b2fSeh146360 * prepare command buffer 1017bb5e3b2fSeh146360 */ 1018bb5e3b2fSeh146360 sc->sc_cmd->type = LE_32(type); 1019bb5e3b2fSeh146360 sc->sc_cmd->subtype = LE_32(0); 1020bb5e3b2fSeh146360 sc->sc_cmd->seq = LE_32(0); 1021bb5e3b2fSeh146360 /* 1022bb5e3b2fSeh146360 * copy data if any 1023bb5e3b2fSeh146360 */ 1024bb5e3b2fSeh146360 if (len && buf) 1025bb5e3b2fSeh146360 (void) memcpy(sc->sc_cmd->data, buf, len); 1026bb5e3b2fSeh146360 sc->sc_cmd->len = LE_32(len); 1027bb5e3b2fSeh146360 1028bb5e3b2fSeh146360 /* 1029bb5e3b2fSeh146360 * get host & device descriptor to submit command 1030bb5e3b2fSeh146360 */ 1031bb5e3b2fSeh146360 mutex_enter(&sc->sc_tx_lock); 1032bb5e3b2fSeh146360 1033bb5e3b2fSeh146360 IPW2100_DBG(IPW2100_DBG_RING, (sc->sc_dip, CE_CONT, 1034bb5e3b2fSeh146360 "ipw2100_cmd(): tx-free=%d\n", sc->sc_tx_free)); 1035bb5e3b2fSeh146360 1036bb5e3b2fSeh146360 /* 1037bb5e3b2fSeh146360 * command need 1 descriptor 1038bb5e3b2fSeh146360 */ 1039bb5e3b2fSeh146360 while (sc->sc_tx_free < 1) { 1040bb5e3b2fSeh146360 sc->sc_flags |= IPW2100_FLAG_CMD_WAIT; 1041bb5e3b2fSeh146360 cv_wait(&sc->sc_tx_cond, &sc->sc_tx_lock); 1042bb5e3b2fSeh146360 } 1043bb5e3b2fSeh146360 idx = sc->sc_tx_cur; 1044bb5e3b2fSeh146360 1045bb5e3b2fSeh146360 IPW2100_DBG(IPW2100_DBG_RING, (sc->sc_dip, CE_CONT, 1046bb5e3b2fSeh146360 "ipw2100_cmd(): tx-cur=%d\n", idx)); 1047bb5e3b2fSeh146360 1048bb5e3b2fSeh146360 sc->sc_done = 0; 1049bb5e3b2fSeh146360 1050bb5e3b2fSeh146360 txbd = &sc->sc_txbd[idx]; 1051bb5e3b2fSeh146360 txbd->phyaddr = LE_32(sc->sc_dma_cmd.dr_pbase); 1052bb5e3b2fSeh146360 txbd->len = LE_32(sizeof (struct ipw2100_cmd)); 1053bb5e3b2fSeh146360 txbd->flags = IPW2100_BD_FLAG_TX_FRAME_COMMAND 1054bb5e3b2fSeh146360 | IPW2100_BD_FLAG_TX_LAST_FRAGMENT; 1055bb5e3b2fSeh146360 txbd->nfrag = 1; 1056bb5e3b2fSeh146360 /* 1057bb5e3b2fSeh146360 * sync for device 1058bb5e3b2fSeh146360 */ 1059bb5e3b2fSeh146360 (void) ddi_dma_sync(sc->sc_dma_cmd.dr_hnd, 0, 1060bb5e3b2fSeh146360 sizeof (struct ipw2100_cmd), DDI_DMA_SYNC_FORDEV); 1061bb5e3b2fSeh146360 (void) ddi_dma_sync(sc->sc_dma_txbd.dr_hnd, 1062bb5e3b2fSeh146360 idx * sizeof (struct ipw2100_bd), 1063bb5e3b2fSeh146360 sizeof (struct ipw2100_bd), DDI_DMA_SYNC_FORDEV); 1064bb5e3b2fSeh146360 1065bb5e3b2fSeh146360 /* 1066bb5e3b2fSeh146360 * ring move forward 1067bb5e3b2fSeh146360 */ 1068bb5e3b2fSeh146360 sc->sc_tx_cur = RING_FORWARD(sc->sc_tx_cur, 1, IPW2100_NUM_TXBD); 1069bb5e3b2fSeh146360 sc->sc_tx_free--; 1070bb5e3b2fSeh146360 ipw2100_csr_put32(sc, IPW2100_CSR_TX_WRITE_INDEX, sc->sc_tx_cur); 1071bb5e3b2fSeh146360 mutex_exit(&sc->sc_tx_lock); 1072bb5e3b2fSeh146360 1073bb5e3b2fSeh146360 /* 1074bb5e3b2fSeh146360 * wait for command done 1075bb5e3b2fSeh146360 */ 1076d3d50737SRafael Vanoni clk = drv_usectohz(1000000); /* 1 second */ 1077bb5e3b2fSeh146360 mutex_enter(&sc->sc_ilock); 1078bb5e3b2fSeh146360 while (sc->sc_done == 0) { 1079bb5e3b2fSeh146360 /* 1080bb5e3b2fSeh146360 * pending for the response 1081bb5e3b2fSeh146360 */ 1082d3d50737SRafael Vanoni if (cv_reltimedwait(&sc->sc_cmd_cond, &sc->sc_ilock, 1083d3d50737SRafael Vanoni clk, TR_CLOCK_TICK) < 0) 1084bb5e3b2fSeh146360 break; 1085bb5e3b2fSeh146360 } 1086bb5e3b2fSeh146360 mutex_exit(&sc->sc_ilock); 1087bb5e3b2fSeh146360 1088bb5e3b2fSeh146360 IPW2100_DBG(IPW2100_DBG_RING, (sc->sc_dip, CE_CONT, 1089bb5e3b2fSeh146360 "ipw2100_cmd(): cmd-done=%s\n", sc->sc_done ? "yes" : "no")); 1090bb5e3b2fSeh146360 1091bb5e3b2fSeh146360 if (sc->sc_done == 0) 1092bb5e3b2fSeh146360 return (DDI_FAILURE); 1093bb5e3b2fSeh146360 1094bb5e3b2fSeh146360 return (DDI_SUCCESS); 1095bb5e3b2fSeh146360 } 1096bb5e3b2fSeh146360 1097bb5e3b2fSeh146360 int 1098bb5e3b2fSeh146360 ipw2100_init(struct ipw2100_softc *sc) 1099bb5e3b2fSeh146360 { 1100bb5e3b2fSeh146360 int err; 1101bb5e3b2fSeh146360 11020f1b305eSSeth Goldberg IPW2100_DBG(IPW2100_DBG_INIT, (sc->sc_dip, CE_CONT, 11030f1b305eSSeth Goldberg "ipw2100_init(): enter\n")); 11040f1b305eSSeth Goldberg 1105bb5e3b2fSeh146360 /* 1106bb5e3b2fSeh146360 * no firmware is available, return fail directly 1107bb5e3b2fSeh146360 */ 1108bb5e3b2fSeh146360 if (!(sc->sc_flags & IPW2100_FLAG_FW_CACHED)) { 1109bb5e3b2fSeh146360 IPW2100_WARN((sc->sc_dip, CE_WARN, 1110bb5e3b2fSeh146360 "ipw2100_init(): no firmware is available\n")); 1111bb5e3b2fSeh146360 return (DDI_FAILURE); 1112bb5e3b2fSeh146360 } 1113bb5e3b2fSeh146360 1114bb5e3b2fSeh146360 ipw2100_stop(sc); 1115bb5e3b2fSeh146360 1116bb5e3b2fSeh146360 err = ipw2100_chip_reset(sc); 1117bb5e3b2fSeh146360 if (err != DDI_SUCCESS) { 1118bb5e3b2fSeh146360 IPW2100_WARN((sc->sc_dip, CE_WARN, 1119bb5e3b2fSeh146360 "ipw2100_init(): could not reset adapter\n")); 1120bb5e3b2fSeh146360 goto fail; 1121bb5e3b2fSeh146360 } 1122bb5e3b2fSeh146360 1123bb5e3b2fSeh146360 /* 1124bb5e3b2fSeh146360 * load microcode 1125bb5e3b2fSeh146360 */ 11260f1b305eSSeth Goldberg IPW2100_DBG(IPW2100_DBG_INIT, (sc->sc_dip, CE_CONT, 11270f1b305eSSeth Goldberg "ipw2100_init(): loading microcode\n")); 1128bb5e3b2fSeh146360 err = ipw2100_load_uc(sc); 1129bb5e3b2fSeh146360 if (err != DDI_SUCCESS) { 1130bb5e3b2fSeh146360 IPW2100_WARN((sc->sc_dip, CE_WARN, 1131bb5e3b2fSeh146360 "ipw2100_init(): could not load microcode, try again\n")); 1132bb5e3b2fSeh146360 goto fail; 1133bb5e3b2fSeh146360 } 1134bb5e3b2fSeh146360 1135bb5e3b2fSeh146360 ipw2100_master_stop(sc); 1136bb5e3b2fSeh146360 1137bb5e3b2fSeh146360 ipw2100_ring_hwsetup(sc); 1138bb5e3b2fSeh146360 1139bb5e3b2fSeh146360 /* 1140bb5e3b2fSeh146360 * load firmware 1141bb5e3b2fSeh146360 */ 11420f1b305eSSeth Goldberg IPW2100_DBG(IPW2100_DBG_INIT, (sc->sc_dip, CE_CONT, 11430f1b305eSSeth Goldberg "ipw2100_init(): loading firmware\n")); 1144bb5e3b2fSeh146360 err = ipw2100_load_fw(sc); 1145bb5e3b2fSeh146360 if (err != DDI_SUCCESS) { 1146bb5e3b2fSeh146360 IPW2100_WARN((sc->sc_dip, CE_WARN, 1147bb5e3b2fSeh146360 "ipw2100_init(): could not load firmware, try again\n")); 1148bb5e3b2fSeh146360 goto fail; 1149bb5e3b2fSeh146360 } 1150bb5e3b2fSeh146360 1151bb5e3b2fSeh146360 /* 1152bb5e3b2fSeh146360 * initialize tables 1153bb5e3b2fSeh146360 */ 1154bb5e3b2fSeh146360 ipw2100_tables_init(sc); 1155bb5e3b2fSeh146360 ipw2100_table1_put32(sc, IPW2100_INFO_LOCK, 0); 1156bb5e3b2fSeh146360 1157bb5e3b2fSeh146360 /* 1158bb5e3b2fSeh146360 * Hardware will be enabled after configuration 1159bb5e3b2fSeh146360 */ 1160bb5e3b2fSeh146360 err = ipw2100_config(sc); 1161bb5e3b2fSeh146360 if (err != DDI_SUCCESS) { 1162bb5e3b2fSeh146360 IPW2100_WARN((sc->sc_dip, CE_WARN, 1163bb5e3b2fSeh146360 "ipw2100_init(): device configuration failed\n")); 1164bb5e3b2fSeh146360 goto fail; 1165bb5e3b2fSeh146360 } 1166bb5e3b2fSeh146360 1167bb5e3b2fSeh146360 delay(drv_usectohz(delay_config_stable)); 1168bb5e3b2fSeh146360 1169bb5e3b2fSeh146360 return (DDI_SUCCESS); 1170bb5e3b2fSeh146360 1171bb5e3b2fSeh146360 fail: 1172bb5e3b2fSeh146360 ipw2100_stop(sc); 1173bb5e3b2fSeh146360 1174bb5e3b2fSeh146360 return (err); 1175bb5e3b2fSeh146360 } 1176bb5e3b2fSeh146360 1177bb5e3b2fSeh146360 /* 1178bb5e3b2fSeh146360 * get hardware configurations from EEPROM embedded within chip 1179bb5e3b2fSeh146360 */ 1180bb5e3b2fSeh146360 static void 1181bb5e3b2fSeh146360 ipw2100_hwconf_get(struct ipw2100_softc *sc) 1182bb5e3b2fSeh146360 { 1183bb5e3b2fSeh146360 int i; 1184bb5e3b2fSeh146360 uint16_t val; 1185bb5e3b2fSeh146360 1186bb5e3b2fSeh146360 /* 1187bb5e3b2fSeh146360 * MAC address 1188bb5e3b2fSeh146360 */ 1189bb5e3b2fSeh146360 i = 0; 1190bb5e3b2fSeh146360 val = ipw2100_rom_get16(sc, IPW2100_ROM_MAC + 0); 1191bb5e3b2fSeh146360 sc->sc_macaddr[i++] = val >> 8; 1192bb5e3b2fSeh146360 sc->sc_macaddr[i++] = val & 0xff; 1193bb5e3b2fSeh146360 val = ipw2100_rom_get16(sc, IPW2100_ROM_MAC + 1); 1194bb5e3b2fSeh146360 sc->sc_macaddr[i++] = val >> 8; 1195bb5e3b2fSeh146360 sc->sc_macaddr[i++] = val & 0xff; 1196bb5e3b2fSeh146360 val = ipw2100_rom_get16(sc, IPW2100_ROM_MAC + 2); 1197bb5e3b2fSeh146360 sc->sc_macaddr[i++] = val >> 8; 1198bb5e3b2fSeh146360 sc->sc_macaddr[i++] = val & 0xff; 1199bb5e3b2fSeh146360 1200bb5e3b2fSeh146360 /* 1201bb5e3b2fSeh146360 * formatted MAC address string 1202bb5e3b2fSeh146360 */ 1203bb5e3b2fSeh146360 (void) snprintf(sc->sc_macstr, sizeof (sc->sc_macstr), 1204bb5e3b2fSeh146360 "%02x:%02x:%02x:%02x:%02x:%02x", 1205bb5e3b2fSeh146360 sc->sc_macaddr[0], sc->sc_macaddr[1], 1206bb5e3b2fSeh146360 sc->sc_macaddr[2], sc->sc_macaddr[3], 1207bb5e3b2fSeh146360 sc->sc_macaddr[4], sc->sc_macaddr[5]); 1208bb5e3b2fSeh146360 1209bb5e3b2fSeh146360 /* 1210bb5e3b2fSeh146360 * channel mask 1211bb5e3b2fSeh146360 */ 1212bb5e3b2fSeh146360 val = ipw2100_rom_get16(sc, IPW2100_ROM_CHANNEL_LIST); 1213bb5e3b2fSeh146360 if (val == 0) 1214bb5e3b2fSeh146360 val = 0x7ff; 1215bb5e3b2fSeh146360 sc->sc_chmask = val << 1; 1216bb5e3b2fSeh146360 IPW2100_DBG(IPW2100_DBG_HWCAP, (sc->sc_dip, CE_CONT, 1217bb5e3b2fSeh146360 "ipw2100_hwconf_get(): channel-mask=0x%08x\n", sc->sc_chmask)); 1218bb5e3b2fSeh146360 1219bb5e3b2fSeh146360 /* 1220bb5e3b2fSeh146360 * radio switch 1221bb5e3b2fSeh146360 */ 1222bb5e3b2fSeh146360 val = ipw2100_rom_get16(sc, IPW2100_ROM_RADIO); 1223bb5e3b2fSeh146360 if (val & 0x08) 1224bb5e3b2fSeh146360 sc->sc_flags |= IPW2100_FLAG_HAS_RADIO_SWITCH; 1225bb5e3b2fSeh146360 1226bb5e3b2fSeh146360 IPW2100_DBG(IPW2100_DBG_HWCAP, (sc->sc_dip, CE_CONT, 1227bb5e3b2fSeh146360 "ipw2100_hwconf_get(): has-radio-switch=%s(%u)\n", 1228bb5e3b2fSeh146360 (sc->sc_flags & IPW2100_FLAG_HAS_RADIO_SWITCH)? "yes" : "no", 1229bb5e3b2fSeh146360 val)); 1230bb5e3b2fSeh146360 } 1231bb5e3b2fSeh146360 1232bb5e3b2fSeh146360 /* 1233bb5e3b2fSeh146360 * all ipw2100 interrupts will be masked by this routine 1234bb5e3b2fSeh146360 */ 1235bb5e3b2fSeh146360 static void 1236bb5e3b2fSeh146360 ipw2100_master_stop(struct ipw2100_softc *sc) 1237bb5e3b2fSeh146360 { 1238bb5e3b2fSeh146360 uint32_t tmp; 1239bb5e3b2fSeh146360 int ntries; 1240bb5e3b2fSeh146360 1241bb5e3b2fSeh146360 /* 1242bb5e3b2fSeh146360 * disable interrupts 1243bb5e3b2fSeh146360 */ 1244bb5e3b2fSeh146360 ipw2100_csr_put32(sc, IPW2100_CSR_INTR_MASK, 0); 1245bb5e3b2fSeh146360 1246bb5e3b2fSeh146360 ipw2100_csr_put32(sc, IPW2100_CSR_RST, IPW2100_RST_STOP_MASTER); 1247bb5e3b2fSeh146360 for (ntries = 0; ntries < 50; ntries++) { 1248bb5e3b2fSeh146360 if (ipw2100_csr_get32(sc, IPW2100_CSR_RST) 1249bb5e3b2fSeh146360 & IPW2100_RST_MASTER_DISABLED) 1250bb5e3b2fSeh146360 break; 1251bb5e3b2fSeh146360 drv_usecwait(10); 1252bb5e3b2fSeh146360 } 1253799aa485SKonstantin Ananyev if (ntries == 50 && !(sc->sc_flags & IPW2100_FLAG_QUIESCED)) 1254bb5e3b2fSeh146360 IPW2100_WARN((sc->sc_dip, CE_WARN, 1255bb5e3b2fSeh146360 "ipw2100_master_stop(): timeout when stop master\n")); 1256bb5e3b2fSeh146360 1257bb5e3b2fSeh146360 tmp = ipw2100_csr_get32(sc, IPW2100_CSR_RST); 1258bb5e3b2fSeh146360 ipw2100_csr_put32(sc, IPW2100_CSR_RST, 1259bb5e3b2fSeh146360 tmp | IPW2100_RST_PRINCETON_RESET); 1260bb5e3b2fSeh146360 1261bb5e3b2fSeh146360 sc->sc_flags &= ~IPW2100_FLAG_FW_INITED; 1262bb5e3b2fSeh146360 } 1263bb5e3b2fSeh146360 1264bb5e3b2fSeh146360 /* 1265bb5e3b2fSeh146360 * all ipw2100 interrupts will be masked by this routine 1266bb5e3b2fSeh146360 */ 1267bb5e3b2fSeh146360 static int 1268bb5e3b2fSeh146360 ipw2100_chip_reset(struct ipw2100_softc *sc) 1269bb5e3b2fSeh146360 { 1270bb5e3b2fSeh146360 int ntries; 1271bb5e3b2fSeh146360 uint32_t tmp; 1272bb5e3b2fSeh146360 1273bb5e3b2fSeh146360 ipw2100_master_stop(sc); 1274bb5e3b2fSeh146360 1275bb5e3b2fSeh146360 /* 1276*0778188fSHengqing Hu * move adapter to DO state 1277bb5e3b2fSeh146360 */ 1278bb5e3b2fSeh146360 tmp = ipw2100_csr_get32(sc, IPW2100_CSR_CTL); 1279bb5e3b2fSeh146360 ipw2100_csr_put32(sc, IPW2100_CSR_CTL, tmp | IPW2100_CTL_INIT); 1280bb5e3b2fSeh146360 1281bb5e3b2fSeh146360 /* 1282bb5e3b2fSeh146360 * wait for clock stabilization 1283bb5e3b2fSeh146360 */ 1284bb5e3b2fSeh146360 for (ntries = 0; ntries < 1000; ntries++) { 1285bb5e3b2fSeh146360 if (ipw2100_csr_get32(sc, IPW2100_CSR_CTL) 1286bb5e3b2fSeh146360 & IPW2100_CTL_CLOCK_READY) 1287bb5e3b2fSeh146360 break; 1288bb5e3b2fSeh146360 drv_usecwait(200); 1289bb5e3b2fSeh146360 } 1290bb5e3b2fSeh146360 if (ntries == 1000) 1291bb5e3b2fSeh146360 return (DDI_FAILURE); 1292bb5e3b2fSeh146360 1293bb5e3b2fSeh146360 tmp = ipw2100_csr_get32(sc, IPW2100_CSR_RST); 1294bb5e3b2fSeh146360 ipw2100_csr_put32(sc, IPW2100_CSR_RST, tmp | IPW2100_RST_SW_RESET); 1295bb5e3b2fSeh146360 1296bb5e3b2fSeh146360 drv_usecwait(10); 1297bb5e3b2fSeh146360 1298bb5e3b2fSeh146360 tmp = ipw2100_csr_get32(sc, IPW2100_CSR_CTL); 1299bb5e3b2fSeh146360 ipw2100_csr_put32(sc, IPW2100_CSR_CTL, tmp | IPW2100_CTL_INIT); 1300bb5e3b2fSeh146360 1301bb5e3b2fSeh146360 return (DDI_SUCCESS); 1302bb5e3b2fSeh146360 } 1303bb5e3b2fSeh146360 1304bb5e3b2fSeh146360 /* 1305bb5e3b2fSeh146360 * get the radio status from IPW_CSR_IO, invoked by wificonfig/dladm 1306bb5e3b2fSeh146360 */ 1307bb5e3b2fSeh146360 int 1308bb5e3b2fSeh146360 ipw2100_get_radio(struct ipw2100_softc *sc) 1309bb5e3b2fSeh146360 { 1310bb5e3b2fSeh146360 if (ipw2100_csr_get32(sc, IPW2100_CSR_IO) & IPW2100_IO_RADIO_DISABLED) 1311bb5e3b2fSeh146360 return (0); 1312bb5e3b2fSeh146360 else 1313bb5e3b2fSeh146360 return (1); 1314bb5e3b2fSeh146360 1315bb5e3b2fSeh146360 } 1316bb5e3b2fSeh146360 /* 1317bb5e3b2fSeh146360 * This function is used to get the statistic, invoked by wificonfig/dladm 1318bb5e3b2fSeh146360 */ 1319bb5e3b2fSeh146360 void 1320bb5e3b2fSeh146360 ipw2100_get_statistics(struct ipw2100_softc *sc) 1321bb5e3b2fSeh146360 { 1322bb5e3b2fSeh146360 struct ieee80211com *ic = &sc->sc_ic; 1323bb5e3b2fSeh146360 uint32_t addr, size, i; 1324bb5e3b2fSeh146360 uint32_t atbl[256], *datatbl; 1325bb5e3b2fSeh146360 1326bb5e3b2fSeh146360 datatbl = atbl; 1327bb5e3b2fSeh146360 1328bb5e3b2fSeh146360 if (!(sc->sc_flags & IPW2100_FLAG_FW_INITED)) { 1329bb5e3b2fSeh146360 IPW2100_DBG(IPW2100_DBG_STATISTIC, (sc->sc_dip, CE_CONT, 1330bb5e3b2fSeh146360 "ipw2100_get_statistic(): fw doesn't download yet.")); 1331bb5e3b2fSeh146360 return; 1332bb5e3b2fSeh146360 } 1333bb5e3b2fSeh146360 1334bb5e3b2fSeh146360 ipw2100_csr_put32(sc, IPW2100_CSR_AUTOINC_ADDR, sc->sc_table1_base); 1335bb5e3b2fSeh146360 1336bb5e3b2fSeh146360 size = ipw2100_csr_get32(sc, IPW2100_CSR_AUTOINC_DATA); 1337bb5e3b2fSeh146360 atbl[0] = size; 1338bb5e3b2fSeh146360 for (i = 1, ++datatbl; i < size; i++, datatbl++) { 1339bb5e3b2fSeh146360 addr = ipw2100_csr_get32(sc, IPW2100_CSR_AUTOINC_DATA); 1340bb5e3b2fSeh146360 *datatbl = ipw2100_imem_get32(sc, addr); 1341bb5e3b2fSeh146360 } 1342bb5e3b2fSeh146360 1343bb5e3b2fSeh146360 /* 1344bb5e3b2fSeh146360 * To retrieve the statistic information into proper places. There are 1345bb5e3b2fSeh146360 * lot of information. 1346bb5e3b2fSeh146360 */ 1347bb5e3b2fSeh146360 IPW2100_DBG(IPW2100_DBG_STATISTIC, (sc->sc_dip, CE_CONT, 1348bb5e3b2fSeh146360 "ipw2100_get_statistic(): \n" 1349bb5e3b2fSeh146360 "operating mode = %u\n" 1350bb5e3b2fSeh146360 "type of authentification= %u\n" 1351bb5e3b2fSeh146360 "average RSSI= %u\n" 1352bb5e3b2fSeh146360 "current channel = %d\n", 1353bb5e3b2fSeh146360 atbl[191], atbl[199], atbl[173], atbl[189])); 1354bb5e3b2fSeh146360 /* WIFI_STAT_TX_FRAGS */ 1355bb5e3b2fSeh146360 ic->ic_stats.is_tx_frags = (uint32_t)atbl[2]; 1356bb5e3b2fSeh146360 /* WIFI_STAT_MCAST_TX = (all frame - unicast frame) */ 1357bb5e3b2fSeh146360 ic->ic_stats.is_tx_mcast = (uint32_t)atbl[2] - (uint32_t)atbl[3]; 1358bb5e3b2fSeh146360 /* WIFI_STAT_TX_RETRANS */ 1359bb5e3b2fSeh146360 ic->ic_stats.is_tx_retries = (uint32_t)atbl[42]; 1360bb5e3b2fSeh146360 /* WIFI_STAT_TX_FAILED */ 1361bb5e3b2fSeh146360 ic->ic_stats.is_tx_failed = (uint32_t)atbl[51]; 1362bb5e3b2fSeh146360 /* MAC_STAT_OBYTES */ 1363bb5e3b2fSeh146360 ic->ic_stats.is_tx_bytes = (uint32_t)atbl[41]; 1364bb5e3b2fSeh146360 /* WIFI_STAT_RX_FRAGS */ 1365bb5e3b2fSeh146360 ic->ic_stats.is_rx_frags = (uint32_t)atbl[61]; 1366bb5e3b2fSeh146360 /* WIFI_STAT_MCAST_RX */ 1367bb5e3b2fSeh146360 ic->ic_stats.is_rx_mcast = (uint32_t)atbl[71]; 1368bb5e3b2fSeh146360 /* MAC_STAT_IBYTES */ 1369bb5e3b2fSeh146360 ic->ic_stats.is_rx_bytes = (uint32_t)atbl[101]; 1370bb5e3b2fSeh146360 /* WIFI_STAT_ACK_FAILURE */ 1371bb5e3b2fSeh146360 ic->ic_stats.is_ack_failure = (uint32_t)atbl[59]; 1372bb5e3b2fSeh146360 /* WIFI_STAT_RTS_SUCCESS */ 1373bb5e3b2fSeh146360 ic->ic_stats.is_rts_success = (uint32_t)atbl[22]; 1374bb5e3b2fSeh146360 } 1375bb5e3b2fSeh146360 1376bb5e3b2fSeh146360 /* 1377bb5e3b2fSeh146360 * dma region alloc 1378bb5e3b2fSeh146360 */ 1379bb5e3b2fSeh146360 static int 1380bb5e3b2fSeh146360 ipw2100_dma_region_alloc(struct ipw2100_softc *sc, 1381bb5e3b2fSeh146360 struct dma_region *dr, size_t size, uint_t dir, uint_t flags) 1382bb5e3b2fSeh146360 { 1383bb5e3b2fSeh146360 dev_info_t *dip = sc->sc_dip; 1384bb5e3b2fSeh146360 int err; 1385bb5e3b2fSeh146360 1386bb5e3b2fSeh146360 IPW2100_DBG(IPW2100_DBG_DMA, (dip, CE_CONT, 1387bb5e3b2fSeh146360 "ipw2100_dma_region_alloc() name=%s size=%u\n", 1388bb5e3b2fSeh146360 dr->dr_name, size)); 1389bb5e3b2fSeh146360 1390bb5e3b2fSeh146360 err = ddi_dma_alloc_handle(dip, &ipw2100_dma_attr, DDI_DMA_SLEEP, NULL, 1391bb5e3b2fSeh146360 &dr->dr_hnd); 1392bb5e3b2fSeh146360 if (err != DDI_SUCCESS) { 1393bb5e3b2fSeh146360 IPW2100_DBG(IPW2100_DBG_DMA, (dip, CE_CONT, 1394bb5e3b2fSeh146360 "ipw2100_dma_region_alloc(): " 1395bb5e3b2fSeh146360 "ddi_dma_alloc_handle() failed\n")); 1396bb5e3b2fSeh146360 goto fail0; 1397bb5e3b2fSeh146360 } 1398bb5e3b2fSeh146360 1399bb5e3b2fSeh146360 err = ddi_dma_mem_alloc(dr->dr_hnd, size, &ipw2100_dma_accattr, 1400bb5e3b2fSeh146360 flags, DDI_DMA_SLEEP, NULL, &dr->dr_base, 1401bb5e3b2fSeh146360 &dr->dr_size, &dr->dr_acc); 1402bb5e3b2fSeh146360 if (err != DDI_SUCCESS) { 1403bb5e3b2fSeh146360 IPW2100_DBG(IPW2100_DBG_DMA, (dip, CE_CONT, 1404bb5e3b2fSeh146360 "ipw2100_dma_region_alloc(): " 1405bb5e3b2fSeh146360 "ddi_dma_mem_alloc() failed\n")); 1406bb5e3b2fSeh146360 goto fail1; 1407bb5e3b2fSeh146360 } 1408bb5e3b2fSeh146360 1409bb5e3b2fSeh146360 err = ddi_dma_addr_bind_handle(dr->dr_hnd, NULL, 1410bb5e3b2fSeh146360 dr->dr_base, dr->dr_size, dir | flags, DDI_DMA_SLEEP, NULL, 1411bb5e3b2fSeh146360 &dr->dr_cookie, &dr->dr_ccnt); 1412bb5e3b2fSeh146360 if (err != DDI_DMA_MAPPED) { 1413bb5e3b2fSeh146360 IPW2100_DBG(IPW2100_DBG_DMA, (dip, CE_CONT, 1414bb5e3b2fSeh146360 "ipw2100_dma_region_alloc(): " 1415bb5e3b2fSeh146360 "ddi_dma_addr_bind_handle() failed\n")); 1416bb5e3b2fSeh146360 goto fail2; 1417bb5e3b2fSeh146360 } 1418bb5e3b2fSeh146360 1419bb5e3b2fSeh146360 if (dr->dr_ccnt != 1) { 1420bb5e3b2fSeh146360 err = DDI_FAILURE; 1421bb5e3b2fSeh146360 goto fail3; 1422bb5e3b2fSeh146360 } 1423bb5e3b2fSeh146360 dr->dr_pbase = dr->dr_cookie.dmac_address; 1424bb5e3b2fSeh146360 1425bb5e3b2fSeh146360 IPW2100_DBG(IPW2100_DBG_DMA, (dip, CE_CONT, 1426bb5e3b2fSeh146360 "ipw2100_dma_region_alloc(): get physical-base=0x%08x\n", 1427bb5e3b2fSeh146360 dr->dr_pbase)); 1428bb5e3b2fSeh146360 1429bb5e3b2fSeh146360 return (DDI_SUCCESS); 1430bb5e3b2fSeh146360 1431bb5e3b2fSeh146360 fail3: 1432bb5e3b2fSeh146360 (void) ddi_dma_unbind_handle(dr->dr_hnd); 1433bb5e3b2fSeh146360 fail2: 1434bb5e3b2fSeh146360 ddi_dma_mem_free(&dr->dr_acc); 1435bb5e3b2fSeh146360 fail1: 1436bb5e3b2fSeh146360 ddi_dma_free_handle(&dr->dr_hnd); 1437bb5e3b2fSeh146360 fail0: 1438bb5e3b2fSeh146360 return (err); 1439bb5e3b2fSeh146360 } 1440bb5e3b2fSeh146360 1441bb5e3b2fSeh146360 static void 1442bb5e3b2fSeh146360 ipw2100_dma_region_free(struct dma_region *dr) 1443bb5e3b2fSeh146360 { 1444bb5e3b2fSeh146360 (void) ddi_dma_unbind_handle(dr->dr_hnd); 1445bb5e3b2fSeh146360 ddi_dma_mem_free(&dr->dr_acc); 1446bb5e3b2fSeh146360 ddi_dma_free_handle(&dr->dr_hnd); 1447bb5e3b2fSeh146360 } 1448bb5e3b2fSeh146360 1449bb5e3b2fSeh146360 static int 1450bb5e3b2fSeh146360 ipw2100_ring_alloc(struct ipw2100_softc *sc) 1451bb5e3b2fSeh146360 { 1452bb5e3b2fSeh146360 int err, i; 1453bb5e3b2fSeh146360 1454bb5e3b2fSeh146360 /* 1455bb5e3b2fSeh146360 * tx ring 1456bb5e3b2fSeh146360 */ 1457bb5e3b2fSeh146360 sc->sc_dma_txbd.dr_name = "ipw2100-tx-ring-bd"; 1458bb5e3b2fSeh146360 err = ipw2100_dma_region_alloc(sc, &sc->sc_dma_txbd, 1459bb5e3b2fSeh146360 IPW2100_TXBD_SIZE, DDI_DMA_WRITE, DDI_DMA_CONSISTENT); 1460bb5e3b2fSeh146360 if (err != DDI_SUCCESS) 1461bb5e3b2fSeh146360 goto fail0; 1462bb5e3b2fSeh146360 /* 1463bb5e3b2fSeh146360 * tx bufs 1464bb5e3b2fSeh146360 */ 1465bb5e3b2fSeh146360 for (i = 0; i < IPW2100_NUM_TXBUF; i++) { 1466bb5e3b2fSeh146360 sc->sc_dma_txbufs[i].dr_name = "ipw2100-tx-buf"; 1467bb5e3b2fSeh146360 err = ipw2100_dma_region_alloc(sc, &sc->sc_dma_txbufs[i], 1468bb5e3b2fSeh146360 IPW2100_TXBUF_SIZE, DDI_DMA_WRITE, DDI_DMA_STREAMING); 1469bb5e3b2fSeh146360 if (err != DDI_SUCCESS) { 1470e5a7a30bSpengcheng chen - Sun Microsystems - Beijing China while (i > 0) { 1471bb5e3b2fSeh146360 i--; 1472e5a7a30bSpengcheng chen - Sun Microsystems - Beijing China ipw2100_dma_region_free(&sc->sc_dma_txbufs[i]); 1473bb5e3b2fSeh146360 } 1474bb5e3b2fSeh146360 goto fail1; 1475bb5e3b2fSeh146360 } 1476bb5e3b2fSeh146360 } 1477bb5e3b2fSeh146360 /* 1478bb5e3b2fSeh146360 * rx ring 1479bb5e3b2fSeh146360 */ 1480bb5e3b2fSeh146360 sc->sc_dma_rxbd.dr_name = "ipw2100-rx-ring-bd"; 1481bb5e3b2fSeh146360 err = ipw2100_dma_region_alloc(sc, &sc->sc_dma_rxbd, 1482bb5e3b2fSeh146360 IPW2100_RXBD_SIZE, DDI_DMA_WRITE, DDI_DMA_CONSISTENT); 1483bb5e3b2fSeh146360 if (err != DDI_SUCCESS) 1484bb5e3b2fSeh146360 goto fail2; 1485bb5e3b2fSeh146360 /* 1486bb5e3b2fSeh146360 * rx bufs 1487bb5e3b2fSeh146360 */ 1488bb5e3b2fSeh146360 for (i = 0; i < IPW2100_NUM_RXBUF; i++) { 1489bb5e3b2fSeh146360 sc->sc_dma_rxbufs[i].dr_name = "ipw2100-rx-buf"; 1490bb5e3b2fSeh146360 err = ipw2100_dma_region_alloc(sc, &sc->sc_dma_rxbufs[i], 1491bb5e3b2fSeh146360 IPW2100_RXBUF_SIZE, DDI_DMA_READ, DDI_DMA_STREAMING); 1492bb5e3b2fSeh146360 if (err != DDI_SUCCESS) { 1493e5a7a30bSpengcheng chen - Sun Microsystems - Beijing China while (i > 0) { 1494bb5e3b2fSeh146360 i--; 1495e5a7a30bSpengcheng chen - Sun Microsystems - Beijing China ipw2100_dma_region_free(&sc->sc_dma_rxbufs[i]); 1496bb5e3b2fSeh146360 } 1497bb5e3b2fSeh146360 goto fail3; 1498bb5e3b2fSeh146360 } 1499bb5e3b2fSeh146360 } 1500bb5e3b2fSeh146360 /* 1501bb5e3b2fSeh146360 * status 1502bb5e3b2fSeh146360 */ 1503bb5e3b2fSeh146360 sc->sc_dma_status.dr_name = "ipw2100-rx-status"; 1504bb5e3b2fSeh146360 err = ipw2100_dma_region_alloc(sc, &sc->sc_dma_status, 1505bb5e3b2fSeh146360 IPW2100_STATUS_SIZE, DDI_DMA_READ, DDI_DMA_CONSISTENT); 1506bb5e3b2fSeh146360 if (err != DDI_SUCCESS) 1507bb5e3b2fSeh146360 goto fail4; 1508bb5e3b2fSeh146360 /* 1509bb5e3b2fSeh146360 * command 1510bb5e3b2fSeh146360 */ 1511bb5e3b2fSeh146360 sc->sc_dma_cmd.dr_name = "ipw2100-cmd"; 1512bb5e3b2fSeh146360 err = ipw2100_dma_region_alloc(sc, &sc->sc_dma_cmd, IPW2100_CMD_SIZE, 1513bb5e3b2fSeh146360 DDI_DMA_WRITE, DDI_DMA_CONSISTENT); 1514bb5e3b2fSeh146360 if (err != DDI_SUCCESS) 1515bb5e3b2fSeh146360 goto fail5; 1516bb5e3b2fSeh146360 1517bb5e3b2fSeh146360 return (DDI_SUCCESS); 1518bb5e3b2fSeh146360 1519bb5e3b2fSeh146360 fail5: 1520bb5e3b2fSeh146360 ipw2100_dma_region_free(&sc->sc_dma_status); 1521bb5e3b2fSeh146360 fail4: 1522bb5e3b2fSeh146360 for (i = 0; i < IPW2100_NUM_RXBUF; i++) 1523bb5e3b2fSeh146360 ipw2100_dma_region_free(&sc->sc_dma_rxbufs[i]); 1524bb5e3b2fSeh146360 fail3: 1525bb5e3b2fSeh146360 ipw2100_dma_region_free(&sc->sc_dma_rxbd); 1526bb5e3b2fSeh146360 fail2: 1527bb5e3b2fSeh146360 for (i = 0; i < IPW2100_NUM_TXBUF; i++) 1528bb5e3b2fSeh146360 ipw2100_dma_region_free(&sc->sc_dma_txbufs[i]); 1529bb5e3b2fSeh146360 fail1: 1530bb5e3b2fSeh146360 ipw2100_dma_region_free(&sc->sc_dma_txbd); 1531bb5e3b2fSeh146360 fail0: 1532bb5e3b2fSeh146360 return (err); 1533bb5e3b2fSeh146360 } 1534bb5e3b2fSeh146360 1535bb5e3b2fSeh146360 static void 1536bb5e3b2fSeh146360 ipw2100_ring_free(struct ipw2100_softc *sc) 1537bb5e3b2fSeh146360 { 1538bb5e3b2fSeh146360 int i; 1539bb5e3b2fSeh146360 1540bb5e3b2fSeh146360 /* 1541bb5e3b2fSeh146360 * tx ring 1542bb5e3b2fSeh146360 */ 1543bb5e3b2fSeh146360 ipw2100_dma_region_free(&sc->sc_dma_txbd); 1544bb5e3b2fSeh146360 /* 1545bb5e3b2fSeh146360 * tx buf 1546bb5e3b2fSeh146360 */ 1547bb5e3b2fSeh146360 for (i = 0; i < IPW2100_NUM_TXBUF; i++) 1548bb5e3b2fSeh146360 ipw2100_dma_region_free(&sc->sc_dma_txbufs[i]); 1549bb5e3b2fSeh146360 /* 1550bb5e3b2fSeh146360 * rx ring 1551bb5e3b2fSeh146360 */ 1552bb5e3b2fSeh146360 ipw2100_dma_region_free(&sc->sc_dma_rxbd); 1553bb5e3b2fSeh146360 /* 1554bb5e3b2fSeh146360 * rx buf 1555bb5e3b2fSeh146360 */ 1556bb5e3b2fSeh146360 for (i = 0; i < IPW2100_NUM_RXBUF; i++) 1557bb5e3b2fSeh146360 ipw2100_dma_region_free(&sc->sc_dma_rxbufs[i]); 1558bb5e3b2fSeh146360 /* 1559bb5e3b2fSeh146360 * status 1560bb5e3b2fSeh146360 */ 1561bb5e3b2fSeh146360 ipw2100_dma_region_free(&sc->sc_dma_status); 1562bb5e3b2fSeh146360 /* 1563bb5e3b2fSeh146360 * command 1564bb5e3b2fSeh146360 */ 1565bb5e3b2fSeh146360 ipw2100_dma_region_free(&sc->sc_dma_cmd); 1566bb5e3b2fSeh146360 } 1567bb5e3b2fSeh146360 1568bb5e3b2fSeh146360 static void 1569bb5e3b2fSeh146360 ipw2100_ring_reset(struct ipw2100_softc *sc) 1570bb5e3b2fSeh146360 { 1571bb5e3b2fSeh146360 int i; 1572bb5e3b2fSeh146360 1573bb5e3b2fSeh146360 /* 1574bb5e3b2fSeh146360 * tx ring 1575bb5e3b2fSeh146360 */ 1576bb5e3b2fSeh146360 sc->sc_tx_cur = 0; 1577bb5e3b2fSeh146360 sc->sc_tx_free = IPW2100_NUM_TXBD; 1578bb5e3b2fSeh146360 sc->sc_txbd = (struct ipw2100_bd *)sc->sc_dma_txbd.dr_base; 1579bb5e3b2fSeh146360 for (i = 0; i < IPW2100_NUM_TXBUF; i++) 1580bb5e3b2fSeh146360 sc->sc_txbufs[i] = 1581bb5e3b2fSeh146360 (struct ipw2100_txb *)sc->sc_dma_txbufs[i].dr_base; 1582bb5e3b2fSeh146360 /* 1583bb5e3b2fSeh146360 * rx ring 1584bb5e3b2fSeh146360 */ 1585bb5e3b2fSeh146360 sc->sc_rx_cur = 0; 1586bb5e3b2fSeh146360 sc->sc_rx_free = IPW2100_NUM_RXBD; 1587bb5e3b2fSeh146360 sc->sc_status = (struct ipw2100_status *)sc->sc_dma_status.dr_base; 1588bb5e3b2fSeh146360 sc->sc_rxbd = (struct ipw2100_bd *)sc->sc_dma_rxbd.dr_base; 1589bb5e3b2fSeh146360 for (i = 0; i < IPW2100_NUM_RXBUF; i++) { 1590bb5e3b2fSeh146360 sc->sc_rxbufs[i] = 1591bb5e3b2fSeh146360 (struct ipw2100_rxb *)sc->sc_dma_rxbufs[i].dr_base; 1592bb5e3b2fSeh146360 /* 1593bb5e3b2fSeh146360 * initialize Rx buffer descriptors, both host and device 1594bb5e3b2fSeh146360 */ 1595bb5e3b2fSeh146360 sc->sc_rxbd[i].phyaddr = LE_32(sc->sc_dma_rxbufs[i].dr_pbase); 1596bb5e3b2fSeh146360 sc->sc_rxbd[i].len = LE_32(sc->sc_dma_rxbufs[i].dr_size); 1597bb5e3b2fSeh146360 sc->sc_rxbd[i].flags = 0; 1598bb5e3b2fSeh146360 sc->sc_rxbd[i].nfrag = 1; 1599bb5e3b2fSeh146360 } 1600bb5e3b2fSeh146360 /* 1601bb5e3b2fSeh146360 * command 1602bb5e3b2fSeh146360 */ 1603bb5e3b2fSeh146360 sc->sc_cmd = (struct ipw2100_cmd *)sc->sc_dma_cmd.dr_base; 1604bb5e3b2fSeh146360 } 1605bb5e3b2fSeh146360 1606bb5e3b2fSeh146360 /* 1607bb5e3b2fSeh146360 * tx, rx rings and command initialization 1608bb5e3b2fSeh146360 */ 1609bb5e3b2fSeh146360 static int 1610bb5e3b2fSeh146360 ipw2100_ring_init(struct ipw2100_softc *sc) 1611bb5e3b2fSeh146360 { 1612bb5e3b2fSeh146360 int err; 1613bb5e3b2fSeh146360 1614bb5e3b2fSeh146360 err = ipw2100_ring_alloc(sc); 1615bb5e3b2fSeh146360 if (err != DDI_SUCCESS) 1616bb5e3b2fSeh146360 return (err); 1617bb5e3b2fSeh146360 1618bb5e3b2fSeh146360 ipw2100_ring_reset(sc); 1619bb5e3b2fSeh146360 1620bb5e3b2fSeh146360 return (DDI_SUCCESS); 1621bb5e3b2fSeh146360 } 1622bb5e3b2fSeh146360 1623bb5e3b2fSeh146360 static void 1624bb5e3b2fSeh146360 ipw2100_ring_hwsetup(struct ipw2100_softc *sc) 1625bb5e3b2fSeh146360 { 1626bb5e3b2fSeh146360 ipw2100_ring_reset(sc); 1627bb5e3b2fSeh146360 /* 1628bb5e3b2fSeh146360 * tx ring 1629bb5e3b2fSeh146360 */ 1630bb5e3b2fSeh146360 ipw2100_csr_put32(sc, IPW2100_CSR_TX_BD_BASE, sc->sc_dma_txbd.dr_pbase); 1631bb5e3b2fSeh146360 ipw2100_csr_put32(sc, IPW2100_CSR_TX_BD_SIZE, IPW2100_NUM_TXBD); 1632bb5e3b2fSeh146360 /* 1633bb5e3b2fSeh146360 * no new packet to transmit, tx-rd-index == tx-wr-index 1634bb5e3b2fSeh146360 */ 1635bb5e3b2fSeh146360 ipw2100_csr_put32(sc, IPW2100_CSR_TX_READ_INDEX, sc->sc_tx_cur); 1636bb5e3b2fSeh146360 ipw2100_csr_put32(sc, IPW2100_CSR_TX_WRITE_INDEX, sc->sc_tx_cur); 1637bb5e3b2fSeh146360 /* 1638bb5e3b2fSeh146360 * rx ring 1639bb5e3b2fSeh146360 */ 1640bb5e3b2fSeh146360 ipw2100_csr_put32(sc, IPW2100_CSR_RX_BD_BASE, sc->sc_dma_rxbd.dr_pbase); 1641bb5e3b2fSeh146360 ipw2100_csr_put32(sc, IPW2100_CSR_RX_BD_SIZE, IPW2100_NUM_RXBD); 1642bb5e3b2fSeh146360 /* 1643bb5e3b2fSeh146360 * all rx buffer are empty, rx-rd-index == 0 && rx-wr-index == N-1 1644bb5e3b2fSeh146360 */ 1645bb5e3b2fSeh146360 IPW2100_DBG(IPW2100_DBG_RING, (sc->sc_dip, CE_CONT, 1646bb5e3b2fSeh146360 "ipw2100_ring_hwsetup(): rx-cur=%u, backward=%u\n", 1647bb5e3b2fSeh146360 sc->sc_rx_cur, RING_BACKWARD(sc->sc_rx_cur, 1, IPW2100_NUM_RXBD))); 1648bb5e3b2fSeh146360 ipw2100_csr_put32(sc, IPW2100_CSR_RX_READ_INDEX, sc->sc_rx_cur); 1649bb5e3b2fSeh146360 ipw2100_csr_put32(sc, IPW2100_CSR_RX_WRITE_INDEX, 1650bb5e3b2fSeh146360 RING_BACKWARD(sc->sc_rx_cur, 1, IPW2100_NUM_RXBD)); 1651bb5e3b2fSeh146360 /* 1652bb5e3b2fSeh146360 * status 1653bb5e3b2fSeh146360 */ 1654bb5e3b2fSeh146360 ipw2100_csr_put32(sc, IPW2100_CSR_RX_STATUS_BASE, 1655bb5e3b2fSeh146360 sc->sc_dma_status.dr_pbase); 1656bb5e3b2fSeh146360 } 1657bb5e3b2fSeh146360 1658bb5e3b2fSeh146360 /* 1659bb5e3b2fSeh146360 * ieee80211_new_state() is not be used, since the hardware can handle the 1660bb5e3b2fSeh146360 * state transfer. Here, we just keep the status of the hardware notification 1661bb5e3b2fSeh146360 * result. 1662bb5e3b2fSeh146360 */ 1663bb5e3b2fSeh146360 /* ARGSUSED */ 1664bb5e3b2fSeh146360 static int 1665bb5e3b2fSeh146360 ipw2100_newstate(struct ieee80211com *ic, enum ieee80211_state state, int arg) 1666bb5e3b2fSeh146360 { 1667bb5e3b2fSeh146360 struct ipw2100_softc *sc = (struct ipw2100_softc *)ic; 1668bb5e3b2fSeh146360 struct ieee80211_node *in; 1669bb5e3b2fSeh146360 uint8_t macaddr[IEEE80211_ADDR_LEN]; 1670bb5e3b2fSeh146360 uint32_t len; 1671bb5e3b2fSeh146360 wifi_data_t wd = { 0 }; 1672bb5e3b2fSeh146360 1673bb5e3b2fSeh146360 IPW2100_DBG(IPW2100_DBG_WIFI, (sc->sc_dip, CE_CONT, 1674bb5e3b2fSeh146360 "ipw2100_newstate(): %s -> %s\n", 1675bb5e3b2fSeh146360 ieee80211_state_name[ic->ic_state], ieee80211_state_name[state])); 1676bb5e3b2fSeh146360 1677bb5e3b2fSeh146360 switch (state) { 1678bb5e3b2fSeh146360 case IEEE80211_S_RUN: 1679bb5e3b2fSeh146360 /* 1680bb5e3b2fSeh146360 * we only need to use BSSID as to find the node 1681bb5e3b2fSeh146360 */ 1682bb5e3b2fSeh146360 drv_usecwait(200); /* firmware needs a short delay here */ 1683bb5e3b2fSeh146360 len = IEEE80211_ADDR_LEN; 1684bb5e3b2fSeh146360 (void) ipw2100_table2_getbuf(sc, IPW2100_INFO_CURRENT_BSSID, 1685bb5e3b2fSeh146360 macaddr, &len); 1686bb5e3b2fSeh146360 1687bb5e3b2fSeh146360 in = ieee80211_find_node(&ic->ic_scan, macaddr); 1688bb5e3b2fSeh146360 if (in == NULL) 1689bb5e3b2fSeh146360 break; 1690bb5e3b2fSeh146360 1691bb5e3b2fSeh146360 (void) ieee80211_sta_join(ic, in); 1692bb5e3b2fSeh146360 ieee80211_node_authorize(in); 1693bb5e3b2fSeh146360 1694bb5e3b2fSeh146360 /* 1695bb5e3b2fSeh146360 * We can send data now; update the fastpath with our 1696bb5e3b2fSeh146360 * current associated BSSID. 1697bb5e3b2fSeh146360 */ 1698bb5e3b2fSeh146360 if (ic->ic_flags & IEEE80211_F_PRIVACY) 1699bb5e3b2fSeh146360 wd.wd_secalloc = WIFI_SEC_WEP; 1700bb5e3b2fSeh146360 else 1701bb5e3b2fSeh146360 wd.wd_secalloc = WIFI_SEC_NONE; 1702bb5e3b2fSeh146360 wd.wd_opmode = ic->ic_opmode; 1703bb5e3b2fSeh146360 IEEE80211_ADDR_COPY(wd.wd_bssid, ic->ic_bss->in_bssid); 1704bb5e3b2fSeh146360 (void) mac_pdata_update(ic->ic_mach, &wd, sizeof (wd)); 1705bb5e3b2fSeh146360 1706bb5e3b2fSeh146360 break; 1707bb5e3b2fSeh146360 1708bb5e3b2fSeh146360 case IEEE80211_S_INIT: 1709bb5e3b2fSeh146360 case IEEE80211_S_SCAN: 1710bb5e3b2fSeh146360 case IEEE80211_S_AUTH: 1711bb5e3b2fSeh146360 case IEEE80211_S_ASSOC: 1712bb5e3b2fSeh146360 break; 1713bb5e3b2fSeh146360 } 1714bb5e3b2fSeh146360 1715bb5e3b2fSeh146360 /* 1716bb5e3b2fSeh146360 * notify to update the link 1717bb5e3b2fSeh146360 */ 1718bb5e3b2fSeh146360 if ((ic->ic_state != IEEE80211_S_RUN) && (state == IEEE80211_S_RUN)) { 1719bb5e3b2fSeh146360 /* 1720bb5e3b2fSeh146360 * previously disconnected and now connected 1721bb5e3b2fSeh146360 */ 1722bb5e3b2fSeh146360 sc->sc_linkstate = LINK_STATE_UP; 1723bb5e3b2fSeh146360 sc->sc_flags |= IPW2100_FLAG_LINK_CHANGE; 1724bb5e3b2fSeh146360 } else if ((ic->ic_state == IEEE80211_S_RUN) && 1725bb5e3b2fSeh146360 (state != IEEE80211_S_RUN)) { 1726bb5e3b2fSeh146360 /* 1727bb5e3b2fSeh146360 * previously connected andd now disconnected 1728bb5e3b2fSeh146360 */ 1729bb5e3b2fSeh146360 sc->sc_linkstate = LINK_STATE_DOWN; 1730bb5e3b2fSeh146360 sc->sc_flags |= IPW2100_FLAG_LINK_CHANGE; 1731bb5e3b2fSeh146360 } 1732bb5e3b2fSeh146360 1733bb5e3b2fSeh146360 ic->ic_state = state; 1734bb5e3b2fSeh146360 return (DDI_SUCCESS); 1735bb5e3b2fSeh146360 } 1736bb5e3b2fSeh146360 1737bb5e3b2fSeh146360 /* 1738bb5e3b2fSeh146360 * GLD operations 1739bb5e3b2fSeh146360 */ 1740bb5e3b2fSeh146360 /* ARGSUSED */ 1741bb5e3b2fSeh146360 static int 1742bb5e3b2fSeh146360 ipw2100_m_stat(void *arg, uint_t stat, uint64_t *val) 1743bb5e3b2fSeh146360 { 1744bb5e3b2fSeh146360 ieee80211com_t *ic = (ieee80211com_t *)arg; 1745bb5e3b2fSeh146360 IPW2100_DBG(IPW2100_DBG_GLD, (((struct ipw2100_softc *)arg)->sc_dip, 1746bb5e3b2fSeh146360 CE_CONT, 1747bb5e3b2fSeh146360 "ipw2100_m_stat(): enter\n")); 1748bb5e3b2fSeh146360 /* 1749bb5e3b2fSeh146360 * some of below statistic data are from hardware, some from net80211 1750bb5e3b2fSeh146360 */ 1751bb5e3b2fSeh146360 switch (stat) { 1752bb5e3b2fSeh146360 case MAC_STAT_RBYTES: 1753bb5e3b2fSeh146360 *val = ic->ic_stats.is_rx_bytes; 1754bb5e3b2fSeh146360 break; 1755bb5e3b2fSeh146360 case MAC_STAT_IPACKETS: 1756bb5e3b2fSeh146360 *val = ic->ic_stats.is_rx_frags; 1757bb5e3b2fSeh146360 break; 1758bb5e3b2fSeh146360 case MAC_STAT_OBYTES: 1759bb5e3b2fSeh146360 *val = ic->ic_stats.is_tx_bytes; 1760bb5e3b2fSeh146360 break; 1761bb5e3b2fSeh146360 case MAC_STAT_OPACKETS: 1762bb5e3b2fSeh146360 *val = ic->ic_stats.is_tx_frags; 1763bb5e3b2fSeh146360 break; 1764bb5e3b2fSeh146360 /* 1765bb5e3b2fSeh146360 * Get below from hardware statistic, retrieve net80211 value once 1s 1766bb5e3b2fSeh146360 */ 1767bb5e3b2fSeh146360 case WIFI_STAT_TX_FRAGS: 1768bb5e3b2fSeh146360 case WIFI_STAT_MCAST_TX: 1769bb5e3b2fSeh146360 case WIFI_STAT_TX_FAILED: 1770bb5e3b2fSeh146360 case WIFI_STAT_TX_RETRANS: 1771bb5e3b2fSeh146360 case WIFI_STAT_RTS_SUCCESS: 1772bb5e3b2fSeh146360 case WIFI_STAT_ACK_FAILURE: 1773bb5e3b2fSeh146360 case WIFI_STAT_RX_FRAGS: 1774bb5e3b2fSeh146360 case WIFI_STAT_MCAST_RX: 1775bb5e3b2fSeh146360 /* 1776bb5e3b2fSeh146360 * Get blow information from net80211 1777bb5e3b2fSeh146360 */ 1778bb5e3b2fSeh146360 case WIFI_STAT_RTS_FAILURE: 1779bb5e3b2fSeh146360 case WIFI_STAT_RX_DUPS: 1780bb5e3b2fSeh146360 case WIFI_STAT_FCS_ERRORS: 1781bb5e3b2fSeh146360 case WIFI_STAT_WEP_ERRORS: 1782bb5e3b2fSeh146360 return (ieee80211_stat(ic, stat, val)); 1783bb5e3b2fSeh146360 /* 1784bb5e3b2fSeh146360 * need be supported in the future 1785bb5e3b2fSeh146360 */ 1786bb5e3b2fSeh146360 case MAC_STAT_IFSPEED: 1787bb5e3b2fSeh146360 case MAC_STAT_NOXMTBUF: 1788bb5e3b2fSeh146360 case MAC_STAT_IERRORS: 1789bb5e3b2fSeh146360 case MAC_STAT_OERRORS: 1790bb5e3b2fSeh146360 default: 1791bb5e3b2fSeh146360 return (ENOTSUP); 1792bb5e3b2fSeh146360 } 1793bb5e3b2fSeh146360 return (0); 1794bb5e3b2fSeh146360 } 1795bb5e3b2fSeh146360 1796bb5e3b2fSeh146360 /* ARGSUSED */ 1797bb5e3b2fSeh146360 static int 1798bb5e3b2fSeh146360 ipw2100_m_multicst(void *arg, boolean_t add, const uint8_t *mca) 1799bb5e3b2fSeh146360 { 1800bb5e3b2fSeh146360 /* not supported */ 1801bb5e3b2fSeh146360 IPW2100_DBG(IPW2100_DBG_GLD, (((struct ipw2100_softc *)arg)->sc_dip, 1802bb5e3b2fSeh146360 CE_CONT, 1803bb5e3b2fSeh146360 "ipw2100_m_multicst(): enter\n")); 1804bb5e3b2fSeh146360 1805922d2c76Seh146360 return (0); 1806bb5e3b2fSeh146360 } 1807bb5e3b2fSeh146360 1808bb5e3b2fSeh146360 /* 1809bb5e3b2fSeh146360 * This thread function is used to handle the fatal error. 1810bb5e3b2fSeh146360 */ 1811bb5e3b2fSeh146360 static void 1812bb5e3b2fSeh146360 ipw2100_thread(struct ipw2100_softc *sc) 1813bb5e3b2fSeh146360 { 1814bb5e3b2fSeh146360 struct ieee80211com *ic = &sc->sc_ic; 1815bb5e3b2fSeh146360 int32_t nlstate; 1816bb5e3b2fSeh146360 int stat_cnt = 0; 1817bb5e3b2fSeh146360 1818bb5e3b2fSeh146360 IPW2100_DBG(IPW2100_DBG_SOFTINT, (sc->sc_dip, CE_CONT, 1819bb5e3b2fSeh146360 "ipw2100_thread(): into ipw2100 thread--> %d\n", 1820bb5e3b2fSeh146360 sc->sc_linkstate)); 1821bb5e3b2fSeh146360 1822bb5e3b2fSeh146360 mutex_enter(&sc->sc_mflock); 1823bb5e3b2fSeh146360 1824bb5e3b2fSeh146360 while (sc->sc_mfthread_switch) { 1825bb5e3b2fSeh146360 /* 1826bb5e3b2fSeh146360 * notify the link state 1827bb5e3b2fSeh146360 */ 1828bb5e3b2fSeh146360 if (ic->ic_mach && (sc->sc_flags & IPW2100_FLAG_LINK_CHANGE)) { 1829bb5e3b2fSeh146360 IPW2100_DBG(IPW2100_DBG_SOFTINT, (sc->sc_dip, CE_CONT, 1830bb5e3b2fSeh146360 "ipw2100_thread(): link status --> %d\n", 1831bb5e3b2fSeh146360 sc->sc_linkstate)); 1832bb5e3b2fSeh146360 1833bb5e3b2fSeh146360 sc->sc_flags &= ~IPW2100_FLAG_LINK_CHANGE; 1834bb5e3b2fSeh146360 nlstate = sc->sc_linkstate; 1835bb5e3b2fSeh146360 1836bb5e3b2fSeh146360 mutex_exit(&sc->sc_mflock); 1837bb5e3b2fSeh146360 mac_link_update(ic->ic_mach, nlstate); 1838bb5e3b2fSeh146360 mutex_enter(&sc->sc_mflock); 1839bb5e3b2fSeh146360 } 1840bb5e3b2fSeh146360 1841bb5e3b2fSeh146360 /* 1842bb5e3b2fSeh146360 * recovery interrupt fatal error 1843bb5e3b2fSeh146360 */ 1844bb5e3b2fSeh146360 if (ic->ic_mach && 1845bb5e3b2fSeh146360 (sc->sc_flags & IPW2100_FLAG_HW_ERR_RECOVER)) { 1846bb5e3b2fSeh146360 1847bb5e3b2fSeh146360 IPW2100_DBG(IPW2100_DBG_FATAL, (sc->sc_dip, CE_CONT, 1848bb5e3b2fSeh146360 "try to recover fatal hw error\n")); 1849bb5e3b2fSeh146360 sc->sc_flags &= ~IPW2100_FLAG_HW_ERR_RECOVER; 1850bb5e3b2fSeh146360 1851bb5e3b2fSeh146360 mutex_exit(&sc->sc_mflock); 1852bb5e3b2fSeh146360 (void) ipw2100_init(sc); /* Force stat machine */ 1853bb5e3b2fSeh146360 delay(drv_usectohz(delay_fatal_recover)); 1854bb5e3b2fSeh146360 mutex_enter(&sc->sc_mflock); 1855bb5e3b2fSeh146360 } 1856bb5e3b2fSeh146360 1857bb5e3b2fSeh146360 /* 1858bb5e3b2fSeh146360 * get statistic, the value will be retrieved by m_stat 1859bb5e3b2fSeh146360 */ 1860bb5e3b2fSeh146360 if (stat_cnt == 10) { 1861bb5e3b2fSeh146360 stat_cnt = 0; /* re-start */ 1862bb5e3b2fSeh146360 1863bb5e3b2fSeh146360 mutex_exit(&sc->sc_mflock); 1864bb5e3b2fSeh146360 ipw2100_get_statistics(sc); 1865bb5e3b2fSeh146360 mutex_enter(&sc->sc_mflock); 1866bb5e3b2fSeh146360 } else 1867bb5e3b2fSeh146360 stat_cnt++; /* until 1s */ 1868bb5e3b2fSeh146360 1869bb5e3b2fSeh146360 mutex_exit(&sc->sc_mflock); 1870bb5e3b2fSeh146360 delay(drv_usectohz(delay_aux_thread)); 1871bb5e3b2fSeh146360 mutex_enter(&sc->sc_mflock); 1872bb5e3b2fSeh146360 } 1873bb5e3b2fSeh146360 sc->sc_mf_thread = NULL; 1874bb5e3b2fSeh146360 cv_broadcast(&sc->sc_mfthread_cv); 1875bb5e3b2fSeh146360 mutex_exit(&sc->sc_mflock); 1876bb5e3b2fSeh146360 } 1877bb5e3b2fSeh146360 1878bb5e3b2fSeh146360 static int 1879bb5e3b2fSeh146360 ipw2100_m_start(void *arg) 1880bb5e3b2fSeh146360 { 1881bb5e3b2fSeh146360 struct ipw2100_softc *sc = (struct ipw2100_softc *)arg; 1882bb5e3b2fSeh146360 1883bb5e3b2fSeh146360 IPW2100_DBG(IPW2100_DBG_GLD, (sc->sc_dip, CE_CONT, 1884bb5e3b2fSeh146360 "ipw2100_m_start(): enter\n")); 1885bb5e3b2fSeh146360 1886bb5e3b2fSeh146360 /* 1887bb5e3b2fSeh146360 * initialize ipw2100 hardware 1888bb5e3b2fSeh146360 */ 1889bb5e3b2fSeh146360 (void) ipw2100_init(sc); 1890bb5e3b2fSeh146360 1891bb5e3b2fSeh146360 sc->sc_flags |= IPW2100_FLAG_RUNNING; 1892bb5e3b2fSeh146360 /* 1893bb5e3b2fSeh146360 * fix KCF bug. - workaround, need to fix it in net80211 1894bb5e3b2fSeh146360 */ 1895bb5e3b2fSeh146360 (void) crypto_mech2id(SUN_CKM_RC4); 1896bb5e3b2fSeh146360 1897922d2c76Seh146360 return (0); 1898bb5e3b2fSeh146360 } 1899bb5e3b2fSeh146360 1900bb5e3b2fSeh146360 static void 1901bb5e3b2fSeh146360 ipw2100_m_stop(void *arg) 1902bb5e3b2fSeh146360 { 1903bb5e3b2fSeh146360 struct ipw2100_softc *sc = (struct ipw2100_softc *)arg; 1904bb5e3b2fSeh146360 1905bb5e3b2fSeh146360 IPW2100_DBG(IPW2100_DBG_GLD, (sc->sc_dip, CE_CONT, 1906bb5e3b2fSeh146360 "ipw2100_m_stop(): enter\n")); 1907bb5e3b2fSeh146360 1908bb5e3b2fSeh146360 ipw2100_stop(sc); 1909bb5e3b2fSeh146360 1910bb5e3b2fSeh146360 sc->sc_flags &= ~IPW2100_FLAG_RUNNING; 1911bb5e3b2fSeh146360 } 1912bb5e3b2fSeh146360 1913bb5e3b2fSeh146360 static int 1914bb5e3b2fSeh146360 ipw2100_m_unicst(void *arg, const uint8_t *macaddr) 1915bb5e3b2fSeh146360 { 1916bb5e3b2fSeh146360 struct ipw2100_softc *sc = (struct ipw2100_softc *)arg; 1917bb5e3b2fSeh146360 struct ieee80211com *ic = &sc->sc_ic; 1918bb5e3b2fSeh146360 int err; 1919bb5e3b2fSeh146360 1920bb5e3b2fSeh146360 IPW2100_DBG(IPW2100_DBG_GLD, (sc->sc_dip, CE_CONT, 1921bb5e3b2fSeh146360 "ipw2100_m_unicst(): enter\n")); 1922bb5e3b2fSeh146360 1923bb5e3b2fSeh146360 IPW2100_DBG(IPW2100_DBG_GLD, (sc->sc_dip, CE_CONT, 1924bb5e3b2fSeh146360 "ipw2100_m_unicst(): GLD setting MAC address to " 1925bb5e3b2fSeh146360 "%02x:%02x:%02x:%02x:%02x:%02x\n", 1926bb5e3b2fSeh146360 macaddr[0], macaddr[1], macaddr[2], 1927bb5e3b2fSeh146360 macaddr[3], macaddr[4], macaddr[5])); 1928bb5e3b2fSeh146360 1929bb5e3b2fSeh146360 if (!IEEE80211_ADDR_EQ(ic->ic_macaddr, macaddr)) { 1930bb5e3b2fSeh146360 IEEE80211_ADDR_COPY(ic->ic_macaddr, macaddr); 1931bb5e3b2fSeh146360 1932bb5e3b2fSeh146360 if (sc->sc_flags & IPW2100_FLAG_RUNNING) { 1933bb5e3b2fSeh146360 err = ipw2100_config(sc); 1934bb5e3b2fSeh146360 if (err != DDI_SUCCESS) { 1935bb5e3b2fSeh146360 IPW2100_WARN((sc->sc_dip, CE_WARN, 1936bb5e3b2fSeh146360 "ipw2100_m_unicst(): " 1937bb5e3b2fSeh146360 "device configuration failed\n")); 1938bb5e3b2fSeh146360 goto fail; 1939bb5e3b2fSeh146360 } 1940bb5e3b2fSeh146360 } 1941bb5e3b2fSeh146360 } 1942bb5e3b2fSeh146360 1943922d2c76Seh146360 return (0); 1944bb5e3b2fSeh146360 fail: 1945922d2c76Seh146360 return (EIO); 1946bb5e3b2fSeh146360 } 1947bb5e3b2fSeh146360 1948bb5e3b2fSeh146360 static int 1949bb5e3b2fSeh146360 ipw2100_m_promisc(void *arg, boolean_t on) 1950bb5e3b2fSeh146360 { 1951bb5e3b2fSeh146360 struct ipw2100_softc *sc = (struct ipw2100_softc *)arg; 1952bb5e3b2fSeh146360 int recfg, err; 1953bb5e3b2fSeh146360 1954bb5e3b2fSeh146360 IPW2100_DBG(IPW2100_DBG_GLD, (sc->sc_dip, CE_CONT, 1955bb5e3b2fSeh146360 "ipw2100_m_promisc(): enter. " 1956bb5e3b2fSeh146360 "GLD setting promiscuous mode - %d\n", on)); 1957bb5e3b2fSeh146360 1958bb5e3b2fSeh146360 recfg = 0; 1959bb5e3b2fSeh146360 if (on) 1960bb5e3b2fSeh146360 if (!(sc->if_flags & IFF_PROMISC)) { 1961bb5e3b2fSeh146360 sc->if_flags |= IFF_PROMISC; 1962bb5e3b2fSeh146360 recfg = 1; 1963bb5e3b2fSeh146360 } 1964bb5e3b2fSeh146360 else 1965bb5e3b2fSeh146360 if (sc->if_flags & IFF_PROMISC) { 1966bb5e3b2fSeh146360 sc->if_flags &= ~IFF_PROMISC; 1967bb5e3b2fSeh146360 recfg = 1; 1968bb5e3b2fSeh146360 } 1969bb5e3b2fSeh146360 1970bb5e3b2fSeh146360 if (recfg && (sc->sc_flags & IPW2100_FLAG_RUNNING)) { 1971bb5e3b2fSeh146360 err = ipw2100_config(sc); 1972bb5e3b2fSeh146360 if (err != DDI_SUCCESS) { 1973bb5e3b2fSeh146360 IPW2100_WARN((sc->sc_dip, CE_WARN, 1974bb5e3b2fSeh146360 "ipw2100_m_promisc(): " 1975bb5e3b2fSeh146360 "device configuration failed\n")); 1976bb5e3b2fSeh146360 goto fail; 1977bb5e3b2fSeh146360 } 1978bb5e3b2fSeh146360 } 1979bb5e3b2fSeh146360 1980922d2c76Seh146360 return (0); 1981bb5e3b2fSeh146360 fail: 1982922d2c76Seh146360 return (EIO); 1983bb5e3b2fSeh146360 } 1984bb5e3b2fSeh146360 1985bb5e3b2fSeh146360 static mblk_t * 1986bb5e3b2fSeh146360 ipw2100_m_tx(void *arg, mblk_t *mp) 1987bb5e3b2fSeh146360 { 1988bb5e3b2fSeh146360 struct ipw2100_softc *sc = (struct ipw2100_softc *)arg; 1989bb5e3b2fSeh146360 struct ieee80211com *ic = &sc->sc_ic; 1990bb5e3b2fSeh146360 mblk_t *next; 1991bb5e3b2fSeh146360 1992bb5e3b2fSeh146360 /* 1993bb5e3b2fSeh146360 * No data frames go out unless we're associated; this 1994bb5e3b2fSeh146360 * should not happen as the 802.11 layer does not enable 1995bb5e3b2fSeh146360 * the xmit queue until we enter the RUN state. 1996bb5e3b2fSeh146360 */ 1997bb5e3b2fSeh146360 if (ic->ic_state != IEEE80211_S_RUN) { 1998bb5e3b2fSeh146360 IPW2100_DBG(IPW2100_DBG_GLD, (sc->sc_dip, CE_CONT, 1999bb5e3b2fSeh146360 "ipw2100_m_tx(): discard msg, ic_state = %u\n", 2000bb5e3b2fSeh146360 ic->ic_state)); 2001bb5e3b2fSeh146360 freemsgchain(mp); 2002bb5e3b2fSeh146360 return (NULL); 2003bb5e3b2fSeh146360 } 2004bb5e3b2fSeh146360 2005bb5e3b2fSeh146360 while (mp != NULL) { 2006bb5e3b2fSeh146360 next = mp->b_next; 2007bb5e3b2fSeh146360 mp->b_next = NULL; 2008bb5e3b2fSeh146360 if (ipw2100_send(ic, mp, IEEE80211_FC0_TYPE_DATA) != 2009bb5e3b2fSeh146360 DDI_SUCCESS) { 2010bb5e3b2fSeh146360 mp->b_next = next; 2011bb5e3b2fSeh146360 break; 2012bb5e3b2fSeh146360 } 2013bb5e3b2fSeh146360 mp = next; 2014bb5e3b2fSeh146360 } 2015bb5e3b2fSeh146360 return (mp); 2016bb5e3b2fSeh146360 } 2017bb5e3b2fSeh146360 2018bb5e3b2fSeh146360 /* ARGSUSED */ 2019bb5e3b2fSeh146360 static int 2020bb5e3b2fSeh146360 ipw2100_send(ieee80211com_t *ic, mblk_t *mp, uint8_t type) 2021bb5e3b2fSeh146360 { 2022bb5e3b2fSeh146360 struct ipw2100_softc *sc = (struct ipw2100_softc *)ic; 2023bb5e3b2fSeh146360 struct ieee80211_node *in; 2024bb5e3b2fSeh146360 struct ieee80211_frame wh, *wh_tmp; 2025bb5e3b2fSeh146360 struct ieee80211_key *k; 2026bb5e3b2fSeh146360 uint8_t *hdat; 2027bb5e3b2fSeh146360 mblk_t *m0, *m; 2028bb5e3b2fSeh146360 size_t cnt, off; 2029bb5e3b2fSeh146360 struct ipw2100_bd *txbd[2]; 2030bb5e3b2fSeh146360 struct ipw2100_txb *txbuf; 2031bb5e3b2fSeh146360 struct dma_region *dr; 2032bb5e3b2fSeh146360 struct ipw2100_hdr *h; 2033bb5e3b2fSeh146360 uint32_t idx, bidx; 2034bb5e3b2fSeh146360 int err; 2035bb5e3b2fSeh146360 2036bb5e3b2fSeh146360 ASSERT(mp->b_next == NULL); 2037bb5e3b2fSeh146360 2038bb5e3b2fSeh146360 m0 = NULL; 2039bb5e3b2fSeh146360 m = NULL; 2040bb5e3b2fSeh146360 err = DDI_SUCCESS; 2041bb5e3b2fSeh146360 2042bb5e3b2fSeh146360 IPW2100_DBG(IPW2100_DBG_GLD, (sc->sc_dip, CE_CONT, 2043bb5e3b2fSeh146360 "ipw2100_send(): enter\n")); 2044bb5e3b2fSeh146360 2045bb5e3b2fSeh146360 if ((type & IEEE80211_FC0_TYPE_MASK) != IEEE80211_FC0_TYPE_DATA) { 2046bb5e3b2fSeh146360 /* 2047bb5e3b2fSeh146360 * it is impossible to send non-data 802.11 frame in current 2048bb5e3b2fSeh146360 * ipw driver. Therefore, drop the package 2049bb5e3b2fSeh146360 */ 2050bb5e3b2fSeh146360 freemsg(mp); 2051bb5e3b2fSeh146360 err = DDI_SUCCESS; 2052bb5e3b2fSeh146360 goto fail0; 2053bb5e3b2fSeh146360 } 2054bb5e3b2fSeh146360 2055bb5e3b2fSeh146360 mutex_enter(&sc->sc_tx_lock); 2056bb5e3b2fSeh146360 2057bb5e3b2fSeh146360 /* 2058bb5e3b2fSeh146360 * need 2 descriptors: 1 for SEND cmd parameter header, 2059bb5e3b2fSeh146360 * and the other for payload, i.e., 802.11 frame including 802.11 2060bb5e3b2fSeh146360 * frame header 2061bb5e3b2fSeh146360 */ 2062bb5e3b2fSeh146360 if (sc->sc_tx_free < 2) { 2063bb5e3b2fSeh146360 mutex_enter(&sc->sc_resched_lock); 2064bb5e3b2fSeh146360 IPW2100_DBG(IPW2100_DBG_RING, (sc->sc_dip, CE_WARN, 2065bb5e3b2fSeh146360 "ipw2100_send(): no enough descriptors(%d)\n", 2066bb5e3b2fSeh146360 sc->sc_tx_free)); 2067bb5e3b2fSeh146360 ic->ic_stats.is_tx_nobuf++; /* no enough buffer */ 2068bb5e3b2fSeh146360 sc->sc_flags |= IPW2100_FLAG_TX_SCHED; 2069bb5e3b2fSeh146360 err = DDI_FAILURE; 2070bb5e3b2fSeh146360 mutex_exit(&sc->sc_resched_lock); 2071bb5e3b2fSeh146360 goto fail1; 2072bb5e3b2fSeh146360 } 2073bb5e3b2fSeh146360 IPW2100_DBG(IPW2100_DBG_RING, (sc->sc_dip, CE_CONT, 2074bb5e3b2fSeh146360 "ipw2100_send(): tx-free=%d,tx-curr=%d\n", 2075bb5e3b2fSeh146360 sc->sc_tx_free, sc->sc_tx_cur)); 2076bb5e3b2fSeh146360 2077bb5e3b2fSeh146360 wh_tmp = (struct ieee80211_frame *)mp->b_rptr; 2078bb5e3b2fSeh146360 in = ieee80211_find_txnode(ic, wh_tmp->i_addr1); 2079bb5e3b2fSeh146360 if (in == NULL) { /* can not find tx node, drop the package */ 2080bb5e3b2fSeh146360 freemsg(mp); 2081bb5e3b2fSeh146360 err = DDI_SUCCESS; 2082bb5e3b2fSeh146360 goto fail1; 2083bb5e3b2fSeh146360 } 2084bb5e3b2fSeh146360 in->in_inact = 0; 2085bb5e3b2fSeh146360 (void) ieee80211_encap(ic, mp, in); 2086bb5e3b2fSeh146360 ieee80211_free_node(in); 2087bb5e3b2fSeh146360 2088bb5e3b2fSeh146360 if (wh_tmp->i_fc[1] & IEEE80211_FC1_WEP) { 2089bb5e3b2fSeh146360 /* 2090bb5e3b2fSeh146360 * it is very bad that ieee80211_crypto_encap can only accept a 2091bb5e3b2fSeh146360 * single continuous buffer. 2092bb5e3b2fSeh146360 */ 2093bb5e3b2fSeh146360 /* 2094bb5e3b2fSeh146360 * allocate 32 more bytes is to be compatible with further 2095bb5e3b2fSeh146360 * ieee802.11i standard. 2096bb5e3b2fSeh146360 */ 2097bb5e3b2fSeh146360 m = allocb(msgdsize(mp) + 32, BPRI_MED); 2098bb5e3b2fSeh146360 if (m == NULL) { /* can not alloc buf, drop this package */ 2099bb5e3b2fSeh146360 IPW2100_DBG(IPW2100_DBG_WIFI, (sc->sc_dip, CE_CONT, 2100bb5e3b2fSeh146360 "ipw2100_send(): msg allocation failed\n")); 2101bb5e3b2fSeh146360 2102bb5e3b2fSeh146360 freemsg(mp); 2103bb5e3b2fSeh146360 2104bb5e3b2fSeh146360 err = DDI_SUCCESS; 2105bb5e3b2fSeh146360 goto fail1; 2106bb5e3b2fSeh146360 } 2107bb5e3b2fSeh146360 off = 0; 2108bb5e3b2fSeh146360 m0 = mp; 2109bb5e3b2fSeh146360 while (m0) { 2110bb5e3b2fSeh146360 cnt = MBLKL(m0); 2111bb5e3b2fSeh146360 if (cnt) { 2112bb5e3b2fSeh146360 (void) memcpy(m->b_rptr + off, m0->b_rptr, cnt); 2113bb5e3b2fSeh146360 off += cnt; 2114bb5e3b2fSeh146360 } 2115bb5e3b2fSeh146360 m0 = m0->b_cont; 2116bb5e3b2fSeh146360 } 2117bb5e3b2fSeh146360 m->b_wptr += off; 2118bb5e3b2fSeh146360 IPW2100_DBG(IPW2100_DBG_WIFI, (sc->sc_dip, CE_CONT, 2119bb5e3b2fSeh146360 "ipw2100_send(): " 2120bb5e3b2fSeh146360 "Encrypting 802.11 frame started, %d, %d\n", 2121bb5e3b2fSeh146360 msgdsize(mp), MBLKL(mp))); 2122bb5e3b2fSeh146360 k = ieee80211_crypto_encap(ic, m); 2123bb5e3b2fSeh146360 if (k == NULL) { /* can not get the key, drop packages */ 2124bb5e3b2fSeh146360 IPW2100_DBG(IPW2100_DBG_WIFI, (sc->sc_dip, CE_CONT, 2125bb5e3b2fSeh146360 "ipw2100_send(): " 2126bb5e3b2fSeh146360 "Encrypting 802.11 frame failed\n")); 2127bb5e3b2fSeh146360 2128bb5e3b2fSeh146360 freemsg(mp); 2129bb5e3b2fSeh146360 err = DDI_SUCCESS; 2130bb5e3b2fSeh146360 goto fail2; 2131bb5e3b2fSeh146360 } 2132bb5e3b2fSeh146360 IPW2100_DBG(IPW2100_DBG_WIFI, (sc->sc_dip, CE_CONT, 2133bb5e3b2fSeh146360 "ipw2100_send(): " 2134bb5e3b2fSeh146360 "Encrypting 802.11 frame finished, %d, %d, k=0x%08x\n", 2135bb5e3b2fSeh146360 msgdsize(mp), MBLKL(mp), k->wk_flags)); 2136bb5e3b2fSeh146360 } 2137bb5e3b2fSeh146360 2138bb5e3b2fSeh146360 /* 2139bb5e3b2fSeh146360 * header descriptor 2140bb5e3b2fSeh146360 */ 2141bb5e3b2fSeh146360 idx = sc->sc_tx_cur; 2142bb5e3b2fSeh146360 txbd[0] = &sc->sc_txbd[idx]; 2143bb5e3b2fSeh146360 if ((idx & 1) == 0) 2144bb5e3b2fSeh146360 bidx = idx / 2; 2145bb5e3b2fSeh146360 sc->sc_tx_cur = RING_FORWARD(sc->sc_tx_cur, 1, IPW2100_NUM_TXBD); 2146bb5e3b2fSeh146360 sc->sc_tx_free--; 2147bb5e3b2fSeh146360 2148bb5e3b2fSeh146360 /* 2149bb5e3b2fSeh146360 * payload descriptor 2150bb5e3b2fSeh146360 */ 2151bb5e3b2fSeh146360 idx = sc->sc_tx_cur; 2152bb5e3b2fSeh146360 txbd[1] = &sc->sc_txbd[idx]; 2153bb5e3b2fSeh146360 if ((idx & 1) == 0) 2154bb5e3b2fSeh146360 bidx = idx / 2; 2155bb5e3b2fSeh146360 sc->sc_tx_cur = RING_FORWARD(sc->sc_tx_cur, 1, IPW2100_NUM_TXBD); 2156bb5e3b2fSeh146360 sc->sc_tx_free--; 2157bb5e3b2fSeh146360 2158bb5e3b2fSeh146360 /* 2159bb5e3b2fSeh146360 * one buffer, SEND cmd header and payload buffer 2160bb5e3b2fSeh146360 */ 2161bb5e3b2fSeh146360 txbuf = sc->sc_txbufs[bidx]; 2162bb5e3b2fSeh146360 dr = &sc->sc_dma_txbufs[bidx]; 2163bb5e3b2fSeh146360 2164bb5e3b2fSeh146360 /* 2165bb5e3b2fSeh146360 * extract 802.11 header from message, fill wh from m0 2166bb5e3b2fSeh146360 */ 2167bb5e3b2fSeh146360 hdat = (uint8_t *)&wh; 2168bb5e3b2fSeh146360 off = 0; 2169bb5e3b2fSeh146360 if (m) 2170bb5e3b2fSeh146360 m0 = m; 2171bb5e3b2fSeh146360 else 2172bb5e3b2fSeh146360 m0 = mp; 2173bb5e3b2fSeh146360 while (off < sizeof (wh)) { 2174bb5e3b2fSeh146360 cnt = MBLKL(m0); 2175bb5e3b2fSeh146360 if (cnt > (sizeof (wh) - off)) 2176bb5e3b2fSeh146360 cnt = sizeof (wh) - off; 2177bb5e3b2fSeh146360 if (cnt) { 2178bb5e3b2fSeh146360 (void) memcpy(hdat + off, m0->b_rptr, cnt); 2179bb5e3b2fSeh146360 off += cnt; 2180bb5e3b2fSeh146360 m0->b_rptr += cnt; 2181bb5e3b2fSeh146360 } 2182bb5e3b2fSeh146360 else 2183bb5e3b2fSeh146360 m0 = m0->b_cont; 2184bb5e3b2fSeh146360 } 2185bb5e3b2fSeh146360 2186bb5e3b2fSeh146360 /* 2187bb5e3b2fSeh146360 * prepare SEND cmd header 2188bb5e3b2fSeh146360 */ 2189bb5e3b2fSeh146360 h = &txbuf->txb_hdr; 2190bb5e3b2fSeh146360 h->type = LE_32(IPW2100_CMD_SEND); 2191bb5e3b2fSeh146360 h->subtype = LE_32(0); 2192bb5e3b2fSeh146360 h->encrypted = ic->ic_flags & IEEE80211_F_PRIVACY ? 1 : 0; 2193bb5e3b2fSeh146360 h->encrypt = 0; 2194bb5e3b2fSeh146360 h->keyidx = 0; 2195bb5e3b2fSeh146360 h->keysz = 0; 2196bb5e3b2fSeh146360 h->fragsz = LE_16(0); 2197bb5e3b2fSeh146360 IEEE80211_ADDR_COPY(h->saddr, wh.i_addr2); 2198bb5e3b2fSeh146360 if (ic->ic_opmode == IEEE80211_M_STA) 2199bb5e3b2fSeh146360 IEEE80211_ADDR_COPY(h->daddr, wh.i_addr3); 2200bb5e3b2fSeh146360 else 2201bb5e3b2fSeh146360 IEEE80211_ADDR_COPY(h->daddr, wh.i_addr1); 2202bb5e3b2fSeh146360 2203bb5e3b2fSeh146360 /* 2204bb5e3b2fSeh146360 * extract payload from message into tx data buffer 2205bb5e3b2fSeh146360 */ 2206bb5e3b2fSeh146360 off = 0; 2207bb5e3b2fSeh146360 while (m0) { 2208bb5e3b2fSeh146360 cnt = MBLKL(m0); 2209bb5e3b2fSeh146360 if (cnt) { 2210bb5e3b2fSeh146360 (void) memcpy(&txbuf->txb_dat[off], m0->b_rptr, cnt); 2211bb5e3b2fSeh146360 off += cnt; 2212bb5e3b2fSeh146360 } 2213bb5e3b2fSeh146360 m0 = m0->b_cont; 2214bb5e3b2fSeh146360 } 2215bb5e3b2fSeh146360 2216bb5e3b2fSeh146360 /* 2217bb5e3b2fSeh146360 * fill SEND cmd header descriptor 2218bb5e3b2fSeh146360 */ 2219bb5e3b2fSeh146360 txbd[0]->phyaddr = LE_32(dr->dr_pbase + 2220bb5e3b2fSeh146360 OFFSETOF(struct ipw2100_txb, txb_hdr)); 2221bb5e3b2fSeh146360 txbd[0]->len = LE_32(sizeof (struct ipw2100_hdr)); 2222bb5e3b2fSeh146360 txbd[0]->flags = IPW2100_BD_FLAG_TX_FRAME_802_3 | 2223bb5e3b2fSeh146360 IPW2100_BD_FLAG_TX_NOT_LAST_FRAGMENT; 2224bb5e3b2fSeh146360 txbd[0]->nfrag = 2; 2225bb5e3b2fSeh146360 /* 2226bb5e3b2fSeh146360 * fill payload descriptor 2227bb5e3b2fSeh146360 */ 2228bb5e3b2fSeh146360 txbd[1]->phyaddr = LE_32(dr->dr_pbase + 2229bb5e3b2fSeh146360 OFFSETOF(struct ipw2100_txb, txb_dat[0])); 2230bb5e3b2fSeh146360 txbd[1]->len = LE_32(off); 2231bb5e3b2fSeh146360 txbd[1]->flags = IPW2100_BD_FLAG_TX_FRAME_802_3 | 2232bb5e3b2fSeh146360 IPW2100_BD_FLAG_TX_LAST_FRAGMENT; 2233bb5e3b2fSeh146360 txbd[1]->nfrag = 0; 2234bb5e3b2fSeh146360 2235bb5e3b2fSeh146360 /* 2236bb5e3b2fSeh146360 * dma sync 2237bb5e3b2fSeh146360 */ 2238bb5e3b2fSeh146360 (void) ddi_dma_sync(dr->dr_hnd, 0, sizeof (struct ipw2100_txb), 2239bb5e3b2fSeh146360 DDI_DMA_SYNC_FORDEV); 2240bb5e3b2fSeh146360 (void) ddi_dma_sync(sc->sc_dma_txbd.dr_hnd, 2241bb5e3b2fSeh146360 (txbd[0] - sc->sc_txbd) * sizeof (struct ipw2100_bd), 2242bb5e3b2fSeh146360 sizeof (struct ipw2100_bd), DDI_DMA_SYNC_FORDEV); 2243bb5e3b2fSeh146360 /* 2244bb5e3b2fSeh146360 * since txbd[1] may not be successive to txbd[0] due to the ring 2245bb5e3b2fSeh146360 * organization, another dma_sync is needed to simplify the logic 2246bb5e3b2fSeh146360 */ 2247bb5e3b2fSeh146360 (void) ddi_dma_sync(sc->sc_dma_txbd.dr_hnd, 2248bb5e3b2fSeh146360 (txbd[1] - sc->sc_txbd) * sizeof (struct ipw2100_bd), 2249bb5e3b2fSeh146360 sizeof (struct ipw2100_bd), DDI_DMA_SYNC_FORDEV); 2250bb5e3b2fSeh146360 /* 2251bb5e3b2fSeh146360 * update txcur 2252bb5e3b2fSeh146360 */ 2253bb5e3b2fSeh146360 ipw2100_csr_put32(sc, IPW2100_CSR_TX_WRITE_INDEX, sc->sc_tx_cur); 2254bb5e3b2fSeh146360 2255bb5e3b2fSeh146360 if (mp) /* success, free the original message */ 2256bb5e3b2fSeh146360 freemsg(mp); 2257bb5e3b2fSeh146360 fail2: 2258bb5e3b2fSeh146360 if (m) 2259bb5e3b2fSeh146360 freemsg(m); 2260bb5e3b2fSeh146360 fail1: 2261bb5e3b2fSeh146360 mutex_exit(&sc->sc_tx_lock); 2262bb5e3b2fSeh146360 fail0: 2263bb5e3b2fSeh146360 IPW2100_DBG(IPW2100_DBG_GLD, (sc->sc_dip, CE_CONT, 2264bb5e3b2fSeh146360 "ipw2100_send(): exit - err=%d\n", err)); 2265bb5e3b2fSeh146360 2266bb5e3b2fSeh146360 return (err); 2267bb5e3b2fSeh146360 } 2268bb5e3b2fSeh146360 2269bb5e3b2fSeh146360 /* 2270bb5e3b2fSeh146360 * IOCTL Handler 2271bb5e3b2fSeh146360 */ 2272bb5e3b2fSeh146360 #define IEEE80211_IOCTL_REQUIRED (1) 2273bb5e3b2fSeh146360 #define IEEE80211_IOCTL_NOT_REQUIRED (0) 2274bb5e3b2fSeh146360 static void 2275bb5e3b2fSeh146360 ipw2100_m_ioctl(void *arg, queue_t *q, mblk_t *m) 2276bb5e3b2fSeh146360 { 2277bb5e3b2fSeh146360 struct ipw2100_softc *sc = (struct ipw2100_softc *)arg; 2278bb5e3b2fSeh146360 struct ieee80211com *ic = &sc->sc_ic; 2279bb5e3b2fSeh146360 int err; 2280bb5e3b2fSeh146360 2281bb5e3b2fSeh146360 IPW2100_DBG(IPW2100_DBG_GLD, (sc->sc_dip, CE_CONT, 2282bb5e3b2fSeh146360 "ipw2100_m_ioctl(): enter\n")); 2283bb5e3b2fSeh146360 2284bb5e3b2fSeh146360 /* 2285bb5e3b2fSeh146360 * check whether or not need to handle this in net80211 2286bb5e3b2fSeh146360 */ 2287bb5e3b2fSeh146360 if (ipw2100_ioctl(sc, q, m) == IEEE80211_IOCTL_NOT_REQUIRED) 2288bb5e3b2fSeh146360 return; /* succes or fail */ 2289bb5e3b2fSeh146360 2290bb5e3b2fSeh146360 err = ieee80211_ioctl(ic, q, m); 2291bb5e3b2fSeh146360 if (err == ENETRESET) { 2292bb5e3b2fSeh146360 if (sc->sc_flags & IPW2100_FLAG_RUNNING) { 2293bb5e3b2fSeh146360 (void) ipw2100_m_start(sc); 2294bb5e3b2fSeh146360 (void) ieee80211_new_state(ic, 2295bb5e3b2fSeh146360 IEEE80211_S_SCAN, -1); 2296bb5e3b2fSeh146360 } 2297bb5e3b2fSeh146360 } 2298bb5e3b2fSeh146360 if (err == ERESTART) { 2299bb5e3b2fSeh146360 if (sc->sc_flags & IPW2100_FLAG_RUNNING) 2300bb5e3b2fSeh146360 (void) ipw2100_chip_reset(sc); 2301bb5e3b2fSeh146360 } 2302bb5e3b2fSeh146360 } 2303bb5e3b2fSeh146360 2304bb5e3b2fSeh146360 static int 2305bb5e3b2fSeh146360 ipw2100_ioctl(struct ipw2100_softc *sc, queue_t *q, mblk_t *m) 2306bb5e3b2fSeh146360 { 2307bb5e3b2fSeh146360 struct iocblk *iocp; 2308bb5e3b2fSeh146360 uint32_t len, ret, cmd; 2309bb5e3b2fSeh146360 mblk_t *m0; 2310bb5e3b2fSeh146360 boolean_t need_privilege; 2311bb5e3b2fSeh146360 boolean_t need_net80211; 2312bb5e3b2fSeh146360 2313bb5e3b2fSeh146360 if (MBLKL(m) < sizeof (struct iocblk)) { 2314bb5e3b2fSeh146360 IPW2100_DBG(IPW2100_DBG_IOCTL, (sc->sc_dip, CE_CONT, 2315bb5e3b2fSeh146360 "ipw2100_ioctl(): ioctl buffer too short, %u\n", 2316bb5e3b2fSeh146360 MBLKL(m))); 2317bb5e3b2fSeh146360 miocnak(q, m, 0, EINVAL); 2318bb5e3b2fSeh146360 return (IEEE80211_IOCTL_NOT_REQUIRED); 2319bb5e3b2fSeh146360 } 2320bb5e3b2fSeh146360 2321bb5e3b2fSeh146360 /* 2322bb5e3b2fSeh146360 * Validate the command 2323bb5e3b2fSeh146360 */ 2324922d2c76Seh146360 iocp = (struct iocblk *)(uintptr_t)m->b_rptr; 2325bb5e3b2fSeh146360 iocp->ioc_error = 0; 2326bb5e3b2fSeh146360 cmd = iocp->ioc_cmd; 2327bb5e3b2fSeh146360 need_privilege = B_TRUE; 2328bb5e3b2fSeh146360 switch (cmd) { 2329bb5e3b2fSeh146360 case WLAN_SET_PARAM: 2330bb5e3b2fSeh146360 case WLAN_COMMAND: 2331bb5e3b2fSeh146360 break; 2332bb5e3b2fSeh146360 case WLAN_GET_PARAM: 2333bb5e3b2fSeh146360 need_privilege = B_FALSE; 2334bb5e3b2fSeh146360 break; 2335bb5e3b2fSeh146360 default: 2336bb5e3b2fSeh146360 IPW2100_DBG(IPW2100_DBG_IOCTL, (sc->sc_dip, CE_CONT, 2337bb5e3b2fSeh146360 "ieee80211_ioctl(): unknown cmd 0x%x", cmd)); 2338bb5e3b2fSeh146360 miocnak(q, m, 0, EINVAL); 2339bb5e3b2fSeh146360 return (IEEE80211_IOCTL_NOT_REQUIRED); 2340bb5e3b2fSeh146360 } 2341bb5e3b2fSeh146360 2342eae72b5bSSebastien Roy if (need_privilege && (ret = secpolicy_dl_config(iocp->ioc_cr)) != 0) { 2343eae72b5bSSebastien Roy miocnak(q, m, 0, ret); 2344bb5e3b2fSeh146360 return (IEEE80211_IOCTL_NOT_REQUIRED); 2345bb5e3b2fSeh146360 } 2346eae72b5bSSebastien Roy 2347bb5e3b2fSeh146360 /* 2348bb5e3b2fSeh146360 * sanity check 2349bb5e3b2fSeh146360 */ 2350bb5e3b2fSeh146360 m0 = m->b_cont; 2351bb5e3b2fSeh146360 if (iocp->ioc_count == 0 || iocp->ioc_count < sizeof (wldp_t) || 2352bb5e3b2fSeh146360 m0 == NULL) { 2353bb5e3b2fSeh146360 miocnak(q, m, 0, EINVAL); 2354bb5e3b2fSeh146360 return (IEEE80211_IOCTL_NOT_REQUIRED); 2355bb5e3b2fSeh146360 } 2356bb5e3b2fSeh146360 /* 2357bb5e3b2fSeh146360 * assuming single data block 2358bb5e3b2fSeh146360 */ 2359bb5e3b2fSeh146360 if (m0->b_cont) { 2360bb5e3b2fSeh146360 freemsg(m0->b_cont); 2361bb5e3b2fSeh146360 m0->b_cont = NULL; 2362bb5e3b2fSeh146360 } 2363bb5e3b2fSeh146360 2364bb5e3b2fSeh146360 need_net80211 = B_FALSE; 2365bb5e3b2fSeh146360 ret = ipw2100_getset(sc, m0, cmd, &need_net80211); 2366bb5e3b2fSeh146360 if (!need_net80211) { 2367bb5e3b2fSeh146360 len = msgdsize(m0); 2368bb5e3b2fSeh146360 2369bb5e3b2fSeh146360 IPW2100_DBG(IPW2100_DBG_IOCTL, (sc->sc_dip, CE_CONT, 2370bb5e3b2fSeh146360 "ipw2100_ioctl(): go to call miocack with " 2371bb5e3b2fSeh146360 "ret = %d, len = %d\n", ret, len)); 2372bb5e3b2fSeh146360 miocack(q, m, len, ret); 2373bb5e3b2fSeh146360 return (IEEE80211_IOCTL_NOT_REQUIRED); 2374bb5e3b2fSeh146360 } 2375bb5e3b2fSeh146360 2376bb5e3b2fSeh146360 /* 2377bb5e3b2fSeh146360 * IEEE80211_IOCTL_REQUIRED - need net80211 handle 2378bb5e3b2fSeh146360 */ 2379bb5e3b2fSeh146360 return (IEEE80211_IOCTL_REQUIRED); 2380bb5e3b2fSeh146360 } 2381bb5e3b2fSeh146360 2382bb5e3b2fSeh146360 static int 2383bb5e3b2fSeh146360 ipw2100_getset(struct ipw2100_softc *sc, mblk_t *m, uint32_t cmd, 2384bb5e3b2fSeh146360 boolean_t *need_net80211) 2385bb5e3b2fSeh146360 { 2386bb5e3b2fSeh146360 wldp_t *infp, *outfp; 2387bb5e3b2fSeh146360 uint32_t id; 2388bb5e3b2fSeh146360 int ret; /* IEEE80211_IOCTL - handled by net80211 */ 2389bb5e3b2fSeh146360 2390922d2c76Seh146360 infp = (wldp_t *)(uintptr_t)m->b_rptr; 2391922d2c76Seh146360 outfp = (wldp_t *)(uintptr_t)m->b_rptr; 2392bb5e3b2fSeh146360 outfp->wldp_result = WL_NOTSUPPORTED; 2393bb5e3b2fSeh146360 2394bb5e3b2fSeh146360 id = infp->wldp_id; 2395bb5e3b2fSeh146360 IPW2100_DBG(IPW2100_DBG_IOCTL, (sc->sc_dip, CE_CONT, 2396bb5e3b2fSeh146360 "ipw2100_getset(): id = 0x%x\n", id)); 2397bb5e3b2fSeh146360 switch (id) { 2398bb5e3b2fSeh146360 /* 2399bb5e3b2fSeh146360 * which is not supported by net80211, so it 2400bb5e3b2fSeh146360 * has to be handled from driver side 2401bb5e3b2fSeh146360 */ 2402bb5e3b2fSeh146360 case WL_RADIO: 2403bb5e3b2fSeh146360 ret = ipw_wificfg_radio(sc, cmd, outfp); 2404bb5e3b2fSeh146360 break; 2405bb5e3b2fSeh146360 /* 2406bb5e3b2fSeh146360 * so far, drier doesn't support fix-rates 2407bb5e3b2fSeh146360 */ 2408bb5e3b2fSeh146360 case WL_DESIRED_RATES: 2409bb5e3b2fSeh146360 ret = ipw_wificfg_desrates(outfp); 2410bb5e3b2fSeh146360 break; 2411bb5e3b2fSeh146360 /* 2412bb5e3b2fSeh146360 * current net80211 implementation clears the bssid while 2413bb5e3b2fSeh146360 * this command received, which will result in the all zero 2414bb5e3b2fSeh146360 * mac address for scan'ed AP which is just disconnected. 2415bb5e3b2fSeh146360 * This is a workaround solution until net80211 find a 2416bb5e3b2fSeh146360 * better method. 2417bb5e3b2fSeh146360 */ 2418bb5e3b2fSeh146360 case WL_DISASSOCIATE: 2419bb5e3b2fSeh146360 ret = ipw_wificfg_disassoc(sc, outfp); 2420bb5e3b2fSeh146360 break; 2421bb5e3b2fSeh146360 default: 2422bb5e3b2fSeh146360 /* 2423bb5e3b2fSeh146360 * The wifi IOCTL net80211 supported: 2424bb5e3b2fSeh146360 * case WL_ESSID: 2425bb5e3b2fSeh146360 * case WL_BSSID: 2426bb5e3b2fSeh146360 * case WL_WEP_KEY_TAB: 2427bb5e3b2fSeh146360 * case WL_WEP_KEY_ID: 2428bb5e3b2fSeh146360 * case WL_AUTH_MODE: 2429bb5e3b2fSeh146360 * case WL_ENCRYPTION: 2430bb5e3b2fSeh146360 * case WL_BSS_TYPE: 2431bb5e3b2fSeh146360 * case WL_ESS_LIST: 2432bb5e3b2fSeh146360 * case WL_LINKSTATUS: 2433bb5e3b2fSeh146360 * case WL_RSSI: 2434bb5e3b2fSeh146360 * case WL_SCAN: 2435bb5e3b2fSeh146360 * case WL_LOAD_DEFAULTS: 2436bb5e3b2fSeh146360 */ 24379e2cd38cSeh146360 24389e2cd38cSeh146360 /* 24399e2cd38cSeh146360 * When radio is off, need to ignore all ioctl. What need to 24409e2cd38cSeh146360 * do is to check radio status firstly. If radio is ON, pass 24419e2cd38cSeh146360 * it to net80211, otherwise, return to upper layer directly. 24429e2cd38cSeh146360 * 24439e2cd38cSeh146360 * Considering the WL_SUCCESS also means WL_CONNECTED for 24449e2cd38cSeh146360 * checking linkstatus, one exception for WL_LINKSTATUS is to 24459e2cd38cSeh146360 * let net80211 handle it. 24469e2cd38cSeh146360 */ 24479e2cd38cSeh146360 if ((ipw2100_get_radio(sc) == 0) && 24489e2cd38cSeh146360 (id != WL_LINKSTATUS)) { 24499e2cd38cSeh146360 24509e2cd38cSeh146360 IPW2100_REPORT((sc->sc_dip, CE_WARN, 24519e2cd38cSeh146360 "ipw: RADIO is OFF\n")); 24529e2cd38cSeh146360 24539e2cd38cSeh146360 outfp->wldp_length = WIFI_BUF_OFFSET; 24549e2cd38cSeh146360 outfp->wldp_result = WL_SUCCESS; 24559e2cd38cSeh146360 ret = 0; 24569e2cd38cSeh146360 break; 24579e2cd38cSeh146360 } 24589e2cd38cSeh146360 2459bb5e3b2fSeh146360 *need_net80211 = B_TRUE; /* let net80211 do the rest */ 2460bb5e3b2fSeh146360 return (0); 2461bb5e3b2fSeh146360 } 2462bb5e3b2fSeh146360 /* 2463bb5e3b2fSeh146360 * we will overwrite everything 2464bb5e3b2fSeh146360 */ 2465bb5e3b2fSeh146360 m->b_wptr = m->b_rptr + outfp->wldp_length; 2466bb5e3b2fSeh146360 2467bb5e3b2fSeh146360 return (ret); 2468bb5e3b2fSeh146360 } 2469bb5e3b2fSeh146360 24707efa17f5Sfei feng - Sun Microsystems - Beijing China /* 24717efa17f5Sfei feng - Sun Microsystems - Beijing China * Call back functions for get/set proporty 24727efa17f5Sfei feng - Sun Microsystems - Beijing China */ 24737efa17f5Sfei feng - Sun Microsystems - Beijing China static int 24747efa17f5Sfei feng - Sun Microsystems - Beijing China ipw2100_m_getprop(void *arg, const char *pr_name, mac_prop_id_t wldp_pr_num, 24750dc2366fSVenugopal Iyer uint_t wldp_length, void *wldp_buf) 24767efa17f5Sfei feng - Sun Microsystems - Beijing China { 24777efa17f5Sfei feng - Sun Microsystems - Beijing China struct ipw2100_softc *sc = (struct ipw2100_softc *)arg; 24787efa17f5Sfei feng - Sun Microsystems - Beijing China struct ieee80211com *ic = &sc->sc_ic; 24797efa17f5Sfei feng - Sun Microsystems - Beijing China int err = 0; 24807efa17f5Sfei feng - Sun Microsystems - Beijing China 24817efa17f5Sfei feng - Sun Microsystems - Beijing China switch (wldp_pr_num) { 24827efa17f5Sfei feng - Sun Microsystems - Beijing China /* mac_prop_id */ 24837efa17f5Sfei feng - Sun Microsystems - Beijing China case MAC_PROP_WL_DESIRED_RATES: 24847efa17f5Sfei feng - Sun Microsystems - Beijing China IPW2100_DBG(IPW2100_DBG_BRUSSELS, (sc->sc_dip, CE_CONT, 24857efa17f5Sfei feng - Sun Microsystems - Beijing China "ipw2100_m_getprop(): Not Support DESIRED_RATES\n")); 24867efa17f5Sfei feng - Sun Microsystems - Beijing China break; 24877efa17f5Sfei feng - Sun Microsystems - Beijing China case MAC_PROP_WL_RADIO: 24887efa17f5Sfei feng - Sun Microsystems - Beijing China *(wl_linkstatus_t *)wldp_buf = ipw2100_get_radio(sc); 24897efa17f5Sfei feng - Sun Microsystems - Beijing China break; 24907efa17f5Sfei feng - Sun Microsystems - Beijing China default: 24917efa17f5Sfei feng - Sun Microsystems - Beijing China /* go through net80211 */ 24920dc2366fSVenugopal Iyer err = ieee80211_getprop(ic, pr_name, wldp_pr_num, 24930dc2366fSVenugopal Iyer wldp_length, wldp_buf); 24947efa17f5Sfei feng - Sun Microsystems - Beijing China break; 24957efa17f5Sfei feng - Sun Microsystems - Beijing China } 24967efa17f5Sfei feng - Sun Microsystems - Beijing China 24977efa17f5Sfei feng - Sun Microsystems - Beijing China return (err); 24987efa17f5Sfei feng - Sun Microsystems - Beijing China } 24997efa17f5Sfei feng - Sun Microsystems - Beijing China 25000dc2366fSVenugopal Iyer static void 25010dc2366fSVenugopal Iyer ipw2100_m_propinfo(void *arg, const char *pr_name, mac_prop_id_t wldp_pr_num, 25020dc2366fSVenugopal Iyer mac_prop_info_handle_t prh) 25030dc2366fSVenugopal Iyer { 25040dc2366fSVenugopal Iyer struct ipw2100_softc *sc = (struct ipw2100_softc *)arg; 25050dc2366fSVenugopal Iyer struct ieee80211com *ic = &sc->sc_ic; 25060dc2366fSVenugopal Iyer 25070dc2366fSVenugopal Iyer ieee80211_propinfo(ic, pr_name, wldp_pr_num, prh); 25080dc2366fSVenugopal Iyer 25090dc2366fSVenugopal Iyer } 25100dc2366fSVenugopal Iyer 25117efa17f5Sfei feng - Sun Microsystems - Beijing China static int 25127efa17f5Sfei feng - Sun Microsystems - Beijing China ipw2100_m_setprop(void *arg, const char *pr_name, mac_prop_id_t wldp_pr_num, 25137efa17f5Sfei feng - Sun Microsystems - Beijing China uint_t wldp_length, const void *wldp_buf) 25147efa17f5Sfei feng - Sun Microsystems - Beijing China { 25157efa17f5Sfei feng - Sun Microsystems - Beijing China struct ipw2100_softc *sc = (struct ipw2100_softc *)arg; 25167efa17f5Sfei feng - Sun Microsystems - Beijing China struct ieee80211com *ic = &sc->sc_ic; 25177efa17f5Sfei feng - Sun Microsystems - Beijing China int err; 25187efa17f5Sfei feng - Sun Microsystems - Beijing China 25197efa17f5Sfei feng - Sun Microsystems - Beijing China switch (wldp_pr_num) { 25207efa17f5Sfei feng - Sun Microsystems - Beijing China /* mac_prop_id */ 25217efa17f5Sfei feng - Sun Microsystems - Beijing China case MAC_PROP_WL_DESIRED_RATES: 25227efa17f5Sfei feng - Sun Microsystems - Beijing China IPW2100_DBG(IPW2100_DBG_BRUSSELS, (sc->sc_dip, CE_CONT, 25237efa17f5Sfei feng - Sun Microsystems - Beijing China "ipw2100_m_setprop(): Not Support DESIRED_RATES\n")); 25247efa17f5Sfei feng - Sun Microsystems - Beijing China err = ENOTSUP; 25257efa17f5Sfei feng - Sun Microsystems - Beijing China break; 25267efa17f5Sfei feng - Sun Microsystems - Beijing China case MAC_PROP_WL_RADIO: 25277efa17f5Sfei feng - Sun Microsystems - Beijing China IPW2100_DBG(IPW2100_DBG_BRUSSELS, (sc->sc_dip, CE_CONT, 25287efa17f5Sfei feng - Sun Microsystems - Beijing China "ipw2100_m_setprop(): Not Support RADIO\n")); 25297efa17f5Sfei feng - Sun Microsystems - Beijing China err = ENOTSUP; 25307efa17f5Sfei feng - Sun Microsystems - Beijing China break; 25317efa17f5Sfei feng - Sun Microsystems - Beijing China default: 25327efa17f5Sfei feng - Sun Microsystems - Beijing China /* go through net80211 */ 25337efa17f5Sfei feng - Sun Microsystems - Beijing China err = ieee80211_setprop(ic, pr_name, wldp_pr_num, wldp_length, 25347efa17f5Sfei feng - Sun Microsystems - Beijing China wldp_buf); 25357efa17f5Sfei feng - Sun Microsystems - Beijing China break; 25367efa17f5Sfei feng - Sun Microsystems - Beijing China } 25377efa17f5Sfei feng - Sun Microsystems - Beijing China 25387efa17f5Sfei feng - Sun Microsystems - Beijing China if (err == ENETRESET) { 25397efa17f5Sfei feng - Sun Microsystems - Beijing China if (sc->sc_flags & IPW2100_FLAG_RUNNING) { 25407efa17f5Sfei feng - Sun Microsystems - Beijing China (void) ipw2100_m_start(sc); 25417efa17f5Sfei feng - Sun Microsystems - Beijing China (void) ieee80211_new_state(ic, 25427efa17f5Sfei feng - Sun Microsystems - Beijing China IEEE80211_S_SCAN, -1); 25437efa17f5Sfei feng - Sun Microsystems - Beijing China } 25447efa17f5Sfei feng - Sun Microsystems - Beijing China 25457efa17f5Sfei feng - Sun Microsystems - Beijing China err = 0; 25467efa17f5Sfei feng - Sun Microsystems - Beijing China } 25477efa17f5Sfei feng - Sun Microsystems - Beijing China 25487efa17f5Sfei feng - Sun Microsystems - Beijing China return (err); 25497efa17f5Sfei feng - Sun Microsystems - Beijing China } 25507efa17f5Sfei feng - Sun Microsystems - Beijing China 2551bb5e3b2fSeh146360 static int 2552bb5e3b2fSeh146360 ipw_wificfg_radio(struct ipw2100_softc *sc, uint32_t cmd, wldp_t *outfp) 2553bb5e3b2fSeh146360 { 2554bb5e3b2fSeh146360 uint32_t ret = ENOTSUP; 2555bb5e3b2fSeh146360 2556bb5e3b2fSeh146360 switch (cmd) { 2557bb5e3b2fSeh146360 case WLAN_GET_PARAM: 2558bb5e3b2fSeh146360 *(wl_linkstatus_t *)(outfp->wldp_buf) = ipw2100_get_radio(sc); 2559bb5e3b2fSeh146360 outfp->wldp_length = WIFI_BUF_OFFSET + sizeof (wl_linkstatus_t); 2560bb5e3b2fSeh146360 outfp->wldp_result = WL_SUCCESS; 2561bb5e3b2fSeh146360 ret = 0; /* command sucess */ 2562bb5e3b2fSeh146360 break; 2563bb5e3b2fSeh146360 case WLAN_SET_PARAM: 2564bb5e3b2fSeh146360 default: 2565bb5e3b2fSeh146360 break; 2566bb5e3b2fSeh146360 } 2567bb5e3b2fSeh146360 return (ret); 2568bb5e3b2fSeh146360 } 2569bb5e3b2fSeh146360 2570bb5e3b2fSeh146360 static int 2571bb5e3b2fSeh146360 ipw_wificfg_desrates(wldp_t *outfp) 2572bb5e3b2fSeh146360 { 2573bb5e3b2fSeh146360 /* 2574bb5e3b2fSeh146360 * return success, but with result NOTSUPPORTED 2575bb5e3b2fSeh146360 */ 2576bb5e3b2fSeh146360 outfp->wldp_length = WIFI_BUF_OFFSET; 2577bb5e3b2fSeh146360 outfp->wldp_result = WL_NOTSUPPORTED; 2578bb5e3b2fSeh146360 return (0); 2579bb5e3b2fSeh146360 } 2580bb5e3b2fSeh146360 2581bb5e3b2fSeh146360 static int 2582bb5e3b2fSeh146360 ipw_wificfg_disassoc(struct ipw2100_softc *sc, wldp_t *outfp) 2583bb5e3b2fSeh146360 { 2584bb5e3b2fSeh146360 struct ieee80211com *ic = &sc->sc_ic; 2585bb5e3b2fSeh146360 2586bb5e3b2fSeh146360 /* 2587bb5e3b2fSeh146360 * init the state 2588bb5e3b2fSeh146360 */ 2589bb5e3b2fSeh146360 if (ic->ic_state != IEEE80211_S_INIT) { 2590bb5e3b2fSeh146360 (void) ieee80211_new_state(ic, IEEE80211_S_INIT, -1); 2591bb5e3b2fSeh146360 } 2592bb5e3b2fSeh146360 2593bb5e3b2fSeh146360 /* 2594bb5e3b2fSeh146360 * return success always 2595bb5e3b2fSeh146360 */ 2596bb5e3b2fSeh146360 outfp->wldp_length = WIFI_BUF_OFFSET; 2597bb5e3b2fSeh146360 outfp->wldp_result = WL_SUCCESS; 2598bb5e3b2fSeh146360 return (0); 2599bb5e3b2fSeh146360 } 2600bb5e3b2fSeh146360 /* End of IOCTL Handler */ 2601bb5e3b2fSeh146360 2602bb5e3b2fSeh146360 static void 2603bb5e3b2fSeh146360 ipw2100_fix_channel(struct ieee80211com *ic, mblk_t *m) 2604bb5e3b2fSeh146360 { 2605bb5e3b2fSeh146360 struct ieee80211_frame *wh; 2606bb5e3b2fSeh146360 uint8_t subtype; 2607bb5e3b2fSeh146360 uint8_t *frm, *efrm; 2608bb5e3b2fSeh146360 2609bb5e3b2fSeh146360 wh = (struct ieee80211_frame *)m->b_rptr; 2610bb5e3b2fSeh146360 2611bb5e3b2fSeh146360 if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) != IEEE80211_FC0_TYPE_MGT) 2612bb5e3b2fSeh146360 return; 2613bb5e3b2fSeh146360 2614bb5e3b2fSeh146360 subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK; 2615bb5e3b2fSeh146360 2616bb5e3b2fSeh146360 if (subtype != IEEE80211_FC0_SUBTYPE_BEACON && 2617bb5e3b2fSeh146360 subtype != IEEE80211_FC0_SUBTYPE_PROBE_RESP) 2618bb5e3b2fSeh146360 return; 2619bb5e3b2fSeh146360 2620bb5e3b2fSeh146360 /* 2621bb5e3b2fSeh146360 * assume the message contains only 1 block 2622bb5e3b2fSeh146360 */ 2623bb5e3b2fSeh146360 frm = (uint8_t *)(wh + 1); 2624bb5e3b2fSeh146360 efrm = (uint8_t *)m->b_wptr; 2625bb5e3b2fSeh146360 frm += 12; /* skip tstamp, bintval and capinfo fields */ 2626bb5e3b2fSeh146360 while (frm < efrm) { 2627bb5e3b2fSeh146360 if (*frm == IEEE80211_ELEMID_DSPARMS) { 2628bb5e3b2fSeh146360 #if IEEE80211_CHAN_MAX < 255 2629bb5e3b2fSeh146360 if (frm[2] <= IEEE80211_CHAN_MAX) 2630bb5e3b2fSeh146360 #endif 2631bb5e3b2fSeh146360 { 2632bb5e3b2fSeh146360 ic->ic_curchan = &ic->ic_sup_channels[frm[2]]; 2633bb5e3b2fSeh146360 } 2634bb5e3b2fSeh146360 } 2635bb5e3b2fSeh146360 frm += frm[1] + 2; 2636bb5e3b2fSeh146360 } 2637bb5e3b2fSeh146360 } 2638bb5e3b2fSeh146360 2639bb5e3b2fSeh146360 static void 2640bb5e3b2fSeh146360 ipw2100_rcvpkt(struct ipw2100_softc *sc, struct ipw2100_status *status, 2641bb5e3b2fSeh146360 uint8_t *rxbuf) 2642bb5e3b2fSeh146360 { 2643bb5e3b2fSeh146360 struct ieee80211com *ic = &sc->sc_ic; 2644bb5e3b2fSeh146360 mblk_t *m; 2645bb5e3b2fSeh146360 struct ieee80211_frame *wh = (struct ieee80211_frame *)rxbuf; 2646bb5e3b2fSeh146360 struct ieee80211_node *in; 2647bb5e3b2fSeh146360 uint32_t rlen; 2648bb5e3b2fSeh146360 2649bb5e3b2fSeh146360 in = ieee80211_find_rxnode(ic, wh); 2650bb5e3b2fSeh146360 rlen = LE_32(status->len); 2651bb5e3b2fSeh146360 m = allocb(rlen, BPRI_MED); 2652bb5e3b2fSeh146360 if (m) { 2653bb5e3b2fSeh146360 (void) memcpy(m->b_wptr, rxbuf, rlen); 2654bb5e3b2fSeh146360 m->b_wptr += rlen; 2655bb5e3b2fSeh146360 if (ic->ic_state == IEEE80211_S_SCAN) 2656bb5e3b2fSeh146360 ipw2100_fix_channel(ic, m); 2657bb5e3b2fSeh146360 (void) ieee80211_input(ic, m, in, status->rssi, 0); 2658bb5e3b2fSeh146360 } else 2659bb5e3b2fSeh146360 IPW2100_WARN((sc->sc_dip, CE_WARN, 2660bb5e3b2fSeh146360 "ipw2100_rcvpkg(): cannot allocate receive message(%u)\n", 2661bb5e3b2fSeh146360 LE_32(status->len))); 2662bb5e3b2fSeh146360 ieee80211_free_node(in); 2663bb5e3b2fSeh146360 } 2664bb5e3b2fSeh146360 2665bb5e3b2fSeh146360 static uint_t 2666bb5e3b2fSeh146360 ipw2100_intr(caddr_t arg) 2667bb5e3b2fSeh146360 { 2668922d2c76Seh146360 struct ipw2100_softc *sc = (struct ipw2100_softc *)(uintptr_t)arg; 2669bb5e3b2fSeh146360 uint32_t ireg, ridx, len, i; 2670bb5e3b2fSeh146360 struct ieee80211com *ic = &sc->sc_ic; 2671bb5e3b2fSeh146360 struct ipw2100_status *status; 2672bb5e3b2fSeh146360 uint8_t *rxbuf; 2673bb5e3b2fSeh146360 struct dma_region *dr; 2674bb5e3b2fSeh146360 uint32_t state; 2675bb5e3b2fSeh146360 #if DEBUG 2676bb5e3b2fSeh146360 struct ipw2100_bd *rxbd; 2677bb5e3b2fSeh146360 #endif 2678bb5e3b2fSeh146360 26790f1b305eSSeth Goldberg if (sc->sc_suspended) 26800f1b305eSSeth Goldberg return (DDI_INTR_UNCLAIMED); 26810f1b305eSSeth Goldberg 2682bb5e3b2fSeh146360 ireg = ipw2100_csr_get32(sc, IPW2100_CSR_INTR); 2683bb5e3b2fSeh146360 2684bb5e3b2fSeh146360 if (!(ireg & IPW2100_INTR_MASK_ALL)) 2685bb5e3b2fSeh146360 return (DDI_INTR_UNCLAIMED); 2686bb5e3b2fSeh146360 2687bb5e3b2fSeh146360 /* 2688bb5e3b2fSeh146360 * mask all interrupts 2689bb5e3b2fSeh146360 */ 2690bb5e3b2fSeh146360 ipw2100_csr_put32(sc, IPW2100_CSR_INTR_MASK, 0); 2691bb5e3b2fSeh146360 2692bb5e3b2fSeh146360 /* 2693bb5e3b2fSeh146360 * acknowledge all fired interrupts 2694bb5e3b2fSeh146360 */ 2695bb5e3b2fSeh146360 ipw2100_csr_put32(sc, IPW2100_CSR_INTR, ireg); 2696bb5e3b2fSeh146360 2697bb5e3b2fSeh146360 IPW2100_DBG(IPW2100_DBG_INT, (sc->sc_dip, CE_CONT, 2698bb5e3b2fSeh146360 "ipw2100_intr(): interrupt is fired. int=0x%08x\n", ireg)); 2699bb5e3b2fSeh146360 2700bb5e3b2fSeh146360 if (ireg & IPW2100_INTR_MASK_ERR) { 2701bb5e3b2fSeh146360 2702bb5e3b2fSeh146360 IPW2100_DBG(IPW2100_DBG_FATAL, (sc->sc_dip, CE_CONT, 2703bb5e3b2fSeh146360 "ipw2100_intr(): interrupt is fired, MASK = 0x%08x\n", 2704bb5e3b2fSeh146360 ireg)); 2705bb5e3b2fSeh146360 2706bb5e3b2fSeh146360 /* 2707bb5e3b2fSeh146360 * inform mfthread to recover hw error 2708bb5e3b2fSeh146360 */ 2709bb5e3b2fSeh146360 mutex_enter(&sc->sc_mflock); 2710bb5e3b2fSeh146360 sc->sc_flags |= IPW2100_FLAG_HW_ERR_RECOVER; 2711bb5e3b2fSeh146360 mutex_exit(&sc->sc_mflock); 2712bb5e3b2fSeh146360 2713922d2c76Seh146360 goto enable_interrupt; 2714922d2c76Seh146360 } 2715bb5e3b2fSeh146360 2716bb5e3b2fSeh146360 /* 2717bb5e3b2fSeh146360 * FW intr 2718bb5e3b2fSeh146360 */ 2719bb5e3b2fSeh146360 if (ireg & IPW2100_INTR_FW_INIT_DONE) { 2720bb5e3b2fSeh146360 mutex_enter(&sc->sc_ilock); 2721bb5e3b2fSeh146360 sc->sc_flags |= IPW2100_FLAG_FW_INITED; 2722bb5e3b2fSeh146360 cv_signal(&sc->sc_fw_cond); 2723bb5e3b2fSeh146360 mutex_exit(&sc->sc_ilock); 2724bb5e3b2fSeh146360 } 2725bb5e3b2fSeh146360 2726bb5e3b2fSeh146360 /* 2727bb5e3b2fSeh146360 * RX intr 2728bb5e3b2fSeh146360 */ 2729bb5e3b2fSeh146360 if (ireg & IPW2100_INTR_RX_TRANSFER) { 2730bb5e3b2fSeh146360 ridx = ipw2100_csr_get32(sc, 2731bb5e3b2fSeh146360 IPW2100_CSR_RX_READ_INDEX); 2732bb5e3b2fSeh146360 2733bb5e3b2fSeh146360 for (; sc->sc_rx_cur != ridx; 2734bb5e3b2fSeh146360 sc->sc_rx_cur = RING_FORWARD( 2735bb5e3b2fSeh146360 sc->sc_rx_cur, 1, IPW2100_NUM_RXBD)) { 2736bb5e3b2fSeh146360 2737bb5e3b2fSeh146360 i = sc->sc_rx_cur; 2738bb5e3b2fSeh146360 status = &sc->sc_status[i]; 2739bb5e3b2fSeh146360 rxbuf = &sc->sc_rxbufs[i]->rxb_dat[0]; 2740bb5e3b2fSeh146360 dr = &sc->sc_dma_rxbufs[i]; 2741bb5e3b2fSeh146360 2742bb5e3b2fSeh146360 /* 2743bb5e3b2fSeh146360 * sync 2744bb5e3b2fSeh146360 */ 2745bb5e3b2fSeh146360 (void) ddi_dma_sync(sc->sc_dma_status.dr_hnd, 2746bb5e3b2fSeh146360 i * sizeof (struct ipw2100_status), 2747bb5e3b2fSeh146360 sizeof (struct ipw2100_status), 2748bb5e3b2fSeh146360 DDI_DMA_SYNC_FORKERNEL); 2749bb5e3b2fSeh146360 (void) ddi_dma_sync(sc->sc_dma_rxbd.dr_hnd, 2750bb5e3b2fSeh146360 i * sizeof (struct ipw2100_bd), 2751bb5e3b2fSeh146360 sizeof (struct ipw2100_bd), 2752bb5e3b2fSeh146360 DDI_DMA_SYNC_FORKERNEL); 2753bb5e3b2fSeh146360 (void) ddi_dma_sync(dr->dr_hnd, 0, 2754bb5e3b2fSeh146360 sizeof (struct ipw2100_rxb), 2755bb5e3b2fSeh146360 DDI_DMA_SYNC_FORKERNEL); 2756922d2c76Seh146360 IPW2100_DBG(IPW2100_DBG_INT, (sc->sc_dip, CE_CONT, 2757922d2c76Seh146360 "ipw2100_intr(): status code=0x%04x, len=0x%08x, " 2758bb5e3b2fSeh146360 "flags=0x%02x, rssi=%02x\n", 2759bb5e3b2fSeh146360 LE_16(status->code), LE_32(status->len), 2760bb5e3b2fSeh146360 status->flags, status->rssi)); 2761bb5e3b2fSeh146360 #if DEBUG 2762bb5e3b2fSeh146360 rxbd = &sc->sc_rxbd[i]; 2763922d2c76Seh146360 IPW2100_DBG(IPW2100_DBG_INT, (sc->sc_dip, CE_CONT, 2764922d2c76Seh146360 "ipw2100_intr(): rxbd,phyaddr=0x%08x, len=0x%08x, " 2765bb5e3b2fSeh146360 "flags=0x%02x,nfrag=%02x\n", 2766bb5e3b2fSeh146360 LE_32(rxbd->phyaddr), LE_32(rxbd->len), 2767bb5e3b2fSeh146360 rxbd->flags, rxbd->nfrag)); 2768bb5e3b2fSeh146360 #endif 2769bb5e3b2fSeh146360 switch (LE_16(status->code) & 0x0f) { 2770bb5e3b2fSeh146360 /* 2771bb5e3b2fSeh146360 * command complete response 2772bb5e3b2fSeh146360 */ 2773bb5e3b2fSeh146360 case IPW2100_STATUS_CODE_COMMAND: 2774bb5e3b2fSeh146360 mutex_enter(&sc->sc_ilock); 2775bb5e3b2fSeh146360 sc->sc_done = 1; 2776bb5e3b2fSeh146360 cv_signal(&sc->sc_cmd_cond); 2777bb5e3b2fSeh146360 mutex_exit(&sc->sc_ilock); 2778bb5e3b2fSeh146360 break; 2779bb5e3b2fSeh146360 /* 2780bb5e3b2fSeh146360 * change state 2781bb5e3b2fSeh146360 */ 2782bb5e3b2fSeh146360 case IPW2100_STATUS_CODE_NEWSTATE: 2783922d2c76Seh146360 state = LE_32(* ((uint32_t *)(uintptr_t)rxbuf)); 2784bb5e3b2fSeh146360 IPW2100_DBG(IPW2100_DBG_INT, 2785bb5e3b2fSeh146360 (sc->sc_dip, CE_CONT, 2786922d2c76Seh146360 "ipw2100_intr(): newstate,state=0x%x\n", 2787922d2c76Seh146360 state)); 2788bb5e3b2fSeh146360 2789bb5e3b2fSeh146360 switch (state) { 2790bb5e3b2fSeh146360 case IPW2100_STATE_ASSOCIATED: 2791bb5e3b2fSeh146360 ieee80211_new_state(ic, 2792bb5e3b2fSeh146360 IEEE80211_S_RUN, -1); 2793bb5e3b2fSeh146360 break; 2794bb5e3b2fSeh146360 case IPW2100_STATE_ASSOCIATION_LOST: 2795bb5e3b2fSeh146360 case IPW2100_STATE_DISABLED: 2796bb5e3b2fSeh146360 ieee80211_new_state(ic, 2797bb5e3b2fSeh146360 IEEE80211_S_INIT, -1); 2798bb5e3b2fSeh146360 break; 27999e2cd38cSeh146360 /* 28009e2cd38cSeh146360 * When radio is OFF, need a better 28019e2cd38cSeh146360 * scan approach to ensure scan 28029e2cd38cSeh146360 * result correct. 28039e2cd38cSeh146360 */ 2804bb5e3b2fSeh146360 case IPW2100_STATE_RADIO_DISABLED: 2805922d2c76Seh146360 IPW2100_REPORT((sc->sc_dip, CE_WARN, 2806922d2c76Seh146360 "ipw2100_intr(): RADIO is OFF\n")); 2807bb5e3b2fSeh146360 ipw2100_stop(sc); 2808bb5e3b2fSeh146360 break; 2809bb5e3b2fSeh146360 case IPW2100_STATE_SCAN_COMPLETE: 2810bb5e3b2fSeh146360 ieee80211_cancel_scan(ic); 2811bb5e3b2fSeh146360 break; 2812bb5e3b2fSeh146360 case IPW2100_STATE_SCANNING: 2813922d2c76Seh146360 if (ic->ic_state != IEEE80211_S_RUN) 2814bb5e3b2fSeh146360 ieee80211_new_state(ic, 2815922d2c76Seh146360 IEEE80211_S_SCAN, -1); 2816922d2c76Seh146360 ic->ic_flags |= IEEE80211_F_SCAN; 2817bb5e3b2fSeh146360 2818bb5e3b2fSeh146360 break; 2819bb5e3b2fSeh146360 default: 2820bb5e3b2fSeh146360 break; 2821bb5e3b2fSeh146360 } 2822bb5e3b2fSeh146360 break; 2823bb5e3b2fSeh146360 case IPW2100_STATUS_CODE_DATA_802_11: 2824bb5e3b2fSeh146360 case IPW2100_STATUS_CODE_DATA_802_3: 2825bb5e3b2fSeh146360 ipw2100_rcvpkt(sc, status, rxbuf); 2826bb5e3b2fSeh146360 break; 2827bb5e3b2fSeh146360 case IPW2100_STATUS_CODE_NOTIFICATION: 2828bb5e3b2fSeh146360 break; 2829bb5e3b2fSeh146360 default: 2830bb5e3b2fSeh146360 IPW2100_WARN((sc->sc_dip, CE_WARN, 2831bb5e3b2fSeh146360 "ipw2100_intr(): " 2832bb5e3b2fSeh146360 "unknown status code 0x%04x\n", 2833bb5e3b2fSeh146360 LE_16(status->code))); 2834bb5e3b2fSeh146360 break; 2835bb5e3b2fSeh146360 } 2836bb5e3b2fSeh146360 } 2837bb5e3b2fSeh146360 /* 2838bb5e3b2fSeh146360 * write sc_rx_cur backward 1 step to RX_WRITE_INDEX 2839bb5e3b2fSeh146360 */ 2840bb5e3b2fSeh146360 ipw2100_csr_put32(sc, IPW2100_CSR_RX_WRITE_INDEX, 2841bb5e3b2fSeh146360 RING_BACKWARD(sc->sc_rx_cur, 1, IPW2100_NUM_RXBD)); 2842bb5e3b2fSeh146360 } 2843bb5e3b2fSeh146360 2844bb5e3b2fSeh146360 /* 2845bb5e3b2fSeh146360 * TX intr 2846bb5e3b2fSeh146360 */ 2847bb5e3b2fSeh146360 if (ireg & IPW2100_INTR_TX_TRANSFER) { 2848bb5e3b2fSeh146360 mutex_enter(&sc->sc_tx_lock); 2849bb5e3b2fSeh146360 ridx = ipw2100_csr_get32(sc, IPW2100_CSR_TX_READ_INDEX); 2850bb5e3b2fSeh146360 len = RING_FLEN(RING_FORWARD(sc->sc_tx_cur, 2851bb5e3b2fSeh146360 sc->sc_tx_free, IPW2100_NUM_TXBD), 2852bb5e3b2fSeh146360 ridx, IPW2100_NUM_TXBD); 2853bb5e3b2fSeh146360 sc->sc_tx_free += len; 2854bb5e3b2fSeh146360 IPW2100_DBG(IPW2100_DBG_INT, (sc->sc_dip, CE_CONT, 2855bb5e3b2fSeh146360 "ipw2100_intr(): len=%d\n", len)); 2856bb5e3b2fSeh146360 mutex_exit(&sc->sc_tx_lock); 2857bb5e3b2fSeh146360 2858bb5e3b2fSeh146360 mutex_enter(&sc->sc_resched_lock); 2859bb5e3b2fSeh146360 if (len > 1 && (sc->sc_flags & IPW2100_FLAG_TX_SCHED)) { 2860bb5e3b2fSeh146360 sc->sc_flags &= ~IPW2100_FLAG_TX_SCHED; 2861bb5e3b2fSeh146360 mac_tx_update(ic->ic_mach); 2862bb5e3b2fSeh146360 } 2863bb5e3b2fSeh146360 mutex_exit(&sc->sc_resched_lock); 2864bb5e3b2fSeh146360 } 2865bb5e3b2fSeh146360 2866922d2c76Seh146360 enable_interrupt: 2867bb5e3b2fSeh146360 /* 2868bb5e3b2fSeh146360 * enable all interrupts 2869bb5e3b2fSeh146360 */ 2870bb5e3b2fSeh146360 ipw2100_csr_put32(sc, IPW2100_CSR_INTR_MASK, IPW2100_INTR_MASK_ALL); 2871bb5e3b2fSeh146360 2872bb5e3b2fSeh146360 return (DDI_INTR_CLAIMED); 2873bb5e3b2fSeh146360 } 2874bb5e3b2fSeh146360 2875bb5e3b2fSeh146360 2876bb5e3b2fSeh146360 /* 2877bb5e3b2fSeh146360 * Module Loading Data & Entry Points 2878bb5e3b2fSeh146360 */ 2879bb5e3b2fSeh146360 DDI_DEFINE_STREAM_OPS(ipw2100_devops, nulldev, nulldev, ipw2100_attach, 2880799aa485SKonstantin Ananyev ipw2100_detach, nodev, NULL, D_MP, NULL, ipw2100_quiesce); 2881bb5e3b2fSeh146360 2882bb5e3b2fSeh146360 static struct modldrv ipw2100_modldrv = { 2883bb5e3b2fSeh146360 &mod_driverops, 2884bb5e3b2fSeh146360 ipw2100_ident, 2885bb5e3b2fSeh146360 &ipw2100_devops 2886bb5e3b2fSeh146360 }; 2887bb5e3b2fSeh146360 2888bb5e3b2fSeh146360 static struct modlinkage ipw2100_modlinkage = { 2889bb5e3b2fSeh146360 MODREV_1, 2890bb5e3b2fSeh146360 &ipw2100_modldrv, 2891bb5e3b2fSeh146360 NULL 2892bb5e3b2fSeh146360 }; 2893bb5e3b2fSeh146360 2894bb5e3b2fSeh146360 int 2895bb5e3b2fSeh146360 _init(void) 2896bb5e3b2fSeh146360 { 2897bb5e3b2fSeh146360 int status; 2898bb5e3b2fSeh146360 2899bb5e3b2fSeh146360 status = ddi_soft_state_init(&ipw2100_ssp, 2900bb5e3b2fSeh146360 sizeof (struct ipw2100_softc), 1); 2901bb5e3b2fSeh146360 if (status != DDI_SUCCESS) 2902bb5e3b2fSeh146360 return (status); 2903bb5e3b2fSeh146360 2904bb5e3b2fSeh146360 mac_init_ops(&ipw2100_devops, IPW2100_DRV_NAME); 2905bb5e3b2fSeh146360 status = mod_install(&ipw2100_modlinkage); 2906bb5e3b2fSeh146360 if (status != DDI_SUCCESS) { 2907bb5e3b2fSeh146360 mac_fini_ops(&ipw2100_devops); 2908bb5e3b2fSeh146360 ddi_soft_state_fini(&ipw2100_ssp); 2909bb5e3b2fSeh146360 } 2910bb5e3b2fSeh146360 2911bb5e3b2fSeh146360 return (status); 2912bb5e3b2fSeh146360 } 2913bb5e3b2fSeh146360 2914bb5e3b2fSeh146360 int 2915bb5e3b2fSeh146360 _fini(void) 2916bb5e3b2fSeh146360 { 2917bb5e3b2fSeh146360 int status; 2918bb5e3b2fSeh146360 2919bb5e3b2fSeh146360 status = mod_remove(&ipw2100_modlinkage); 2920bb5e3b2fSeh146360 if (status == DDI_SUCCESS) { 2921bb5e3b2fSeh146360 mac_fini_ops(&ipw2100_devops); 2922bb5e3b2fSeh146360 ddi_soft_state_fini(&ipw2100_ssp); 2923bb5e3b2fSeh146360 } 2924bb5e3b2fSeh146360 2925bb5e3b2fSeh146360 return (status); 2926bb5e3b2fSeh146360 } 2927bb5e3b2fSeh146360 2928bb5e3b2fSeh146360 int 2929bb5e3b2fSeh146360 _info(struct modinfo *mip) 2930bb5e3b2fSeh146360 { 2931bb5e3b2fSeh146360 return (mod_info(&ipw2100_modlinkage, mip)); 2932bb5e3b2fSeh146360 } 2933