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