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 617*76939ce0Seh146360 /* ARGSUSED */ 618*76939ce0Seh146360 int 619*76939ce0Seh146360 ipw2200_reset(dev_info_t *dip, ddi_reset_cmd_t cmd) 620*76939ce0Seh146360 { 621*76939ce0Seh146360 struct ipw2200_softc *sc = 622*76939ce0Seh146360 ddi_get_soft_state(ipw2200_ssp, ddi_get_instance(dip)); 623*76939ce0Seh146360 ASSERT(sc != NULL); 624*76939ce0Seh146360 625*76939ce0Seh146360 ipw2200_stop(sc); 626*76939ce0Seh146360 627*76939ce0Seh146360 return (DDI_SUCCESS); 628*76939ce0Seh146360 } 629*76939ce0Seh146360 630bb5e3b2fSeh146360 static void 631bb5e3b2fSeh146360 ipw2200_stop(struct ipw2200_softc *sc) 632bb5e3b2fSeh146360 { 633bb5e3b2fSeh146360 struct ieee80211com *ic = &sc->sc_ic; 634bb5e3b2fSeh146360 635bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_HWCAP, (sc->sc_dip, CE_CONT, 636bb5e3b2fSeh146360 "ipw2200_stop(): enter\n")); 637bb5e3b2fSeh146360 638bb5e3b2fSeh146360 ipw2200_master_stop(sc); 639bb5e3b2fSeh146360 ipw2200_csr_put32(sc, IPW2200_CSR_RST, IPW2200_RST_SW_RESET); 640bb5e3b2fSeh146360 641bb5e3b2fSeh146360 /* 642bb5e3b2fSeh146360 * Reset ring 643bb5e3b2fSeh146360 */ 644bb5e3b2fSeh146360 ipw2200_ring_reset(sc); 645bb5e3b2fSeh146360 646bb5e3b2fSeh146360 ieee80211_new_state(ic, IEEE80211_S_INIT, -1); 647bb5e3b2fSeh146360 sc->sc_flags &= ~IPW2200_FLAG_SCANNING; 648bb5e3b2fSeh146360 649bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_HWCAP, (sc->sc_dip, CE_CONT, 650bb5e3b2fSeh146360 "ipw2200_stop(): exit\n")); 651bb5e3b2fSeh146360 } 652bb5e3b2fSeh146360 653bb5e3b2fSeh146360 static int 654bb5e3b2fSeh146360 ipw2200_config(struct ipw2200_softc *sc) 655bb5e3b2fSeh146360 { 656bb5e3b2fSeh146360 struct ieee80211com *ic = &sc->sc_ic; 657bb5e3b2fSeh146360 struct ipw2200_configuration cfg; 658bb5e3b2fSeh146360 uint32_t data; 659bb5e3b2fSeh146360 struct ipw2200_txpower pwr; 660bb5e3b2fSeh146360 struct ipw2200_rateset rs; 661bb5e3b2fSeh146360 struct ipw2200_wep_key wkey; 662bb5e3b2fSeh146360 int err, i; 663bb5e3b2fSeh146360 664bb5e3b2fSeh146360 /* 665bb5e3b2fSeh146360 * Set the IBSS mode channel: Tx power 666bb5e3b2fSeh146360 */ 667bb5e3b2fSeh146360 if (ic->ic_opmode == IEEE80211_M_IBSS) { 668bb5e3b2fSeh146360 pwr.mode = IPW2200_MODE_11B; 669bb5e3b2fSeh146360 pwr.nchan = 11; 670bb5e3b2fSeh146360 for (i = 0; i < pwr.nchan; i++) { 671bb5e3b2fSeh146360 pwr.chan[i].chan = i + 1; 672bb5e3b2fSeh146360 pwr.chan[i].power = IPW2200_TXPOWER_MAX; 673bb5e3b2fSeh146360 } 674bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT, 675bb5e3b2fSeh146360 "ipw2200_config(): Setting .11b channels Tx power\n")); 676bb5e3b2fSeh146360 err = ipw2200_cmd(sc, IPW2200_CMD_SET_TX_POWER, 677bb5e3b2fSeh146360 &pwr, sizeof (pwr), 0); 678bb5e3b2fSeh146360 if (err != DDI_SUCCESS) 679bb5e3b2fSeh146360 return (err); 680bb5e3b2fSeh146360 681bb5e3b2fSeh146360 pwr.mode = IPW2200_MODE_11G; 682bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT, 683bb5e3b2fSeh146360 "ipw2200_config(): Setting .11g channels Tx power\n")); 684bb5e3b2fSeh146360 err = ipw2200_cmd(sc, IPW2200_CMD_SET_TX_POWER, 685bb5e3b2fSeh146360 &pwr, sizeof (pwr), 0); 686bb5e3b2fSeh146360 if (err != DDI_SUCCESS) 687bb5e3b2fSeh146360 return (err); 688bb5e3b2fSeh146360 } 689bb5e3b2fSeh146360 690bb5e3b2fSeh146360 /* 691bb5e3b2fSeh146360 * Set MAC address 692bb5e3b2fSeh146360 */ 693bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT, 694bb5e3b2fSeh146360 "ipw2200_config(): Setting MAC address to " 695bb5e3b2fSeh146360 "%02x:%02x:%02x:%02x:%02x:%02x\n", 696bb5e3b2fSeh146360 ic->ic_macaddr[0], ic->ic_macaddr[1], ic->ic_macaddr[2], 697bb5e3b2fSeh146360 ic->ic_macaddr[3], ic->ic_macaddr[4], ic->ic_macaddr[5])); 698bb5e3b2fSeh146360 err = ipw2200_cmd(sc, IPW2200_CMD_SET_MAC_ADDRESS, ic->ic_macaddr, 699bb5e3b2fSeh146360 IEEE80211_ADDR_LEN, 0); 700bb5e3b2fSeh146360 if (err != DDI_SUCCESS) 701bb5e3b2fSeh146360 return (err); 702bb5e3b2fSeh146360 703bb5e3b2fSeh146360 /* 704bb5e3b2fSeh146360 * Set basic system config settings: configuration(capabilities) 705bb5e3b2fSeh146360 */ 706bb5e3b2fSeh146360 (void) memset(&cfg, 0, sizeof (cfg)); 707bb5e3b2fSeh146360 cfg.bluetooth_coexistence = 1; 708bb5e3b2fSeh146360 cfg.multicast_enabled = 1; 709bb5e3b2fSeh146360 cfg.answer_pbreq = 1; 710bb5e3b2fSeh146360 cfg.noise_reported = 1; 711bb5e3b2fSeh146360 712bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT, 713bb5e3b2fSeh146360 "ipw2200_config(): Configuring adapter\n")); 714bb5e3b2fSeh146360 err = ipw2200_cmd(sc, IPW2200_CMD_SET_CONFIG, 715bb5e3b2fSeh146360 &cfg, sizeof (cfg), 0); 716bb5e3b2fSeh146360 if (err != DDI_SUCCESS) 717bb5e3b2fSeh146360 return (err); 718bb5e3b2fSeh146360 719bb5e3b2fSeh146360 /* 720bb5e3b2fSeh146360 * Set power mode 721bb5e3b2fSeh146360 */ 722bb5e3b2fSeh146360 data = LE_32(IPW2200_POWER_MODE_CAM); 723bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT, 724bb5e3b2fSeh146360 "ipw2200_config(): Setting power mode to %u\n", LE_32(data))); 725bb5e3b2fSeh146360 err = ipw2200_cmd(sc, IPW2200_CMD_SET_POWER_MODE, 726bb5e3b2fSeh146360 &data, sizeof (data), 0); 727bb5e3b2fSeh146360 if (err != DDI_SUCCESS) 728bb5e3b2fSeh146360 return (err); 729bb5e3b2fSeh146360 730bb5e3b2fSeh146360 /* 731bb5e3b2fSeh146360 * Set supported rates 732bb5e3b2fSeh146360 */ 733bb5e3b2fSeh146360 rs.mode = IPW2200_MODE_11G; 734bb5e3b2fSeh146360 rs.type = IPW2200_RATESET_TYPE_SUPPORTED; 735bb5e3b2fSeh146360 rs.nrates = ic->ic_sup_rates[IEEE80211_MODE_11G].ir_nrates; 736bb5e3b2fSeh146360 (void) memcpy(rs.rates, ic->ic_sup_rates[IEEE80211_MODE_11G].ir_rates, 737bb5e3b2fSeh146360 rs.nrates); 738bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT, 739bb5e3b2fSeh146360 "ipw2200_config(): Setting .11g supported rates(%u)\n", rs.nrates)); 740bb5e3b2fSeh146360 err = ipw2200_cmd(sc, IPW2200_CMD_SET_RATES, &rs, sizeof (rs), 0); 741bb5e3b2fSeh146360 if (err != DDI_SUCCESS) 742bb5e3b2fSeh146360 return (err); 743bb5e3b2fSeh146360 744bb5e3b2fSeh146360 rs.mode = IPW2200_MODE_11A; 745bb5e3b2fSeh146360 rs.type = IPW2200_RATESET_TYPE_SUPPORTED; 746bb5e3b2fSeh146360 rs.nrates = ic->ic_sup_rates[IEEE80211_MODE_11A].ir_nrates; 747bb5e3b2fSeh146360 (void) memcpy(rs.rates, ic->ic_sup_rates[IEEE80211_MODE_11A].ir_rates, 748bb5e3b2fSeh146360 rs.nrates); 749bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT, 750bb5e3b2fSeh146360 "ipw2200_config(): Setting .11a supported rates(%u)\n", rs.nrates)); 751bb5e3b2fSeh146360 err = ipw2200_cmd(sc, IPW2200_CMD_SET_RATES, &rs, sizeof (rs), 0); 752bb5e3b2fSeh146360 if (err != DDI_SUCCESS) 753bb5e3b2fSeh146360 return (err); 754bb5e3b2fSeh146360 755bb5e3b2fSeh146360 /* 756bb5e3b2fSeh146360 * Set RTS(request-to-send) threshold 757bb5e3b2fSeh146360 */ 758bb5e3b2fSeh146360 data = LE_32(ic->ic_rtsthreshold); 759bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT, 760bb5e3b2fSeh146360 "ipw2200_config(): Setting RTS threshold to %u\n", LE_32(data))); 761bb5e3b2fSeh146360 err = ipw2200_cmd(sc, IPW2200_CMD_SET_RTS_THRESHOLD, &data, 762bb5e3b2fSeh146360 sizeof (data), 0); 763bb5e3b2fSeh146360 if (err != DDI_SUCCESS) 764bb5e3b2fSeh146360 return (err); 765bb5e3b2fSeh146360 766bb5e3b2fSeh146360 /* 767bb5e3b2fSeh146360 * Set fragmentation threshold 768bb5e3b2fSeh146360 */ 769bb5e3b2fSeh146360 data = LE_32(ic->ic_fragthreshold); 770bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT, 771bb5e3b2fSeh146360 "ipw2200_config(): Setting fragmentation threshold to %u\n", 772bb5e3b2fSeh146360 LE_32(data))); 773bb5e3b2fSeh146360 err = ipw2200_cmd(sc, IPW2200_CMD_SET_FRAG_THRESHOLD, &data, 774bb5e3b2fSeh146360 sizeof (data), 0); 775bb5e3b2fSeh146360 if (err != DDI_SUCCESS) 776bb5e3b2fSeh146360 return (err); 777bb5e3b2fSeh146360 778bb5e3b2fSeh146360 /* 779bb5e3b2fSeh146360 * Set desired ESSID if we have 780bb5e3b2fSeh146360 */ 781bb5e3b2fSeh146360 if (ic->ic_des_esslen != 0) { 782bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT, 783bb5e3b2fSeh146360 "ipw2200_config(): Setting desired ESSID to " 784bb5e3b2fSeh146360 "(%u),%c%c%c%c%c%c%c%c\n", 785bb5e3b2fSeh146360 ic->ic_des_esslen, 786bb5e3b2fSeh146360 ic->ic_des_essid[0], ic->ic_des_essid[1], 787bb5e3b2fSeh146360 ic->ic_des_essid[2], ic->ic_des_essid[3], 788bb5e3b2fSeh146360 ic->ic_des_essid[4], ic->ic_des_essid[5], 789bb5e3b2fSeh146360 ic->ic_des_essid[6], ic->ic_des_essid[7])); 790bb5e3b2fSeh146360 err = ipw2200_cmd(sc, IPW2200_CMD_SET_ESSID, ic->ic_des_essid, 791bb5e3b2fSeh146360 ic->ic_des_esslen, 0); 792bb5e3b2fSeh146360 if (err != DDI_SUCCESS) 793bb5e3b2fSeh146360 return (err); 794bb5e3b2fSeh146360 } 795bb5e3b2fSeh146360 796bb5e3b2fSeh146360 /* 797bb5e3b2fSeh146360 * Set WEP initial vector(random seed) 798bb5e3b2fSeh146360 */ 799bb5e3b2fSeh146360 (void) random_get_pseudo_bytes((uint8_t *)&data, sizeof (data)); 800bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT, 801bb5e3b2fSeh146360 "ipw2200_config(): Setting initialization vector to %u\n", 802bb5e3b2fSeh146360 LE_32(data))); 803bb5e3b2fSeh146360 err = ipw2200_cmd(sc, IPW2200_CMD_SET_IV, &data, sizeof (data), 0); 804bb5e3b2fSeh146360 if (err != DDI_SUCCESS) 805bb5e3b2fSeh146360 return (err); 806bb5e3b2fSeh146360 807bb5e3b2fSeh146360 /* 808bb5e3b2fSeh146360 * Set WEP if any 809bb5e3b2fSeh146360 */ 810bb5e3b2fSeh146360 if (ic->ic_flags & IEEE80211_F_PRIVACY) { 811bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT, 812bb5e3b2fSeh146360 "ipw2200_config(): Setting Wep Key\n", LE_32(data))); 813bb5e3b2fSeh146360 for (i = 0; i < IEEE80211_WEP_NKID; i++) { 814bb5e3b2fSeh146360 wkey.cmd = IPW2200_WEP_KEY_CMD_SETKEY; 815bb5e3b2fSeh146360 wkey.idx = (uint8_t)i; 816bb5e3b2fSeh146360 wkey.len = ic->ic_nw_keys[i].wk_keylen; 817bb5e3b2fSeh146360 (void) memset(wkey.key, 0, sizeof (wkey.key)); 818bb5e3b2fSeh146360 if (ic->ic_nw_keys[i].wk_keylen) 819bb5e3b2fSeh146360 (void) memcpy(wkey.key, 820bb5e3b2fSeh146360 ic->ic_nw_keys[i].wk_key, 821bb5e3b2fSeh146360 ic->ic_nw_keys[i].wk_keylen); 822bb5e3b2fSeh146360 err = ipw2200_cmd(sc, IPW2200_CMD_SET_WEP_KEY, 823bb5e3b2fSeh146360 &wkey, sizeof (wkey), 0); 824bb5e3b2fSeh146360 if (err != DDI_SUCCESS) 825bb5e3b2fSeh146360 return (err); 826bb5e3b2fSeh146360 } 827bb5e3b2fSeh146360 } 828bb5e3b2fSeh146360 829bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT, 830bb5e3b2fSeh146360 "ipw2200_config(): Enabling adapter\n")); 831bb5e3b2fSeh146360 832bb5e3b2fSeh146360 return (ipw2200_cmd(sc, IPW2200_CMD_ENABLE, NULL, 0, 0)); 833bb5e3b2fSeh146360 } 834bb5e3b2fSeh146360 835bb5e3b2fSeh146360 static int 836bb5e3b2fSeh146360 ipw2200_cmd(struct ipw2200_softc *sc, 837bb5e3b2fSeh146360 uint32_t type, void *buf, size_t len, int async) 838bb5e3b2fSeh146360 { 839bb5e3b2fSeh146360 struct ipw2200_cmd_desc *cmd; 840bb5e3b2fSeh146360 clock_t clk; 841bb5e3b2fSeh146360 uint32_t idx; 842bb5e3b2fSeh146360 843bb5e3b2fSeh146360 mutex_enter(&sc->sc_cmd_lock); 844bb5e3b2fSeh146360 while (sc->sc_cmd_free < 1) 845bb5e3b2fSeh146360 cv_wait(&sc->sc_cmd_cond, &sc->sc_cmd_lock); 846bb5e3b2fSeh146360 847bb5e3b2fSeh146360 idx = sc->sc_cmd_cur; 848bb5e3b2fSeh146360 cmd = &sc->sc_cmdsc[idx]; 849bb5e3b2fSeh146360 (void) memset(cmd, 0, sizeof (*cmd)); 850bb5e3b2fSeh146360 851bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_RING, (sc->sc_dip, CE_CONT, 852bb5e3b2fSeh146360 "ipw2200_cmd(): cmd-cur=%d\n", idx)); 853bb5e3b2fSeh146360 854bb5e3b2fSeh146360 cmd->hdr.type = IPW2200_HDR_TYPE_COMMAND; 855bb5e3b2fSeh146360 cmd->hdr.flags = IPW2200_HDR_FLAG_IRQ; 856bb5e3b2fSeh146360 cmd->type = (uint8_t)type; 857bb5e3b2fSeh146360 if (len == 0 || buf == NULL) 858bb5e3b2fSeh146360 cmd->len = 0; 859bb5e3b2fSeh146360 else { 860bb5e3b2fSeh146360 cmd->len = (uint8_t)len; 861bb5e3b2fSeh146360 (void) memcpy(cmd->data, buf, len); 862bb5e3b2fSeh146360 } 863bb5e3b2fSeh146360 sc->sc_done[idx] = 0; 864bb5e3b2fSeh146360 865bb5e3b2fSeh146360 /* 866bb5e3b2fSeh146360 * DMA sync 867bb5e3b2fSeh146360 */ 868bb5e3b2fSeh146360 (void) ddi_dma_sync(sc->sc_dma_cmdsc.dr_hnd, 869bb5e3b2fSeh146360 idx * sizeof (struct ipw2200_cmd_desc), 870bb5e3b2fSeh146360 sizeof (struct ipw2200_cmd_desc), DDI_DMA_SYNC_FORDEV); 871bb5e3b2fSeh146360 872bb5e3b2fSeh146360 sc->sc_cmd_cur = RING_FORWARD(sc->sc_cmd_cur, 1, IPW2200_CMD_RING_SIZE); 873bb5e3b2fSeh146360 sc->sc_cmd_free--; 874bb5e3b2fSeh146360 875bb5e3b2fSeh146360 ipw2200_csr_put32(sc, IPW2200_CSR_CMD_WRITE_INDEX, sc->sc_cmd_cur); 876bb5e3b2fSeh146360 877bb5e3b2fSeh146360 mutex_exit(&sc->sc_cmd_lock); 878bb5e3b2fSeh146360 879bb5e3b2fSeh146360 if (async) 880bb5e3b2fSeh146360 goto out; 881bb5e3b2fSeh146360 882bb5e3b2fSeh146360 /* 883bb5e3b2fSeh146360 * Wait for command done 884bb5e3b2fSeh146360 */ 885bb5e3b2fSeh146360 mutex_enter(&sc->sc_ilock); 886bb5e3b2fSeh146360 while (sc->sc_done[idx] == 0) { 887bb5e3b2fSeh146360 /* pending */ 888bb5e3b2fSeh146360 clk = ddi_get_lbolt() + drv_usectohz(5000000); /* 5 second */ 889bb5e3b2fSeh146360 if (cv_timedwait(&sc->sc_cmd_status_cond, &sc->sc_ilock, clk) 890bb5e3b2fSeh146360 < 0) 891bb5e3b2fSeh146360 break; 892bb5e3b2fSeh146360 } 893bb5e3b2fSeh146360 mutex_exit(&sc->sc_ilock); 894bb5e3b2fSeh146360 895bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_RING, (sc->sc_dip, CE_CONT, 896bb5e3b2fSeh146360 "ipw2200_cmd(): cmd-done=%s\n", sc->sc_done[idx] ? "yes" : "no")); 897bb5e3b2fSeh146360 898bb5e3b2fSeh146360 if (sc->sc_done[idx] == 0) 899bb5e3b2fSeh146360 return (DDI_FAILURE); 900bb5e3b2fSeh146360 901bb5e3b2fSeh146360 out: 902bb5e3b2fSeh146360 return (DDI_SUCCESS); 903bb5e3b2fSeh146360 } 904bb5e3b2fSeh146360 905bb5e3b2fSeh146360 /* 906bb5e3b2fSeh146360 * If init failed, it will call stop internally. Therefore, it's unnecessary 907bb5e3b2fSeh146360 * to call ipw2200_stop() when this subroutine is failed. Otherwise, it may 908bb5e3b2fSeh146360 * be called twice. 909bb5e3b2fSeh146360 */ 910bb5e3b2fSeh146360 int 911bb5e3b2fSeh146360 ipw2200_init(struct ipw2200_softc *sc) 912bb5e3b2fSeh146360 { 913bb5e3b2fSeh146360 int err; 914bb5e3b2fSeh146360 915bb5e3b2fSeh146360 /* 916bb5e3b2fSeh146360 * No firmware is available, failed 917bb5e3b2fSeh146360 */ 918bb5e3b2fSeh146360 if (!(sc->sc_flags & IPW2200_FLAG_FW_CACHED)) { 919bb5e3b2fSeh146360 IPW2200_WARN((sc->sc_dip, CE_WARN, 920bb5e3b2fSeh146360 "ipw2200_init(): no firmware is available\n")); 921bb5e3b2fSeh146360 return (DDI_FAILURE); /* return directly */ 922bb5e3b2fSeh146360 } 923bb5e3b2fSeh146360 924bb5e3b2fSeh146360 ipw2200_stop(sc); 925bb5e3b2fSeh146360 926bb5e3b2fSeh146360 err = ipw2200_chip_reset(sc); 927bb5e3b2fSeh146360 if (err != DDI_SUCCESS) { 928bb5e3b2fSeh146360 IPW2200_WARN((sc->sc_dip, CE_WARN, 929bb5e3b2fSeh146360 "ipw2200_init(): could not reset adapter\n")); 930bb5e3b2fSeh146360 goto fail; 931bb5e3b2fSeh146360 } 932bb5e3b2fSeh146360 933bb5e3b2fSeh146360 /* 934bb5e3b2fSeh146360 * Load boot code 935bb5e3b2fSeh146360 */ 936bb5e3b2fSeh146360 err = ipw2200_load_fw(sc, sc->sc_fw.boot_base, sc->sc_fw.boot_size); 937bb5e3b2fSeh146360 if (err != DDI_SUCCESS) { 938bb5e3b2fSeh146360 IPW2200_WARN((sc->sc_dip, CE_WARN, 939bb5e3b2fSeh146360 "ipw2200_init(): could not load boot code\n")); 940bb5e3b2fSeh146360 goto fail; 941bb5e3b2fSeh146360 } 942bb5e3b2fSeh146360 943bb5e3b2fSeh146360 /* 944bb5e3b2fSeh146360 * Load boot microcode 945bb5e3b2fSeh146360 */ 946bb5e3b2fSeh146360 err = ipw2200_load_uc(sc, sc->sc_fw.uc_base, sc->sc_fw.uc_size); 947bb5e3b2fSeh146360 if (err != DDI_SUCCESS) { 948bb5e3b2fSeh146360 IPW2200_WARN((sc->sc_dip, CE_WARN, 949bb5e3b2fSeh146360 "ipw2200_init(): could not load microcode\n")); 950bb5e3b2fSeh146360 goto fail; 951bb5e3b2fSeh146360 } 952bb5e3b2fSeh146360 953bb5e3b2fSeh146360 ipw2200_master_stop(sc); 954bb5e3b2fSeh146360 ipw2200_ring_hwsetup(sc); 955bb5e3b2fSeh146360 956bb5e3b2fSeh146360 /* 957bb5e3b2fSeh146360 * Load firmware 958bb5e3b2fSeh146360 */ 959bb5e3b2fSeh146360 err = ipw2200_load_fw(sc, sc->sc_fw.fw_base, sc->sc_fw.fw_size); 960bb5e3b2fSeh146360 if (err != DDI_SUCCESS) { 961bb5e3b2fSeh146360 IPW2200_WARN((sc->sc_dip, CE_WARN, 962bb5e3b2fSeh146360 "ipw2200_init(): could not load firmware\n")); 963bb5e3b2fSeh146360 goto fail; 964bb5e3b2fSeh146360 } 965bb5e3b2fSeh146360 966bb5e3b2fSeh146360 sc->sc_flags |= IPW2200_FLAG_FW_INITED; 967bb5e3b2fSeh146360 968bb5e3b2fSeh146360 /* 969bb5e3b2fSeh146360 * Hardware will be enabled after configuration 970bb5e3b2fSeh146360 */ 971bb5e3b2fSeh146360 err = ipw2200_config(sc); 972bb5e3b2fSeh146360 if (err != DDI_SUCCESS) { 973bb5e3b2fSeh146360 IPW2200_WARN((sc->sc_dip, CE_WARN, 974bb5e3b2fSeh146360 "ipw2200_init(): device configuration failed\n")); 975bb5e3b2fSeh146360 goto fail; 976bb5e3b2fSeh146360 } 977bb5e3b2fSeh146360 978bb5e3b2fSeh146360 /* 979bb5e3b2fSeh146360 * workround to prevent too many h/w error. 980bb5e3b2fSeh146360 * delay for a while till h/w is stable. 981bb5e3b2fSeh146360 */ 982bb5e3b2fSeh146360 delay(drv_usectohz(delay_config_stable)); 983bb5e3b2fSeh146360 984bb5e3b2fSeh146360 return (DDI_SUCCESS); /* return successfully */ 985bb5e3b2fSeh146360 fail: 986bb5e3b2fSeh146360 ipw2200_stop(sc); 987bb5e3b2fSeh146360 return (err); 988bb5e3b2fSeh146360 } 989bb5e3b2fSeh146360 990bb5e3b2fSeh146360 /* 991bb5e3b2fSeh146360 * get hardware configurations from EEPROM embedded within PRO/2200 992bb5e3b2fSeh146360 */ 993bb5e3b2fSeh146360 static void 994bb5e3b2fSeh146360 ipw2200_hwconf_get(struct ipw2200_softc *sc) 995bb5e3b2fSeh146360 { 996bb5e3b2fSeh146360 int i; 997bb5e3b2fSeh146360 uint16_t val; 998bb5e3b2fSeh146360 999bb5e3b2fSeh146360 /* 1000bb5e3b2fSeh146360 * Get mac address 1001bb5e3b2fSeh146360 */ 1002bb5e3b2fSeh146360 i = 0; 1003bb5e3b2fSeh146360 val = ipw2200_rom_get16(sc, IPW2200_EEPROM_MAC + 0); 1004bb5e3b2fSeh146360 sc->sc_macaddr[i++] = val >> 8; 1005bb5e3b2fSeh146360 sc->sc_macaddr[i++] = val & 0xff; 1006bb5e3b2fSeh146360 val = ipw2200_rom_get16(sc, IPW2200_EEPROM_MAC + 1); 1007bb5e3b2fSeh146360 sc->sc_macaddr[i++] = val >> 8; 1008bb5e3b2fSeh146360 sc->sc_macaddr[i++] = val & 0xff; 1009bb5e3b2fSeh146360 val = ipw2200_rom_get16(sc, IPW2200_EEPROM_MAC + 2); 1010bb5e3b2fSeh146360 sc->sc_macaddr[i++] = val >> 8; 1011bb5e3b2fSeh146360 sc->sc_macaddr[i++] = val & 0xff; 1012bb5e3b2fSeh146360 1013bb5e3b2fSeh146360 /* 1014bb5e3b2fSeh146360 * formatted MAC address string 1015bb5e3b2fSeh146360 */ 1016bb5e3b2fSeh146360 (void) snprintf(sc->sc_macstr, sizeof (sc->sc_macstr), 1017bb5e3b2fSeh146360 "%02x:%02x:%02x:%02x:%02x:%02x", 1018bb5e3b2fSeh146360 sc->sc_macaddr[0], sc->sc_macaddr[1], 1019bb5e3b2fSeh146360 sc->sc_macaddr[2], sc->sc_macaddr[3], 1020bb5e3b2fSeh146360 sc->sc_macaddr[4], sc->sc_macaddr[5]); 1021bb5e3b2fSeh146360 1022bb5e3b2fSeh146360 } 1023bb5e3b2fSeh146360 1024bb5e3b2fSeh146360 /* 1025bb5e3b2fSeh146360 * all ipw2200 interrupts will be masked by this routine 1026bb5e3b2fSeh146360 */ 1027bb5e3b2fSeh146360 static void 1028bb5e3b2fSeh146360 ipw2200_master_stop(struct ipw2200_softc *sc) 1029bb5e3b2fSeh146360 { 1030bb5e3b2fSeh146360 int ntries; 1031bb5e3b2fSeh146360 1032bb5e3b2fSeh146360 /* 1033bb5e3b2fSeh146360 * disable interrupts 1034bb5e3b2fSeh146360 */ 1035bb5e3b2fSeh146360 ipw2200_csr_put32(sc, IPW2200_CSR_INTR_MASK, 0); 1036bb5e3b2fSeh146360 ipw2200_csr_put32(sc, IPW2200_CSR_RST, IPW2200_RST_STOP_MASTER); 1037bb5e3b2fSeh146360 1038bb5e3b2fSeh146360 /* 1039bb5e3b2fSeh146360 * wait long enough to ensure hardware stop successfully. 1040bb5e3b2fSeh146360 */ 1041bb5e3b2fSeh146360 for (ntries = 0; ntries < 500; ntries++) { 1042bb5e3b2fSeh146360 if (ipw2200_csr_get32(sc, IPW2200_CSR_RST) & 1043bb5e3b2fSeh146360 IPW2200_RST_MASTER_DISABLED) 1044bb5e3b2fSeh146360 break; 1045bb5e3b2fSeh146360 /* wait for a while */ 1046bb5e3b2fSeh146360 drv_usecwait(100); 1047bb5e3b2fSeh146360 } 1048bb5e3b2fSeh146360 if (ntries == 500) 1049bb5e3b2fSeh146360 IPW2200_WARN((sc->sc_dip, CE_WARN, 1050bb5e3b2fSeh146360 "ipw2200_master_stop(): timeout\n")); 1051bb5e3b2fSeh146360 1052bb5e3b2fSeh146360 ipw2200_csr_put32(sc, IPW2200_CSR_RST, 1053bb5e3b2fSeh146360 IPW2200_RST_PRINCETON_RESET | 1054bb5e3b2fSeh146360 ipw2200_csr_get32(sc, IPW2200_CSR_RST)); 1055bb5e3b2fSeh146360 1056bb5e3b2fSeh146360 sc->sc_flags &= ~IPW2200_FLAG_FW_INITED; 1057bb5e3b2fSeh146360 } 1058bb5e3b2fSeh146360 1059bb5e3b2fSeh146360 /* 1060bb5e3b2fSeh146360 * all ipw2200 interrupts will be masked by this routine 1061bb5e3b2fSeh146360 */ 1062bb5e3b2fSeh146360 static int 1063bb5e3b2fSeh146360 ipw2200_chip_reset(struct ipw2200_softc *sc) 1064bb5e3b2fSeh146360 { 1065bb5e3b2fSeh146360 uint32_t tmp; 1066bb5e3b2fSeh146360 int ntries, i; 1067bb5e3b2fSeh146360 1068bb5e3b2fSeh146360 ipw2200_master_stop(sc); 1069bb5e3b2fSeh146360 1070bb5e3b2fSeh146360 /* 1071bb5e3b2fSeh146360 * Move adapter to DO state 1072bb5e3b2fSeh146360 */ 1073bb5e3b2fSeh146360 tmp = ipw2200_csr_get32(sc, IPW2200_CSR_CTL); 1074bb5e3b2fSeh146360 ipw2200_csr_put32(sc, IPW2200_CSR_CTL, tmp | IPW2200_CTL_INIT); 1075bb5e3b2fSeh146360 1076bb5e3b2fSeh146360 /* 1077bb5e3b2fSeh146360 * Initialize Phase-Locked Level (PLL) 1078bb5e3b2fSeh146360 */ 1079bb5e3b2fSeh146360 ipw2200_csr_put32(sc, IPW2200_CSR_READ_INT, IPW2200_READ_INT_INIT_HOST); 1080bb5e3b2fSeh146360 1081bb5e3b2fSeh146360 /* 1082bb5e3b2fSeh146360 * Wait for clock stabilization 1083bb5e3b2fSeh146360 */ 1084bb5e3b2fSeh146360 for (ntries = 0; ntries < 1000; ntries++) { 1085bb5e3b2fSeh146360 if (ipw2200_csr_get32(sc, IPW2200_CSR_CTL) & 1086bb5e3b2fSeh146360 IPW2200_CTL_CLOCK_READY) 1087bb5e3b2fSeh146360 break; 1088bb5e3b2fSeh146360 drv_usecwait(200); 1089bb5e3b2fSeh146360 } 1090bb5e3b2fSeh146360 if (ntries == 1000) { 1091bb5e3b2fSeh146360 IPW2200_WARN((sc->sc_dip, CE_WARN, 1092bb5e3b2fSeh146360 "ipw2200_chip_reset(): timeout\n")); 1093bb5e3b2fSeh146360 return (DDI_FAILURE); 1094bb5e3b2fSeh146360 } 1095bb5e3b2fSeh146360 1096bb5e3b2fSeh146360 tmp = ipw2200_csr_get32(sc, IPW2200_CSR_RST); 1097bb5e3b2fSeh146360 ipw2200_csr_put32(sc, IPW2200_CSR_RST, tmp | IPW2200_RST_SW_RESET); 1098bb5e3b2fSeh146360 1099bb5e3b2fSeh146360 drv_usecwait(10); 1100bb5e3b2fSeh146360 1101bb5e3b2fSeh146360 tmp = ipw2200_csr_get32(sc, IPW2200_CSR_CTL); 1102bb5e3b2fSeh146360 ipw2200_csr_put32(sc, IPW2200_CSR_CTL, tmp | IPW2200_CTL_INIT); 1103bb5e3b2fSeh146360 1104bb5e3b2fSeh146360 /* 1105bb5e3b2fSeh146360 * clear NIC memory 1106bb5e3b2fSeh146360 */ 1107bb5e3b2fSeh146360 ipw2200_csr_put32(sc, IPW2200_CSR_AUTOINC_ADDR, 0); 1108bb5e3b2fSeh146360 for (i = 0; i < 0xc000; i++) 1109bb5e3b2fSeh146360 ipw2200_csr_put32(sc, IPW2200_CSR_AUTOINC_DATA, 0); 1110bb5e3b2fSeh146360 1111bb5e3b2fSeh146360 return (DDI_SUCCESS); 1112bb5e3b2fSeh146360 } 1113bb5e3b2fSeh146360 1114bb5e3b2fSeh146360 /* 1115bb5e3b2fSeh146360 * This function is used by wificonfig/dladm to get the current 1116bb5e3b2fSeh146360 * radio status, it is off/on 1117bb5e3b2fSeh146360 */ 1118bb5e3b2fSeh146360 int 1119bb5e3b2fSeh146360 ipw2200_radio_status(struct ipw2200_softc *sc) 1120bb5e3b2fSeh146360 { 1121bb5e3b2fSeh146360 int val; 1122bb5e3b2fSeh146360 1123bb5e3b2fSeh146360 val = (ipw2200_csr_get32(sc, IPW2200_CSR_IO) & 1124bb5e3b2fSeh146360 IPW2200_IO_RADIO_ENABLED) ? 1 : 0; 1125bb5e3b2fSeh146360 1126bb5e3b2fSeh146360 return (val); 1127bb5e3b2fSeh146360 } 1128bb5e3b2fSeh146360 /* 1129bb5e3b2fSeh146360 * This function is used to get the statistic 1130bb5e3b2fSeh146360 */ 1131bb5e3b2fSeh146360 void 1132bb5e3b2fSeh146360 ipw2200_get_statistics(struct ipw2200_softc *sc) 1133bb5e3b2fSeh146360 { 1134bb5e3b2fSeh146360 struct ieee80211com *ic = &sc->sc_ic; 1135bb5e3b2fSeh146360 1136bb5e3b2fSeh146360 uint32_t size, buf[128]; 1137bb5e3b2fSeh146360 1138bb5e3b2fSeh146360 if (!(sc->sc_flags & IPW2200_FLAG_FW_INITED)) { 1139bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_IOCTL, (sc->sc_dip, CE_CONT, 1140bb5e3b2fSeh146360 "ipw2200_get_statistic(): fw doesn't download yet.")); 1141bb5e3b2fSeh146360 return; 1142bb5e3b2fSeh146360 } 1143bb5e3b2fSeh146360 1144bb5e3b2fSeh146360 size = min(ipw2200_csr_get32(sc, IPW2200_CSR_TABLE0_SIZE), 128 - 1); 1145bb5e3b2fSeh146360 ipw2200_csr_getbuf32(sc, IPW2200_CSR_TABLE0_BASE, &buf[1], size); 1146bb5e3b2fSeh146360 1147bb5e3b2fSeh146360 /* 1148bb5e3b2fSeh146360 * To retrieve the statistic information into proper places. There are 1149bb5e3b2fSeh146360 * lot of information. These table will be read once a second. 1150bb5e3b2fSeh146360 * Hopefully, it will not effect the performance. 1151bb5e3b2fSeh146360 */ 1152bb5e3b2fSeh146360 1153bb5e3b2fSeh146360 /* 1154bb5e3b2fSeh146360 * For the tx/crc information, we can get them from chip directly; 1155bb5e3b2fSeh146360 * For the rx/wep error/(rts) related information, leave them net80211. 1156bb5e3b2fSeh146360 */ 1157bb5e3b2fSeh146360 /* WIFI_STAT_TX_FRAGS */ 1158bb5e3b2fSeh146360 ic->ic_stats.is_tx_frags = (uint32_t)buf[5]; 1159bb5e3b2fSeh146360 /* WIFI_STAT_MCAST_TX */ 1160bb5e3b2fSeh146360 ic->ic_stats.is_tx_mcast = (uint32_t)buf[31]; 1161bb5e3b2fSeh146360 /* WIFI_STAT_TX_RETRANS */ 1162bb5e3b2fSeh146360 ic->ic_stats.is_tx_retries = (uint32_t)buf[56]; 1163bb5e3b2fSeh146360 /* WIFI_STAT_TX_FAILED */ 1164bb5e3b2fSeh146360 ic->ic_stats.is_tx_failed = (uint32_t)buf[57]; 1165bb5e3b2fSeh146360 /* MAC_STAT_OBYTES */ 1166bb5e3b2fSeh146360 ic->ic_stats.is_tx_bytes = (uint32_t)buf[64]; 1167bb5e3b2fSeh146360 } 1168bb5e3b2fSeh146360 1169bb5e3b2fSeh146360 /* 1170bb5e3b2fSeh146360 * DMA region alloc subroutine 1171bb5e3b2fSeh146360 */ 1172bb5e3b2fSeh146360 int 1173bb5e3b2fSeh146360 ipw2200_dma_region_alloc(struct ipw2200_softc *sc, struct dma_region *dr, 1174bb5e3b2fSeh146360 size_t size, uint_t dir, uint_t flags) 1175bb5e3b2fSeh146360 { 1176bb5e3b2fSeh146360 dev_info_t *dip = sc->sc_dip; 1177bb5e3b2fSeh146360 int err; 1178bb5e3b2fSeh146360 1179bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_DMA, (sc->sc_dip, CE_CONT, 1180bb5e3b2fSeh146360 "ipw2200_dma_region_alloc(): size =%u\n", size)); 1181bb5e3b2fSeh146360 1182bb5e3b2fSeh146360 err = ddi_dma_alloc_handle(dip, &ipw2200_dma_attr, DDI_DMA_SLEEP, NULL, 1183bb5e3b2fSeh146360 &dr->dr_hnd); 1184bb5e3b2fSeh146360 if (err != DDI_SUCCESS) { 1185bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_DMA, (sc->sc_dip, CE_CONT, 1186bb5e3b2fSeh146360 "ipw2200_dma_region_alloc(): " 1187bb5e3b2fSeh146360 "ddi_dma_alloc_handle() failed\n")); 1188bb5e3b2fSeh146360 goto fail0; 1189bb5e3b2fSeh146360 } 1190bb5e3b2fSeh146360 1191bb5e3b2fSeh146360 err = ddi_dma_mem_alloc(dr->dr_hnd, size, &ipw2200_dma_accattr, 1192bb5e3b2fSeh146360 flags, DDI_DMA_SLEEP, NULL, 1193bb5e3b2fSeh146360 &dr->dr_base, &dr->dr_size, &dr->dr_acc); 1194bb5e3b2fSeh146360 if (err != DDI_SUCCESS) { 1195bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_DMA, (sc->sc_dip, CE_CONT, 1196bb5e3b2fSeh146360 "ipw2200_dma_region_alloc(): " 1197bb5e3b2fSeh146360 "ddi_dma_mem_alloc() failed\n")); 1198bb5e3b2fSeh146360 goto fail1; 1199bb5e3b2fSeh146360 } 1200bb5e3b2fSeh146360 1201bb5e3b2fSeh146360 err = ddi_dma_addr_bind_handle(dr->dr_hnd, NULL, 1202bb5e3b2fSeh146360 dr->dr_base, dr->dr_size, 1203bb5e3b2fSeh146360 dir | flags, DDI_DMA_SLEEP, NULL, 1204bb5e3b2fSeh146360 &dr->dr_cookie, &dr->dr_ccnt); 1205bb5e3b2fSeh146360 if (err != DDI_DMA_MAPPED) { 1206bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_DMA, (sc->sc_dip, CE_CONT, 1207bb5e3b2fSeh146360 "ipw2200_dma_region_alloc(): " 1208bb5e3b2fSeh146360 "ddi_dma_addr_bind_handle() failed\n")); 1209bb5e3b2fSeh146360 goto fail2; 1210bb5e3b2fSeh146360 } 1211bb5e3b2fSeh146360 1212bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_DMA, (sc->sc_dip, CE_CONT, 1213bb5e3b2fSeh146360 "ipw2200_dma_region_alloc(): ccnt=%u\n", dr->dr_ccnt)); 1214bb5e3b2fSeh146360 1215bb5e3b2fSeh146360 if (dr->dr_ccnt != 1) { 1216bb5e3b2fSeh146360 err = DDI_FAILURE; 1217bb5e3b2fSeh146360 goto fail3; 1218bb5e3b2fSeh146360 } 1219bb5e3b2fSeh146360 1220bb5e3b2fSeh146360 dr->dr_pbase = dr->dr_cookie.dmac_address; 1221bb5e3b2fSeh146360 1222bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_DMA, (sc->sc_dip, CE_CONT, 1223bb5e3b2fSeh146360 "ipw2200_dma_region_alloc(): get physical-base=0x%08x\n", 1224bb5e3b2fSeh146360 dr->dr_pbase)); 1225bb5e3b2fSeh146360 1226bb5e3b2fSeh146360 return (DDI_SUCCESS); 1227bb5e3b2fSeh146360 1228bb5e3b2fSeh146360 fail3: 1229bb5e3b2fSeh146360 (void) ddi_dma_unbind_handle(dr->dr_hnd); 1230bb5e3b2fSeh146360 fail2: 1231bb5e3b2fSeh146360 ddi_dma_mem_free(&dr->dr_acc); 1232bb5e3b2fSeh146360 fail1: 1233bb5e3b2fSeh146360 ddi_dma_free_handle(&dr->dr_hnd); 1234bb5e3b2fSeh146360 fail0: 1235bb5e3b2fSeh146360 return (err); 1236bb5e3b2fSeh146360 } 1237bb5e3b2fSeh146360 1238bb5e3b2fSeh146360 void 1239bb5e3b2fSeh146360 ipw2200_dma_region_free(struct dma_region *dr) 1240bb5e3b2fSeh146360 { 1241bb5e3b2fSeh146360 (void) ddi_dma_unbind_handle(dr->dr_hnd); 1242bb5e3b2fSeh146360 ddi_dma_mem_free(&dr->dr_acc); 1243bb5e3b2fSeh146360 ddi_dma_free_handle(&dr->dr_hnd); 1244bb5e3b2fSeh146360 } 1245bb5e3b2fSeh146360 1246bb5e3b2fSeh146360 static int 1247bb5e3b2fSeh146360 ipw2200_ring_alloc(struct ipw2200_softc *sc) 1248bb5e3b2fSeh146360 { 1249bb5e3b2fSeh146360 int err, i; 1250bb5e3b2fSeh146360 1251bb5e3b2fSeh146360 /* 1252bb5e3b2fSeh146360 * tx desc ring 1253bb5e3b2fSeh146360 */ 1254bb5e3b2fSeh146360 sc->sc_dma_txdsc.dr_name = "ipw2200-tx-desc-ring"; 1255bb5e3b2fSeh146360 err = ipw2200_dma_region_alloc(sc, &sc->sc_dma_txdsc, 1256bb5e3b2fSeh146360 IPW2200_TX_RING_SIZE * sizeof (struct ipw2200_tx_desc), 1257bb5e3b2fSeh146360 DDI_DMA_WRITE, DDI_DMA_CONSISTENT); 1258bb5e3b2fSeh146360 if (err != DDI_SUCCESS) 1259bb5e3b2fSeh146360 goto fail0; 1260bb5e3b2fSeh146360 /* 1261bb5e3b2fSeh146360 * tx buffer array 1262bb5e3b2fSeh146360 */ 1263bb5e3b2fSeh146360 for (i = 0; i < IPW2200_TX_RING_SIZE; i++) { 1264bb5e3b2fSeh146360 sc->sc_dma_txbufs[i].dr_name = "ipw2200-tx-buf"; 1265bb5e3b2fSeh146360 err = ipw2200_dma_region_alloc(sc, &sc->sc_dma_txbufs[i], 1266bb5e3b2fSeh146360 IPW2200_TXBUF_SIZE, DDI_DMA_WRITE, DDI_DMA_STREAMING); 1267bb5e3b2fSeh146360 if (err != DDI_SUCCESS) { 1268bb5e3b2fSeh146360 while (i >= 0) { 1269bb5e3b2fSeh146360 ipw2200_dma_region_free(&sc->sc_dma_txbufs[i]); 1270bb5e3b2fSeh146360 i--; 1271bb5e3b2fSeh146360 } 1272bb5e3b2fSeh146360 goto fail1; 1273bb5e3b2fSeh146360 } 1274bb5e3b2fSeh146360 } 1275bb5e3b2fSeh146360 /* 1276bb5e3b2fSeh146360 * rx buffer array 1277bb5e3b2fSeh146360 */ 1278bb5e3b2fSeh146360 for (i = 0; i < IPW2200_RX_RING_SIZE; i++) { 1279bb5e3b2fSeh146360 sc->sc_dma_rxbufs[i].dr_name = "ipw2200-rx-buf"; 1280bb5e3b2fSeh146360 err = ipw2200_dma_region_alloc(sc, &sc->sc_dma_rxbufs[i], 1281bb5e3b2fSeh146360 IPW2200_RXBUF_SIZE, DDI_DMA_READ, DDI_DMA_STREAMING); 1282bb5e3b2fSeh146360 if (err != DDI_SUCCESS) { 1283bb5e3b2fSeh146360 while (i >= 0) { 1284bb5e3b2fSeh146360 ipw2200_dma_region_free(&sc->sc_dma_rxbufs[i]); 1285bb5e3b2fSeh146360 i--; 1286bb5e3b2fSeh146360 } 1287bb5e3b2fSeh146360 goto fail2; 1288bb5e3b2fSeh146360 } 1289bb5e3b2fSeh146360 } 1290bb5e3b2fSeh146360 /* 1291bb5e3b2fSeh146360 * cmd desc ring 1292bb5e3b2fSeh146360 */ 1293bb5e3b2fSeh146360 sc->sc_dma_cmdsc.dr_name = "ipw2200-cmd-desc-ring"; 1294bb5e3b2fSeh146360 err = ipw2200_dma_region_alloc(sc, &sc->sc_dma_cmdsc, 1295bb5e3b2fSeh146360 IPW2200_CMD_RING_SIZE * sizeof (struct ipw2200_cmd_desc), 1296bb5e3b2fSeh146360 DDI_DMA_WRITE, DDI_DMA_CONSISTENT); 1297bb5e3b2fSeh146360 if (err != DDI_SUCCESS) 1298bb5e3b2fSeh146360 goto fail3; 1299bb5e3b2fSeh146360 1300bb5e3b2fSeh146360 return (DDI_SUCCESS); 1301bb5e3b2fSeh146360 1302bb5e3b2fSeh146360 fail3: 1303bb5e3b2fSeh146360 for (i = 0; i < IPW2200_RX_RING_SIZE; i++) 1304bb5e3b2fSeh146360 ipw2200_dma_region_free(&sc->sc_dma_rxbufs[i]); 1305bb5e3b2fSeh146360 fail2: 1306bb5e3b2fSeh146360 for (i = 0; i < IPW2200_TX_RING_SIZE; i++) 1307bb5e3b2fSeh146360 ipw2200_dma_region_free(&sc->sc_dma_txbufs[i]); 1308bb5e3b2fSeh146360 fail1: 1309bb5e3b2fSeh146360 ipw2200_dma_region_free(&sc->sc_dma_txdsc); 1310bb5e3b2fSeh146360 fail0: 1311bb5e3b2fSeh146360 return (err); 1312bb5e3b2fSeh146360 } 1313bb5e3b2fSeh146360 1314bb5e3b2fSeh146360 static void 1315bb5e3b2fSeh146360 ipw2200_ring_free(struct ipw2200_softc *sc) 1316bb5e3b2fSeh146360 { 1317bb5e3b2fSeh146360 int i; 1318bb5e3b2fSeh146360 1319bb5e3b2fSeh146360 /* 1320bb5e3b2fSeh146360 * tx ring desc 1321bb5e3b2fSeh146360 */ 1322bb5e3b2fSeh146360 ipw2200_dma_region_free(&sc->sc_dma_txdsc); 1323bb5e3b2fSeh146360 /* 1324bb5e3b2fSeh146360 * tx buf 1325bb5e3b2fSeh146360 */ 1326bb5e3b2fSeh146360 for (i = 0; i < IPW2200_TX_RING_SIZE; i++) 1327bb5e3b2fSeh146360 ipw2200_dma_region_free(&sc->sc_dma_txbufs[i]); 1328bb5e3b2fSeh146360 /* 1329bb5e3b2fSeh146360 * rx buf 1330bb5e3b2fSeh146360 */ 1331bb5e3b2fSeh146360 for (i = 0; i < IPW2200_RX_RING_SIZE; i++) 1332bb5e3b2fSeh146360 ipw2200_dma_region_free(&sc->sc_dma_rxbufs[i]); 1333bb5e3b2fSeh146360 /* 1334bb5e3b2fSeh146360 * command ring desc 1335bb5e3b2fSeh146360 */ 1336bb5e3b2fSeh146360 ipw2200_dma_region_free(&sc->sc_dma_cmdsc); 1337bb5e3b2fSeh146360 } 1338bb5e3b2fSeh146360 1339bb5e3b2fSeh146360 static void 1340bb5e3b2fSeh146360 ipw2200_ring_reset(struct ipw2200_softc *sc) 1341bb5e3b2fSeh146360 { 1342bb5e3b2fSeh146360 int i; 1343bb5e3b2fSeh146360 1344bb5e3b2fSeh146360 /* 1345bb5e3b2fSeh146360 * tx desc ring & buffer array 1346bb5e3b2fSeh146360 */ 1347bb5e3b2fSeh146360 sc->sc_tx_cur = 0; 1348bb5e3b2fSeh146360 sc->sc_tx_free = IPW2200_TX_RING_SIZE; 1349bb5e3b2fSeh146360 sc->sc_txdsc = (struct ipw2200_tx_desc *)sc->sc_dma_txdsc.dr_base; 1350bb5e3b2fSeh146360 for (i = 0; i < IPW2200_TX_RING_SIZE; i++) 1351bb5e3b2fSeh146360 sc->sc_txbufs[i] = (uint8_t *)sc->sc_dma_txbufs[i].dr_base; 1352bb5e3b2fSeh146360 /* 1353bb5e3b2fSeh146360 * rx buffer array 1354bb5e3b2fSeh146360 */ 1355bb5e3b2fSeh146360 sc->sc_rx_cur = 0; 1356bb5e3b2fSeh146360 sc->sc_rx_free = IPW2200_RX_RING_SIZE; 1357bb5e3b2fSeh146360 for (i = 0; i < IPW2200_RX_RING_SIZE; i++) 1358bb5e3b2fSeh146360 sc->sc_rxbufs[i] = (uint8_t *)sc->sc_dma_rxbufs[i].dr_base; 1359bb5e3b2fSeh146360 1360bb5e3b2fSeh146360 /* 1361bb5e3b2fSeh146360 * command desc ring 1362bb5e3b2fSeh146360 */ 1363bb5e3b2fSeh146360 sc->sc_cmd_cur = 0; 1364bb5e3b2fSeh146360 sc->sc_cmd_free = IPW2200_CMD_RING_SIZE; 1365bb5e3b2fSeh146360 sc->sc_cmdsc = (struct ipw2200_cmd_desc *)sc->sc_dma_cmdsc.dr_base; 1366bb5e3b2fSeh146360 } 1367bb5e3b2fSeh146360 1368bb5e3b2fSeh146360 /* 1369bb5e3b2fSeh146360 * tx, rx rings and command initialization 1370bb5e3b2fSeh146360 */ 1371bb5e3b2fSeh146360 static int 1372bb5e3b2fSeh146360 ipw2200_ring_init(struct ipw2200_softc *sc) 1373bb5e3b2fSeh146360 { 1374bb5e3b2fSeh146360 int err; 1375bb5e3b2fSeh146360 1376bb5e3b2fSeh146360 err = ipw2200_ring_alloc(sc); 1377bb5e3b2fSeh146360 if (err != DDI_SUCCESS) 1378bb5e3b2fSeh146360 return (err); 1379bb5e3b2fSeh146360 1380bb5e3b2fSeh146360 ipw2200_ring_reset(sc); 1381bb5e3b2fSeh146360 1382bb5e3b2fSeh146360 return (DDI_SUCCESS); 1383bb5e3b2fSeh146360 } 1384bb5e3b2fSeh146360 1385bb5e3b2fSeh146360 static void 1386bb5e3b2fSeh146360 ipw2200_ring_hwsetup(struct ipw2200_softc *sc) 1387bb5e3b2fSeh146360 { 1388bb5e3b2fSeh146360 int i; 1389bb5e3b2fSeh146360 1390bb5e3b2fSeh146360 /* 1391bb5e3b2fSeh146360 * command desc ring 1392bb5e3b2fSeh146360 */ 1393bb5e3b2fSeh146360 ipw2200_csr_put32(sc, IPW2200_CSR_CMD_BASE, sc->sc_dma_cmdsc.dr_pbase); 1394bb5e3b2fSeh146360 ipw2200_csr_put32(sc, IPW2200_CSR_CMD_SIZE, IPW2200_CMD_RING_SIZE); 1395bb5e3b2fSeh146360 ipw2200_csr_put32(sc, IPW2200_CSR_CMD_WRITE_INDEX, sc->sc_cmd_cur); 1396bb5e3b2fSeh146360 1397bb5e3b2fSeh146360 /* 1398bb5e3b2fSeh146360 * tx desc ring. only tx1 is used, tx2, tx3, and tx4 are unused 1399bb5e3b2fSeh146360 */ 1400bb5e3b2fSeh146360 ipw2200_csr_put32(sc, IPW2200_CSR_TX1_BASE, sc->sc_dma_txdsc.dr_pbase); 1401bb5e3b2fSeh146360 ipw2200_csr_put32(sc, IPW2200_CSR_TX1_SIZE, IPW2200_TX_RING_SIZE); 1402bb5e3b2fSeh146360 ipw2200_csr_put32(sc, IPW2200_CSR_TX1_WRITE_INDEX, sc->sc_tx_cur); 1403bb5e3b2fSeh146360 1404bb5e3b2fSeh146360 /* 1405bb5e3b2fSeh146360 * tx2, tx3, tx4 is not used 1406bb5e3b2fSeh146360 */ 1407bb5e3b2fSeh146360 ipw2200_csr_put32(sc, IPW2200_CSR_TX2_BASE, sc->sc_dma_txdsc.dr_pbase); 1408bb5e3b2fSeh146360 ipw2200_csr_put32(sc, IPW2200_CSR_TX2_SIZE, IPW2200_TX_RING_SIZE); 1409bb5e3b2fSeh146360 ipw2200_csr_put32(sc, IPW2200_CSR_TX2_READ_INDEX, 0); 1410bb5e3b2fSeh146360 ipw2200_csr_put32(sc, IPW2200_CSR_TX2_WRITE_INDEX, 0); 1411bb5e3b2fSeh146360 ipw2200_csr_put32(sc, IPW2200_CSR_TX3_BASE, sc->sc_dma_txdsc.dr_pbase); 1412bb5e3b2fSeh146360 ipw2200_csr_put32(sc, IPW2200_CSR_TX3_SIZE, IPW2200_TX_RING_SIZE); 1413bb5e3b2fSeh146360 ipw2200_csr_put32(sc, IPW2200_CSR_TX3_READ_INDEX, 0); 1414bb5e3b2fSeh146360 ipw2200_csr_put32(sc, IPW2200_CSR_TX3_WRITE_INDEX, 0); 1415bb5e3b2fSeh146360 ipw2200_csr_put32(sc, IPW2200_CSR_TX4_BASE, sc->sc_dma_txdsc.dr_pbase); 1416bb5e3b2fSeh146360 ipw2200_csr_put32(sc, IPW2200_CSR_TX4_SIZE, IPW2200_TX_RING_SIZE); 1417bb5e3b2fSeh146360 ipw2200_csr_put32(sc, IPW2200_CSR_TX4_READ_INDEX, 0); 1418bb5e3b2fSeh146360 ipw2200_csr_put32(sc, IPW2200_CSR_TX4_WRITE_INDEX, 0); 1419bb5e3b2fSeh146360 1420bb5e3b2fSeh146360 /* 1421bb5e3b2fSeh146360 * rx buffer ring 1422bb5e3b2fSeh146360 */ 1423bb5e3b2fSeh146360 for (i = 0; i < IPW2200_RX_RING_SIZE; i++) 1424bb5e3b2fSeh146360 ipw2200_csr_put32(sc, IPW2200_CSR_RX_BASE + i * 4, 1425bb5e3b2fSeh146360 sc->sc_dma_rxbufs[i].dr_pbase); 1426bb5e3b2fSeh146360 /* 1427bb5e3b2fSeh146360 * all rx buffer are empty, rx-rd-index == 0 && rx-wr-index == N-1 1428bb5e3b2fSeh146360 */ 1429bb5e3b2fSeh146360 ipw2200_csr_put32(sc, IPW2200_CSR_RX_WRITE_INDEX, 1430bb5e3b2fSeh146360 RING_BACKWARD(sc->sc_rx_cur, 1, IPW2200_RX_RING_SIZE)); 1431bb5e3b2fSeh146360 } 1432bb5e3b2fSeh146360 1433bb5e3b2fSeh146360 int 1434bb5e3b2fSeh146360 ipw2200_start_scan(struct ipw2200_softc *sc) 1435bb5e3b2fSeh146360 { 1436bb5e3b2fSeh146360 struct ieee80211com *ic = &sc->sc_ic; 1437bb5e3b2fSeh146360 struct ipw2200_scan scan; 1438bb5e3b2fSeh146360 uint8_t *ch; 1439bb5e3b2fSeh146360 int cnt, i; 1440bb5e3b2fSeh146360 1441bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_SCAN, (sc->sc_dip, CE_CONT, 1442bb5e3b2fSeh146360 "ipw2200_start_scan(): start scanning \n")); 1443bb5e3b2fSeh146360 1444bb5e3b2fSeh146360 /* 1445bb5e3b2fSeh146360 * start scanning 1446bb5e3b2fSeh146360 */ 1447bb5e3b2fSeh146360 sc->sc_flags |= IPW2200_FLAG_SCANNING; 1448bb5e3b2fSeh146360 1449bb5e3b2fSeh146360 (void) memset(&scan, 0, sizeof (scan)); 1450bb5e3b2fSeh146360 scan.type = (ic->ic_des_esslen != 0) ? IPW2200_SCAN_TYPE_BDIRECTED : 1451bb5e3b2fSeh146360 IPW2200_SCAN_TYPE_BROADCAST; 1452bb5e3b2fSeh146360 scan.dwelltime = LE_16(40); /* The interval is set up to 40 */ 1453bb5e3b2fSeh146360 1454bb5e3b2fSeh146360 /* 1455bb5e3b2fSeh146360 * Compact supported channel number(5G) into a single buffer 1456bb5e3b2fSeh146360 */ 1457bb5e3b2fSeh146360 ch = scan.channels; 1458bb5e3b2fSeh146360 cnt = 0; 1459bb5e3b2fSeh146360 for (i = 0; i <= IEEE80211_CHAN_MAX; i++) { 1460bb5e3b2fSeh146360 if (IEEE80211_IS_CHAN_5GHZ(&ic->ic_sup_channels[i]) && 1461bb5e3b2fSeh146360 isset(ic->ic_chan_active, i)) { 1462bb5e3b2fSeh146360 *++ch = (uint8_t)i; 1463bb5e3b2fSeh146360 cnt++; 1464bb5e3b2fSeh146360 } 1465bb5e3b2fSeh146360 } 1466bb5e3b2fSeh146360 *(ch - cnt) = IPW2200_CHAN_5GHZ | (uint8_t)cnt; 14673b608f65Sql147931 ch = (cnt > 0) ? (ch + 1) : (scan.channels); 1468bb5e3b2fSeh146360 1469bb5e3b2fSeh146360 /* 1470bb5e3b2fSeh146360 * Compact supported channel number(2G) into a single buffer 1471bb5e3b2fSeh146360 */ 1472bb5e3b2fSeh146360 cnt = 0; 1473bb5e3b2fSeh146360 for (i = 0; i <= IEEE80211_CHAN_MAX; i++) { 1474bb5e3b2fSeh146360 if (IEEE80211_IS_CHAN_2GHZ(&ic->ic_sup_channels[i]) && 1475bb5e3b2fSeh146360 isset(ic->ic_chan_active, i)) { 1476bb5e3b2fSeh146360 *++ch = (uint8_t)i; 1477bb5e3b2fSeh146360 cnt++; 1478bb5e3b2fSeh146360 } 1479bb5e3b2fSeh146360 } 1480bb5e3b2fSeh146360 *(ch - cnt) = IPW2200_CHAN_2GHZ | cnt; 1481bb5e3b2fSeh146360 1482bb5e3b2fSeh146360 return (ipw2200_cmd(sc, IPW2200_CMD_SCAN, &scan, sizeof (scan), 1)); 1483bb5e3b2fSeh146360 } 1484bb5e3b2fSeh146360 1485bb5e3b2fSeh146360 int 1486bb5e3b2fSeh146360 ipw2200_auth_and_assoc(struct ipw2200_softc *sc) 1487bb5e3b2fSeh146360 { 1488bb5e3b2fSeh146360 struct ieee80211com *ic = &sc->sc_ic; 1489bb5e3b2fSeh146360 struct ieee80211_node *in = ic->ic_bss; 1490bb5e3b2fSeh146360 struct ipw2200_configuration cfg; 1491bb5e3b2fSeh146360 struct ipw2200_rateset rs; 1492bb5e3b2fSeh146360 struct ipw2200_associate assoc; 1493bb5e3b2fSeh146360 uint32_t data; 1494bb5e3b2fSeh146360 int err; 1495bb5e3b2fSeh146360 1496bb5e3b2fSeh146360 /* 1497bb5e3b2fSeh146360 * set the confiuration 1498bb5e3b2fSeh146360 */ 1499bb5e3b2fSeh146360 if (IEEE80211_IS_CHAN_2GHZ(in->in_chan)) { 1500bb5e3b2fSeh146360 /* enable b/g auto-detection */ 1501bb5e3b2fSeh146360 (void) memset(&cfg, 0, sizeof (cfg)); 1502bb5e3b2fSeh146360 cfg.bluetooth_coexistence = 1; 1503bb5e3b2fSeh146360 cfg.multicast_enabled = 1; 1504bb5e3b2fSeh146360 cfg.use_protection = 1; 1505bb5e3b2fSeh146360 cfg.answer_pbreq = 1; 1506bb5e3b2fSeh146360 cfg.noise_reported = 1; 1507bb5e3b2fSeh146360 err = ipw2200_cmd(sc, IPW2200_CMD_SET_CONFIG, 1508bb5e3b2fSeh146360 &cfg, sizeof (cfg), 1); 1509bb5e3b2fSeh146360 if (err != DDI_SUCCESS) 1510bb5e3b2fSeh146360 return (err); 1511bb5e3b2fSeh146360 } 1512bb5e3b2fSeh146360 1513bb5e3b2fSeh146360 /* 1514bb5e3b2fSeh146360 * set the essid, may be null/hidden AP 1515bb5e3b2fSeh146360 */ 1516bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT, 1517bb5e3b2fSeh146360 "ipw2200_auth_and_assoc(): " 1518bb5e3b2fSeh146360 "setting ESSID to(%u),%c%c%c%c%c%c%c%c\n", 1519bb5e3b2fSeh146360 in->in_esslen, 1520bb5e3b2fSeh146360 in->in_essid[0], in->in_essid[1], 1521bb5e3b2fSeh146360 in->in_essid[2], in->in_essid[3], 1522bb5e3b2fSeh146360 in->in_essid[4], in->in_essid[5], 1523bb5e3b2fSeh146360 in->in_essid[6], in->in_essid[7])); 1524bb5e3b2fSeh146360 err = ipw2200_cmd(sc, IPW2200_CMD_SET_ESSID, in->in_essid, 1525bb5e3b2fSeh146360 in->in_esslen, 1); 1526bb5e3b2fSeh146360 if (err != DDI_SUCCESS) 1527bb5e3b2fSeh146360 return (err); 1528bb5e3b2fSeh146360 1529bb5e3b2fSeh146360 /* 1530bb5e3b2fSeh146360 * set the rate: the rate set has already been ''negocitated'' 1531bb5e3b2fSeh146360 */ 1532bb5e3b2fSeh146360 rs.mode = IEEE80211_IS_CHAN_5GHZ(in->in_chan) ? 1533bb5e3b2fSeh146360 IPW2200_MODE_11A : IPW2200_MODE_11G; 1534bb5e3b2fSeh146360 rs.type = IPW2200_RATESET_TYPE_NEGOCIATED; 1535bb5e3b2fSeh146360 rs.nrates = in->in_rates.ir_nrates; 1536bb5e3b2fSeh146360 (void) memcpy(rs.rates, in->in_rates.ir_rates, in->in_rates.ir_nrates); 1537bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT, 1538bb5e3b2fSeh146360 "ipw2200_auth_and_assoc(): " 1539bb5e3b2fSeh146360 "setting negotiated rates to(nrates = %u)\n", rs.nrates)); 1540bb5e3b2fSeh146360 1541bb5e3b2fSeh146360 err = ipw2200_cmd(sc, IPW2200_CMD_SET_RATES, &rs, sizeof (rs), 1); 1542bb5e3b2fSeh146360 if (err != DDI_SUCCESS) 1543bb5e3b2fSeh146360 return (err); 1544bb5e3b2fSeh146360 1545bb5e3b2fSeh146360 /* 1546bb5e3b2fSeh146360 * set the sensitive 1547bb5e3b2fSeh146360 */ 1548bb5e3b2fSeh146360 data = LE_32(in->in_rssi); 1549bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT, 1550bb5e3b2fSeh146360 "ipw2200_auth_and_assoc(): " 1551bb5e3b2fSeh146360 "setting sensitivity to rssi:(%u)\n", (uint8_t)in->in_rssi)); 1552bb5e3b2fSeh146360 err = ipw2200_cmd(sc, IPW2200_CMD_SET_SENSITIVITY, 1553bb5e3b2fSeh146360 &data, sizeof (data), 1); 1554bb5e3b2fSeh146360 if (err != DDI_SUCCESS) 1555bb5e3b2fSeh146360 return (err); 1556bb5e3b2fSeh146360 1557bb5e3b2fSeh146360 /* 1558bb5e3b2fSeh146360 * invoke command associate 1559bb5e3b2fSeh146360 */ 1560bb5e3b2fSeh146360 (void) memset(&assoc, 0, sizeof (assoc)); 1561bb5e3b2fSeh146360 assoc.mode = IEEE80211_IS_CHAN_5GHZ(in->in_chan) ? 1562bb5e3b2fSeh146360 IPW2200_MODE_11A : IPW2200_MODE_11G; 1563bb5e3b2fSeh146360 assoc.chan = ieee80211_chan2ieee(ic, in->in_chan); 1564bb5e3b2fSeh146360 /* 1565bb5e3b2fSeh146360 * use the value set to ic_bss to retraive current sharedmode 1566bb5e3b2fSeh146360 */ 1567bb5e3b2fSeh146360 if (ic->ic_bss->in_authmode == WL_SHAREDKEY) { 1568bb5e3b2fSeh146360 assoc.auth = (ic->ic_def_txkey << 4) | IPW2200_AUTH_SHARED; 1569bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_IOCTL, (sc->sc_dip, CE_CONT, 1570bb5e3b2fSeh146360 "ipw2200_auth_and_assoc(): " 1571bb5e3b2fSeh146360 "associate to shared key mode, set thru. ioctl")); 1572bb5e3b2fSeh146360 } 1573bb5e3b2fSeh146360 (void) memcpy(assoc.tstamp, in->in_tstamp.data, 8); 1574bb5e3b2fSeh146360 assoc.capinfo = LE_16(in->in_capinfo); 1575bb5e3b2fSeh146360 assoc.lintval = LE_16(ic->ic_lintval); 1576bb5e3b2fSeh146360 assoc.intval = LE_16(in->in_intval); 1577bb5e3b2fSeh146360 IEEE80211_ADDR_COPY(assoc.bssid, in->in_bssid); 1578bb5e3b2fSeh146360 if (ic->ic_opmode == IEEE80211_M_IBSS) 1579bb5e3b2fSeh146360 IEEE80211_ADDR_COPY(assoc.dst, ipw2200_broadcast_addr); 1580bb5e3b2fSeh146360 else 1581bb5e3b2fSeh146360 IEEE80211_ADDR_COPY(assoc.dst, in->in_bssid); 1582bb5e3b2fSeh146360 1583bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT, 1584bb5e3b2fSeh146360 "ipw2200_auth_and_assoc(): " 1585bb5e3b2fSeh146360 "associate to bssid(%2x:%2x:%2x:%2x:%2x:%2x:), " 1586bb5e3b2fSeh146360 "chan(%u), auth(%u)\n", 1587bb5e3b2fSeh146360 assoc.bssid[0], assoc.bssid[1], assoc.bssid[2], 1588bb5e3b2fSeh146360 assoc.bssid[3], assoc.bssid[4], assoc.bssid[5], 1589bb5e3b2fSeh146360 assoc.chan, assoc.auth)); 1590bb5e3b2fSeh146360 return (ipw2200_cmd(sc, IPW2200_CMD_ASSOCIATE, 1591bb5e3b2fSeh146360 &assoc, sizeof (assoc), 1)); 1592bb5e3b2fSeh146360 } 1593bb5e3b2fSeh146360 1594bb5e3b2fSeh146360 /* ARGSUSED */ 1595bb5e3b2fSeh146360 static int 1596bb5e3b2fSeh146360 ipw2200_newstate(struct ieee80211com *ic, enum ieee80211_state state, int arg) 1597bb5e3b2fSeh146360 { 1598bb5e3b2fSeh146360 struct ipw2200_softc *sc = (struct ipw2200_softc *)ic; 1599bb5e3b2fSeh146360 wifi_data_t wd = { 0 }; 1600bb5e3b2fSeh146360 1601bb5e3b2fSeh146360 switch (state) { 1602bb5e3b2fSeh146360 case IEEE80211_S_SCAN: 1603bb5e3b2fSeh146360 if (!(sc->sc_flags & IPW2200_FLAG_SCANNING)) { 1604bb5e3b2fSeh146360 ic->ic_flags |= IEEE80211_F_SCAN | IEEE80211_F_ASCAN; 1605bb5e3b2fSeh146360 (void) ipw2200_start_scan(sc); 1606bb5e3b2fSeh146360 } 1607bb5e3b2fSeh146360 break; 1608bb5e3b2fSeh146360 case IEEE80211_S_AUTH: 1609bb5e3b2fSeh146360 (void) ipw2200_auth_and_assoc(sc); 1610bb5e3b2fSeh146360 break; 1611bb5e3b2fSeh146360 case IEEE80211_S_RUN: 1612bb5e3b2fSeh146360 /* 1613bb5e3b2fSeh146360 * We can send data now; update the fastpath with our 1614bb5e3b2fSeh146360 * current associated BSSID and other relevant settings. 1615bb5e3b2fSeh146360 * 1616bb5e3b2fSeh146360 * Hardware to ahndle the wep encryption. Set the encryption 1617bb5e3b2fSeh146360 * as false. 1618bb5e3b2fSeh146360 * wd.wd_wep = ic->ic_flags & IEEE80211_F_WEPON; 1619bb5e3b2fSeh146360 */ 1620bb5e3b2fSeh146360 wd.wd_secalloc = WIFI_SEC_NONE; 1621bb5e3b2fSeh146360 wd.wd_opmode = ic->ic_opmode; 1622bb5e3b2fSeh146360 IEEE80211_ADDR_COPY(wd.wd_bssid, ic->ic_bss->in_bssid); 1623bb5e3b2fSeh146360 (void) mac_pdata_update(ic->ic_mach, &wd, sizeof (wd)); 1624bb5e3b2fSeh146360 break; 1625bb5e3b2fSeh146360 case IEEE80211_S_ASSOC: 1626bb5e3b2fSeh146360 case IEEE80211_S_INIT: 1627bb5e3b2fSeh146360 break; 1628bb5e3b2fSeh146360 } 1629bb5e3b2fSeh146360 1630bb5e3b2fSeh146360 /* 1631bb5e3b2fSeh146360 * notify to update the link 1632bb5e3b2fSeh146360 */ 1633bb5e3b2fSeh146360 if ((ic->ic_state != IEEE80211_S_RUN) && (state == IEEE80211_S_RUN)) { 1634bb5e3b2fSeh146360 mac_link_update(ic->ic_mach, LINK_STATE_UP); 1635bb5e3b2fSeh146360 } else if ((ic->ic_state == IEEE80211_S_RUN) && 1636bb5e3b2fSeh146360 (state != IEEE80211_S_RUN)) { 1637bb5e3b2fSeh146360 mac_link_update(ic->ic_mach, LINK_STATE_DOWN); 1638bb5e3b2fSeh146360 } 1639bb5e3b2fSeh146360 1640bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT, 1641bb5e3b2fSeh146360 "ipw2200_newstat(): %s -> %s\n", 1642bb5e3b2fSeh146360 ieee80211_state_name[ic->ic_state], 1643bb5e3b2fSeh146360 ieee80211_state_name[state])); 1644bb5e3b2fSeh146360 1645bb5e3b2fSeh146360 ic->ic_state = state; 1646bb5e3b2fSeh146360 return (DDI_SUCCESS); 1647bb5e3b2fSeh146360 } 1648bb5e3b2fSeh146360 1649bb5e3b2fSeh146360 /* 1650bb5e3b2fSeh146360 * GLD operations 1651bb5e3b2fSeh146360 */ 1652bb5e3b2fSeh146360 /* ARGSUSED */ 1653bb5e3b2fSeh146360 static int 1654bb5e3b2fSeh146360 ipw2200_m_stat(void *arg, uint_t stat, uint64_t *val) 1655bb5e3b2fSeh146360 { 1656bb5e3b2fSeh146360 ieee80211com_t *ic = (ieee80211com_t *)arg; 1657bb5e3b2fSeh146360 1658bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_GLD, (((struct ipw2200_softc *)arg)->sc_dip, 1659bb5e3b2fSeh146360 CE_CONT, 1660bb5e3b2fSeh146360 "ipw2200_m_stat(): enter\n")); 1661bb5e3b2fSeh146360 1662bb5e3b2fSeh146360 /* 1663bb5e3b2fSeh146360 * Some of below statistic data are from hardware, some from net80211 1664bb5e3b2fSeh146360 */ 1665bb5e3b2fSeh146360 switch (stat) { 1666bb5e3b2fSeh146360 case MAC_STAT_RBYTES: 1667bb5e3b2fSeh146360 *val = ic->ic_stats.is_rx_bytes; 1668bb5e3b2fSeh146360 break; 1669bb5e3b2fSeh146360 case MAC_STAT_IPACKETS: 1670bb5e3b2fSeh146360 *val = ic->ic_stats.is_rx_frags; 1671bb5e3b2fSeh146360 break; 1672bb5e3b2fSeh146360 case MAC_STAT_OBYTES: 1673bb5e3b2fSeh146360 *val = ic->ic_stats.is_tx_bytes; 1674bb5e3b2fSeh146360 break; 1675bb5e3b2fSeh146360 case MAC_STAT_OPACKETS: 1676bb5e3b2fSeh146360 *val = ic->ic_stats.is_tx_frags; 1677bb5e3b2fSeh146360 break; 1678bb5e3b2fSeh146360 /* 1679bb5e3b2fSeh146360 * Get below from hardware statistic, retraive net80211 value once 1s 1680bb5e3b2fSeh146360 */ 1681bb5e3b2fSeh146360 case WIFI_STAT_TX_FRAGS: 1682bb5e3b2fSeh146360 case WIFI_STAT_MCAST_TX: 1683bb5e3b2fSeh146360 case WIFI_STAT_TX_FAILED: 1684bb5e3b2fSeh146360 case WIFI_STAT_TX_RETRANS: 1685bb5e3b2fSeh146360 /* 1686bb5e3b2fSeh146360 * Get blow information from net80211 1687bb5e3b2fSeh146360 */ 1688bb5e3b2fSeh146360 case WIFI_STAT_RTS_SUCCESS: 1689bb5e3b2fSeh146360 case WIFI_STAT_RTS_FAILURE: 1690bb5e3b2fSeh146360 case WIFI_STAT_ACK_FAILURE: 1691bb5e3b2fSeh146360 case WIFI_STAT_RX_FRAGS: 1692bb5e3b2fSeh146360 case WIFI_STAT_MCAST_RX: 1693bb5e3b2fSeh146360 case WIFI_STAT_RX_DUPS: 1694bb5e3b2fSeh146360 case WIFI_STAT_FCS_ERRORS: 1695bb5e3b2fSeh146360 case WIFI_STAT_WEP_ERRORS: 1696bb5e3b2fSeh146360 return (ieee80211_stat(ic, stat, val)); 1697bb5e3b2fSeh146360 /* 1698bb5e3b2fSeh146360 * Need be supported later 1699bb5e3b2fSeh146360 */ 1700bb5e3b2fSeh146360 case MAC_STAT_IFSPEED: 1701bb5e3b2fSeh146360 case MAC_STAT_NOXMTBUF: 1702bb5e3b2fSeh146360 case MAC_STAT_IERRORS: 1703bb5e3b2fSeh146360 case MAC_STAT_OERRORS: 1704bb5e3b2fSeh146360 default: 1705bb5e3b2fSeh146360 return (ENOTSUP); 1706bb5e3b2fSeh146360 } 1707bb5e3b2fSeh146360 return (0); 1708bb5e3b2fSeh146360 } 1709bb5e3b2fSeh146360 1710bb5e3b2fSeh146360 /* ARGSUSED */ 1711bb5e3b2fSeh146360 static int 1712bb5e3b2fSeh146360 ipw2200_m_multicst(void *arg, boolean_t add, const uint8_t *mca) 1713bb5e3b2fSeh146360 { 1714bb5e3b2fSeh146360 /* not supported */ 1715bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_GLD, (((struct ipw2200_softc *)arg)->sc_dip, 1716bb5e3b2fSeh146360 CE_CONT, 1717bb5e3b2fSeh146360 "ipw2200_m_multicst(): enter\n")); 1718bb5e3b2fSeh146360 1719bb5e3b2fSeh146360 return (DDI_SUCCESS); 1720bb5e3b2fSeh146360 } 1721bb5e3b2fSeh146360 1722bb5e3b2fSeh146360 /* 1723bb5e3b2fSeh146360 * Multithread handler for linkstatus, fatal error recovery, get statistic 1724bb5e3b2fSeh146360 */ 1725bb5e3b2fSeh146360 static void 1726bb5e3b2fSeh146360 ipw2200_thread(struct ipw2200_softc *sc) 1727bb5e3b2fSeh146360 { 1728bb5e3b2fSeh146360 struct ieee80211com *ic = &sc->sc_ic; 1729982d85fcSeh146360 enum ieee80211_state ostate; 1730bb5e3b2fSeh146360 int32_t nlstate; 1731bb5e3b2fSeh146360 int stat_cnt = 0; 1732bb5e3b2fSeh146360 1733bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_SOFTINT, (sc->sc_dip, CE_CONT, 1734bb5e3b2fSeh146360 "ipw2200_thread(): enter, linkstate %d\n", sc->sc_linkstate)); 1735bb5e3b2fSeh146360 1736bb5e3b2fSeh146360 mutex_enter(&sc->sc_mflock); 1737bb5e3b2fSeh146360 1738bb5e3b2fSeh146360 while (sc->sc_mfthread_switch) { 1739bb5e3b2fSeh146360 /* 1740bb5e3b2fSeh146360 * notify the link state 1741bb5e3b2fSeh146360 */ 1742bb5e3b2fSeh146360 if (ic->ic_mach && (sc->sc_flags & IPW2200_FLAG_LINK_CHANGE)) { 1743bb5e3b2fSeh146360 1744bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_SOFTINT, (sc->sc_dip, CE_CONT, 1745bb5e3b2fSeh146360 "ipw2200_thread(): link status --> %d\n", 1746bb5e3b2fSeh146360 sc->sc_linkstate)); 1747bb5e3b2fSeh146360 1748bb5e3b2fSeh146360 sc->sc_flags &= ~IPW2200_FLAG_LINK_CHANGE; 1749bb5e3b2fSeh146360 nlstate = sc->sc_linkstate; 1750bb5e3b2fSeh146360 1751bb5e3b2fSeh146360 mutex_exit(&sc->sc_mflock); 1752bb5e3b2fSeh146360 mac_link_update(ic->ic_mach, nlstate); 1753bb5e3b2fSeh146360 mutex_enter(&sc->sc_mflock); 1754bb5e3b2fSeh146360 } 1755bb5e3b2fSeh146360 1756bb5e3b2fSeh146360 /* 1757bb5e3b2fSeh146360 * recovery fatal error 1758bb5e3b2fSeh146360 */ 1759bb5e3b2fSeh146360 if (ic->ic_mach && 1760bb5e3b2fSeh146360 (sc->sc_flags & IPW2200_FLAG_HW_ERR_RECOVER)) { 1761bb5e3b2fSeh146360 1762bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_FATAL, (sc->sc_dip, CE_CONT, 1763bb5e3b2fSeh146360 "ipw2200_thread(): " 1764bb5e3b2fSeh146360 "try to recover fatal hw error\n")); 1765bb5e3b2fSeh146360 1766bb5e3b2fSeh146360 sc->sc_flags &= ~IPW2200_FLAG_HW_ERR_RECOVER; 1767bb5e3b2fSeh146360 1768bb5e3b2fSeh146360 mutex_exit(&sc->sc_mflock); 1769982d85fcSeh146360 1770982d85fcSeh146360 ostate = ic->ic_state; 1771bb5e3b2fSeh146360 (void) ipw2200_init(sc); /* Force state machine */ 1772bb5e3b2fSeh146360 /* 1773bb5e3b2fSeh146360 * workround. Delay for a while after init especially 1774bb5e3b2fSeh146360 * when something wrong happened already. 1775bb5e3b2fSeh146360 */ 1776bb5e3b2fSeh146360 delay(drv_usectohz(delay_fatal_recover)); 1777982d85fcSeh146360 1778982d85fcSeh146360 /* 1779982d85fcSeh146360 * Init scan will recovery the original connection if 1780982d85fcSeh146360 * the original state is run 1781982d85fcSeh146360 */ 1782982d85fcSeh146360 if (ostate != IEEE80211_S_INIT) 1783982d85fcSeh146360 ieee80211_begin_scan(ic, 0); 1784982d85fcSeh146360 1785bb5e3b2fSeh146360 mutex_enter(&sc->sc_mflock); 1786bb5e3b2fSeh146360 } 1787bb5e3b2fSeh146360 1788bb5e3b2fSeh146360 /* 1789bb5e3b2fSeh146360 * get statistic, the value will be retrieved by m_stat 1790bb5e3b2fSeh146360 */ 1791bb5e3b2fSeh146360 if (stat_cnt == 10) { 1792bb5e3b2fSeh146360 1793bb5e3b2fSeh146360 stat_cnt = 0; /* re-start */ 1794bb5e3b2fSeh146360 mutex_exit(&sc->sc_mflock); 1795bb5e3b2fSeh146360 ipw2200_get_statistics(sc); 1796bb5e3b2fSeh146360 mutex_enter(&sc->sc_mflock); 1797bb5e3b2fSeh146360 1798bb5e3b2fSeh146360 } else 1799bb5e3b2fSeh146360 stat_cnt++; /* until 1s */ 1800bb5e3b2fSeh146360 1801bb5e3b2fSeh146360 mutex_exit(&sc->sc_mflock); 1802bb5e3b2fSeh146360 delay(drv_usectohz(delay_aux_thread)); 1803bb5e3b2fSeh146360 mutex_enter(&sc->sc_mflock); 1804bb5e3b2fSeh146360 1805bb5e3b2fSeh146360 } 1806bb5e3b2fSeh146360 sc->sc_mf_thread = NULL; 1807bb5e3b2fSeh146360 cv_signal(&sc->sc_mfthread_cv); 1808bb5e3b2fSeh146360 mutex_exit(&sc->sc_mflock); 1809bb5e3b2fSeh146360 } 1810bb5e3b2fSeh146360 1811bb5e3b2fSeh146360 static int 1812bb5e3b2fSeh146360 ipw2200_m_start(void *arg) 1813bb5e3b2fSeh146360 { 1814bb5e3b2fSeh146360 struct ipw2200_softc *sc = (struct ipw2200_softc *)arg; 1815982d85fcSeh146360 struct ieee80211com *ic = &sc->sc_ic; 1816bb5e3b2fSeh146360 1817bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_GLD, (sc->sc_dip, CE_CONT, 1818bb5e3b2fSeh146360 "ipw2200_m_start(): enter\n")); 1819bb5e3b2fSeh146360 /* 1820bb5e3b2fSeh146360 * initialize ipw2200 hardware, everything ok will start scan 1821bb5e3b2fSeh146360 */ 1822bb5e3b2fSeh146360 (void) ipw2200_init(sc); 1823982d85fcSeh146360 /* 1824982d85fcSeh146360 * set the state machine to INIT 1825982d85fcSeh146360 */ 1826982d85fcSeh146360 ieee80211_new_state(ic, IEEE80211_S_INIT, -1); 1827bb5e3b2fSeh146360 1828bb5e3b2fSeh146360 sc->sc_flags |= IPW2200_FLAG_RUNNING; 1829bb5e3b2fSeh146360 1830bb5e3b2fSeh146360 return (DDI_SUCCESS); 1831bb5e3b2fSeh146360 } 1832bb5e3b2fSeh146360 1833bb5e3b2fSeh146360 static void 1834bb5e3b2fSeh146360 ipw2200_m_stop(void *arg) 1835bb5e3b2fSeh146360 { 1836bb5e3b2fSeh146360 struct ipw2200_softc *sc = (struct ipw2200_softc *)arg; 1837982d85fcSeh146360 struct ieee80211com *ic = &sc->sc_ic; 1838bb5e3b2fSeh146360 1839bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_GLD, (sc->sc_dip, CE_CONT, 1840bb5e3b2fSeh146360 "ipw2200_m_stop(): enter\n")); 1841bb5e3b2fSeh146360 1842bb5e3b2fSeh146360 ipw2200_stop(sc); 1843982d85fcSeh146360 /* 1844982d85fcSeh146360 * set the state machine to INIT 1845982d85fcSeh146360 */ 1846982d85fcSeh146360 ieee80211_new_state(ic, IEEE80211_S_INIT, -1); 1847bb5e3b2fSeh146360 1848bb5e3b2fSeh146360 sc->sc_flags &= ~IPW2200_FLAG_RUNNING; 1849bb5e3b2fSeh146360 } 1850bb5e3b2fSeh146360 1851bb5e3b2fSeh146360 static int 1852bb5e3b2fSeh146360 ipw2200_m_unicst(void *arg, const uint8_t *macaddr) 1853bb5e3b2fSeh146360 { 1854bb5e3b2fSeh146360 struct ipw2200_softc *sc = (struct ipw2200_softc *)arg; 1855bb5e3b2fSeh146360 struct ieee80211com *ic = &sc->sc_ic; 1856bb5e3b2fSeh146360 int err; 1857bb5e3b2fSeh146360 1858bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_GLD, (sc->sc_dip, CE_CONT, 1859bb5e3b2fSeh146360 "ipw2200_m_unicst(): enter\n")); 1860bb5e3b2fSeh146360 1861bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_GLD, (sc->sc_dip, CE_CONT, 1862bb5e3b2fSeh146360 "ipw2200_m_unicst(): GLD setting MAC address to " 1863bb5e3b2fSeh146360 "%02x:%02x:%02x:%02x:%02x:%02x\n", 1864bb5e3b2fSeh146360 macaddr[0], macaddr[1], macaddr[2], 1865bb5e3b2fSeh146360 macaddr[3], macaddr[4], macaddr[5])); 1866bb5e3b2fSeh146360 1867bb5e3b2fSeh146360 if (!IEEE80211_ADDR_EQ(ic->ic_macaddr, macaddr)) { 1868bb5e3b2fSeh146360 1869bb5e3b2fSeh146360 IEEE80211_ADDR_COPY(ic->ic_macaddr, macaddr); 1870bb5e3b2fSeh146360 1871bb5e3b2fSeh146360 if (sc->sc_flags & IPW2200_FLAG_RUNNING) { 1872bb5e3b2fSeh146360 err = ipw2200_config(sc); 1873bb5e3b2fSeh146360 if (err != DDI_SUCCESS) { 1874bb5e3b2fSeh146360 IPW2200_WARN((sc->sc_dip, CE_WARN, 1875bb5e3b2fSeh146360 "ipw2200_m_unicst(): " 1876bb5e3b2fSeh146360 "device configuration failed\n")); 1877bb5e3b2fSeh146360 goto fail; 1878bb5e3b2fSeh146360 } 1879bb5e3b2fSeh146360 } 1880bb5e3b2fSeh146360 } 1881bb5e3b2fSeh146360 return (DDI_SUCCESS); 1882bb5e3b2fSeh146360 fail: 1883bb5e3b2fSeh146360 return (err); 1884bb5e3b2fSeh146360 } 1885bb5e3b2fSeh146360 1886bb5e3b2fSeh146360 static int 1887bb5e3b2fSeh146360 ipw2200_m_promisc(void *arg, boolean_t on) 1888bb5e3b2fSeh146360 { 1889bb5e3b2fSeh146360 /* not supported */ 1890bb5e3b2fSeh146360 struct ipw2200_softc *sc = (struct ipw2200_softc *)arg; 1891bb5e3b2fSeh146360 1892bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_GLD, (sc->sc_dip, CE_CONT, 1893bb5e3b2fSeh146360 "ipw2200_m_promisc(): enter. " 1894bb5e3b2fSeh146360 "GLD setting promiscuous mode - %d\n", on)); 1895bb5e3b2fSeh146360 1896bb5e3b2fSeh146360 return (DDI_SUCCESS); 1897bb5e3b2fSeh146360 } 1898bb5e3b2fSeh146360 1899bb5e3b2fSeh146360 static mblk_t * 1900bb5e3b2fSeh146360 ipw2200_m_tx(void *arg, mblk_t *mp) 1901bb5e3b2fSeh146360 { 1902bb5e3b2fSeh146360 struct ipw2200_softc *sc = (struct ipw2200_softc *)arg; 1903bb5e3b2fSeh146360 struct ieee80211com *ic = &sc->sc_ic; 1904bb5e3b2fSeh146360 mblk_t *next; 1905bb5e3b2fSeh146360 1906bb5e3b2fSeh146360 /* 1907bb5e3b2fSeh146360 * No data frames go out unless we're associated; this 1908bb5e3b2fSeh146360 * should not happen as the 802.11 layer does not enable 1909bb5e3b2fSeh146360 * the xmit queue until we enter the RUN state. 1910bb5e3b2fSeh146360 */ 1911bb5e3b2fSeh146360 if (ic->ic_state != IEEE80211_S_RUN) { 1912bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_GLD, (sc->sc_dip, CE_CONT, 1913bb5e3b2fSeh146360 "ipw2200_m_tx(): discard msg, ic_state = %u\n", 1914bb5e3b2fSeh146360 ic->ic_state)); 1915bb5e3b2fSeh146360 freemsgchain(mp); 1916bb5e3b2fSeh146360 return (NULL); 1917bb5e3b2fSeh146360 } 1918bb5e3b2fSeh146360 1919bb5e3b2fSeh146360 while (mp != NULL) { 1920bb5e3b2fSeh146360 next = mp->b_next; 1921bb5e3b2fSeh146360 mp->b_next = NULL; 1922bb5e3b2fSeh146360 if (ipw2200_send(ic, mp, IEEE80211_FC0_TYPE_DATA) == 1923bb5e3b2fSeh146360 DDI_FAILURE) { 1924bb5e3b2fSeh146360 mp->b_next = next; 1925bb5e3b2fSeh146360 break; 1926bb5e3b2fSeh146360 } 1927bb5e3b2fSeh146360 mp = next; 1928bb5e3b2fSeh146360 } 1929bb5e3b2fSeh146360 return (mp); 1930bb5e3b2fSeh146360 } 1931bb5e3b2fSeh146360 1932bb5e3b2fSeh146360 /* ARGSUSED */ 1933bb5e3b2fSeh146360 static int 1934bb5e3b2fSeh146360 ipw2200_send(ieee80211com_t *ic, mblk_t *mp, uint8_t type) 1935bb5e3b2fSeh146360 { 1936bb5e3b2fSeh146360 struct ipw2200_softc *sc = (struct ipw2200_softc *)ic; 1937bb5e3b2fSeh146360 struct ieee80211_node *in; 1938bb5e3b2fSeh146360 struct ieee80211_frame *wh; 1939bb5e3b2fSeh146360 mblk_t *m0; 1940bb5e3b2fSeh146360 size_t cnt, off; 1941bb5e3b2fSeh146360 struct ipw2200_tx_desc *txdsc; 1942bb5e3b2fSeh146360 struct dma_region *dr; 1943bb5e3b2fSeh146360 uint32_t idx; 1944bb5e3b2fSeh146360 int err; 1945bb5e3b2fSeh146360 /* tmp pointer, used to pack header and payload */ 1946bb5e3b2fSeh146360 uint8_t *p; 1947bb5e3b2fSeh146360 1948bb5e3b2fSeh146360 ASSERT(mp->b_next == NULL); 1949bb5e3b2fSeh146360 1950bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_GLD, (sc->sc_dip, CE_CONT, 1951bb5e3b2fSeh146360 "ipw2200_send(): enter\n")); 1952bb5e3b2fSeh146360 1953bb5e3b2fSeh146360 m0 = NULL; 1954bb5e3b2fSeh146360 err = DDI_SUCCESS; 1955bb5e3b2fSeh146360 1956bb5e3b2fSeh146360 if ((type & IEEE80211_FC0_TYPE_MASK) != IEEE80211_FC0_TYPE_DATA) { 1957bb5e3b2fSeh146360 /* 1958bb5e3b2fSeh146360 * skip all management frames since ipw2200 won't generate any 1959bb5e3b2fSeh146360 * management frames. Therefore, drop this package. 1960bb5e3b2fSeh146360 */ 1961bb5e3b2fSeh146360 freemsg(mp); 1962bb5e3b2fSeh146360 err = DDI_SUCCESS; 1963bb5e3b2fSeh146360 goto fail0; 1964bb5e3b2fSeh146360 } 1965bb5e3b2fSeh146360 1966bb5e3b2fSeh146360 mutex_enter(&sc->sc_tx_lock); 1967bb5e3b2fSeh146360 1968bb5e3b2fSeh146360 /* 1969bb5e3b2fSeh146360 * need 1 empty descriptor 1970bb5e3b2fSeh146360 */ 1971bb5e3b2fSeh146360 if (sc->sc_tx_free <= IPW2200_TX_RING_MIN) { 1972bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_RING, (sc->sc_dip, CE_WARN, 1973bb5e3b2fSeh146360 "ipw2200_send(): no enough descriptors(%d)\n", 1974bb5e3b2fSeh146360 sc->sc_tx_free)); 1975bb5e3b2fSeh146360 ic->ic_stats.is_tx_nobuf++; /* no enough buffer */ 1976bb5e3b2fSeh146360 sc->sc_flags |= IPW2200_FLAG_TX_SCHED; 1977bb5e3b2fSeh146360 err = DDI_FAILURE; 1978bb5e3b2fSeh146360 goto fail1; 1979bb5e3b2fSeh146360 } 1980bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_RING, (sc->sc_dip, CE_CONT, 1981bb5e3b2fSeh146360 "ipw2200_send(): tx-free=%d,tx-curr=%d\n", 1982bb5e3b2fSeh146360 sc->sc_tx_free, sc->sc_tx_cur)); 1983bb5e3b2fSeh146360 1984bb5e3b2fSeh146360 wh = (struct ieee80211_frame *)mp->b_rptr; 1985bb5e3b2fSeh146360 in = ieee80211_find_txnode(ic, wh->i_addr1); 1986bb5e3b2fSeh146360 if (in == NULL) { /* can not find the tx node, drop the package */ 1987bb5e3b2fSeh146360 ic->ic_stats.is_tx_failed++; 1988bb5e3b2fSeh146360 freemsg(mp); 1989bb5e3b2fSeh146360 err = DDI_SUCCESS; 1990bb5e3b2fSeh146360 goto fail1; 1991bb5e3b2fSeh146360 } 1992bb5e3b2fSeh146360 in->in_inact = 0; 1993bb5e3b2fSeh146360 (void) ieee80211_encap(ic, mp, in); 1994bb5e3b2fSeh146360 ieee80211_free_node(in); 1995bb5e3b2fSeh146360 1996bb5e3b2fSeh146360 /* 1997bb5e3b2fSeh146360 * get txdsc and wh 1998bb5e3b2fSeh146360 */ 1999bb5e3b2fSeh146360 idx = sc->sc_tx_cur; 2000bb5e3b2fSeh146360 txdsc = &sc->sc_txdsc[idx]; 2001bb5e3b2fSeh146360 (void) memset(txdsc, 0, sizeof (*txdsc)); 2002bb5e3b2fSeh146360 wh = (struct ieee80211_frame *)&txdsc->wh; 2003bb5e3b2fSeh146360 2004bb5e3b2fSeh146360 /* 2005bb5e3b2fSeh146360 * extract header from message 2006bb5e3b2fSeh146360 */ 2007bb5e3b2fSeh146360 p = (uint8_t *)&txdsc->wh; 2008bb5e3b2fSeh146360 off = 0; 2009bb5e3b2fSeh146360 m0 = mp; 2010bb5e3b2fSeh146360 while (off < sizeof (struct ieee80211_frame)) { 2011bb5e3b2fSeh146360 cnt = MBLKL(m0); 2012bb5e3b2fSeh146360 if (cnt > (sizeof (struct ieee80211_frame) - off)) 2013bb5e3b2fSeh146360 cnt = sizeof (struct ieee80211_frame) - off; 2014bb5e3b2fSeh146360 if (cnt) { 2015bb5e3b2fSeh146360 (void) memcpy(p + off, m0->b_rptr, cnt); 2016bb5e3b2fSeh146360 off += cnt; 2017bb5e3b2fSeh146360 m0->b_rptr += cnt; 2018bb5e3b2fSeh146360 } else 2019bb5e3b2fSeh146360 m0 = m0->b_cont; 2020bb5e3b2fSeh146360 } 2021bb5e3b2fSeh146360 2022bb5e3b2fSeh146360 /* 2023bb5e3b2fSeh146360 * extract payload from message 2024bb5e3b2fSeh146360 */ 2025bb5e3b2fSeh146360 dr = &sc->sc_dma_txbufs[idx]; 2026bb5e3b2fSeh146360 p = sc->sc_txbufs[idx]; 2027bb5e3b2fSeh146360 off = 0; 2028bb5e3b2fSeh146360 while (m0) { 2029bb5e3b2fSeh146360 cnt = MBLKL(m0); 2030bb5e3b2fSeh146360 if (cnt) 2031bb5e3b2fSeh146360 (void) memcpy(p + off, m0->b_rptr, cnt); 2032bb5e3b2fSeh146360 off += cnt; 2033bb5e3b2fSeh146360 m0 = m0->b_cont; 2034bb5e3b2fSeh146360 } 2035bb5e3b2fSeh146360 2036bb5e3b2fSeh146360 txdsc->hdr.type = IPW2200_HDR_TYPE_DATA; 2037bb5e3b2fSeh146360 txdsc->hdr.flags = IPW2200_HDR_FLAG_IRQ; 2038bb5e3b2fSeh146360 txdsc->cmd = IPW2200_DATA_CMD_TX; 2039bb5e3b2fSeh146360 txdsc->len = LE_16(off); 2040bb5e3b2fSeh146360 txdsc->flags = 0; 2041bb5e3b2fSeh146360 2042bb5e3b2fSeh146360 if (ic->ic_opmode == IEEE80211_M_IBSS) { 2043bb5e3b2fSeh146360 if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) 2044bb5e3b2fSeh146360 txdsc->flags |= IPW2200_DATA_FLAG_NEED_ACK; 2045bb5e3b2fSeh146360 } else if (!IEEE80211_IS_MULTICAST(wh->i_addr3)) 2046bb5e3b2fSeh146360 txdsc->flags |= IPW2200_DATA_FLAG_NEED_ACK; 2047bb5e3b2fSeh146360 2048bb5e3b2fSeh146360 if (ic->ic_flags & IEEE80211_F_PRIVACY) { 2049bb5e3b2fSeh146360 wh->i_fc[1] |= IEEE80211_FC1_WEP; 2050bb5e3b2fSeh146360 txdsc->wep_txkey = ic->ic_def_txkey; 2051bb5e3b2fSeh146360 } 2052bb5e3b2fSeh146360 else 2053bb5e3b2fSeh146360 txdsc->flags |= IPW2200_DATA_FLAG_NO_WEP; 2054bb5e3b2fSeh146360 2055bb5e3b2fSeh146360 if (ic->ic_flags & IEEE80211_F_SHPREAMBLE) 2056bb5e3b2fSeh146360 txdsc->flags |= IPW2200_DATA_FLAG_SHPREAMBLE; 2057bb5e3b2fSeh146360 2058bb5e3b2fSeh146360 txdsc->nseg = LE_32(1); 2059bb5e3b2fSeh146360 txdsc->seg_addr[0] = LE_32(dr->dr_pbase); 2060bb5e3b2fSeh146360 txdsc->seg_len[0] = LE_32(off); 2061bb5e3b2fSeh146360 2062bb5e3b2fSeh146360 /* 2063bb5e3b2fSeh146360 * DMA sync: buffer and desc 2064bb5e3b2fSeh146360 */ 2065bb5e3b2fSeh146360 (void) ddi_dma_sync(dr->dr_hnd, 0, 2066bb5e3b2fSeh146360 IPW2200_TXBUF_SIZE, DDI_DMA_SYNC_FORDEV); 2067bb5e3b2fSeh146360 (void) ddi_dma_sync(sc->sc_dma_txdsc.dr_hnd, 2068bb5e3b2fSeh146360 idx * sizeof (struct ipw2200_tx_desc), 2069bb5e3b2fSeh146360 sizeof (struct ipw2200_tx_desc), DDI_DMA_SYNC_FORDEV); 2070bb5e3b2fSeh146360 2071bb5e3b2fSeh146360 sc->sc_tx_cur = RING_FORWARD(sc->sc_tx_cur, 1, IPW2200_TX_RING_SIZE); 2072bb5e3b2fSeh146360 sc->sc_tx_free--; 2073bb5e3b2fSeh146360 2074bb5e3b2fSeh146360 /* 2075bb5e3b2fSeh146360 * update txcur 2076bb5e3b2fSeh146360 */ 2077bb5e3b2fSeh146360 ipw2200_csr_put32(sc, IPW2200_CSR_TX1_WRITE_INDEX, sc->sc_tx_cur); 2078bb5e3b2fSeh146360 2079bb5e3b2fSeh146360 /* 2080bb5e3b2fSeh146360 * success, free the original message 2081bb5e3b2fSeh146360 */ 2082bb5e3b2fSeh146360 if (mp) 2083bb5e3b2fSeh146360 freemsg(mp); 2084bb5e3b2fSeh146360 2085bb5e3b2fSeh146360 fail1: 2086bb5e3b2fSeh146360 mutex_exit(&sc->sc_tx_lock); 2087bb5e3b2fSeh146360 fail0: 2088bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_GLD, (sc->sc_dip, CE_CONT, 2089bb5e3b2fSeh146360 "ipw2200_send(): exit - err=%d\n", err)); 2090bb5e3b2fSeh146360 2091bb5e3b2fSeh146360 return (err); 2092bb5e3b2fSeh146360 } 2093bb5e3b2fSeh146360 2094bb5e3b2fSeh146360 /* 2095bb5e3b2fSeh146360 * IOCTL handlers 2096bb5e3b2fSeh146360 */ 2097bb5e3b2fSeh146360 #define IEEE80211_IOCTL_REQUIRED (1) 2098bb5e3b2fSeh146360 #define IEEE80211_IOCTL_NOT_REQUIRED (0) 2099bb5e3b2fSeh146360 static void 2100bb5e3b2fSeh146360 ipw2200_m_ioctl(void *arg, queue_t *q, mblk_t *m) 2101bb5e3b2fSeh146360 { 2102bb5e3b2fSeh146360 struct ipw2200_softc *sc = (struct ipw2200_softc *)arg; 2103bb5e3b2fSeh146360 struct ieee80211com *ic = &sc->sc_ic; 2104bb5e3b2fSeh146360 uint32_t err; 2105bb5e3b2fSeh146360 2106bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_GLD, (sc->sc_dip, CE_CONT, 2107bb5e3b2fSeh146360 "ipw2200_m_ioctl(): enter\n")); 2108bb5e3b2fSeh146360 2109bb5e3b2fSeh146360 /* 2110bb5e3b2fSeh146360 * Check whether or not need to handle this in net80211 2111bb5e3b2fSeh146360 * 2112bb5e3b2fSeh146360 */ 2113bb5e3b2fSeh146360 if (ipw2200_ioctl(sc, q, m) == IEEE80211_IOCTL_NOT_REQUIRED) 2114bb5e3b2fSeh146360 return; 2115bb5e3b2fSeh146360 2116bb5e3b2fSeh146360 err = ieee80211_ioctl(ic, q, m); 2117bb5e3b2fSeh146360 if (err == ENETRESET) { 2118bb5e3b2fSeh146360 if (sc->sc_flags & IPW2200_FLAG_RUNNING) { 2119bb5e3b2fSeh146360 (void) ipw2200_m_start(sc); 2120bb5e3b2fSeh146360 (void) ieee80211_new_state(ic, 2121bb5e3b2fSeh146360 IEEE80211_S_SCAN, -1); 2122bb5e3b2fSeh146360 } 2123bb5e3b2fSeh146360 } 2124bb5e3b2fSeh146360 if (err == ERESTART) { 2125bb5e3b2fSeh146360 if (sc->sc_flags & IPW2200_FLAG_RUNNING) 2126bb5e3b2fSeh146360 (void) ipw2200_chip_reset(sc); 2127bb5e3b2fSeh146360 } 2128bb5e3b2fSeh146360 } 2129bb5e3b2fSeh146360 static int 2130bb5e3b2fSeh146360 ipw2200_ioctl(struct ipw2200_softc *sc, queue_t *q, mblk_t *m) 2131bb5e3b2fSeh146360 { 2132bb5e3b2fSeh146360 struct iocblk *iocp; 2133bb5e3b2fSeh146360 uint32_t len, ret, cmd; 2134bb5e3b2fSeh146360 mblk_t *m0; 2135bb5e3b2fSeh146360 boolean_t need_privilege; 2136bb5e3b2fSeh146360 boolean_t need_net80211; 2137bb5e3b2fSeh146360 2138bb5e3b2fSeh146360 if (MBLKL(m) < sizeof (struct iocblk)) { 2139bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_IOCTL, (sc->sc_dip, CE_CONT, 2140bb5e3b2fSeh146360 "ipw2200_ioctl(): ioctl buffer too short, %u\n", 2141bb5e3b2fSeh146360 MBLKL(m))); 2142bb5e3b2fSeh146360 miocnak(q, m, 0, EINVAL); 2143bb5e3b2fSeh146360 /* 2144bb5e3b2fSeh146360 * Buf not enough, do not need net80211 either 2145bb5e3b2fSeh146360 */ 2146bb5e3b2fSeh146360 return (IEEE80211_IOCTL_NOT_REQUIRED); 2147bb5e3b2fSeh146360 } 2148bb5e3b2fSeh146360 2149bb5e3b2fSeh146360 /* 2150bb5e3b2fSeh146360 * Validate the command 2151bb5e3b2fSeh146360 */ 2152bb5e3b2fSeh146360 iocp = (struct iocblk *)m->b_rptr; 2153bb5e3b2fSeh146360 iocp->ioc_error = 0; 2154bb5e3b2fSeh146360 cmd = iocp->ioc_cmd; 2155bb5e3b2fSeh146360 need_privilege = B_TRUE; 2156bb5e3b2fSeh146360 switch (cmd) { 2157bb5e3b2fSeh146360 case WLAN_SET_PARAM: 2158bb5e3b2fSeh146360 case WLAN_COMMAND: 2159bb5e3b2fSeh146360 break; 2160bb5e3b2fSeh146360 case WLAN_GET_PARAM: 2161bb5e3b2fSeh146360 need_privilege = B_FALSE; 2162bb5e3b2fSeh146360 break; 2163bb5e3b2fSeh146360 default: 2164bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_IOCTL, (sc->sc_dip, CE_CONT, 2165bb5e3b2fSeh146360 "ipw2200_ioctl(): unknown cmd 0x%x", cmd)); 2166bb5e3b2fSeh146360 miocnak(q, m, 0, EINVAL); 2167bb5e3b2fSeh146360 /* 2168bb5e3b2fSeh146360 * Unknown cmd, do not need net80211 either 2169bb5e3b2fSeh146360 */ 2170bb5e3b2fSeh146360 return (IEEE80211_IOCTL_NOT_REQUIRED); 2171bb5e3b2fSeh146360 } 2172bb5e3b2fSeh146360 2173bb5e3b2fSeh146360 if (need_privilege) { 2174bb5e3b2fSeh146360 /* 2175bb5e3b2fSeh146360 * Check for specific net_config privilege on Solaris 10+. 2176bb5e3b2fSeh146360 * Otherwise just check for root access ... 2177bb5e3b2fSeh146360 */ 2178bb5e3b2fSeh146360 if (secpolicy_net_config != NULL) 2179bb5e3b2fSeh146360 ret = secpolicy_net_config(iocp->ioc_cr, B_FALSE); 2180bb5e3b2fSeh146360 else 2181bb5e3b2fSeh146360 ret = drv_priv(iocp->ioc_cr); 2182bb5e3b2fSeh146360 if (ret != 0) { 2183bb5e3b2fSeh146360 miocnak(q, m, 0, ret); 2184bb5e3b2fSeh146360 /* 2185bb5e3b2fSeh146360 * privilege check fail, do not need net80211 either 2186bb5e3b2fSeh146360 */ 2187bb5e3b2fSeh146360 return (IEEE80211_IOCTL_NOT_REQUIRED); 2188bb5e3b2fSeh146360 } 2189bb5e3b2fSeh146360 } 2190bb5e3b2fSeh146360 /* 2191bb5e3b2fSeh146360 * sanity check 2192bb5e3b2fSeh146360 */ 2193bb5e3b2fSeh146360 m0 = m->b_cont; 2194bb5e3b2fSeh146360 if (iocp->ioc_count == 0 || iocp->ioc_count < sizeof (wldp_t) || 2195bb5e3b2fSeh146360 m0 == NULL) { 2196bb5e3b2fSeh146360 miocnak(q, m, 0, EINVAL); 2197bb5e3b2fSeh146360 /* 2198bb5e3b2fSeh146360 * invalid format, do not need net80211 either 2199bb5e3b2fSeh146360 */ 2200bb5e3b2fSeh146360 return (IEEE80211_IOCTL_NOT_REQUIRED); 2201bb5e3b2fSeh146360 } 2202bb5e3b2fSeh146360 /* 2203bb5e3b2fSeh146360 * assuming single data block 2204bb5e3b2fSeh146360 */ 2205bb5e3b2fSeh146360 if (m0->b_cont) { 2206bb5e3b2fSeh146360 freemsg(m0->b_cont); 2207bb5e3b2fSeh146360 m0->b_cont = NULL; 2208bb5e3b2fSeh146360 } 2209bb5e3b2fSeh146360 2210bb5e3b2fSeh146360 need_net80211 = B_FALSE; 2211bb5e3b2fSeh146360 ret = ipw2200_getset(sc, m0, cmd, &need_net80211); 2212bb5e3b2fSeh146360 if (!need_net80211) { 2213bb5e3b2fSeh146360 len = msgdsize(m0); 2214bb5e3b2fSeh146360 2215bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_IOCTL, (sc->sc_dip, CE_CONT, 2216bb5e3b2fSeh146360 "ipw2200_ioctl(): go to call miocack with " 2217bb5e3b2fSeh146360 "ret = %d, len = %d\n", ret, len)); 2218bb5e3b2fSeh146360 miocack(q, m, len, ret); 2219bb5e3b2fSeh146360 return (IEEE80211_IOCTL_NOT_REQUIRED); 2220bb5e3b2fSeh146360 } 2221bb5e3b2fSeh146360 2222bb5e3b2fSeh146360 /* 2223bb5e3b2fSeh146360 * IEEE80211_IOCTL - need net80211 handle 2224bb5e3b2fSeh146360 */ 2225bb5e3b2fSeh146360 return (IEEE80211_IOCTL_REQUIRED); 2226bb5e3b2fSeh146360 } 2227bb5e3b2fSeh146360 2228bb5e3b2fSeh146360 static int 2229bb5e3b2fSeh146360 ipw2200_getset(struct ipw2200_softc *sc, mblk_t *m, uint32_t cmd, 2230bb5e3b2fSeh146360 boolean_t *need_net80211) 2231bb5e3b2fSeh146360 { 2232bb5e3b2fSeh146360 wldp_t *infp, *outfp; 2233bb5e3b2fSeh146360 uint32_t id; 2234bb5e3b2fSeh146360 int ret; 2235bb5e3b2fSeh146360 2236bb5e3b2fSeh146360 infp = (wldp_t *)m->b_rptr; 2237bb5e3b2fSeh146360 outfp = (wldp_t *)m->b_rptr; 2238bb5e3b2fSeh146360 outfp->wldp_result = WL_NOTSUPPORTED; 2239bb5e3b2fSeh146360 2240bb5e3b2fSeh146360 id = infp->wldp_id; 2241bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_IOCTL, (sc->sc_dip, CE_CONT, 2242bb5e3b2fSeh146360 "ipw2200_getset(): id = 0x%x\n", id)); 2243bb5e3b2fSeh146360 switch (id) { 2244bb5e3b2fSeh146360 case WL_RADIO: /* which is not supported by net80211 */ 2245bb5e3b2fSeh146360 ret = iwi_wificfg_radio(sc, cmd, outfp); 2246bb5e3b2fSeh146360 break; 2247bb5e3b2fSeh146360 case WL_DESIRED_RATES: /* hardware doesn't support fix-rates */ 2248bb5e3b2fSeh146360 ret = iwi_wificfg_desrates(outfp); 2249bb5e3b2fSeh146360 break; 2250bb5e3b2fSeh146360 default: 2251bb5e3b2fSeh146360 /* 2252bb5e3b2fSeh146360 * The wifi IOCTL net80211 supported: 2253bb5e3b2fSeh146360 * case WL_ESSID: 2254bb5e3b2fSeh146360 * case WL_BSSID: 2255bb5e3b2fSeh146360 * case WL_WEP_KEY_TAB: 2256bb5e3b2fSeh146360 * case WL_WEP_KEY_ID: 2257bb5e3b2fSeh146360 * case WL_AUTH_MODE: 2258bb5e3b2fSeh146360 * case WL_ENCRYPTION: 2259bb5e3b2fSeh146360 * case WL_BSS_TYPE: 2260bb5e3b2fSeh146360 * case WL_ESS_LIST: 2261bb5e3b2fSeh146360 * case WL_LINKSTATUS: 2262bb5e3b2fSeh146360 * case WL_RSSI: 2263bb5e3b2fSeh146360 * case WL_SCAN: 2264bb5e3b2fSeh146360 * case WL_LOAD_DEFAULTS: 2265bb5e3b2fSeh146360 * case WL_DISASSOCIATE: 2266bb5e3b2fSeh146360 */ 2267bb5e3b2fSeh146360 *need_net80211 = B_TRUE; /* let net80211 do the rest */ 2268bb5e3b2fSeh146360 return (0); 2269bb5e3b2fSeh146360 } 2270bb5e3b2fSeh146360 /* 2271bb5e3b2fSeh146360 * we will overwrite everything 2272bb5e3b2fSeh146360 */ 2273bb5e3b2fSeh146360 m->b_wptr = m->b_rptr + outfp->wldp_length; 2274bb5e3b2fSeh146360 return (ret); 2275bb5e3b2fSeh146360 } 2276bb5e3b2fSeh146360 2277bb5e3b2fSeh146360 static int 2278bb5e3b2fSeh146360 iwi_wificfg_radio(struct ipw2200_softc *sc, uint32_t cmd, wldp_t *outfp) 2279bb5e3b2fSeh146360 { 2280bb5e3b2fSeh146360 uint32_t ret = ENOTSUP; 2281bb5e3b2fSeh146360 2282bb5e3b2fSeh146360 switch (cmd) { 2283bb5e3b2fSeh146360 case WLAN_GET_PARAM: 2284bb5e3b2fSeh146360 *(wl_linkstatus_t *)(outfp->wldp_buf) = 2285bb5e3b2fSeh146360 ipw2200_radio_status(sc); 2286bb5e3b2fSeh146360 outfp->wldp_length = WIFI_BUF_OFFSET + sizeof (wl_linkstatus_t); 2287bb5e3b2fSeh146360 outfp->wldp_result = WL_SUCCESS; 2288bb5e3b2fSeh146360 ret = 0; /* command success */ 2289bb5e3b2fSeh146360 break; 2290bb5e3b2fSeh146360 case WLAN_SET_PARAM: 2291bb5e3b2fSeh146360 default: 2292bb5e3b2fSeh146360 break; 2293bb5e3b2fSeh146360 } 2294bb5e3b2fSeh146360 return (ret); 2295bb5e3b2fSeh146360 } 2296bb5e3b2fSeh146360 2297bb5e3b2fSeh146360 static int 2298bb5e3b2fSeh146360 iwi_wificfg_desrates(wldp_t *outfp) 2299bb5e3b2fSeh146360 { 2300bb5e3b2fSeh146360 /* return success, but with result NOTSUPPORTED */ 2301bb5e3b2fSeh146360 outfp->wldp_length = WIFI_BUF_OFFSET; 2302bb5e3b2fSeh146360 outfp->wldp_result = WL_NOTSUPPORTED; 2303bb5e3b2fSeh146360 return (0); 2304bb5e3b2fSeh146360 } 2305bb5e3b2fSeh146360 /* End of IOCTL Handlers */ 2306bb5e3b2fSeh146360 2307bb5e3b2fSeh146360 void 2308bb5e3b2fSeh146360 ipw2200_fix_channel(struct ieee80211com *ic, mblk_t *m) 2309bb5e3b2fSeh146360 { 2310bb5e3b2fSeh146360 struct ieee80211_frame *wh; 2311bb5e3b2fSeh146360 uint8_t subtype; 2312bb5e3b2fSeh146360 uint8_t *frm, *efrm; 2313bb5e3b2fSeh146360 2314bb5e3b2fSeh146360 wh = (struct ieee80211_frame *)m->b_rptr; 2315bb5e3b2fSeh146360 2316bb5e3b2fSeh146360 if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) != IEEE80211_FC0_TYPE_MGT) 2317bb5e3b2fSeh146360 return; 2318bb5e3b2fSeh146360 2319bb5e3b2fSeh146360 subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK; 2320bb5e3b2fSeh146360 2321bb5e3b2fSeh146360 if (subtype != IEEE80211_FC0_SUBTYPE_BEACON && 2322bb5e3b2fSeh146360 subtype != IEEE80211_FC0_SUBTYPE_PROBE_RESP) 2323bb5e3b2fSeh146360 return; 2324bb5e3b2fSeh146360 2325bb5e3b2fSeh146360 /* 2326bb5e3b2fSeh146360 * assume the message contains only 1 block 2327bb5e3b2fSeh146360 */ 2328bb5e3b2fSeh146360 frm = (uint8_t *)(wh + 1); 2329bb5e3b2fSeh146360 efrm = (uint8_t *)m->b_wptr; 2330bb5e3b2fSeh146360 frm += 12; /* skip tstamp, bintval and capinfo fields */ 2331bb5e3b2fSeh146360 while (frm < efrm) { 2332bb5e3b2fSeh146360 if (*frm == IEEE80211_ELEMID_DSPARMS) 2333bb5e3b2fSeh146360 #if IEEE80211_CHAN_MAX < 255 2334bb5e3b2fSeh146360 if (frm[2] <= IEEE80211_CHAN_MAX) 2335bb5e3b2fSeh146360 #endif 2336bb5e3b2fSeh146360 ic->ic_curchan = &ic->ic_sup_channels[frm[2]]; 2337bb5e3b2fSeh146360 frm += frm[1] + 2; 2338bb5e3b2fSeh146360 } 2339bb5e3b2fSeh146360 } 2340bb5e3b2fSeh146360 2341bb5e3b2fSeh146360 static void 2342bb5e3b2fSeh146360 ipw2200_rcv_frame(struct ipw2200_softc *sc, struct ipw2200_frame *frame) 2343bb5e3b2fSeh146360 { 2344bb5e3b2fSeh146360 struct ieee80211com *ic = &sc->sc_ic; 2345bb5e3b2fSeh146360 uint8_t *data = (uint8_t *)frame; 2346bb5e3b2fSeh146360 uint32_t len; 2347bb5e3b2fSeh146360 struct ieee80211_frame *wh; 2348bb5e3b2fSeh146360 struct ieee80211_node *in; 2349bb5e3b2fSeh146360 mblk_t *m; 2350bb5e3b2fSeh146360 int i; 2351bb5e3b2fSeh146360 2352bb5e3b2fSeh146360 len = LE_16(frame->len); 2353bb5e3b2fSeh146360 2354bb5e3b2fSeh146360 /* 2355bb5e3b2fSeh146360 * Skip the frame header, get the real data from the input 2356bb5e3b2fSeh146360 */ 2357bb5e3b2fSeh146360 data += sizeof (struct ipw2200_frame); 2358bb5e3b2fSeh146360 2359bb5e3b2fSeh146360 if ((len < sizeof (struct ieee80211_frame_min)) || 2360bb5e3b2fSeh146360 (len > IPW2200_RXBUF_SIZE)) { 2361bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_RX, (sc->sc_dip, CE_CONT, 2362bb5e3b2fSeh146360 "ipw2200_rcv_frame(): bad frame length=%u\n", 2363bb5e3b2fSeh146360 LE_16(frame->len))); 2364bb5e3b2fSeh146360 return; 2365bb5e3b2fSeh146360 } 2366bb5e3b2fSeh146360 2367bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_RX, (sc->sc_dip, CE_CONT, 2368bb5e3b2fSeh146360 "ipw2200_rcv_frame(): chan = %d, length = %d\n", frame->chan, len)); 2369bb5e3b2fSeh146360 2370bb5e3b2fSeh146360 m = allocb(len, BPRI_MED); 2371bb5e3b2fSeh146360 if (m) { 2372bb5e3b2fSeh146360 (void) memcpy(m->b_wptr, data, len); 2373bb5e3b2fSeh146360 m->b_wptr += len; 2374bb5e3b2fSeh146360 2375bb5e3b2fSeh146360 wh = (struct ieee80211_frame *)m->b_rptr; 2376bb5e3b2fSeh146360 if (wh->i_fc[1] & IEEE80211_FC1_WEP) { 2377bb5e3b2fSeh146360 /* 2378bb5e3b2fSeh146360 * h/w decyption leaves the WEP bit, iv and CRC fields 2379bb5e3b2fSeh146360 */ 2380bb5e3b2fSeh146360 wh->i_fc[1] &= ~IEEE80211_FC1_WEP; 2381bb5e3b2fSeh146360 for (i = sizeof (struct ieee80211_frame) - 1; 2382bb5e3b2fSeh146360 i >= 0; i--) 2383bb5e3b2fSeh146360 *(m->b_rptr + IEEE80211_WEP_IVLEN + 2384bb5e3b2fSeh146360 IEEE80211_WEP_KIDLEN + i) = 2385bb5e3b2fSeh146360 *(m->b_rptr + i); 2386bb5e3b2fSeh146360 m->b_rptr += IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN; 2387bb5e3b2fSeh146360 m->b_wptr -= IEEE80211_WEP_CRCLEN; 2388bb5e3b2fSeh146360 wh = (struct ieee80211_frame *)m->b_rptr; 2389bb5e3b2fSeh146360 } 2390bb5e3b2fSeh146360 2391bb5e3b2fSeh146360 if (ic->ic_state == IEEE80211_S_SCAN) { 2392bb5e3b2fSeh146360 ic->ic_ibss_chan = &ic->ic_sup_channels[frame->chan]; 2393bb5e3b2fSeh146360 ipw2200_fix_channel(ic, m); 2394bb5e3b2fSeh146360 } 2395bb5e3b2fSeh146360 2396bb5e3b2fSeh146360 in = ieee80211_find_rxnode(ic, wh); 2397bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_RX, (sc->sc_dip, CE_CONT, 2398bb5e3b2fSeh146360 "ipw2200_rcv_frame(): " 2399bb5e3b2fSeh146360 "ni_esslen:%d, ni_essid[0-5]:%c%c%c%c%c%c\n", 2400bb5e3b2fSeh146360 in->in_esslen, 2401bb5e3b2fSeh146360 in->in_essid[0], in->in_essid[1], in->in_essid[2], 2402bb5e3b2fSeh146360 in->in_essid[3], in->in_essid[4], in->in_essid[5])); 2403bb5e3b2fSeh146360 2404bb5e3b2fSeh146360 (void) ieee80211_input(ic, m, in, frame->rssi_dbm, 0); 2405bb5e3b2fSeh146360 2406bb5e3b2fSeh146360 ieee80211_free_node(in); 2407bb5e3b2fSeh146360 } 2408bb5e3b2fSeh146360 else 2409bb5e3b2fSeh146360 IPW2200_WARN((sc->sc_dip, CE_WARN, 2410bb5e3b2fSeh146360 "ipw2200_rcv_frame(): " 2411bb5e3b2fSeh146360 "cannot allocate receive message(%u)\n", 2412bb5e3b2fSeh146360 LE_16(frame->len))); 2413bb5e3b2fSeh146360 } 2414bb5e3b2fSeh146360 2415bb5e3b2fSeh146360 static void 2416bb5e3b2fSeh146360 ipw2200_rcv_notif(struct ipw2200_softc *sc, struct ipw2200_notif *notif) 2417bb5e3b2fSeh146360 { 2418bb5e3b2fSeh146360 struct ieee80211com *ic = &sc->sc_ic; 2419bb5e3b2fSeh146360 struct ipw2200_notif_association *assoc; 2420bb5e3b2fSeh146360 struct ipw2200_notif_authentication *auth; 2421bb5e3b2fSeh146360 uint8_t *ndata = (uint8_t *)notif; 2422bb5e3b2fSeh146360 2423bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_NOTIF, (sc->sc_dip, CE_CONT, 2424bb5e3b2fSeh146360 "ipw2200_rcv_notif(): type=%u\n", notif->type)); 2425bb5e3b2fSeh146360 2426bb5e3b2fSeh146360 ndata += sizeof (struct ipw2200_notif); 2427bb5e3b2fSeh146360 switch (notif->type) { 2428bb5e3b2fSeh146360 case IPW2200_NOTIF_TYPE_ASSOCIATION: 2429bb5e3b2fSeh146360 assoc = (struct ipw2200_notif_association *)ndata; 2430bb5e3b2fSeh146360 2431bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT, 2432bb5e3b2fSeh146360 "ipw2200_rcv_notif(): association=%u,%u\n", 2433bb5e3b2fSeh146360 assoc->state, assoc->status)); 2434bb5e3b2fSeh146360 2435bb5e3b2fSeh146360 switch (assoc->state) { 2436bb5e3b2fSeh146360 case IPW2200_ASSOC_SUCCESS: 2437bb5e3b2fSeh146360 ieee80211_new_state(ic, IEEE80211_S_RUN, -1); 2438bb5e3b2fSeh146360 break; 2439bb5e3b2fSeh146360 case IPW2200_ASSOC_FAIL: 2440bb5e3b2fSeh146360 ieee80211_begin_scan(ic, 1); /* reset */ 2441bb5e3b2fSeh146360 break; 2442bb5e3b2fSeh146360 default: 2443bb5e3b2fSeh146360 break; 2444bb5e3b2fSeh146360 } 2445bb5e3b2fSeh146360 break; 2446bb5e3b2fSeh146360 2447bb5e3b2fSeh146360 case IPW2200_NOTIF_TYPE_AUTHENTICATION: 2448bb5e3b2fSeh146360 auth = (struct ipw2200_notif_authentication *)ndata; 2449bb5e3b2fSeh146360 2450bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT, 2451bb5e3b2fSeh146360 "ipw2200_rcv_notif(): authentication=%u\n", auth->state)); 2452bb5e3b2fSeh146360 2453bb5e3b2fSeh146360 switch (auth->state) { 2454bb5e3b2fSeh146360 case IPW2200_AUTH_SUCCESS: 2455bb5e3b2fSeh146360 ieee80211_new_state(ic, IEEE80211_S_ASSOC, -1); 2456bb5e3b2fSeh146360 break; 2457bb5e3b2fSeh146360 case IPW2200_AUTH_FAIL: 2458bb5e3b2fSeh146360 break; 2459bb5e3b2fSeh146360 default: 2460bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_NOTIF, (sc->sc_dip, CE_CONT, 2461bb5e3b2fSeh146360 "ipw2200_rcv_notif(): " 2462bb5e3b2fSeh146360 "unknown authentication state(%u)\n", auth->state)); 2463bb5e3b2fSeh146360 break; 2464bb5e3b2fSeh146360 } 2465bb5e3b2fSeh146360 break; 2466bb5e3b2fSeh146360 2467bb5e3b2fSeh146360 case IPW2200_NOTIF_TYPE_SCAN_CHANNEL: 2468bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_SCAN, (sc->sc_dip, CE_CONT, 2469bb5e3b2fSeh146360 "ipw2200_rcv_notif(): scan-channel=%u\n", 2470bb5e3b2fSeh146360 ((struct ipw2200_notif_scan_channel *)ndata)->nchan)); 2471bb5e3b2fSeh146360 break; 2472bb5e3b2fSeh146360 2473bb5e3b2fSeh146360 case IPW2200_NOTIF_TYPE_SCAN_COMPLETE: 2474bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_SCAN, (sc->sc_dip, CE_CONT, 2475bb5e3b2fSeh146360 "ipw2200_rcv_notif():scan-completed,(%u,%u)\n", 2476bb5e3b2fSeh146360 ((struct ipw2200_notif_scan_complete *)ndata)->nchan, 2477bb5e3b2fSeh146360 ((struct ipw2200_notif_scan_complete *)ndata)->status)); 2478bb5e3b2fSeh146360 2479bb5e3b2fSeh146360 /* 2480bb5e3b2fSeh146360 * scan complete 2481bb5e3b2fSeh146360 */ 2482bb5e3b2fSeh146360 sc->sc_flags &= ~IPW2200_FLAG_SCANNING; 2483bb5e3b2fSeh146360 ieee80211_end_scan(ic); 2484bb5e3b2fSeh146360 break; 2485bb5e3b2fSeh146360 2486bb5e3b2fSeh146360 case IPW2200_NOTIF_TYPE_BEACON: 2487bb5e3b2fSeh146360 case IPW2200_NOTIF_TYPE_CALIBRATION: 2488bb5e3b2fSeh146360 case IPW2200_NOTIF_TYPE_NOISE: 2489bb5e3b2fSeh146360 /* 2490bb5e3b2fSeh146360 * just ignore 2491bb5e3b2fSeh146360 */ 2492bb5e3b2fSeh146360 break; 2493bb5e3b2fSeh146360 default: 2494bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_NOTIF, (sc->sc_dip, CE_CONT, 2495bb5e3b2fSeh146360 "ipw2200_rcv_notif(): unknown notification type(%u)\n", 2496bb5e3b2fSeh146360 notif->type)); 2497bb5e3b2fSeh146360 break; 2498bb5e3b2fSeh146360 } 2499bb5e3b2fSeh146360 } 2500bb5e3b2fSeh146360 2501bb5e3b2fSeh146360 static uint_t 2502bb5e3b2fSeh146360 ipw2200_intr(caddr_t arg) 2503bb5e3b2fSeh146360 { 2504bb5e3b2fSeh146360 struct ipw2200_softc *sc = (struct ipw2200_softc *)arg; 2505bb5e3b2fSeh146360 struct ieee80211com *ic = &sc->sc_ic; 2506bb5e3b2fSeh146360 uint32_t ireg, ridx, len, i; 2507bb5e3b2fSeh146360 uint8_t *p, *rxbuf; 2508bb5e3b2fSeh146360 struct dma_region *dr; 2509bb5e3b2fSeh146360 struct ipw2200_hdr *hdr; 2510bb5e3b2fSeh146360 int need_sched; 2511bb5e3b2fSeh146360 uint32_t widx; 2512bb5e3b2fSeh146360 2513bb5e3b2fSeh146360 ireg = ipw2200_csr_get32(sc, IPW2200_CSR_INTR); 2514bb5e3b2fSeh146360 2515bb5e3b2fSeh146360 if (ireg == 0xffffffff) 2516bb5e3b2fSeh146360 return (DDI_INTR_UNCLAIMED); 2517bb5e3b2fSeh146360 2518bb5e3b2fSeh146360 if (!(ireg & IPW2200_INTR_MASK_ALL)) 2519bb5e3b2fSeh146360 return (DDI_INTR_UNCLAIMED); 2520bb5e3b2fSeh146360 2521bb5e3b2fSeh146360 /* 2522bb5e3b2fSeh146360 * mask all interrupts 2523bb5e3b2fSeh146360 */ 2524bb5e3b2fSeh146360 ipw2200_csr_put32(sc, IPW2200_CSR_INTR_MASK, 0); 2525bb5e3b2fSeh146360 2526bb5e3b2fSeh146360 /* 2527bb5e3b2fSeh146360 * acknowledge all fired interrupts 2528bb5e3b2fSeh146360 */ 2529bb5e3b2fSeh146360 ipw2200_csr_put32(sc, IPW2200_CSR_INTR, ireg); 2530bb5e3b2fSeh146360 2531bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_INT, (sc->sc_dip, CE_CONT, 2532bb5e3b2fSeh146360 "ipw2200_intr(): enter. interrupt fired, int=0x%08x\n", ireg)); 2533bb5e3b2fSeh146360 2534bb5e3b2fSeh146360 if (ireg & IPW2200_INTR_MASK_ERR) { 2535bb5e3b2fSeh146360 2536bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_FATAL, (sc->sc_dip, CE_CONT, 2537bb5e3b2fSeh146360 "ipw2200 interrupt(): int= 0x%08x\n", ireg)); 2538bb5e3b2fSeh146360 2539bb5e3b2fSeh146360 /* 2540bb5e3b2fSeh146360 * inform mfthread to recover hw error by stopping it 2541bb5e3b2fSeh146360 */ 2542bb5e3b2fSeh146360 mutex_enter(&sc->sc_mflock); 2543bb5e3b2fSeh146360 sc->sc_flags |= IPW2200_FLAG_HW_ERR_RECOVER; 2544bb5e3b2fSeh146360 mutex_exit(&sc->sc_mflock); 2545bb5e3b2fSeh146360 2546bb5e3b2fSeh146360 } else { 2547bb5e3b2fSeh146360 if (ireg & IPW2200_INTR_FW_INITED) { 2548bb5e3b2fSeh146360 mutex_enter(&sc->sc_ilock); 2549bb5e3b2fSeh146360 sc->sc_fw_ok = 1; 2550bb5e3b2fSeh146360 cv_signal(&sc->sc_fw_cond); 2551bb5e3b2fSeh146360 mutex_exit(&sc->sc_ilock); 2552bb5e3b2fSeh146360 } 2553bb5e3b2fSeh146360 if (ireg & IPW2200_INTR_RADIO_OFF) { 2554bb5e3b2fSeh146360 IPW2200_REPORT((sc->sc_dip, CE_CONT, 2555bb5e3b2fSeh146360 "ipw2200_intr(): radio is OFF\n")); 2556bb5e3b2fSeh146360 /* 2557bb5e3b2fSeh146360 * Stop hardware, will notify LINK is down 2558bb5e3b2fSeh146360 */ 2559bb5e3b2fSeh146360 ipw2200_stop(sc); 2560bb5e3b2fSeh146360 } 2561bb5e3b2fSeh146360 if (ireg & IPW2200_INTR_CMD_TRANSFER) { 2562bb5e3b2fSeh146360 mutex_enter(&sc->sc_cmd_lock); 2563bb5e3b2fSeh146360 ridx = ipw2200_csr_get32(sc, 2564bb5e3b2fSeh146360 IPW2200_CSR_CMD_READ_INDEX); 2565bb5e3b2fSeh146360 i = RING_FORWARD(sc->sc_cmd_cur, 2566bb5e3b2fSeh146360 sc->sc_cmd_free, IPW2200_CMD_RING_SIZE); 2567bb5e3b2fSeh146360 len = RING_FLEN(i, ridx, IPW2200_CMD_RING_SIZE); 2568bb5e3b2fSeh146360 2569bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_INT, (sc->sc_dip, CE_CONT, 2570bb5e3b2fSeh146360 "ipw2200_intr(): cmd-ring,i=%u,ridx=%u,len=%u\n", 2571bb5e3b2fSeh146360 i, ridx, len)); 2572bb5e3b2fSeh146360 2573bb5e3b2fSeh146360 if (len > 0) { 2574bb5e3b2fSeh146360 sc->sc_cmd_free += len; 2575bb5e3b2fSeh146360 cv_signal(&sc->sc_cmd_cond); 2576bb5e3b2fSeh146360 } 2577bb5e3b2fSeh146360 for (; i != ridx; 2578bb5e3b2fSeh146360 i = RING_FORWARD(i, 1, IPW2200_CMD_RING_SIZE)) 2579bb5e3b2fSeh146360 sc->sc_done[i] = 1; 2580bb5e3b2fSeh146360 mutex_exit(&sc->sc_cmd_lock); 2581bb5e3b2fSeh146360 2582bb5e3b2fSeh146360 mutex_enter(&sc->sc_ilock); 2583bb5e3b2fSeh146360 cv_signal(&sc->sc_cmd_status_cond); 2584bb5e3b2fSeh146360 mutex_exit(&sc->sc_ilock); 2585bb5e3b2fSeh146360 } 2586bb5e3b2fSeh146360 if (ireg & IPW2200_INTR_RX_TRANSFER) { 2587bb5e3b2fSeh146360 ridx = ipw2200_csr_get32(sc, 2588bb5e3b2fSeh146360 IPW2200_CSR_RX_READ_INDEX); 2589bb5e3b2fSeh146360 widx = ipw2200_csr_get32(sc, 2590bb5e3b2fSeh146360 IPW2200_CSR_RX_WRITE_INDEX); 2591bb5e3b2fSeh146360 2592bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_INT, (sc->sc_dip, CE_CONT, 2593bb5e3b2fSeh146360 "ipw2200_intr(): rx-ring,widx=%u,ridx=%u\n", 2594bb5e3b2fSeh146360 ridx, widx)); 2595bb5e3b2fSeh146360 2596bb5e3b2fSeh146360 for (; sc->sc_rx_cur != ridx; 2597bb5e3b2fSeh146360 sc->sc_rx_cur = RING_FORWARD(sc->sc_rx_cur, 1, 2598bb5e3b2fSeh146360 IPW2200_RX_RING_SIZE)) { 2599bb5e3b2fSeh146360 i = sc->sc_rx_cur; 2600bb5e3b2fSeh146360 rxbuf = sc->sc_rxbufs[i]; 2601bb5e3b2fSeh146360 dr = &sc->sc_dma_rxbufs[i]; 2602bb5e3b2fSeh146360 2603bb5e3b2fSeh146360 /* 2604bb5e3b2fSeh146360 * DMA sync 2605bb5e3b2fSeh146360 */ 2606bb5e3b2fSeh146360 (void) ddi_dma_sync(dr->dr_hnd, 0, 2607bb5e3b2fSeh146360 IPW2200_RXBUF_SIZE, DDI_DMA_SYNC_FORKERNEL); 2608bb5e3b2fSeh146360 /* 2609bb5e3b2fSeh146360 * Get rx header(hdr) and rx data(p) from rxbuf 2610bb5e3b2fSeh146360 */ 2611bb5e3b2fSeh146360 p = rxbuf; 2612bb5e3b2fSeh146360 hdr = (struct ipw2200_hdr *)p; 2613bb5e3b2fSeh146360 p += sizeof (struct ipw2200_hdr); 2614bb5e3b2fSeh146360 2615bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_INT, 2616bb5e3b2fSeh146360 (sc->sc_dip, CE_CONT, 2617bb5e3b2fSeh146360 "ipw2200_intr(): Rx hdr type %u\n", 2618bb5e3b2fSeh146360 hdr->type)); 2619bb5e3b2fSeh146360 2620bb5e3b2fSeh146360 switch (hdr->type) { 2621bb5e3b2fSeh146360 case IPW2200_HDR_TYPE_FRAME: 2622bb5e3b2fSeh146360 ipw2200_rcv_frame(sc, 2623bb5e3b2fSeh146360 (struct ipw2200_frame *)p); 2624bb5e3b2fSeh146360 break; 2625bb5e3b2fSeh146360 2626bb5e3b2fSeh146360 case IPW2200_HDR_TYPE_NOTIF: 2627bb5e3b2fSeh146360 ipw2200_rcv_notif(sc, 2628bb5e3b2fSeh146360 (struct ipw2200_notif *)p); 2629bb5e3b2fSeh146360 break; 2630bb5e3b2fSeh146360 default: 2631bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_INT, 2632bb5e3b2fSeh146360 (sc->sc_dip, CE_CONT, 2633bb5e3b2fSeh146360 "ipw2200_intr(): " 2634bb5e3b2fSeh146360 "unknown Rx hdr type %u\n", 2635bb5e3b2fSeh146360 hdr->type)); 2636bb5e3b2fSeh146360 break; 2637bb5e3b2fSeh146360 } 2638bb5e3b2fSeh146360 } 2639bb5e3b2fSeh146360 /* 2640bb5e3b2fSeh146360 * write sc_rx_cur backward 1 step into RX_WRITE_INDEX 2641bb5e3b2fSeh146360 */ 2642bb5e3b2fSeh146360 ipw2200_csr_put32(sc, IPW2200_CSR_RX_WRITE_INDEX, 2643bb5e3b2fSeh146360 RING_BACKWARD(sc->sc_rx_cur, 1, 2644bb5e3b2fSeh146360 IPW2200_RX_RING_SIZE)); 2645bb5e3b2fSeh146360 } 2646bb5e3b2fSeh146360 if (ireg & IPW2200_INTR_TX1_TRANSFER) { 2647bb5e3b2fSeh146360 mutex_enter(&sc->sc_tx_lock); 2648bb5e3b2fSeh146360 ridx = ipw2200_csr_get32(sc, 2649bb5e3b2fSeh146360 IPW2200_CSR_TX1_READ_INDEX); 2650bb5e3b2fSeh146360 len = RING_FLEN(RING_FORWARD(sc->sc_tx_cur, 2651bb5e3b2fSeh146360 sc->sc_tx_free, IPW2200_TX_RING_SIZE), 2652bb5e3b2fSeh146360 ridx, IPW2200_TX_RING_SIZE); 2653bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_RING, (sc->sc_dip, CE_CONT, 2654bb5e3b2fSeh146360 "ipw2200_intr(): tx-ring,ridx=%u,len=%u\n", 2655bb5e3b2fSeh146360 ridx, len)); 2656bb5e3b2fSeh146360 sc->sc_tx_free += len; 2657bb5e3b2fSeh146360 2658bb5e3b2fSeh146360 need_sched = 0; 2659bb5e3b2fSeh146360 if ((sc->sc_tx_free > IPW2200_TX_RING_MIN) && 2660bb5e3b2fSeh146360 (sc->sc_flags & IPW2200_FLAG_TX_SCHED)) { 2661bb5e3b2fSeh146360 IPW2200_DBG(IPW2200_DBG_RING, (sc->sc_dip, 2662bb5e3b2fSeh146360 CE_CONT, 2663bb5e3b2fSeh146360 "ipw2200_intr(): Need Reschedule!")); 2664bb5e3b2fSeh146360 need_sched = 1; 2665bb5e3b2fSeh146360 sc->sc_flags &= ~IPW2200_FLAG_TX_SCHED; 2666bb5e3b2fSeh146360 } 2667bb5e3b2fSeh146360 mutex_exit(&sc->sc_tx_lock); 2668bb5e3b2fSeh146360 2669bb5e3b2fSeh146360 if (need_sched) 2670bb5e3b2fSeh146360 mac_tx_update(ic->ic_mach); 2671bb5e3b2fSeh146360 } 2672bb5e3b2fSeh146360 } 2673bb5e3b2fSeh146360 2674bb5e3b2fSeh146360 /* 2675bb5e3b2fSeh146360 * enable all interrupts 2676bb5e3b2fSeh146360 */ 2677bb5e3b2fSeh146360 ipw2200_csr_put32(sc, IPW2200_CSR_INTR_MASK, IPW2200_INTR_MASK_ALL); 2678bb5e3b2fSeh146360 2679bb5e3b2fSeh146360 return (DDI_INTR_CLAIMED); 2680bb5e3b2fSeh146360 } 2681bb5e3b2fSeh146360 2682bb5e3b2fSeh146360 2683bb5e3b2fSeh146360 /* 2684bb5e3b2fSeh146360 * Module Loading Data & Entry Points 2685bb5e3b2fSeh146360 */ 2686bb5e3b2fSeh146360 DDI_DEFINE_STREAM_OPS(ipw2200_devops, nulldev, nulldev, ipw2200_attach, 2687*76939ce0Seh146360 ipw2200_detach, ipw2200_reset, NULL, D_MP, NULL); 2688bb5e3b2fSeh146360 2689bb5e3b2fSeh146360 static struct modldrv ipw2200_modldrv = { 2690bb5e3b2fSeh146360 &mod_driverops, 2691bb5e3b2fSeh146360 ipw2200_ident, 2692bb5e3b2fSeh146360 &ipw2200_devops 2693bb5e3b2fSeh146360 }; 2694bb5e3b2fSeh146360 2695bb5e3b2fSeh146360 static struct modlinkage ipw2200_modlinkage = { 2696bb5e3b2fSeh146360 MODREV_1, 2697bb5e3b2fSeh146360 &ipw2200_modldrv, 2698bb5e3b2fSeh146360 NULL 2699bb5e3b2fSeh146360 }; 2700bb5e3b2fSeh146360 2701bb5e3b2fSeh146360 int 2702bb5e3b2fSeh146360 _init(void) 2703bb5e3b2fSeh146360 { 2704bb5e3b2fSeh146360 int status; 2705bb5e3b2fSeh146360 2706bb5e3b2fSeh146360 status = ddi_soft_state_init(&ipw2200_ssp, 2707bb5e3b2fSeh146360 sizeof (struct ipw2200_softc), 1); 2708bb5e3b2fSeh146360 if (status != DDI_SUCCESS) 2709bb5e3b2fSeh146360 return (status); 2710bb5e3b2fSeh146360 2711bb5e3b2fSeh146360 mac_init_ops(&ipw2200_devops, IPW2200_DRV_NAME); 2712bb5e3b2fSeh146360 status = mod_install(&ipw2200_modlinkage); 2713bb5e3b2fSeh146360 if (status != DDI_SUCCESS) { 2714bb5e3b2fSeh146360 mac_fini_ops(&ipw2200_devops); 2715bb5e3b2fSeh146360 ddi_soft_state_fini(&ipw2200_ssp); 2716bb5e3b2fSeh146360 } 2717bb5e3b2fSeh146360 2718bb5e3b2fSeh146360 return (status); 2719bb5e3b2fSeh146360 } 2720bb5e3b2fSeh146360 2721bb5e3b2fSeh146360 int 2722bb5e3b2fSeh146360 _fini(void) 2723bb5e3b2fSeh146360 { 2724bb5e3b2fSeh146360 int status; 2725bb5e3b2fSeh146360 2726bb5e3b2fSeh146360 status = mod_remove(&ipw2200_modlinkage); 2727bb5e3b2fSeh146360 if (status == DDI_SUCCESS) { 2728bb5e3b2fSeh146360 mac_fini_ops(&ipw2200_devops); 2729bb5e3b2fSeh146360 ddi_soft_state_fini(&ipw2200_ssp); 2730bb5e3b2fSeh146360 } 2731bb5e3b2fSeh146360 2732bb5e3b2fSeh146360 return (status); 2733bb5e3b2fSeh146360 } 2734bb5e3b2fSeh146360 2735bb5e3b2fSeh146360 int 2736bb5e3b2fSeh146360 _info(struct modinfo *modinfop) 2737bb5e3b2fSeh146360 { 2738bb5e3b2fSeh146360 return (mod_info(&ipw2200_modlinkage, modinfop)); 2739bb5e3b2fSeh146360 } 2740