13847Seh146360 /* 25838Seh146360 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 33847Seh146360 * Use is subject to license terms. 43847Seh146360 */ 53847Seh146360 63847Seh146360 /* 73847Seh146360 * Copyright(c) 2004 83847Seh146360 * Damien Bergamini <damien.bergamini@free.fr>. All rights reserved. 93847Seh146360 * 103847Seh146360 * Redistribution and use in source and binary forms, with or without 113847Seh146360 * modification, are permitted provided that the following conditions 123847Seh146360 * are met: 133847Seh146360 * 1. Redistributions of source code must retain the above copyright 143847Seh146360 * notice unmodified, this list of conditions, and the following 153847Seh146360 * disclaimer. 163847Seh146360 * 2. Redistributions in binary form must reproduce the above copyright 173847Seh146360 * notice, this list of conditions and the following disclaimer in the 183847Seh146360 * documentation and/or other materials provided with the distribution. 193847Seh146360 * 203847Seh146360 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 213847Seh146360 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 223847Seh146360 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 233847Seh146360 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 243847Seh146360 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 253847Seh146360 * DAMAGES(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 263847Seh146360 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 273847Seh146360 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 283847Seh146360 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 293847Seh146360 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 303847Seh146360 * SUCH DAMAGE. 313847Seh146360 */ 323847Seh146360 333847Seh146360 #include <sys/types.h> 343847Seh146360 #include <sys/byteorder.h> 353847Seh146360 #include <sys/conf.h> 363847Seh146360 #include <sys/cmn_err.h> 373847Seh146360 #include <sys/stat.h> 383847Seh146360 #include <sys/ddi.h> 393847Seh146360 #include <sys/sunddi.h> 403847Seh146360 #include <sys/strsubr.h> 413847Seh146360 #include <sys/ethernet.h> 423847Seh146360 #include <inet/common.h> 433847Seh146360 #include <inet/nd.h> 443847Seh146360 #include <inet/mi.h> 453847Seh146360 #include <sys/note.h> 463847Seh146360 #include <sys/stream.h> 473847Seh146360 #include <sys/strsun.h> 483847Seh146360 #include <sys/modctl.h> 493847Seh146360 #include <sys/devops.h> 503847Seh146360 #include <sys/dlpi.h> 513847Seh146360 #include <sys/mac.h> 525903Ssowmini #include <net/if.h> 533847Seh146360 #include <sys/mac_wifi.h> 543847Seh146360 #include <sys/varargs.h> 553847Seh146360 #include <sys/policy.h> 563847Seh146360 573847Seh146360 #include "ipw2100.h" 583847Seh146360 #include "ipw2100_impl.h" 593847Seh146360 #include <inet/wifi_ioctl.h> 603847Seh146360 613847Seh146360 /* 623847Seh146360 * kCF framework include files 633847Seh146360 */ 643847Seh146360 #include <sys/crypto/common.h> 653847Seh146360 #include <sys/crypto/api.h> 663847Seh146360 673847Seh146360 static void *ipw2100_ssp = NULL; 683847Seh146360 static char ipw2100_ident[] = IPW2100_DRV_DESC " " IPW2100_DRV_REV; 693847Seh146360 703847Seh146360 /* 713847Seh146360 * PIO access attribute for register 723847Seh146360 */ 733847Seh146360 static ddi_device_acc_attr_t ipw2100_csr_accattr = { 743847Seh146360 DDI_DEVICE_ATTR_V0, 753847Seh146360 DDI_STRUCTURE_LE_ACC, 763847Seh146360 DDI_STRICTORDER_ACC 773847Seh146360 }; 783847Seh146360 793847Seh146360 static ddi_device_acc_attr_t ipw2100_dma_accattr = { 803847Seh146360 DDI_DEVICE_ATTR_V0, 813847Seh146360 DDI_NEVERSWAP_ACC, 823847Seh146360 DDI_STRICTORDER_ACC 833847Seh146360 }; 843847Seh146360 853847Seh146360 static ddi_dma_attr_t ipw2100_dma_attr = { 863847Seh146360 DMA_ATTR_V0, 873847Seh146360 0x0000000000000000ULL, 883847Seh146360 0x00000000ffffffffULL, 893847Seh146360 0x00000000ffffffffULL, 903847Seh146360 0x0000000000000004ULL, 913847Seh146360 0xfff, 923847Seh146360 1, 933847Seh146360 0x00000000ffffffffULL, 943847Seh146360 0x00000000ffffffffULL, 953847Seh146360 1, 963847Seh146360 1, 973847Seh146360 0 983847Seh146360 }; 993847Seh146360 1003847Seh146360 static const struct ieee80211_rateset ipw2100_rateset_11b = { 4, 1013847Seh146360 {2, 4, 11, 22} 1023847Seh146360 }; 1033847Seh146360 1043847Seh146360 /* 1053847Seh146360 * For mfthread only 1063847Seh146360 */ 1073847Seh146360 extern pri_t minclsyspri; 1083847Seh146360 1093847Seh146360 /* 1103847Seh146360 * ipw2100 specific hardware operations 1113847Seh146360 */ 1123847Seh146360 static void ipw2100_hwconf_get(struct ipw2100_softc *sc); 1133847Seh146360 static int ipw2100_chip_reset(struct ipw2100_softc *sc); 1143847Seh146360 static void ipw2100_master_stop(struct ipw2100_softc *sc); 1153847Seh146360 static void ipw2100_stop(struct ipw2100_softc *sc); 1163847Seh146360 static int ipw2100_config(struct ipw2100_softc *sc); 1173847Seh146360 static int ipw2100_cmd(struct ipw2100_softc *sc, uint32_t type, 1183847Seh146360 void *buf, size_t len); 1193847Seh146360 static int ipw2100_dma_region_alloc(struct ipw2100_softc *sc, 1203847Seh146360 struct dma_region *dr, size_t size, uint_t dir, uint_t flags); 1213847Seh146360 static void ipw2100_dma_region_free(struct dma_region *dr); 1223847Seh146360 static void ipw2100_tables_init(struct ipw2100_softc *sc); 1233847Seh146360 static void ipw2100_ring_hwsetup(struct ipw2100_softc *sc); 1243847Seh146360 static int ipw2100_ring_alloc(struct ipw2100_softc *sc); 1253847Seh146360 static void ipw2100_ring_free(struct ipw2100_softc *sc); 1263847Seh146360 static void ipw2100_ring_reset(struct ipw2100_softc *sc); 1273847Seh146360 static int ipw2100_ring_init(struct ipw2100_softc *sc); 1283847Seh146360 1293847Seh146360 /* 1303847Seh146360 * GLD specific operations 1313847Seh146360 */ 1323847Seh146360 static int ipw2100_m_stat(void *arg, uint_t stat, uint64_t *val); 1333847Seh146360 static int ipw2100_m_start(void *arg); 1343847Seh146360 static void ipw2100_m_stop(void *arg); 1353847Seh146360 static int ipw2100_m_unicst(void *arg, const uint8_t *macaddr); 1363847Seh146360 static int ipw2100_m_multicst(void *arg, boolean_t add, const uint8_t *m); 1373847Seh146360 static int ipw2100_m_promisc(void *arg, boolean_t on); 1383847Seh146360 static mblk_t *ipw2100_m_tx(void *arg, mblk_t *mp); 1393847Seh146360 static void ipw2100_m_ioctl(void *arg, queue_t *wq, mblk_t *mp); 1403847Seh146360 1413847Seh146360 /* 1423847Seh146360 * Interrupt and Data transferring operations 1433847Seh146360 */ 1443847Seh146360 static uint_t ipw2100_intr(caddr_t arg); 1453847Seh146360 static int ipw2100_send(struct ieee80211com *ic, mblk_t *mp, uint8_t type); 1463847Seh146360 static void ipw2100_rcvpkt(struct ipw2100_softc *sc, 1473847Seh146360 struct ipw2100_status *status, uint8_t *rxbuf); 1483847Seh146360 1493847Seh146360 /* 1503847Seh146360 * WiFi specific operations 1513847Seh146360 */ 1523847Seh146360 static int ipw2100_newstate(struct ieee80211com *ic, 1533847Seh146360 enum ieee80211_state state, int arg); 1543847Seh146360 static void ipw2100_thread(struct ipw2100_softc *sc); 1553847Seh146360 1563847Seh146360 /* 1573847Seh146360 * IOCTL Handler 1583847Seh146360 */ 1593847Seh146360 static int ipw2100_ioctl(struct ipw2100_softc *sc, queue_t *q, mblk_t *m); 1603847Seh146360 static int ipw2100_getset(struct ipw2100_softc *sc, 1613847Seh146360 mblk_t *m, uint32_t cmd, boolean_t *need_net80211); 1623847Seh146360 static int ipw_wificfg_radio(struct ipw2100_softc *sc, 1633847Seh146360 uint32_t cmd, wldp_t *outfp); 1643847Seh146360 static int ipw_wificfg_desrates(wldp_t *outfp); 1653847Seh146360 static int ipw_wificfg_disassoc(struct ipw2100_softc *sc, 1663847Seh146360 wldp_t *outfp); 1673847Seh146360 1683847Seh146360 /* 1693847Seh146360 * Mac Call Back entries 1703847Seh146360 */ 1713847Seh146360 mac_callbacks_t ipw2100_m_callbacks = { 1723847Seh146360 MC_IOCTL, 1733847Seh146360 ipw2100_m_stat, 1743847Seh146360 ipw2100_m_start, 1753847Seh146360 ipw2100_m_stop, 1763847Seh146360 ipw2100_m_promisc, 1773847Seh146360 ipw2100_m_multicst, 1783847Seh146360 ipw2100_m_unicst, 1793847Seh146360 ipw2100_m_tx, 1803847Seh146360 NULL, 1813847Seh146360 ipw2100_m_ioctl 1823847Seh146360 }; 1833847Seh146360 1843847Seh146360 1853847Seh146360 /* 1863847Seh146360 * DEBUG Facility 1873847Seh146360 */ 1883847Seh146360 #define MAX_MSG (128) 1893847Seh146360 uint32_t ipw2100_debug = 0; 1903847Seh146360 /* 1913847Seh146360 * supported debug marsks: 1923847Seh146360 * | IPW2100_DBG_INIT 1933847Seh146360 * | IPW2100_DBG_GLD 1943847Seh146360 * | IPW2100_DBG_TABLE 1953847Seh146360 * | IPW2100_DBG_SOFTINT 1963847Seh146360 * | IPW2100_DBG_CSR 1973847Seh146360 * | IPW2100_DBG_INT 1983847Seh146360 * | IPW2100_DBG_FW 1993847Seh146360 * | IPW2100_DBG_IOCTL 2003847Seh146360 * | IPW2100_DBG_HWCAP 2013847Seh146360 * | IPW2100_DBG_STATISTIC 2023847Seh146360 * | IPW2100_DBG_RING 2033847Seh146360 * | IPW2100_DBG_WIFI 2043847Seh146360 */ 2053847Seh146360 2063847Seh146360 /* 2073847Seh146360 * global tuning parameters to work around unknown hardware issues 2083847Seh146360 */ 2093847Seh146360 static uint32_t delay_config_stable = 100000; /* 100ms */ 2103847Seh146360 static uint32_t delay_fatal_recover = 100000 * 20; /* 2s */ 2113847Seh146360 static uint32_t delay_aux_thread = 100000; /* 100ms */ 2123847Seh146360 2133847Seh146360 void 2143847Seh146360 ipw2100_dbg(dev_info_t *dip, int level, const char *fmt, ...) 2153847Seh146360 { 2163847Seh146360 va_list ap; 2173847Seh146360 char buf[MAX_MSG]; 2183847Seh146360 int instance; 2193847Seh146360 2203847Seh146360 va_start(ap, fmt); 2213847Seh146360 (void) vsnprintf(buf, sizeof (buf), fmt, ap); 2223847Seh146360 va_end(ap); 2233847Seh146360 2243847Seh146360 if (dip) { 2253847Seh146360 instance = ddi_get_instance(dip); 2263847Seh146360 cmn_err(level, "%s%d: %s", IPW2100_DRV_NAME, instance, buf); 2273847Seh146360 } else 2283847Seh146360 cmn_err(level, "%s: %s", IPW2100_DRV_NAME, buf); 2293847Seh146360 } 2303847Seh146360 2313847Seh146360 /* 2323847Seh146360 * device operations 2333847Seh146360 */ 2343847Seh146360 int 2353847Seh146360 ipw2100_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 2363847Seh146360 { 2373847Seh146360 struct ipw2100_softc *sc; 2383847Seh146360 ddi_acc_handle_t cfgh; 2393847Seh146360 caddr_t regs; 2403847Seh146360 struct ieee80211com *ic; 2413847Seh146360 int instance, err, i; 2423847Seh146360 char strbuf[32]; 2433847Seh146360 wifi_data_t wd = { 0 }; 2443847Seh146360 mac_register_t *macp; 2453847Seh146360 2463847Seh146360 if (cmd != DDI_ATTACH) { 2473847Seh146360 err = DDI_FAILURE; 2483847Seh146360 goto fail1; 2493847Seh146360 } 2503847Seh146360 2513847Seh146360 instance = ddi_get_instance(dip); 2523847Seh146360 err = ddi_soft_state_zalloc(ipw2100_ssp, instance); 2533847Seh146360 if (err != DDI_SUCCESS) { 2543847Seh146360 IPW2100_WARN((dip, CE_WARN, 2553847Seh146360 "ipw2100_attach(): unable to allocate soft state\n")); 2563847Seh146360 goto fail1; 2573847Seh146360 } 2583847Seh146360 sc = ddi_get_soft_state(ipw2100_ssp, instance); 2593847Seh146360 sc->sc_dip = dip; 2603847Seh146360 2613847Seh146360 /* 2623847Seh146360 * Map config spaces register 2633847Seh146360 */ 2643847Seh146360 err = ddi_regs_map_setup(dip, IPW2100_PCI_CFG_RNUM, ®s, 2653847Seh146360 0, 0, &ipw2100_csr_accattr, &cfgh); 2663847Seh146360 if (err != DDI_SUCCESS) { 2673847Seh146360 IPW2100_WARN((dip, CE_WARN, 2683847Seh146360 "ipw2100_attach(): unable to map spaces regs\n")); 2693847Seh146360 goto fail2; 2703847Seh146360 } 2713847Seh146360 ddi_put8(cfgh, (uint8_t *)(regs + 0x41), 0); 2723847Seh146360 ddi_regs_map_free(&cfgh); 2733847Seh146360 2743847Seh146360 /* 2753847Seh146360 * Map operating registers 2763847Seh146360 */ 2773847Seh146360 err = ddi_regs_map_setup(dip, IPW2100_PCI_CSR_RNUM, &sc->sc_regs, 2783847Seh146360 0, 0, &ipw2100_csr_accattr, &sc->sc_ioh); 2793847Seh146360 if (err != DDI_SUCCESS) { 2803847Seh146360 IPW2100_WARN((dip, CE_WARN, 2813847Seh146360 "ipw2100_attach(): unable to map device regs\n")); 2823847Seh146360 goto fail2; 2833847Seh146360 } 2843847Seh146360 2853847Seh146360 /* 2863847Seh146360 * Reset the chip 2873847Seh146360 */ 2883847Seh146360 err = ipw2100_chip_reset(sc); 2893847Seh146360 if (err != DDI_SUCCESS) { 2903847Seh146360 IPW2100_WARN((dip, CE_WARN, 2913847Seh146360 "ipw2100_attach(): reset failed\n")); 2923847Seh146360 goto fail3; 2933847Seh146360 } 2943847Seh146360 2953847Seh146360 /* 2963847Seh146360 * Get the hw conf, including MAC address, then init all rings. 2973847Seh146360 */ 2983847Seh146360 ipw2100_hwconf_get(sc); 2993847Seh146360 err = ipw2100_ring_init(sc); 3003847Seh146360 if (err != DDI_SUCCESS) { 3013847Seh146360 IPW2100_WARN((dip, CE_WARN, 3023847Seh146360 "ipw2100_attach(): " 3033847Seh146360 "unable to allocate and initialize rings\n")); 3043847Seh146360 goto fail3; 3053847Seh146360 } 3063847Seh146360 3073847Seh146360 /* 3083847Seh146360 * Initialize mutexs and condvars 3093847Seh146360 */ 3103847Seh146360 err = ddi_get_iblock_cookie(dip, 0, &sc->sc_iblk); 3113847Seh146360 if (err != DDI_SUCCESS) { 3123847Seh146360 IPW2100_WARN((dip, CE_WARN, 3133847Seh146360 "ipw2100_attach(): ddi_get_iblock_cookie() failed\n")); 3143847Seh146360 goto fail4; 3153847Seh146360 } 3163847Seh146360 /* 3173847Seh146360 * interrupt lock 3183847Seh146360 */ 3193847Seh146360 mutex_init(&sc->sc_ilock, "interrupt-lock", MUTEX_DRIVER, 3203847Seh146360 (void *) sc->sc_iblk); 3213847Seh146360 cv_init(&sc->sc_fw_cond, "firmware", CV_DRIVER, NULL); 3223847Seh146360 cv_init(&sc->sc_cmd_cond, "command", CV_DRIVER, NULL); 3233847Seh146360 /* 3243847Seh146360 * tx ring lock 3253847Seh146360 */ 3263847Seh146360 mutex_init(&sc->sc_tx_lock, "tx-ring", MUTEX_DRIVER, 3273847Seh146360 (void *) sc->sc_iblk); 3283847Seh146360 cv_init(&sc->sc_tx_cond, "tx-ring", CV_DRIVER, NULL); 3293847Seh146360 /* 3303847Seh146360 * rescheuled lock 3313847Seh146360 */ 3323847Seh146360 mutex_init(&sc->sc_resched_lock, "reschedule-lock", MUTEX_DRIVER, 3333847Seh146360 (void *) sc->sc_iblk); 3343847Seh146360 /* 3353847Seh146360 * initialize the mfthread 3363847Seh146360 */ 3373847Seh146360 mutex_init(&sc->sc_mflock, "function-lock", MUTEX_DRIVER, 3383847Seh146360 (void *) sc->sc_iblk); 3393847Seh146360 cv_init(&sc->sc_mfthread_cv, NULL, CV_DRIVER, NULL); 3403847Seh146360 sc->sc_mf_thread = NULL; 3413847Seh146360 sc->sc_mfthread_switch = 0; 3423847Seh146360 /* 3433847Seh146360 * Initialize the wifi part, which will be used by 3443847Seh146360 * generic layer 3453847Seh146360 */ 3463847Seh146360 ic = &sc->sc_ic; 3473847Seh146360 ic->ic_phytype = IEEE80211_T_DS; 3483847Seh146360 ic->ic_opmode = IEEE80211_M_STA; 3493847Seh146360 ic->ic_state = IEEE80211_S_INIT; 3503847Seh146360 ic->ic_maxrssi = 49; 3513847Seh146360 /* 3523847Seh146360 * Future, could use s/w to handle encryption: IEEE80211_C_WEP 3533847Seh146360 * and need to add support for IEEE80211_C_IBSS 3543847Seh146360 */ 3553847Seh146360 ic->ic_caps = IEEE80211_C_SHPREAMBLE | IEEE80211_C_TXPMGT | 3563847Seh146360 IEEE80211_C_PMGT; 3573847Seh146360 ic->ic_sup_rates[IEEE80211_MODE_11B] = ipw2100_rateset_11b; 3583847Seh146360 IEEE80211_ADDR_COPY(ic->ic_macaddr, sc->sc_macaddr); 3593847Seh146360 for (i = 1; i < 16; i++) { 3603847Seh146360 if (sc->sc_chmask &(1 << i)) { 3613847Seh146360 /* IEEE80211_CHAN_B */ 3623847Seh146360 ic->ic_sup_channels[i].ich_freq = ieee80211_ieee2mhz(i, 3633847Seh146360 IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_CCK); 3643847Seh146360 ic->ic_sup_channels[i].ich_flags = 3653847Seh146360 IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_CCK; 3663847Seh146360 } 3673847Seh146360 } 3683847Seh146360 ic->ic_ibss_chan = &ic->ic_sup_channels[0]; 3693847Seh146360 ic->ic_xmit = ipw2100_send; 3703847Seh146360 /* 3713847Seh146360 * init Wifi layer 3723847Seh146360 */ 3733847Seh146360 ieee80211_attach(ic); 3743847Seh146360 3753847Seh146360 /* 3763847Seh146360 * Override 80211 default routines 3773847Seh146360 */ 3783847Seh146360 ieee80211_media_init(ic); 3793847Seh146360 sc->sc_newstate = ic->ic_newstate; 3803847Seh146360 ic->ic_newstate = ipw2100_newstate; 3813847Seh146360 /* 3823847Seh146360 * initialize default tx key 3833847Seh146360 */ 3843847Seh146360 ic->ic_def_txkey = 0; 3853847Seh146360 /* 3863847Seh146360 * Set the Authentication to AUTH_Open only. 3873847Seh146360 */ 3883847Seh146360 sc->sc_authmode = IEEE80211_AUTH_OPEN; 3893847Seh146360 3903847Seh146360 /* 3913847Seh146360 * Add the interrupt handler 3923847Seh146360 */ 3933847Seh146360 err = ddi_add_intr(dip, 0, &sc->sc_iblk, NULL, 3943847Seh146360 ipw2100_intr, (caddr_t)sc); 3953847Seh146360 if (err != DDI_SUCCESS) { 3963847Seh146360 IPW2100_WARN((dip, CE_WARN, 3973847Seh146360 "ipw2100_attach(): ddi_add_intr() failed\n")); 3983847Seh146360 goto fail5; 3993847Seh146360 } 4003847Seh146360 4013847Seh146360 /* 4023847Seh146360 * Initialize pointer to device specific functions 4033847Seh146360 */ 4043847Seh146360 wd.wd_secalloc = WIFI_SEC_NONE; 4053847Seh146360 wd.wd_opmode = ic->ic_opmode; 4067194Seh146360 IEEE80211_ADDR_COPY(wd.wd_bssid, ic->ic_bss->in_bssid); 4073847Seh146360 4083847Seh146360 macp = mac_alloc(MAC_VERSION); 4093847Seh146360 if (err != 0) { 4103847Seh146360 IPW2100_WARN((dip, CE_WARN, 4113847Seh146360 "ipw2100_attach(): mac_alloc() failed\n")); 4123847Seh146360 goto fail6; 4133847Seh146360 } 4143847Seh146360 4153847Seh146360 macp->m_type_ident = MAC_PLUGIN_IDENT_WIFI; 4163847Seh146360 macp->m_driver = sc; 4173847Seh146360 macp->m_dip = dip; 4183847Seh146360 macp->m_src_addr = ic->ic_macaddr; 4193847Seh146360 macp->m_callbacks = &ipw2100_m_callbacks; 4203847Seh146360 macp->m_min_sdu = 0; 4213847Seh146360 macp->m_max_sdu = IEEE80211_MTU; 4223847Seh146360 macp->m_pdata = &wd; 4233847Seh146360 macp->m_pdata_size = sizeof (wd); 4243847Seh146360 4253847Seh146360 /* 4263847Seh146360 * Register the macp to mac 4273847Seh146360 */ 4283847Seh146360 err = mac_register(macp, &ic->ic_mach); 4293847Seh146360 mac_free(macp); 4303847Seh146360 if (err != DDI_SUCCESS) { 4313847Seh146360 IPW2100_WARN((dip, CE_WARN, 4323847Seh146360 "ipw2100_attach(): mac_register() failed\n")); 4333847Seh146360 goto fail6; 4343847Seh146360 } 4353847Seh146360 4363847Seh146360 /* 4373847Seh146360 * Create minor node of type DDI_NT_NET_WIFI 4383847Seh146360 */ 4393847Seh146360 (void) snprintf(strbuf, sizeof (strbuf), "%s%d", 4403847Seh146360 IPW2100_DRV_NAME, instance); 4413847Seh146360 err = ddi_create_minor_node(dip, strbuf, S_IFCHR, 4423847Seh146360 instance + 1, DDI_NT_NET_WIFI, 0); 4433847Seh146360 if (err != DDI_SUCCESS) 4443847Seh146360 IPW2100_WARN((dip, CE_WARN, 4453847Seh146360 "ipw2100_attach(): ddi_create_minor_node() failed\n")); 4463847Seh146360 4473847Seh146360 /* 4483847Seh146360 * Cache firmware, always return true 4493847Seh146360 */ 4503847Seh146360 (void) ipw2100_cache_firmware(sc); 4513847Seh146360 4523847Seh146360 /* 4533847Seh146360 * Notify link is down now 4543847Seh146360 */ 4553847Seh146360 mac_link_update(ic->ic_mach, LINK_STATE_DOWN); 4563847Seh146360 4573847Seh146360 /* 4583847Seh146360 * create the mf thread to handle the link status, 4593847Seh146360 * recovery fatal error, etc. 4603847Seh146360 */ 4613847Seh146360 sc->sc_mfthread_switch = 1; 4623847Seh146360 if (sc->sc_mf_thread == NULL) 4633847Seh146360 sc->sc_mf_thread = thread_create((caddr_t)NULL, 0, 4643847Seh146360 ipw2100_thread, sc, 0, &p0, TS_RUN, minclsyspri); 4653847Seh146360 4663847Seh146360 return (DDI_SUCCESS); 4673847Seh146360 4683847Seh146360 fail6: 4693847Seh146360 ddi_remove_intr(dip, 0, sc->sc_iblk); 4703847Seh146360 fail5: 4713847Seh146360 ieee80211_detach(ic); 4723847Seh146360 4733847Seh146360 mutex_destroy(&sc->sc_ilock); 4743847Seh146360 mutex_destroy(&sc->sc_tx_lock); 4753847Seh146360 mutex_destroy(&sc->sc_mflock); 4763847Seh146360 mutex_destroy(&sc->sc_resched_lock); 4773847Seh146360 cv_destroy(&sc->sc_mfthread_cv); 4783847Seh146360 cv_destroy(&sc->sc_tx_cond); 4793847Seh146360 cv_destroy(&sc->sc_cmd_cond); 4803847Seh146360 cv_destroy(&sc->sc_fw_cond); 4813847Seh146360 fail4: 4823847Seh146360 ipw2100_ring_free(sc); 4833847Seh146360 fail3: 4843847Seh146360 ddi_regs_map_free(&sc->sc_ioh); 4853847Seh146360 fail2: 4863847Seh146360 ddi_soft_state_free(ipw2100_ssp, instance); 4873847Seh146360 fail1: 4883847Seh146360 return (err); 4893847Seh146360 } 4903847Seh146360 4913847Seh146360 int 4923847Seh146360 ipw2100_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 4933847Seh146360 { 4943847Seh146360 struct ipw2100_softc *sc = 4953847Seh146360 ddi_get_soft_state(ipw2100_ssp, ddi_get_instance(dip)); 4963847Seh146360 int err; 4973847Seh146360 4983847Seh146360 ASSERT(sc != NULL); 4993847Seh146360 5003847Seh146360 if (cmd != DDI_DETACH) 5013847Seh146360 return (DDI_FAILURE); 5023847Seh146360 5033847Seh146360 /* 5043847Seh146360 * Destroy the mf_thread 5053847Seh146360 */ 5063847Seh146360 mutex_enter(&sc->sc_mflock); 5073847Seh146360 sc->sc_mfthread_switch = 0; 5083847Seh146360 while (sc->sc_mf_thread != NULL) { 5093847Seh146360 if (cv_wait_sig(&sc->sc_mfthread_cv, &sc->sc_mflock) == 0) 5103847Seh146360 break; 5113847Seh146360 } 5123847Seh146360 mutex_exit(&sc->sc_mflock); 5133847Seh146360 5143847Seh146360 /* 5153847Seh146360 * Unregiste from the MAC layer subsystem 5163847Seh146360 */ 5173847Seh146360 err = mac_unregister(sc->sc_ic.ic_mach); 5183847Seh146360 if (err != DDI_SUCCESS) 5193847Seh146360 return (err); 5203847Seh146360 5213847Seh146360 ddi_remove_intr(dip, 0, sc->sc_iblk); 5223847Seh146360 5233847Seh146360 /* 5243847Seh146360 * destroy the cv 5253847Seh146360 */ 5263847Seh146360 mutex_destroy(&sc->sc_ilock); 5273847Seh146360 mutex_destroy(&sc->sc_tx_lock); 5283847Seh146360 mutex_destroy(&sc->sc_mflock); 5293847Seh146360 mutex_destroy(&sc->sc_resched_lock); 5303847Seh146360 cv_destroy(&sc->sc_mfthread_cv); 5313847Seh146360 cv_destroy(&sc->sc_tx_cond); 5323847Seh146360 cv_destroy(&sc->sc_cmd_cond); 5333847Seh146360 cv_destroy(&sc->sc_fw_cond); 5343847Seh146360 5353847Seh146360 /* 5363847Seh146360 * detach ieee80211 5373847Seh146360 */ 5383847Seh146360 ieee80211_detach(&sc->sc_ic); 5393847Seh146360 5403847Seh146360 (void) ipw2100_free_firmware(sc); 5413847Seh146360 ipw2100_ring_free(sc); 5423847Seh146360 5433847Seh146360 ddi_regs_map_free(&sc->sc_ioh); 5443847Seh146360 ddi_remove_minor_node(dip, NULL); 5453847Seh146360 ddi_soft_state_free(ipw2100_ssp, ddi_get_instance(dip)); 5463847Seh146360 5473847Seh146360 return (DDI_SUCCESS); 5483847Seh146360 } 5493847Seh146360 5504784Seh146360 /* ARGSUSED */ 5514784Seh146360 int 5524784Seh146360 ipw2100_reset(dev_info_t *dip, ddi_reset_cmd_t cmd) 5534784Seh146360 { 5544784Seh146360 struct ipw2100_softc *sc = 5554784Seh146360 ddi_get_soft_state(ipw2100_ssp, ddi_get_instance(dip)); 5564784Seh146360 ASSERT(sc != NULL); 5574784Seh146360 5584784Seh146360 ipw2100_stop(sc); 5594784Seh146360 5604784Seh146360 return (DDI_SUCCESS); 5614784Seh146360 } 5624784Seh146360 5633847Seh146360 static void 5643847Seh146360 ipw2100_tables_init(struct ipw2100_softc *sc) 5653847Seh146360 { 5663847Seh146360 sc->sc_table1_base = ipw2100_csr_get32(sc, IPW2100_CSR_TABLE1_BASE); 5673847Seh146360 sc->sc_table2_base = ipw2100_csr_get32(sc, IPW2100_CSR_TABLE2_BASE); 5683847Seh146360 } 5693847Seh146360 5703847Seh146360 static void 5713847Seh146360 ipw2100_stop(struct ipw2100_softc *sc) 5723847Seh146360 { 5733847Seh146360 struct ieee80211com *ic = &sc->sc_ic; 5743847Seh146360 5753847Seh146360 ipw2100_master_stop(sc); 5763847Seh146360 ipw2100_csr_put32(sc, IPW2100_CSR_RST, IPW2100_RST_SW_RESET); 5773847Seh146360 sc->sc_flags &= ~IPW2100_FLAG_FW_INITED; 5783847Seh146360 5793847Seh146360 ieee80211_new_state(ic, IEEE80211_S_INIT, -1); 5803847Seh146360 } 5813847Seh146360 5823847Seh146360 static int 5833847Seh146360 ipw2100_config(struct ipw2100_softc *sc) 5843847Seh146360 { 5853847Seh146360 struct ieee80211com *ic = &sc->sc_ic; 5863847Seh146360 struct ipw2100_security sec; 5873847Seh146360 struct ipw2100_wep_key wkey; 5883847Seh146360 struct ipw2100_scan_options sopt; 5893847Seh146360 struct ipw2100_configuration cfg; 5903847Seh146360 uint32_t data; 5913847Seh146360 int err, i; 5923847Seh146360 5933847Seh146360 /* 5943847Seh146360 * operation mode 5953847Seh146360 */ 5963847Seh146360 switch (ic->ic_opmode) { 5973847Seh146360 case IEEE80211_M_STA: 5983847Seh146360 case IEEE80211_M_HOSTAP: 5993847Seh146360 data = LE_32(IPW2100_MODE_BSS); 6003847Seh146360 break; 6013847Seh146360 6023847Seh146360 case IEEE80211_M_IBSS: 6033847Seh146360 case IEEE80211_M_AHDEMO: 6043847Seh146360 data = LE_32(IPW2100_MODE_IBSS); 6053847Seh146360 break; 6063847Seh146360 } 6073847Seh146360 6083847Seh146360 IPW2100_DBG(IPW2100_DBG_WIFI, (sc->sc_dip, CE_CONT, 6093847Seh146360 "ipw2100_config(): Setting mode to %u\n", LE_32(data))); 6103847Seh146360 6113847Seh146360 err = ipw2100_cmd(sc, IPW2100_CMD_SET_MODE, 6123847Seh146360 &data, sizeof (data)); 6133847Seh146360 if (err != DDI_SUCCESS) 6143847Seh146360 return (err); 6153847Seh146360 6163847Seh146360 /* 6173847Seh146360 * operation channel if IBSS or MONITOR 6183847Seh146360 */ 6193847Seh146360 if (ic->ic_opmode == IEEE80211_M_IBSS) { 6203847Seh146360 6213847Seh146360 data = LE_32(ieee80211_chan2ieee(ic, ic->ic_ibss_chan)); 6223847Seh146360 6233847Seh146360 IPW2100_DBG(IPW2100_DBG_WIFI, (sc->sc_dip, CE_CONT, 6243847Seh146360 "ipw2100_config(): Setting channel to %u\n", LE_32(data))); 6253847Seh146360 6263847Seh146360 err = ipw2100_cmd(sc, IPW2100_CMD_SET_CHANNEL, 6273847Seh146360 &data, sizeof (data)); 6283847Seh146360 if (err != DDI_SUCCESS) 6293847Seh146360 return (err); 6303847Seh146360 } 6313847Seh146360 6323847Seh146360 /* 6333847Seh146360 * set MAC address 6343847Seh146360 */ 6353847Seh146360 IPW2100_DBG(IPW2100_DBG_WIFI, (sc->sc_dip, CE_CONT, 6363847Seh146360 "ipw2100_config(): Setting MAC address to " 6373847Seh146360 "%02x:%02x:%02x:%02x:%02x:%02x\n", 6383847Seh146360 ic->ic_macaddr[0], ic->ic_macaddr[1], ic->ic_macaddr[2], 6393847Seh146360 ic->ic_macaddr[3], ic->ic_macaddr[4], ic->ic_macaddr[5])); 6403847Seh146360 err = ipw2100_cmd(sc, IPW2100_CMD_SET_MAC_ADDRESS, ic->ic_macaddr, 6413847Seh146360 IEEE80211_ADDR_LEN); 6423847Seh146360 if (err != DDI_SUCCESS) 6433847Seh146360 return (err); 6443847Seh146360 6453847Seh146360 /* 6463847Seh146360 * configuration capabilities 6473847Seh146360 */ 6483847Seh146360 cfg.flags = IPW2100_CFG_BSS_MASK | IPW2100_CFG_IBSS_MASK | 6493847Seh146360 IPW2100_CFG_PREAMBLE_AUTO | IPW2100_CFG_802_1x_ENABLE; 6503847Seh146360 if (ic->ic_opmode == IEEE80211_M_IBSS) 6513847Seh146360 cfg.flags |= IPW2100_CFG_IBSS_AUTO_START; 6523847Seh146360 if (sc->if_flags & IFF_PROMISC) 6533847Seh146360 cfg.flags |= IPW2100_CFG_PROMISCUOUS; 6543847Seh146360 cfg.flags = LE_32(cfg.flags); 6553847Seh146360 cfg.bss_chan = LE_32(sc->sc_chmask >> 1); 6563847Seh146360 cfg.ibss_chan = LE_32(sc->sc_chmask >> 1); 6573847Seh146360 6583847Seh146360 IPW2100_DBG(IPW2100_DBG_WIFI, (sc->sc_dip, CE_CONT, 6593847Seh146360 "ipw2100_config(): Setting configuration to 0x%x\n", 6603847Seh146360 LE_32(cfg.flags))); 6613847Seh146360 6623847Seh146360 err = ipw2100_cmd(sc, IPW2100_CMD_SET_CONFIGURATION, 6633847Seh146360 &cfg, sizeof (cfg)); 6643847Seh146360 6653847Seh146360 if (err != DDI_SUCCESS) 6663847Seh146360 return (err); 6673847Seh146360 6683847Seh146360 /* 6693847Seh146360 * set 802.11 Tx rates 6703847Seh146360 */ 6713847Seh146360 data = LE_32(0x3); /* 1, 2 */ 6723847Seh146360 IPW2100_DBG(IPW2100_DBG_WIFI, (sc->sc_dip, CE_CONT, 6733847Seh146360 "ipw2100_config(): Setting 802.11 Tx rates to 0x%x\n", 6743847Seh146360 LE_32(data))); 6753847Seh146360 err = ipw2100_cmd(sc, IPW2100_CMD_SET_BASIC_TX_RATES, 6763847Seh146360 &data, sizeof (data)); 6773847Seh146360 if (err != DDI_SUCCESS) 6783847Seh146360 return (err); 6793847Seh146360 6803847Seh146360 /* 6813847Seh146360 * set 802.11b Tx rates 6823847Seh146360 */ 6833847Seh146360 data = LE_32(0xf); /* 1, 2, 5.5, 11 */ 6843847Seh146360 IPW2100_DBG(IPW2100_DBG_WIFI, (sc->sc_dip, CE_CONT, 6853847Seh146360 "ipw2100_config(): Setting 802.11b Tx rates to 0x%x\n", 6863847Seh146360 LE_32(data))); 6873847Seh146360 err = ipw2100_cmd(sc, IPW2100_CMD_SET_TX_RATES, &data, sizeof (data)); 6883847Seh146360 if (err != DDI_SUCCESS) 6893847Seh146360 return (err); 6903847Seh146360 6913847Seh146360 /* 6923847Seh146360 * set power mode 6933847Seh146360 */ 6943847Seh146360 data = LE_32(IPW2100_POWER_MODE_CAM); 6953847Seh146360 IPW2100_DBG(IPW2100_DBG_WIFI, (sc->sc_dip, CE_CONT, 6963847Seh146360 "ipw2100_config(): Setting power mode to %u\n", LE_32(data))); 6973847Seh146360 err = ipw2100_cmd(sc, IPW2100_CMD_SET_POWER_MODE, &data, sizeof (data)); 6983847Seh146360 if (err != DDI_SUCCESS) 6993847Seh146360 return (err); 7003847Seh146360 7013847Seh146360 /* 7023847Seh146360 * set power index 7033847Seh146360 */ 7043847Seh146360 if (ic->ic_opmode == IEEE80211_M_IBSS) { 7053847Seh146360 data = LE_32(32); 7063847Seh146360 IPW2100_DBG(IPW2100_DBG_WIFI, (sc->sc_dip, CE_CONT, 7073847Seh146360 "ipw2100_config(): Setting Tx power index to %u\n", 7083847Seh146360 LE_32(data))); 7093847Seh146360 err = ipw2100_cmd(sc, IPW2100_CMD_SET_TX_POWER_INDEX, 7103847Seh146360 &data, sizeof (data)); 7113847Seh146360 if (err != DDI_SUCCESS) 7123847Seh146360 return (err); 7133847Seh146360 } 7143847Seh146360 7153847Seh146360 /* 7163847Seh146360 * set RTS threshold 7173847Seh146360 */ 7183847Seh146360 ic->ic_rtsthreshold = 2346; 7193847Seh146360 data = LE_32(ic->ic_rtsthreshold); 7203847Seh146360 IPW2100_DBG(IPW2100_DBG_WIFI, (sc->sc_dip, CE_CONT, 7213847Seh146360 "ipw2100_config(): Setting RTS threshold to %u\n", LE_32(data))); 7223847Seh146360 err = ipw2100_cmd(sc, IPW2100_CMD_SET_RTS_THRESHOLD, 7233847Seh146360 &data, sizeof (data)); 7243847Seh146360 if (err != DDI_SUCCESS) 7253847Seh146360 return (err); 7263847Seh146360 7273847Seh146360 /* 7283847Seh146360 * set frag threshold 7293847Seh146360 */ 7303847Seh146360 ic->ic_fragthreshold = 2346; 7313847Seh146360 data = LE_32(ic->ic_fragthreshold); 7323847Seh146360 IPW2100_DBG(IPW2100_DBG_WIFI, (sc->sc_dip, CE_CONT, 7333847Seh146360 "ipw2100_config(): Setting frag threshold to %u\n", LE_32(data))); 7343847Seh146360 err = ipw2100_cmd(sc, IPW2100_CMD_SET_FRAG_THRESHOLD, 7353847Seh146360 &data, sizeof (data)); 7363847Seh146360 if (err != DDI_SUCCESS) 7373847Seh146360 return (err); 7383847Seh146360 7393847Seh146360 /* 7403847Seh146360 * set ESSID 7413847Seh146360 */ 7423847Seh146360 IPW2100_DBG(IPW2100_DBG_WIFI, (sc->sc_dip, CE_CONT, 7433847Seh146360 "ipw2100_config(): Setting ESSID to %u, ESSID[0]%c\n", 7443847Seh146360 ic->ic_des_esslen, ic->ic_des_essid[0])); 7453847Seh146360 err = ipw2100_cmd(sc, IPW2100_CMD_SET_ESSID, 7463847Seh146360 ic->ic_des_essid, ic->ic_des_esslen); 7473847Seh146360 if (err != DDI_SUCCESS) 7483847Seh146360 return (err); 7493847Seh146360 7503847Seh146360 /* 7513847Seh146360 * no mandatory BSSID 7523847Seh146360 */ 7533847Seh146360 err = ipw2100_cmd(sc, IPW2100_CMD_SET_MANDATORY_BSSID, NULL, 0); 7543847Seh146360 if (err != DDI_SUCCESS) 7553847Seh146360 return (err); 7563847Seh146360 7573847Seh146360 /* 7583847Seh146360 * set BSSID, if any 7593847Seh146360 */ 7603847Seh146360 if (ic->ic_flags & IEEE80211_F_DESBSSID) { 7613847Seh146360 IPW2100_DBG(IPW2100_DBG_WIFI, (sc->sc_dip, CE_CONT, 7623847Seh146360 "ipw2100_config(): Setting BSSID to %u\n", 7633847Seh146360 IEEE80211_ADDR_LEN)); 7643847Seh146360 err = ipw2100_cmd(sc, IPW2100_CMD_SET_DESIRED_BSSID, 7653847Seh146360 ic->ic_des_bssid, IEEE80211_ADDR_LEN); 7663847Seh146360 if (err != DDI_SUCCESS) 7673847Seh146360 return (err); 7683847Seh146360 } 7693847Seh146360 7703847Seh146360 /* 7713847Seh146360 * set security information 7723847Seh146360 */ 7733847Seh146360 (void) memset(&sec, 0, sizeof (sec)); 7743847Seh146360 /* 7753847Seh146360 * use the value set to ic_bss to retrieve current sharedmode 7763847Seh146360 */ 7773847Seh146360 sec.authmode = (ic->ic_bss->in_authmode == WL_SHAREDKEY) ? 7783847Seh146360 IPW2100_AUTH_SHARED : IPW2100_AUTH_OPEN; 7793847Seh146360 sec.ciphers = LE_32(IPW2100_CIPHER_NONE); 7803847Seh146360 IPW2100_DBG(IPW2100_DBG_WIFI, (sc->sc_dip, CE_CONT, 7813847Seh146360 "ipw2100_config(): Setting authmode to %u\n", sec.authmode)); 7823847Seh146360 err = ipw2100_cmd(sc, IPW2100_CMD_SET_SECURITY_INFORMATION, 7833847Seh146360 &sec, sizeof (sec)); 7843847Seh146360 if (err != DDI_SUCCESS) 7853847Seh146360 return (err); 7863847Seh146360 7873847Seh146360 /* 7883847Seh146360 * set WEP if any 7893847Seh146360 */ 7903847Seh146360 if (ic->ic_flags & IEEE80211_F_PRIVACY) { 7913847Seh146360 for (i = 0; i < IEEE80211_WEP_NKID; i++) { 7923847Seh146360 if (ic->ic_nw_keys[i].wk_keylen == 0) 7933847Seh146360 continue; 7943847Seh146360 wkey.idx = (uint8_t)i; 7953847Seh146360 wkey.len = ic->ic_nw_keys[i].wk_keylen; 7963847Seh146360 (void) memset(wkey.key, 0, sizeof (wkey.key)); 7973847Seh146360 if (ic->ic_nw_keys[i].wk_keylen) 7983847Seh146360 (void) memcpy(wkey.key, 7993847Seh146360 ic->ic_nw_keys[i].wk_key, 8003847Seh146360 ic->ic_nw_keys[i].wk_keylen); 8013847Seh146360 err = ipw2100_cmd(sc, IPW2100_CMD_SET_WEP_KEY, 8023847Seh146360 &wkey, sizeof (wkey)); 8033847Seh146360 if (err != DDI_SUCCESS) 8043847Seh146360 return (err); 8053847Seh146360 } 8063847Seh146360 data = LE_32(ic->ic_def_txkey); 8073847Seh146360 err = ipw2100_cmd(sc, IPW2100_CMD_SET_WEP_KEY_INDEX, 8083847Seh146360 &data, sizeof (data)); 8093847Seh146360 if (err != DDI_SUCCESS) 8103847Seh146360 return (err); 8113847Seh146360 } 8123847Seh146360 8133847Seh146360 /* 8143847Seh146360 * turn on WEP 8153847Seh146360 */ 8163847Seh146360 data = LE_32((ic->ic_flags & IEEE80211_F_PRIVACY) ? 0x8 : 0); 8173847Seh146360 IPW2100_DBG(IPW2100_DBG_WIFI, (sc->sc_dip, CE_CONT, 8183847Seh146360 "ipw2100_config(): Setting WEP flags to %u\n", LE_32(data))); 8193847Seh146360 err = ipw2100_cmd(sc, IPW2100_CMD_SET_WEP_FLAGS, &data, sizeof (data)); 8203847Seh146360 if (err != DDI_SUCCESS) 8213847Seh146360 return (err); 8223847Seh146360 8233847Seh146360 /* 8243847Seh146360 * set beacon interval if IBSS or HostAP 8253847Seh146360 */ 8263847Seh146360 if (ic->ic_opmode == IEEE80211_M_IBSS || 8273847Seh146360 ic->ic_opmode == IEEE80211_M_HOSTAP) { 8283847Seh146360 8293847Seh146360 data = LE_32(ic->ic_lintval); 8303847Seh146360 IPW2100_DBG(IPW2100_DBG_WIFI, (sc->sc_dip, CE_CONT, 8313847Seh146360 "ipw2100_config(): Setting beacon interval to %u\n", 8323847Seh146360 LE_32(data))); 8333847Seh146360 err = ipw2100_cmd(sc, IPW2100_CMD_SET_BEACON_INTERVAL, 8343847Seh146360 &data, sizeof (data)); 8353847Seh146360 if (err != DDI_SUCCESS) 8363847Seh146360 return (err); 8373847Seh146360 } 8383847Seh146360 8393847Seh146360 /* 8403847Seh146360 * set scan options 8413847Seh146360 */ 8423847Seh146360 sopt.flags = LE_32(0); 8433847Seh146360 sopt.channels = LE_32(sc->sc_chmask >> 1); 8443847Seh146360 err = ipw2100_cmd(sc, IPW2100_CMD_SET_SCAN_OPTIONS, 8453847Seh146360 &sopt, sizeof (sopt)); 8463847Seh146360 if (err != DDI_SUCCESS) 8473847Seh146360 return (err); 8483847Seh146360 8493847Seh146360 en_adapter: 8503847Seh146360 8513847Seh146360 IPW2100_DBG(IPW2100_DBG_WIFI, (sc->sc_dip, CE_CONT, 8523847Seh146360 "ipw2100_config(): Enabling adapter\n")); 8533847Seh146360 8543847Seh146360 return (ipw2100_cmd(sc, IPW2100_CMD_ENABLE, NULL, 0)); 8553847Seh146360 } 8563847Seh146360 8573847Seh146360 static int 8583847Seh146360 ipw2100_cmd(struct ipw2100_softc *sc, uint32_t type, void *buf, size_t len) 8593847Seh146360 { 8603847Seh146360 struct ipw2100_bd *txbd; 8613847Seh146360 clock_t clk; 8623847Seh146360 uint32_t idx; 8633847Seh146360 8643847Seh146360 /* 8653847Seh146360 * prepare command buffer 8663847Seh146360 */ 8673847Seh146360 sc->sc_cmd->type = LE_32(type); 8683847Seh146360 sc->sc_cmd->subtype = LE_32(0); 8693847Seh146360 sc->sc_cmd->seq = LE_32(0); 8703847Seh146360 /* 8713847Seh146360 * copy data if any 8723847Seh146360 */ 8733847Seh146360 if (len && buf) 8743847Seh146360 (void) memcpy(sc->sc_cmd->data, buf, len); 8753847Seh146360 sc->sc_cmd->len = LE_32(len); 8763847Seh146360 8773847Seh146360 /* 8783847Seh146360 * get host & device descriptor to submit command 8793847Seh146360 */ 8803847Seh146360 mutex_enter(&sc->sc_tx_lock); 8813847Seh146360 8823847Seh146360 IPW2100_DBG(IPW2100_DBG_RING, (sc->sc_dip, CE_CONT, 8833847Seh146360 "ipw2100_cmd(): tx-free=%d\n", sc->sc_tx_free)); 8843847Seh146360 8853847Seh146360 /* 8863847Seh146360 * command need 1 descriptor 8873847Seh146360 */ 8883847Seh146360 while (sc->sc_tx_free < 1) { 8893847Seh146360 sc->sc_flags |= IPW2100_FLAG_CMD_WAIT; 8903847Seh146360 cv_wait(&sc->sc_tx_cond, &sc->sc_tx_lock); 8913847Seh146360 } 8923847Seh146360 idx = sc->sc_tx_cur; 8933847Seh146360 8943847Seh146360 IPW2100_DBG(IPW2100_DBG_RING, (sc->sc_dip, CE_CONT, 8953847Seh146360 "ipw2100_cmd(): tx-cur=%d\n", idx)); 8963847Seh146360 8973847Seh146360 sc->sc_done = 0; 8983847Seh146360 8993847Seh146360 txbd = &sc->sc_txbd[idx]; 9003847Seh146360 txbd->phyaddr = LE_32(sc->sc_dma_cmd.dr_pbase); 9013847Seh146360 txbd->len = LE_32(sizeof (struct ipw2100_cmd)); 9023847Seh146360 txbd->flags = IPW2100_BD_FLAG_TX_FRAME_COMMAND 9033847Seh146360 | IPW2100_BD_FLAG_TX_LAST_FRAGMENT; 9043847Seh146360 txbd->nfrag = 1; 9053847Seh146360 /* 9063847Seh146360 * sync for device 9073847Seh146360 */ 9083847Seh146360 (void) ddi_dma_sync(sc->sc_dma_cmd.dr_hnd, 0, 9093847Seh146360 sizeof (struct ipw2100_cmd), DDI_DMA_SYNC_FORDEV); 9103847Seh146360 (void) ddi_dma_sync(sc->sc_dma_txbd.dr_hnd, 9113847Seh146360 idx * sizeof (struct ipw2100_bd), 9123847Seh146360 sizeof (struct ipw2100_bd), DDI_DMA_SYNC_FORDEV); 9133847Seh146360 9143847Seh146360 /* 9153847Seh146360 * ring move forward 9163847Seh146360 */ 9173847Seh146360 sc->sc_tx_cur = RING_FORWARD(sc->sc_tx_cur, 1, IPW2100_NUM_TXBD); 9183847Seh146360 sc->sc_tx_free--; 9193847Seh146360 ipw2100_csr_put32(sc, IPW2100_CSR_TX_WRITE_INDEX, sc->sc_tx_cur); 9203847Seh146360 mutex_exit(&sc->sc_tx_lock); 9213847Seh146360 9223847Seh146360 /* 9233847Seh146360 * wait for command done 9243847Seh146360 */ 9253847Seh146360 mutex_enter(&sc->sc_ilock); 9263847Seh146360 while (sc->sc_done == 0) { 9273847Seh146360 /* 9283847Seh146360 * pending for the response 9293847Seh146360 */ 9303847Seh146360 clk = ddi_get_lbolt() + drv_usectohz(1000000); /* 1 second */ 9313847Seh146360 if (cv_timedwait(&sc->sc_cmd_cond, &sc->sc_ilock, clk) < 0) 9323847Seh146360 break; 9333847Seh146360 } 9343847Seh146360 mutex_exit(&sc->sc_ilock); 9353847Seh146360 9363847Seh146360 IPW2100_DBG(IPW2100_DBG_RING, (sc->sc_dip, CE_CONT, 9373847Seh146360 "ipw2100_cmd(): cmd-done=%s\n", sc->sc_done ? "yes" : "no")); 9383847Seh146360 9393847Seh146360 if (sc->sc_done == 0) 9403847Seh146360 return (DDI_FAILURE); 9413847Seh146360 9423847Seh146360 return (DDI_SUCCESS); 9433847Seh146360 } 9443847Seh146360 9453847Seh146360 int 9463847Seh146360 ipw2100_init(struct ipw2100_softc *sc) 9473847Seh146360 { 9483847Seh146360 int err; 9493847Seh146360 9503847Seh146360 /* 9513847Seh146360 * no firmware is available, return fail directly 9523847Seh146360 */ 9533847Seh146360 if (!(sc->sc_flags & IPW2100_FLAG_FW_CACHED)) { 9543847Seh146360 IPW2100_WARN((sc->sc_dip, CE_WARN, 9553847Seh146360 "ipw2100_init(): no firmware is available\n")); 9563847Seh146360 return (DDI_FAILURE); 9573847Seh146360 } 9583847Seh146360 9593847Seh146360 ipw2100_stop(sc); 9603847Seh146360 9613847Seh146360 err = ipw2100_chip_reset(sc); 9623847Seh146360 if (err != DDI_SUCCESS) { 9633847Seh146360 IPW2100_WARN((sc->sc_dip, CE_WARN, 9643847Seh146360 "ipw2100_init(): could not reset adapter\n")); 9653847Seh146360 goto fail; 9663847Seh146360 } 9673847Seh146360 9683847Seh146360 /* 9693847Seh146360 * load microcode 9703847Seh146360 */ 9713847Seh146360 err = ipw2100_load_uc(sc); 9723847Seh146360 if (err != DDI_SUCCESS) { 9733847Seh146360 IPW2100_WARN((sc->sc_dip, CE_WARN, 9743847Seh146360 "ipw2100_init(): could not load microcode, try again\n")); 9753847Seh146360 goto fail; 9763847Seh146360 } 9773847Seh146360 9783847Seh146360 ipw2100_master_stop(sc); 9793847Seh146360 9803847Seh146360 ipw2100_ring_hwsetup(sc); 9813847Seh146360 9823847Seh146360 /* 9833847Seh146360 * load firmware 9843847Seh146360 */ 9853847Seh146360 err = ipw2100_load_fw(sc); 9863847Seh146360 if (err != DDI_SUCCESS) { 9873847Seh146360 IPW2100_WARN((sc->sc_dip, CE_WARN, 9883847Seh146360 "ipw2100_init(): could not load firmware, try again\n")); 9893847Seh146360 goto fail; 9903847Seh146360 } 9913847Seh146360 9923847Seh146360 /* 9933847Seh146360 * initialize tables 9943847Seh146360 */ 9953847Seh146360 ipw2100_tables_init(sc); 9963847Seh146360 ipw2100_table1_put32(sc, IPW2100_INFO_LOCK, 0); 9973847Seh146360 9983847Seh146360 /* 9993847Seh146360 * Hardware will be enabled after configuration 10003847Seh146360 */ 10013847Seh146360 err = ipw2100_config(sc); 10023847Seh146360 if (err != DDI_SUCCESS) { 10033847Seh146360 IPW2100_WARN((sc->sc_dip, CE_WARN, 10043847Seh146360 "ipw2100_init(): device configuration failed\n")); 10053847Seh146360 goto fail; 10063847Seh146360 } 10073847Seh146360 10083847Seh146360 delay(drv_usectohz(delay_config_stable)); 10093847Seh146360 10103847Seh146360 return (DDI_SUCCESS); 10113847Seh146360 10123847Seh146360 fail: 10133847Seh146360 ipw2100_stop(sc); 10143847Seh146360 10153847Seh146360 return (err); 10163847Seh146360 } 10173847Seh146360 10183847Seh146360 /* 10193847Seh146360 * get hardware configurations from EEPROM embedded within chip 10203847Seh146360 */ 10213847Seh146360 static void 10223847Seh146360 ipw2100_hwconf_get(struct ipw2100_softc *sc) 10233847Seh146360 { 10243847Seh146360 int i; 10253847Seh146360 uint16_t val; 10263847Seh146360 10273847Seh146360 /* 10283847Seh146360 * MAC address 10293847Seh146360 */ 10303847Seh146360 i = 0; 10313847Seh146360 val = ipw2100_rom_get16(sc, IPW2100_ROM_MAC + 0); 10323847Seh146360 sc->sc_macaddr[i++] = val >> 8; 10333847Seh146360 sc->sc_macaddr[i++] = val & 0xff; 10343847Seh146360 val = ipw2100_rom_get16(sc, IPW2100_ROM_MAC + 1); 10353847Seh146360 sc->sc_macaddr[i++] = val >> 8; 10363847Seh146360 sc->sc_macaddr[i++] = val & 0xff; 10373847Seh146360 val = ipw2100_rom_get16(sc, IPW2100_ROM_MAC + 2); 10383847Seh146360 sc->sc_macaddr[i++] = val >> 8; 10393847Seh146360 sc->sc_macaddr[i++] = val & 0xff; 10403847Seh146360 10413847Seh146360 /* 10423847Seh146360 * formatted MAC address string 10433847Seh146360 */ 10443847Seh146360 (void) snprintf(sc->sc_macstr, sizeof (sc->sc_macstr), 10454784Seh146360 "%02x:%02x:%02x:%02x:%02x:%02x", 10464784Seh146360 sc->sc_macaddr[0], sc->sc_macaddr[1], 10474784Seh146360 sc->sc_macaddr[2], sc->sc_macaddr[3], 10484784Seh146360 sc->sc_macaddr[4], sc->sc_macaddr[5]); 10493847Seh146360 10503847Seh146360 /* 10513847Seh146360 * channel mask 10523847Seh146360 */ 10533847Seh146360 val = ipw2100_rom_get16(sc, IPW2100_ROM_CHANNEL_LIST); 10543847Seh146360 if (val == 0) 10553847Seh146360 val = 0x7ff; 10563847Seh146360 sc->sc_chmask = val << 1; 10573847Seh146360 IPW2100_DBG(IPW2100_DBG_HWCAP, (sc->sc_dip, CE_CONT, 10583847Seh146360 "ipw2100_hwconf_get(): channel-mask=0x%08x\n", sc->sc_chmask)); 10593847Seh146360 10603847Seh146360 /* 10613847Seh146360 * radio switch 10623847Seh146360 */ 10633847Seh146360 val = ipw2100_rom_get16(sc, IPW2100_ROM_RADIO); 10643847Seh146360 if (val & 0x08) 10653847Seh146360 sc->sc_flags |= IPW2100_FLAG_HAS_RADIO_SWITCH; 10663847Seh146360 10673847Seh146360 IPW2100_DBG(IPW2100_DBG_HWCAP, (sc->sc_dip, CE_CONT, 10683847Seh146360 "ipw2100_hwconf_get(): has-radio-switch=%s(%u)\n", 10693847Seh146360 (sc->sc_flags & IPW2100_FLAG_HAS_RADIO_SWITCH)? "yes" : "no", 10703847Seh146360 val)); 10713847Seh146360 } 10723847Seh146360 10733847Seh146360 /* 10743847Seh146360 * all ipw2100 interrupts will be masked by this routine 10753847Seh146360 */ 10763847Seh146360 static void 10773847Seh146360 ipw2100_master_stop(struct ipw2100_softc *sc) 10783847Seh146360 { 10793847Seh146360 uint32_t tmp; 10803847Seh146360 int ntries; 10813847Seh146360 10823847Seh146360 /* 10833847Seh146360 * disable interrupts 10843847Seh146360 */ 10853847Seh146360 ipw2100_csr_put32(sc, IPW2100_CSR_INTR_MASK, 0); 10863847Seh146360 10873847Seh146360 ipw2100_csr_put32(sc, IPW2100_CSR_RST, IPW2100_RST_STOP_MASTER); 10883847Seh146360 for (ntries = 0; ntries < 50; ntries++) { 10893847Seh146360 if (ipw2100_csr_get32(sc, IPW2100_CSR_RST) 10903847Seh146360 & IPW2100_RST_MASTER_DISABLED) 10913847Seh146360 break; 10923847Seh146360 drv_usecwait(10); 10933847Seh146360 } 10943847Seh146360 if (ntries == 50) 10953847Seh146360 IPW2100_WARN((sc->sc_dip, CE_WARN, 10963847Seh146360 "ipw2100_master_stop(): timeout when stop master\n")); 10973847Seh146360 10983847Seh146360 tmp = ipw2100_csr_get32(sc, IPW2100_CSR_RST); 10993847Seh146360 ipw2100_csr_put32(sc, IPW2100_CSR_RST, 11003847Seh146360 tmp | IPW2100_RST_PRINCETON_RESET); 11013847Seh146360 11023847Seh146360 sc->sc_flags &= ~IPW2100_FLAG_FW_INITED; 11033847Seh146360 } 11043847Seh146360 11053847Seh146360 /* 11063847Seh146360 * all ipw2100 interrupts will be masked by this routine 11073847Seh146360 */ 11083847Seh146360 static int 11093847Seh146360 ipw2100_chip_reset(struct ipw2100_softc *sc) 11103847Seh146360 { 11113847Seh146360 int ntries; 11123847Seh146360 uint32_t tmp; 11133847Seh146360 11143847Seh146360 ipw2100_master_stop(sc); 11153847Seh146360 11163847Seh146360 /* 11173847Seh146360 * move adatper to DO state 11183847Seh146360 */ 11193847Seh146360 tmp = ipw2100_csr_get32(sc, IPW2100_CSR_CTL); 11203847Seh146360 ipw2100_csr_put32(sc, IPW2100_CSR_CTL, tmp | IPW2100_CTL_INIT); 11213847Seh146360 11223847Seh146360 /* 11233847Seh146360 * wait for clock stabilization 11243847Seh146360 */ 11253847Seh146360 for (ntries = 0; ntries < 1000; ntries++) { 11263847Seh146360 if (ipw2100_csr_get32(sc, IPW2100_CSR_CTL) 11273847Seh146360 & IPW2100_CTL_CLOCK_READY) 11283847Seh146360 break; 11293847Seh146360 drv_usecwait(200); 11303847Seh146360 } 11313847Seh146360 if (ntries == 1000) 11323847Seh146360 return (DDI_FAILURE); 11333847Seh146360 11343847Seh146360 tmp = ipw2100_csr_get32(sc, IPW2100_CSR_RST); 11353847Seh146360 ipw2100_csr_put32(sc, IPW2100_CSR_RST, tmp | IPW2100_RST_SW_RESET); 11363847Seh146360 11373847Seh146360 drv_usecwait(10); 11383847Seh146360 11393847Seh146360 tmp = ipw2100_csr_get32(sc, IPW2100_CSR_CTL); 11403847Seh146360 ipw2100_csr_put32(sc, IPW2100_CSR_CTL, tmp | IPW2100_CTL_INIT); 11413847Seh146360 11423847Seh146360 return (DDI_SUCCESS); 11433847Seh146360 } 11443847Seh146360 11453847Seh146360 /* 11463847Seh146360 * get the radio status from IPW_CSR_IO, invoked by wificonfig/dladm 11473847Seh146360 */ 11483847Seh146360 int 11493847Seh146360 ipw2100_get_radio(struct ipw2100_softc *sc) 11503847Seh146360 { 11513847Seh146360 if (ipw2100_csr_get32(sc, IPW2100_CSR_IO) & IPW2100_IO_RADIO_DISABLED) 11523847Seh146360 return (0); 11533847Seh146360 else 11543847Seh146360 return (1); 11553847Seh146360 11563847Seh146360 } 11573847Seh146360 /* 11583847Seh146360 * This function is used to get the statistic, invoked by wificonfig/dladm 11593847Seh146360 */ 11603847Seh146360 void 11613847Seh146360 ipw2100_get_statistics(struct ipw2100_softc *sc) 11623847Seh146360 { 11633847Seh146360 struct ieee80211com *ic = &sc->sc_ic; 11643847Seh146360 uint32_t addr, size, i; 11653847Seh146360 uint32_t atbl[256], *datatbl; 11663847Seh146360 11673847Seh146360 datatbl = atbl; 11683847Seh146360 11693847Seh146360 if (!(sc->sc_flags & IPW2100_FLAG_FW_INITED)) { 11703847Seh146360 IPW2100_DBG(IPW2100_DBG_STATISTIC, (sc->sc_dip, CE_CONT, 11713847Seh146360 "ipw2100_get_statistic(): fw doesn't download yet.")); 11723847Seh146360 return; 11733847Seh146360 } 11743847Seh146360 11753847Seh146360 ipw2100_csr_put32(sc, IPW2100_CSR_AUTOINC_ADDR, sc->sc_table1_base); 11763847Seh146360 11773847Seh146360 size = ipw2100_csr_get32(sc, IPW2100_CSR_AUTOINC_DATA); 11783847Seh146360 atbl[0] = size; 11793847Seh146360 for (i = 1, ++datatbl; i < size; i++, datatbl++) { 11803847Seh146360 addr = ipw2100_csr_get32(sc, IPW2100_CSR_AUTOINC_DATA); 11813847Seh146360 *datatbl = ipw2100_imem_get32(sc, addr); 11823847Seh146360 } 11833847Seh146360 11843847Seh146360 /* 11853847Seh146360 * To retrieve the statistic information into proper places. There are 11863847Seh146360 * lot of information. 11873847Seh146360 */ 11883847Seh146360 IPW2100_DBG(IPW2100_DBG_STATISTIC, (sc->sc_dip, CE_CONT, 11893847Seh146360 "ipw2100_get_statistic(): \n" 11903847Seh146360 "operating mode = %u\n" 11913847Seh146360 "type of authentification= %u\n" 11923847Seh146360 "average RSSI= %u\n" 11933847Seh146360 "current channel = %d\n", 11943847Seh146360 atbl[191], atbl[199], atbl[173], atbl[189])); 11953847Seh146360 /* WIFI_STAT_TX_FRAGS */ 11963847Seh146360 ic->ic_stats.is_tx_frags = (uint32_t)atbl[2]; 11973847Seh146360 /* WIFI_STAT_MCAST_TX = (all frame - unicast frame) */ 11983847Seh146360 ic->ic_stats.is_tx_mcast = (uint32_t)atbl[2] - (uint32_t)atbl[3]; 11993847Seh146360 /* WIFI_STAT_TX_RETRANS */ 12003847Seh146360 ic->ic_stats.is_tx_retries = (uint32_t)atbl[42]; 12013847Seh146360 /* WIFI_STAT_TX_FAILED */ 12023847Seh146360 ic->ic_stats.is_tx_failed = (uint32_t)atbl[51]; 12033847Seh146360 /* MAC_STAT_OBYTES */ 12043847Seh146360 ic->ic_stats.is_tx_bytes = (uint32_t)atbl[41]; 12053847Seh146360 /* WIFI_STAT_RX_FRAGS */ 12063847Seh146360 ic->ic_stats.is_rx_frags = (uint32_t)atbl[61]; 12073847Seh146360 /* WIFI_STAT_MCAST_RX */ 12083847Seh146360 ic->ic_stats.is_rx_mcast = (uint32_t)atbl[71]; 12093847Seh146360 /* MAC_STAT_IBYTES */ 12103847Seh146360 ic->ic_stats.is_rx_bytes = (uint32_t)atbl[101]; 12113847Seh146360 /* WIFI_STAT_ACK_FAILURE */ 12123847Seh146360 ic->ic_stats.is_ack_failure = (uint32_t)atbl[59]; 12133847Seh146360 /* WIFI_STAT_RTS_SUCCESS */ 12143847Seh146360 ic->ic_stats.is_rts_success = (uint32_t)atbl[22]; 12153847Seh146360 } 12163847Seh146360 12173847Seh146360 /* 12183847Seh146360 * dma region alloc 12193847Seh146360 */ 12203847Seh146360 static int 12213847Seh146360 ipw2100_dma_region_alloc(struct ipw2100_softc *sc, 12223847Seh146360 struct dma_region *dr, size_t size, uint_t dir, uint_t flags) 12233847Seh146360 { 12243847Seh146360 dev_info_t *dip = sc->sc_dip; 12253847Seh146360 int err; 12263847Seh146360 12273847Seh146360 IPW2100_DBG(IPW2100_DBG_DMA, (dip, CE_CONT, 12283847Seh146360 "ipw2100_dma_region_alloc() name=%s size=%u\n", 12293847Seh146360 dr->dr_name, size)); 12303847Seh146360 12313847Seh146360 err = ddi_dma_alloc_handle(dip, &ipw2100_dma_attr, DDI_DMA_SLEEP, NULL, 12323847Seh146360 &dr->dr_hnd); 12333847Seh146360 if (err != DDI_SUCCESS) { 12343847Seh146360 IPW2100_DBG(IPW2100_DBG_DMA, (dip, CE_CONT, 12353847Seh146360 "ipw2100_dma_region_alloc(): " 12363847Seh146360 "ddi_dma_alloc_handle() failed\n")); 12373847Seh146360 goto fail0; 12383847Seh146360 } 12393847Seh146360 12403847Seh146360 err = ddi_dma_mem_alloc(dr->dr_hnd, size, &ipw2100_dma_accattr, 12413847Seh146360 flags, DDI_DMA_SLEEP, NULL, &dr->dr_base, 12423847Seh146360 &dr->dr_size, &dr->dr_acc); 12433847Seh146360 if (err != DDI_SUCCESS) { 12443847Seh146360 IPW2100_DBG(IPW2100_DBG_DMA, (dip, CE_CONT, 12453847Seh146360 "ipw2100_dma_region_alloc(): " 12463847Seh146360 "ddi_dma_mem_alloc() failed\n")); 12473847Seh146360 goto fail1; 12483847Seh146360 } 12493847Seh146360 12503847Seh146360 err = ddi_dma_addr_bind_handle(dr->dr_hnd, NULL, 12513847Seh146360 dr->dr_base, dr->dr_size, dir | flags, DDI_DMA_SLEEP, NULL, 12523847Seh146360 &dr->dr_cookie, &dr->dr_ccnt); 12533847Seh146360 if (err != DDI_DMA_MAPPED) { 12543847Seh146360 IPW2100_DBG(IPW2100_DBG_DMA, (dip, CE_CONT, 12553847Seh146360 "ipw2100_dma_region_alloc(): " 12563847Seh146360 "ddi_dma_addr_bind_handle() failed\n")); 12573847Seh146360 goto fail2; 12583847Seh146360 } 12593847Seh146360 12603847Seh146360 if (dr->dr_ccnt != 1) { 12613847Seh146360 err = DDI_FAILURE; 12623847Seh146360 goto fail3; 12633847Seh146360 } 12643847Seh146360 dr->dr_pbase = dr->dr_cookie.dmac_address; 12653847Seh146360 12663847Seh146360 IPW2100_DBG(IPW2100_DBG_DMA, (dip, CE_CONT, 12673847Seh146360 "ipw2100_dma_region_alloc(): get physical-base=0x%08x\n", 12683847Seh146360 dr->dr_pbase)); 12693847Seh146360 12703847Seh146360 return (DDI_SUCCESS); 12713847Seh146360 12723847Seh146360 fail3: 12733847Seh146360 (void) ddi_dma_unbind_handle(dr->dr_hnd); 12743847Seh146360 fail2: 12753847Seh146360 ddi_dma_mem_free(&dr->dr_acc); 12763847Seh146360 fail1: 12773847Seh146360 ddi_dma_free_handle(&dr->dr_hnd); 12783847Seh146360 fail0: 12793847Seh146360 return (err); 12803847Seh146360 } 12813847Seh146360 12823847Seh146360 static void 12833847Seh146360 ipw2100_dma_region_free(struct dma_region *dr) 12843847Seh146360 { 12853847Seh146360 (void) ddi_dma_unbind_handle(dr->dr_hnd); 12863847Seh146360 ddi_dma_mem_free(&dr->dr_acc); 12873847Seh146360 ddi_dma_free_handle(&dr->dr_hnd); 12883847Seh146360 } 12893847Seh146360 12903847Seh146360 static int 12913847Seh146360 ipw2100_ring_alloc(struct ipw2100_softc *sc) 12923847Seh146360 { 12933847Seh146360 int err, i; 12943847Seh146360 12953847Seh146360 /* 12963847Seh146360 * tx ring 12973847Seh146360 */ 12983847Seh146360 sc->sc_dma_txbd.dr_name = "ipw2100-tx-ring-bd"; 12993847Seh146360 err = ipw2100_dma_region_alloc(sc, &sc->sc_dma_txbd, 13003847Seh146360 IPW2100_TXBD_SIZE, DDI_DMA_WRITE, DDI_DMA_CONSISTENT); 13013847Seh146360 if (err != DDI_SUCCESS) 13023847Seh146360 goto fail0; 13033847Seh146360 /* 13043847Seh146360 * tx bufs 13053847Seh146360 */ 13063847Seh146360 for (i = 0; i < IPW2100_NUM_TXBUF; i++) { 13073847Seh146360 sc->sc_dma_txbufs[i].dr_name = "ipw2100-tx-buf"; 13083847Seh146360 err = ipw2100_dma_region_alloc(sc, &sc->sc_dma_txbufs[i], 13093847Seh146360 IPW2100_TXBUF_SIZE, DDI_DMA_WRITE, DDI_DMA_STREAMING); 13103847Seh146360 if (err != DDI_SUCCESS) { 13113847Seh146360 while (i >= 0) { 13123847Seh146360 ipw2100_dma_region_free(&sc->sc_dma_txbufs[i]); 13133847Seh146360 i--; 13143847Seh146360 } 13153847Seh146360 goto fail1; 13163847Seh146360 } 13173847Seh146360 } 13183847Seh146360 /* 13193847Seh146360 * rx ring 13203847Seh146360 */ 13213847Seh146360 sc->sc_dma_rxbd.dr_name = "ipw2100-rx-ring-bd"; 13223847Seh146360 err = ipw2100_dma_region_alloc(sc, &sc->sc_dma_rxbd, 13233847Seh146360 IPW2100_RXBD_SIZE, DDI_DMA_WRITE, DDI_DMA_CONSISTENT); 13243847Seh146360 if (err != DDI_SUCCESS) 13253847Seh146360 goto fail2; 13263847Seh146360 /* 13273847Seh146360 * rx bufs 13283847Seh146360 */ 13293847Seh146360 for (i = 0; i < IPW2100_NUM_RXBUF; i++) { 13303847Seh146360 sc->sc_dma_rxbufs[i].dr_name = "ipw2100-rx-buf"; 13313847Seh146360 err = ipw2100_dma_region_alloc(sc, &sc->sc_dma_rxbufs[i], 13323847Seh146360 IPW2100_RXBUF_SIZE, DDI_DMA_READ, DDI_DMA_STREAMING); 13333847Seh146360 if (err != DDI_SUCCESS) { 13343847Seh146360 while (i >= 0) { 13353847Seh146360 ipw2100_dma_region_free(&sc->sc_dma_rxbufs[i]); 13363847Seh146360 i--; 13373847Seh146360 } 13383847Seh146360 goto fail3; 13393847Seh146360 } 13403847Seh146360 } 13413847Seh146360 /* 13423847Seh146360 * status 13433847Seh146360 */ 13443847Seh146360 sc->sc_dma_status.dr_name = "ipw2100-rx-status"; 13453847Seh146360 err = ipw2100_dma_region_alloc(sc, &sc->sc_dma_status, 13463847Seh146360 IPW2100_STATUS_SIZE, DDI_DMA_READ, DDI_DMA_CONSISTENT); 13473847Seh146360 if (err != DDI_SUCCESS) 13483847Seh146360 goto fail4; 13493847Seh146360 /* 13503847Seh146360 * command 13513847Seh146360 */ 13523847Seh146360 sc->sc_dma_cmd.dr_name = "ipw2100-cmd"; 13533847Seh146360 err = ipw2100_dma_region_alloc(sc, &sc->sc_dma_cmd, IPW2100_CMD_SIZE, 13543847Seh146360 DDI_DMA_WRITE, DDI_DMA_CONSISTENT); 13553847Seh146360 if (err != DDI_SUCCESS) 13563847Seh146360 goto fail5; 13573847Seh146360 13583847Seh146360 return (DDI_SUCCESS); 13593847Seh146360 13603847Seh146360 fail5: 13613847Seh146360 ipw2100_dma_region_free(&sc->sc_dma_status); 13623847Seh146360 fail4: 13633847Seh146360 for (i = 0; i < IPW2100_NUM_RXBUF; i++) 13643847Seh146360 ipw2100_dma_region_free(&sc->sc_dma_rxbufs[i]); 13653847Seh146360 fail3: 13663847Seh146360 ipw2100_dma_region_free(&sc->sc_dma_rxbd); 13673847Seh146360 fail2: 13683847Seh146360 for (i = 0; i < IPW2100_NUM_TXBUF; i++) 13693847Seh146360 ipw2100_dma_region_free(&sc->sc_dma_txbufs[i]); 13703847Seh146360 fail1: 13713847Seh146360 ipw2100_dma_region_free(&sc->sc_dma_txbd); 13723847Seh146360 fail0: 13733847Seh146360 return (err); 13743847Seh146360 } 13753847Seh146360 13763847Seh146360 static void 13773847Seh146360 ipw2100_ring_free(struct ipw2100_softc *sc) 13783847Seh146360 { 13793847Seh146360 int i; 13803847Seh146360 13813847Seh146360 /* 13823847Seh146360 * tx ring 13833847Seh146360 */ 13843847Seh146360 ipw2100_dma_region_free(&sc->sc_dma_txbd); 13853847Seh146360 /* 13863847Seh146360 * tx buf 13873847Seh146360 */ 13883847Seh146360 for (i = 0; i < IPW2100_NUM_TXBUF; i++) 13893847Seh146360 ipw2100_dma_region_free(&sc->sc_dma_txbufs[i]); 13903847Seh146360 /* 13913847Seh146360 * rx ring 13923847Seh146360 */ 13933847Seh146360 ipw2100_dma_region_free(&sc->sc_dma_rxbd); 13943847Seh146360 /* 13953847Seh146360 * rx buf 13963847Seh146360 */ 13973847Seh146360 for (i = 0; i < IPW2100_NUM_RXBUF; i++) 13983847Seh146360 ipw2100_dma_region_free(&sc->sc_dma_rxbufs[i]); 13993847Seh146360 /* 14003847Seh146360 * status 14013847Seh146360 */ 14023847Seh146360 ipw2100_dma_region_free(&sc->sc_dma_status); 14033847Seh146360 /* 14043847Seh146360 * command 14053847Seh146360 */ 14063847Seh146360 ipw2100_dma_region_free(&sc->sc_dma_cmd); 14073847Seh146360 } 14083847Seh146360 14093847Seh146360 static void 14103847Seh146360 ipw2100_ring_reset(struct ipw2100_softc *sc) 14113847Seh146360 { 14123847Seh146360 int i; 14133847Seh146360 14143847Seh146360 /* 14153847Seh146360 * tx ring 14163847Seh146360 */ 14173847Seh146360 sc->sc_tx_cur = 0; 14183847Seh146360 sc->sc_tx_free = IPW2100_NUM_TXBD; 14193847Seh146360 sc->sc_txbd = (struct ipw2100_bd *)sc->sc_dma_txbd.dr_base; 14203847Seh146360 for (i = 0; i < IPW2100_NUM_TXBUF; i++) 14213847Seh146360 sc->sc_txbufs[i] = 14223847Seh146360 (struct ipw2100_txb *)sc->sc_dma_txbufs[i].dr_base; 14233847Seh146360 /* 14243847Seh146360 * rx ring 14253847Seh146360 */ 14263847Seh146360 sc->sc_rx_cur = 0; 14273847Seh146360 sc->sc_rx_free = IPW2100_NUM_RXBD; 14283847Seh146360 sc->sc_status = (struct ipw2100_status *)sc->sc_dma_status.dr_base; 14293847Seh146360 sc->sc_rxbd = (struct ipw2100_bd *)sc->sc_dma_rxbd.dr_base; 14303847Seh146360 for (i = 0; i < IPW2100_NUM_RXBUF; i++) { 14313847Seh146360 sc->sc_rxbufs[i] = 14323847Seh146360 (struct ipw2100_rxb *)sc->sc_dma_rxbufs[i].dr_base; 14333847Seh146360 /* 14343847Seh146360 * initialize Rx buffer descriptors, both host and device 14353847Seh146360 */ 14363847Seh146360 sc->sc_rxbd[i].phyaddr = LE_32(sc->sc_dma_rxbufs[i].dr_pbase); 14373847Seh146360 sc->sc_rxbd[i].len = LE_32(sc->sc_dma_rxbufs[i].dr_size); 14383847Seh146360 sc->sc_rxbd[i].flags = 0; 14393847Seh146360 sc->sc_rxbd[i].nfrag = 1; 14403847Seh146360 } 14413847Seh146360 /* 14423847Seh146360 * command 14433847Seh146360 */ 14443847Seh146360 sc->sc_cmd = (struct ipw2100_cmd *)sc->sc_dma_cmd.dr_base; 14453847Seh146360 } 14463847Seh146360 14473847Seh146360 /* 14483847Seh146360 * tx, rx rings and command initialization 14493847Seh146360 */ 14503847Seh146360 static int 14513847Seh146360 ipw2100_ring_init(struct ipw2100_softc *sc) 14523847Seh146360 { 14533847Seh146360 int err; 14543847Seh146360 14553847Seh146360 err = ipw2100_ring_alloc(sc); 14563847Seh146360 if (err != DDI_SUCCESS) 14573847Seh146360 return (err); 14583847Seh146360 14593847Seh146360 ipw2100_ring_reset(sc); 14603847Seh146360 14613847Seh146360 return (DDI_SUCCESS); 14623847Seh146360 } 14633847Seh146360 14643847Seh146360 static void 14653847Seh146360 ipw2100_ring_hwsetup(struct ipw2100_softc *sc) 14663847Seh146360 { 14673847Seh146360 ipw2100_ring_reset(sc); 14683847Seh146360 /* 14693847Seh146360 * tx ring 14703847Seh146360 */ 14713847Seh146360 ipw2100_csr_put32(sc, IPW2100_CSR_TX_BD_BASE, sc->sc_dma_txbd.dr_pbase); 14723847Seh146360 ipw2100_csr_put32(sc, IPW2100_CSR_TX_BD_SIZE, IPW2100_NUM_TXBD); 14733847Seh146360 /* 14743847Seh146360 * no new packet to transmit, tx-rd-index == tx-wr-index 14753847Seh146360 */ 14763847Seh146360 ipw2100_csr_put32(sc, IPW2100_CSR_TX_READ_INDEX, sc->sc_tx_cur); 14773847Seh146360 ipw2100_csr_put32(sc, IPW2100_CSR_TX_WRITE_INDEX, sc->sc_tx_cur); 14783847Seh146360 /* 14793847Seh146360 * rx ring 14803847Seh146360 */ 14813847Seh146360 ipw2100_csr_put32(sc, IPW2100_CSR_RX_BD_BASE, sc->sc_dma_rxbd.dr_pbase); 14823847Seh146360 ipw2100_csr_put32(sc, IPW2100_CSR_RX_BD_SIZE, IPW2100_NUM_RXBD); 14833847Seh146360 /* 14843847Seh146360 * all rx buffer are empty, rx-rd-index == 0 && rx-wr-index == N-1 14853847Seh146360 */ 14863847Seh146360 IPW2100_DBG(IPW2100_DBG_RING, (sc->sc_dip, CE_CONT, 14873847Seh146360 "ipw2100_ring_hwsetup(): rx-cur=%u, backward=%u\n", 14883847Seh146360 sc->sc_rx_cur, RING_BACKWARD(sc->sc_rx_cur, 1, IPW2100_NUM_RXBD))); 14893847Seh146360 ipw2100_csr_put32(sc, IPW2100_CSR_RX_READ_INDEX, sc->sc_rx_cur); 14903847Seh146360 ipw2100_csr_put32(sc, IPW2100_CSR_RX_WRITE_INDEX, 14913847Seh146360 RING_BACKWARD(sc->sc_rx_cur, 1, IPW2100_NUM_RXBD)); 14923847Seh146360 /* 14933847Seh146360 * status 14943847Seh146360 */ 14953847Seh146360 ipw2100_csr_put32(sc, IPW2100_CSR_RX_STATUS_BASE, 14963847Seh146360 sc->sc_dma_status.dr_pbase); 14973847Seh146360 } 14983847Seh146360 14993847Seh146360 /* 15003847Seh146360 * ieee80211_new_state() is not be used, since the hardware can handle the 15013847Seh146360 * state transfer. Here, we just keep the status of the hardware notification 15023847Seh146360 * result. 15033847Seh146360 */ 15043847Seh146360 /* ARGSUSED */ 15053847Seh146360 static int 15063847Seh146360 ipw2100_newstate(struct ieee80211com *ic, enum ieee80211_state state, int arg) 15073847Seh146360 { 15083847Seh146360 struct ipw2100_softc *sc = (struct ipw2100_softc *)ic; 15093847Seh146360 struct ieee80211_node *in; 15103847Seh146360 uint8_t macaddr[IEEE80211_ADDR_LEN]; 15113847Seh146360 uint32_t len; 15123847Seh146360 wifi_data_t wd = { 0 }; 15133847Seh146360 15143847Seh146360 IPW2100_DBG(IPW2100_DBG_WIFI, (sc->sc_dip, CE_CONT, 15153847Seh146360 "ipw2100_newstate(): %s -> %s\n", 15163847Seh146360 ieee80211_state_name[ic->ic_state], ieee80211_state_name[state])); 15173847Seh146360 15183847Seh146360 switch (state) { 15193847Seh146360 case IEEE80211_S_RUN: 15203847Seh146360 /* 15213847Seh146360 * we only need to use BSSID as to find the node 15223847Seh146360 */ 15233847Seh146360 drv_usecwait(200); /* firmware needs a short delay here */ 15243847Seh146360 len = IEEE80211_ADDR_LEN; 15253847Seh146360 (void) ipw2100_table2_getbuf(sc, IPW2100_INFO_CURRENT_BSSID, 15263847Seh146360 macaddr, &len); 15273847Seh146360 15283847Seh146360 in = ieee80211_find_node(&ic->ic_scan, macaddr); 15293847Seh146360 if (in == NULL) 15303847Seh146360 break; 15313847Seh146360 15323847Seh146360 (void) ieee80211_sta_join(ic, in); 15333847Seh146360 ieee80211_node_authorize(in); 15343847Seh146360 15353847Seh146360 /* 15363847Seh146360 * We can send data now; update the fastpath with our 15373847Seh146360 * current associated BSSID. 15383847Seh146360 */ 15393847Seh146360 if (ic->ic_flags & IEEE80211_F_PRIVACY) 15403847Seh146360 wd.wd_secalloc = WIFI_SEC_WEP; 15413847Seh146360 else 15423847Seh146360 wd.wd_secalloc = WIFI_SEC_NONE; 15433847Seh146360 wd.wd_opmode = ic->ic_opmode; 15443847Seh146360 IEEE80211_ADDR_COPY(wd.wd_bssid, ic->ic_bss->in_bssid); 15453847Seh146360 (void) mac_pdata_update(ic->ic_mach, &wd, sizeof (wd)); 15463847Seh146360 15473847Seh146360 break; 15483847Seh146360 15493847Seh146360 case IEEE80211_S_INIT: 15503847Seh146360 case IEEE80211_S_SCAN: 15513847Seh146360 case IEEE80211_S_AUTH: 15523847Seh146360 case IEEE80211_S_ASSOC: 15533847Seh146360 break; 15543847Seh146360 } 15553847Seh146360 15563847Seh146360 /* 15573847Seh146360 * notify to update the link 15583847Seh146360 */ 15593847Seh146360 if ((ic->ic_state != IEEE80211_S_RUN) && (state == IEEE80211_S_RUN)) { 15603847Seh146360 /* 15613847Seh146360 * previously disconnected and now connected 15623847Seh146360 */ 15633847Seh146360 sc->sc_linkstate = LINK_STATE_UP; 15643847Seh146360 sc->sc_flags |= IPW2100_FLAG_LINK_CHANGE; 15653847Seh146360 } else if ((ic->ic_state == IEEE80211_S_RUN) && 15663847Seh146360 (state != IEEE80211_S_RUN)) { 15673847Seh146360 /* 15683847Seh146360 * previously connected andd now disconnected 15693847Seh146360 */ 15703847Seh146360 sc->sc_linkstate = LINK_STATE_DOWN; 15713847Seh146360 sc->sc_flags |= IPW2100_FLAG_LINK_CHANGE; 15723847Seh146360 } 15733847Seh146360 15743847Seh146360 ic->ic_state = state; 15753847Seh146360 return (DDI_SUCCESS); 15763847Seh146360 } 15773847Seh146360 15783847Seh146360 /* 15793847Seh146360 * GLD operations 15803847Seh146360 */ 15813847Seh146360 /* ARGSUSED */ 15823847Seh146360 static int 15833847Seh146360 ipw2100_m_stat(void *arg, uint_t stat, uint64_t *val) 15843847Seh146360 { 15853847Seh146360 ieee80211com_t *ic = (ieee80211com_t *)arg; 15863847Seh146360 IPW2100_DBG(IPW2100_DBG_GLD, (((struct ipw2100_softc *)arg)->sc_dip, 15874784Seh146360 CE_CONT, 15884784Seh146360 "ipw2100_m_stat(): enter\n")); 15893847Seh146360 /* 15903847Seh146360 * some of below statistic data are from hardware, some from net80211 15913847Seh146360 */ 15923847Seh146360 switch (stat) { 15933847Seh146360 case MAC_STAT_RBYTES: 15943847Seh146360 *val = ic->ic_stats.is_rx_bytes; 15953847Seh146360 break; 15963847Seh146360 case MAC_STAT_IPACKETS: 15973847Seh146360 *val = ic->ic_stats.is_rx_frags; 15983847Seh146360 break; 15993847Seh146360 case MAC_STAT_OBYTES: 16003847Seh146360 *val = ic->ic_stats.is_tx_bytes; 16013847Seh146360 break; 16023847Seh146360 case MAC_STAT_OPACKETS: 16033847Seh146360 *val = ic->ic_stats.is_tx_frags; 16043847Seh146360 break; 16053847Seh146360 /* 16063847Seh146360 * Get below from hardware statistic, retrieve net80211 value once 1s 16073847Seh146360 */ 16083847Seh146360 case WIFI_STAT_TX_FRAGS: 16093847Seh146360 case WIFI_STAT_MCAST_TX: 16103847Seh146360 case WIFI_STAT_TX_FAILED: 16113847Seh146360 case WIFI_STAT_TX_RETRANS: 16123847Seh146360 case WIFI_STAT_RTS_SUCCESS: 16133847Seh146360 case WIFI_STAT_ACK_FAILURE: 16143847Seh146360 case WIFI_STAT_RX_FRAGS: 16153847Seh146360 case WIFI_STAT_MCAST_RX: 16163847Seh146360 /* 16173847Seh146360 * Get blow information from net80211 16183847Seh146360 */ 16193847Seh146360 case WIFI_STAT_RTS_FAILURE: 16203847Seh146360 case WIFI_STAT_RX_DUPS: 16213847Seh146360 case WIFI_STAT_FCS_ERRORS: 16223847Seh146360 case WIFI_STAT_WEP_ERRORS: 16233847Seh146360 return (ieee80211_stat(ic, stat, val)); 16243847Seh146360 /* 16253847Seh146360 * need be supported in the future 16263847Seh146360 */ 16273847Seh146360 case MAC_STAT_IFSPEED: 16283847Seh146360 case MAC_STAT_NOXMTBUF: 16293847Seh146360 case MAC_STAT_IERRORS: 16303847Seh146360 case MAC_STAT_OERRORS: 16313847Seh146360 default: 16323847Seh146360 return (ENOTSUP); 16333847Seh146360 } 16343847Seh146360 return (0); 16353847Seh146360 } 16363847Seh146360 16373847Seh146360 /* ARGSUSED */ 16383847Seh146360 static int 16393847Seh146360 ipw2100_m_multicst(void *arg, boolean_t add, const uint8_t *mca) 16403847Seh146360 { 16413847Seh146360 /* not supported */ 16423847Seh146360 IPW2100_DBG(IPW2100_DBG_GLD, (((struct ipw2100_softc *)arg)->sc_dip, 16434784Seh146360 CE_CONT, 16444784Seh146360 "ipw2100_m_multicst(): enter\n")); 16453847Seh146360 16467194Seh146360 return (0); 16473847Seh146360 } 16483847Seh146360 16493847Seh146360 /* 16503847Seh146360 * This thread function is used to handle the fatal error. 16513847Seh146360 */ 16523847Seh146360 static void 16533847Seh146360 ipw2100_thread(struct ipw2100_softc *sc) 16543847Seh146360 { 16553847Seh146360 struct ieee80211com *ic = &sc->sc_ic; 16563847Seh146360 int32_t nlstate; 16573847Seh146360 int stat_cnt = 0; 16583847Seh146360 16593847Seh146360 IPW2100_DBG(IPW2100_DBG_SOFTINT, (sc->sc_dip, CE_CONT, 16603847Seh146360 "ipw2100_thread(): into ipw2100 thread--> %d\n", 16613847Seh146360 sc->sc_linkstate)); 16623847Seh146360 16633847Seh146360 mutex_enter(&sc->sc_mflock); 16643847Seh146360 16653847Seh146360 while (sc->sc_mfthread_switch) { 16663847Seh146360 /* 16673847Seh146360 * notify the link state 16683847Seh146360 */ 16693847Seh146360 if (ic->ic_mach && (sc->sc_flags & IPW2100_FLAG_LINK_CHANGE)) { 16703847Seh146360 IPW2100_DBG(IPW2100_DBG_SOFTINT, (sc->sc_dip, CE_CONT, 16713847Seh146360 "ipw2100_thread(): link status --> %d\n", 16723847Seh146360 sc->sc_linkstate)); 16733847Seh146360 16743847Seh146360 sc->sc_flags &= ~IPW2100_FLAG_LINK_CHANGE; 16753847Seh146360 nlstate = sc->sc_linkstate; 16763847Seh146360 16773847Seh146360 mutex_exit(&sc->sc_mflock); 16783847Seh146360 mac_link_update(ic->ic_mach, nlstate); 16793847Seh146360 mutex_enter(&sc->sc_mflock); 16803847Seh146360 } 16813847Seh146360 16823847Seh146360 /* 16833847Seh146360 * recovery interrupt fatal error 16843847Seh146360 */ 16853847Seh146360 if (ic->ic_mach && 16863847Seh146360 (sc->sc_flags & IPW2100_FLAG_HW_ERR_RECOVER)) { 16873847Seh146360 16883847Seh146360 IPW2100_DBG(IPW2100_DBG_FATAL, (sc->sc_dip, CE_CONT, 16893847Seh146360 "try to recover fatal hw error\n")); 16903847Seh146360 sc->sc_flags &= ~IPW2100_FLAG_HW_ERR_RECOVER; 16913847Seh146360 16923847Seh146360 mutex_exit(&sc->sc_mflock); 16933847Seh146360 (void) ipw2100_init(sc); /* Force stat machine */ 16943847Seh146360 delay(drv_usectohz(delay_fatal_recover)); 16953847Seh146360 mutex_enter(&sc->sc_mflock); 16963847Seh146360 } 16973847Seh146360 16983847Seh146360 /* 16993847Seh146360 * get statistic, the value will be retrieved by m_stat 17003847Seh146360 */ 17013847Seh146360 if (stat_cnt == 10) { 17023847Seh146360 stat_cnt = 0; /* re-start */ 17033847Seh146360 17043847Seh146360 mutex_exit(&sc->sc_mflock); 17053847Seh146360 ipw2100_get_statistics(sc); 17063847Seh146360 mutex_enter(&sc->sc_mflock); 17073847Seh146360 } else 17083847Seh146360 stat_cnt++; /* until 1s */ 17093847Seh146360 17103847Seh146360 mutex_exit(&sc->sc_mflock); 17113847Seh146360 delay(drv_usectohz(delay_aux_thread)); 17123847Seh146360 mutex_enter(&sc->sc_mflock); 17133847Seh146360 } 17143847Seh146360 sc->sc_mf_thread = NULL; 17153847Seh146360 cv_broadcast(&sc->sc_mfthread_cv); 17163847Seh146360 mutex_exit(&sc->sc_mflock); 17173847Seh146360 } 17183847Seh146360 17193847Seh146360 static int 17203847Seh146360 ipw2100_m_start(void *arg) 17213847Seh146360 { 17223847Seh146360 struct ipw2100_softc *sc = (struct ipw2100_softc *)arg; 17233847Seh146360 17243847Seh146360 IPW2100_DBG(IPW2100_DBG_GLD, (sc->sc_dip, CE_CONT, 17253847Seh146360 "ipw2100_m_start(): enter\n")); 17263847Seh146360 17273847Seh146360 /* 17283847Seh146360 * initialize ipw2100 hardware 17293847Seh146360 */ 17303847Seh146360 (void) ipw2100_init(sc); 17313847Seh146360 17323847Seh146360 sc->sc_flags |= IPW2100_FLAG_RUNNING; 17333847Seh146360 /* 17343847Seh146360 * fix KCF bug. - workaround, need to fix it in net80211 17353847Seh146360 */ 17363847Seh146360 (void) crypto_mech2id(SUN_CKM_RC4); 17373847Seh146360 17387194Seh146360 return (0); 17393847Seh146360 } 17403847Seh146360 17413847Seh146360 static void 17423847Seh146360 ipw2100_m_stop(void *arg) 17433847Seh146360 { 17443847Seh146360 struct ipw2100_softc *sc = (struct ipw2100_softc *)arg; 17453847Seh146360 17463847Seh146360 IPW2100_DBG(IPW2100_DBG_GLD, (sc->sc_dip, CE_CONT, 17473847Seh146360 "ipw2100_m_stop(): enter\n")); 17483847Seh146360 17493847Seh146360 ipw2100_stop(sc); 17503847Seh146360 17513847Seh146360 sc->sc_flags &= ~IPW2100_FLAG_RUNNING; 17523847Seh146360 } 17533847Seh146360 17543847Seh146360 static int 17553847Seh146360 ipw2100_m_unicst(void *arg, const uint8_t *macaddr) 17563847Seh146360 { 17573847Seh146360 struct ipw2100_softc *sc = (struct ipw2100_softc *)arg; 17583847Seh146360 struct ieee80211com *ic = &sc->sc_ic; 17593847Seh146360 int err; 17603847Seh146360 17613847Seh146360 IPW2100_DBG(IPW2100_DBG_GLD, (sc->sc_dip, CE_CONT, 17623847Seh146360 "ipw2100_m_unicst(): enter\n")); 17633847Seh146360 17643847Seh146360 IPW2100_DBG(IPW2100_DBG_GLD, (sc->sc_dip, CE_CONT, 17653847Seh146360 "ipw2100_m_unicst(): GLD setting MAC address to " 17663847Seh146360 "%02x:%02x:%02x:%02x:%02x:%02x\n", 17673847Seh146360 macaddr[0], macaddr[1], macaddr[2], 17683847Seh146360 macaddr[3], macaddr[4], macaddr[5])); 17693847Seh146360 17703847Seh146360 if (!IEEE80211_ADDR_EQ(ic->ic_macaddr, macaddr)) { 17713847Seh146360 IEEE80211_ADDR_COPY(ic->ic_macaddr, macaddr); 17723847Seh146360 17733847Seh146360 if (sc->sc_flags & IPW2100_FLAG_RUNNING) { 17743847Seh146360 err = ipw2100_config(sc); 17753847Seh146360 if (err != DDI_SUCCESS) { 17763847Seh146360 IPW2100_WARN((sc->sc_dip, CE_WARN, 17773847Seh146360 "ipw2100_m_unicst(): " 17783847Seh146360 "device configuration failed\n")); 17793847Seh146360 goto fail; 17803847Seh146360 } 17813847Seh146360 } 17823847Seh146360 } 17833847Seh146360 17847194Seh146360 return (0); 17853847Seh146360 fail: 17867194Seh146360 return (EIO); 17873847Seh146360 } 17883847Seh146360 17893847Seh146360 static int 17903847Seh146360 ipw2100_m_promisc(void *arg, boolean_t on) 17913847Seh146360 { 17923847Seh146360 struct ipw2100_softc *sc = (struct ipw2100_softc *)arg; 17933847Seh146360 int recfg, err; 17943847Seh146360 17953847Seh146360 IPW2100_DBG(IPW2100_DBG_GLD, (sc->sc_dip, CE_CONT, 17963847Seh146360 "ipw2100_m_promisc(): enter. " 17973847Seh146360 "GLD setting promiscuous mode - %d\n", on)); 17983847Seh146360 17993847Seh146360 recfg = 0; 18003847Seh146360 if (on) 18013847Seh146360 if (!(sc->if_flags & IFF_PROMISC)) { 18023847Seh146360 sc->if_flags |= IFF_PROMISC; 18033847Seh146360 recfg = 1; 18043847Seh146360 } 18053847Seh146360 else 18063847Seh146360 if (sc->if_flags & IFF_PROMISC) { 18073847Seh146360 sc->if_flags &= ~IFF_PROMISC; 18083847Seh146360 recfg = 1; 18093847Seh146360 } 18103847Seh146360 18113847Seh146360 if (recfg && (sc->sc_flags & IPW2100_FLAG_RUNNING)) { 18123847Seh146360 err = ipw2100_config(sc); 18133847Seh146360 if (err != DDI_SUCCESS) { 18143847Seh146360 IPW2100_WARN((sc->sc_dip, CE_WARN, 18153847Seh146360 "ipw2100_m_promisc(): " 18163847Seh146360 "device configuration failed\n")); 18173847Seh146360 goto fail; 18183847Seh146360 } 18193847Seh146360 } 18203847Seh146360 18217194Seh146360 return (0); 18223847Seh146360 fail: 18237194Seh146360 return (EIO); 18243847Seh146360 } 18253847Seh146360 18263847Seh146360 static mblk_t * 18273847Seh146360 ipw2100_m_tx(void *arg, mblk_t *mp) 18283847Seh146360 { 18293847Seh146360 struct ipw2100_softc *sc = (struct ipw2100_softc *)arg; 18303847Seh146360 struct ieee80211com *ic = &sc->sc_ic; 18313847Seh146360 mblk_t *next; 18323847Seh146360 18333847Seh146360 /* 18343847Seh146360 * No data frames go out unless we're associated; this 18353847Seh146360 * should not happen as the 802.11 layer does not enable 18363847Seh146360 * the xmit queue until we enter the RUN state. 18373847Seh146360 */ 18383847Seh146360 if (ic->ic_state != IEEE80211_S_RUN) { 18393847Seh146360 IPW2100_DBG(IPW2100_DBG_GLD, (sc->sc_dip, CE_CONT, 18403847Seh146360 "ipw2100_m_tx(): discard msg, ic_state = %u\n", 18413847Seh146360 ic->ic_state)); 18423847Seh146360 freemsgchain(mp); 18433847Seh146360 return (NULL); 18443847Seh146360 } 18453847Seh146360 18463847Seh146360 while (mp != NULL) { 18473847Seh146360 next = mp->b_next; 18483847Seh146360 mp->b_next = NULL; 18493847Seh146360 if (ipw2100_send(ic, mp, IEEE80211_FC0_TYPE_DATA) != 18503847Seh146360 DDI_SUCCESS) { 18513847Seh146360 mp->b_next = next; 18523847Seh146360 break; 18533847Seh146360 } 18543847Seh146360 mp = next; 18553847Seh146360 } 18563847Seh146360 return (mp); 18573847Seh146360 } 18583847Seh146360 18593847Seh146360 /* ARGSUSED */ 18603847Seh146360 static int 18613847Seh146360 ipw2100_send(ieee80211com_t *ic, mblk_t *mp, uint8_t type) 18623847Seh146360 { 18633847Seh146360 struct ipw2100_softc *sc = (struct ipw2100_softc *)ic; 18643847Seh146360 struct ieee80211_node *in; 18653847Seh146360 struct ieee80211_frame wh, *wh_tmp; 18663847Seh146360 struct ieee80211_key *k; 18673847Seh146360 uint8_t *hdat; 18683847Seh146360 mblk_t *m0, *m; 18693847Seh146360 size_t cnt, off; 18703847Seh146360 struct ipw2100_bd *txbd[2]; 18713847Seh146360 struct ipw2100_txb *txbuf; 18723847Seh146360 struct dma_region *dr; 18733847Seh146360 struct ipw2100_hdr *h; 18743847Seh146360 uint32_t idx, bidx; 18753847Seh146360 int err; 18763847Seh146360 18773847Seh146360 ASSERT(mp->b_next == NULL); 18783847Seh146360 18793847Seh146360 m0 = NULL; 18803847Seh146360 m = NULL; 18813847Seh146360 err = DDI_SUCCESS; 18823847Seh146360 18833847Seh146360 IPW2100_DBG(IPW2100_DBG_GLD, (sc->sc_dip, CE_CONT, 18843847Seh146360 "ipw2100_send(): enter\n")); 18853847Seh146360 18863847Seh146360 if ((type & IEEE80211_FC0_TYPE_MASK) != IEEE80211_FC0_TYPE_DATA) { 18873847Seh146360 /* 18883847Seh146360 * it is impossible to send non-data 802.11 frame in current 18893847Seh146360 * ipw driver. Therefore, drop the package 18903847Seh146360 */ 18913847Seh146360 freemsg(mp); 18923847Seh146360 err = DDI_SUCCESS; 18933847Seh146360 goto fail0; 18943847Seh146360 } 18953847Seh146360 18963847Seh146360 mutex_enter(&sc->sc_tx_lock); 18973847Seh146360 18983847Seh146360 /* 18993847Seh146360 * need 2 descriptors: 1 for SEND cmd parameter header, 19003847Seh146360 * and the other for payload, i.e., 802.11 frame including 802.11 19013847Seh146360 * frame header 19023847Seh146360 */ 19033847Seh146360 if (sc->sc_tx_free < 2) { 19043847Seh146360 mutex_enter(&sc->sc_resched_lock); 19053847Seh146360 IPW2100_DBG(IPW2100_DBG_RING, (sc->sc_dip, CE_WARN, 19063847Seh146360 "ipw2100_send(): no enough descriptors(%d)\n", 19073847Seh146360 sc->sc_tx_free)); 19083847Seh146360 ic->ic_stats.is_tx_nobuf++; /* no enough buffer */ 19093847Seh146360 sc->sc_flags |= IPW2100_FLAG_TX_SCHED; 19103847Seh146360 err = DDI_FAILURE; 19113847Seh146360 mutex_exit(&sc->sc_resched_lock); 19123847Seh146360 goto fail1; 19133847Seh146360 } 19143847Seh146360 IPW2100_DBG(IPW2100_DBG_RING, (sc->sc_dip, CE_CONT, 19153847Seh146360 "ipw2100_send(): tx-free=%d,tx-curr=%d\n", 19163847Seh146360 sc->sc_tx_free, sc->sc_tx_cur)); 19173847Seh146360 19183847Seh146360 wh_tmp = (struct ieee80211_frame *)mp->b_rptr; 19193847Seh146360 in = ieee80211_find_txnode(ic, wh_tmp->i_addr1); 19203847Seh146360 if (in == NULL) { /* can not find tx node, drop the package */ 19213847Seh146360 freemsg(mp); 19223847Seh146360 err = DDI_SUCCESS; 19233847Seh146360 goto fail1; 19243847Seh146360 } 19253847Seh146360 in->in_inact = 0; 19263847Seh146360 (void) ieee80211_encap(ic, mp, in); 19273847Seh146360 ieee80211_free_node(in); 19283847Seh146360 19293847Seh146360 if (wh_tmp->i_fc[1] & IEEE80211_FC1_WEP) { 19303847Seh146360 /* 19313847Seh146360 * it is very bad that ieee80211_crypto_encap can only accept a 19323847Seh146360 * single continuous buffer. 19333847Seh146360 */ 19343847Seh146360 /* 19353847Seh146360 * allocate 32 more bytes is to be compatible with further 19363847Seh146360 * ieee802.11i standard. 19373847Seh146360 */ 19383847Seh146360 m = allocb(msgdsize(mp) + 32, BPRI_MED); 19393847Seh146360 if (m == NULL) { /* can not alloc buf, drop this package */ 19403847Seh146360 IPW2100_DBG(IPW2100_DBG_WIFI, (sc->sc_dip, CE_CONT, 19413847Seh146360 "ipw2100_send(): msg allocation failed\n")); 19423847Seh146360 19433847Seh146360 freemsg(mp); 19443847Seh146360 19453847Seh146360 err = DDI_SUCCESS; 19463847Seh146360 goto fail1; 19473847Seh146360 } 19483847Seh146360 off = 0; 19493847Seh146360 m0 = mp; 19503847Seh146360 while (m0) { 19513847Seh146360 cnt = MBLKL(m0); 19523847Seh146360 if (cnt) { 19533847Seh146360 (void) memcpy(m->b_rptr + off, m0->b_rptr, cnt); 19543847Seh146360 off += cnt; 19553847Seh146360 } 19563847Seh146360 m0 = m0->b_cont; 19573847Seh146360 } 19583847Seh146360 m->b_wptr += off; 19593847Seh146360 IPW2100_DBG(IPW2100_DBG_WIFI, (sc->sc_dip, CE_CONT, 19603847Seh146360 "ipw2100_send(): " 19613847Seh146360 "Encrypting 802.11 frame started, %d, %d\n", 19623847Seh146360 msgdsize(mp), MBLKL(mp))); 19633847Seh146360 k = ieee80211_crypto_encap(ic, m); 19643847Seh146360 if (k == NULL) { /* can not get the key, drop packages */ 19653847Seh146360 IPW2100_DBG(IPW2100_DBG_WIFI, (sc->sc_dip, CE_CONT, 19663847Seh146360 "ipw2100_send(): " 19673847Seh146360 "Encrypting 802.11 frame failed\n")); 19683847Seh146360 19693847Seh146360 freemsg(mp); 19703847Seh146360 err = DDI_SUCCESS; 19713847Seh146360 goto fail2; 19723847Seh146360 } 19733847Seh146360 IPW2100_DBG(IPW2100_DBG_WIFI, (sc->sc_dip, CE_CONT, 19743847Seh146360 "ipw2100_send(): " 19753847Seh146360 "Encrypting 802.11 frame finished, %d, %d, k=0x%08x\n", 19763847Seh146360 msgdsize(mp), MBLKL(mp), k->wk_flags)); 19773847Seh146360 } 19783847Seh146360 19793847Seh146360 /* 19803847Seh146360 * header descriptor 19813847Seh146360 */ 19823847Seh146360 idx = sc->sc_tx_cur; 19833847Seh146360 txbd[0] = &sc->sc_txbd[idx]; 19843847Seh146360 if ((idx & 1) == 0) 19853847Seh146360 bidx = idx / 2; 19863847Seh146360 sc->sc_tx_cur = RING_FORWARD(sc->sc_tx_cur, 1, IPW2100_NUM_TXBD); 19873847Seh146360 sc->sc_tx_free--; 19883847Seh146360 19893847Seh146360 /* 19903847Seh146360 * payload descriptor 19913847Seh146360 */ 19923847Seh146360 idx = sc->sc_tx_cur; 19933847Seh146360 txbd[1] = &sc->sc_txbd[idx]; 19943847Seh146360 if ((idx & 1) == 0) 19953847Seh146360 bidx = idx / 2; 19963847Seh146360 sc->sc_tx_cur = RING_FORWARD(sc->sc_tx_cur, 1, IPW2100_NUM_TXBD); 19973847Seh146360 sc->sc_tx_free--; 19983847Seh146360 19993847Seh146360 /* 20003847Seh146360 * one buffer, SEND cmd header and payload buffer 20013847Seh146360 */ 20023847Seh146360 txbuf = sc->sc_txbufs[bidx]; 20033847Seh146360 dr = &sc->sc_dma_txbufs[bidx]; 20043847Seh146360 20053847Seh146360 /* 20063847Seh146360 * extract 802.11 header from message, fill wh from m0 20073847Seh146360 */ 20083847Seh146360 hdat = (uint8_t *)&wh; 20093847Seh146360 off = 0; 20103847Seh146360 if (m) 20113847Seh146360 m0 = m; 20123847Seh146360 else 20133847Seh146360 m0 = mp; 20143847Seh146360 while (off < sizeof (wh)) { 20153847Seh146360 cnt = MBLKL(m0); 20163847Seh146360 if (cnt > (sizeof (wh) - off)) 20173847Seh146360 cnt = sizeof (wh) - off; 20183847Seh146360 if (cnt) { 20193847Seh146360 (void) memcpy(hdat + off, m0->b_rptr, cnt); 20203847Seh146360 off += cnt; 20213847Seh146360 m0->b_rptr += cnt; 20223847Seh146360 } 20233847Seh146360 else 20243847Seh146360 m0 = m0->b_cont; 20253847Seh146360 } 20263847Seh146360 20273847Seh146360 /* 20283847Seh146360 * prepare SEND cmd header 20293847Seh146360 */ 20303847Seh146360 h = &txbuf->txb_hdr; 20313847Seh146360 h->type = LE_32(IPW2100_CMD_SEND); 20323847Seh146360 h->subtype = LE_32(0); 20333847Seh146360 h->encrypted = ic->ic_flags & IEEE80211_F_PRIVACY ? 1 : 0; 20343847Seh146360 h->encrypt = 0; 20353847Seh146360 h->keyidx = 0; 20363847Seh146360 h->keysz = 0; 20373847Seh146360 h->fragsz = LE_16(0); 20383847Seh146360 IEEE80211_ADDR_COPY(h->saddr, wh.i_addr2); 20393847Seh146360 if (ic->ic_opmode == IEEE80211_M_STA) 20403847Seh146360 IEEE80211_ADDR_COPY(h->daddr, wh.i_addr3); 20413847Seh146360 else 20423847Seh146360 IEEE80211_ADDR_COPY(h->daddr, wh.i_addr1); 20433847Seh146360 20443847Seh146360 /* 20453847Seh146360 * extract payload from message into tx data buffer 20463847Seh146360 */ 20473847Seh146360 off = 0; 20483847Seh146360 while (m0) { 20493847Seh146360 cnt = MBLKL(m0); 20503847Seh146360 if (cnt) { 20513847Seh146360 (void) memcpy(&txbuf->txb_dat[off], m0->b_rptr, cnt); 20523847Seh146360 off += cnt; 20533847Seh146360 } 20543847Seh146360 m0 = m0->b_cont; 20553847Seh146360 } 20563847Seh146360 20573847Seh146360 /* 20583847Seh146360 * fill SEND cmd header descriptor 20593847Seh146360 */ 20603847Seh146360 txbd[0]->phyaddr = LE_32(dr->dr_pbase + 20613847Seh146360 OFFSETOF(struct ipw2100_txb, txb_hdr)); 20623847Seh146360 txbd[0]->len = LE_32(sizeof (struct ipw2100_hdr)); 20633847Seh146360 txbd[0]->flags = IPW2100_BD_FLAG_TX_FRAME_802_3 | 20643847Seh146360 IPW2100_BD_FLAG_TX_NOT_LAST_FRAGMENT; 20653847Seh146360 txbd[0]->nfrag = 2; 20663847Seh146360 /* 20673847Seh146360 * fill payload descriptor 20683847Seh146360 */ 20693847Seh146360 txbd[1]->phyaddr = LE_32(dr->dr_pbase + 20703847Seh146360 OFFSETOF(struct ipw2100_txb, txb_dat[0])); 20713847Seh146360 txbd[1]->len = LE_32(off); 20723847Seh146360 txbd[1]->flags = IPW2100_BD_FLAG_TX_FRAME_802_3 | 20733847Seh146360 IPW2100_BD_FLAG_TX_LAST_FRAGMENT; 20743847Seh146360 txbd[1]->nfrag = 0; 20753847Seh146360 20763847Seh146360 /* 20773847Seh146360 * dma sync 20783847Seh146360 */ 20793847Seh146360 (void) ddi_dma_sync(dr->dr_hnd, 0, sizeof (struct ipw2100_txb), 20803847Seh146360 DDI_DMA_SYNC_FORDEV); 20813847Seh146360 (void) ddi_dma_sync(sc->sc_dma_txbd.dr_hnd, 20823847Seh146360 (txbd[0] - sc->sc_txbd) * sizeof (struct ipw2100_bd), 20833847Seh146360 sizeof (struct ipw2100_bd), DDI_DMA_SYNC_FORDEV); 20843847Seh146360 /* 20853847Seh146360 * since txbd[1] may not be successive to txbd[0] due to the ring 20863847Seh146360 * organization, another dma_sync is needed to simplify the logic 20873847Seh146360 */ 20883847Seh146360 (void) ddi_dma_sync(sc->sc_dma_txbd.dr_hnd, 20893847Seh146360 (txbd[1] - sc->sc_txbd) * sizeof (struct ipw2100_bd), 20903847Seh146360 sizeof (struct ipw2100_bd), DDI_DMA_SYNC_FORDEV); 20913847Seh146360 /* 20923847Seh146360 * update txcur 20933847Seh146360 */ 20943847Seh146360 ipw2100_csr_put32(sc, IPW2100_CSR_TX_WRITE_INDEX, sc->sc_tx_cur); 20953847Seh146360 20963847Seh146360 if (mp) /* success, free the original message */ 20973847Seh146360 freemsg(mp); 20983847Seh146360 fail2: 20993847Seh146360 if (m) 21003847Seh146360 freemsg(m); 21013847Seh146360 fail1: 21023847Seh146360 mutex_exit(&sc->sc_tx_lock); 21033847Seh146360 fail0: 21043847Seh146360 IPW2100_DBG(IPW2100_DBG_GLD, (sc->sc_dip, CE_CONT, 21053847Seh146360 "ipw2100_send(): exit - err=%d\n", err)); 21063847Seh146360 21073847Seh146360 return (err); 21083847Seh146360 } 21093847Seh146360 21103847Seh146360 /* 21113847Seh146360 * IOCTL Handler 21123847Seh146360 */ 21133847Seh146360 #define IEEE80211_IOCTL_REQUIRED (1) 21143847Seh146360 #define IEEE80211_IOCTL_NOT_REQUIRED (0) 21153847Seh146360 static void 21163847Seh146360 ipw2100_m_ioctl(void *arg, queue_t *q, mblk_t *m) 21173847Seh146360 { 21183847Seh146360 struct ipw2100_softc *sc = (struct ipw2100_softc *)arg; 21193847Seh146360 struct ieee80211com *ic = &sc->sc_ic; 21203847Seh146360 int err; 21213847Seh146360 21223847Seh146360 IPW2100_DBG(IPW2100_DBG_GLD, (sc->sc_dip, CE_CONT, 21233847Seh146360 "ipw2100_m_ioctl(): enter\n")); 21243847Seh146360 21253847Seh146360 /* 21263847Seh146360 * check whether or not need to handle this in net80211 21273847Seh146360 */ 21283847Seh146360 if (ipw2100_ioctl(sc, q, m) == IEEE80211_IOCTL_NOT_REQUIRED) 21293847Seh146360 return; /* succes or fail */ 21303847Seh146360 21313847Seh146360 err = ieee80211_ioctl(ic, q, m); 21323847Seh146360 if (err == ENETRESET) { 21333847Seh146360 if (sc->sc_flags & IPW2100_FLAG_RUNNING) { 21343847Seh146360 (void) ipw2100_m_start(sc); 21353847Seh146360 (void) ieee80211_new_state(ic, 21363847Seh146360 IEEE80211_S_SCAN, -1); 21373847Seh146360 } 21383847Seh146360 } 21393847Seh146360 if (err == ERESTART) { 21403847Seh146360 if (sc->sc_flags & IPW2100_FLAG_RUNNING) 21413847Seh146360 (void) ipw2100_chip_reset(sc); 21423847Seh146360 } 21433847Seh146360 } 21443847Seh146360 21453847Seh146360 static int 21463847Seh146360 ipw2100_ioctl(struct ipw2100_softc *sc, queue_t *q, mblk_t *m) 21473847Seh146360 { 21483847Seh146360 struct iocblk *iocp; 21493847Seh146360 uint32_t len, ret, cmd; 21503847Seh146360 mblk_t *m0; 21513847Seh146360 boolean_t need_privilege; 21523847Seh146360 boolean_t need_net80211; 21533847Seh146360 21543847Seh146360 if (MBLKL(m) < sizeof (struct iocblk)) { 21553847Seh146360 IPW2100_DBG(IPW2100_DBG_IOCTL, (sc->sc_dip, CE_CONT, 21563847Seh146360 "ipw2100_ioctl(): ioctl buffer too short, %u\n", 21573847Seh146360 MBLKL(m))); 21583847Seh146360 miocnak(q, m, 0, EINVAL); 21593847Seh146360 return (IEEE80211_IOCTL_NOT_REQUIRED); 21603847Seh146360 } 21613847Seh146360 21623847Seh146360 /* 21633847Seh146360 * Validate the command 21643847Seh146360 */ 21657194Seh146360 iocp = (struct iocblk *)(uintptr_t)m->b_rptr; 21663847Seh146360 iocp->ioc_error = 0; 21673847Seh146360 cmd = iocp->ioc_cmd; 21683847Seh146360 need_privilege = B_TRUE; 21693847Seh146360 switch (cmd) { 21703847Seh146360 case WLAN_SET_PARAM: 21713847Seh146360 case WLAN_COMMAND: 21723847Seh146360 break; 21733847Seh146360 case WLAN_GET_PARAM: 21743847Seh146360 need_privilege = B_FALSE; 21753847Seh146360 break; 21763847Seh146360 default: 21773847Seh146360 IPW2100_DBG(IPW2100_DBG_IOCTL, (sc->sc_dip, CE_CONT, 21783847Seh146360 "ieee80211_ioctl(): unknown cmd 0x%x", cmd)); 21793847Seh146360 miocnak(q, m, 0, EINVAL); 21803847Seh146360 return (IEEE80211_IOCTL_NOT_REQUIRED); 21813847Seh146360 } 21823847Seh146360 2183*7408SSebastien.Roy@Sun.COM if (need_privilege && (ret = secpolicy_dl_config(iocp->ioc_cr)) != 0) { 2184*7408SSebastien.Roy@Sun.COM miocnak(q, m, 0, ret); 2185*7408SSebastien.Roy@Sun.COM return (IEEE80211_IOCTL_NOT_REQUIRED); 21863847Seh146360 } 2187*7408SSebastien.Roy@Sun.COM 21883847Seh146360 /* 21893847Seh146360 * sanity check 21903847Seh146360 */ 21913847Seh146360 m0 = m->b_cont; 21923847Seh146360 if (iocp->ioc_count == 0 || iocp->ioc_count < sizeof (wldp_t) || 21933847Seh146360 m0 == NULL) { 21943847Seh146360 miocnak(q, m, 0, EINVAL); 21953847Seh146360 return (IEEE80211_IOCTL_NOT_REQUIRED); 21963847Seh146360 } 21973847Seh146360 /* 21983847Seh146360 * assuming single data block 21993847Seh146360 */ 22003847Seh146360 if (m0->b_cont) { 22013847Seh146360 freemsg(m0->b_cont); 22023847Seh146360 m0->b_cont = NULL; 22033847Seh146360 } 22043847Seh146360 22053847Seh146360 need_net80211 = B_FALSE; 22063847Seh146360 ret = ipw2100_getset(sc, m0, cmd, &need_net80211); 22073847Seh146360 if (!need_net80211) { 22083847Seh146360 len = msgdsize(m0); 22093847Seh146360 22103847Seh146360 IPW2100_DBG(IPW2100_DBG_IOCTL, (sc->sc_dip, CE_CONT, 22113847Seh146360 "ipw2100_ioctl(): go to call miocack with " 22123847Seh146360 "ret = %d, len = %d\n", ret, len)); 22133847Seh146360 miocack(q, m, len, ret); 22143847Seh146360 return (IEEE80211_IOCTL_NOT_REQUIRED); 22153847Seh146360 } 22163847Seh146360 22173847Seh146360 /* 22183847Seh146360 * IEEE80211_IOCTL_REQUIRED - need net80211 handle 22193847Seh146360 */ 22203847Seh146360 return (IEEE80211_IOCTL_REQUIRED); 22213847Seh146360 } 22223847Seh146360 22233847Seh146360 static int 22243847Seh146360 ipw2100_getset(struct ipw2100_softc *sc, mblk_t *m, uint32_t cmd, 22253847Seh146360 boolean_t *need_net80211) 22263847Seh146360 { 22273847Seh146360 wldp_t *infp, *outfp; 22283847Seh146360 uint32_t id; 22293847Seh146360 int ret; /* IEEE80211_IOCTL - handled by net80211 */ 22303847Seh146360 22317194Seh146360 infp = (wldp_t *)(uintptr_t)m->b_rptr; 22327194Seh146360 outfp = (wldp_t *)(uintptr_t)m->b_rptr; 22333847Seh146360 outfp->wldp_result = WL_NOTSUPPORTED; 22343847Seh146360 22353847Seh146360 id = infp->wldp_id; 22363847Seh146360 IPW2100_DBG(IPW2100_DBG_IOCTL, (sc->sc_dip, CE_CONT, 22373847Seh146360 "ipw2100_getset(): id = 0x%x\n", id)); 22383847Seh146360 switch (id) { 22393847Seh146360 /* 22403847Seh146360 * which is not supported by net80211, so it 22413847Seh146360 * has to be handled from driver side 22423847Seh146360 */ 22433847Seh146360 case WL_RADIO: 22443847Seh146360 ret = ipw_wificfg_radio(sc, cmd, outfp); 22453847Seh146360 break; 22463847Seh146360 /* 22473847Seh146360 * so far, drier doesn't support fix-rates 22483847Seh146360 */ 22493847Seh146360 case WL_DESIRED_RATES: 22503847Seh146360 ret = ipw_wificfg_desrates(outfp); 22513847Seh146360 break; 22523847Seh146360 /* 22533847Seh146360 * current net80211 implementation clears the bssid while 22543847Seh146360 * this command received, which will result in the all zero 22553847Seh146360 * mac address for scan'ed AP which is just disconnected. 22563847Seh146360 * This is a workaround solution until net80211 find a 22573847Seh146360 * better method. 22583847Seh146360 */ 22593847Seh146360 case WL_DISASSOCIATE: 22603847Seh146360 ret = ipw_wificfg_disassoc(sc, outfp); 22613847Seh146360 break; 22623847Seh146360 default: 22633847Seh146360 /* 22643847Seh146360 * The wifi IOCTL net80211 supported: 22653847Seh146360 * case WL_ESSID: 22663847Seh146360 * case WL_BSSID: 22673847Seh146360 * case WL_WEP_KEY_TAB: 22683847Seh146360 * case WL_WEP_KEY_ID: 22693847Seh146360 * case WL_AUTH_MODE: 22703847Seh146360 * case WL_ENCRYPTION: 22713847Seh146360 * case WL_BSS_TYPE: 22723847Seh146360 * case WL_ESS_LIST: 22733847Seh146360 * case WL_LINKSTATUS: 22743847Seh146360 * case WL_RSSI: 22753847Seh146360 * case WL_SCAN: 22763847Seh146360 * case WL_LOAD_DEFAULTS: 22773847Seh146360 */ 22785838Seh146360 22795838Seh146360 /* 22805838Seh146360 * When radio is off, need to ignore all ioctl. What need to 22815838Seh146360 * do is to check radio status firstly. If radio is ON, pass 22825838Seh146360 * it to net80211, otherwise, return to upper layer directly. 22835838Seh146360 * 22845838Seh146360 * Considering the WL_SUCCESS also means WL_CONNECTED for 22855838Seh146360 * checking linkstatus, one exception for WL_LINKSTATUS is to 22865838Seh146360 * let net80211 handle it. 22875838Seh146360 */ 22885838Seh146360 if ((ipw2100_get_radio(sc) == 0) && 22895838Seh146360 (id != WL_LINKSTATUS)) { 22905838Seh146360 22915838Seh146360 IPW2100_REPORT((sc->sc_dip, CE_WARN, 22925838Seh146360 "ipw: RADIO is OFF\n")); 22935838Seh146360 22945838Seh146360 outfp->wldp_length = WIFI_BUF_OFFSET; 22955838Seh146360 outfp->wldp_result = WL_SUCCESS; 22965838Seh146360 ret = 0; 22975838Seh146360 break; 22985838Seh146360 } 22995838Seh146360 23003847Seh146360 *need_net80211 = B_TRUE; /* let net80211 do the rest */ 23013847Seh146360 return (0); 23023847Seh146360 } 23033847Seh146360 /* 23043847Seh146360 * we will overwrite everything 23053847Seh146360 */ 23063847Seh146360 m->b_wptr = m->b_rptr + outfp->wldp_length; 23073847Seh146360 23083847Seh146360 return (ret); 23093847Seh146360 } 23103847Seh146360 23113847Seh146360 static int 23123847Seh146360 ipw_wificfg_radio(struct ipw2100_softc *sc, uint32_t cmd, wldp_t *outfp) 23133847Seh146360 { 23143847Seh146360 uint32_t ret = ENOTSUP; 23153847Seh146360 23163847Seh146360 switch (cmd) { 23173847Seh146360 case WLAN_GET_PARAM: 23183847Seh146360 *(wl_linkstatus_t *)(outfp->wldp_buf) = ipw2100_get_radio(sc); 23193847Seh146360 outfp->wldp_length = WIFI_BUF_OFFSET + sizeof (wl_linkstatus_t); 23203847Seh146360 outfp->wldp_result = WL_SUCCESS; 23213847Seh146360 ret = 0; /* command sucess */ 23223847Seh146360 break; 23233847Seh146360 case WLAN_SET_PARAM: 23243847Seh146360 default: 23253847Seh146360 break; 23263847Seh146360 } 23273847Seh146360 return (ret); 23283847Seh146360 } 23293847Seh146360 23303847Seh146360 static int 23313847Seh146360 ipw_wificfg_desrates(wldp_t *outfp) 23323847Seh146360 { 23333847Seh146360 /* 23343847Seh146360 * return success, but with result NOTSUPPORTED 23353847Seh146360 */ 23363847Seh146360 outfp->wldp_length = WIFI_BUF_OFFSET; 23373847Seh146360 outfp->wldp_result = WL_NOTSUPPORTED; 23383847Seh146360 return (0); 23393847Seh146360 } 23403847Seh146360 23413847Seh146360 static int 23423847Seh146360 ipw_wificfg_disassoc(struct ipw2100_softc *sc, wldp_t *outfp) 23433847Seh146360 { 23443847Seh146360 struct ieee80211com *ic = &sc->sc_ic; 23453847Seh146360 23463847Seh146360 /* 23473847Seh146360 * init the state 23483847Seh146360 */ 23493847Seh146360 if (ic->ic_state != IEEE80211_S_INIT) { 23503847Seh146360 (void) ieee80211_new_state(ic, IEEE80211_S_INIT, -1); 23513847Seh146360 } 23523847Seh146360 23533847Seh146360 /* 23543847Seh146360 * return success always 23553847Seh146360 */ 23563847Seh146360 outfp->wldp_length = WIFI_BUF_OFFSET; 23573847Seh146360 outfp->wldp_result = WL_SUCCESS; 23583847Seh146360 return (0); 23593847Seh146360 } 23603847Seh146360 /* End of IOCTL Handler */ 23613847Seh146360 23623847Seh146360 static void 23633847Seh146360 ipw2100_fix_channel(struct ieee80211com *ic, mblk_t *m) 23643847Seh146360 { 23653847Seh146360 struct ieee80211_frame *wh; 23663847Seh146360 uint8_t subtype; 23673847Seh146360 uint8_t *frm, *efrm; 23683847Seh146360 23693847Seh146360 wh = (struct ieee80211_frame *)m->b_rptr; 23703847Seh146360 23713847Seh146360 if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) != IEEE80211_FC0_TYPE_MGT) 23723847Seh146360 return; 23733847Seh146360 23743847Seh146360 subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK; 23753847Seh146360 23763847Seh146360 if (subtype != IEEE80211_FC0_SUBTYPE_BEACON && 23773847Seh146360 subtype != IEEE80211_FC0_SUBTYPE_PROBE_RESP) 23783847Seh146360 return; 23793847Seh146360 23803847Seh146360 /* 23813847Seh146360 * assume the message contains only 1 block 23823847Seh146360 */ 23833847Seh146360 frm = (uint8_t *)(wh + 1); 23843847Seh146360 efrm = (uint8_t *)m->b_wptr; 23853847Seh146360 frm += 12; /* skip tstamp, bintval and capinfo fields */ 23863847Seh146360 while (frm < efrm) { 23873847Seh146360 if (*frm == IEEE80211_ELEMID_DSPARMS) { 23883847Seh146360 #if IEEE80211_CHAN_MAX < 255 23894784Seh146360 if (frm[2] <= IEEE80211_CHAN_MAX) 23903847Seh146360 #endif 23914784Seh146360 { 23924784Seh146360 ic->ic_curchan = &ic->ic_sup_channels[frm[2]]; 23934784Seh146360 } 23943847Seh146360 } 23953847Seh146360 frm += frm[1] + 2; 23963847Seh146360 } 23973847Seh146360 } 23983847Seh146360 23993847Seh146360 static void 24003847Seh146360 ipw2100_rcvpkt(struct ipw2100_softc *sc, struct ipw2100_status *status, 24013847Seh146360 uint8_t *rxbuf) 24023847Seh146360 { 24033847Seh146360 struct ieee80211com *ic = &sc->sc_ic; 24043847Seh146360 mblk_t *m; 24053847Seh146360 struct ieee80211_frame *wh = (struct ieee80211_frame *)rxbuf; 24063847Seh146360 struct ieee80211_node *in; 24073847Seh146360 uint32_t rlen; 24083847Seh146360 24093847Seh146360 in = ieee80211_find_rxnode(ic, wh); 24103847Seh146360 rlen = LE_32(status->len); 24113847Seh146360 m = allocb(rlen, BPRI_MED); 24123847Seh146360 if (m) { 24133847Seh146360 (void) memcpy(m->b_wptr, rxbuf, rlen); 24143847Seh146360 m->b_wptr += rlen; 24153847Seh146360 if (ic->ic_state == IEEE80211_S_SCAN) 24163847Seh146360 ipw2100_fix_channel(ic, m); 24173847Seh146360 (void) ieee80211_input(ic, m, in, status->rssi, 0); 24183847Seh146360 } else 24193847Seh146360 IPW2100_WARN((sc->sc_dip, CE_WARN, 24203847Seh146360 "ipw2100_rcvpkg(): cannot allocate receive message(%u)\n", 24213847Seh146360 LE_32(status->len))); 24223847Seh146360 ieee80211_free_node(in); 24233847Seh146360 } 24243847Seh146360 24253847Seh146360 static uint_t 24263847Seh146360 ipw2100_intr(caddr_t arg) 24273847Seh146360 { 24287194Seh146360 struct ipw2100_softc *sc = (struct ipw2100_softc *)(uintptr_t)arg; 24293847Seh146360 uint32_t ireg, ridx, len, i; 24303847Seh146360 struct ieee80211com *ic = &sc->sc_ic; 24313847Seh146360 struct ipw2100_status *status; 24323847Seh146360 uint8_t *rxbuf; 24333847Seh146360 struct dma_region *dr; 24343847Seh146360 uint32_t state; 24353847Seh146360 #if DEBUG 24363847Seh146360 struct ipw2100_bd *rxbd; 24373847Seh146360 #endif 24383847Seh146360 24393847Seh146360 ireg = ipw2100_csr_get32(sc, IPW2100_CSR_INTR); 24403847Seh146360 24413847Seh146360 if (!(ireg & IPW2100_INTR_MASK_ALL)) 24423847Seh146360 return (DDI_INTR_UNCLAIMED); 24433847Seh146360 24443847Seh146360 /* 24453847Seh146360 * mask all interrupts 24463847Seh146360 */ 24473847Seh146360 ipw2100_csr_put32(sc, IPW2100_CSR_INTR_MASK, 0); 24483847Seh146360 24493847Seh146360 /* 24503847Seh146360 * acknowledge all fired interrupts 24513847Seh146360 */ 24523847Seh146360 ipw2100_csr_put32(sc, IPW2100_CSR_INTR, ireg); 24533847Seh146360 24543847Seh146360 IPW2100_DBG(IPW2100_DBG_INT, (sc->sc_dip, CE_CONT, 24553847Seh146360 "ipw2100_intr(): interrupt is fired. int=0x%08x\n", ireg)); 24563847Seh146360 24573847Seh146360 if (ireg & IPW2100_INTR_MASK_ERR) { 24583847Seh146360 24593847Seh146360 IPW2100_DBG(IPW2100_DBG_FATAL, (sc->sc_dip, CE_CONT, 24603847Seh146360 "ipw2100_intr(): interrupt is fired, MASK = 0x%08x\n", 24613847Seh146360 ireg)); 24623847Seh146360 24633847Seh146360 /* 24643847Seh146360 * inform mfthread to recover hw error 24653847Seh146360 */ 24663847Seh146360 mutex_enter(&sc->sc_mflock); 24673847Seh146360 sc->sc_flags |= IPW2100_FLAG_HW_ERR_RECOVER; 24683847Seh146360 mutex_exit(&sc->sc_mflock); 24693847Seh146360 24707194Seh146360 goto enable_interrupt; 24717194Seh146360 } 24727194Seh146360 24737194Seh146360 /* 24747194Seh146360 * FW intr 24757194Seh146360 */ 24767194Seh146360 if (ireg & IPW2100_INTR_FW_INIT_DONE) { 24777194Seh146360 mutex_enter(&sc->sc_ilock); 24787194Seh146360 sc->sc_flags |= IPW2100_FLAG_FW_INITED; 24797194Seh146360 cv_signal(&sc->sc_fw_cond); 24807194Seh146360 mutex_exit(&sc->sc_ilock); 24817194Seh146360 } 24823847Seh146360 24837194Seh146360 /* 24847194Seh146360 * RX intr 24857194Seh146360 */ 24867194Seh146360 if (ireg & IPW2100_INTR_RX_TRANSFER) { 24877194Seh146360 ridx = ipw2100_csr_get32(sc, 24887194Seh146360 IPW2100_CSR_RX_READ_INDEX); 24893847Seh146360 24907194Seh146360 for (; sc->sc_rx_cur != ridx; 24917194Seh146360 sc->sc_rx_cur = RING_FORWARD( 24927194Seh146360 sc->sc_rx_cur, 1, IPW2100_NUM_RXBD)) { 24937194Seh146360 24947194Seh146360 i = sc->sc_rx_cur; 24957194Seh146360 status = &sc->sc_status[i]; 24967194Seh146360 rxbuf = &sc->sc_rxbufs[i]->rxb_dat[0]; 24977194Seh146360 dr = &sc->sc_dma_rxbufs[i]; 24983847Seh146360 24997194Seh146360 /* 25007194Seh146360 * sync 25017194Seh146360 */ 25027194Seh146360 (void) ddi_dma_sync(sc->sc_dma_status.dr_hnd, 25037194Seh146360 i * sizeof (struct ipw2100_status), 25047194Seh146360 sizeof (struct ipw2100_status), 25057194Seh146360 DDI_DMA_SYNC_FORKERNEL); 25067194Seh146360 (void) ddi_dma_sync(sc->sc_dma_rxbd.dr_hnd, 25077194Seh146360 i * sizeof (struct ipw2100_bd), 25087194Seh146360 sizeof (struct ipw2100_bd), 25097194Seh146360 DDI_DMA_SYNC_FORKERNEL); 25107194Seh146360 (void) ddi_dma_sync(dr->dr_hnd, 0, 25117194Seh146360 sizeof (struct ipw2100_rxb), 25127194Seh146360 DDI_DMA_SYNC_FORKERNEL); 25137194Seh146360 IPW2100_DBG(IPW2100_DBG_INT, (sc->sc_dip, CE_CONT, 25147194Seh146360 "ipw2100_intr(): status code=0x%04x, len=0x%08x, " 25157194Seh146360 "flags=0x%02x, rssi=%02x\n", 25167194Seh146360 LE_16(status->code), LE_32(status->len), 25177194Seh146360 status->flags, status->rssi)); 25187194Seh146360 #if DEBUG 25197194Seh146360 rxbd = &sc->sc_rxbd[i]; 25207194Seh146360 IPW2100_DBG(IPW2100_DBG_INT, (sc->sc_dip, CE_CONT, 25217194Seh146360 "ipw2100_intr(): rxbd,phyaddr=0x%08x, len=0x%08x, " 25227194Seh146360 "flags=0x%02x,nfrag=%02x\n", 25237194Seh146360 LE_32(rxbd->phyaddr), LE_32(rxbd->len), 25247194Seh146360 rxbd->flags, rxbd->nfrag)); 25257194Seh146360 #endif 25267194Seh146360 switch (LE_16(status->code) & 0x0f) { 25277194Seh146360 /* 25287194Seh146360 * command complete response 25297194Seh146360 */ 25307194Seh146360 case IPW2100_STATUS_CODE_COMMAND: 25317194Seh146360 mutex_enter(&sc->sc_ilock); 25327194Seh146360 sc->sc_done = 1; 25337194Seh146360 cv_signal(&sc->sc_cmd_cond); 25347194Seh146360 mutex_exit(&sc->sc_ilock); 25357194Seh146360 break; 25367194Seh146360 /* 25377194Seh146360 * change state 25387194Seh146360 */ 25397194Seh146360 case IPW2100_STATUS_CODE_NEWSTATE: 25407194Seh146360 state = LE_32(* ((uint32_t *)(uintptr_t)rxbuf)); 25413847Seh146360 IPW2100_DBG(IPW2100_DBG_INT, 25423847Seh146360 (sc->sc_dip, CE_CONT, 25437194Seh146360 "ipw2100_intr(): newstate,state=0x%x\n", 25447194Seh146360 state)); 25457194Seh146360 25467194Seh146360 switch (state) { 25477194Seh146360 case IPW2100_STATE_ASSOCIATED: 25487194Seh146360 ieee80211_new_state(ic, 25497194Seh146360 IEEE80211_S_RUN, -1); 25507194Seh146360 break; 25517194Seh146360 case IPW2100_STATE_ASSOCIATION_LOST: 25527194Seh146360 case IPW2100_STATE_DISABLED: 25537194Seh146360 ieee80211_new_state(ic, 25547194Seh146360 IEEE80211_S_INIT, -1); 25553847Seh146360 break; 25563847Seh146360 /* 25577194Seh146360 * When radio is OFF, need a better 25587194Seh146360 * scan approach to ensure scan 25597194Seh146360 * result correct. 25603847Seh146360 */ 25617194Seh146360 case IPW2100_STATE_RADIO_DISABLED: 25627194Seh146360 IPW2100_REPORT((sc->sc_dip, CE_WARN, 25637194Seh146360 "ipw2100_intr(): RADIO is OFF\n")); 25647194Seh146360 ipw2100_stop(sc); 25657194Seh146360 break; 25667194Seh146360 case IPW2100_STATE_SCAN_COMPLETE: 25677194Seh146360 ieee80211_cancel_scan(ic); 25687194Seh146360 break; 25697194Seh146360 case IPW2100_STATE_SCANNING: 25707194Seh146360 if (ic->ic_state != IEEE80211_S_RUN) 25713847Seh146360 ieee80211_new_state(ic, 25727194Seh146360 IEEE80211_S_SCAN, -1); 25737194Seh146360 ic->ic_flags |= IEEE80211_F_SCAN; 25743847Seh146360 25753847Seh146360 break; 25763847Seh146360 default: 25773847Seh146360 break; 25783847Seh146360 } 25797194Seh146360 break; 25807194Seh146360 case IPW2100_STATUS_CODE_DATA_802_11: 25817194Seh146360 case IPW2100_STATUS_CODE_DATA_802_3: 25827194Seh146360 ipw2100_rcvpkt(sc, status, rxbuf); 25837194Seh146360 break; 25847194Seh146360 case IPW2100_STATUS_CODE_NOTIFICATION: 25857194Seh146360 break; 25867194Seh146360 default: 25877194Seh146360 IPW2100_WARN((sc->sc_dip, CE_WARN, 25887194Seh146360 "ipw2100_intr(): " 25897194Seh146360 "unknown status code 0x%04x\n", 25907194Seh146360 LE_16(status->code))); 25917194Seh146360 break; 25923847Seh146360 } 25933847Seh146360 } 25943847Seh146360 /* 25957194Seh146360 * write sc_rx_cur backward 1 step to RX_WRITE_INDEX 25963847Seh146360 */ 25977194Seh146360 ipw2100_csr_put32(sc, IPW2100_CSR_RX_WRITE_INDEX, 25987194Seh146360 RING_BACKWARD(sc->sc_rx_cur, 1, IPW2100_NUM_RXBD)); 25993847Seh146360 } 26003847Seh146360 26013847Seh146360 /* 26027194Seh146360 * TX intr 26037194Seh146360 */ 26047194Seh146360 if (ireg & IPW2100_INTR_TX_TRANSFER) { 26057194Seh146360 mutex_enter(&sc->sc_tx_lock); 26067194Seh146360 ridx = ipw2100_csr_get32(sc, IPW2100_CSR_TX_READ_INDEX); 26077194Seh146360 len = RING_FLEN(RING_FORWARD(sc->sc_tx_cur, 26087194Seh146360 sc->sc_tx_free, IPW2100_NUM_TXBD), 26097194Seh146360 ridx, IPW2100_NUM_TXBD); 26107194Seh146360 sc->sc_tx_free += len; 26117194Seh146360 IPW2100_DBG(IPW2100_DBG_INT, (sc->sc_dip, CE_CONT, 26127194Seh146360 "ipw2100_intr(): len=%d\n", len)); 26137194Seh146360 mutex_exit(&sc->sc_tx_lock); 26147194Seh146360 26157194Seh146360 mutex_enter(&sc->sc_resched_lock); 26167194Seh146360 if (len > 1 && (sc->sc_flags & IPW2100_FLAG_TX_SCHED)) { 26177194Seh146360 sc->sc_flags &= ~IPW2100_FLAG_TX_SCHED; 26187194Seh146360 mac_tx_update(ic->ic_mach); 26197194Seh146360 } 26207194Seh146360 mutex_exit(&sc->sc_resched_lock); 26217194Seh146360 } 26227194Seh146360 26237194Seh146360 enable_interrupt: 26247194Seh146360 /* 26253847Seh146360 * enable all interrupts 26263847Seh146360 */ 26273847Seh146360 ipw2100_csr_put32(sc, IPW2100_CSR_INTR_MASK, IPW2100_INTR_MASK_ALL); 26283847Seh146360 26293847Seh146360 return (DDI_INTR_CLAIMED); 26303847Seh146360 } 26313847Seh146360 26323847Seh146360 26333847Seh146360 /* 26343847Seh146360 * Module Loading Data & Entry Points 26353847Seh146360 */ 26363847Seh146360 DDI_DEFINE_STREAM_OPS(ipw2100_devops, nulldev, nulldev, ipw2100_attach, 26374784Seh146360 ipw2100_detach, ipw2100_reset, NULL, D_MP, NULL); 26383847Seh146360 26393847Seh146360 static struct modldrv ipw2100_modldrv = { 26403847Seh146360 &mod_driverops, 26413847Seh146360 ipw2100_ident, 26423847Seh146360 &ipw2100_devops 26433847Seh146360 }; 26443847Seh146360 26453847Seh146360 static struct modlinkage ipw2100_modlinkage = { 26463847Seh146360 MODREV_1, 26473847Seh146360 &ipw2100_modldrv, 26483847Seh146360 NULL 26493847Seh146360 }; 26503847Seh146360 26513847Seh146360 int 26523847Seh146360 _init(void) 26533847Seh146360 { 26543847Seh146360 int status; 26553847Seh146360 26563847Seh146360 status = ddi_soft_state_init(&ipw2100_ssp, 26573847Seh146360 sizeof (struct ipw2100_softc), 1); 26583847Seh146360 if (status != DDI_SUCCESS) 26593847Seh146360 return (status); 26603847Seh146360 26613847Seh146360 mac_init_ops(&ipw2100_devops, IPW2100_DRV_NAME); 26623847Seh146360 status = mod_install(&ipw2100_modlinkage); 26633847Seh146360 if (status != DDI_SUCCESS) { 26643847Seh146360 mac_fini_ops(&ipw2100_devops); 26653847Seh146360 ddi_soft_state_fini(&ipw2100_ssp); 26663847Seh146360 } 26673847Seh146360 26683847Seh146360 return (status); 26693847Seh146360 } 26703847Seh146360 26713847Seh146360 int 26723847Seh146360 _fini(void) 26733847Seh146360 { 26743847Seh146360 int status; 26753847Seh146360 26763847Seh146360 status = mod_remove(&ipw2100_modlinkage); 26773847Seh146360 if (status == DDI_SUCCESS) { 26783847Seh146360 mac_fini_ops(&ipw2100_devops); 26793847Seh146360 ddi_soft_state_fini(&ipw2100_ssp); 26803847Seh146360 } 26813847Seh146360 26823847Seh146360 return (status); 26833847Seh146360 } 26843847Seh146360 26853847Seh146360 int 26863847Seh146360 _info(struct modinfo *mip) 26873847Seh146360 { 26883847Seh146360 return (mod_info(&ipw2100_modlinkage, mip)); 26893847Seh146360 } 2690