1bb5e3b2fSeh146360 /* 2bb5e3b2fSeh146360 * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 3bb5e3b2fSeh146360 * Use is subject to license terms. 4bb5e3b2fSeh146360 */ 5bb5e3b2fSeh146360 6bb5e3b2fSeh146360 /* 7bb5e3b2fSeh146360 * Copyright (c) 2004, 2005 8bb5e3b2fSeh146360 * Damien Bergamini <damien.bergamini@free.fr>. All rights reserved. 9bb5e3b2fSeh146360 * 10bb5e3b2fSeh146360 * Redistribution and use in source and binary forms, with or without 11bb5e3b2fSeh146360 * modification, are permitted provided that the following conditions 12bb5e3b2fSeh146360 * are met: 13bb5e3b2fSeh146360 * 1. Redistributions of source code must retain the above copyright 14bb5e3b2fSeh146360 * notice unmodified, this list of conditions, and the following 15bb5e3b2fSeh146360 * disclaimer. 16bb5e3b2fSeh146360 * 2. Redistributions in binary form must reproduce the above copyright 17bb5e3b2fSeh146360 * notice, this list of conditions and the following disclaimer in the 18bb5e3b2fSeh146360 * documentation and/or other materials provided with the distribution. 19bb5e3b2fSeh146360 * 20bb5e3b2fSeh146360 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 21bb5e3b2fSeh146360 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22bb5e3b2fSeh146360 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23bb5e3b2fSeh146360 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 24bb5e3b2fSeh146360 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25bb5e3b2fSeh146360 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26bb5e3b2fSeh146360 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27bb5e3b2fSeh146360 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28bb5e3b2fSeh146360 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29bb5e3b2fSeh146360 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30bb5e3b2fSeh146360 * SUCH DAMAGE. 31bb5e3b2fSeh146360 */ 32bb5e3b2fSeh146360 33bb5e3b2fSeh146360 #pragma ident "%Z%%M% %I% %E% SMI" 34bb5e3b2fSeh146360 35bb5e3b2fSeh146360 #include <sys/types.h> 36bb5e3b2fSeh146360 #include <sys/byteorder.h> 37bb5e3b2fSeh146360 #include <sys/conf.h> 38bb5e3b2fSeh146360 #include <sys/cmn_err.h> 39bb5e3b2fSeh146360 #include <sys/stat.h> 40bb5e3b2fSeh146360 #include <sys/ddi.h> 41bb5e3b2fSeh146360 #include <sys/sunddi.h> 42bb5e3b2fSeh146360 #include <sys/strsubr.h> 43bb5e3b2fSeh146360 #include <sys/ethernet.h> 44bb5e3b2fSeh146360 #include <inet/common.h> 45bb5e3b2fSeh146360 #include <inet/nd.h> 46bb5e3b2fSeh146360 #include <inet/mi.h> 47bb5e3b2fSeh146360 #include <sys/note.h> 48bb5e3b2fSeh146360 #include <sys/stream.h> 49bb5e3b2fSeh146360 #include <sys/strsun.h> 50bb5e3b2fSeh146360 #include <sys/modctl.h> 51bb5e3b2fSeh146360 #include <sys/devops.h> 52bb5e3b2fSeh146360 #include <sys/dlpi.h> 53bb5e3b2fSeh146360 #include <sys/mac.h> 54bb5e3b2fSeh146360 #include <sys/mac_wifi.h> 55bb5e3b2fSeh146360 #include <sys/varargs.h> 56bb5e3b2fSeh146360 #include <sys/pci.h> 57bb5e3b2fSeh146360 #include <sys/policy.h> 58bb5e3b2fSeh146360 #include <sys/random.h> 59bb5e3b2fSeh146360 60bb5e3b2fSeh146360 #include "ipw2200.h" 61bb5e3b2fSeh146360 #include "ipw2200_impl.h" 62bb5e3b2fSeh146360 #include <inet/wifi_ioctl.h> 63bb5e3b2fSeh146360 64bb5e3b2fSeh146360 /* 65bb5e3b2fSeh146360 * minimal size reserved in tx-ring 66bb5e3b2fSeh146360 */ 67bb5e3b2fSeh146360 #define IPW2200_TX_RING_MIN (8) 68bb5e3b2fSeh146360 #define IPW2200_TXBUF_SIZE (IEEE80211_MAX_LEN) 69bb5e3b2fSeh146360 #define IPW2200_RXBUF_SIZE (4096) 70bb5e3b2fSeh146360 71bb5e3b2fSeh146360 static void *ipw2200_ssp = NULL; 72bb5e3b2fSeh146360 static char ipw2200_ident[] = IPW2200_DRV_DESC " " IPW2200_DRV_REV; 73bb5e3b2fSeh146360 74bb5e3b2fSeh146360 /* 75bb5e3b2fSeh146360 * PIO access attributor for registers 76bb5e3b2fSeh146360 */ 77bb5e3b2fSeh146360 static ddi_device_acc_attr_t ipw2200_csr_accattr = { 78bb5e3b2fSeh146360 DDI_DEVICE_ATTR_V0, 79bb5e3b2fSeh146360 DDI_STRUCTURE_LE_ACC, 80bb5e3b2fSeh146360 DDI_STRICTORDER_ACC 81bb5e3b2fSeh146360 }; 82bb5e3b2fSeh146360 83bb5e3b2fSeh146360 /* 84bb5e3b2fSeh146360 * DMA access attributor for descriptors 85bb5e3b2fSeh146360 */ 86bb5e3b2fSeh146360 static ddi_device_acc_attr_t ipw2200_dma_accattr = { 87bb5e3b2fSeh146360 DDI_DEVICE_ATTR_V0, 88bb5e3b2fSeh146360 DDI_NEVERSWAP_ACC, 89bb5e3b2fSeh146360 DDI_STRICTORDER_ACC 90bb5e3b2fSeh146360 }; 91bb5e3b2fSeh146360 92bb5e3b2fSeh146360 /* 93bb5e3b2fSeh146360 * Describes the chip's DMA engine 94bb5e3b2fSeh146360 */ 95bb5e3b2fSeh146360 static ddi_dma_attr_t ipw2200_dma_attr = { 96bb5e3b2fSeh146360 DMA_ATTR_V0, /* version */ 97bb5e3b2fSeh146360 0x0000000000000000ULL, /* addr_lo */ 98bb5e3b2fSeh146360 0x00000000ffffffffULL, /* addr_hi */ 99bb5e3b2fSeh146360 0x00000000ffffffffULL, /* counter */ 100bb5e3b2fSeh146360 0x0000000000000004ULL, /* alignment */ 101bb5e3b2fSeh146360 0xfff, /* burst */ 102bb5e3b2fSeh146360 1, /* min xfer */ 103bb5e3b2fSeh146360 0x00000000ffffffffULL, /* max xfer */ 104bb5e3b2fSeh146360 0x00000000ffffffffULL, /* seg boud */ 105bb5e3b2fSeh146360 1, /* s/g list */ 106bb5e3b2fSeh146360 1, /* granularity */ 107bb5e3b2fSeh146360 0 /* flags */ 108bb5e3b2fSeh146360 }; 109bb5e3b2fSeh146360 110bb5e3b2fSeh146360 static uint8_t ipw2200_broadcast_addr[] = { 111bb5e3b2fSeh146360 0xff, 0xff, 0xff, 0xff, 0xff, 0xff 112bb5e3b2fSeh146360 }; 113bb5e3b2fSeh146360 static const struct ieee80211_rateset ipw2200_rateset_11a = { 8, 114bb5e3b2fSeh146360 {12, 18, 24, 36, 48, 72, 96, 108} 115bb5e3b2fSeh146360 }; 116bb5e3b2fSeh146360 static const struct ieee80211_rateset ipw2200_rateset_11b = { 4, 117bb5e3b2fSeh146360 {2, 4, 11, 22} 118bb5e3b2fSeh146360 }; 119bb5e3b2fSeh146360 static const struct ieee80211_rateset ipw2200_rateset_11g = { 12, 120bb5e3b2fSeh146360 {2, 4, 11, 22, 12, 18, 24, 36, 48, 72, 96, 108} 121bb5e3b2fSeh146360 }; 122bb5e3b2fSeh146360 123bb5e3b2fSeh146360 /* 124bb5e3b2fSeh146360 * Used by multi function thread 125bb5e3b2fSeh146360 */ 126bb5e3b2fSeh146360 extern pri_t minclsyspri; 127bb5e3b2fSeh146360 128bb5e3b2fSeh146360 /* 129bb5e3b2fSeh146360 * ipw2200 specific hardware operations 130bb5e3b2fSeh146360 */ 131bb5e3b2fSeh146360 static void ipw2200_hwconf_get(struct ipw2200_softc *sc); 132bb5e3b2fSeh146360 static int ipw2200_chip_reset(struct ipw2200_softc *sc); 133bb5e3b2fSeh146360 static void ipw2200_master_stop(struct ipw2200_softc *sc); 134bb5e3b2fSeh146360 static void ipw2200_stop(struct ipw2200_softc *sc); 135bb5e3b2fSeh146360 static int ipw2200_config(struct ipw2200_softc *sc); 136bb5e3b2fSeh146360 static int ipw2200_cmd(struct ipw2200_softc *sc, 137bb5e3b2fSeh146360 uint32_t type, void *buf, size_t len, int async); 138bb5e3b2fSeh146360 static void ipw2200_ring_hwsetup(struct ipw2200_softc *sc); 139bb5e3b2fSeh146360 static int ipw2200_ring_alloc(struct ipw2200_softc *sc); 140bb5e3b2fSeh146360 static void ipw2200_ring_free(struct ipw2200_softc *sc); 141bb5e3b2fSeh146360 static void ipw2200_ring_reset(struct ipw2200_softc *sc); 142bb5e3b2fSeh146360 static int ipw2200_ring_init(struct ipw2200_softc *sc); 143bb5e3b2fSeh146360 144bb5e3b2fSeh146360 /* 145bb5e3b2fSeh146360 * GLD specific operations 146bb5e3b2fSeh146360 */ 147bb5e3b2fSeh146360 static int ipw2200_m_stat(void *arg, uint_t stat, uint64_t *val); 148bb5e3b2fSeh146360 static int ipw2200_m_start(void *arg); 149bb5e3b2fSeh146360 static void ipw2200_m_stop(void *arg); 150bb5e3b2fSeh146360 static int ipw2200_m_unicst(void *arg, const uint8_t *macaddr); 151bb5e3b2fSeh146360 static int ipw2200_m_multicst(void *arg, boolean_t add, const uint8_t *m); 152bb5e3b2fSeh146360 static int ipw2200_m_promisc(void *arg, boolean_t on); 153bb5e3b2fSeh146360 static void ipw2200_m_ioctl(void *arg, queue_t *wq, mblk_t *mp); 154bb5e3b2fSeh146360 static mblk_t *ipw2200_m_tx(void *arg, mblk_t *mp); 155bb5e3b2fSeh146360 156bb5e3b2fSeh146360 /* 157bb5e3b2fSeh146360 * Interrupt and Data transferring operations 158bb5e3b2fSeh146360 */ 159bb5e3b2fSeh146360 static uint_t ipw2200_intr(caddr_t arg); 160bb5e3b2fSeh146360 static int ipw2200_send(struct ieee80211com *ic, mblk_t *mp, uint8_t type); 161bb5e3b2fSeh146360 static void ipw2200_rcv_frame(struct ipw2200_softc *sc, 162bb5e3b2fSeh146360 struct ipw2200_frame *frame); 163bb5e3b2fSeh146360 static void ipw2200_rcv_notif(struct ipw2200_softc *sc, 164bb5e3b2fSeh146360 struct ipw2200_notif *notif); 165bb5e3b2fSeh146360 166bb5e3b2fSeh146360 /* 167bb5e3b2fSeh146360 * WiFi specific operations 168bb5e3b2fSeh146360 */ 169bb5e3b2fSeh146360 static int ipw2200_newstate(struct ieee80211com *ic, 170bb5e3b2fSeh146360 enum ieee80211_state state, int arg); 171bb5e3b2fSeh146360 static void ipw2200_thread(struct ipw2200_softc *sc); 172bb5e3b2fSeh146360 173bb5e3b2fSeh146360 /* 174bb5e3b2fSeh146360 * IOCTL Handler 175bb5e3b2fSeh146360 */ 176bb5e3b2fSeh146360 static int ipw2200_ioctl(struct ipw2200_softc *sc, queue_t *q, mblk_t *m); 177bb5e3b2fSeh146360 static int ipw2200_getset(struct ipw2200_softc *sc, 178bb5e3b2fSeh146360 mblk_t *m, uint32_t cmd, boolean_t *need_net80211); 179bb5e3b2fSeh146360 static int iwi_wificfg_radio(struct ipw2200_softc *sc, 180bb5e3b2fSeh146360 uint32_t cmd, wldp_t *outfp); 181bb5e3b2fSeh146360 static int iwi_wificfg_desrates(wldp_t *outfp); 182bb5e3b2fSeh146360 183bb5e3b2fSeh146360 /* 184bb5e3b2fSeh146360 * Mac Call Back entries 185bb5e3b2fSeh146360 */ 186bb5e3b2fSeh146360 mac_callbacks_t ipw2200_m_callbacks = { 187bb5e3b2fSeh146360 MC_IOCTL, 188bb5e3b2fSeh146360 ipw2200_m_stat, 189bb5e3b2fSeh146360 ipw2200_m_start, 190bb5e3b2fSeh146360 ipw2200_m_stop, 191bb5e3b2fSeh146360 ipw2200_m_promisc, 192bb5e3b2fSeh146360 ipw2200_m_multicst, 193bb5e3b2fSeh146360 ipw2200_m_unicst, 194bb5e3b2fSeh146360 ipw2200_m_tx, 195bb5e3b2fSeh146360 NULL, 196bb5e3b2fSeh146360 ipw2200_m_ioctl 197bb5e3b2fSeh146360 }; 198bb5e3b2fSeh146360 199bb5e3b2fSeh146360 /* 200bb5e3b2fSeh146360 * DEBUG Facility 201bb5e3b2fSeh146360 */ 202bb5e3b2fSeh146360 #define MAX_MSG (128) 203bb5e3b2fSeh146360 uint32_t ipw2200_debug = 0; 204bb5e3b2fSeh146360 /* 205bb5e3b2fSeh146360 * supported debug marks are: 206bb5e3b2fSeh146360 * | IPW2200_DBG_CSR 207bb5e3b2fSeh146360 * | IPW2200_DBG_TABLE 208bb5e3b2fSeh146360 * | IPW2200_DBG_HWCAP 209bb5e3b2fSeh146360 * | IPW2200_DBG_TX 210bb5e3b2fSeh146360 * | IPW2200_DBG_INIT 211bb5e3b2fSeh146360 * | IPW2200_DBG_FW 212bb5e3b2fSeh146360 * | IPW2200_DBG_NOTIF 213bb5e3b2fSeh146360 * | IPW2200_DBG_SCAN 214bb5e3b2fSeh146360 * | IPW2200_DBG_IOCTL 215bb5e3b2fSeh146360 * | IPW2200_DBG_RING 216bb5e3b2fSeh146360 * | IPW2200_DBG_INT 217bb5e3b2fSeh146360 * | IPW2200_DBG_RX 218bb5e3b2fSeh146360 * | IPW2200_DBG_DMA 219bb5e3b2fSeh146360 * | IPW2200_DBG_GLD 220bb5e3b2fSeh146360 * | IPW2200_DBG_WIFI 221bb5e3b2fSeh146360 * | IPW2200_DBG_SOFTINT 222bb5e3b2fSeh146360 */ 223bb5e3b2fSeh146360 224bb5e3b2fSeh146360 /* 225bb5e3b2fSeh146360 * Global tunning parameter to work around unknown hardware issues 226bb5e3b2fSeh146360 */ 227bb5e3b2fSeh146360 static uint32_t delay_config_stable = 100000; /* 100ms */ 228bb5e3b2fSeh146360 static uint32_t delay_fatal_recover = 100000 * 20; /* 2s */ 229bb5e3b2fSeh146360 static uint32_t delay_aux_thread = 100000; /* 100ms */ 230bb5e3b2fSeh146360 231bb5e3b2fSeh146360 #define IEEE80211_IS_CHAN_2GHZ(_c) \ 232bb5e3b2fSeh146360 (((_c)->ich_flags & IEEE80211_CHAN_2GHZ) != 0) 233bb5e3b2fSeh146360 #define IEEE80211_IS_CHAN_5GHZ(_c) \ 234bb5e3b2fSeh146360 (((_c)->ich_flags & IEEE80211_CHAN_5GHZ) != 0) 235bb5e3b2fSeh146360 #define isset(a, i) ((a)[(i)/NBBY] & (1 << ((i)%NBBY))) 236bb5e3b2fSeh146360 237bb5e3b2fSeh146360 void 238bb5e3b2fSeh146360 ipw2200_dbg(dev_info_t *dip, int level, const char *fmt, ...) 239bb5e3b2fSeh146360 { 240bb5e3b2fSeh146360 va_list ap; 241bb5e3b2fSeh146360 char buf[MAX_MSG]; 242bb5e3b2fSeh146360 int instance; 243bb5e3b2fSeh146360 244bb5e3b2fSeh146360 va_start(ap, fmt); 245bb5e3b2fSeh146360 (void) vsnprintf(buf, sizeof (buf), fmt, ap); 246bb5e3b2fSeh146360 va_end(ap); 247bb5e3b2fSeh146360 248bb5e3b2fSeh146360 if (dip) { 249bb5e3b2fSeh146360 instance = ddi_get_instance(dip); 250bb5e3b2fSeh146360 cmn_err(level, "%s%d: %s", IPW2200_DRV_NAME, instance, buf); 251bb5e3b2fSeh146360 } else 252bb5e3b2fSeh146360 cmn_err(level, "%s: %s", IPW2200_DRV_NAME, buf); 253bb5e3b2fSeh146360 254bb5e3b2fSeh146360 } 255bb5e3b2fSeh146360 256bb5e3b2fSeh146360 /* 257bb5e3b2fSeh146360 * Device operations 258bb5e3b2fSeh146360 */ 259bb5e3b2fSeh146360 int 260bb5e3b2fSeh146360 ipw2200_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 261bb5e3b2fSeh146360 { 262bb5e3b2fSeh146360 struct ipw2200_softc *sc; 263bb5e3b2fSeh146360 ddi_acc_handle_t cfgh; 264bb5e3b2fSeh146360 caddr_t regs; 265bb5e3b2fSeh146360 struct ieee80211com *ic; 266bb5e3b2fSeh146360 int instance, err, i; 267bb5e3b2fSeh146360 char strbuf[32]; 268bb5e3b2fSeh146360 wifi_data_t wd = { 0 }; 269bb5e3b2fSeh146360 mac_register_t *macp; 270bb5e3b2fSeh146360 uint16_t vendor, device, subven, subdev; 271bb5e3b2fSeh146360 272bb5e3b2fSeh146360 if (cmd != DDI_ATTACH) { 273bb5e3b2fSeh146360 err = DDI_FAILURE; 274bb5e3b2fSeh146360 goto fail1; 275bb5e3b2fSeh146360 } 276bb5e3b2fSeh146360 277bb5e3b2fSeh146360 instance = ddi_get_instance(dip); 278bb5e3b2fSeh146360 err = ddi_soft_state_zalloc(ipw2200_ssp, instance); 279bb5e3b2fSeh146360 if (err != DDI_SUCCESS) { 280bb5e3b2fSeh146360 IPW2200_WARN((dip, CE_WARN, 281bb5e3b2fSeh146360 "ipw2200_attach(): unable to allocate soft state\n")); 282bb5e3b2fSeh146360 goto fail1; 283bb5e3b2fSeh146360 } 284bb5e3b2fSeh146360 sc = ddi_get_soft_state(ipw2200_ssp, instance); 285bb5e3b2fSeh146360 sc->sc_dip = dip; 286bb5e3b2fSeh146360 287bb5e3b2fSeh146360 /* 288bb5e3b2fSeh146360 * Map config spaces register to read the vendor id, device id, sub 289bb5e3b2fSeh146360 * vendor id, and sub device id. 290bb5e3b2fSeh146360 */ 291bb5e3b2fSeh146360 err = ddi_regs_map_setup(dip, IPW2200_PCI_CFG_RNUM, ®s, 292bb5e3b2fSeh146360 0, 0, &ipw2200_csr_accattr, &cfgh); 293bb5e3b2fSeh146360 if (err != DDI_SUCCESS) { 294bb5e3b2fSeh146360 IPW2200_WARN((dip, CE_WARN, 295bb5e3b2fSeh146360 "ipw2200_attach(): unable to map spaces regs\n")); 296bb5e3b2fSeh146360 goto fail2; 297bb5e3b2fSeh146360 } 298bb5e3b2fSeh146360 ddi_put8(cfgh, (uint8_t *)(regs + 0x41), 0); 299bb5e3b2fSeh146360 vendor = ddi_get16(cfgh, (uint16_t *)(regs + PCI_CONF_VENID)); 300bb5e3b2fSeh146360 device = ddi_get16(cfgh, (uint16_t *)(regs + PCI_CONF_DEVID)); 301bb5e3b2fSeh146360 subven = ddi_get16(cfgh, (uint16_t *)(regs + PCI_CONF_SUBVENID)); 302bb5e3b2fSeh146360 subdev = ddi_get16(cfgh, (uint16_t *)(regs + PCI_CONF_SUBSYSID)); 303bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT, 304bb5e3b2fSeh146360 "ipw2200_attach(): vendor = 0x%04x, devic = 0x%04x," 305bb5e3b2fSeh146360 "subversion = 0x%04x, subdev = 0x%04x", 306bb5e3b2fSeh146360 vendor, device, subven, subdev)); 307bb5e3b2fSeh146360 ddi_regs_map_free(&cfgh); 308bb5e3b2fSeh146360 309bb5e3b2fSeh146360 /* 310bb5e3b2fSeh146360 * Map operating registers 311bb5e3b2fSeh146360 */ 312bb5e3b2fSeh146360 err = ddi_regs_map_setup(dip, IPW2200_PCI_CSR_RNUM, &sc->sc_regs, 313bb5e3b2fSeh146360 0, 0, &ipw2200_csr_accattr, &sc->sc_ioh); 314bb5e3b2fSeh146360 if (err != DDI_SUCCESS) { 315bb5e3b2fSeh146360 IPW2200_WARN((dip, CE_WARN, 316bb5e3b2fSeh146360 "ipw2200_attach(): ddi_regs_map_setup() failed\n")); 317bb5e3b2fSeh146360 goto fail2; 318bb5e3b2fSeh146360 } 319bb5e3b2fSeh146360 320bb5e3b2fSeh146360 /* 321bb5e3b2fSeh146360 * Reset the chip 322bb5e3b2fSeh146360 */ 323bb5e3b2fSeh146360 err = ipw2200_chip_reset(sc); 324bb5e3b2fSeh146360 if (err != DDI_SUCCESS) { 325bb5e3b2fSeh146360 IPW2200_WARN((dip, CE_WARN, 326bb5e3b2fSeh146360 "ipw2200_attach(): ipw2200_chip_reset() failed\n")); 327bb5e3b2fSeh146360 goto fail3; 328bb5e3b2fSeh146360 } 329bb5e3b2fSeh146360 330bb5e3b2fSeh146360 /* 331bb5e3b2fSeh146360 * Get the hardware configuration, including the MAC address 332bb5e3b2fSeh146360 * Then, init all the rings needed. 333bb5e3b2fSeh146360 */ 334bb5e3b2fSeh146360 ipw2200_hwconf_get(sc); 335bb5e3b2fSeh146360 err = ipw2200_ring_init(sc); 336bb5e3b2fSeh146360 if (err != DDI_SUCCESS) { 337bb5e3b2fSeh146360 IPW2200_WARN((dip, CE_WARN, 338bb5e3b2fSeh146360 "ipw2200_attach(): ipw2200_ring_init() failed\n")); 339bb5e3b2fSeh146360 goto fail3; 340bb5e3b2fSeh146360 } 341bb5e3b2fSeh146360 342bb5e3b2fSeh146360 /* 343bb5e3b2fSeh146360 * Initialize mutexs and condvars 344bb5e3b2fSeh146360 */ 345bb5e3b2fSeh146360 err = ddi_get_iblock_cookie(dip, 0, &sc->sc_iblk); 346bb5e3b2fSeh146360 if (err != DDI_SUCCESS) { 347bb5e3b2fSeh146360 IPW2200_WARN((dip, CE_WARN, 348bb5e3b2fSeh146360 "ipw2200_attach(): ddi_get_iblock_cookie() failed\n")); 349bb5e3b2fSeh146360 goto fail4; 350bb5e3b2fSeh146360 } 351bb5e3b2fSeh146360 352bb5e3b2fSeh146360 /* 353bb5e3b2fSeh146360 * interrupt lock 354bb5e3b2fSeh146360 */ 355bb5e3b2fSeh146360 mutex_init(&sc->sc_ilock, "intr-lock", MUTEX_DRIVER, 356bb5e3b2fSeh146360 (void *) sc->sc_iblk); 357bb5e3b2fSeh146360 cv_init(&sc->sc_fw_cond, "firmware-ok", CV_DRIVER, NULL); 358bb5e3b2fSeh146360 cv_init(&sc->sc_cmd_status_cond, "cmd-status-ring", CV_DRIVER, NULL); 359bb5e3b2fSeh146360 360bb5e3b2fSeh146360 /* 361bb5e3b2fSeh146360 * command ring lock 362bb5e3b2fSeh146360 */ 363bb5e3b2fSeh146360 mutex_init(&sc->sc_cmd_lock, "cmd-ring", MUTEX_DRIVER, 364bb5e3b2fSeh146360 (void *) sc->sc_iblk); 365bb5e3b2fSeh146360 cv_init(&sc->sc_cmd_cond, "cmd-ring", CV_DRIVER, NULL); 366bb5e3b2fSeh146360 367bb5e3b2fSeh146360 /* 368bb5e3b2fSeh146360 * tx ring lock 369bb5e3b2fSeh146360 */ 370bb5e3b2fSeh146360 mutex_init(&sc->sc_tx_lock, "tx-ring", MUTEX_DRIVER, 371bb5e3b2fSeh146360 (void *) sc->sc_iblk); 372bb5e3b2fSeh146360 373bb5e3b2fSeh146360 /* 374bb5e3b2fSeh146360 * multi-function lock, may acquire this during interrupt 375bb5e3b2fSeh146360 */ 376bb5e3b2fSeh146360 mutex_init(&sc->sc_mflock, "function-lock", MUTEX_DRIVER, 377bb5e3b2fSeh146360 (void *) sc->sc_iblk); 378bb5e3b2fSeh146360 cv_init(&sc->sc_mfthread_cv, NULL, CV_DRIVER, NULL); 379bb5e3b2fSeh146360 sc->sc_mf_thread = NULL; 380bb5e3b2fSeh146360 sc->sc_mfthread_switch = 0; 381bb5e3b2fSeh146360 382bb5e3b2fSeh146360 /* 383bb5e3b2fSeh146360 * Initialize the WiFi part, which will be used by generic layer 384bb5e3b2fSeh146360 * Need support more features in the furture, such as 385bb5e3b2fSeh146360 * IEEE80211_C_IBSS 386bb5e3b2fSeh146360 */ 387bb5e3b2fSeh146360 ic = &sc->sc_ic; 388bb5e3b2fSeh146360 ic->ic_phytype = IEEE80211_T_OFDM; 389bb5e3b2fSeh146360 ic->ic_opmode = IEEE80211_M_STA; 390bb5e3b2fSeh146360 ic->ic_state = IEEE80211_S_INIT; 391bb5e3b2fSeh146360 ic->ic_maxrssi = 100; /* experimental number */ 392bb5e3b2fSeh146360 ic->ic_caps = IEEE80211_C_SHPREAMBLE | IEEE80211_C_TXPMGT 393bb5e3b2fSeh146360 | IEEE80211_C_PMGT | IEEE80211_C_WEP; 394bb5e3b2fSeh146360 395bb5e3b2fSeh146360 /* 396bb5e3b2fSeh146360 * set mac addr 397bb5e3b2fSeh146360 */ 398bb5e3b2fSeh146360 IEEE80211_ADDR_COPY(ic->ic_macaddr, sc->sc_macaddr); 399bb5e3b2fSeh146360 400bb5e3b2fSeh146360 /* 401bb5e3b2fSeh146360 * set supported .11a rates and channel - (2915ABG only) 402bb5e3b2fSeh146360 */ 403bb5e3b2fSeh146360 if (device >= 0x4223) { 404bb5e3b2fSeh146360 /* .11a rates */ 405bb5e3b2fSeh146360 ic->ic_sup_rates[IEEE80211_MODE_11A] = ipw2200_rateset_11a; 406bb5e3b2fSeh146360 /* .11a channels */ 407bb5e3b2fSeh146360 for (i = 36; i <= 64; i += 4) { 408bb5e3b2fSeh146360 ic->ic_sup_channels[i].ich_freq = 409bb5e3b2fSeh146360 ieee80211_ieee2mhz(i, IEEE80211_CHAN_5GHZ); 410bb5e3b2fSeh146360 ic->ic_sup_channels[i].ich_flags = /* CHAN_A */ 411bb5e3b2fSeh146360 IEEE80211_CHAN_5GHZ | IEEE80211_CHAN_OFDM; 412bb5e3b2fSeh146360 } 413bb5e3b2fSeh146360 for (i = 149; i <= 165; i += 4) { 414bb5e3b2fSeh146360 ic->ic_sup_channels[i].ich_freq = 415bb5e3b2fSeh146360 ieee80211_ieee2mhz(i, IEEE80211_CHAN_5GHZ); 416bb5e3b2fSeh146360 ic->ic_sup_channels[i].ich_flags = /* CHAN_A */ 417bb5e3b2fSeh146360 IEEE80211_CHAN_5GHZ | IEEE80211_CHAN_OFDM; 418bb5e3b2fSeh146360 } 419bb5e3b2fSeh146360 } 420bb5e3b2fSeh146360 421bb5e3b2fSeh146360 /* 422bb5e3b2fSeh146360 * set supported .11b and .11g rates 423bb5e3b2fSeh146360 */ 424bb5e3b2fSeh146360 ic->ic_sup_rates[IEEE80211_MODE_11B] = ipw2200_rateset_11b; 425bb5e3b2fSeh146360 ic->ic_sup_rates[IEEE80211_MODE_11G] = ipw2200_rateset_11g; 426bb5e3b2fSeh146360 427bb5e3b2fSeh146360 /* 428bb5e3b2fSeh146360 * set supported .11b and .11g channels(1 through 14) 429bb5e3b2fSeh146360 */ 430bb5e3b2fSeh146360 for (i = 1; i < 14; i++) { 431bb5e3b2fSeh146360 ic->ic_sup_channels[i].ich_freq = 432bb5e3b2fSeh146360 ieee80211_ieee2mhz(i, IEEE80211_CHAN_2GHZ); 433bb5e3b2fSeh146360 ic->ic_sup_channels[i].ich_flags = 434bb5e3b2fSeh146360 IEEE80211_CHAN_CCK | IEEE80211_CHAN_OFDM | 435bb5e3b2fSeh146360 IEEE80211_CHAN_DYN | IEEE80211_CHAN_2GHZ; 436bb5e3b2fSeh146360 } 437bb5e3b2fSeh146360 438bb5e3b2fSeh146360 /* 439bb5e3b2fSeh146360 * IBSS channal undefined for now 440bb5e3b2fSeh146360 */ 441bb5e3b2fSeh146360 ic->ic_ibss_chan = &ic->ic_sup_channels[0]; 442bb5e3b2fSeh146360 ic->ic_xmit = ipw2200_send; 443bb5e3b2fSeh146360 444bb5e3b2fSeh146360 /* 445bb5e3b2fSeh146360 * init generic layer, then override state transition machine 446bb5e3b2fSeh146360 */ 447bb5e3b2fSeh146360 ieee80211_attach(ic); 448bb5e3b2fSeh146360 449bb5e3b2fSeh146360 /* 450bb5e3b2fSeh146360 * Override 80211 default routines 451bb5e3b2fSeh146360 */ 452bb5e3b2fSeh146360 ieee80211_media_init(ic); /* initial the node table and bss */ 453bb5e3b2fSeh146360 sc->sc_newstate = ic->ic_newstate; 454bb5e3b2fSeh146360 ic->ic_newstate = ipw2200_newstate; 455bb5e3b2fSeh146360 ic->ic_def_txkey = 0; 456bb5e3b2fSeh146360 sc->sc_authmode = IEEE80211_AUTH_OPEN; 457bb5e3b2fSeh146360 458bb5e3b2fSeh146360 /* 459bb5e3b2fSeh146360 * Add the interrupt handler 460bb5e3b2fSeh146360 */ 461bb5e3b2fSeh146360 err = ddi_add_intr(dip, 0, &sc->sc_iblk, NULL, 462bb5e3b2fSeh146360 ipw2200_intr, (caddr_t)sc); 463bb5e3b2fSeh146360 if (err != DDI_SUCCESS) { 464bb5e3b2fSeh146360 IPW2200_WARN((dip, CE_WARN, 465bb5e3b2fSeh146360 "ipw2200_attach(): ddi_add_intr() failed\n")); 466bb5e3b2fSeh146360 goto fail5; 467bb5e3b2fSeh146360 } 468bb5e3b2fSeh146360 469bb5e3b2fSeh146360 /* 470bb5e3b2fSeh146360 * Initialize pointer to device specific functions 471bb5e3b2fSeh146360 */ 472bb5e3b2fSeh146360 wd.wd_secalloc = WIFI_SEC_NONE; 473bb5e3b2fSeh146360 wd.wd_opmode = ic->ic_opmode; 474bb5e3b2fSeh146360 IEEE80211_ADDR_COPY(wd.wd_bssid, ic->ic_macaddr); 475bb5e3b2fSeh146360 476bb5e3b2fSeh146360 macp = mac_alloc(MAC_VERSION); 477bb5e3b2fSeh146360 if (err != 0) { 478bb5e3b2fSeh146360 IPW2200_WARN((dip, CE_WARN, 479bb5e3b2fSeh146360 "ipw2200_attach(): mac_alloc() failed\n")); 480bb5e3b2fSeh146360 goto fail6; 481bb5e3b2fSeh146360 } 482bb5e3b2fSeh146360 483bb5e3b2fSeh146360 macp->m_type_ident = MAC_PLUGIN_IDENT_WIFI; 484bb5e3b2fSeh146360 macp->m_driver = sc; 485bb5e3b2fSeh146360 macp->m_dip = dip; 486bb5e3b2fSeh146360 macp->m_src_addr = ic->ic_macaddr; 487bb5e3b2fSeh146360 macp->m_callbacks = &ipw2200_m_callbacks; 488bb5e3b2fSeh146360 macp->m_min_sdu = 0; 489bb5e3b2fSeh146360 macp->m_max_sdu = IEEE80211_MTU; 490bb5e3b2fSeh146360 macp->m_pdata = &wd; 491bb5e3b2fSeh146360 macp->m_pdata_size = sizeof (wd); 492bb5e3b2fSeh146360 493bb5e3b2fSeh146360 /* 494bb5e3b2fSeh146360 * Register the macp to mac 495bb5e3b2fSeh146360 */ 496bb5e3b2fSeh146360 err = mac_register(macp, &ic->ic_mach); 497bb5e3b2fSeh146360 mac_free(macp); 498bb5e3b2fSeh146360 if (err != DDI_SUCCESS) { 499bb5e3b2fSeh146360 IPW2200_WARN((dip, CE_WARN, 500bb5e3b2fSeh146360 "ipw2200_attach(): mac_register() failed\n")); 501bb5e3b2fSeh146360 goto fail6; 502bb5e3b2fSeh146360 } 503bb5e3b2fSeh146360 504bb5e3b2fSeh146360 /* 505bb5e3b2fSeh146360 * Create minor node of type DDI_NT_NET_WIFI 506bb5e3b2fSeh146360 */ 507bb5e3b2fSeh146360 (void) snprintf(strbuf, sizeof (strbuf), "%s%d", 508bb5e3b2fSeh146360 IPW2200_DRV_NAME, instance); 509bb5e3b2fSeh146360 err = ddi_create_minor_node(dip, strbuf, S_IFCHR, 510bb5e3b2fSeh146360 instance + 1, DDI_NT_NET_WIFI, 0); 511bb5e3b2fSeh146360 if (err != DDI_SUCCESS) 512bb5e3b2fSeh146360 IPW2200_WARN((dip, CE_WARN, 513bb5e3b2fSeh146360 "ipw2200_attach(): ddi_create_minor_node() failed\n")); 514bb5e3b2fSeh146360 515bb5e3b2fSeh146360 /* 516bb5e3b2fSeh146360 * Cache firmware will always be true 517bb5e3b2fSeh146360 */ 518bb5e3b2fSeh146360 (void) ipw2200_cache_firmware(sc); 519bb5e3b2fSeh146360 520bb5e3b2fSeh146360 /* 521bb5e3b2fSeh146360 * Notify link is down now 522bb5e3b2fSeh146360 */ 523bb5e3b2fSeh146360 mac_link_update(ic->ic_mach, LINK_STATE_DOWN); 524bb5e3b2fSeh146360 525bb5e3b2fSeh146360 /* 526bb5e3b2fSeh146360 * Create the mf thread to handle the link status, 527bb5e3b2fSeh146360 * recovery fatal error, etc. 528bb5e3b2fSeh146360 */ 529bb5e3b2fSeh146360 sc->sc_mfthread_switch = 1; 530bb5e3b2fSeh146360 if (sc->sc_mf_thread == NULL) 531bb5e3b2fSeh146360 sc->sc_mf_thread = thread_create((caddr_t)NULL, 0, 532bb5e3b2fSeh146360 ipw2200_thread, sc, 0, &p0, TS_RUN, minclsyspri); 533bb5e3b2fSeh146360 534bb5e3b2fSeh146360 return (DDI_SUCCESS); 535bb5e3b2fSeh146360 536bb5e3b2fSeh146360 fail6: 537bb5e3b2fSeh146360 ddi_remove_intr(dip, 0, sc->sc_iblk); 538bb5e3b2fSeh146360 fail5: 539bb5e3b2fSeh146360 ieee80211_detach(ic); 540bb5e3b2fSeh146360 541bb5e3b2fSeh146360 mutex_destroy(&sc->sc_ilock); 542bb5e3b2fSeh146360 mutex_destroy(&sc->sc_cmd_lock); 543bb5e3b2fSeh146360 mutex_destroy(&sc->sc_tx_lock); 544bb5e3b2fSeh146360 mutex_destroy(&sc->sc_mflock); 545bb5e3b2fSeh146360 cv_destroy(&sc->sc_fw_cond); 546bb5e3b2fSeh146360 cv_destroy(&sc->sc_cmd_status_cond); 547bb5e3b2fSeh146360 cv_destroy(&sc->sc_cmd_cond); 548bb5e3b2fSeh146360 cv_destroy(&sc->sc_mfthread_cv); 549bb5e3b2fSeh146360 fail4: 550bb5e3b2fSeh146360 ipw2200_ring_free(sc); 551bb5e3b2fSeh146360 fail3: 552bb5e3b2fSeh146360 ddi_regs_map_free(&sc->sc_ioh); 553bb5e3b2fSeh146360 fail2: 554bb5e3b2fSeh146360 ddi_soft_state_free(ipw2200_ssp, instance); 555bb5e3b2fSeh146360 fail1: 556bb5e3b2fSeh146360 return (err); 557bb5e3b2fSeh146360 } 558bb5e3b2fSeh146360 559bb5e3b2fSeh146360 560bb5e3b2fSeh146360 int 561bb5e3b2fSeh146360 ipw2200_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 562bb5e3b2fSeh146360 { 563bb5e3b2fSeh146360 struct ipw2200_softc *sc = 564bb5e3b2fSeh146360 ddi_get_soft_state(ipw2200_ssp, ddi_get_instance(dip)); 565bb5e3b2fSeh146360 int err; 566bb5e3b2fSeh146360 ASSERT(sc != NULL); 567bb5e3b2fSeh146360 568bb5e3b2fSeh146360 if (cmd != DDI_DETACH) 569bb5e3b2fSeh146360 return (DDI_FAILURE); 570bb5e3b2fSeh146360 571bb5e3b2fSeh146360 ipw2200_stop(sc); 572bb5e3b2fSeh146360 573bb5e3b2fSeh146360 /* 574bb5e3b2fSeh146360 * Destroy the mf_thread 575bb5e3b2fSeh146360 */ 576bb5e3b2fSeh146360 mutex_enter(&sc->sc_mflock); 577bb5e3b2fSeh146360 sc->sc_mfthread_switch = 0; 578bb5e3b2fSeh146360 while (sc->sc_mf_thread != NULL) { 579bb5e3b2fSeh146360 if (cv_wait_sig(&sc->sc_mfthread_cv, &sc->sc_mflock) == 0) 580bb5e3b2fSeh146360 break; 581bb5e3b2fSeh146360 } 582bb5e3b2fSeh146360 mutex_exit(&sc->sc_mflock); 583bb5e3b2fSeh146360 584bb5e3b2fSeh146360 /* 585bb5e3b2fSeh146360 * Unregister from the MAC layer subsystem 586bb5e3b2fSeh146360 */ 587bb5e3b2fSeh146360 err = mac_unregister(sc->sc_ic.ic_mach); 588bb5e3b2fSeh146360 if (err != DDI_SUCCESS) 589bb5e3b2fSeh146360 return (err); 590bb5e3b2fSeh146360 591bb5e3b2fSeh146360 ddi_remove_intr(dip, IPW2200_PCI_INTR_NUM, sc->sc_iblk); 592bb5e3b2fSeh146360 593bb5e3b2fSeh146360 mutex_destroy(&sc->sc_ilock); 594bb5e3b2fSeh146360 mutex_destroy(&sc->sc_cmd_lock); 595bb5e3b2fSeh146360 mutex_destroy(&sc->sc_tx_lock); 596bb5e3b2fSeh146360 mutex_destroy(&sc->sc_mflock); 597bb5e3b2fSeh146360 cv_destroy(&sc->sc_fw_cond); 598bb5e3b2fSeh146360 cv_destroy(&sc->sc_cmd_status_cond); 599bb5e3b2fSeh146360 cv_destroy(&sc->sc_cmd_cond); 600bb5e3b2fSeh146360 cv_destroy(&sc->sc_mfthread_cv); 601bb5e3b2fSeh146360 602bb5e3b2fSeh146360 /* 603bb5e3b2fSeh146360 * Detach ieee80211 604bb5e3b2fSeh146360 */ 605bb5e3b2fSeh146360 ieee80211_detach(&sc->sc_ic); 606bb5e3b2fSeh146360 607bb5e3b2fSeh146360 (void) ipw2200_free_firmware(sc); 608bb5e3b2fSeh146360 ipw2200_ring_free(sc); 609bb5e3b2fSeh146360 610bb5e3b2fSeh146360 ddi_regs_map_free(&sc->sc_ioh); 611bb5e3b2fSeh146360 ddi_remove_minor_node(dip, NULL); 612bb5e3b2fSeh146360 ddi_soft_state_free(ipw2200_ssp, ddi_get_instance(dip)); 613bb5e3b2fSeh146360 614bb5e3b2fSeh146360 return (DDI_SUCCESS); 615bb5e3b2fSeh146360 } 616bb5e3b2fSeh146360 617bb5e3b2fSeh146360 static void 618bb5e3b2fSeh146360 ipw2200_stop(struct ipw2200_softc *sc) 619bb5e3b2fSeh146360 { 620bb5e3b2fSeh146360 struct ieee80211com *ic = &sc->sc_ic; 621bb5e3b2fSeh146360 622bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_HWCAP, (sc->sc_dip, CE_CONT, 623bb5e3b2fSeh146360 "ipw2200_stop(): enter\n")); 624bb5e3b2fSeh146360 625bb5e3b2fSeh146360 ipw2200_master_stop(sc); 626bb5e3b2fSeh146360 ipw2200_csr_put32(sc, IPW2200_CSR_RST, IPW2200_RST_SW_RESET); 627bb5e3b2fSeh146360 628bb5e3b2fSeh146360 /* 629bb5e3b2fSeh146360 * Reset ring 630bb5e3b2fSeh146360 */ 631bb5e3b2fSeh146360 ipw2200_ring_reset(sc); 632bb5e3b2fSeh146360 633bb5e3b2fSeh146360 ieee80211_new_state(ic, IEEE80211_S_INIT, -1); 634bb5e3b2fSeh146360 sc->sc_flags &= ~IPW2200_FLAG_SCANNING; 635bb5e3b2fSeh146360 636bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_HWCAP, (sc->sc_dip, CE_CONT, 637bb5e3b2fSeh146360 "ipw2200_stop(): exit\n")); 638bb5e3b2fSeh146360 } 639bb5e3b2fSeh146360 640bb5e3b2fSeh146360 static int 641bb5e3b2fSeh146360 ipw2200_config(struct ipw2200_softc *sc) 642bb5e3b2fSeh146360 { 643bb5e3b2fSeh146360 struct ieee80211com *ic = &sc->sc_ic; 644bb5e3b2fSeh146360 struct ipw2200_configuration cfg; 645bb5e3b2fSeh146360 uint32_t data; 646bb5e3b2fSeh146360 struct ipw2200_txpower pwr; 647bb5e3b2fSeh146360 struct ipw2200_rateset rs; 648bb5e3b2fSeh146360 struct ipw2200_wep_key wkey; 649bb5e3b2fSeh146360 int err, i; 650bb5e3b2fSeh146360 651bb5e3b2fSeh146360 /* 652bb5e3b2fSeh146360 * Set the IBSS mode channel: Tx power 653bb5e3b2fSeh146360 */ 654bb5e3b2fSeh146360 if (ic->ic_opmode == IEEE80211_M_IBSS) { 655bb5e3b2fSeh146360 pwr.mode = IPW2200_MODE_11B; 656bb5e3b2fSeh146360 pwr.nchan = 11; 657bb5e3b2fSeh146360 for (i = 0; i < pwr.nchan; i++) { 658bb5e3b2fSeh146360 pwr.chan[i].chan = i + 1; 659bb5e3b2fSeh146360 pwr.chan[i].power = IPW2200_TXPOWER_MAX; 660bb5e3b2fSeh146360 } 661bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT, 662bb5e3b2fSeh146360 "ipw2200_config(): Setting .11b channels Tx power\n")); 663bb5e3b2fSeh146360 err = ipw2200_cmd(sc, IPW2200_CMD_SET_TX_POWER, 664bb5e3b2fSeh146360 &pwr, sizeof (pwr), 0); 665bb5e3b2fSeh146360 if (err != DDI_SUCCESS) 666bb5e3b2fSeh146360 return (err); 667bb5e3b2fSeh146360 668bb5e3b2fSeh146360 pwr.mode = IPW2200_MODE_11G; 669bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT, 670bb5e3b2fSeh146360 "ipw2200_config(): Setting .11g channels Tx power\n")); 671bb5e3b2fSeh146360 err = ipw2200_cmd(sc, IPW2200_CMD_SET_TX_POWER, 672bb5e3b2fSeh146360 &pwr, sizeof (pwr), 0); 673bb5e3b2fSeh146360 if (err != DDI_SUCCESS) 674bb5e3b2fSeh146360 return (err); 675bb5e3b2fSeh146360 } 676bb5e3b2fSeh146360 677bb5e3b2fSeh146360 /* 678bb5e3b2fSeh146360 * Set MAC address 679bb5e3b2fSeh146360 */ 680bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT, 681bb5e3b2fSeh146360 "ipw2200_config(): Setting MAC address to " 682bb5e3b2fSeh146360 "%02x:%02x:%02x:%02x:%02x:%02x\n", 683bb5e3b2fSeh146360 ic->ic_macaddr[0], ic->ic_macaddr[1], ic->ic_macaddr[2], 684bb5e3b2fSeh146360 ic->ic_macaddr[3], ic->ic_macaddr[4], ic->ic_macaddr[5])); 685bb5e3b2fSeh146360 err = ipw2200_cmd(sc, IPW2200_CMD_SET_MAC_ADDRESS, ic->ic_macaddr, 686bb5e3b2fSeh146360 IEEE80211_ADDR_LEN, 0); 687bb5e3b2fSeh146360 if (err != DDI_SUCCESS) 688bb5e3b2fSeh146360 return (err); 689bb5e3b2fSeh146360 690bb5e3b2fSeh146360 /* 691bb5e3b2fSeh146360 * Set basic system config settings: configuration(capabilities) 692bb5e3b2fSeh146360 */ 693bb5e3b2fSeh146360 (void) memset(&cfg, 0, sizeof (cfg)); 694bb5e3b2fSeh146360 cfg.bluetooth_coexistence = 1; 695bb5e3b2fSeh146360 cfg.multicast_enabled = 1; 696bb5e3b2fSeh146360 cfg.answer_pbreq = 1; 697bb5e3b2fSeh146360 cfg.noise_reported = 1; 698bb5e3b2fSeh146360 699bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT, 700bb5e3b2fSeh146360 "ipw2200_config(): Configuring adapter\n")); 701bb5e3b2fSeh146360 err = ipw2200_cmd(sc, IPW2200_CMD_SET_CONFIG, 702bb5e3b2fSeh146360 &cfg, sizeof (cfg), 0); 703bb5e3b2fSeh146360 if (err != DDI_SUCCESS) 704bb5e3b2fSeh146360 return (err); 705bb5e3b2fSeh146360 706bb5e3b2fSeh146360 /* 707bb5e3b2fSeh146360 * Set power mode 708bb5e3b2fSeh146360 */ 709bb5e3b2fSeh146360 data = LE_32(IPW2200_POWER_MODE_CAM); 710bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT, 711bb5e3b2fSeh146360 "ipw2200_config(): Setting power mode to %u\n", LE_32(data))); 712bb5e3b2fSeh146360 err = ipw2200_cmd(sc, IPW2200_CMD_SET_POWER_MODE, 713bb5e3b2fSeh146360 &data, sizeof (data), 0); 714bb5e3b2fSeh146360 if (err != DDI_SUCCESS) 715bb5e3b2fSeh146360 return (err); 716bb5e3b2fSeh146360 717bb5e3b2fSeh146360 /* 718bb5e3b2fSeh146360 * Set supported rates 719bb5e3b2fSeh146360 */ 720bb5e3b2fSeh146360 rs.mode = IPW2200_MODE_11G; 721bb5e3b2fSeh146360 rs.type = IPW2200_RATESET_TYPE_SUPPORTED; 722bb5e3b2fSeh146360 rs.nrates = ic->ic_sup_rates[IEEE80211_MODE_11G].ir_nrates; 723bb5e3b2fSeh146360 (void) memcpy(rs.rates, ic->ic_sup_rates[IEEE80211_MODE_11G].ir_rates, 724bb5e3b2fSeh146360 rs.nrates); 725bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT, 726bb5e3b2fSeh146360 "ipw2200_config(): Setting .11g supported rates(%u)\n", rs.nrates)); 727bb5e3b2fSeh146360 err = ipw2200_cmd(sc, IPW2200_CMD_SET_RATES, &rs, sizeof (rs), 0); 728bb5e3b2fSeh146360 if (err != DDI_SUCCESS) 729bb5e3b2fSeh146360 return (err); 730bb5e3b2fSeh146360 731bb5e3b2fSeh146360 rs.mode = IPW2200_MODE_11A; 732bb5e3b2fSeh146360 rs.type = IPW2200_RATESET_TYPE_SUPPORTED; 733bb5e3b2fSeh146360 rs.nrates = ic->ic_sup_rates[IEEE80211_MODE_11A].ir_nrates; 734bb5e3b2fSeh146360 (void) memcpy(rs.rates, ic->ic_sup_rates[IEEE80211_MODE_11A].ir_rates, 735bb5e3b2fSeh146360 rs.nrates); 736bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT, 737bb5e3b2fSeh146360 "ipw2200_config(): Setting .11a supported rates(%u)\n", rs.nrates)); 738bb5e3b2fSeh146360 err = ipw2200_cmd(sc, IPW2200_CMD_SET_RATES, &rs, sizeof (rs), 0); 739bb5e3b2fSeh146360 if (err != DDI_SUCCESS) 740bb5e3b2fSeh146360 return (err); 741bb5e3b2fSeh146360 742bb5e3b2fSeh146360 /* 743bb5e3b2fSeh146360 * Set RTS(request-to-send) threshold 744bb5e3b2fSeh146360 */ 745bb5e3b2fSeh146360 data = LE_32(ic->ic_rtsthreshold); 746bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT, 747bb5e3b2fSeh146360 "ipw2200_config(): Setting RTS threshold to %u\n", LE_32(data))); 748bb5e3b2fSeh146360 err = ipw2200_cmd(sc, IPW2200_CMD_SET_RTS_THRESHOLD, &data, 749bb5e3b2fSeh146360 sizeof (data), 0); 750bb5e3b2fSeh146360 if (err != DDI_SUCCESS) 751bb5e3b2fSeh146360 return (err); 752bb5e3b2fSeh146360 753bb5e3b2fSeh146360 /* 754bb5e3b2fSeh146360 * Set fragmentation threshold 755bb5e3b2fSeh146360 */ 756bb5e3b2fSeh146360 data = LE_32(ic->ic_fragthreshold); 757bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT, 758bb5e3b2fSeh146360 "ipw2200_config(): Setting fragmentation threshold to %u\n", 759bb5e3b2fSeh146360 LE_32(data))); 760bb5e3b2fSeh146360 err = ipw2200_cmd(sc, IPW2200_CMD_SET_FRAG_THRESHOLD, &data, 761bb5e3b2fSeh146360 sizeof (data), 0); 762bb5e3b2fSeh146360 if (err != DDI_SUCCESS) 763bb5e3b2fSeh146360 return (err); 764bb5e3b2fSeh146360 765bb5e3b2fSeh146360 /* 766bb5e3b2fSeh146360 * Set desired ESSID if we have 767bb5e3b2fSeh146360 */ 768bb5e3b2fSeh146360 if (ic->ic_des_esslen != 0) { 769bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT, 770bb5e3b2fSeh146360 "ipw2200_config(): Setting desired ESSID to " 771bb5e3b2fSeh146360 "(%u),%c%c%c%c%c%c%c%c\n", 772bb5e3b2fSeh146360 ic->ic_des_esslen, 773bb5e3b2fSeh146360 ic->ic_des_essid[0], ic->ic_des_essid[1], 774bb5e3b2fSeh146360 ic->ic_des_essid[2], ic->ic_des_essid[3], 775bb5e3b2fSeh146360 ic->ic_des_essid[4], ic->ic_des_essid[5], 776bb5e3b2fSeh146360 ic->ic_des_essid[6], ic->ic_des_essid[7])); 777bb5e3b2fSeh146360 err = ipw2200_cmd(sc, IPW2200_CMD_SET_ESSID, ic->ic_des_essid, 778bb5e3b2fSeh146360 ic->ic_des_esslen, 0); 779bb5e3b2fSeh146360 if (err != DDI_SUCCESS) 780bb5e3b2fSeh146360 return (err); 781bb5e3b2fSeh146360 } 782bb5e3b2fSeh146360 783bb5e3b2fSeh146360 /* 784bb5e3b2fSeh146360 * Set WEP initial vector(random seed) 785bb5e3b2fSeh146360 */ 786bb5e3b2fSeh146360 (void) random_get_pseudo_bytes((uint8_t *)&data, sizeof (data)); 787bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT, 788bb5e3b2fSeh146360 "ipw2200_config(): Setting initialization vector to %u\n", 789bb5e3b2fSeh146360 LE_32(data))); 790bb5e3b2fSeh146360 err = ipw2200_cmd(sc, IPW2200_CMD_SET_IV, &data, sizeof (data), 0); 791bb5e3b2fSeh146360 if (err != DDI_SUCCESS) 792bb5e3b2fSeh146360 return (err); 793bb5e3b2fSeh146360 794bb5e3b2fSeh146360 /* 795bb5e3b2fSeh146360 * Set WEP if any 796bb5e3b2fSeh146360 */ 797bb5e3b2fSeh146360 if (ic->ic_flags & IEEE80211_F_PRIVACY) { 798bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT, 799bb5e3b2fSeh146360 "ipw2200_config(): Setting Wep Key\n", LE_32(data))); 800bb5e3b2fSeh146360 for (i = 0; i < IEEE80211_WEP_NKID; i++) { 801bb5e3b2fSeh146360 wkey.cmd = IPW2200_WEP_KEY_CMD_SETKEY; 802bb5e3b2fSeh146360 wkey.idx = (uint8_t)i; 803bb5e3b2fSeh146360 wkey.len = ic->ic_nw_keys[i].wk_keylen; 804bb5e3b2fSeh146360 (void) memset(wkey.key, 0, sizeof (wkey.key)); 805bb5e3b2fSeh146360 if (ic->ic_nw_keys[i].wk_keylen) 806bb5e3b2fSeh146360 (void) memcpy(wkey.key, 807bb5e3b2fSeh146360 ic->ic_nw_keys[i].wk_key, 808bb5e3b2fSeh146360 ic->ic_nw_keys[i].wk_keylen); 809bb5e3b2fSeh146360 err = ipw2200_cmd(sc, IPW2200_CMD_SET_WEP_KEY, 810bb5e3b2fSeh146360 &wkey, sizeof (wkey), 0); 811bb5e3b2fSeh146360 if (err != DDI_SUCCESS) 812bb5e3b2fSeh146360 return (err); 813bb5e3b2fSeh146360 } 814bb5e3b2fSeh146360 } 815bb5e3b2fSeh146360 816bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT, 817bb5e3b2fSeh146360 "ipw2200_config(): Enabling adapter\n")); 818bb5e3b2fSeh146360 819bb5e3b2fSeh146360 return (ipw2200_cmd(sc, IPW2200_CMD_ENABLE, NULL, 0, 0)); 820bb5e3b2fSeh146360 } 821bb5e3b2fSeh146360 822bb5e3b2fSeh146360 static int 823bb5e3b2fSeh146360 ipw2200_cmd(struct ipw2200_softc *sc, 824bb5e3b2fSeh146360 uint32_t type, void *buf, size_t len, int async) 825bb5e3b2fSeh146360 { 826bb5e3b2fSeh146360 struct ipw2200_cmd_desc *cmd; 827bb5e3b2fSeh146360 clock_t clk; 828bb5e3b2fSeh146360 uint32_t idx; 829bb5e3b2fSeh146360 830bb5e3b2fSeh146360 mutex_enter(&sc->sc_cmd_lock); 831bb5e3b2fSeh146360 while (sc->sc_cmd_free < 1) 832bb5e3b2fSeh146360 cv_wait(&sc->sc_cmd_cond, &sc->sc_cmd_lock); 833bb5e3b2fSeh146360 834bb5e3b2fSeh146360 idx = sc->sc_cmd_cur; 835bb5e3b2fSeh146360 cmd = &sc->sc_cmdsc[idx]; 836bb5e3b2fSeh146360 (void) memset(cmd, 0, sizeof (*cmd)); 837bb5e3b2fSeh146360 838bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_RING, (sc->sc_dip, CE_CONT, 839bb5e3b2fSeh146360 "ipw2200_cmd(): cmd-cur=%d\n", idx)); 840bb5e3b2fSeh146360 841bb5e3b2fSeh146360 cmd->hdr.type = IPW2200_HDR_TYPE_COMMAND; 842bb5e3b2fSeh146360 cmd->hdr.flags = IPW2200_HDR_FLAG_IRQ; 843bb5e3b2fSeh146360 cmd->type = (uint8_t)type; 844bb5e3b2fSeh146360 if (len == 0 || buf == NULL) 845bb5e3b2fSeh146360 cmd->len = 0; 846bb5e3b2fSeh146360 else { 847bb5e3b2fSeh146360 cmd->len = (uint8_t)len; 848bb5e3b2fSeh146360 (void) memcpy(cmd->data, buf, len); 849bb5e3b2fSeh146360 } 850bb5e3b2fSeh146360 sc->sc_done[idx] = 0; 851bb5e3b2fSeh146360 852bb5e3b2fSeh146360 /* 853bb5e3b2fSeh146360 * DMA sync 854bb5e3b2fSeh146360 */ 855bb5e3b2fSeh146360 (void) ddi_dma_sync(sc->sc_dma_cmdsc.dr_hnd, 856bb5e3b2fSeh146360 idx * sizeof (struct ipw2200_cmd_desc), 857bb5e3b2fSeh146360 sizeof (struct ipw2200_cmd_desc), DDI_DMA_SYNC_FORDEV); 858bb5e3b2fSeh146360 859bb5e3b2fSeh146360 sc->sc_cmd_cur = RING_FORWARD(sc->sc_cmd_cur, 1, IPW2200_CMD_RING_SIZE); 860bb5e3b2fSeh146360 sc->sc_cmd_free--; 861bb5e3b2fSeh146360 862bb5e3b2fSeh146360 ipw2200_csr_put32(sc, IPW2200_CSR_CMD_WRITE_INDEX, sc->sc_cmd_cur); 863bb5e3b2fSeh146360 864bb5e3b2fSeh146360 mutex_exit(&sc->sc_cmd_lock); 865bb5e3b2fSeh146360 866bb5e3b2fSeh146360 if (async) 867bb5e3b2fSeh146360 goto out; 868bb5e3b2fSeh146360 869bb5e3b2fSeh146360 /* 870bb5e3b2fSeh146360 * Wait for command done 871bb5e3b2fSeh146360 */ 872bb5e3b2fSeh146360 mutex_enter(&sc->sc_ilock); 873bb5e3b2fSeh146360 while (sc->sc_done[idx] == 0) { 874bb5e3b2fSeh146360 /* pending */ 875bb5e3b2fSeh146360 clk = ddi_get_lbolt() + drv_usectohz(5000000); /* 5 second */ 876bb5e3b2fSeh146360 if (cv_timedwait(&sc->sc_cmd_status_cond, &sc->sc_ilock, clk) 877bb5e3b2fSeh146360 < 0) 878bb5e3b2fSeh146360 break; 879bb5e3b2fSeh146360 } 880bb5e3b2fSeh146360 mutex_exit(&sc->sc_ilock); 881bb5e3b2fSeh146360 882bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_RING, (sc->sc_dip, CE_CONT, 883bb5e3b2fSeh146360 "ipw2200_cmd(): cmd-done=%s\n", sc->sc_done[idx] ? "yes" : "no")); 884bb5e3b2fSeh146360 885bb5e3b2fSeh146360 if (sc->sc_done[idx] == 0) 886bb5e3b2fSeh146360 return (DDI_FAILURE); 887bb5e3b2fSeh146360 888bb5e3b2fSeh146360 out: 889bb5e3b2fSeh146360 return (DDI_SUCCESS); 890bb5e3b2fSeh146360 } 891bb5e3b2fSeh146360 892bb5e3b2fSeh146360 /* 893bb5e3b2fSeh146360 * If init failed, it will call stop internally. Therefore, it's unnecessary 894bb5e3b2fSeh146360 * to call ipw2200_stop() when this subroutine is failed. Otherwise, it may 895bb5e3b2fSeh146360 * be called twice. 896bb5e3b2fSeh146360 */ 897bb5e3b2fSeh146360 int 898bb5e3b2fSeh146360 ipw2200_init(struct ipw2200_softc *sc) 899bb5e3b2fSeh146360 { 900bb5e3b2fSeh146360 int err; 901bb5e3b2fSeh146360 902bb5e3b2fSeh146360 /* 903bb5e3b2fSeh146360 * No firmware is available, failed 904bb5e3b2fSeh146360 */ 905bb5e3b2fSeh146360 if (!(sc->sc_flags & IPW2200_FLAG_FW_CACHED)) { 906bb5e3b2fSeh146360 IPW2200_WARN((sc->sc_dip, CE_WARN, 907bb5e3b2fSeh146360 "ipw2200_init(): no firmware is available\n")); 908bb5e3b2fSeh146360 return (DDI_FAILURE); /* return directly */ 909bb5e3b2fSeh146360 } 910bb5e3b2fSeh146360 911bb5e3b2fSeh146360 ipw2200_stop(sc); 912bb5e3b2fSeh146360 913bb5e3b2fSeh146360 err = ipw2200_chip_reset(sc); 914bb5e3b2fSeh146360 if (err != DDI_SUCCESS) { 915bb5e3b2fSeh146360 IPW2200_WARN((sc->sc_dip, CE_WARN, 916bb5e3b2fSeh146360 "ipw2200_init(): could not reset adapter\n")); 917bb5e3b2fSeh146360 goto fail; 918bb5e3b2fSeh146360 } 919bb5e3b2fSeh146360 920bb5e3b2fSeh146360 /* 921bb5e3b2fSeh146360 * Load boot code 922bb5e3b2fSeh146360 */ 923bb5e3b2fSeh146360 err = ipw2200_load_fw(sc, sc->sc_fw.boot_base, sc->sc_fw.boot_size); 924bb5e3b2fSeh146360 if (err != DDI_SUCCESS) { 925bb5e3b2fSeh146360 IPW2200_WARN((sc->sc_dip, CE_WARN, 926bb5e3b2fSeh146360 "ipw2200_init(): could not load boot code\n")); 927bb5e3b2fSeh146360 goto fail; 928bb5e3b2fSeh146360 } 929bb5e3b2fSeh146360 930bb5e3b2fSeh146360 /* 931bb5e3b2fSeh146360 * Load boot microcode 932bb5e3b2fSeh146360 */ 933bb5e3b2fSeh146360 err = ipw2200_load_uc(sc, sc->sc_fw.uc_base, sc->sc_fw.uc_size); 934bb5e3b2fSeh146360 if (err != DDI_SUCCESS) { 935bb5e3b2fSeh146360 IPW2200_WARN((sc->sc_dip, CE_WARN, 936bb5e3b2fSeh146360 "ipw2200_init(): could not load microcode\n")); 937bb5e3b2fSeh146360 goto fail; 938bb5e3b2fSeh146360 } 939bb5e3b2fSeh146360 940bb5e3b2fSeh146360 ipw2200_master_stop(sc); 941bb5e3b2fSeh146360 ipw2200_ring_hwsetup(sc); 942bb5e3b2fSeh146360 943bb5e3b2fSeh146360 /* 944bb5e3b2fSeh146360 * Load firmware 945bb5e3b2fSeh146360 */ 946bb5e3b2fSeh146360 err = ipw2200_load_fw(sc, sc->sc_fw.fw_base, sc->sc_fw.fw_size); 947bb5e3b2fSeh146360 if (err != DDI_SUCCESS) { 948bb5e3b2fSeh146360 IPW2200_WARN((sc->sc_dip, CE_WARN, 949bb5e3b2fSeh146360 "ipw2200_init(): could not load firmware\n")); 950bb5e3b2fSeh146360 goto fail; 951bb5e3b2fSeh146360 } 952bb5e3b2fSeh146360 953bb5e3b2fSeh146360 sc->sc_flags |= IPW2200_FLAG_FW_INITED; 954bb5e3b2fSeh146360 955bb5e3b2fSeh146360 /* 956bb5e3b2fSeh146360 * Hardware will be enabled after configuration 957bb5e3b2fSeh146360 */ 958bb5e3b2fSeh146360 err = ipw2200_config(sc); 959bb5e3b2fSeh146360 if (err != DDI_SUCCESS) { 960bb5e3b2fSeh146360 IPW2200_WARN((sc->sc_dip, CE_WARN, 961bb5e3b2fSeh146360 "ipw2200_init(): device configuration failed\n")); 962bb5e3b2fSeh146360 goto fail; 963bb5e3b2fSeh146360 } 964bb5e3b2fSeh146360 965bb5e3b2fSeh146360 /* 966bb5e3b2fSeh146360 * workround to prevent too many h/w error. 967bb5e3b2fSeh146360 * delay for a while till h/w is stable. 968bb5e3b2fSeh146360 */ 969bb5e3b2fSeh146360 delay(drv_usectohz(delay_config_stable)); 970bb5e3b2fSeh146360 971bb5e3b2fSeh146360 ieee80211_begin_scan(&sc->sc_ic, 1); /* reset scan */ 972bb5e3b2fSeh146360 return (DDI_SUCCESS); /* return successfully */ 973bb5e3b2fSeh146360 fail: 974bb5e3b2fSeh146360 ipw2200_stop(sc); 975bb5e3b2fSeh146360 return (err); 976bb5e3b2fSeh146360 } 977bb5e3b2fSeh146360 978bb5e3b2fSeh146360 /* 979bb5e3b2fSeh146360 * get hardware configurations from EEPROM embedded within PRO/2200 980bb5e3b2fSeh146360 */ 981bb5e3b2fSeh146360 static void 982bb5e3b2fSeh146360 ipw2200_hwconf_get(struct ipw2200_softc *sc) 983bb5e3b2fSeh146360 { 984bb5e3b2fSeh146360 int i; 985bb5e3b2fSeh146360 uint16_t val; 986bb5e3b2fSeh146360 987bb5e3b2fSeh146360 /* 988bb5e3b2fSeh146360 * Get mac address 989bb5e3b2fSeh146360 */ 990bb5e3b2fSeh146360 i = 0; 991bb5e3b2fSeh146360 val = ipw2200_rom_get16(sc, IPW2200_EEPROM_MAC + 0); 992bb5e3b2fSeh146360 sc->sc_macaddr[i++] = val >> 8; 993bb5e3b2fSeh146360 sc->sc_macaddr[i++] = val & 0xff; 994bb5e3b2fSeh146360 val = ipw2200_rom_get16(sc, IPW2200_EEPROM_MAC + 1); 995bb5e3b2fSeh146360 sc->sc_macaddr[i++] = val >> 8; 996bb5e3b2fSeh146360 sc->sc_macaddr[i++] = val & 0xff; 997bb5e3b2fSeh146360 val = ipw2200_rom_get16(sc, IPW2200_EEPROM_MAC + 2); 998bb5e3b2fSeh146360 sc->sc_macaddr[i++] = val >> 8; 999bb5e3b2fSeh146360 sc->sc_macaddr[i++] = val & 0xff; 1000bb5e3b2fSeh146360 1001bb5e3b2fSeh146360 /* 1002bb5e3b2fSeh146360 * formatted MAC address string 1003bb5e3b2fSeh146360 */ 1004bb5e3b2fSeh146360 (void) snprintf(sc->sc_macstr, sizeof (sc->sc_macstr), 1005bb5e3b2fSeh146360 "%02x:%02x:%02x:%02x:%02x:%02x", 1006bb5e3b2fSeh146360 sc->sc_macaddr[0], sc->sc_macaddr[1], 1007bb5e3b2fSeh146360 sc->sc_macaddr[2], sc->sc_macaddr[3], 1008bb5e3b2fSeh146360 sc->sc_macaddr[4], sc->sc_macaddr[5]); 1009bb5e3b2fSeh146360 1010bb5e3b2fSeh146360 } 1011bb5e3b2fSeh146360 1012bb5e3b2fSeh146360 /* 1013bb5e3b2fSeh146360 * all ipw2200 interrupts will be masked by this routine 1014bb5e3b2fSeh146360 */ 1015bb5e3b2fSeh146360 static void 1016bb5e3b2fSeh146360 ipw2200_master_stop(struct ipw2200_softc *sc) 1017bb5e3b2fSeh146360 { 1018bb5e3b2fSeh146360 int ntries; 1019bb5e3b2fSeh146360 1020bb5e3b2fSeh146360 /* 1021bb5e3b2fSeh146360 * disable interrupts 1022bb5e3b2fSeh146360 */ 1023bb5e3b2fSeh146360 ipw2200_csr_put32(sc, IPW2200_CSR_INTR_MASK, 0); 1024bb5e3b2fSeh146360 ipw2200_csr_put32(sc, IPW2200_CSR_RST, IPW2200_RST_STOP_MASTER); 1025bb5e3b2fSeh146360 1026bb5e3b2fSeh146360 /* 1027bb5e3b2fSeh146360 * wait long enough to ensure hardware stop successfully. 1028bb5e3b2fSeh146360 */ 1029bb5e3b2fSeh146360 for (ntries = 0; ntries < 500; ntries++) { 1030bb5e3b2fSeh146360 if (ipw2200_csr_get32(sc, IPW2200_CSR_RST) & 1031bb5e3b2fSeh146360 IPW2200_RST_MASTER_DISABLED) 1032bb5e3b2fSeh146360 break; 1033bb5e3b2fSeh146360 /* wait for a while */ 1034bb5e3b2fSeh146360 drv_usecwait(100); 1035bb5e3b2fSeh146360 } 1036bb5e3b2fSeh146360 if (ntries == 500) 1037bb5e3b2fSeh146360 IPW2200_WARN((sc->sc_dip, CE_WARN, 1038bb5e3b2fSeh146360 "ipw2200_master_stop(): timeout\n")); 1039bb5e3b2fSeh146360 1040bb5e3b2fSeh146360 ipw2200_csr_put32(sc, IPW2200_CSR_RST, 1041bb5e3b2fSeh146360 IPW2200_RST_PRINCETON_RESET | 1042bb5e3b2fSeh146360 ipw2200_csr_get32(sc, IPW2200_CSR_RST)); 1043bb5e3b2fSeh146360 1044bb5e3b2fSeh146360 sc->sc_flags &= ~IPW2200_FLAG_FW_INITED; 1045bb5e3b2fSeh146360 } 1046bb5e3b2fSeh146360 1047bb5e3b2fSeh146360 /* 1048bb5e3b2fSeh146360 * all ipw2200 interrupts will be masked by this routine 1049bb5e3b2fSeh146360 */ 1050bb5e3b2fSeh146360 static int 1051bb5e3b2fSeh146360 ipw2200_chip_reset(struct ipw2200_softc *sc) 1052bb5e3b2fSeh146360 { 1053bb5e3b2fSeh146360 uint32_t tmp; 1054bb5e3b2fSeh146360 int ntries, i; 1055bb5e3b2fSeh146360 1056bb5e3b2fSeh146360 ipw2200_master_stop(sc); 1057bb5e3b2fSeh146360 1058bb5e3b2fSeh146360 /* 1059bb5e3b2fSeh146360 * Move adapter to DO state 1060bb5e3b2fSeh146360 */ 1061bb5e3b2fSeh146360 tmp = ipw2200_csr_get32(sc, IPW2200_CSR_CTL); 1062bb5e3b2fSeh146360 ipw2200_csr_put32(sc, IPW2200_CSR_CTL, tmp | IPW2200_CTL_INIT); 1063bb5e3b2fSeh146360 1064bb5e3b2fSeh146360 /* 1065bb5e3b2fSeh146360 * Initialize Phase-Locked Level (PLL) 1066bb5e3b2fSeh146360 */ 1067bb5e3b2fSeh146360 ipw2200_csr_put32(sc, IPW2200_CSR_READ_INT, IPW2200_READ_INT_INIT_HOST); 1068bb5e3b2fSeh146360 1069bb5e3b2fSeh146360 /* 1070bb5e3b2fSeh146360 * Wait for clock stabilization 1071bb5e3b2fSeh146360 */ 1072bb5e3b2fSeh146360 for (ntries = 0; ntries < 1000; ntries++) { 1073bb5e3b2fSeh146360 if (ipw2200_csr_get32(sc, IPW2200_CSR_CTL) & 1074bb5e3b2fSeh146360 IPW2200_CTL_CLOCK_READY) 1075bb5e3b2fSeh146360 break; 1076bb5e3b2fSeh146360 drv_usecwait(200); 1077bb5e3b2fSeh146360 } 1078bb5e3b2fSeh146360 if (ntries == 1000) { 1079bb5e3b2fSeh146360 IPW2200_WARN((sc->sc_dip, CE_WARN, 1080bb5e3b2fSeh146360 "ipw2200_chip_reset(): timeout\n")); 1081bb5e3b2fSeh146360 return (DDI_FAILURE); 1082bb5e3b2fSeh146360 } 1083bb5e3b2fSeh146360 1084bb5e3b2fSeh146360 tmp = ipw2200_csr_get32(sc, IPW2200_CSR_RST); 1085bb5e3b2fSeh146360 ipw2200_csr_put32(sc, IPW2200_CSR_RST, tmp | IPW2200_RST_SW_RESET); 1086bb5e3b2fSeh146360 1087bb5e3b2fSeh146360 drv_usecwait(10); 1088bb5e3b2fSeh146360 1089bb5e3b2fSeh146360 tmp = ipw2200_csr_get32(sc, IPW2200_CSR_CTL); 1090bb5e3b2fSeh146360 ipw2200_csr_put32(sc, IPW2200_CSR_CTL, tmp | IPW2200_CTL_INIT); 1091bb5e3b2fSeh146360 1092bb5e3b2fSeh146360 /* 1093bb5e3b2fSeh146360 * clear NIC memory 1094bb5e3b2fSeh146360 */ 1095bb5e3b2fSeh146360 ipw2200_csr_put32(sc, IPW2200_CSR_AUTOINC_ADDR, 0); 1096bb5e3b2fSeh146360 for (i = 0; i < 0xc000; i++) 1097bb5e3b2fSeh146360 ipw2200_csr_put32(sc, IPW2200_CSR_AUTOINC_DATA, 0); 1098bb5e3b2fSeh146360 1099bb5e3b2fSeh146360 return (DDI_SUCCESS); 1100bb5e3b2fSeh146360 } 1101bb5e3b2fSeh146360 1102bb5e3b2fSeh146360 /* 1103bb5e3b2fSeh146360 * This function is used by wificonfig/dladm to get the current 1104bb5e3b2fSeh146360 * radio status, it is off/on 1105bb5e3b2fSeh146360 */ 1106bb5e3b2fSeh146360 int 1107bb5e3b2fSeh146360 ipw2200_radio_status(struct ipw2200_softc *sc) 1108bb5e3b2fSeh146360 { 1109bb5e3b2fSeh146360 int val; 1110bb5e3b2fSeh146360 1111bb5e3b2fSeh146360 val = (ipw2200_csr_get32(sc, IPW2200_CSR_IO) & 1112bb5e3b2fSeh146360 IPW2200_IO_RADIO_ENABLED) ? 1 : 0; 1113bb5e3b2fSeh146360 1114bb5e3b2fSeh146360 return (val); 1115bb5e3b2fSeh146360 } 1116bb5e3b2fSeh146360 /* 1117bb5e3b2fSeh146360 * This function is used to get the statistic 1118bb5e3b2fSeh146360 */ 1119bb5e3b2fSeh146360 void 1120bb5e3b2fSeh146360 ipw2200_get_statistics(struct ipw2200_softc *sc) 1121bb5e3b2fSeh146360 { 1122bb5e3b2fSeh146360 struct ieee80211com *ic = &sc->sc_ic; 1123bb5e3b2fSeh146360 1124bb5e3b2fSeh146360 uint32_t size, buf[128]; 1125bb5e3b2fSeh146360 1126bb5e3b2fSeh146360 if (!(sc->sc_flags & IPW2200_FLAG_FW_INITED)) { 1127bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_IOCTL, (sc->sc_dip, CE_CONT, 1128bb5e3b2fSeh146360 "ipw2200_get_statistic(): fw doesn't download yet.")); 1129bb5e3b2fSeh146360 return; 1130bb5e3b2fSeh146360 } 1131bb5e3b2fSeh146360 1132bb5e3b2fSeh146360 size = min(ipw2200_csr_get32(sc, IPW2200_CSR_TABLE0_SIZE), 128 - 1); 1133bb5e3b2fSeh146360 ipw2200_csr_getbuf32(sc, IPW2200_CSR_TABLE0_BASE, &buf[1], size); 1134bb5e3b2fSeh146360 1135bb5e3b2fSeh146360 /* 1136bb5e3b2fSeh146360 * To retrieve the statistic information into proper places. There are 1137bb5e3b2fSeh146360 * lot of information. These table will be read once a second. 1138bb5e3b2fSeh146360 * Hopefully, it will not effect the performance. 1139bb5e3b2fSeh146360 */ 1140bb5e3b2fSeh146360 1141bb5e3b2fSeh146360 /* 1142bb5e3b2fSeh146360 * For the tx/crc information, we can get them from chip directly; 1143bb5e3b2fSeh146360 * For the rx/wep error/(rts) related information, leave them net80211. 1144bb5e3b2fSeh146360 */ 1145bb5e3b2fSeh146360 /* WIFI_STAT_TX_FRAGS */ 1146bb5e3b2fSeh146360 ic->ic_stats.is_tx_frags = (uint32_t)buf[5]; 1147bb5e3b2fSeh146360 /* WIFI_STAT_MCAST_TX */ 1148bb5e3b2fSeh146360 ic->ic_stats.is_tx_mcast = (uint32_t)buf[31]; 1149bb5e3b2fSeh146360 /* WIFI_STAT_TX_RETRANS */ 1150bb5e3b2fSeh146360 ic->ic_stats.is_tx_retries = (uint32_t)buf[56]; 1151bb5e3b2fSeh146360 /* WIFI_STAT_TX_FAILED */ 1152bb5e3b2fSeh146360 ic->ic_stats.is_tx_failed = (uint32_t)buf[57]; 1153bb5e3b2fSeh146360 /* MAC_STAT_OBYTES */ 1154bb5e3b2fSeh146360 ic->ic_stats.is_tx_bytes = (uint32_t)buf[64]; 1155bb5e3b2fSeh146360 } 1156bb5e3b2fSeh146360 1157bb5e3b2fSeh146360 /* 1158bb5e3b2fSeh146360 * DMA region alloc subroutine 1159bb5e3b2fSeh146360 */ 1160bb5e3b2fSeh146360 int 1161bb5e3b2fSeh146360 ipw2200_dma_region_alloc(struct ipw2200_softc *sc, struct dma_region *dr, 1162bb5e3b2fSeh146360 size_t size, uint_t dir, uint_t flags) 1163bb5e3b2fSeh146360 { 1164bb5e3b2fSeh146360 dev_info_t *dip = sc->sc_dip; 1165bb5e3b2fSeh146360 int err; 1166bb5e3b2fSeh146360 1167bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_DMA, (sc->sc_dip, CE_CONT, 1168bb5e3b2fSeh146360 "ipw2200_dma_region_alloc(): size =%u\n", size)); 1169bb5e3b2fSeh146360 1170bb5e3b2fSeh146360 err = ddi_dma_alloc_handle(dip, &ipw2200_dma_attr, DDI_DMA_SLEEP, NULL, 1171bb5e3b2fSeh146360 &dr->dr_hnd); 1172bb5e3b2fSeh146360 if (err != DDI_SUCCESS) { 1173bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_DMA, (sc->sc_dip, CE_CONT, 1174bb5e3b2fSeh146360 "ipw2200_dma_region_alloc(): " 1175bb5e3b2fSeh146360 "ddi_dma_alloc_handle() failed\n")); 1176bb5e3b2fSeh146360 goto fail0; 1177bb5e3b2fSeh146360 } 1178bb5e3b2fSeh146360 1179bb5e3b2fSeh146360 err = ddi_dma_mem_alloc(dr->dr_hnd, size, &ipw2200_dma_accattr, 1180bb5e3b2fSeh146360 flags, DDI_DMA_SLEEP, NULL, 1181bb5e3b2fSeh146360 &dr->dr_base, &dr->dr_size, &dr->dr_acc); 1182bb5e3b2fSeh146360 if (err != DDI_SUCCESS) { 1183bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_DMA, (sc->sc_dip, CE_CONT, 1184bb5e3b2fSeh146360 "ipw2200_dma_region_alloc(): " 1185bb5e3b2fSeh146360 "ddi_dma_mem_alloc() failed\n")); 1186bb5e3b2fSeh146360 goto fail1; 1187bb5e3b2fSeh146360 } 1188bb5e3b2fSeh146360 1189bb5e3b2fSeh146360 err = ddi_dma_addr_bind_handle(dr->dr_hnd, NULL, 1190bb5e3b2fSeh146360 dr->dr_base, dr->dr_size, 1191bb5e3b2fSeh146360 dir | flags, DDI_DMA_SLEEP, NULL, 1192bb5e3b2fSeh146360 &dr->dr_cookie, &dr->dr_ccnt); 1193bb5e3b2fSeh146360 if (err != DDI_DMA_MAPPED) { 1194bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_DMA, (sc->sc_dip, CE_CONT, 1195bb5e3b2fSeh146360 "ipw2200_dma_region_alloc(): " 1196bb5e3b2fSeh146360 "ddi_dma_addr_bind_handle() failed\n")); 1197bb5e3b2fSeh146360 goto fail2; 1198bb5e3b2fSeh146360 } 1199bb5e3b2fSeh146360 1200bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_DMA, (sc->sc_dip, CE_CONT, 1201bb5e3b2fSeh146360 "ipw2200_dma_region_alloc(): ccnt=%u\n", dr->dr_ccnt)); 1202bb5e3b2fSeh146360 1203bb5e3b2fSeh146360 if (dr->dr_ccnt != 1) { 1204bb5e3b2fSeh146360 err = DDI_FAILURE; 1205bb5e3b2fSeh146360 goto fail3; 1206bb5e3b2fSeh146360 } 1207bb5e3b2fSeh146360 1208bb5e3b2fSeh146360 dr->dr_pbase = dr->dr_cookie.dmac_address; 1209bb5e3b2fSeh146360 1210bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_DMA, (sc->sc_dip, CE_CONT, 1211bb5e3b2fSeh146360 "ipw2200_dma_region_alloc(): get physical-base=0x%08x\n", 1212bb5e3b2fSeh146360 dr->dr_pbase)); 1213bb5e3b2fSeh146360 1214bb5e3b2fSeh146360 return (DDI_SUCCESS); 1215bb5e3b2fSeh146360 1216bb5e3b2fSeh146360 fail3: 1217bb5e3b2fSeh146360 (void) ddi_dma_unbind_handle(dr->dr_hnd); 1218bb5e3b2fSeh146360 fail2: 1219bb5e3b2fSeh146360 ddi_dma_mem_free(&dr->dr_acc); 1220bb5e3b2fSeh146360 fail1: 1221bb5e3b2fSeh146360 ddi_dma_free_handle(&dr->dr_hnd); 1222bb5e3b2fSeh146360 fail0: 1223bb5e3b2fSeh146360 return (err); 1224bb5e3b2fSeh146360 } 1225bb5e3b2fSeh146360 1226bb5e3b2fSeh146360 void 1227bb5e3b2fSeh146360 ipw2200_dma_region_free(struct dma_region *dr) 1228bb5e3b2fSeh146360 { 1229bb5e3b2fSeh146360 (void) ddi_dma_unbind_handle(dr->dr_hnd); 1230bb5e3b2fSeh146360 ddi_dma_mem_free(&dr->dr_acc); 1231bb5e3b2fSeh146360 ddi_dma_free_handle(&dr->dr_hnd); 1232bb5e3b2fSeh146360 } 1233bb5e3b2fSeh146360 1234bb5e3b2fSeh146360 static int 1235bb5e3b2fSeh146360 ipw2200_ring_alloc(struct ipw2200_softc *sc) 1236bb5e3b2fSeh146360 { 1237bb5e3b2fSeh146360 int err, i; 1238bb5e3b2fSeh146360 1239bb5e3b2fSeh146360 /* 1240bb5e3b2fSeh146360 * tx desc ring 1241bb5e3b2fSeh146360 */ 1242bb5e3b2fSeh146360 sc->sc_dma_txdsc.dr_name = "ipw2200-tx-desc-ring"; 1243bb5e3b2fSeh146360 err = ipw2200_dma_region_alloc(sc, &sc->sc_dma_txdsc, 1244bb5e3b2fSeh146360 IPW2200_TX_RING_SIZE * sizeof (struct ipw2200_tx_desc), 1245bb5e3b2fSeh146360 DDI_DMA_WRITE, DDI_DMA_CONSISTENT); 1246bb5e3b2fSeh146360 if (err != DDI_SUCCESS) 1247bb5e3b2fSeh146360 goto fail0; 1248bb5e3b2fSeh146360 /* 1249bb5e3b2fSeh146360 * tx buffer array 1250bb5e3b2fSeh146360 */ 1251bb5e3b2fSeh146360 for (i = 0; i < IPW2200_TX_RING_SIZE; i++) { 1252bb5e3b2fSeh146360 sc->sc_dma_txbufs[i].dr_name = "ipw2200-tx-buf"; 1253bb5e3b2fSeh146360 err = ipw2200_dma_region_alloc(sc, &sc->sc_dma_txbufs[i], 1254bb5e3b2fSeh146360 IPW2200_TXBUF_SIZE, DDI_DMA_WRITE, DDI_DMA_STREAMING); 1255bb5e3b2fSeh146360 if (err != DDI_SUCCESS) { 1256bb5e3b2fSeh146360 while (i >= 0) { 1257bb5e3b2fSeh146360 ipw2200_dma_region_free(&sc->sc_dma_txbufs[i]); 1258bb5e3b2fSeh146360 i--; 1259bb5e3b2fSeh146360 } 1260bb5e3b2fSeh146360 goto fail1; 1261bb5e3b2fSeh146360 } 1262bb5e3b2fSeh146360 } 1263bb5e3b2fSeh146360 /* 1264bb5e3b2fSeh146360 * rx buffer array 1265bb5e3b2fSeh146360 */ 1266bb5e3b2fSeh146360 for (i = 0; i < IPW2200_RX_RING_SIZE; i++) { 1267bb5e3b2fSeh146360 sc->sc_dma_rxbufs[i].dr_name = "ipw2200-rx-buf"; 1268bb5e3b2fSeh146360 err = ipw2200_dma_region_alloc(sc, &sc->sc_dma_rxbufs[i], 1269bb5e3b2fSeh146360 IPW2200_RXBUF_SIZE, DDI_DMA_READ, DDI_DMA_STREAMING); 1270bb5e3b2fSeh146360 if (err != DDI_SUCCESS) { 1271bb5e3b2fSeh146360 while (i >= 0) { 1272bb5e3b2fSeh146360 ipw2200_dma_region_free(&sc->sc_dma_rxbufs[i]); 1273bb5e3b2fSeh146360 i--; 1274bb5e3b2fSeh146360 } 1275bb5e3b2fSeh146360 goto fail2; 1276bb5e3b2fSeh146360 } 1277bb5e3b2fSeh146360 } 1278bb5e3b2fSeh146360 /* 1279bb5e3b2fSeh146360 * cmd desc ring 1280bb5e3b2fSeh146360 */ 1281bb5e3b2fSeh146360 sc->sc_dma_cmdsc.dr_name = "ipw2200-cmd-desc-ring"; 1282bb5e3b2fSeh146360 err = ipw2200_dma_region_alloc(sc, &sc->sc_dma_cmdsc, 1283bb5e3b2fSeh146360 IPW2200_CMD_RING_SIZE * sizeof (struct ipw2200_cmd_desc), 1284bb5e3b2fSeh146360 DDI_DMA_WRITE, DDI_DMA_CONSISTENT); 1285bb5e3b2fSeh146360 if (err != DDI_SUCCESS) 1286bb5e3b2fSeh146360 goto fail3; 1287bb5e3b2fSeh146360 1288bb5e3b2fSeh146360 return (DDI_SUCCESS); 1289bb5e3b2fSeh146360 1290bb5e3b2fSeh146360 fail3: 1291bb5e3b2fSeh146360 for (i = 0; i < IPW2200_RX_RING_SIZE; i++) 1292bb5e3b2fSeh146360 ipw2200_dma_region_free(&sc->sc_dma_rxbufs[i]); 1293bb5e3b2fSeh146360 fail2: 1294bb5e3b2fSeh146360 for (i = 0; i < IPW2200_TX_RING_SIZE; i++) 1295bb5e3b2fSeh146360 ipw2200_dma_region_free(&sc->sc_dma_txbufs[i]); 1296bb5e3b2fSeh146360 fail1: 1297bb5e3b2fSeh146360 ipw2200_dma_region_free(&sc->sc_dma_txdsc); 1298bb5e3b2fSeh146360 fail0: 1299bb5e3b2fSeh146360 return (err); 1300bb5e3b2fSeh146360 } 1301bb5e3b2fSeh146360 1302bb5e3b2fSeh146360 static void 1303bb5e3b2fSeh146360 ipw2200_ring_free(struct ipw2200_softc *sc) 1304bb5e3b2fSeh146360 { 1305bb5e3b2fSeh146360 int i; 1306bb5e3b2fSeh146360 1307bb5e3b2fSeh146360 /* 1308bb5e3b2fSeh146360 * tx ring desc 1309bb5e3b2fSeh146360 */ 1310bb5e3b2fSeh146360 ipw2200_dma_region_free(&sc->sc_dma_txdsc); 1311bb5e3b2fSeh146360 /* 1312bb5e3b2fSeh146360 * tx buf 1313bb5e3b2fSeh146360 */ 1314bb5e3b2fSeh146360 for (i = 0; i < IPW2200_TX_RING_SIZE; i++) 1315bb5e3b2fSeh146360 ipw2200_dma_region_free(&sc->sc_dma_txbufs[i]); 1316bb5e3b2fSeh146360 /* 1317bb5e3b2fSeh146360 * rx buf 1318bb5e3b2fSeh146360 */ 1319bb5e3b2fSeh146360 for (i = 0; i < IPW2200_RX_RING_SIZE; i++) 1320bb5e3b2fSeh146360 ipw2200_dma_region_free(&sc->sc_dma_rxbufs[i]); 1321bb5e3b2fSeh146360 /* 1322bb5e3b2fSeh146360 * command ring desc 1323bb5e3b2fSeh146360 */ 1324bb5e3b2fSeh146360 ipw2200_dma_region_free(&sc->sc_dma_cmdsc); 1325bb5e3b2fSeh146360 } 1326bb5e3b2fSeh146360 1327bb5e3b2fSeh146360 static void 1328bb5e3b2fSeh146360 ipw2200_ring_reset(struct ipw2200_softc *sc) 1329bb5e3b2fSeh146360 { 1330bb5e3b2fSeh146360 int i; 1331bb5e3b2fSeh146360 1332bb5e3b2fSeh146360 /* 1333bb5e3b2fSeh146360 * tx desc ring & buffer array 1334bb5e3b2fSeh146360 */ 1335bb5e3b2fSeh146360 sc->sc_tx_cur = 0; 1336bb5e3b2fSeh146360 sc->sc_tx_free = IPW2200_TX_RING_SIZE; 1337bb5e3b2fSeh146360 sc->sc_txdsc = (struct ipw2200_tx_desc *)sc->sc_dma_txdsc.dr_base; 1338bb5e3b2fSeh146360 for (i = 0; i < IPW2200_TX_RING_SIZE; i++) 1339bb5e3b2fSeh146360 sc->sc_txbufs[i] = (uint8_t *)sc->sc_dma_txbufs[i].dr_base; 1340bb5e3b2fSeh146360 /* 1341bb5e3b2fSeh146360 * rx buffer array 1342bb5e3b2fSeh146360 */ 1343bb5e3b2fSeh146360 sc->sc_rx_cur = 0; 1344bb5e3b2fSeh146360 sc->sc_rx_free = IPW2200_RX_RING_SIZE; 1345bb5e3b2fSeh146360 for (i = 0; i < IPW2200_RX_RING_SIZE; i++) 1346bb5e3b2fSeh146360 sc->sc_rxbufs[i] = (uint8_t *)sc->sc_dma_rxbufs[i].dr_base; 1347bb5e3b2fSeh146360 1348bb5e3b2fSeh146360 /* 1349bb5e3b2fSeh146360 * command desc ring 1350bb5e3b2fSeh146360 */ 1351bb5e3b2fSeh146360 sc->sc_cmd_cur = 0; 1352bb5e3b2fSeh146360 sc->sc_cmd_free = IPW2200_CMD_RING_SIZE; 1353bb5e3b2fSeh146360 sc->sc_cmdsc = (struct ipw2200_cmd_desc *)sc->sc_dma_cmdsc.dr_base; 1354bb5e3b2fSeh146360 } 1355bb5e3b2fSeh146360 1356bb5e3b2fSeh146360 /* 1357bb5e3b2fSeh146360 * tx, rx rings and command initialization 1358bb5e3b2fSeh146360 */ 1359bb5e3b2fSeh146360 static int 1360bb5e3b2fSeh146360 ipw2200_ring_init(struct ipw2200_softc *sc) 1361bb5e3b2fSeh146360 { 1362bb5e3b2fSeh146360 int err; 1363bb5e3b2fSeh146360 1364bb5e3b2fSeh146360 err = ipw2200_ring_alloc(sc); 1365bb5e3b2fSeh146360 if (err != DDI_SUCCESS) 1366bb5e3b2fSeh146360 return (err); 1367bb5e3b2fSeh146360 1368bb5e3b2fSeh146360 ipw2200_ring_reset(sc); 1369bb5e3b2fSeh146360 1370bb5e3b2fSeh146360 return (DDI_SUCCESS); 1371bb5e3b2fSeh146360 } 1372bb5e3b2fSeh146360 1373bb5e3b2fSeh146360 static void 1374bb5e3b2fSeh146360 ipw2200_ring_hwsetup(struct ipw2200_softc *sc) 1375bb5e3b2fSeh146360 { 1376bb5e3b2fSeh146360 int i; 1377bb5e3b2fSeh146360 1378bb5e3b2fSeh146360 /* 1379bb5e3b2fSeh146360 * command desc ring 1380bb5e3b2fSeh146360 */ 1381bb5e3b2fSeh146360 ipw2200_csr_put32(sc, IPW2200_CSR_CMD_BASE, sc->sc_dma_cmdsc.dr_pbase); 1382bb5e3b2fSeh146360 ipw2200_csr_put32(sc, IPW2200_CSR_CMD_SIZE, IPW2200_CMD_RING_SIZE); 1383bb5e3b2fSeh146360 ipw2200_csr_put32(sc, IPW2200_CSR_CMD_WRITE_INDEX, sc->sc_cmd_cur); 1384bb5e3b2fSeh146360 1385bb5e3b2fSeh146360 /* 1386bb5e3b2fSeh146360 * tx desc ring. only tx1 is used, tx2, tx3, and tx4 are unused 1387bb5e3b2fSeh146360 */ 1388bb5e3b2fSeh146360 ipw2200_csr_put32(sc, IPW2200_CSR_TX1_BASE, sc->sc_dma_txdsc.dr_pbase); 1389bb5e3b2fSeh146360 ipw2200_csr_put32(sc, IPW2200_CSR_TX1_SIZE, IPW2200_TX_RING_SIZE); 1390bb5e3b2fSeh146360 ipw2200_csr_put32(sc, IPW2200_CSR_TX1_WRITE_INDEX, sc->sc_tx_cur); 1391bb5e3b2fSeh146360 1392bb5e3b2fSeh146360 /* 1393bb5e3b2fSeh146360 * tx2, tx3, tx4 is not used 1394bb5e3b2fSeh146360 */ 1395bb5e3b2fSeh146360 ipw2200_csr_put32(sc, IPW2200_CSR_TX2_BASE, sc->sc_dma_txdsc.dr_pbase); 1396bb5e3b2fSeh146360 ipw2200_csr_put32(sc, IPW2200_CSR_TX2_SIZE, IPW2200_TX_RING_SIZE); 1397bb5e3b2fSeh146360 ipw2200_csr_put32(sc, IPW2200_CSR_TX2_READ_INDEX, 0); 1398bb5e3b2fSeh146360 ipw2200_csr_put32(sc, IPW2200_CSR_TX2_WRITE_INDEX, 0); 1399bb5e3b2fSeh146360 ipw2200_csr_put32(sc, IPW2200_CSR_TX3_BASE, sc->sc_dma_txdsc.dr_pbase); 1400bb5e3b2fSeh146360 ipw2200_csr_put32(sc, IPW2200_CSR_TX3_SIZE, IPW2200_TX_RING_SIZE); 1401bb5e3b2fSeh146360 ipw2200_csr_put32(sc, IPW2200_CSR_TX3_READ_INDEX, 0); 1402bb5e3b2fSeh146360 ipw2200_csr_put32(sc, IPW2200_CSR_TX3_WRITE_INDEX, 0); 1403bb5e3b2fSeh146360 ipw2200_csr_put32(sc, IPW2200_CSR_TX4_BASE, sc->sc_dma_txdsc.dr_pbase); 1404bb5e3b2fSeh146360 ipw2200_csr_put32(sc, IPW2200_CSR_TX4_SIZE, IPW2200_TX_RING_SIZE); 1405bb5e3b2fSeh146360 ipw2200_csr_put32(sc, IPW2200_CSR_TX4_READ_INDEX, 0); 1406bb5e3b2fSeh146360 ipw2200_csr_put32(sc, IPW2200_CSR_TX4_WRITE_INDEX, 0); 1407bb5e3b2fSeh146360 1408bb5e3b2fSeh146360 /* 1409bb5e3b2fSeh146360 * rx buffer ring 1410bb5e3b2fSeh146360 */ 1411bb5e3b2fSeh146360 for (i = 0; i < IPW2200_RX_RING_SIZE; i++) 1412bb5e3b2fSeh146360 ipw2200_csr_put32(sc, IPW2200_CSR_RX_BASE + i * 4, 1413bb5e3b2fSeh146360 sc->sc_dma_rxbufs[i].dr_pbase); 1414bb5e3b2fSeh146360 /* 1415bb5e3b2fSeh146360 * all rx buffer are empty, rx-rd-index == 0 && rx-wr-index == N-1 1416bb5e3b2fSeh146360 */ 1417bb5e3b2fSeh146360 ipw2200_csr_put32(sc, IPW2200_CSR_RX_WRITE_INDEX, 1418bb5e3b2fSeh146360 RING_BACKWARD(sc->sc_rx_cur, 1, IPW2200_RX_RING_SIZE)); 1419bb5e3b2fSeh146360 } 1420bb5e3b2fSeh146360 1421bb5e3b2fSeh146360 int 1422bb5e3b2fSeh146360 ipw2200_start_scan(struct ipw2200_softc *sc) 1423bb5e3b2fSeh146360 { 1424bb5e3b2fSeh146360 struct ieee80211com *ic = &sc->sc_ic; 1425bb5e3b2fSeh146360 struct ipw2200_scan scan; 1426bb5e3b2fSeh146360 uint8_t *ch; 1427bb5e3b2fSeh146360 int cnt, i; 1428bb5e3b2fSeh146360 1429bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_SCAN, (sc->sc_dip, CE_CONT, 1430bb5e3b2fSeh146360 "ipw2200_start_scan(): start scanning \n")); 1431bb5e3b2fSeh146360 1432bb5e3b2fSeh146360 /* 1433bb5e3b2fSeh146360 * start scanning 1434bb5e3b2fSeh146360 */ 1435bb5e3b2fSeh146360 sc->sc_flags |= IPW2200_FLAG_SCANNING; 1436bb5e3b2fSeh146360 1437bb5e3b2fSeh146360 (void) memset(&scan, 0, sizeof (scan)); 1438bb5e3b2fSeh146360 scan.type = (ic->ic_des_esslen != 0) ? IPW2200_SCAN_TYPE_BDIRECTED : 1439bb5e3b2fSeh146360 IPW2200_SCAN_TYPE_BROADCAST; 1440bb5e3b2fSeh146360 scan.dwelltime = LE_16(40); /* The interval is set up to 40 */ 1441bb5e3b2fSeh146360 1442bb5e3b2fSeh146360 /* 1443bb5e3b2fSeh146360 * Compact supported channel number(5G) into a single buffer 1444bb5e3b2fSeh146360 */ 1445bb5e3b2fSeh146360 ch = scan.channels; 1446bb5e3b2fSeh146360 cnt = 0; 1447bb5e3b2fSeh146360 for (i = 0; i <= IEEE80211_CHAN_MAX; i++) { 1448bb5e3b2fSeh146360 if (IEEE80211_IS_CHAN_5GHZ(&ic->ic_sup_channels[i]) && 1449bb5e3b2fSeh146360 isset(ic->ic_chan_active, i)) { 1450bb5e3b2fSeh146360 *++ch = (uint8_t)i; 1451bb5e3b2fSeh146360 cnt++; 1452bb5e3b2fSeh146360 } 1453bb5e3b2fSeh146360 } 1454bb5e3b2fSeh146360 *(ch - cnt) = IPW2200_CHAN_5GHZ | (uint8_t)cnt; 1455*3b608f65Sql147931 ch = (cnt > 0) ? (ch + 1) : (scan.channels); 1456bb5e3b2fSeh146360 1457bb5e3b2fSeh146360 /* 1458bb5e3b2fSeh146360 * Compact supported channel number(2G) into a single buffer 1459bb5e3b2fSeh146360 */ 1460bb5e3b2fSeh146360 cnt = 0; 1461bb5e3b2fSeh146360 for (i = 0; i <= IEEE80211_CHAN_MAX; i++) { 1462bb5e3b2fSeh146360 if (IEEE80211_IS_CHAN_2GHZ(&ic->ic_sup_channels[i]) && 1463bb5e3b2fSeh146360 isset(ic->ic_chan_active, i)) { 1464bb5e3b2fSeh146360 *++ch = (uint8_t)i; 1465bb5e3b2fSeh146360 cnt++; 1466bb5e3b2fSeh146360 } 1467bb5e3b2fSeh146360 } 1468bb5e3b2fSeh146360 *(ch - cnt) = IPW2200_CHAN_2GHZ | cnt; 1469bb5e3b2fSeh146360 1470bb5e3b2fSeh146360 return (ipw2200_cmd(sc, IPW2200_CMD_SCAN, &scan, sizeof (scan), 1)); 1471bb5e3b2fSeh146360 } 1472bb5e3b2fSeh146360 1473bb5e3b2fSeh146360 int 1474bb5e3b2fSeh146360 ipw2200_auth_and_assoc(struct ipw2200_softc *sc) 1475bb5e3b2fSeh146360 { 1476bb5e3b2fSeh146360 struct ieee80211com *ic = &sc->sc_ic; 1477bb5e3b2fSeh146360 struct ieee80211_node *in = ic->ic_bss; 1478bb5e3b2fSeh146360 struct ipw2200_configuration cfg; 1479bb5e3b2fSeh146360 struct ipw2200_rateset rs; 1480bb5e3b2fSeh146360 struct ipw2200_associate assoc; 1481bb5e3b2fSeh146360 uint32_t data; 1482bb5e3b2fSeh146360 int err; 1483bb5e3b2fSeh146360 1484bb5e3b2fSeh146360 /* 1485bb5e3b2fSeh146360 * set the confiuration 1486bb5e3b2fSeh146360 */ 1487bb5e3b2fSeh146360 if (IEEE80211_IS_CHAN_2GHZ(in->in_chan)) { 1488bb5e3b2fSeh146360 /* enable b/g auto-detection */ 1489bb5e3b2fSeh146360 (void) memset(&cfg, 0, sizeof (cfg)); 1490bb5e3b2fSeh146360 cfg.bluetooth_coexistence = 1; 1491bb5e3b2fSeh146360 cfg.multicast_enabled = 1; 1492bb5e3b2fSeh146360 cfg.use_protection = 1; 1493bb5e3b2fSeh146360 cfg.answer_pbreq = 1; 1494bb5e3b2fSeh146360 cfg.noise_reported = 1; 1495bb5e3b2fSeh146360 err = ipw2200_cmd(sc, IPW2200_CMD_SET_CONFIG, 1496bb5e3b2fSeh146360 &cfg, sizeof (cfg), 1); 1497bb5e3b2fSeh146360 if (err != DDI_SUCCESS) 1498bb5e3b2fSeh146360 return (err); 1499bb5e3b2fSeh146360 } 1500bb5e3b2fSeh146360 1501bb5e3b2fSeh146360 /* 1502bb5e3b2fSeh146360 * set the essid, may be null/hidden AP 1503bb5e3b2fSeh146360 */ 1504bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT, 1505bb5e3b2fSeh146360 "ipw2200_auth_and_assoc(): " 1506bb5e3b2fSeh146360 "setting ESSID to(%u),%c%c%c%c%c%c%c%c\n", 1507bb5e3b2fSeh146360 in->in_esslen, 1508bb5e3b2fSeh146360 in->in_essid[0], in->in_essid[1], 1509bb5e3b2fSeh146360 in->in_essid[2], in->in_essid[3], 1510bb5e3b2fSeh146360 in->in_essid[4], in->in_essid[5], 1511bb5e3b2fSeh146360 in->in_essid[6], in->in_essid[7])); 1512bb5e3b2fSeh146360 err = ipw2200_cmd(sc, IPW2200_CMD_SET_ESSID, in->in_essid, 1513bb5e3b2fSeh146360 in->in_esslen, 1); 1514bb5e3b2fSeh146360 if (err != DDI_SUCCESS) 1515bb5e3b2fSeh146360 return (err); 1516bb5e3b2fSeh146360 1517bb5e3b2fSeh146360 /* 1518bb5e3b2fSeh146360 * set the rate: the rate set has already been ''negocitated'' 1519bb5e3b2fSeh146360 */ 1520bb5e3b2fSeh146360 rs.mode = IEEE80211_IS_CHAN_5GHZ(in->in_chan) ? 1521bb5e3b2fSeh146360 IPW2200_MODE_11A : IPW2200_MODE_11G; 1522bb5e3b2fSeh146360 rs.type = IPW2200_RATESET_TYPE_NEGOCIATED; 1523bb5e3b2fSeh146360 rs.nrates = in->in_rates.ir_nrates; 1524bb5e3b2fSeh146360 (void) memcpy(rs.rates, in->in_rates.ir_rates, in->in_rates.ir_nrates); 1525bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT, 1526bb5e3b2fSeh146360 "ipw2200_auth_and_assoc(): " 1527bb5e3b2fSeh146360 "setting negotiated rates to(nrates = %u)\n", rs.nrates)); 1528bb5e3b2fSeh146360 1529bb5e3b2fSeh146360 err = ipw2200_cmd(sc, IPW2200_CMD_SET_RATES, &rs, sizeof (rs), 1); 1530bb5e3b2fSeh146360 if (err != DDI_SUCCESS) 1531bb5e3b2fSeh146360 return (err); 1532bb5e3b2fSeh146360 1533bb5e3b2fSeh146360 /* 1534bb5e3b2fSeh146360 * set the sensitive 1535bb5e3b2fSeh146360 */ 1536bb5e3b2fSeh146360 data = LE_32(in->in_rssi); 1537bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT, 1538bb5e3b2fSeh146360 "ipw2200_auth_and_assoc(): " 1539bb5e3b2fSeh146360 "setting sensitivity to rssi:(%u)\n", (uint8_t)in->in_rssi)); 1540bb5e3b2fSeh146360 err = ipw2200_cmd(sc, IPW2200_CMD_SET_SENSITIVITY, 1541bb5e3b2fSeh146360 &data, sizeof (data), 1); 1542bb5e3b2fSeh146360 if (err != DDI_SUCCESS) 1543bb5e3b2fSeh146360 return (err); 1544bb5e3b2fSeh146360 1545bb5e3b2fSeh146360 /* 1546bb5e3b2fSeh146360 * invoke command associate 1547bb5e3b2fSeh146360 */ 1548bb5e3b2fSeh146360 (void) memset(&assoc, 0, sizeof (assoc)); 1549bb5e3b2fSeh146360 assoc.mode = IEEE80211_IS_CHAN_5GHZ(in->in_chan) ? 1550bb5e3b2fSeh146360 IPW2200_MODE_11A : IPW2200_MODE_11G; 1551bb5e3b2fSeh146360 assoc.chan = ieee80211_chan2ieee(ic, in->in_chan); 1552bb5e3b2fSeh146360 /* 1553bb5e3b2fSeh146360 * use the value set to ic_bss to retraive current sharedmode 1554bb5e3b2fSeh146360 */ 1555bb5e3b2fSeh146360 if (ic->ic_bss->in_authmode == WL_SHAREDKEY) { 1556bb5e3b2fSeh146360 assoc.auth = (ic->ic_def_txkey << 4) | IPW2200_AUTH_SHARED; 1557bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_IOCTL, (sc->sc_dip, CE_CONT, 1558bb5e3b2fSeh146360 "ipw2200_auth_and_assoc(): " 1559bb5e3b2fSeh146360 "associate to shared key mode, set thru. ioctl")); 1560bb5e3b2fSeh146360 } 1561bb5e3b2fSeh146360 (void) memcpy(assoc.tstamp, in->in_tstamp.data, 8); 1562bb5e3b2fSeh146360 assoc.capinfo = LE_16(in->in_capinfo); 1563bb5e3b2fSeh146360 assoc.lintval = LE_16(ic->ic_lintval); 1564bb5e3b2fSeh146360 assoc.intval = LE_16(in->in_intval); 1565bb5e3b2fSeh146360 IEEE80211_ADDR_COPY(assoc.bssid, in->in_bssid); 1566bb5e3b2fSeh146360 if (ic->ic_opmode == IEEE80211_M_IBSS) 1567bb5e3b2fSeh146360 IEEE80211_ADDR_COPY(assoc.dst, ipw2200_broadcast_addr); 1568bb5e3b2fSeh146360 else 1569bb5e3b2fSeh146360 IEEE80211_ADDR_COPY(assoc.dst, in->in_bssid); 1570bb5e3b2fSeh146360 1571bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT, 1572bb5e3b2fSeh146360 "ipw2200_auth_and_assoc(): " 1573bb5e3b2fSeh146360 "associate to bssid(%2x:%2x:%2x:%2x:%2x:%2x:), " 1574bb5e3b2fSeh146360 "chan(%u), auth(%u)\n", 1575bb5e3b2fSeh146360 assoc.bssid[0], assoc.bssid[1], assoc.bssid[2], 1576bb5e3b2fSeh146360 assoc.bssid[3], assoc.bssid[4], assoc.bssid[5], 1577bb5e3b2fSeh146360 assoc.chan, assoc.auth)); 1578bb5e3b2fSeh146360 return (ipw2200_cmd(sc, IPW2200_CMD_ASSOCIATE, 1579bb5e3b2fSeh146360 &assoc, sizeof (assoc), 1)); 1580bb5e3b2fSeh146360 } 1581bb5e3b2fSeh146360 1582bb5e3b2fSeh146360 /* ARGSUSED */ 1583bb5e3b2fSeh146360 static int 1584bb5e3b2fSeh146360 ipw2200_newstate(struct ieee80211com *ic, enum ieee80211_state state, int arg) 1585bb5e3b2fSeh146360 { 1586bb5e3b2fSeh146360 struct ipw2200_softc *sc = (struct ipw2200_softc *)ic; 1587bb5e3b2fSeh146360 wifi_data_t wd = { 0 }; 1588bb5e3b2fSeh146360 1589bb5e3b2fSeh146360 switch (state) { 1590bb5e3b2fSeh146360 case IEEE80211_S_SCAN: 1591bb5e3b2fSeh146360 if (!(sc->sc_flags & IPW2200_FLAG_SCANNING)) { 1592bb5e3b2fSeh146360 ic->ic_flags |= IEEE80211_F_SCAN | IEEE80211_F_ASCAN; 1593bb5e3b2fSeh146360 (void) ipw2200_start_scan(sc); 1594bb5e3b2fSeh146360 } 1595bb5e3b2fSeh146360 break; 1596bb5e3b2fSeh146360 case IEEE80211_S_AUTH: 1597bb5e3b2fSeh146360 (void) ipw2200_auth_and_assoc(sc); 1598bb5e3b2fSeh146360 break; 1599bb5e3b2fSeh146360 case IEEE80211_S_RUN: 1600bb5e3b2fSeh146360 /* 1601bb5e3b2fSeh146360 * We can send data now; update the fastpath with our 1602bb5e3b2fSeh146360 * current associated BSSID and other relevant settings. 1603bb5e3b2fSeh146360 * 1604bb5e3b2fSeh146360 * Hardware to ahndle the wep encryption. Set the encryption 1605bb5e3b2fSeh146360 * as false. 1606bb5e3b2fSeh146360 * wd.wd_wep = ic->ic_flags & IEEE80211_F_WEPON; 1607bb5e3b2fSeh146360 */ 1608bb5e3b2fSeh146360 wd.wd_secalloc = WIFI_SEC_NONE; 1609bb5e3b2fSeh146360 wd.wd_opmode = ic->ic_opmode; 1610bb5e3b2fSeh146360 IEEE80211_ADDR_COPY(wd.wd_bssid, ic->ic_bss->in_bssid); 1611bb5e3b2fSeh146360 (void) mac_pdata_update(ic->ic_mach, &wd, sizeof (wd)); 1612bb5e3b2fSeh146360 break; 1613bb5e3b2fSeh146360 case IEEE80211_S_ASSOC: 1614bb5e3b2fSeh146360 case IEEE80211_S_INIT: 1615bb5e3b2fSeh146360 break; 1616bb5e3b2fSeh146360 } 1617bb5e3b2fSeh146360 1618bb5e3b2fSeh146360 /* 1619bb5e3b2fSeh146360 * notify to update the link 1620bb5e3b2fSeh146360 */ 1621bb5e3b2fSeh146360 if ((ic->ic_state != IEEE80211_S_RUN) && (state == IEEE80211_S_RUN)) { 1622bb5e3b2fSeh146360 mac_link_update(ic->ic_mach, LINK_STATE_UP); 1623bb5e3b2fSeh146360 } else if ((ic->ic_state == IEEE80211_S_RUN) && 1624bb5e3b2fSeh146360 (state != IEEE80211_S_RUN)) { 1625bb5e3b2fSeh146360 mac_link_update(ic->ic_mach, LINK_STATE_DOWN); 1626bb5e3b2fSeh146360 } 1627bb5e3b2fSeh146360 1628bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT, 1629bb5e3b2fSeh146360 "ipw2200_newstat(): %s -> %s\n", 1630bb5e3b2fSeh146360 ieee80211_state_name[ic->ic_state], 1631bb5e3b2fSeh146360 ieee80211_state_name[state])); 1632bb5e3b2fSeh146360 1633bb5e3b2fSeh146360 ic->ic_state = state; 1634bb5e3b2fSeh146360 return (DDI_SUCCESS); 1635bb5e3b2fSeh146360 } 1636bb5e3b2fSeh146360 1637bb5e3b2fSeh146360 /* 1638bb5e3b2fSeh146360 * GLD operations 1639bb5e3b2fSeh146360 */ 1640bb5e3b2fSeh146360 /* ARGSUSED */ 1641bb5e3b2fSeh146360 static int 1642bb5e3b2fSeh146360 ipw2200_m_stat(void *arg, uint_t stat, uint64_t *val) 1643bb5e3b2fSeh146360 { 1644bb5e3b2fSeh146360 ieee80211com_t *ic = (ieee80211com_t *)arg; 1645bb5e3b2fSeh146360 1646bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_GLD, (((struct ipw2200_softc *)arg)->sc_dip, 1647bb5e3b2fSeh146360 CE_CONT, 1648bb5e3b2fSeh146360 "ipw2200_m_stat(): enter\n")); 1649bb5e3b2fSeh146360 1650bb5e3b2fSeh146360 /* 1651bb5e3b2fSeh146360 * Some of below statistic data are from hardware, some from net80211 1652bb5e3b2fSeh146360 */ 1653bb5e3b2fSeh146360 switch (stat) { 1654bb5e3b2fSeh146360 case MAC_STAT_RBYTES: 1655bb5e3b2fSeh146360 *val = ic->ic_stats.is_rx_bytes; 1656bb5e3b2fSeh146360 break; 1657bb5e3b2fSeh146360 case MAC_STAT_IPACKETS: 1658bb5e3b2fSeh146360 *val = ic->ic_stats.is_rx_frags; 1659bb5e3b2fSeh146360 break; 1660bb5e3b2fSeh146360 case MAC_STAT_OBYTES: 1661bb5e3b2fSeh146360 *val = ic->ic_stats.is_tx_bytes; 1662bb5e3b2fSeh146360 break; 1663bb5e3b2fSeh146360 case MAC_STAT_OPACKETS: 1664bb5e3b2fSeh146360 *val = ic->ic_stats.is_tx_frags; 1665bb5e3b2fSeh146360 break; 1666bb5e3b2fSeh146360 /* 1667bb5e3b2fSeh146360 * Get below from hardware statistic, retraive net80211 value once 1s 1668bb5e3b2fSeh146360 */ 1669bb5e3b2fSeh146360 case WIFI_STAT_TX_FRAGS: 1670bb5e3b2fSeh146360 case WIFI_STAT_MCAST_TX: 1671bb5e3b2fSeh146360 case WIFI_STAT_TX_FAILED: 1672bb5e3b2fSeh146360 case WIFI_STAT_TX_RETRANS: 1673bb5e3b2fSeh146360 /* 1674bb5e3b2fSeh146360 * Get blow information from net80211 1675bb5e3b2fSeh146360 */ 1676bb5e3b2fSeh146360 case WIFI_STAT_RTS_SUCCESS: 1677bb5e3b2fSeh146360 case WIFI_STAT_RTS_FAILURE: 1678bb5e3b2fSeh146360 case WIFI_STAT_ACK_FAILURE: 1679bb5e3b2fSeh146360 case WIFI_STAT_RX_FRAGS: 1680bb5e3b2fSeh146360 case WIFI_STAT_MCAST_RX: 1681bb5e3b2fSeh146360 case WIFI_STAT_RX_DUPS: 1682bb5e3b2fSeh146360 case WIFI_STAT_FCS_ERRORS: 1683bb5e3b2fSeh146360 case WIFI_STAT_WEP_ERRORS: 1684bb5e3b2fSeh146360 return (ieee80211_stat(ic, stat, val)); 1685bb5e3b2fSeh146360 /* 1686bb5e3b2fSeh146360 * Need be supported later 1687bb5e3b2fSeh146360 */ 1688bb5e3b2fSeh146360 case MAC_STAT_IFSPEED: 1689bb5e3b2fSeh146360 case MAC_STAT_NOXMTBUF: 1690bb5e3b2fSeh146360 case MAC_STAT_IERRORS: 1691bb5e3b2fSeh146360 case MAC_STAT_OERRORS: 1692bb5e3b2fSeh146360 default: 1693bb5e3b2fSeh146360 return (ENOTSUP); 1694bb5e3b2fSeh146360 } 1695bb5e3b2fSeh146360 return (0); 1696bb5e3b2fSeh146360 } 1697bb5e3b2fSeh146360 1698bb5e3b2fSeh146360 /* ARGSUSED */ 1699bb5e3b2fSeh146360 static int 1700bb5e3b2fSeh146360 ipw2200_m_multicst(void *arg, boolean_t add, const uint8_t *mca) 1701bb5e3b2fSeh146360 { 1702bb5e3b2fSeh146360 /* not supported */ 1703bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_GLD, (((struct ipw2200_softc *)arg)->sc_dip, 1704bb5e3b2fSeh146360 CE_CONT, 1705bb5e3b2fSeh146360 "ipw2200_m_multicst(): enter\n")); 1706bb5e3b2fSeh146360 1707bb5e3b2fSeh146360 return (DDI_SUCCESS); 1708bb5e3b2fSeh146360 } 1709bb5e3b2fSeh146360 1710bb5e3b2fSeh146360 /* 1711bb5e3b2fSeh146360 * Multithread handler for linkstatus, fatal error recovery, get statistic 1712bb5e3b2fSeh146360 */ 1713bb5e3b2fSeh146360 static void 1714bb5e3b2fSeh146360 ipw2200_thread(struct ipw2200_softc *sc) 1715bb5e3b2fSeh146360 { 1716bb5e3b2fSeh146360 struct ieee80211com *ic = &sc->sc_ic; 1717bb5e3b2fSeh146360 int32_t nlstate; 1718bb5e3b2fSeh146360 int stat_cnt = 0; 1719bb5e3b2fSeh146360 1720bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_SOFTINT, (sc->sc_dip, CE_CONT, 1721bb5e3b2fSeh146360 "ipw2200_thread(): enter, linkstate %d\n", sc->sc_linkstate)); 1722bb5e3b2fSeh146360 1723bb5e3b2fSeh146360 mutex_enter(&sc->sc_mflock); 1724bb5e3b2fSeh146360 1725bb5e3b2fSeh146360 while (sc->sc_mfthread_switch) { 1726bb5e3b2fSeh146360 /* 1727bb5e3b2fSeh146360 * notify the link state 1728bb5e3b2fSeh146360 */ 1729bb5e3b2fSeh146360 if (ic->ic_mach && (sc->sc_flags & IPW2200_FLAG_LINK_CHANGE)) { 1730bb5e3b2fSeh146360 1731bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_SOFTINT, (sc->sc_dip, CE_CONT, 1732bb5e3b2fSeh146360 "ipw2200_thread(): link status --> %d\n", 1733bb5e3b2fSeh146360 sc->sc_linkstate)); 1734bb5e3b2fSeh146360 1735bb5e3b2fSeh146360 sc->sc_flags &= ~IPW2200_FLAG_LINK_CHANGE; 1736bb5e3b2fSeh146360 nlstate = sc->sc_linkstate; 1737bb5e3b2fSeh146360 1738bb5e3b2fSeh146360 mutex_exit(&sc->sc_mflock); 1739bb5e3b2fSeh146360 mac_link_update(ic->ic_mach, nlstate); 1740bb5e3b2fSeh146360 mutex_enter(&sc->sc_mflock); 1741bb5e3b2fSeh146360 } 1742bb5e3b2fSeh146360 1743bb5e3b2fSeh146360 /* 1744bb5e3b2fSeh146360 * recovery fatal error 1745bb5e3b2fSeh146360 */ 1746bb5e3b2fSeh146360 if (ic->ic_mach && 1747bb5e3b2fSeh146360 (sc->sc_flags & IPW2200_FLAG_HW_ERR_RECOVER)) { 1748bb5e3b2fSeh146360 1749bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_FATAL, (sc->sc_dip, CE_CONT, 1750bb5e3b2fSeh146360 "ipw2200_thread(): " 1751bb5e3b2fSeh146360 "try to recover fatal hw error\n")); 1752bb5e3b2fSeh146360 1753bb5e3b2fSeh146360 sc->sc_flags &= ~IPW2200_FLAG_HW_ERR_RECOVER; 1754bb5e3b2fSeh146360 1755bb5e3b2fSeh146360 mutex_exit(&sc->sc_mflock); 1756bb5e3b2fSeh146360 (void) ipw2200_init(sc); /* Force state machine */ 1757bb5e3b2fSeh146360 /* 1758bb5e3b2fSeh146360 * workround. Delay for a while after init especially 1759bb5e3b2fSeh146360 * when something wrong happened already. 1760bb5e3b2fSeh146360 */ 1761bb5e3b2fSeh146360 delay(drv_usectohz(delay_fatal_recover)); 1762bb5e3b2fSeh146360 mutex_enter(&sc->sc_mflock); 1763bb5e3b2fSeh146360 } 1764bb5e3b2fSeh146360 1765bb5e3b2fSeh146360 /* 1766bb5e3b2fSeh146360 * get statistic, the value will be retrieved by m_stat 1767bb5e3b2fSeh146360 */ 1768bb5e3b2fSeh146360 if (stat_cnt == 10) { 1769bb5e3b2fSeh146360 1770bb5e3b2fSeh146360 stat_cnt = 0; /* re-start */ 1771bb5e3b2fSeh146360 mutex_exit(&sc->sc_mflock); 1772bb5e3b2fSeh146360 ipw2200_get_statistics(sc); 1773bb5e3b2fSeh146360 mutex_enter(&sc->sc_mflock); 1774bb5e3b2fSeh146360 1775bb5e3b2fSeh146360 } else 1776bb5e3b2fSeh146360 stat_cnt++; /* until 1s */ 1777bb5e3b2fSeh146360 1778bb5e3b2fSeh146360 mutex_exit(&sc->sc_mflock); 1779bb5e3b2fSeh146360 delay(drv_usectohz(delay_aux_thread)); 1780bb5e3b2fSeh146360 mutex_enter(&sc->sc_mflock); 1781bb5e3b2fSeh146360 1782bb5e3b2fSeh146360 } 1783bb5e3b2fSeh146360 sc->sc_mf_thread = NULL; 1784bb5e3b2fSeh146360 cv_signal(&sc->sc_mfthread_cv); 1785bb5e3b2fSeh146360 mutex_exit(&sc->sc_mflock); 1786bb5e3b2fSeh146360 } 1787bb5e3b2fSeh146360 1788bb5e3b2fSeh146360 static int 1789bb5e3b2fSeh146360 ipw2200_m_start(void *arg) 1790bb5e3b2fSeh146360 { 1791bb5e3b2fSeh146360 struct ipw2200_softc *sc = (struct ipw2200_softc *)arg; 1792bb5e3b2fSeh146360 1793bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_GLD, (sc->sc_dip, CE_CONT, 1794bb5e3b2fSeh146360 "ipw2200_m_start(): enter\n")); 1795bb5e3b2fSeh146360 /* 1796bb5e3b2fSeh146360 * initialize ipw2200 hardware, everything ok will start scan 1797bb5e3b2fSeh146360 */ 1798bb5e3b2fSeh146360 (void) ipw2200_init(sc); 1799bb5e3b2fSeh146360 1800bb5e3b2fSeh146360 sc->sc_flags |= IPW2200_FLAG_RUNNING; 1801bb5e3b2fSeh146360 1802bb5e3b2fSeh146360 return (DDI_SUCCESS); 1803bb5e3b2fSeh146360 } 1804bb5e3b2fSeh146360 1805bb5e3b2fSeh146360 static void 1806bb5e3b2fSeh146360 ipw2200_m_stop(void *arg) 1807bb5e3b2fSeh146360 { 1808bb5e3b2fSeh146360 struct ipw2200_softc *sc = (struct ipw2200_softc *)arg; 1809bb5e3b2fSeh146360 1810bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_GLD, (sc->sc_dip, CE_CONT, 1811bb5e3b2fSeh146360 "ipw2200_m_stop(): enter\n")); 1812bb5e3b2fSeh146360 1813bb5e3b2fSeh146360 ipw2200_stop(sc); 1814bb5e3b2fSeh146360 1815bb5e3b2fSeh146360 sc->sc_flags &= ~IPW2200_FLAG_RUNNING; 1816bb5e3b2fSeh146360 } 1817bb5e3b2fSeh146360 1818bb5e3b2fSeh146360 static int 1819bb5e3b2fSeh146360 ipw2200_m_unicst(void *arg, const uint8_t *macaddr) 1820bb5e3b2fSeh146360 { 1821bb5e3b2fSeh146360 struct ipw2200_softc *sc = (struct ipw2200_softc *)arg; 1822bb5e3b2fSeh146360 struct ieee80211com *ic = &sc->sc_ic; 1823bb5e3b2fSeh146360 int err; 1824bb5e3b2fSeh146360 1825bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_GLD, (sc->sc_dip, CE_CONT, 1826bb5e3b2fSeh146360 "ipw2200_m_unicst(): enter\n")); 1827bb5e3b2fSeh146360 1828bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_GLD, (sc->sc_dip, CE_CONT, 1829bb5e3b2fSeh146360 "ipw2200_m_unicst(): GLD setting MAC address to " 1830bb5e3b2fSeh146360 "%02x:%02x:%02x:%02x:%02x:%02x\n", 1831bb5e3b2fSeh146360 macaddr[0], macaddr[1], macaddr[2], 1832bb5e3b2fSeh146360 macaddr[3], macaddr[4], macaddr[5])); 1833bb5e3b2fSeh146360 1834bb5e3b2fSeh146360 if (!IEEE80211_ADDR_EQ(ic->ic_macaddr, macaddr)) { 1835bb5e3b2fSeh146360 1836bb5e3b2fSeh146360 IEEE80211_ADDR_COPY(ic->ic_macaddr, macaddr); 1837bb5e3b2fSeh146360 1838bb5e3b2fSeh146360 if (sc->sc_flags & IPW2200_FLAG_RUNNING) { 1839bb5e3b2fSeh146360 err = ipw2200_config(sc); 1840bb5e3b2fSeh146360 if (err != DDI_SUCCESS) { 1841bb5e3b2fSeh146360 IPW2200_WARN((sc->sc_dip, CE_WARN, 1842bb5e3b2fSeh146360 "ipw2200_m_unicst(): " 1843bb5e3b2fSeh146360 "device configuration failed\n")); 1844bb5e3b2fSeh146360 goto fail; 1845bb5e3b2fSeh146360 } 1846bb5e3b2fSeh146360 } 1847bb5e3b2fSeh146360 } 1848bb5e3b2fSeh146360 return (DDI_SUCCESS); 1849bb5e3b2fSeh146360 fail: 1850bb5e3b2fSeh146360 return (err); 1851bb5e3b2fSeh146360 } 1852bb5e3b2fSeh146360 1853bb5e3b2fSeh146360 static int 1854bb5e3b2fSeh146360 ipw2200_m_promisc(void *arg, boolean_t on) 1855bb5e3b2fSeh146360 { 1856bb5e3b2fSeh146360 /* not supported */ 1857bb5e3b2fSeh146360 struct ipw2200_softc *sc = (struct ipw2200_softc *)arg; 1858bb5e3b2fSeh146360 1859bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_GLD, (sc->sc_dip, CE_CONT, 1860bb5e3b2fSeh146360 "ipw2200_m_promisc(): enter. " 1861bb5e3b2fSeh146360 "GLD setting promiscuous mode - %d\n", on)); 1862bb5e3b2fSeh146360 1863bb5e3b2fSeh146360 return (DDI_SUCCESS); 1864bb5e3b2fSeh146360 } 1865bb5e3b2fSeh146360 1866bb5e3b2fSeh146360 static mblk_t * 1867bb5e3b2fSeh146360 ipw2200_m_tx(void *arg, mblk_t *mp) 1868bb5e3b2fSeh146360 { 1869bb5e3b2fSeh146360 struct ipw2200_softc *sc = (struct ipw2200_softc *)arg; 1870bb5e3b2fSeh146360 struct ieee80211com *ic = &sc->sc_ic; 1871bb5e3b2fSeh146360 mblk_t *next; 1872bb5e3b2fSeh146360 1873bb5e3b2fSeh146360 /* 1874bb5e3b2fSeh146360 * No data frames go out unless we're associated; this 1875bb5e3b2fSeh146360 * should not happen as the 802.11 layer does not enable 1876bb5e3b2fSeh146360 * the xmit queue until we enter the RUN state. 1877bb5e3b2fSeh146360 */ 1878bb5e3b2fSeh146360 if (ic->ic_state != IEEE80211_S_RUN) { 1879bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_GLD, (sc->sc_dip, CE_CONT, 1880bb5e3b2fSeh146360 "ipw2200_m_tx(): discard msg, ic_state = %u\n", 1881bb5e3b2fSeh146360 ic->ic_state)); 1882bb5e3b2fSeh146360 freemsgchain(mp); 1883bb5e3b2fSeh146360 return (NULL); 1884bb5e3b2fSeh146360 } 1885bb5e3b2fSeh146360 1886bb5e3b2fSeh146360 while (mp != NULL) { 1887bb5e3b2fSeh146360 next = mp->b_next; 1888bb5e3b2fSeh146360 mp->b_next = NULL; 1889bb5e3b2fSeh146360 if (ipw2200_send(ic, mp, IEEE80211_FC0_TYPE_DATA) == 1890bb5e3b2fSeh146360 DDI_FAILURE) { 1891bb5e3b2fSeh146360 mp->b_next = next; 1892bb5e3b2fSeh146360 break; 1893bb5e3b2fSeh146360 } 1894bb5e3b2fSeh146360 mp = next; 1895bb5e3b2fSeh146360 } 1896bb5e3b2fSeh146360 return (mp); 1897bb5e3b2fSeh146360 } 1898bb5e3b2fSeh146360 1899bb5e3b2fSeh146360 /* ARGSUSED */ 1900bb5e3b2fSeh146360 static int 1901bb5e3b2fSeh146360 ipw2200_send(ieee80211com_t *ic, mblk_t *mp, uint8_t type) 1902bb5e3b2fSeh146360 { 1903bb5e3b2fSeh146360 struct ipw2200_softc *sc = (struct ipw2200_softc *)ic; 1904bb5e3b2fSeh146360 struct ieee80211_node *in; 1905bb5e3b2fSeh146360 struct ieee80211_frame *wh; 1906bb5e3b2fSeh146360 mblk_t *m0; 1907bb5e3b2fSeh146360 size_t cnt, off; 1908bb5e3b2fSeh146360 struct ipw2200_tx_desc *txdsc; 1909bb5e3b2fSeh146360 struct dma_region *dr; 1910bb5e3b2fSeh146360 uint32_t idx; 1911bb5e3b2fSeh146360 int err; 1912bb5e3b2fSeh146360 /* tmp pointer, used to pack header and payload */ 1913bb5e3b2fSeh146360 uint8_t *p; 1914bb5e3b2fSeh146360 1915bb5e3b2fSeh146360 ASSERT(mp->b_next == NULL); 1916bb5e3b2fSeh146360 1917bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_GLD, (sc->sc_dip, CE_CONT, 1918bb5e3b2fSeh146360 "ipw2200_send(): enter\n")); 1919bb5e3b2fSeh146360 1920bb5e3b2fSeh146360 m0 = NULL; 1921bb5e3b2fSeh146360 err = DDI_SUCCESS; 1922bb5e3b2fSeh146360 1923bb5e3b2fSeh146360 if ((type & IEEE80211_FC0_TYPE_MASK) != IEEE80211_FC0_TYPE_DATA) { 1924bb5e3b2fSeh146360 /* 1925bb5e3b2fSeh146360 * skip all management frames since ipw2200 won't generate any 1926bb5e3b2fSeh146360 * management frames. Therefore, drop this package. 1927bb5e3b2fSeh146360 */ 1928bb5e3b2fSeh146360 freemsg(mp); 1929bb5e3b2fSeh146360 err = DDI_SUCCESS; 1930bb5e3b2fSeh146360 goto fail0; 1931bb5e3b2fSeh146360 } 1932bb5e3b2fSeh146360 1933bb5e3b2fSeh146360 mutex_enter(&sc->sc_tx_lock); 1934bb5e3b2fSeh146360 1935bb5e3b2fSeh146360 /* 1936bb5e3b2fSeh146360 * need 1 empty descriptor 1937bb5e3b2fSeh146360 */ 1938bb5e3b2fSeh146360 if (sc->sc_tx_free <= IPW2200_TX_RING_MIN) { 1939bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_RING, (sc->sc_dip, CE_WARN, 1940bb5e3b2fSeh146360 "ipw2200_send(): no enough descriptors(%d)\n", 1941bb5e3b2fSeh146360 sc->sc_tx_free)); 1942bb5e3b2fSeh146360 ic->ic_stats.is_tx_nobuf++; /* no enough buffer */ 1943bb5e3b2fSeh146360 sc->sc_flags |= IPW2200_FLAG_TX_SCHED; 1944bb5e3b2fSeh146360 err = DDI_FAILURE; 1945bb5e3b2fSeh146360 goto fail1; 1946bb5e3b2fSeh146360 } 1947bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_RING, (sc->sc_dip, CE_CONT, 1948bb5e3b2fSeh146360 "ipw2200_send(): tx-free=%d,tx-curr=%d\n", 1949bb5e3b2fSeh146360 sc->sc_tx_free, sc->sc_tx_cur)); 1950bb5e3b2fSeh146360 1951bb5e3b2fSeh146360 wh = (struct ieee80211_frame *)mp->b_rptr; 1952bb5e3b2fSeh146360 in = ieee80211_find_txnode(ic, wh->i_addr1); 1953bb5e3b2fSeh146360 if (in == NULL) { /* can not find the tx node, drop the package */ 1954bb5e3b2fSeh146360 ic->ic_stats.is_tx_failed++; 1955bb5e3b2fSeh146360 freemsg(mp); 1956bb5e3b2fSeh146360 err = DDI_SUCCESS; 1957bb5e3b2fSeh146360 goto fail1; 1958bb5e3b2fSeh146360 } 1959bb5e3b2fSeh146360 in->in_inact = 0; 1960bb5e3b2fSeh146360 (void) ieee80211_encap(ic, mp, in); 1961bb5e3b2fSeh146360 ieee80211_free_node(in); 1962bb5e3b2fSeh146360 1963bb5e3b2fSeh146360 /* 1964bb5e3b2fSeh146360 * get txdsc and wh 1965bb5e3b2fSeh146360 */ 1966bb5e3b2fSeh146360 idx = sc->sc_tx_cur; 1967bb5e3b2fSeh146360 txdsc = &sc->sc_txdsc[idx]; 1968bb5e3b2fSeh146360 (void) memset(txdsc, 0, sizeof (*txdsc)); 1969bb5e3b2fSeh146360 wh = (struct ieee80211_frame *)&txdsc->wh; 1970bb5e3b2fSeh146360 1971bb5e3b2fSeh146360 /* 1972bb5e3b2fSeh146360 * extract header from message 1973bb5e3b2fSeh146360 */ 1974bb5e3b2fSeh146360 p = (uint8_t *)&txdsc->wh; 1975bb5e3b2fSeh146360 off = 0; 1976bb5e3b2fSeh146360 m0 = mp; 1977bb5e3b2fSeh146360 while (off < sizeof (struct ieee80211_frame)) { 1978bb5e3b2fSeh146360 cnt = MBLKL(m0); 1979bb5e3b2fSeh146360 if (cnt > (sizeof (struct ieee80211_frame) - off)) 1980bb5e3b2fSeh146360 cnt = sizeof (struct ieee80211_frame) - off; 1981bb5e3b2fSeh146360 if (cnt) { 1982bb5e3b2fSeh146360 (void) memcpy(p + off, m0->b_rptr, cnt); 1983bb5e3b2fSeh146360 off += cnt; 1984bb5e3b2fSeh146360 m0->b_rptr += cnt; 1985bb5e3b2fSeh146360 } else 1986bb5e3b2fSeh146360 m0 = m0->b_cont; 1987bb5e3b2fSeh146360 } 1988bb5e3b2fSeh146360 1989bb5e3b2fSeh146360 /* 1990bb5e3b2fSeh146360 * extract payload from message 1991bb5e3b2fSeh146360 */ 1992bb5e3b2fSeh146360 dr = &sc->sc_dma_txbufs[idx]; 1993bb5e3b2fSeh146360 p = sc->sc_txbufs[idx]; 1994bb5e3b2fSeh146360 off = 0; 1995bb5e3b2fSeh146360 while (m0) { 1996bb5e3b2fSeh146360 cnt = MBLKL(m0); 1997bb5e3b2fSeh146360 if (cnt) 1998bb5e3b2fSeh146360 (void) memcpy(p + off, m0->b_rptr, cnt); 1999bb5e3b2fSeh146360 off += cnt; 2000bb5e3b2fSeh146360 m0 = m0->b_cont; 2001bb5e3b2fSeh146360 } 2002bb5e3b2fSeh146360 2003bb5e3b2fSeh146360 txdsc->hdr.type = IPW2200_HDR_TYPE_DATA; 2004bb5e3b2fSeh146360 txdsc->hdr.flags = IPW2200_HDR_FLAG_IRQ; 2005bb5e3b2fSeh146360 txdsc->cmd = IPW2200_DATA_CMD_TX; 2006bb5e3b2fSeh146360 txdsc->len = LE_16(off); 2007bb5e3b2fSeh146360 txdsc->flags = 0; 2008bb5e3b2fSeh146360 2009bb5e3b2fSeh146360 if (ic->ic_opmode == IEEE80211_M_IBSS) { 2010bb5e3b2fSeh146360 if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) 2011bb5e3b2fSeh146360 txdsc->flags |= IPW2200_DATA_FLAG_NEED_ACK; 2012bb5e3b2fSeh146360 } else if (!IEEE80211_IS_MULTICAST(wh->i_addr3)) 2013bb5e3b2fSeh146360 txdsc->flags |= IPW2200_DATA_FLAG_NEED_ACK; 2014bb5e3b2fSeh146360 2015bb5e3b2fSeh146360 if (ic->ic_flags & IEEE80211_F_PRIVACY) { 2016bb5e3b2fSeh146360 wh->i_fc[1] |= IEEE80211_FC1_WEP; 2017bb5e3b2fSeh146360 txdsc->wep_txkey = ic->ic_def_txkey; 2018bb5e3b2fSeh146360 } 2019bb5e3b2fSeh146360 else 2020bb5e3b2fSeh146360 txdsc->flags |= IPW2200_DATA_FLAG_NO_WEP; 2021bb5e3b2fSeh146360 2022bb5e3b2fSeh146360 if (ic->ic_flags & IEEE80211_F_SHPREAMBLE) 2023bb5e3b2fSeh146360 txdsc->flags |= IPW2200_DATA_FLAG_SHPREAMBLE; 2024bb5e3b2fSeh146360 2025bb5e3b2fSeh146360 txdsc->nseg = LE_32(1); 2026bb5e3b2fSeh146360 txdsc->seg_addr[0] = LE_32(dr->dr_pbase); 2027bb5e3b2fSeh146360 txdsc->seg_len[0] = LE_32(off); 2028bb5e3b2fSeh146360 2029bb5e3b2fSeh146360 /* 2030bb5e3b2fSeh146360 * DMA sync: buffer and desc 2031bb5e3b2fSeh146360 */ 2032bb5e3b2fSeh146360 (void) ddi_dma_sync(dr->dr_hnd, 0, 2033bb5e3b2fSeh146360 IPW2200_TXBUF_SIZE, DDI_DMA_SYNC_FORDEV); 2034bb5e3b2fSeh146360 (void) ddi_dma_sync(sc->sc_dma_txdsc.dr_hnd, 2035bb5e3b2fSeh146360 idx * sizeof (struct ipw2200_tx_desc), 2036bb5e3b2fSeh146360 sizeof (struct ipw2200_tx_desc), DDI_DMA_SYNC_FORDEV); 2037bb5e3b2fSeh146360 2038bb5e3b2fSeh146360 sc->sc_tx_cur = RING_FORWARD(sc->sc_tx_cur, 1, IPW2200_TX_RING_SIZE); 2039bb5e3b2fSeh146360 sc->sc_tx_free--; 2040bb5e3b2fSeh146360 2041bb5e3b2fSeh146360 /* 2042bb5e3b2fSeh146360 * update txcur 2043bb5e3b2fSeh146360 */ 2044bb5e3b2fSeh146360 ipw2200_csr_put32(sc, IPW2200_CSR_TX1_WRITE_INDEX, sc->sc_tx_cur); 2045bb5e3b2fSeh146360 2046bb5e3b2fSeh146360 /* 2047bb5e3b2fSeh146360 * success, free the original message 2048bb5e3b2fSeh146360 */ 2049bb5e3b2fSeh146360 if (mp) 2050bb5e3b2fSeh146360 freemsg(mp); 2051bb5e3b2fSeh146360 2052bb5e3b2fSeh146360 fail1: 2053bb5e3b2fSeh146360 mutex_exit(&sc->sc_tx_lock); 2054bb5e3b2fSeh146360 fail0: 2055bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_GLD, (sc->sc_dip, CE_CONT, 2056bb5e3b2fSeh146360 "ipw2200_send(): exit - err=%d\n", err)); 2057bb5e3b2fSeh146360 2058bb5e3b2fSeh146360 return (err); 2059bb5e3b2fSeh146360 } 2060bb5e3b2fSeh146360 2061bb5e3b2fSeh146360 /* 2062bb5e3b2fSeh146360 * IOCTL handlers 2063bb5e3b2fSeh146360 */ 2064bb5e3b2fSeh146360 #define IEEE80211_IOCTL_REQUIRED (1) 2065bb5e3b2fSeh146360 #define IEEE80211_IOCTL_NOT_REQUIRED (0) 2066bb5e3b2fSeh146360 static void 2067bb5e3b2fSeh146360 ipw2200_m_ioctl(void *arg, queue_t *q, mblk_t *m) 2068bb5e3b2fSeh146360 { 2069bb5e3b2fSeh146360 struct ipw2200_softc *sc = (struct ipw2200_softc *)arg; 2070bb5e3b2fSeh146360 struct ieee80211com *ic = &sc->sc_ic; 2071bb5e3b2fSeh146360 uint32_t err; 2072bb5e3b2fSeh146360 2073bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_GLD, (sc->sc_dip, CE_CONT, 2074bb5e3b2fSeh146360 "ipw2200_m_ioctl(): enter\n")); 2075bb5e3b2fSeh146360 2076bb5e3b2fSeh146360 /* 2077bb5e3b2fSeh146360 * Check whether or not need to handle this in net80211 2078bb5e3b2fSeh146360 * 2079bb5e3b2fSeh146360 */ 2080bb5e3b2fSeh146360 if (ipw2200_ioctl(sc, q, m) == IEEE80211_IOCTL_NOT_REQUIRED) 2081bb5e3b2fSeh146360 return; 2082bb5e3b2fSeh146360 2083bb5e3b2fSeh146360 err = ieee80211_ioctl(ic, q, m); 2084bb5e3b2fSeh146360 if (err == ENETRESET) { 2085bb5e3b2fSeh146360 if (sc->sc_flags & IPW2200_FLAG_RUNNING) { 2086bb5e3b2fSeh146360 (void) ipw2200_m_start(sc); 2087bb5e3b2fSeh146360 (void) ieee80211_new_state(ic, 2088bb5e3b2fSeh146360 IEEE80211_S_SCAN, -1); 2089bb5e3b2fSeh146360 } 2090bb5e3b2fSeh146360 } 2091bb5e3b2fSeh146360 if (err == ERESTART) { 2092bb5e3b2fSeh146360 if (sc->sc_flags & IPW2200_FLAG_RUNNING) 2093bb5e3b2fSeh146360 (void) ipw2200_chip_reset(sc); 2094bb5e3b2fSeh146360 } 2095bb5e3b2fSeh146360 } 2096bb5e3b2fSeh146360 static int 2097bb5e3b2fSeh146360 ipw2200_ioctl(struct ipw2200_softc *sc, queue_t *q, mblk_t *m) 2098bb5e3b2fSeh146360 { 2099bb5e3b2fSeh146360 struct iocblk *iocp; 2100bb5e3b2fSeh146360 uint32_t len, ret, cmd; 2101bb5e3b2fSeh146360 mblk_t *m0; 2102bb5e3b2fSeh146360 boolean_t need_privilege; 2103bb5e3b2fSeh146360 boolean_t need_net80211; 2104bb5e3b2fSeh146360 2105bb5e3b2fSeh146360 if (MBLKL(m) < sizeof (struct iocblk)) { 2106bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_IOCTL, (sc->sc_dip, CE_CONT, 2107bb5e3b2fSeh146360 "ipw2200_ioctl(): ioctl buffer too short, %u\n", 2108bb5e3b2fSeh146360 MBLKL(m))); 2109bb5e3b2fSeh146360 miocnak(q, m, 0, EINVAL); 2110bb5e3b2fSeh146360 /* 2111bb5e3b2fSeh146360 * Buf not enough, do not need net80211 either 2112bb5e3b2fSeh146360 */ 2113bb5e3b2fSeh146360 return (IEEE80211_IOCTL_NOT_REQUIRED); 2114bb5e3b2fSeh146360 } 2115bb5e3b2fSeh146360 2116bb5e3b2fSeh146360 /* 2117bb5e3b2fSeh146360 * Validate the command 2118bb5e3b2fSeh146360 */ 2119bb5e3b2fSeh146360 iocp = (struct iocblk *)m->b_rptr; 2120bb5e3b2fSeh146360 iocp->ioc_error = 0; 2121bb5e3b2fSeh146360 cmd = iocp->ioc_cmd; 2122bb5e3b2fSeh146360 need_privilege = B_TRUE; 2123bb5e3b2fSeh146360 switch (cmd) { 2124bb5e3b2fSeh146360 case WLAN_SET_PARAM: 2125bb5e3b2fSeh146360 case WLAN_COMMAND: 2126bb5e3b2fSeh146360 break; 2127bb5e3b2fSeh146360 case WLAN_GET_PARAM: 2128bb5e3b2fSeh146360 need_privilege = B_FALSE; 2129bb5e3b2fSeh146360 break; 2130bb5e3b2fSeh146360 default: 2131bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_IOCTL, (sc->sc_dip, CE_CONT, 2132bb5e3b2fSeh146360 "ipw2200_ioctl(): unknown cmd 0x%x", cmd)); 2133bb5e3b2fSeh146360 miocnak(q, m, 0, EINVAL); 2134bb5e3b2fSeh146360 /* 2135bb5e3b2fSeh146360 * Unknown cmd, do not need net80211 either 2136bb5e3b2fSeh146360 */ 2137bb5e3b2fSeh146360 return (IEEE80211_IOCTL_NOT_REQUIRED); 2138bb5e3b2fSeh146360 } 2139bb5e3b2fSeh146360 2140bb5e3b2fSeh146360 if (need_privilege) { 2141bb5e3b2fSeh146360 /* 2142bb5e3b2fSeh146360 * Check for specific net_config privilege on Solaris 10+. 2143bb5e3b2fSeh146360 * Otherwise just check for root access ... 2144bb5e3b2fSeh146360 */ 2145bb5e3b2fSeh146360 if (secpolicy_net_config != NULL) 2146bb5e3b2fSeh146360 ret = secpolicy_net_config(iocp->ioc_cr, B_FALSE); 2147bb5e3b2fSeh146360 else 2148bb5e3b2fSeh146360 ret = drv_priv(iocp->ioc_cr); 2149bb5e3b2fSeh146360 if (ret != 0) { 2150bb5e3b2fSeh146360 miocnak(q, m, 0, ret); 2151bb5e3b2fSeh146360 /* 2152bb5e3b2fSeh146360 * privilege check fail, do not need net80211 either 2153bb5e3b2fSeh146360 */ 2154bb5e3b2fSeh146360 return (IEEE80211_IOCTL_NOT_REQUIRED); 2155bb5e3b2fSeh146360 } 2156bb5e3b2fSeh146360 } 2157bb5e3b2fSeh146360 /* 2158bb5e3b2fSeh146360 * sanity check 2159bb5e3b2fSeh146360 */ 2160bb5e3b2fSeh146360 m0 = m->b_cont; 2161bb5e3b2fSeh146360 if (iocp->ioc_count == 0 || iocp->ioc_count < sizeof (wldp_t) || 2162bb5e3b2fSeh146360 m0 == NULL) { 2163bb5e3b2fSeh146360 miocnak(q, m, 0, EINVAL); 2164bb5e3b2fSeh146360 /* 2165bb5e3b2fSeh146360 * invalid format, do not need net80211 either 2166bb5e3b2fSeh146360 */ 2167bb5e3b2fSeh146360 return (IEEE80211_IOCTL_NOT_REQUIRED); 2168bb5e3b2fSeh146360 } 2169bb5e3b2fSeh146360 /* 2170bb5e3b2fSeh146360 * assuming single data block 2171bb5e3b2fSeh146360 */ 2172bb5e3b2fSeh146360 if (m0->b_cont) { 2173bb5e3b2fSeh146360 freemsg(m0->b_cont); 2174bb5e3b2fSeh146360 m0->b_cont = NULL; 2175bb5e3b2fSeh146360 } 2176bb5e3b2fSeh146360 2177bb5e3b2fSeh146360 need_net80211 = B_FALSE; 2178bb5e3b2fSeh146360 ret = ipw2200_getset(sc, m0, cmd, &need_net80211); 2179bb5e3b2fSeh146360 if (!need_net80211) { 2180bb5e3b2fSeh146360 len = msgdsize(m0); 2181bb5e3b2fSeh146360 2182bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_IOCTL, (sc->sc_dip, CE_CONT, 2183bb5e3b2fSeh146360 "ipw2200_ioctl(): go to call miocack with " 2184bb5e3b2fSeh146360 "ret = %d, len = %d\n", ret, len)); 2185bb5e3b2fSeh146360 miocack(q, m, len, ret); 2186bb5e3b2fSeh146360 return (IEEE80211_IOCTL_NOT_REQUIRED); 2187bb5e3b2fSeh146360 } 2188bb5e3b2fSeh146360 2189bb5e3b2fSeh146360 /* 2190bb5e3b2fSeh146360 * IEEE80211_IOCTL - need net80211 handle 2191bb5e3b2fSeh146360 */ 2192bb5e3b2fSeh146360 return (IEEE80211_IOCTL_REQUIRED); 2193bb5e3b2fSeh146360 } 2194bb5e3b2fSeh146360 2195bb5e3b2fSeh146360 static int 2196bb5e3b2fSeh146360 ipw2200_getset(struct ipw2200_softc *sc, mblk_t *m, uint32_t cmd, 2197bb5e3b2fSeh146360 boolean_t *need_net80211) 2198bb5e3b2fSeh146360 { 2199bb5e3b2fSeh146360 wldp_t *infp, *outfp; 2200bb5e3b2fSeh146360 uint32_t id; 2201bb5e3b2fSeh146360 int ret; 2202bb5e3b2fSeh146360 2203bb5e3b2fSeh146360 infp = (wldp_t *)m->b_rptr; 2204bb5e3b2fSeh146360 outfp = (wldp_t *)m->b_rptr; 2205bb5e3b2fSeh146360 outfp->wldp_result = WL_NOTSUPPORTED; 2206bb5e3b2fSeh146360 2207bb5e3b2fSeh146360 id = infp->wldp_id; 2208bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_IOCTL, (sc->sc_dip, CE_CONT, 2209bb5e3b2fSeh146360 "ipw2200_getset(): id = 0x%x\n", id)); 2210bb5e3b2fSeh146360 switch (id) { 2211bb5e3b2fSeh146360 case WL_RADIO: /* which is not supported by net80211 */ 2212bb5e3b2fSeh146360 ret = iwi_wificfg_radio(sc, cmd, outfp); 2213bb5e3b2fSeh146360 break; 2214bb5e3b2fSeh146360 case WL_DESIRED_RATES: /* hardware doesn't support fix-rates */ 2215bb5e3b2fSeh146360 ret = iwi_wificfg_desrates(outfp); 2216bb5e3b2fSeh146360 break; 2217bb5e3b2fSeh146360 default: 2218bb5e3b2fSeh146360 /* 2219bb5e3b2fSeh146360 * The wifi IOCTL net80211 supported: 2220bb5e3b2fSeh146360 * case WL_ESSID: 2221bb5e3b2fSeh146360 * case WL_BSSID: 2222bb5e3b2fSeh146360 * case WL_WEP_KEY_TAB: 2223bb5e3b2fSeh146360 * case WL_WEP_KEY_ID: 2224bb5e3b2fSeh146360 * case WL_AUTH_MODE: 2225bb5e3b2fSeh146360 * case WL_ENCRYPTION: 2226bb5e3b2fSeh146360 * case WL_BSS_TYPE: 2227bb5e3b2fSeh146360 * case WL_ESS_LIST: 2228bb5e3b2fSeh146360 * case WL_LINKSTATUS: 2229bb5e3b2fSeh146360 * case WL_RSSI: 2230bb5e3b2fSeh146360 * case WL_SCAN: 2231bb5e3b2fSeh146360 * case WL_LOAD_DEFAULTS: 2232bb5e3b2fSeh146360 * case WL_DISASSOCIATE: 2233bb5e3b2fSeh146360 */ 2234bb5e3b2fSeh146360 *need_net80211 = B_TRUE; /* let net80211 do the rest */ 2235bb5e3b2fSeh146360 return (0); 2236bb5e3b2fSeh146360 } 2237bb5e3b2fSeh146360 /* 2238bb5e3b2fSeh146360 * we will overwrite everything 2239bb5e3b2fSeh146360 */ 2240bb5e3b2fSeh146360 m->b_wptr = m->b_rptr + outfp->wldp_length; 2241bb5e3b2fSeh146360 return (ret); 2242bb5e3b2fSeh146360 } 2243bb5e3b2fSeh146360 2244bb5e3b2fSeh146360 static int 2245bb5e3b2fSeh146360 iwi_wificfg_radio(struct ipw2200_softc *sc, uint32_t cmd, wldp_t *outfp) 2246bb5e3b2fSeh146360 { 2247bb5e3b2fSeh146360 uint32_t ret = ENOTSUP; 2248bb5e3b2fSeh146360 2249bb5e3b2fSeh146360 switch (cmd) { 2250bb5e3b2fSeh146360 case WLAN_GET_PARAM: 2251bb5e3b2fSeh146360 *(wl_linkstatus_t *)(outfp->wldp_buf) = 2252bb5e3b2fSeh146360 ipw2200_radio_status(sc); 2253bb5e3b2fSeh146360 outfp->wldp_length = WIFI_BUF_OFFSET + sizeof (wl_linkstatus_t); 2254bb5e3b2fSeh146360 outfp->wldp_result = WL_SUCCESS; 2255bb5e3b2fSeh146360 ret = 0; /* command success */ 2256bb5e3b2fSeh146360 break; 2257bb5e3b2fSeh146360 case WLAN_SET_PARAM: 2258bb5e3b2fSeh146360 default: 2259bb5e3b2fSeh146360 break; 2260bb5e3b2fSeh146360 } 2261bb5e3b2fSeh146360 return (ret); 2262bb5e3b2fSeh146360 } 2263bb5e3b2fSeh146360 2264bb5e3b2fSeh146360 static int 2265bb5e3b2fSeh146360 iwi_wificfg_desrates(wldp_t *outfp) 2266bb5e3b2fSeh146360 { 2267bb5e3b2fSeh146360 /* return success, but with result NOTSUPPORTED */ 2268bb5e3b2fSeh146360 outfp->wldp_length = WIFI_BUF_OFFSET; 2269bb5e3b2fSeh146360 outfp->wldp_result = WL_NOTSUPPORTED; 2270bb5e3b2fSeh146360 return (0); 2271bb5e3b2fSeh146360 } 2272bb5e3b2fSeh146360 /* End of IOCTL Handlers */ 2273bb5e3b2fSeh146360 2274bb5e3b2fSeh146360 void 2275bb5e3b2fSeh146360 ipw2200_fix_channel(struct ieee80211com *ic, mblk_t *m) 2276bb5e3b2fSeh146360 { 2277bb5e3b2fSeh146360 struct ieee80211_frame *wh; 2278bb5e3b2fSeh146360 uint8_t subtype; 2279bb5e3b2fSeh146360 uint8_t *frm, *efrm; 2280bb5e3b2fSeh146360 2281bb5e3b2fSeh146360 wh = (struct ieee80211_frame *)m->b_rptr; 2282bb5e3b2fSeh146360 2283bb5e3b2fSeh146360 if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) != IEEE80211_FC0_TYPE_MGT) 2284bb5e3b2fSeh146360 return; 2285bb5e3b2fSeh146360 2286bb5e3b2fSeh146360 subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK; 2287bb5e3b2fSeh146360 2288bb5e3b2fSeh146360 if (subtype != IEEE80211_FC0_SUBTYPE_BEACON && 2289bb5e3b2fSeh146360 subtype != IEEE80211_FC0_SUBTYPE_PROBE_RESP) 2290bb5e3b2fSeh146360 return; 2291bb5e3b2fSeh146360 2292bb5e3b2fSeh146360 /* 2293bb5e3b2fSeh146360 * assume the message contains only 1 block 2294bb5e3b2fSeh146360 */ 2295bb5e3b2fSeh146360 frm = (uint8_t *)(wh + 1); 2296bb5e3b2fSeh146360 efrm = (uint8_t *)m->b_wptr; 2297bb5e3b2fSeh146360 frm += 12; /* skip tstamp, bintval and capinfo fields */ 2298bb5e3b2fSeh146360 while (frm < efrm) { 2299bb5e3b2fSeh146360 if (*frm == IEEE80211_ELEMID_DSPARMS) 2300bb5e3b2fSeh146360 #if IEEE80211_CHAN_MAX < 255 2301bb5e3b2fSeh146360 if (frm[2] <= IEEE80211_CHAN_MAX) 2302bb5e3b2fSeh146360 #endif 2303bb5e3b2fSeh146360 ic->ic_curchan = &ic->ic_sup_channels[frm[2]]; 2304bb5e3b2fSeh146360 frm += frm[1] + 2; 2305bb5e3b2fSeh146360 } 2306bb5e3b2fSeh146360 } 2307bb5e3b2fSeh146360 2308bb5e3b2fSeh146360 static void 2309bb5e3b2fSeh146360 ipw2200_rcv_frame(struct ipw2200_softc *sc, struct ipw2200_frame *frame) 2310bb5e3b2fSeh146360 { 2311bb5e3b2fSeh146360 struct ieee80211com *ic = &sc->sc_ic; 2312bb5e3b2fSeh146360 uint8_t *data = (uint8_t *)frame; 2313bb5e3b2fSeh146360 uint32_t len; 2314bb5e3b2fSeh146360 struct ieee80211_frame *wh; 2315bb5e3b2fSeh146360 struct ieee80211_node *in; 2316bb5e3b2fSeh146360 mblk_t *m; 2317bb5e3b2fSeh146360 int i; 2318bb5e3b2fSeh146360 2319bb5e3b2fSeh146360 len = LE_16(frame->len); 2320bb5e3b2fSeh146360 2321bb5e3b2fSeh146360 /* 2322bb5e3b2fSeh146360 * Skip the frame header, get the real data from the input 2323bb5e3b2fSeh146360 */ 2324bb5e3b2fSeh146360 data += sizeof (struct ipw2200_frame); 2325bb5e3b2fSeh146360 2326bb5e3b2fSeh146360 if ((len < sizeof (struct ieee80211_frame_min)) || 2327bb5e3b2fSeh146360 (len > IPW2200_RXBUF_SIZE)) { 2328bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_RX, (sc->sc_dip, CE_CONT, 2329bb5e3b2fSeh146360 "ipw2200_rcv_frame(): bad frame length=%u\n", 2330bb5e3b2fSeh146360 LE_16(frame->len))); 2331bb5e3b2fSeh146360 return; 2332bb5e3b2fSeh146360 } 2333bb5e3b2fSeh146360 2334bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_RX, (sc->sc_dip, CE_CONT, 2335bb5e3b2fSeh146360 "ipw2200_rcv_frame(): chan = %d, length = %d\n", frame->chan, len)); 2336bb5e3b2fSeh146360 2337bb5e3b2fSeh146360 m = allocb(len, BPRI_MED); 2338bb5e3b2fSeh146360 if (m) { 2339bb5e3b2fSeh146360 (void) memcpy(m->b_wptr, data, len); 2340bb5e3b2fSeh146360 m->b_wptr += len; 2341bb5e3b2fSeh146360 2342bb5e3b2fSeh146360 wh = (struct ieee80211_frame *)m->b_rptr; 2343bb5e3b2fSeh146360 if (wh->i_fc[1] & IEEE80211_FC1_WEP) { 2344bb5e3b2fSeh146360 /* 2345bb5e3b2fSeh146360 * h/w decyption leaves the WEP bit, iv and CRC fields 2346bb5e3b2fSeh146360 */ 2347bb5e3b2fSeh146360 wh->i_fc[1] &= ~IEEE80211_FC1_WEP; 2348bb5e3b2fSeh146360 for (i = sizeof (struct ieee80211_frame) - 1; 2349bb5e3b2fSeh146360 i >= 0; i--) 2350bb5e3b2fSeh146360 *(m->b_rptr + IEEE80211_WEP_IVLEN + 2351bb5e3b2fSeh146360 IEEE80211_WEP_KIDLEN + i) = 2352bb5e3b2fSeh146360 *(m->b_rptr + i); 2353bb5e3b2fSeh146360 m->b_rptr += IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN; 2354bb5e3b2fSeh146360 m->b_wptr -= IEEE80211_WEP_CRCLEN; 2355bb5e3b2fSeh146360 wh = (struct ieee80211_frame *)m->b_rptr; 2356bb5e3b2fSeh146360 } 2357bb5e3b2fSeh146360 2358bb5e3b2fSeh146360 if (ic->ic_state == IEEE80211_S_SCAN) { 2359bb5e3b2fSeh146360 ic->ic_ibss_chan = &ic->ic_sup_channels[frame->chan]; 2360bb5e3b2fSeh146360 ipw2200_fix_channel(ic, m); 2361bb5e3b2fSeh146360 } 2362bb5e3b2fSeh146360 2363bb5e3b2fSeh146360 in = ieee80211_find_rxnode(ic, wh); 2364bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_RX, (sc->sc_dip, CE_CONT, 2365bb5e3b2fSeh146360 "ipw2200_rcv_frame(): " 2366bb5e3b2fSeh146360 "ni_esslen:%d, ni_essid[0-5]:%c%c%c%c%c%c\n", 2367bb5e3b2fSeh146360 in->in_esslen, 2368bb5e3b2fSeh146360 in->in_essid[0], in->in_essid[1], in->in_essid[2], 2369bb5e3b2fSeh146360 in->in_essid[3], in->in_essid[4], in->in_essid[5])); 2370bb5e3b2fSeh146360 2371bb5e3b2fSeh146360 (void) ieee80211_input(ic, m, in, frame->rssi_dbm, 0); 2372bb5e3b2fSeh146360 2373bb5e3b2fSeh146360 ieee80211_free_node(in); 2374bb5e3b2fSeh146360 } 2375bb5e3b2fSeh146360 else 2376bb5e3b2fSeh146360 IPW2200_WARN((sc->sc_dip, CE_WARN, 2377bb5e3b2fSeh146360 "ipw2200_rcv_frame(): " 2378bb5e3b2fSeh146360 "cannot allocate receive message(%u)\n", 2379bb5e3b2fSeh146360 LE_16(frame->len))); 2380bb5e3b2fSeh146360 } 2381bb5e3b2fSeh146360 2382bb5e3b2fSeh146360 static void 2383bb5e3b2fSeh146360 ipw2200_rcv_notif(struct ipw2200_softc *sc, struct ipw2200_notif *notif) 2384bb5e3b2fSeh146360 { 2385bb5e3b2fSeh146360 struct ieee80211com *ic = &sc->sc_ic; 2386bb5e3b2fSeh146360 struct ipw2200_notif_association *assoc; 2387bb5e3b2fSeh146360 struct ipw2200_notif_authentication *auth; 2388bb5e3b2fSeh146360 uint8_t *ndata = (uint8_t *)notif; 2389bb5e3b2fSeh146360 2390bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_NOTIF, (sc->sc_dip, CE_CONT, 2391bb5e3b2fSeh146360 "ipw2200_rcv_notif(): type=%u\n", notif->type)); 2392bb5e3b2fSeh146360 2393bb5e3b2fSeh146360 ndata += sizeof (struct ipw2200_notif); 2394bb5e3b2fSeh146360 switch (notif->type) { 2395bb5e3b2fSeh146360 case IPW2200_NOTIF_TYPE_ASSOCIATION: 2396bb5e3b2fSeh146360 assoc = (struct ipw2200_notif_association *)ndata; 2397bb5e3b2fSeh146360 2398bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT, 2399bb5e3b2fSeh146360 "ipw2200_rcv_notif(): association=%u,%u\n", 2400bb5e3b2fSeh146360 assoc->state, assoc->status)); 2401bb5e3b2fSeh146360 2402bb5e3b2fSeh146360 switch (assoc->state) { 2403bb5e3b2fSeh146360 case IPW2200_ASSOC_SUCCESS: 2404bb5e3b2fSeh146360 ieee80211_new_state(ic, IEEE80211_S_RUN, -1); 2405bb5e3b2fSeh146360 break; 2406bb5e3b2fSeh146360 case IPW2200_ASSOC_FAIL: 2407bb5e3b2fSeh146360 ieee80211_begin_scan(ic, 1); /* reset */ 2408bb5e3b2fSeh146360 break; 2409bb5e3b2fSeh146360 default: 2410bb5e3b2fSeh146360 break; 2411bb5e3b2fSeh146360 } 2412bb5e3b2fSeh146360 break; 2413bb5e3b2fSeh146360 2414bb5e3b2fSeh146360 case IPW2200_NOTIF_TYPE_AUTHENTICATION: 2415bb5e3b2fSeh146360 auth = (struct ipw2200_notif_authentication *)ndata; 2416bb5e3b2fSeh146360 2417bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT, 2418bb5e3b2fSeh146360 "ipw2200_rcv_notif(): authentication=%u\n", auth->state)); 2419bb5e3b2fSeh146360 2420bb5e3b2fSeh146360 switch (auth->state) { 2421bb5e3b2fSeh146360 case IPW2200_AUTH_SUCCESS: 2422bb5e3b2fSeh146360 ieee80211_new_state(ic, IEEE80211_S_ASSOC, -1); 2423bb5e3b2fSeh146360 break; 2424bb5e3b2fSeh146360 case IPW2200_AUTH_FAIL: 2425bb5e3b2fSeh146360 break; 2426bb5e3b2fSeh146360 default: 2427bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_NOTIF, (sc->sc_dip, CE_CONT, 2428bb5e3b2fSeh146360 "ipw2200_rcv_notif(): " 2429bb5e3b2fSeh146360 "unknown authentication state(%u)\n", auth->state)); 2430bb5e3b2fSeh146360 break; 2431bb5e3b2fSeh146360 } 2432bb5e3b2fSeh146360 break; 2433bb5e3b2fSeh146360 2434bb5e3b2fSeh146360 case IPW2200_NOTIF_TYPE_SCAN_CHANNEL: 2435bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_SCAN, (sc->sc_dip, CE_CONT, 2436bb5e3b2fSeh146360 "ipw2200_rcv_notif(): scan-channel=%u\n", 2437bb5e3b2fSeh146360 ((struct ipw2200_notif_scan_channel *)ndata)->nchan)); 2438bb5e3b2fSeh146360 break; 2439bb5e3b2fSeh146360 2440bb5e3b2fSeh146360 case IPW2200_NOTIF_TYPE_SCAN_COMPLETE: 2441bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_SCAN, (sc->sc_dip, CE_CONT, 2442bb5e3b2fSeh146360 "ipw2200_rcv_notif():scan-completed,(%u,%u)\n", 2443bb5e3b2fSeh146360 ((struct ipw2200_notif_scan_complete *)ndata)->nchan, 2444bb5e3b2fSeh146360 ((struct ipw2200_notif_scan_complete *)ndata)->status)); 2445bb5e3b2fSeh146360 2446bb5e3b2fSeh146360 /* 2447bb5e3b2fSeh146360 * scan complete 2448bb5e3b2fSeh146360 */ 2449bb5e3b2fSeh146360 sc->sc_flags &= ~IPW2200_FLAG_SCANNING; 2450bb5e3b2fSeh146360 ieee80211_end_scan(ic); 2451bb5e3b2fSeh146360 break; 2452bb5e3b2fSeh146360 2453bb5e3b2fSeh146360 case IPW2200_NOTIF_TYPE_BEACON: 2454bb5e3b2fSeh146360 case IPW2200_NOTIF_TYPE_CALIBRATION: 2455bb5e3b2fSeh146360 case IPW2200_NOTIF_TYPE_NOISE: 2456bb5e3b2fSeh146360 /* 2457bb5e3b2fSeh146360 * just ignore 2458bb5e3b2fSeh146360 */ 2459bb5e3b2fSeh146360 break; 2460bb5e3b2fSeh146360 default: 2461bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_NOTIF, (sc->sc_dip, CE_CONT, 2462bb5e3b2fSeh146360 "ipw2200_rcv_notif(): unknown notification type(%u)\n", 2463bb5e3b2fSeh146360 notif->type)); 2464bb5e3b2fSeh146360 break; 2465bb5e3b2fSeh146360 } 2466bb5e3b2fSeh146360 } 2467bb5e3b2fSeh146360 2468bb5e3b2fSeh146360 static uint_t 2469bb5e3b2fSeh146360 ipw2200_intr(caddr_t arg) 2470bb5e3b2fSeh146360 { 2471bb5e3b2fSeh146360 struct ipw2200_softc *sc = (struct ipw2200_softc *)arg; 2472bb5e3b2fSeh146360 struct ieee80211com *ic = &sc->sc_ic; 2473bb5e3b2fSeh146360 uint32_t ireg, ridx, len, i; 2474bb5e3b2fSeh146360 uint8_t *p, *rxbuf; 2475bb5e3b2fSeh146360 struct dma_region *dr; 2476bb5e3b2fSeh146360 struct ipw2200_hdr *hdr; 2477bb5e3b2fSeh146360 int need_sched; 2478bb5e3b2fSeh146360 uint32_t widx; 2479bb5e3b2fSeh146360 2480bb5e3b2fSeh146360 ireg = ipw2200_csr_get32(sc, IPW2200_CSR_INTR); 2481bb5e3b2fSeh146360 2482bb5e3b2fSeh146360 if (ireg == 0xffffffff) 2483bb5e3b2fSeh146360 return (DDI_INTR_UNCLAIMED); 2484bb5e3b2fSeh146360 2485bb5e3b2fSeh146360 if (!(ireg & IPW2200_INTR_MASK_ALL)) 2486bb5e3b2fSeh146360 return (DDI_INTR_UNCLAIMED); 2487bb5e3b2fSeh146360 2488bb5e3b2fSeh146360 /* 2489bb5e3b2fSeh146360 * mask all interrupts 2490bb5e3b2fSeh146360 */ 2491bb5e3b2fSeh146360 ipw2200_csr_put32(sc, IPW2200_CSR_INTR_MASK, 0); 2492bb5e3b2fSeh146360 2493bb5e3b2fSeh146360 /* 2494bb5e3b2fSeh146360 * acknowledge all fired interrupts 2495bb5e3b2fSeh146360 */ 2496bb5e3b2fSeh146360 ipw2200_csr_put32(sc, IPW2200_CSR_INTR, ireg); 2497bb5e3b2fSeh146360 2498bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_INT, (sc->sc_dip, CE_CONT, 2499bb5e3b2fSeh146360 "ipw2200_intr(): enter. interrupt fired, int=0x%08x\n", ireg)); 2500bb5e3b2fSeh146360 2501bb5e3b2fSeh146360 if (ireg & IPW2200_INTR_MASK_ERR) { 2502bb5e3b2fSeh146360 2503bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_FATAL, (sc->sc_dip, CE_CONT, 2504bb5e3b2fSeh146360 "ipw2200 interrupt(): int= 0x%08x\n", ireg)); 2505bb5e3b2fSeh146360 2506bb5e3b2fSeh146360 /* 2507bb5e3b2fSeh146360 * inform mfthread to recover hw error by stopping it 2508bb5e3b2fSeh146360 */ 2509bb5e3b2fSeh146360 mutex_enter(&sc->sc_mflock); 2510bb5e3b2fSeh146360 sc->sc_flags |= IPW2200_FLAG_HW_ERR_RECOVER; 2511bb5e3b2fSeh146360 mutex_exit(&sc->sc_mflock); 2512bb5e3b2fSeh146360 2513bb5e3b2fSeh146360 } else { 2514bb5e3b2fSeh146360 if (ireg & IPW2200_INTR_FW_INITED) { 2515bb5e3b2fSeh146360 mutex_enter(&sc->sc_ilock); 2516bb5e3b2fSeh146360 sc->sc_fw_ok = 1; 2517bb5e3b2fSeh146360 cv_signal(&sc->sc_fw_cond); 2518bb5e3b2fSeh146360 mutex_exit(&sc->sc_ilock); 2519bb5e3b2fSeh146360 } 2520bb5e3b2fSeh146360 if (ireg & IPW2200_INTR_RADIO_OFF) { 2521bb5e3b2fSeh146360 IPW2200_REPORT((sc->sc_dip, CE_CONT, 2522bb5e3b2fSeh146360 "ipw2200_intr(): radio is OFF\n")); 2523bb5e3b2fSeh146360 /* 2524bb5e3b2fSeh146360 * Stop hardware, will notify LINK is down 2525bb5e3b2fSeh146360 */ 2526bb5e3b2fSeh146360 ipw2200_stop(sc); 2527bb5e3b2fSeh146360 } 2528bb5e3b2fSeh146360 if (ireg & IPW2200_INTR_CMD_TRANSFER) { 2529bb5e3b2fSeh146360 mutex_enter(&sc->sc_cmd_lock); 2530bb5e3b2fSeh146360 ridx = ipw2200_csr_get32(sc, 2531bb5e3b2fSeh146360 IPW2200_CSR_CMD_READ_INDEX); 2532bb5e3b2fSeh146360 i = RING_FORWARD(sc->sc_cmd_cur, 2533bb5e3b2fSeh146360 sc->sc_cmd_free, IPW2200_CMD_RING_SIZE); 2534bb5e3b2fSeh146360 len = RING_FLEN(i, ridx, IPW2200_CMD_RING_SIZE); 2535bb5e3b2fSeh146360 2536bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_INT, (sc->sc_dip, CE_CONT, 2537bb5e3b2fSeh146360 "ipw2200_intr(): cmd-ring,i=%u,ridx=%u,len=%u\n", 2538bb5e3b2fSeh146360 i, ridx, len)); 2539bb5e3b2fSeh146360 2540bb5e3b2fSeh146360 if (len > 0) { 2541bb5e3b2fSeh146360 sc->sc_cmd_free += len; 2542bb5e3b2fSeh146360 cv_signal(&sc->sc_cmd_cond); 2543bb5e3b2fSeh146360 } 2544bb5e3b2fSeh146360 for (; i != ridx; 2545bb5e3b2fSeh146360 i = RING_FORWARD(i, 1, IPW2200_CMD_RING_SIZE)) 2546bb5e3b2fSeh146360 sc->sc_done[i] = 1; 2547bb5e3b2fSeh146360 mutex_exit(&sc->sc_cmd_lock); 2548bb5e3b2fSeh146360 2549bb5e3b2fSeh146360 mutex_enter(&sc->sc_ilock); 2550bb5e3b2fSeh146360 cv_signal(&sc->sc_cmd_status_cond); 2551bb5e3b2fSeh146360 mutex_exit(&sc->sc_ilock); 2552bb5e3b2fSeh146360 } 2553bb5e3b2fSeh146360 if (ireg & IPW2200_INTR_RX_TRANSFER) { 2554bb5e3b2fSeh146360 ridx = ipw2200_csr_get32(sc, 2555bb5e3b2fSeh146360 IPW2200_CSR_RX_READ_INDEX); 2556bb5e3b2fSeh146360 widx = ipw2200_csr_get32(sc, 2557bb5e3b2fSeh146360 IPW2200_CSR_RX_WRITE_INDEX); 2558bb5e3b2fSeh146360 2559bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_INT, (sc->sc_dip, CE_CONT, 2560bb5e3b2fSeh146360 "ipw2200_intr(): rx-ring,widx=%u,ridx=%u\n", 2561bb5e3b2fSeh146360 ridx, widx)); 2562bb5e3b2fSeh146360 2563bb5e3b2fSeh146360 for (; sc->sc_rx_cur != ridx; 2564bb5e3b2fSeh146360 sc->sc_rx_cur = RING_FORWARD(sc->sc_rx_cur, 1, 2565bb5e3b2fSeh146360 IPW2200_RX_RING_SIZE)) { 2566bb5e3b2fSeh146360 i = sc->sc_rx_cur; 2567bb5e3b2fSeh146360 rxbuf = sc->sc_rxbufs[i]; 2568bb5e3b2fSeh146360 dr = &sc->sc_dma_rxbufs[i]; 2569bb5e3b2fSeh146360 2570bb5e3b2fSeh146360 /* 2571bb5e3b2fSeh146360 * DMA sync 2572bb5e3b2fSeh146360 */ 2573bb5e3b2fSeh146360 (void) ddi_dma_sync(dr->dr_hnd, 0, 2574bb5e3b2fSeh146360 IPW2200_RXBUF_SIZE, DDI_DMA_SYNC_FORKERNEL); 2575bb5e3b2fSeh146360 /* 2576bb5e3b2fSeh146360 * Get rx header(hdr) and rx data(p) from rxbuf 2577bb5e3b2fSeh146360 */ 2578bb5e3b2fSeh146360 p = rxbuf; 2579bb5e3b2fSeh146360 hdr = (struct ipw2200_hdr *)p; 2580bb5e3b2fSeh146360 p += sizeof (struct ipw2200_hdr); 2581bb5e3b2fSeh146360 2582bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_INT, 2583bb5e3b2fSeh146360 (sc->sc_dip, CE_CONT, 2584bb5e3b2fSeh146360 "ipw2200_intr(): Rx hdr type %u\n", 2585bb5e3b2fSeh146360 hdr->type)); 2586bb5e3b2fSeh146360 2587bb5e3b2fSeh146360 switch (hdr->type) { 2588bb5e3b2fSeh146360 case IPW2200_HDR_TYPE_FRAME: 2589bb5e3b2fSeh146360 ipw2200_rcv_frame(sc, 2590bb5e3b2fSeh146360 (struct ipw2200_frame *)p); 2591bb5e3b2fSeh146360 break; 2592bb5e3b2fSeh146360 2593bb5e3b2fSeh146360 case IPW2200_HDR_TYPE_NOTIF: 2594bb5e3b2fSeh146360 ipw2200_rcv_notif(sc, 2595bb5e3b2fSeh146360 (struct ipw2200_notif *)p); 2596bb5e3b2fSeh146360 break; 2597bb5e3b2fSeh146360 default: 2598bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_INT, 2599bb5e3b2fSeh146360 (sc->sc_dip, CE_CONT, 2600bb5e3b2fSeh146360 "ipw2200_intr(): " 2601bb5e3b2fSeh146360 "unknown Rx hdr type %u\n", 2602bb5e3b2fSeh146360 hdr->type)); 2603bb5e3b2fSeh146360 break; 2604bb5e3b2fSeh146360 } 2605bb5e3b2fSeh146360 } 2606bb5e3b2fSeh146360 /* 2607bb5e3b2fSeh146360 * write sc_rx_cur backward 1 step into RX_WRITE_INDEX 2608bb5e3b2fSeh146360 */ 2609bb5e3b2fSeh146360 ipw2200_csr_put32(sc, IPW2200_CSR_RX_WRITE_INDEX, 2610bb5e3b2fSeh146360 RING_BACKWARD(sc->sc_rx_cur, 1, 2611bb5e3b2fSeh146360 IPW2200_RX_RING_SIZE)); 2612bb5e3b2fSeh146360 } 2613bb5e3b2fSeh146360 if (ireg & IPW2200_INTR_TX1_TRANSFER) { 2614bb5e3b2fSeh146360 mutex_enter(&sc->sc_tx_lock); 2615bb5e3b2fSeh146360 ridx = ipw2200_csr_get32(sc, 2616bb5e3b2fSeh146360 IPW2200_CSR_TX1_READ_INDEX); 2617bb5e3b2fSeh146360 len = RING_FLEN(RING_FORWARD(sc->sc_tx_cur, 2618bb5e3b2fSeh146360 sc->sc_tx_free, IPW2200_TX_RING_SIZE), 2619bb5e3b2fSeh146360 ridx, IPW2200_TX_RING_SIZE); 2620bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_RING, (sc->sc_dip, CE_CONT, 2621bb5e3b2fSeh146360 "ipw2200_intr(): tx-ring,ridx=%u,len=%u\n", 2622bb5e3b2fSeh146360 ridx, len)); 2623bb5e3b2fSeh146360 sc->sc_tx_free += len; 2624bb5e3b2fSeh146360 2625bb5e3b2fSeh146360 need_sched = 0; 2626bb5e3b2fSeh146360 if ((sc->sc_tx_free > IPW2200_TX_RING_MIN) && 2627bb5e3b2fSeh146360 (sc->sc_flags & IPW2200_FLAG_TX_SCHED)) { 2628bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_RING, (sc->sc_dip, 2629bb5e3b2fSeh146360 CE_CONT, 2630bb5e3b2fSeh146360 "ipw2200_intr(): Need Reschedule!")); 2631bb5e3b2fSeh146360 need_sched = 1; 2632bb5e3b2fSeh146360 sc->sc_flags &= ~IPW2200_FLAG_TX_SCHED; 2633bb5e3b2fSeh146360 } 2634bb5e3b2fSeh146360 mutex_exit(&sc->sc_tx_lock); 2635bb5e3b2fSeh146360 2636bb5e3b2fSeh146360 if (need_sched) 2637bb5e3b2fSeh146360 mac_tx_update(ic->ic_mach); 2638bb5e3b2fSeh146360 } 2639bb5e3b2fSeh146360 } 2640bb5e3b2fSeh146360 2641bb5e3b2fSeh146360 /* 2642bb5e3b2fSeh146360 * enable all interrupts 2643bb5e3b2fSeh146360 */ 2644bb5e3b2fSeh146360 ipw2200_csr_put32(sc, IPW2200_CSR_INTR_MASK, IPW2200_INTR_MASK_ALL); 2645bb5e3b2fSeh146360 2646bb5e3b2fSeh146360 return (DDI_INTR_CLAIMED); 2647bb5e3b2fSeh146360 } 2648bb5e3b2fSeh146360 2649bb5e3b2fSeh146360 2650bb5e3b2fSeh146360 /* 2651bb5e3b2fSeh146360 * Module Loading Data & Entry Points 2652bb5e3b2fSeh146360 */ 2653bb5e3b2fSeh146360 DDI_DEFINE_STREAM_OPS(ipw2200_devops, nulldev, nulldev, ipw2200_attach, 2654bb5e3b2fSeh146360 ipw2200_detach, nodev, NULL, D_MP, NULL); 2655bb5e3b2fSeh146360 2656bb5e3b2fSeh146360 static struct modldrv ipw2200_modldrv = { 2657bb5e3b2fSeh146360 &mod_driverops, 2658bb5e3b2fSeh146360 ipw2200_ident, 2659bb5e3b2fSeh146360 &ipw2200_devops 2660bb5e3b2fSeh146360 }; 2661bb5e3b2fSeh146360 2662bb5e3b2fSeh146360 static struct modlinkage ipw2200_modlinkage = { 2663bb5e3b2fSeh146360 MODREV_1, 2664bb5e3b2fSeh146360 &ipw2200_modldrv, 2665bb5e3b2fSeh146360 NULL 2666bb5e3b2fSeh146360 }; 2667bb5e3b2fSeh146360 2668bb5e3b2fSeh146360 int 2669bb5e3b2fSeh146360 _init(void) 2670bb5e3b2fSeh146360 { 2671bb5e3b2fSeh146360 int status; 2672bb5e3b2fSeh146360 2673bb5e3b2fSeh146360 status = ddi_soft_state_init(&ipw2200_ssp, 2674bb5e3b2fSeh146360 sizeof (struct ipw2200_softc), 1); 2675bb5e3b2fSeh146360 if (status != DDI_SUCCESS) 2676bb5e3b2fSeh146360 return (status); 2677bb5e3b2fSeh146360 2678bb5e3b2fSeh146360 mac_init_ops(&ipw2200_devops, IPW2200_DRV_NAME); 2679bb5e3b2fSeh146360 status = mod_install(&ipw2200_modlinkage); 2680bb5e3b2fSeh146360 if (status != DDI_SUCCESS) { 2681bb5e3b2fSeh146360 mac_fini_ops(&ipw2200_devops); 2682bb5e3b2fSeh146360 ddi_soft_state_fini(&ipw2200_ssp); 2683bb5e3b2fSeh146360 } 2684bb5e3b2fSeh146360 2685bb5e3b2fSeh146360 return (status); 2686bb5e3b2fSeh146360 } 2687bb5e3b2fSeh146360 2688bb5e3b2fSeh146360 int 2689bb5e3b2fSeh146360 _fini(void) 2690bb5e3b2fSeh146360 { 2691bb5e3b2fSeh146360 int status; 2692bb5e3b2fSeh146360 2693bb5e3b2fSeh146360 status = mod_remove(&ipw2200_modlinkage); 2694bb5e3b2fSeh146360 if (status == DDI_SUCCESS) { 2695bb5e3b2fSeh146360 mac_fini_ops(&ipw2200_devops); 2696bb5e3b2fSeh146360 ddi_soft_state_fini(&ipw2200_ssp); 2697bb5e3b2fSeh146360 } 2698bb5e3b2fSeh146360 2699bb5e3b2fSeh146360 return (status); 2700bb5e3b2fSeh146360 } 2701bb5e3b2fSeh146360 2702bb5e3b2fSeh146360 int 2703bb5e3b2fSeh146360 _info(struct modinfo *modinfop) 2704bb5e3b2fSeh146360 { 2705bb5e3b2fSeh146360 return (mod_info(&ipw2200_modlinkage, modinfop)); 2706bb5e3b2fSeh146360 } 2707