13847Seh146360 /* 25838Seh146360 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 33847Seh146360 * Use is subject to license terms. 43847Seh146360 */ 53847Seh146360 63847Seh146360 /* 73847Seh146360 * Copyright (c) 2004, 2005 83847Seh146360 * Damien Bergamini <damien.bergamini@free.fr>. All rights reserved. 93847Seh146360 * 103847Seh146360 * Redistribution and use in source and binary forms, with or without 113847Seh146360 * modification, are permitted provided that the following conditions 123847Seh146360 * are met: 133847Seh146360 * 1. Redistributions of source code must retain the above copyright 143847Seh146360 * notice unmodified, this list of conditions, and the following 153847Seh146360 * disclaimer. 163847Seh146360 * 2. Redistributions in binary form must reproduce the above copyright 173847Seh146360 * notice, this list of conditions and the following disclaimer in the 183847Seh146360 * documentation and/or other materials provided with the distribution. 193847Seh146360 * 203847Seh146360 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 213847Seh146360 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 223847Seh146360 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 233847Seh146360 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 243847Seh146360 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 253847Seh146360 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 263847Seh146360 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 273847Seh146360 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 283847Seh146360 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 293847Seh146360 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 303847Seh146360 * SUCH DAMAGE. 313847Seh146360 */ 323847Seh146360 333847Seh146360 #include <sys/types.h> 343847Seh146360 #include <sys/byteorder.h> 353847Seh146360 #include <sys/conf.h> 363847Seh146360 #include <sys/cmn_err.h> 373847Seh146360 #include <sys/stat.h> 383847Seh146360 #include <sys/ddi.h> 393847Seh146360 #include <sys/sunddi.h> 403847Seh146360 #include <sys/strsubr.h> 413847Seh146360 #include <sys/ethernet.h> 423847Seh146360 #include <inet/common.h> 433847Seh146360 #include <inet/nd.h> 443847Seh146360 #include <inet/mi.h> 453847Seh146360 #include <sys/note.h> 463847Seh146360 #include <sys/stream.h> 473847Seh146360 #include <sys/strsun.h> 483847Seh146360 #include <sys/modctl.h> 493847Seh146360 #include <sys/devops.h> 503847Seh146360 #include <sys/dlpi.h> 518275SEric Cheng #include <sys/mac_provider.h> 523847Seh146360 #include <sys/mac_wifi.h> 533847Seh146360 #include <sys/varargs.h> 543847Seh146360 #include <sys/pci.h> 553847Seh146360 #include <sys/policy.h> 563847Seh146360 #include <sys/random.h> 575485Seh146360 #include <sys/crypto/common.h> 585485Seh146360 #include <sys/crypto/api.h> 593847Seh146360 603847Seh146360 #include "ipw2200.h" 613847Seh146360 #include "ipw2200_impl.h" 623847Seh146360 #include <inet/wifi_ioctl.h> 633847Seh146360 643847Seh146360 /* 655485Seh146360 * for net80211 kernel usage 665485Seh146360 */ 675485Seh146360 #include <sys/net80211.h> 685485Seh146360 #include <sys/net80211_proto.h> 695485Seh146360 705485Seh146360 /* 713847Seh146360 * minimal size reserved in tx-ring 723847Seh146360 */ 733847Seh146360 #define IPW2200_TX_RING_MIN (8) 743847Seh146360 #define IPW2200_TXBUF_SIZE (IEEE80211_MAX_LEN) 753847Seh146360 #define IPW2200_RXBUF_SIZE (4096) 763847Seh146360 773847Seh146360 static void *ipw2200_ssp = NULL; 787656SSherry.Moore@Sun.COM static char ipw2200_ident[] = IPW2200_DRV_DESC; 793847Seh146360 803847Seh146360 /* 813847Seh146360 * PIO access attributor for registers 823847Seh146360 */ 833847Seh146360 static ddi_device_acc_attr_t ipw2200_csr_accattr = { 843847Seh146360 DDI_DEVICE_ATTR_V0, 853847Seh146360 DDI_STRUCTURE_LE_ACC, 863847Seh146360 DDI_STRICTORDER_ACC 873847Seh146360 }; 883847Seh146360 893847Seh146360 /* 903847Seh146360 * DMA access attributor for descriptors 913847Seh146360 */ 923847Seh146360 static ddi_device_acc_attr_t ipw2200_dma_accattr = { 933847Seh146360 DDI_DEVICE_ATTR_V0, 943847Seh146360 DDI_NEVERSWAP_ACC, 953847Seh146360 DDI_STRICTORDER_ACC 963847Seh146360 }; 973847Seh146360 983847Seh146360 /* 993847Seh146360 * Describes the chip's DMA engine 1003847Seh146360 */ 1013847Seh146360 static ddi_dma_attr_t ipw2200_dma_attr = { 1023847Seh146360 DMA_ATTR_V0, /* version */ 1033847Seh146360 0x0000000000000000ULL, /* addr_lo */ 1043847Seh146360 0x00000000ffffffffULL, /* addr_hi */ 1053847Seh146360 0x00000000ffffffffULL, /* counter */ 1063847Seh146360 0x0000000000000004ULL, /* alignment */ 1073847Seh146360 0xfff, /* burst */ 1083847Seh146360 1, /* min xfer */ 1093847Seh146360 0x00000000ffffffffULL, /* max xfer */ 1103847Seh146360 0x00000000ffffffffULL, /* seg boud */ 1113847Seh146360 1, /* s/g list */ 1123847Seh146360 1, /* granularity */ 1133847Seh146360 0 /* flags */ 1143847Seh146360 }; 1153847Seh146360 1163847Seh146360 static uint8_t ipw2200_broadcast_addr[] = { 1173847Seh146360 0xff, 0xff, 0xff, 0xff, 0xff, 0xff 1183847Seh146360 }; 1193847Seh146360 static const struct ieee80211_rateset ipw2200_rateset_11a = { 8, 1203847Seh146360 {12, 18, 24, 36, 48, 72, 96, 108} 1213847Seh146360 }; 1223847Seh146360 static const struct ieee80211_rateset ipw2200_rateset_11b = { 4, 1233847Seh146360 {2, 4, 11, 22} 1243847Seh146360 }; 1253847Seh146360 static const struct ieee80211_rateset ipw2200_rateset_11g = { 12, 1263847Seh146360 {2, 4, 11, 22, 12, 18, 24, 36, 48, 72, 96, 108} 1273847Seh146360 }; 1283847Seh146360 1293847Seh146360 /* 1303847Seh146360 * Used by multi function thread 1313847Seh146360 */ 1323847Seh146360 extern pri_t minclsyspri; 1333847Seh146360 1343847Seh146360 /* 1353847Seh146360 * ipw2200 specific hardware operations 1363847Seh146360 */ 1373847Seh146360 static void ipw2200_hwconf_get(struct ipw2200_softc *sc); 1383847Seh146360 static int ipw2200_chip_reset(struct ipw2200_softc *sc); 1393847Seh146360 static void ipw2200_master_stop(struct ipw2200_softc *sc); 1403847Seh146360 static void ipw2200_stop(struct ipw2200_softc *sc); 1413847Seh146360 static int ipw2200_config(struct ipw2200_softc *sc); 1423847Seh146360 static int ipw2200_cmd(struct ipw2200_softc *sc, 1433847Seh146360 uint32_t type, void *buf, size_t len, int async); 1443847Seh146360 static void ipw2200_ring_hwsetup(struct ipw2200_softc *sc); 1453847Seh146360 static int ipw2200_ring_alloc(struct ipw2200_softc *sc); 1463847Seh146360 static void ipw2200_ring_free(struct ipw2200_softc *sc); 1473847Seh146360 static void ipw2200_ring_reset(struct ipw2200_softc *sc); 1483847Seh146360 static int ipw2200_ring_init(struct ipw2200_softc *sc); 1493847Seh146360 1503847Seh146360 /* 1513847Seh146360 * GLD specific operations 1523847Seh146360 */ 1533847Seh146360 static int ipw2200_m_stat(void *arg, uint_t stat, uint64_t *val); 1543847Seh146360 static int ipw2200_m_start(void *arg); 1553847Seh146360 static void ipw2200_m_stop(void *arg); 1563847Seh146360 static int ipw2200_m_unicst(void *arg, const uint8_t *macaddr); 1573847Seh146360 static int ipw2200_m_multicst(void *arg, boolean_t add, const uint8_t *m); 1583847Seh146360 static int ipw2200_m_promisc(void *arg, boolean_t on); 1593847Seh146360 static void ipw2200_m_ioctl(void *arg, queue_t *wq, mblk_t *mp); 1603847Seh146360 static mblk_t *ipw2200_m_tx(void *arg, mblk_t *mp); 161*8367SFei.Feng@Sun.COM static int ipw2200_m_setprop(void *arg, const char *pr_name, 162*8367SFei.Feng@Sun.COM mac_prop_id_t wldp_pr_num, uint_t wldp_length, const void *wldp_buf); 163*8367SFei.Feng@Sun.COM static int ipw2200_m_getprop(void *arg, const char *pr_name, 164*8367SFei.Feng@Sun.COM mac_prop_id_t wldp_pr_num, uint_t pr_flags, uint_t wldp_length, 165*8367SFei.Feng@Sun.COM void *wldp_buf, uint_t *perm); 166*8367SFei.Feng@Sun.COM 1673847Seh146360 1683847Seh146360 /* 1693847Seh146360 * Interrupt and Data transferring operations 1703847Seh146360 */ 1713847Seh146360 static uint_t ipw2200_intr(caddr_t arg); 1723847Seh146360 static int ipw2200_send(struct ieee80211com *ic, mblk_t *mp, uint8_t type); 1733847Seh146360 static void ipw2200_rcv_frame(struct ipw2200_softc *sc, 1743847Seh146360 struct ipw2200_frame *frame); 1753847Seh146360 static void ipw2200_rcv_notif(struct ipw2200_softc *sc, 1763847Seh146360 struct ipw2200_notif *notif); 1773847Seh146360 1783847Seh146360 /* 1793847Seh146360 * WiFi specific operations 1803847Seh146360 */ 1813847Seh146360 static int ipw2200_newstate(struct ieee80211com *ic, 1823847Seh146360 enum ieee80211_state state, int arg); 1833847Seh146360 static void ipw2200_thread(struct ipw2200_softc *sc); 1843847Seh146360 1853847Seh146360 /* 1863847Seh146360 * IOCTL Handler 1873847Seh146360 */ 1883847Seh146360 static int ipw2200_ioctl(struct ipw2200_softc *sc, queue_t *q, mblk_t *m); 1893847Seh146360 static int ipw2200_getset(struct ipw2200_softc *sc, 1903847Seh146360 mblk_t *m, uint32_t cmd, boolean_t *need_net80211); 1913847Seh146360 static int iwi_wificfg_radio(struct ipw2200_softc *sc, 1923847Seh146360 uint32_t cmd, wldp_t *outfp); 1933847Seh146360 static int iwi_wificfg_desrates(wldp_t *outfp); 1943847Seh146360 1953847Seh146360 /* 1965485Seh146360 * net80211 functions 1975485Seh146360 */ 1985485Seh146360 extern uint8_t ieee80211_crypto_getciphertype(ieee80211com_t *ic); 1995485Seh146360 extern void ieee80211_notify_node_join(ieee80211com_t *ic, 2005485Seh146360 ieee80211_node_t *in); 2015485Seh146360 extern void ieee80211_notify_node_leave(ieee80211com_t *ic, 2025485Seh146360 ieee80211_node_t *in); 2035485Seh146360 2045485Seh146360 /* 2053847Seh146360 * Mac Call Back entries 2063847Seh146360 */ 2073847Seh146360 mac_callbacks_t ipw2200_m_callbacks = { 208*8367SFei.Feng@Sun.COM MC_IOCTL | MC_SETPROP | MC_GETPROP, 2093847Seh146360 ipw2200_m_stat, 2103847Seh146360 ipw2200_m_start, 2113847Seh146360 ipw2200_m_stop, 2123847Seh146360 ipw2200_m_promisc, 2133847Seh146360 ipw2200_m_multicst, 2143847Seh146360 ipw2200_m_unicst, 2153847Seh146360 ipw2200_m_tx, 216*8367SFei.Feng@Sun.COM ipw2200_m_ioctl, 217*8367SFei.Feng@Sun.COM NULL, 218*8367SFei.Feng@Sun.COM NULL, 219*8367SFei.Feng@Sun.COM NULL, 220*8367SFei.Feng@Sun.COM ipw2200_m_setprop, 221*8367SFei.Feng@Sun.COM ipw2200_m_getprop 2223847Seh146360 }; 2233847Seh146360 2243847Seh146360 /* 2253847Seh146360 * DEBUG Facility 2263847Seh146360 */ 2273847Seh146360 #define MAX_MSG (128) 2283847Seh146360 uint32_t ipw2200_debug = 0; 2293847Seh146360 /* 2303847Seh146360 * supported debug marks are: 2313847Seh146360 * | IPW2200_DBG_CSR 2323847Seh146360 * | IPW2200_DBG_TABLE 2333847Seh146360 * | IPW2200_DBG_HWCAP 2343847Seh146360 * | IPW2200_DBG_TX 2353847Seh146360 * | IPW2200_DBG_INIT 2363847Seh146360 * | IPW2200_DBG_FW 2373847Seh146360 * | IPW2200_DBG_NOTIF 2383847Seh146360 * | IPW2200_DBG_SCAN 2393847Seh146360 * | IPW2200_DBG_IOCTL 2403847Seh146360 * | IPW2200_DBG_RING 2413847Seh146360 * | IPW2200_DBG_INT 2423847Seh146360 * | IPW2200_DBG_RX 2433847Seh146360 * | IPW2200_DBG_DMA 2443847Seh146360 * | IPW2200_DBG_GLD 2453847Seh146360 * | IPW2200_DBG_WIFI 2463847Seh146360 * | IPW2200_DBG_SOFTINT 2477194Seh146360 * | IPW2200_DBG_SUSPEND 248*8367SFei.Feng@Sun.COM * | IPW2200_DBG_BRUSSELS 2493847Seh146360 */ 2503847Seh146360 2513847Seh146360 /* 2523847Seh146360 * Global tunning parameter to work around unknown hardware issues 2533847Seh146360 */ 2543847Seh146360 static uint32_t delay_config_stable = 100000; /* 100ms */ 2553847Seh146360 static uint32_t delay_fatal_recover = 100000 * 20; /* 2s */ 2563847Seh146360 static uint32_t delay_aux_thread = 100000; /* 100ms */ 2573847Seh146360 2583847Seh146360 #define IEEE80211_IS_CHAN_2GHZ(_c) \ 2593847Seh146360 (((_c)->ich_flags & IEEE80211_CHAN_2GHZ) != 0) 2603847Seh146360 #define IEEE80211_IS_CHAN_5GHZ(_c) \ 2613847Seh146360 (((_c)->ich_flags & IEEE80211_CHAN_5GHZ) != 0) 2623847Seh146360 #define isset(a, i) ((a)[(i)/NBBY] & (1 << ((i)%NBBY))) 2633847Seh146360 2643847Seh146360 void 2653847Seh146360 ipw2200_dbg(dev_info_t *dip, int level, const char *fmt, ...) 2663847Seh146360 { 2673847Seh146360 va_list ap; 2683847Seh146360 char buf[MAX_MSG]; 2693847Seh146360 int instance; 2703847Seh146360 2713847Seh146360 va_start(ap, fmt); 2723847Seh146360 (void) vsnprintf(buf, sizeof (buf), fmt, ap); 2733847Seh146360 va_end(ap); 2743847Seh146360 2753847Seh146360 if (dip) { 2763847Seh146360 instance = ddi_get_instance(dip); 2773847Seh146360 cmn_err(level, "%s%d: %s", IPW2200_DRV_NAME, instance, buf); 2783847Seh146360 } else 2793847Seh146360 cmn_err(level, "%s: %s", IPW2200_DRV_NAME, buf); 2803847Seh146360 2813847Seh146360 } 2823847Seh146360 2833847Seh146360 /* 2847194Seh146360 * Set up pci 2857194Seh146360 */ 2867194Seh146360 int 2877194Seh146360 ipw2200_setup_pci(dev_info_t *dip, struct ipw2200_softc *sc) 2887194Seh146360 { 2897194Seh146360 ddi_acc_handle_t cfgh; 2907194Seh146360 caddr_t regs; 2917194Seh146360 int err; 2927194Seh146360 2937194Seh146360 /* 2947194Seh146360 * Map config spaces register to read the vendor id, device id, sub 2957194Seh146360 * vendor id, and sub device id. 2967194Seh146360 */ 2977194Seh146360 err = ddi_regs_map_setup(dip, IPW2200_PCI_CFG_RNUM, ®s, 2987194Seh146360 0, 0, &ipw2200_csr_accattr, &cfgh); 2997194Seh146360 if (err != DDI_SUCCESS) { 3007194Seh146360 IPW2200_WARN((dip, CE_WARN, 3017194Seh146360 "ipw2200_attach(): unable to map spaces regs\n")); 3027194Seh146360 return (DDI_FAILURE); 3037194Seh146360 } 3047194Seh146360 3057194Seh146360 ddi_put8(cfgh, (uint8_t *)(regs + 0x41), 0); 3067194Seh146360 sc->sc_vendor = ddi_get16(cfgh, 3077194Seh146360 (uint16_t *)((uintptr_t)regs + PCI_CONF_VENID)); 3087194Seh146360 sc->sc_device = ddi_get16(cfgh, 3097194Seh146360 (uint16_t *)((uintptr_t)regs + PCI_CONF_DEVID)); 3107194Seh146360 sc->sc_subven = ddi_get16(cfgh, 3117194Seh146360 (uint16_t *)((uintptr_t)regs + PCI_CONF_SUBVENID)); 3127194Seh146360 sc->sc_subdev = ddi_get16(cfgh, 3137194Seh146360 (uint16_t *)((uintptr_t)regs + PCI_CONF_SUBSYSID)); 3147194Seh146360 IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT, 3157194Seh146360 "ipw2200_setup_pci(): vendor = 0x%04x, devic = 0x%04x," 3167194Seh146360 "subversion = 0x%04x, subdev = 0x%04x", 3177194Seh146360 sc->sc_vendor, sc->sc_device, sc->sc_subven, sc->sc_subdev)); 3187194Seh146360 3197194Seh146360 ddi_regs_map_free(&cfgh); 3207194Seh146360 3217194Seh146360 return (DDI_SUCCESS); 3227194Seh146360 3237194Seh146360 } 3247194Seh146360 3257194Seh146360 /* 3263847Seh146360 * Device operations 3273847Seh146360 */ 3283847Seh146360 int 3293847Seh146360 ipw2200_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 3303847Seh146360 { 3313847Seh146360 struct ipw2200_softc *sc; 3323847Seh146360 struct ieee80211com *ic; 3333847Seh146360 int instance, err, i; 3343847Seh146360 char strbuf[32]; 3353847Seh146360 wifi_data_t wd = { 0 }; 3363847Seh146360 mac_register_t *macp; 3377194Seh146360 3387194Seh146360 switch (cmd) { 3397194Seh146360 case DDI_ATTACH: 3407194Seh146360 break; 3417194Seh146360 case DDI_RESUME: 3427194Seh146360 sc = ddi_get_soft_state(ipw2200_ssp, ddi_get_instance(dip)); 3437194Seh146360 ASSERT(sc != NULL); 3447194Seh146360 3457194Seh146360 /* 3467194Seh146360 * set up pci 3477194Seh146360 */ 3487194Seh146360 err = ipw2200_setup_pci(dip, sc); 3497194Seh146360 if (err != DDI_SUCCESS) { 3507194Seh146360 IPW2200_DBG(IPW2200_DBG_SUSPEND, (sc->sc_dip, CE_CONT, 3517194Seh146360 "ipw2200_attach(): resume failure\n")); 3527194Seh146360 return (DDI_FAILURE); 3537194Seh146360 } 3547194Seh146360 3557194Seh146360 /* 3567194Seh146360 * resume hardware. 3577194Seh146360 * If it was on runnning status, reset to INIT state 3587194Seh146360 */ 3597194Seh146360 sc->sc_flags &= ~IPW2200_FLAG_SUSPEND; 3607194Seh146360 if (sc->sc_flags & IPW2200_FLAG_RUNNING) 3617194Seh146360 (void) ipw2200_init(sc); 3627194Seh146360 3637194Seh146360 IPW2200_DBG(IPW2200_DBG_SUSPEND, (sc->sc_dip, CE_CONT, 3647194Seh146360 "ipw2200_attach(): resume successful\n")); 3657194Seh146360 return (DDI_SUCCESS); 3667194Seh146360 default: 3677194Seh146360 return (DDI_FAILURE); 3683847Seh146360 } 3693847Seh146360 3703847Seh146360 instance = ddi_get_instance(dip); 3713847Seh146360 err = ddi_soft_state_zalloc(ipw2200_ssp, instance); 3723847Seh146360 if (err != DDI_SUCCESS) { 3733847Seh146360 IPW2200_WARN((dip, CE_WARN, 3743847Seh146360 "ipw2200_attach(): unable to allocate soft state\n")); 3753847Seh146360 goto fail1; 3763847Seh146360 } 3773847Seh146360 sc = ddi_get_soft_state(ipw2200_ssp, instance); 3783847Seh146360 sc->sc_dip = dip; 3793847Seh146360 3807194Seh146360 /* set up pci, put reg+0x41 0 */ 3817194Seh146360 err = ipw2200_setup_pci(dip, sc); 3823847Seh146360 if (err != DDI_SUCCESS) { 3833847Seh146360 IPW2200_WARN((dip, CE_WARN, 3847194Seh146360 "ipw2200_attach(): unable to setup pci\n")); 3853847Seh146360 goto fail2; 3863847Seh146360 } 3873847Seh146360 3883847Seh146360 /* 3893847Seh146360 * Map operating registers 3903847Seh146360 */ 3913847Seh146360 err = ddi_regs_map_setup(dip, IPW2200_PCI_CSR_RNUM, &sc->sc_regs, 3923847Seh146360 0, 0, &ipw2200_csr_accattr, &sc->sc_ioh); 3933847Seh146360 if (err != DDI_SUCCESS) { 3943847Seh146360 IPW2200_WARN((dip, CE_WARN, 3953847Seh146360 "ipw2200_attach(): ddi_regs_map_setup() failed\n")); 3963847Seh146360 goto fail2; 3973847Seh146360 } 3983847Seh146360 3993847Seh146360 /* 4003847Seh146360 * Reset the chip 4013847Seh146360 */ 4023847Seh146360 err = ipw2200_chip_reset(sc); 4033847Seh146360 if (err != DDI_SUCCESS) { 4043847Seh146360 IPW2200_WARN((dip, CE_WARN, 4053847Seh146360 "ipw2200_attach(): ipw2200_chip_reset() failed\n")); 4063847Seh146360 goto fail3; 4073847Seh146360 } 4083847Seh146360 4093847Seh146360 /* 4103847Seh146360 * Get the hardware configuration, including the MAC address 4113847Seh146360 * Then, init all the rings needed. 4123847Seh146360 */ 4133847Seh146360 ipw2200_hwconf_get(sc); 4143847Seh146360 err = ipw2200_ring_init(sc); 4153847Seh146360 if (err != DDI_SUCCESS) { 4163847Seh146360 IPW2200_WARN((dip, CE_WARN, 4173847Seh146360 "ipw2200_attach(): ipw2200_ring_init() failed\n")); 4183847Seh146360 goto fail3; 4193847Seh146360 } 4203847Seh146360 4213847Seh146360 /* 4223847Seh146360 * Initialize mutexs and condvars 4233847Seh146360 */ 4243847Seh146360 err = ddi_get_iblock_cookie(dip, 0, &sc->sc_iblk); 4253847Seh146360 if (err != DDI_SUCCESS) { 4263847Seh146360 IPW2200_WARN((dip, CE_WARN, 4273847Seh146360 "ipw2200_attach(): ddi_get_iblock_cookie() failed\n")); 4283847Seh146360 goto fail4; 4293847Seh146360 } 4303847Seh146360 4313847Seh146360 /* 4323847Seh146360 * interrupt lock 4333847Seh146360 */ 4343847Seh146360 mutex_init(&sc->sc_ilock, "intr-lock", MUTEX_DRIVER, 4353847Seh146360 (void *) sc->sc_iblk); 4363847Seh146360 cv_init(&sc->sc_fw_cond, "firmware-ok", CV_DRIVER, NULL); 4373847Seh146360 cv_init(&sc->sc_cmd_status_cond, "cmd-status-ring", CV_DRIVER, NULL); 4383847Seh146360 4393847Seh146360 /* 4403847Seh146360 * command ring lock 4413847Seh146360 */ 4423847Seh146360 mutex_init(&sc->sc_cmd_lock, "cmd-ring", MUTEX_DRIVER, 4433847Seh146360 (void *) sc->sc_iblk); 4443847Seh146360 cv_init(&sc->sc_cmd_cond, "cmd-ring", CV_DRIVER, NULL); 4453847Seh146360 4463847Seh146360 /* 4473847Seh146360 * tx ring lock 4483847Seh146360 */ 4493847Seh146360 mutex_init(&sc->sc_tx_lock, "tx-ring", MUTEX_DRIVER, 4503847Seh146360 (void *) sc->sc_iblk); 4513847Seh146360 4523847Seh146360 /* 4535485Seh146360 * rescheduled lock 4545485Seh146360 */ 4555485Seh146360 mutex_init(&sc->sc_resched_lock, "reschedule-lock", MUTEX_DRIVER, 4565485Seh146360 (void *) sc->sc_iblk); 4575485Seh146360 4585485Seh146360 /* 4593847Seh146360 * multi-function lock, may acquire this during interrupt 4603847Seh146360 */ 4613847Seh146360 mutex_init(&sc->sc_mflock, "function-lock", MUTEX_DRIVER, 4623847Seh146360 (void *) sc->sc_iblk); 4633847Seh146360 cv_init(&sc->sc_mfthread_cv, NULL, CV_DRIVER, NULL); 4643847Seh146360 sc->sc_mf_thread = NULL; 4653847Seh146360 sc->sc_mfthread_switch = 0; 4663847Seh146360 4673847Seh146360 /* 4685485Seh146360 * Initialize the WiFi part 4693847Seh146360 */ 4703847Seh146360 ic = &sc->sc_ic; 4713847Seh146360 ic->ic_phytype = IEEE80211_T_OFDM; 4723847Seh146360 ic->ic_opmode = IEEE80211_M_STA; 4733847Seh146360 ic->ic_state = IEEE80211_S_INIT; 4743847Seh146360 ic->ic_maxrssi = 100; /* experimental number */ 4755485Seh146360 ic->ic_caps = 4765485Seh146360 IEEE80211_C_SHPREAMBLE | 4775485Seh146360 IEEE80211_C_TXPMGT | 4785485Seh146360 IEEE80211_C_PMGT | 4795485Seh146360 IEEE80211_C_WPA; 4803847Seh146360 4813847Seh146360 /* 4823847Seh146360 * set mac addr 4833847Seh146360 */ 4843847Seh146360 IEEE80211_ADDR_COPY(ic->ic_macaddr, sc->sc_macaddr); 4853847Seh146360 4863847Seh146360 /* 4873847Seh146360 * set supported .11a rates and channel - (2915ABG only) 4883847Seh146360 */ 4897194Seh146360 if (sc->sc_device >= 0x4223) { 4903847Seh146360 /* .11a rates */ 4913847Seh146360 ic->ic_sup_rates[IEEE80211_MODE_11A] = ipw2200_rateset_11a; 4923847Seh146360 /* .11a channels */ 4933847Seh146360 for (i = 36; i <= 64; i += 4) { 4943847Seh146360 ic->ic_sup_channels[i].ich_freq = 4953847Seh146360 ieee80211_ieee2mhz(i, IEEE80211_CHAN_5GHZ); 4963847Seh146360 ic->ic_sup_channels[i].ich_flags = /* CHAN_A */ 4973847Seh146360 IEEE80211_CHAN_5GHZ | IEEE80211_CHAN_OFDM; 4983847Seh146360 } 4993847Seh146360 for (i = 149; i <= 165; i += 4) { 5003847Seh146360 ic->ic_sup_channels[i].ich_freq = 5013847Seh146360 ieee80211_ieee2mhz(i, IEEE80211_CHAN_5GHZ); 5023847Seh146360 ic->ic_sup_channels[i].ich_flags = /* CHAN_A */ 5033847Seh146360 IEEE80211_CHAN_5GHZ | IEEE80211_CHAN_OFDM; 5043847Seh146360 } 5053847Seh146360 } 5063847Seh146360 5073847Seh146360 /* 5083847Seh146360 * set supported .11b and .11g rates 5093847Seh146360 */ 5103847Seh146360 ic->ic_sup_rates[IEEE80211_MODE_11B] = ipw2200_rateset_11b; 5113847Seh146360 ic->ic_sup_rates[IEEE80211_MODE_11G] = ipw2200_rateset_11g; 5123847Seh146360 5133847Seh146360 /* 5143847Seh146360 * set supported .11b and .11g channels(1 through 14) 5153847Seh146360 */ 5163847Seh146360 for (i = 1; i < 14; i++) { 5173847Seh146360 ic->ic_sup_channels[i].ich_freq = 5183847Seh146360 ieee80211_ieee2mhz(i, IEEE80211_CHAN_2GHZ); 5193847Seh146360 ic->ic_sup_channels[i].ich_flags = 5203847Seh146360 IEEE80211_CHAN_CCK | IEEE80211_CHAN_OFDM | 5213847Seh146360 IEEE80211_CHAN_DYN | IEEE80211_CHAN_2GHZ; 5223847Seh146360 } 5233847Seh146360 5243847Seh146360 /* 5253847Seh146360 * IBSS channal undefined for now 5263847Seh146360 */ 5273847Seh146360 ic->ic_ibss_chan = &ic->ic_sup_channels[0]; 5283847Seh146360 ic->ic_xmit = ipw2200_send; 5293847Seh146360 5303847Seh146360 /* 5313847Seh146360 * init generic layer, then override state transition machine 5323847Seh146360 */ 5333847Seh146360 ieee80211_attach(ic); 5343847Seh146360 5353847Seh146360 /* 5365485Seh146360 * different instance has different WPA door 5375485Seh146360 */ 5385485Seh146360 ieee80211_register_door(ic, ddi_driver_name(dip), instance); 5395485Seh146360 5405485Seh146360 /* 5413847Seh146360 * Override 80211 default routines 5423847Seh146360 */ 5433847Seh146360 ieee80211_media_init(ic); /* initial the node table and bss */ 5443847Seh146360 sc->sc_newstate = ic->ic_newstate; 5453847Seh146360 ic->ic_newstate = ipw2200_newstate; 5463847Seh146360 ic->ic_def_txkey = 0; 5473847Seh146360 sc->sc_authmode = IEEE80211_AUTH_OPEN; 5483847Seh146360 5493847Seh146360 /* 5503847Seh146360 * Add the interrupt handler 5513847Seh146360 */ 5523847Seh146360 err = ddi_add_intr(dip, 0, &sc->sc_iblk, NULL, 5533847Seh146360 ipw2200_intr, (caddr_t)sc); 5543847Seh146360 if (err != DDI_SUCCESS) { 5553847Seh146360 IPW2200_WARN((dip, CE_WARN, 5563847Seh146360 "ipw2200_attach(): ddi_add_intr() failed\n")); 5573847Seh146360 goto fail5; 5583847Seh146360 } 5593847Seh146360 5603847Seh146360 /* 5613847Seh146360 * Initialize pointer to device specific functions 5623847Seh146360 */ 5633847Seh146360 wd.wd_secalloc = WIFI_SEC_NONE; 5643847Seh146360 wd.wd_opmode = ic->ic_opmode; 5655485Seh146360 IEEE80211_ADDR_COPY(wd.wd_bssid, ic->ic_bss->in_bssid); 5663847Seh146360 5673847Seh146360 macp = mac_alloc(MAC_VERSION); 5683847Seh146360 if (err != 0) { 5693847Seh146360 IPW2200_WARN((dip, CE_WARN, 5703847Seh146360 "ipw2200_attach(): mac_alloc() failed\n")); 5713847Seh146360 goto fail6; 5723847Seh146360 } 5733847Seh146360 5743847Seh146360 macp->m_type_ident = MAC_PLUGIN_IDENT_WIFI; 5753847Seh146360 macp->m_driver = sc; 5763847Seh146360 macp->m_dip = dip; 5773847Seh146360 macp->m_src_addr = ic->ic_macaddr; 5783847Seh146360 macp->m_callbacks = &ipw2200_m_callbacks; 5793847Seh146360 macp->m_min_sdu = 0; 5803847Seh146360 macp->m_max_sdu = IEEE80211_MTU; 5813847Seh146360 macp->m_pdata = &wd; 5823847Seh146360 macp->m_pdata_size = sizeof (wd); 5833847Seh146360 5843847Seh146360 /* 5853847Seh146360 * Register the macp to mac 5863847Seh146360 */ 5873847Seh146360 err = mac_register(macp, &ic->ic_mach); 5883847Seh146360 mac_free(macp); 5893847Seh146360 if (err != DDI_SUCCESS) { 5903847Seh146360 IPW2200_WARN((dip, CE_WARN, 5913847Seh146360 "ipw2200_attach(): mac_register() failed\n")); 5923847Seh146360 goto fail6; 5933847Seh146360 } 5943847Seh146360 5953847Seh146360 /* 5963847Seh146360 * Create minor node of type DDI_NT_NET_WIFI 5973847Seh146360 */ 5983847Seh146360 (void) snprintf(strbuf, sizeof (strbuf), "%s%d", 5993847Seh146360 IPW2200_DRV_NAME, instance); 6003847Seh146360 err = ddi_create_minor_node(dip, strbuf, S_IFCHR, 6013847Seh146360 instance + 1, DDI_NT_NET_WIFI, 0); 6023847Seh146360 if (err != DDI_SUCCESS) 6033847Seh146360 IPW2200_WARN((dip, CE_WARN, 6043847Seh146360 "ipw2200_attach(): ddi_create_minor_node() failed\n")); 6053847Seh146360 6063847Seh146360 /* 6073847Seh146360 * Cache firmware will always be true 6083847Seh146360 */ 6093847Seh146360 (void) ipw2200_cache_firmware(sc); 6103847Seh146360 6113847Seh146360 /* 6123847Seh146360 * Notify link is down now 6133847Seh146360 */ 6143847Seh146360 mac_link_update(ic->ic_mach, LINK_STATE_DOWN); 6153847Seh146360 6163847Seh146360 /* 6173847Seh146360 * Create the mf thread to handle the link status, 6183847Seh146360 * recovery fatal error, etc. 6193847Seh146360 */ 6203847Seh146360 sc->sc_mfthread_switch = 1; 6213847Seh146360 if (sc->sc_mf_thread == NULL) 6223847Seh146360 sc->sc_mf_thread = thread_create((caddr_t)NULL, 0, 6233847Seh146360 ipw2200_thread, sc, 0, &p0, TS_RUN, minclsyspri); 6243847Seh146360 6253847Seh146360 return (DDI_SUCCESS); 6263847Seh146360 6273847Seh146360 fail6: 6283847Seh146360 ddi_remove_intr(dip, 0, sc->sc_iblk); 6293847Seh146360 fail5: 6303847Seh146360 ieee80211_detach(ic); 6313847Seh146360 6323847Seh146360 mutex_destroy(&sc->sc_ilock); 6333847Seh146360 mutex_destroy(&sc->sc_cmd_lock); 6343847Seh146360 mutex_destroy(&sc->sc_tx_lock); 6353847Seh146360 mutex_destroy(&sc->sc_mflock); 6365485Seh146360 mutex_destroy(&sc->sc_resched_lock); 6373847Seh146360 cv_destroy(&sc->sc_fw_cond); 6383847Seh146360 cv_destroy(&sc->sc_cmd_status_cond); 6393847Seh146360 cv_destroy(&sc->sc_cmd_cond); 6403847Seh146360 cv_destroy(&sc->sc_mfthread_cv); 6413847Seh146360 fail4: 6423847Seh146360 ipw2200_ring_free(sc); 6433847Seh146360 fail3: 6443847Seh146360 ddi_regs_map_free(&sc->sc_ioh); 6453847Seh146360 fail2: 6463847Seh146360 ddi_soft_state_free(ipw2200_ssp, instance); 6473847Seh146360 fail1: 6483847Seh146360 return (err); 6493847Seh146360 } 6503847Seh146360 6513847Seh146360 6523847Seh146360 int 6533847Seh146360 ipw2200_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 6543847Seh146360 { 6557194Seh146360 struct ipw2200_softc *sc; 6563847Seh146360 int err; 6577194Seh146360 6587194Seh146360 sc = ddi_get_soft_state(ipw2200_ssp, ddi_get_instance(dip)); 6593847Seh146360 ASSERT(sc != NULL); 6603847Seh146360 6617194Seh146360 switch (cmd) { 6627194Seh146360 case DDI_DETACH: 6637194Seh146360 break; 6647194Seh146360 case DDI_SUSPEND: 6657194Seh146360 if (sc->sc_flags & IPW2200_FLAG_RUNNING) { 6667194Seh146360 ipw2200_stop(sc); 6677194Seh146360 } 6687194Seh146360 sc->sc_flags |= IPW2200_FLAG_SUSPEND; 6697194Seh146360 6707194Seh146360 IPW2200_DBG(IPW2200_DBG_SUSPEND, (sc->sc_dip, CE_CONT, 6717194Seh146360 "ipw2200_detach(): suspend\n")); 6727194Seh146360 return (DDI_SUCCESS); 6737194Seh146360 default: 6743847Seh146360 return (DDI_FAILURE); 6757194Seh146360 } 6763847Seh146360 6777507SXinghua.Wen@Sun.COM err = mac_disable(sc->sc_ic.ic_mach); 6787507SXinghua.Wen@Sun.COM if (err != DDI_SUCCESS) 6797507SXinghua.Wen@Sun.COM return (err); 6807507SXinghua.Wen@Sun.COM 6813847Seh146360 ipw2200_stop(sc); 6823847Seh146360 6833847Seh146360 /* 6843847Seh146360 * Destroy the mf_thread 6853847Seh146360 */ 6863847Seh146360 mutex_enter(&sc->sc_mflock); 6873847Seh146360 sc->sc_mfthread_switch = 0; 6883847Seh146360 while (sc->sc_mf_thread != NULL) { 6893847Seh146360 if (cv_wait_sig(&sc->sc_mfthread_cv, &sc->sc_mflock) == 0) 6903847Seh146360 break; 6913847Seh146360 } 6923847Seh146360 mutex_exit(&sc->sc_mflock); 6933847Seh146360 6943847Seh146360 /* 6953847Seh146360 * Unregister from the MAC layer subsystem 6963847Seh146360 */ 6977507SXinghua.Wen@Sun.COM (void) mac_unregister(sc->sc_ic.ic_mach); 6983847Seh146360 6993847Seh146360 ddi_remove_intr(dip, IPW2200_PCI_INTR_NUM, sc->sc_iblk); 7003847Seh146360 7013847Seh146360 mutex_destroy(&sc->sc_ilock); 7023847Seh146360 mutex_destroy(&sc->sc_cmd_lock); 7033847Seh146360 mutex_destroy(&sc->sc_tx_lock); 7043847Seh146360 mutex_destroy(&sc->sc_mflock); 7055485Seh146360 mutex_destroy(&sc->sc_resched_lock); 7063847Seh146360 cv_destroy(&sc->sc_fw_cond); 7073847Seh146360 cv_destroy(&sc->sc_cmd_status_cond); 7083847Seh146360 cv_destroy(&sc->sc_cmd_cond); 7093847Seh146360 cv_destroy(&sc->sc_mfthread_cv); 7103847Seh146360 7113847Seh146360 /* 7123847Seh146360 * Detach ieee80211 7133847Seh146360 */ 7143847Seh146360 ieee80211_detach(&sc->sc_ic); 7153847Seh146360 7163847Seh146360 (void) ipw2200_free_firmware(sc); 7173847Seh146360 ipw2200_ring_free(sc); 7183847Seh146360 7193847Seh146360 ddi_regs_map_free(&sc->sc_ioh); 7203847Seh146360 ddi_remove_minor_node(dip, NULL); 7213847Seh146360 ddi_soft_state_free(ipw2200_ssp, ddi_get_instance(dip)); 7223847Seh146360 7233847Seh146360 return (DDI_SUCCESS); 7243847Seh146360 } 7253847Seh146360 7267809SFei.Feng@Sun.COM /* 7277809SFei.Feng@Sun.COM * quiesce(9E) entry point. 7287809SFei.Feng@Sun.COM * This function is called when the system is single-threaded at high 7297809SFei.Feng@Sun.COM * PIL with preemption disabled. Therefore, this function must not be 7307809SFei.Feng@Sun.COM * blocked. 7317809SFei.Feng@Sun.COM * This function returns DDI_SUCCESS on success, or DDI_FAILURE on failure. 7327809SFei.Feng@Sun.COM * DDI_FAILURE indicates an error condition and should almost never happen. 7337809SFei.Feng@Sun.COM */ 7347809SFei.Feng@Sun.COM static int 7357809SFei.Feng@Sun.COM ipw2200_quiesce(dev_info_t *dip) 7364812Seh146360 { 7374812Seh146360 struct ipw2200_softc *sc = 7384812Seh146360 ddi_get_soft_state(ipw2200_ssp, ddi_get_instance(dip)); 7397809SFei.Feng@Sun.COM if (sc == NULL) 7407809SFei.Feng@Sun.COM return (DDI_FAILURE); 7417809SFei.Feng@Sun.COM 7428165SFei.Feng@Sun.COM /* by pass any messages, if it's quiesce */ 7438165SFei.Feng@Sun.COM ipw2200_debug = 0; 7448165SFei.Feng@Sun.COM 7457809SFei.Feng@Sun.COM /* 7467809SFei.Feng@Sun.COM * No more blocking is allowed while we are in the 7477809SFei.Feng@Sun.COM * quiesce(9E) entry point. 7487809SFei.Feng@Sun.COM */ 7497809SFei.Feng@Sun.COM sc->sc_flags |= IPW2200_FLAG_QUIESCED; 7507809SFei.Feng@Sun.COM 7517809SFei.Feng@Sun.COM /* 7527809SFei.Feng@Sun.COM * Disable and mask all interrupts. 7537809SFei.Feng@Sun.COM */ 7548165SFei.Feng@Sun.COM ipw2200_master_stop(sc); 7558165SFei.Feng@Sun.COM ipw2200_csr_put32(sc, IPW2200_CSR_RST, IPW2200_RST_SW_RESET); 7564812Seh146360 return (DDI_SUCCESS); 7574812Seh146360 } 7584812Seh146360 7593847Seh146360 static void 7603847Seh146360 ipw2200_stop(struct ipw2200_softc *sc) 7613847Seh146360 { 7623847Seh146360 struct ieee80211com *ic = &sc->sc_ic; 7633847Seh146360 7643847Seh146360 IPW2200_DBG(IPW2200_DBG_HWCAP, (sc->sc_dip, CE_CONT, 7653847Seh146360 "ipw2200_stop(): enter\n")); 7663847Seh146360 7673847Seh146360 ipw2200_master_stop(sc); 7683847Seh146360 ipw2200_csr_put32(sc, IPW2200_CSR_RST, IPW2200_RST_SW_RESET); 7693847Seh146360 7703847Seh146360 /* 7713847Seh146360 * Reset ring 7723847Seh146360 */ 7733847Seh146360 ipw2200_ring_reset(sc); 7743847Seh146360 7758165SFei.Feng@Sun.COM ieee80211_new_state(ic, IEEE80211_S_INIT, -1); 7763847Seh146360 sc->sc_flags &= ~IPW2200_FLAG_SCANNING; 7775485Seh146360 sc->sc_flags &= ~IPW2200_FLAG_ASSOCIATED; 7783847Seh146360 7798165SFei.Feng@Sun.COM IPW2200_DBG(IPW2200_DBG_HWCAP, (sc->sc_dip, CE_CONT, 7808165SFei.Feng@Sun.COM "ipw2200_stop(): exit\n")); 7813847Seh146360 } 7823847Seh146360 7833847Seh146360 static int 7843847Seh146360 ipw2200_config(struct ipw2200_softc *sc) 7853847Seh146360 { 7863847Seh146360 struct ieee80211com *ic = &sc->sc_ic; 7873847Seh146360 struct ipw2200_configuration cfg; 7883847Seh146360 uint32_t data; 7893847Seh146360 struct ipw2200_txpower pwr; 7903847Seh146360 struct ipw2200_rateset rs; 7913847Seh146360 struct ipw2200_wep_key wkey; 7923847Seh146360 int err, i; 7933847Seh146360 7943847Seh146360 /* 7953847Seh146360 * Set the IBSS mode channel: Tx power 7963847Seh146360 */ 7973847Seh146360 if (ic->ic_opmode == IEEE80211_M_IBSS) { 7983847Seh146360 pwr.mode = IPW2200_MODE_11B; 7993847Seh146360 pwr.nchan = 11; 8003847Seh146360 for (i = 0; i < pwr.nchan; i++) { 8013847Seh146360 pwr.chan[i].chan = i + 1; 8023847Seh146360 pwr.chan[i].power = IPW2200_TXPOWER_MAX; 8033847Seh146360 } 8043847Seh146360 IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT, 8053847Seh146360 "ipw2200_config(): Setting .11b channels Tx power\n")); 8063847Seh146360 err = ipw2200_cmd(sc, IPW2200_CMD_SET_TX_POWER, 8073847Seh146360 &pwr, sizeof (pwr), 0); 8083847Seh146360 if (err != DDI_SUCCESS) 8093847Seh146360 return (err); 8103847Seh146360 8113847Seh146360 pwr.mode = IPW2200_MODE_11G; 8123847Seh146360 IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT, 8133847Seh146360 "ipw2200_config(): Setting .11g channels Tx power\n")); 8143847Seh146360 err = ipw2200_cmd(sc, IPW2200_CMD_SET_TX_POWER, 8153847Seh146360 &pwr, sizeof (pwr), 0); 8163847Seh146360 if (err != DDI_SUCCESS) 8173847Seh146360 return (err); 8183847Seh146360 } 8193847Seh146360 8203847Seh146360 /* 8213847Seh146360 * Set MAC address 8223847Seh146360 */ 8233847Seh146360 IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT, 8243847Seh146360 "ipw2200_config(): Setting MAC address to " 8253847Seh146360 "%02x:%02x:%02x:%02x:%02x:%02x\n", 8263847Seh146360 ic->ic_macaddr[0], ic->ic_macaddr[1], ic->ic_macaddr[2], 8273847Seh146360 ic->ic_macaddr[3], ic->ic_macaddr[4], ic->ic_macaddr[5])); 8283847Seh146360 err = ipw2200_cmd(sc, IPW2200_CMD_SET_MAC_ADDRESS, ic->ic_macaddr, 8293847Seh146360 IEEE80211_ADDR_LEN, 0); 8303847Seh146360 if (err != DDI_SUCCESS) 8313847Seh146360 return (err); 8323847Seh146360 8333847Seh146360 /* 8343847Seh146360 * Set basic system config settings: configuration(capabilities) 8353847Seh146360 */ 8363847Seh146360 (void) memset(&cfg, 0, sizeof (cfg)); 8373847Seh146360 cfg.bluetooth_coexistence = 1; 8383847Seh146360 cfg.multicast_enabled = 1; 8393847Seh146360 cfg.answer_pbreq = 1; 8403847Seh146360 cfg.noise_reported = 1; 8415485Seh146360 cfg.disable_multicast_decryption = 1; /* WPA */ 8425485Seh146360 cfg.disable_unicast_decryption = 1; /* WPA */ 8433847Seh146360 8443847Seh146360 IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT, 8453847Seh146360 "ipw2200_config(): Configuring adapter\n")); 8463847Seh146360 err = ipw2200_cmd(sc, IPW2200_CMD_SET_CONFIG, 8473847Seh146360 &cfg, sizeof (cfg), 0); 8483847Seh146360 if (err != DDI_SUCCESS) 8493847Seh146360 return (err); 8503847Seh146360 8513847Seh146360 /* 8523847Seh146360 * Set power mode 8533847Seh146360 */ 8543847Seh146360 data = LE_32(IPW2200_POWER_MODE_CAM); 8553847Seh146360 IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT, 8563847Seh146360 "ipw2200_config(): Setting power mode to %u\n", LE_32(data))); 8573847Seh146360 err = ipw2200_cmd(sc, IPW2200_CMD_SET_POWER_MODE, 8583847Seh146360 &data, sizeof (data), 0); 8593847Seh146360 if (err != DDI_SUCCESS) 8603847Seh146360 return (err); 8613847Seh146360 8623847Seh146360 /* 8633847Seh146360 * Set supported rates 8643847Seh146360 */ 8653847Seh146360 rs.mode = IPW2200_MODE_11G; 8663847Seh146360 rs.type = IPW2200_RATESET_TYPE_SUPPORTED; 8673847Seh146360 rs.nrates = ic->ic_sup_rates[IEEE80211_MODE_11G].ir_nrates; 8683847Seh146360 (void) memcpy(rs.rates, ic->ic_sup_rates[IEEE80211_MODE_11G].ir_rates, 8693847Seh146360 rs.nrates); 8703847Seh146360 IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT, 8713847Seh146360 "ipw2200_config(): Setting .11g supported rates(%u)\n", rs.nrates)); 8723847Seh146360 err = ipw2200_cmd(sc, IPW2200_CMD_SET_RATES, &rs, sizeof (rs), 0); 8733847Seh146360 if (err != DDI_SUCCESS) 8743847Seh146360 return (err); 8753847Seh146360 8763847Seh146360 rs.mode = IPW2200_MODE_11A; 8773847Seh146360 rs.type = IPW2200_RATESET_TYPE_SUPPORTED; 8783847Seh146360 rs.nrates = ic->ic_sup_rates[IEEE80211_MODE_11A].ir_nrates; 8793847Seh146360 (void) memcpy(rs.rates, ic->ic_sup_rates[IEEE80211_MODE_11A].ir_rates, 8803847Seh146360 rs.nrates); 8813847Seh146360 IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT, 8823847Seh146360 "ipw2200_config(): Setting .11a supported rates(%u)\n", rs.nrates)); 8833847Seh146360 err = ipw2200_cmd(sc, IPW2200_CMD_SET_RATES, &rs, sizeof (rs), 0); 8843847Seh146360 if (err != DDI_SUCCESS) 8853847Seh146360 return (err); 8863847Seh146360 8873847Seh146360 /* 8883847Seh146360 * Set RTS(request-to-send) threshold 8893847Seh146360 */ 8903847Seh146360 data = LE_32(ic->ic_rtsthreshold); 8913847Seh146360 IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT, 8923847Seh146360 "ipw2200_config(): Setting RTS threshold to %u\n", LE_32(data))); 8933847Seh146360 err = ipw2200_cmd(sc, IPW2200_CMD_SET_RTS_THRESHOLD, &data, 8943847Seh146360 sizeof (data), 0); 8953847Seh146360 if (err != DDI_SUCCESS) 8963847Seh146360 return (err); 8973847Seh146360 8983847Seh146360 /* 8993847Seh146360 * Set fragmentation threshold 9003847Seh146360 */ 9013847Seh146360 data = LE_32(ic->ic_fragthreshold); 9023847Seh146360 IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT, 9033847Seh146360 "ipw2200_config(): Setting fragmentation threshold to %u\n", 9043847Seh146360 LE_32(data))); 9053847Seh146360 err = ipw2200_cmd(sc, IPW2200_CMD_SET_FRAG_THRESHOLD, &data, 9063847Seh146360 sizeof (data), 0); 9073847Seh146360 if (err != DDI_SUCCESS) 9083847Seh146360 return (err); 9093847Seh146360 9103847Seh146360 /* 9113847Seh146360 * Set desired ESSID if we have 9123847Seh146360 */ 9133847Seh146360 if (ic->ic_des_esslen != 0) { 9143847Seh146360 IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT, 9153847Seh146360 "ipw2200_config(): Setting desired ESSID to " 9163847Seh146360 "(%u),%c%c%c%c%c%c%c%c\n", 9173847Seh146360 ic->ic_des_esslen, 9183847Seh146360 ic->ic_des_essid[0], ic->ic_des_essid[1], 9193847Seh146360 ic->ic_des_essid[2], ic->ic_des_essid[3], 9203847Seh146360 ic->ic_des_essid[4], ic->ic_des_essid[5], 9213847Seh146360 ic->ic_des_essid[6], ic->ic_des_essid[7])); 9223847Seh146360 err = ipw2200_cmd(sc, IPW2200_CMD_SET_ESSID, ic->ic_des_essid, 9233847Seh146360 ic->ic_des_esslen, 0); 9243847Seh146360 if (err != DDI_SUCCESS) 9253847Seh146360 return (err); 9263847Seh146360 } 9273847Seh146360 9283847Seh146360 /* 9293847Seh146360 * Set WEP initial vector(random seed) 9303847Seh146360 */ 9313847Seh146360 (void) random_get_pseudo_bytes((uint8_t *)&data, sizeof (data)); 9323847Seh146360 IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT, 9333847Seh146360 "ipw2200_config(): Setting initialization vector to %u\n", 9343847Seh146360 LE_32(data))); 9353847Seh146360 err = ipw2200_cmd(sc, IPW2200_CMD_SET_IV, &data, sizeof (data), 0); 9363847Seh146360 if (err != DDI_SUCCESS) 9373847Seh146360 return (err); 9383847Seh146360 9393847Seh146360 /* 9403847Seh146360 * Set WEP if any 9413847Seh146360 */ 9423847Seh146360 if (ic->ic_flags & IEEE80211_F_PRIVACY) { 9433847Seh146360 IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT, 9443847Seh146360 "ipw2200_config(): Setting Wep Key\n", LE_32(data))); 9453847Seh146360 for (i = 0; i < IEEE80211_WEP_NKID; i++) { 9463847Seh146360 wkey.cmd = IPW2200_WEP_KEY_CMD_SETKEY; 9473847Seh146360 wkey.idx = (uint8_t)i; 9483847Seh146360 wkey.len = ic->ic_nw_keys[i].wk_keylen; 9493847Seh146360 (void) memset(wkey.key, 0, sizeof (wkey.key)); 9503847Seh146360 if (ic->ic_nw_keys[i].wk_keylen) 9513847Seh146360 (void) memcpy(wkey.key, 9523847Seh146360 ic->ic_nw_keys[i].wk_key, 9533847Seh146360 ic->ic_nw_keys[i].wk_keylen); 9543847Seh146360 err = ipw2200_cmd(sc, IPW2200_CMD_SET_WEP_KEY, 9553847Seh146360 &wkey, sizeof (wkey), 0); 9563847Seh146360 if (err != DDI_SUCCESS) 9573847Seh146360 return (err); 9583847Seh146360 } 9593847Seh146360 } 9603847Seh146360 9613847Seh146360 IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT, 9623847Seh146360 "ipw2200_config(): Enabling adapter\n")); 9633847Seh146360 9643847Seh146360 return (ipw2200_cmd(sc, IPW2200_CMD_ENABLE, NULL, 0, 0)); 9653847Seh146360 } 9663847Seh146360 9673847Seh146360 static int 9683847Seh146360 ipw2200_cmd(struct ipw2200_softc *sc, 9693847Seh146360 uint32_t type, void *buf, size_t len, int async) 9703847Seh146360 { 9713847Seh146360 struct ipw2200_cmd_desc *cmd; 9723847Seh146360 clock_t clk; 9733847Seh146360 uint32_t idx; 9743847Seh146360 9753847Seh146360 mutex_enter(&sc->sc_cmd_lock); 9763847Seh146360 while (sc->sc_cmd_free < 1) 9773847Seh146360 cv_wait(&sc->sc_cmd_cond, &sc->sc_cmd_lock); 9783847Seh146360 9793847Seh146360 idx = sc->sc_cmd_cur; 9803847Seh146360 cmd = &sc->sc_cmdsc[idx]; 9813847Seh146360 (void) memset(cmd, 0, sizeof (*cmd)); 9823847Seh146360 9833847Seh146360 IPW2200_DBG(IPW2200_DBG_RING, (sc->sc_dip, CE_CONT, 9843847Seh146360 "ipw2200_cmd(): cmd-cur=%d\n", idx)); 9853847Seh146360 9863847Seh146360 cmd->hdr.type = IPW2200_HDR_TYPE_COMMAND; 9873847Seh146360 cmd->hdr.flags = IPW2200_HDR_FLAG_IRQ; 9883847Seh146360 cmd->type = (uint8_t)type; 9893847Seh146360 if (len == 0 || buf == NULL) 9903847Seh146360 cmd->len = 0; 9913847Seh146360 else { 9923847Seh146360 cmd->len = (uint8_t)len; 9933847Seh146360 (void) memcpy(cmd->data, buf, len); 9943847Seh146360 } 9953847Seh146360 sc->sc_done[idx] = 0; 9963847Seh146360 9973847Seh146360 /* 9983847Seh146360 * DMA sync 9993847Seh146360 */ 10003847Seh146360 (void) ddi_dma_sync(sc->sc_dma_cmdsc.dr_hnd, 10013847Seh146360 idx * sizeof (struct ipw2200_cmd_desc), 10023847Seh146360 sizeof (struct ipw2200_cmd_desc), DDI_DMA_SYNC_FORDEV); 10033847Seh146360 10043847Seh146360 sc->sc_cmd_cur = RING_FORWARD(sc->sc_cmd_cur, 1, IPW2200_CMD_RING_SIZE); 10053847Seh146360 sc->sc_cmd_free--; 10063847Seh146360 10073847Seh146360 ipw2200_csr_put32(sc, IPW2200_CSR_CMD_WRITE_INDEX, sc->sc_cmd_cur); 10083847Seh146360 10093847Seh146360 mutex_exit(&sc->sc_cmd_lock); 10103847Seh146360 10113847Seh146360 if (async) 10123847Seh146360 goto out; 10133847Seh146360 10143847Seh146360 /* 10153847Seh146360 * Wait for command done 10163847Seh146360 */ 10173847Seh146360 mutex_enter(&sc->sc_ilock); 10183847Seh146360 while (sc->sc_done[idx] == 0) { 10193847Seh146360 /* pending */ 10203847Seh146360 clk = ddi_get_lbolt() + drv_usectohz(5000000); /* 5 second */ 10213847Seh146360 if (cv_timedwait(&sc->sc_cmd_status_cond, &sc->sc_ilock, clk) 10223847Seh146360 < 0) 10233847Seh146360 break; 10243847Seh146360 } 10253847Seh146360 mutex_exit(&sc->sc_ilock); 10263847Seh146360 10273847Seh146360 IPW2200_DBG(IPW2200_DBG_RING, (sc->sc_dip, CE_CONT, 10283847Seh146360 "ipw2200_cmd(): cmd-done=%s\n", sc->sc_done[idx] ? "yes" : "no")); 10293847Seh146360 10303847Seh146360 if (sc->sc_done[idx] == 0) 10313847Seh146360 return (DDI_FAILURE); 10323847Seh146360 10333847Seh146360 out: 10343847Seh146360 return (DDI_SUCCESS); 10353847Seh146360 } 10363847Seh146360 10373847Seh146360 /* 10383847Seh146360 * If init failed, it will call stop internally. Therefore, it's unnecessary 10393847Seh146360 * to call ipw2200_stop() when this subroutine is failed. Otherwise, it may 10403847Seh146360 * be called twice. 10413847Seh146360 */ 10423847Seh146360 int 10433847Seh146360 ipw2200_init(struct ipw2200_softc *sc) 10443847Seh146360 { 10453847Seh146360 int err; 10463847Seh146360 10473847Seh146360 /* 10483847Seh146360 * No firmware is available, failed 10493847Seh146360 */ 10503847Seh146360 if (!(sc->sc_flags & IPW2200_FLAG_FW_CACHED)) { 10513847Seh146360 IPW2200_WARN((sc->sc_dip, CE_WARN, 10523847Seh146360 "ipw2200_init(): no firmware is available\n")); 10533847Seh146360 return (DDI_FAILURE); /* return directly */ 10543847Seh146360 } 10553847Seh146360 10563847Seh146360 ipw2200_stop(sc); 10573847Seh146360 10583847Seh146360 err = ipw2200_chip_reset(sc); 10593847Seh146360 if (err != DDI_SUCCESS) { 10603847Seh146360 IPW2200_WARN((sc->sc_dip, CE_WARN, 10613847Seh146360 "ipw2200_init(): could not reset adapter\n")); 10623847Seh146360 goto fail; 10633847Seh146360 } 10643847Seh146360 10653847Seh146360 /* 10663847Seh146360 * Load boot code 10673847Seh146360 */ 10683847Seh146360 err = ipw2200_load_fw(sc, sc->sc_fw.boot_base, sc->sc_fw.boot_size); 10693847Seh146360 if (err != DDI_SUCCESS) { 10703847Seh146360 IPW2200_WARN((sc->sc_dip, CE_WARN, 10713847Seh146360 "ipw2200_init(): could not load boot code\n")); 10723847Seh146360 goto fail; 10733847Seh146360 } 10743847Seh146360 10753847Seh146360 /* 10763847Seh146360 * Load boot microcode 10773847Seh146360 */ 10783847Seh146360 err = ipw2200_load_uc(sc, sc->sc_fw.uc_base, sc->sc_fw.uc_size); 10793847Seh146360 if (err != DDI_SUCCESS) { 10803847Seh146360 IPW2200_WARN((sc->sc_dip, CE_WARN, 10813847Seh146360 "ipw2200_init(): could not load microcode\n")); 10823847Seh146360 goto fail; 10833847Seh146360 } 10843847Seh146360 10853847Seh146360 ipw2200_master_stop(sc); 10863847Seh146360 ipw2200_ring_hwsetup(sc); 10873847Seh146360 10883847Seh146360 /* 10893847Seh146360 * Load firmware 10903847Seh146360 */ 10913847Seh146360 err = ipw2200_load_fw(sc, sc->sc_fw.fw_base, sc->sc_fw.fw_size); 10923847Seh146360 if (err != DDI_SUCCESS) { 10933847Seh146360 IPW2200_WARN((sc->sc_dip, CE_WARN, 10943847Seh146360 "ipw2200_init(): could not load firmware\n")); 10953847Seh146360 goto fail; 10963847Seh146360 } 10973847Seh146360 10983847Seh146360 sc->sc_flags |= IPW2200_FLAG_FW_INITED; 10993847Seh146360 11003847Seh146360 /* 11013847Seh146360 * Hardware will be enabled after configuration 11023847Seh146360 */ 11033847Seh146360 err = ipw2200_config(sc); 11043847Seh146360 if (err != DDI_SUCCESS) { 11053847Seh146360 IPW2200_WARN((sc->sc_dip, CE_WARN, 11063847Seh146360 "ipw2200_init(): device configuration failed\n")); 11073847Seh146360 goto fail; 11083847Seh146360 } 11093847Seh146360 11103847Seh146360 /* 11113847Seh146360 * workround to prevent too many h/w error. 11123847Seh146360 * delay for a while till h/w is stable. 11133847Seh146360 */ 11143847Seh146360 delay(drv_usectohz(delay_config_stable)); 11153847Seh146360 11163847Seh146360 return (DDI_SUCCESS); /* return successfully */ 11173847Seh146360 fail: 11183847Seh146360 ipw2200_stop(sc); 11193847Seh146360 return (err); 11203847Seh146360 } 11213847Seh146360 11223847Seh146360 /* 11233847Seh146360 * get hardware configurations from EEPROM embedded within PRO/2200 11243847Seh146360 */ 11253847Seh146360 static void 11263847Seh146360 ipw2200_hwconf_get(struct ipw2200_softc *sc) 11273847Seh146360 { 11283847Seh146360 int i; 11293847Seh146360 uint16_t val; 11303847Seh146360 11313847Seh146360 /* 11323847Seh146360 * Get mac address 11333847Seh146360 */ 11343847Seh146360 i = 0; 11353847Seh146360 val = ipw2200_rom_get16(sc, IPW2200_EEPROM_MAC + 0); 11363847Seh146360 sc->sc_macaddr[i++] = val >> 8; 11373847Seh146360 sc->sc_macaddr[i++] = val & 0xff; 11383847Seh146360 val = ipw2200_rom_get16(sc, IPW2200_EEPROM_MAC + 1); 11393847Seh146360 sc->sc_macaddr[i++] = val >> 8; 11403847Seh146360 sc->sc_macaddr[i++] = val & 0xff; 11413847Seh146360 val = ipw2200_rom_get16(sc, IPW2200_EEPROM_MAC + 2); 11423847Seh146360 sc->sc_macaddr[i++] = val >> 8; 11433847Seh146360 sc->sc_macaddr[i++] = val & 0xff; 11443847Seh146360 11453847Seh146360 /* 11463847Seh146360 * formatted MAC address string 11473847Seh146360 */ 11483847Seh146360 (void) snprintf(sc->sc_macstr, sizeof (sc->sc_macstr), 11493847Seh146360 "%02x:%02x:%02x:%02x:%02x:%02x", 11503847Seh146360 sc->sc_macaddr[0], sc->sc_macaddr[1], 11513847Seh146360 sc->sc_macaddr[2], sc->sc_macaddr[3], 11523847Seh146360 sc->sc_macaddr[4], sc->sc_macaddr[5]); 11533847Seh146360 11543847Seh146360 } 11553847Seh146360 11563847Seh146360 /* 11573847Seh146360 * all ipw2200 interrupts will be masked by this routine 11583847Seh146360 */ 11593847Seh146360 static void 11603847Seh146360 ipw2200_master_stop(struct ipw2200_softc *sc) 11613847Seh146360 { 11623847Seh146360 int ntries; 11633847Seh146360 11643847Seh146360 /* 11653847Seh146360 * disable interrupts 11663847Seh146360 */ 11673847Seh146360 ipw2200_csr_put32(sc, IPW2200_CSR_INTR_MASK, 0); 11683847Seh146360 ipw2200_csr_put32(sc, IPW2200_CSR_RST, IPW2200_RST_STOP_MASTER); 11693847Seh146360 11703847Seh146360 /* 11713847Seh146360 * wait long enough to ensure hardware stop successfully. 11723847Seh146360 */ 11733847Seh146360 for (ntries = 0; ntries < 500; ntries++) { 11743847Seh146360 if (ipw2200_csr_get32(sc, IPW2200_CSR_RST) & 11753847Seh146360 IPW2200_RST_MASTER_DISABLED) 11763847Seh146360 break; 11773847Seh146360 /* wait for a while */ 11783847Seh146360 drv_usecwait(100); 11793847Seh146360 } 11808165SFei.Feng@Sun.COM if (ntries == 500) 11813847Seh146360 IPW2200_WARN((sc->sc_dip, CE_WARN, 11823847Seh146360 "ipw2200_master_stop(): timeout\n")); 11833847Seh146360 11843847Seh146360 ipw2200_csr_put32(sc, IPW2200_CSR_RST, 11853847Seh146360 IPW2200_RST_PRINCETON_RESET | 11863847Seh146360 ipw2200_csr_get32(sc, IPW2200_CSR_RST)); 11873847Seh146360 11883847Seh146360 sc->sc_flags &= ~IPW2200_FLAG_FW_INITED; 11893847Seh146360 } 11903847Seh146360 11913847Seh146360 /* 11923847Seh146360 * all ipw2200 interrupts will be masked by this routine 11933847Seh146360 */ 11943847Seh146360 static int 11953847Seh146360 ipw2200_chip_reset(struct ipw2200_softc *sc) 11963847Seh146360 { 11973847Seh146360 uint32_t tmp; 11983847Seh146360 int ntries, i; 11993847Seh146360 12003847Seh146360 ipw2200_master_stop(sc); 12013847Seh146360 12023847Seh146360 /* 12033847Seh146360 * Move adapter to DO state 12043847Seh146360 */ 12053847Seh146360 tmp = ipw2200_csr_get32(sc, IPW2200_CSR_CTL); 12063847Seh146360 ipw2200_csr_put32(sc, IPW2200_CSR_CTL, tmp | IPW2200_CTL_INIT); 12073847Seh146360 12083847Seh146360 /* 12093847Seh146360 * Initialize Phase-Locked Level (PLL) 12103847Seh146360 */ 12113847Seh146360 ipw2200_csr_put32(sc, IPW2200_CSR_READ_INT, IPW2200_READ_INT_INIT_HOST); 12123847Seh146360 12133847Seh146360 /* 12143847Seh146360 * Wait for clock stabilization 12153847Seh146360 */ 12163847Seh146360 for (ntries = 0; ntries < 1000; ntries++) { 12173847Seh146360 if (ipw2200_csr_get32(sc, IPW2200_CSR_CTL) & 12183847Seh146360 IPW2200_CTL_CLOCK_READY) 12193847Seh146360 break; 12203847Seh146360 drv_usecwait(200); 12213847Seh146360 } 12223847Seh146360 if (ntries == 1000) { 12233847Seh146360 IPW2200_WARN((sc->sc_dip, CE_WARN, 12243847Seh146360 "ipw2200_chip_reset(): timeout\n")); 12253847Seh146360 return (DDI_FAILURE); 12263847Seh146360 } 12273847Seh146360 12283847Seh146360 tmp = ipw2200_csr_get32(sc, IPW2200_CSR_RST); 12293847Seh146360 ipw2200_csr_put32(sc, IPW2200_CSR_RST, tmp | IPW2200_RST_SW_RESET); 12303847Seh146360 12313847Seh146360 drv_usecwait(10); 12323847Seh146360 12333847Seh146360 tmp = ipw2200_csr_get32(sc, IPW2200_CSR_CTL); 12343847Seh146360 ipw2200_csr_put32(sc, IPW2200_CSR_CTL, tmp | IPW2200_CTL_INIT); 12353847Seh146360 12363847Seh146360 /* 12373847Seh146360 * clear NIC memory 12383847Seh146360 */ 12393847Seh146360 ipw2200_csr_put32(sc, IPW2200_CSR_AUTOINC_ADDR, 0); 12403847Seh146360 for (i = 0; i < 0xc000; i++) 12413847Seh146360 ipw2200_csr_put32(sc, IPW2200_CSR_AUTOINC_DATA, 0); 12423847Seh146360 12433847Seh146360 return (DDI_SUCCESS); 12443847Seh146360 } 12453847Seh146360 12463847Seh146360 /* 12473847Seh146360 * This function is used by wificonfig/dladm to get the current 12483847Seh146360 * radio status, it is off/on 12493847Seh146360 */ 12503847Seh146360 int 12513847Seh146360 ipw2200_radio_status(struct ipw2200_softc *sc) 12523847Seh146360 { 12533847Seh146360 int val; 12543847Seh146360 12553847Seh146360 val = (ipw2200_csr_get32(sc, IPW2200_CSR_IO) & 12563847Seh146360 IPW2200_IO_RADIO_ENABLED) ? 1 : 0; 12573847Seh146360 12583847Seh146360 return (val); 12593847Seh146360 } 12603847Seh146360 /* 12613847Seh146360 * This function is used to get the statistic 12623847Seh146360 */ 12633847Seh146360 void 12643847Seh146360 ipw2200_get_statistics(struct ipw2200_softc *sc) 12653847Seh146360 { 12663847Seh146360 struct ieee80211com *ic = &sc->sc_ic; 12673847Seh146360 12683847Seh146360 uint32_t size, buf[128]; 12693847Seh146360 12703847Seh146360 if (!(sc->sc_flags & IPW2200_FLAG_FW_INITED)) { 12713847Seh146360 IPW2200_DBG(IPW2200_DBG_IOCTL, (sc->sc_dip, CE_CONT, 12723847Seh146360 "ipw2200_get_statistic(): fw doesn't download yet.")); 12733847Seh146360 return; 12743847Seh146360 } 12753847Seh146360 12763847Seh146360 size = min(ipw2200_csr_get32(sc, IPW2200_CSR_TABLE0_SIZE), 128 - 1); 12773847Seh146360 ipw2200_csr_getbuf32(sc, IPW2200_CSR_TABLE0_BASE, &buf[1], size); 12783847Seh146360 12793847Seh146360 /* 12803847Seh146360 * To retrieve the statistic information into proper places. There are 12813847Seh146360 * lot of information. These table will be read once a second. 12823847Seh146360 * Hopefully, it will not effect the performance. 12833847Seh146360 */ 12843847Seh146360 12853847Seh146360 /* 12863847Seh146360 * For the tx/crc information, we can get them from chip directly; 12873847Seh146360 * For the rx/wep error/(rts) related information, leave them net80211. 12883847Seh146360 */ 12893847Seh146360 /* WIFI_STAT_TX_FRAGS */ 12903847Seh146360 ic->ic_stats.is_tx_frags = (uint32_t)buf[5]; 12913847Seh146360 /* WIFI_STAT_MCAST_TX */ 12923847Seh146360 ic->ic_stats.is_tx_mcast = (uint32_t)buf[31]; 12933847Seh146360 /* WIFI_STAT_TX_RETRANS */ 12943847Seh146360 ic->ic_stats.is_tx_retries = (uint32_t)buf[56]; 12953847Seh146360 /* WIFI_STAT_TX_FAILED */ 12963847Seh146360 ic->ic_stats.is_tx_failed = (uint32_t)buf[57]; 12973847Seh146360 /* MAC_STAT_OBYTES */ 12983847Seh146360 ic->ic_stats.is_tx_bytes = (uint32_t)buf[64]; 12993847Seh146360 } 13003847Seh146360 13013847Seh146360 /* 13023847Seh146360 * DMA region alloc subroutine 13033847Seh146360 */ 13043847Seh146360 int 13053847Seh146360 ipw2200_dma_region_alloc(struct ipw2200_softc *sc, struct dma_region *dr, 13063847Seh146360 size_t size, uint_t dir, uint_t flags) 13073847Seh146360 { 13083847Seh146360 dev_info_t *dip = sc->sc_dip; 13093847Seh146360 int err; 13103847Seh146360 13113847Seh146360 IPW2200_DBG(IPW2200_DBG_DMA, (sc->sc_dip, CE_CONT, 13123847Seh146360 "ipw2200_dma_region_alloc(): size =%u\n", size)); 13133847Seh146360 13143847Seh146360 err = ddi_dma_alloc_handle(dip, &ipw2200_dma_attr, DDI_DMA_SLEEP, NULL, 13153847Seh146360 &dr->dr_hnd); 13163847Seh146360 if (err != DDI_SUCCESS) { 13173847Seh146360 IPW2200_DBG(IPW2200_DBG_DMA, (sc->sc_dip, CE_CONT, 13183847Seh146360 "ipw2200_dma_region_alloc(): " 13193847Seh146360 "ddi_dma_alloc_handle() failed\n")); 13203847Seh146360 goto fail0; 13213847Seh146360 } 13223847Seh146360 13233847Seh146360 err = ddi_dma_mem_alloc(dr->dr_hnd, size, &ipw2200_dma_accattr, 13243847Seh146360 flags, DDI_DMA_SLEEP, NULL, 13253847Seh146360 &dr->dr_base, &dr->dr_size, &dr->dr_acc); 13263847Seh146360 if (err != DDI_SUCCESS) { 13273847Seh146360 IPW2200_DBG(IPW2200_DBG_DMA, (sc->sc_dip, CE_CONT, 13283847Seh146360 "ipw2200_dma_region_alloc(): " 13293847Seh146360 "ddi_dma_mem_alloc() failed\n")); 13303847Seh146360 goto fail1; 13313847Seh146360 } 13323847Seh146360 13333847Seh146360 err = ddi_dma_addr_bind_handle(dr->dr_hnd, NULL, 13343847Seh146360 dr->dr_base, dr->dr_size, 13353847Seh146360 dir | flags, DDI_DMA_SLEEP, NULL, 13363847Seh146360 &dr->dr_cookie, &dr->dr_ccnt); 13373847Seh146360 if (err != DDI_DMA_MAPPED) { 13383847Seh146360 IPW2200_DBG(IPW2200_DBG_DMA, (sc->sc_dip, CE_CONT, 13393847Seh146360 "ipw2200_dma_region_alloc(): " 13403847Seh146360 "ddi_dma_addr_bind_handle() failed\n")); 13413847Seh146360 goto fail2; 13423847Seh146360 } 13433847Seh146360 13443847Seh146360 IPW2200_DBG(IPW2200_DBG_DMA, (sc->sc_dip, CE_CONT, 13453847Seh146360 "ipw2200_dma_region_alloc(): ccnt=%u\n", dr->dr_ccnt)); 13463847Seh146360 13473847Seh146360 if (dr->dr_ccnt != 1) { 13483847Seh146360 err = DDI_FAILURE; 13493847Seh146360 goto fail3; 13503847Seh146360 } 13513847Seh146360 13523847Seh146360 dr->dr_pbase = dr->dr_cookie.dmac_address; 13533847Seh146360 13543847Seh146360 IPW2200_DBG(IPW2200_DBG_DMA, (sc->sc_dip, CE_CONT, 13553847Seh146360 "ipw2200_dma_region_alloc(): get physical-base=0x%08x\n", 13563847Seh146360 dr->dr_pbase)); 13573847Seh146360 13583847Seh146360 return (DDI_SUCCESS); 13593847Seh146360 13603847Seh146360 fail3: 13613847Seh146360 (void) ddi_dma_unbind_handle(dr->dr_hnd); 13623847Seh146360 fail2: 13633847Seh146360 ddi_dma_mem_free(&dr->dr_acc); 13643847Seh146360 fail1: 13653847Seh146360 ddi_dma_free_handle(&dr->dr_hnd); 13663847Seh146360 fail0: 13673847Seh146360 return (err); 13683847Seh146360 } 13693847Seh146360 13703847Seh146360 void 13713847Seh146360 ipw2200_dma_region_free(struct dma_region *dr) 13723847Seh146360 { 13733847Seh146360 (void) ddi_dma_unbind_handle(dr->dr_hnd); 13743847Seh146360 ddi_dma_mem_free(&dr->dr_acc); 13753847Seh146360 ddi_dma_free_handle(&dr->dr_hnd); 13763847Seh146360 } 13773847Seh146360 13783847Seh146360 static int 13793847Seh146360 ipw2200_ring_alloc(struct ipw2200_softc *sc) 13803847Seh146360 { 13813847Seh146360 int err, i; 13823847Seh146360 13833847Seh146360 /* 13843847Seh146360 * tx desc ring 13853847Seh146360 */ 13863847Seh146360 sc->sc_dma_txdsc.dr_name = "ipw2200-tx-desc-ring"; 13873847Seh146360 err = ipw2200_dma_region_alloc(sc, &sc->sc_dma_txdsc, 13883847Seh146360 IPW2200_TX_RING_SIZE * sizeof (struct ipw2200_tx_desc), 13893847Seh146360 DDI_DMA_WRITE, DDI_DMA_CONSISTENT); 13903847Seh146360 if (err != DDI_SUCCESS) 13913847Seh146360 goto fail0; 13923847Seh146360 /* 13933847Seh146360 * tx buffer array 13943847Seh146360 */ 13953847Seh146360 for (i = 0; i < IPW2200_TX_RING_SIZE; i++) { 13963847Seh146360 sc->sc_dma_txbufs[i].dr_name = "ipw2200-tx-buf"; 13973847Seh146360 err = ipw2200_dma_region_alloc(sc, &sc->sc_dma_txbufs[i], 13983847Seh146360 IPW2200_TXBUF_SIZE, DDI_DMA_WRITE, DDI_DMA_STREAMING); 13993847Seh146360 if (err != DDI_SUCCESS) { 14003847Seh146360 while (i >= 0) { 14013847Seh146360 ipw2200_dma_region_free(&sc->sc_dma_txbufs[i]); 14023847Seh146360 i--; 14033847Seh146360 } 14043847Seh146360 goto fail1; 14053847Seh146360 } 14063847Seh146360 } 14073847Seh146360 /* 14083847Seh146360 * rx buffer array 14093847Seh146360 */ 14103847Seh146360 for (i = 0; i < IPW2200_RX_RING_SIZE; i++) { 14113847Seh146360 sc->sc_dma_rxbufs[i].dr_name = "ipw2200-rx-buf"; 14123847Seh146360 err = ipw2200_dma_region_alloc(sc, &sc->sc_dma_rxbufs[i], 14133847Seh146360 IPW2200_RXBUF_SIZE, DDI_DMA_READ, DDI_DMA_STREAMING); 14143847Seh146360 if (err != DDI_SUCCESS) { 14153847Seh146360 while (i >= 0) { 14163847Seh146360 ipw2200_dma_region_free(&sc->sc_dma_rxbufs[i]); 14173847Seh146360 i--; 14183847Seh146360 } 14193847Seh146360 goto fail2; 14203847Seh146360 } 14213847Seh146360 } 14223847Seh146360 /* 14233847Seh146360 * cmd desc ring 14243847Seh146360 */ 14253847Seh146360 sc->sc_dma_cmdsc.dr_name = "ipw2200-cmd-desc-ring"; 14263847Seh146360 err = ipw2200_dma_region_alloc(sc, &sc->sc_dma_cmdsc, 14273847Seh146360 IPW2200_CMD_RING_SIZE * sizeof (struct ipw2200_cmd_desc), 14283847Seh146360 DDI_DMA_WRITE, DDI_DMA_CONSISTENT); 14293847Seh146360 if (err != DDI_SUCCESS) 14303847Seh146360 goto fail3; 14313847Seh146360 14323847Seh146360 return (DDI_SUCCESS); 14333847Seh146360 14343847Seh146360 fail3: 14353847Seh146360 for (i = 0; i < IPW2200_RX_RING_SIZE; i++) 14363847Seh146360 ipw2200_dma_region_free(&sc->sc_dma_rxbufs[i]); 14373847Seh146360 fail2: 14383847Seh146360 for (i = 0; i < IPW2200_TX_RING_SIZE; i++) 14393847Seh146360 ipw2200_dma_region_free(&sc->sc_dma_txbufs[i]); 14403847Seh146360 fail1: 14413847Seh146360 ipw2200_dma_region_free(&sc->sc_dma_txdsc); 14423847Seh146360 fail0: 14433847Seh146360 return (err); 14443847Seh146360 } 14453847Seh146360 14463847Seh146360 static void 14473847Seh146360 ipw2200_ring_free(struct ipw2200_softc *sc) 14483847Seh146360 { 14493847Seh146360 int i; 14503847Seh146360 14513847Seh146360 /* 14523847Seh146360 * tx ring desc 14533847Seh146360 */ 14543847Seh146360 ipw2200_dma_region_free(&sc->sc_dma_txdsc); 14553847Seh146360 /* 14563847Seh146360 * tx buf 14573847Seh146360 */ 14583847Seh146360 for (i = 0; i < IPW2200_TX_RING_SIZE; i++) 14593847Seh146360 ipw2200_dma_region_free(&sc->sc_dma_txbufs[i]); 14603847Seh146360 /* 14613847Seh146360 * rx buf 14623847Seh146360 */ 14633847Seh146360 for (i = 0; i < IPW2200_RX_RING_SIZE; i++) 14643847Seh146360 ipw2200_dma_region_free(&sc->sc_dma_rxbufs[i]); 14653847Seh146360 /* 14663847Seh146360 * command ring desc 14673847Seh146360 */ 14683847Seh146360 ipw2200_dma_region_free(&sc->sc_dma_cmdsc); 14693847Seh146360 } 14703847Seh146360 14713847Seh146360 static void 14723847Seh146360 ipw2200_ring_reset(struct ipw2200_softc *sc) 14733847Seh146360 { 14743847Seh146360 int i; 14753847Seh146360 14763847Seh146360 /* 14773847Seh146360 * tx desc ring & buffer array 14783847Seh146360 */ 14793847Seh146360 sc->sc_tx_cur = 0; 14803847Seh146360 sc->sc_tx_free = IPW2200_TX_RING_SIZE; 14813847Seh146360 sc->sc_txdsc = (struct ipw2200_tx_desc *)sc->sc_dma_txdsc.dr_base; 14823847Seh146360 for (i = 0; i < IPW2200_TX_RING_SIZE; i++) 14833847Seh146360 sc->sc_txbufs[i] = (uint8_t *)sc->sc_dma_txbufs[i].dr_base; 14843847Seh146360 /* 14853847Seh146360 * rx buffer array 14863847Seh146360 */ 14873847Seh146360 sc->sc_rx_cur = 0; 14883847Seh146360 sc->sc_rx_free = IPW2200_RX_RING_SIZE; 14893847Seh146360 for (i = 0; i < IPW2200_RX_RING_SIZE; i++) 14903847Seh146360 sc->sc_rxbufs[i] = (uint8_t *)sc->sc_dma_rxbufs[i].dr_base; 14913847Seh146360 14923847Seh146360 /* 14933847Seh146360 * command desc ring 14943847Seh146360 */ 14953847Seh146360 sc->sc_cmd_cur = 0; 14963847Seh146360 sc->sc_cmd_free = IPW2200_CMD_RING_SIZE; 14973847Seh146360 sc->sc_cmdsc = (struct ipw2200_cmd_desc *)sc->sc_dma_cmdsc.dr_base; 14983847Seh146360 } 14993847Seh146360 15003847Seh146360 /* 15013847Seh146360 * tx, rx rings and command initialization 15023847Seh146360 */ 15033847Seh146360 static int 15043847Seh146360 ipw2200_ring_init(struct ipw2200_softc *sc) 15053847Seh146360 { 15063847Seh146360 int err; 15073847Seh146360 15083847Seh146360 err = ipw2200_ring_alloc(sc); 15093847Seh146360 if (err != DDI_SUCCESS) 15103847Seh146360 return (err); 15113847Seh146360 15123847Seh146360 ipw2200_ring_reset(sc); 15133847Seh146360 15143847Seh146360 return (DDI_SUCCESS); 15153847Seh146360 } 15163847Seh146360 15173847Seh146360 static void 15183847Seh146360 ipw2200_ring_hwsetup(struct ipw2200_softc *sc) 15193847Seh146360 { 15203847Seh146360 int i; 15213847Seh146360 15223847Seh146360 /* 15233847Seh146360 * command desc ring 15243847Seh146360 */ 15253847Seh146360 ipw2200_csr_put32(sc, IPW2200_CSR_CMD_BASE, sc->sc_dma_cmdsc.dr_pbase); 15263847Seh146360 ipw2200_csr_put32(sc, IPW2200_CSR_CMD_SIZE, IPW2200_CMD_RING_SIZE); 15273847Seh146360 ipw2200_csr_put32(sc, IPW2200_CSR_CMD_WRITE_INDEX, sc->sc_cmd_cur); 15283847Seh146360 15293847Seh146360 /* 15303847Seh146360 * tx desc ring. only tx1 is used, tx2, tx3, and tx4 are unused 15313847Seh146360 */ 15323847Seh146360 ipw2200_csr_put32(sc, IPW2200_CSR_TX1_BASE, sc->sc_dma_txdsc.dr_pbase); 15333847Seh146360 ipw2200_csr_put32(sc, IPW2200_CSR_TX1_SIZE, IPW2200_TX_RING_SIZE); 15343847Seh146360 ipw2200_csr_put32(sc, IPW2200_CSR_TX1_WRITE_INDEX, sc->sc_tx_cur); 15353847Seh146360 15363847Seh146360 /* 15373847Seh146360 * tx2, tx3, tx4 is not used 15383847Seh146360 */ 15393847Seh146360 ipw2200_csr_put32(sc, IPW2200_CSR_TX2_BASE, sc->sc_dma_txdsc.dr_pbase); 15403847Seh146360 ipw2200_csr_put32(sc, IPW2200_CSR_TX2_SIZE, IPW2200_TX_RING_SIZE); 15413847Seh146360 ipw2200_csr_put32(sc, IPW2200_CSR_TX2_READ_INDEX, 0); 15423847Seh146360 ipw2200_csr_put32(sc, IPW2200_CSR_TX2_WRITE_INDEX, 0); 15433847Seh146360 ipw2200_csr_put32(sc, IPW2200_CSR_TX3_BASE, sc->sc_dma_txdsc.dr_pbase); 15443847Seh146360 ipw2200_csr_put32(sc, IPW2200_CSR_TX3_SIZE, IPW2200_TX_RING_SIZE); 15453847Seh146360 ipw2200_csr_put32(sc, IPW2200_CSR_TX3_READ_INDEX, 0); 15463847Seh146360 ipw2200_csr_put32(sc, IPW2200_CSR_TX3_WRITE_INDEX, 0); 15473847Seh146360 ipw2200_csr_put32(sc, IPW2200_CSR_TX4_BASE, sc->sc_dma_txdsc.dr_pbase); 15483847Seh146360 ipw2200_csr_put32(sc, IPW2200_CSR_TX4_SIZE, IPW2200_TX_RING_SIZE); 15493847Seh146360 ipw2200_csr_put32(sc, IPW2200_CSR_TX4_READ_INDEX, 0); 15503847Seh146360 ipw2200_csr_put32(sc, IPW2200_CSR_TX4_WRITE_INDEX, 0); 15513847Seh146360 15523847Seh146360 /* 15533847Seh146360 * rx buffer ring 15543847Seh146360 */ 15553847Seh146360 for (i = 0; i < IPW2200_RX_RING_SIZE; i++) 15563847Seh146360 ipw2200_csr_put32(sc, IPW2200_CSR_RX_BASE + i * 4, 15573847Seh146360 sc->sc_dma_rxbufs[i].dr_pbase); 15583847Seh146360 /* 15593847Seh146360 * all rx buffer are empty, rx-rd-index == 0 && rx-wr-index == N-1 15603847Seh146360 */ 15613847Seh146360 ipw2200_csr_put32(sc, IPW2200_CSR_RX_WRITE_INDEX, 15623847Seh146360 RING_BACKWARD(sc->sc_rx_cur, 1, IPW2200_RX_RING_SIZE)); 15633847Seh146360 } 15643847Seh146360 15653847Seh146360 int 15663847Seh146360 ipw2200_start_scan(struct ipw2200_softc *sc) 15673847Seh146360 { 15683847Seh146360 struct ieee80211com *ic = &sc->sc_ic; 15693847Seh146360 struct ipw2200_scan scan; 15703847Seh146360 uint8_t *ch; 15713847Seh146360 int cnt, i; 15723847Seh146360 15733847Seh146360 IPW2200_DBG(IPW2200_DBG_SCAN, (sc->sc_dip, CE_CONT, 15743847Seh146360 "ipw2200_start_scan(): start scanning \n")); 15753847Seh146360 15763847Seh146360 /* 15773847Seh146360 * start scanning 15783847Seh146360 */ 15793847Seh146360 sc->sc_flags |= IPW2200_FLAG_SCANNING; 15803847Seh146360 15813847Seh146360 (void) memset(&scan, 0, sizeof (scan)); 15823847Seh146360 scan.type = (ic->ic_des_esslen != 0) ? IPW2200_SCAN_TYPE_BDIRECTED : 15833847Seh146360 IPW2200_SCAN_TYPE_BROADCAST; 15843847Seh146360 scan.dwelltime = LE_16(40); /* The interval is set up to 40 */ 15853847Seh146360 15863847Seh146360 /* 15873847Seh146360 * Compact supported channel number(5G) into a single buffer 15883847Seh146360 */ 15893847Seh146360 ch = scan.channels; 15903847Seh146360 cnt = 0; 15913847Seh146360 for (i = 0; i <= IEEE80211_CHAN_MAX; i++) { 15923847Seh146360 if (IEEE80211_IS_CHAN_5GHZ(&ic->ic_sup_channels[i]) && 15933847Seh146360 isset(ic->ic_chan_active, i)) { 15943847Seh146360 *++ch = (uint8_t)i; 15953847Seh146360 cnt++; 15963847Seh146360 } 15973847Seh146360 } 15983847Seh146360 *(ch - cnt) = IPW2200_CHAN_5GHZ | (uint8_t)cnt; 15994103Sql147931 ch = (cnt > 0) ? (ch + 1) : (scan.channels); 16003847Seh146360 16013847Seh146360 /* 16023847Seh146360 * Compact supported channel number(2G) into a single buffer 16033847Seh146360 */ 16043847Seh146360 cnt = 0; 16053847Seh146360 for (i = 0; i <= IEEE80211_CHAN_MAX; i++) { 16063847Seh146360 if (IEEE80211_IS_CHAN_2GHZ(&ic->ic_sup_channels[i]) && 16073847Seh146360 isset(ic->ic_chan_active, i)) { 16083847Seh146360 *++ch = (uint8_t)i; 16093847Seh146360 cnt++; 16103847Seh146360 } 16113847Seh146360 } 16123847Seh146360 *(ch - cnt) = IPW2200_CHAN_2GHZ | cnt; 16133847Seh146360 16143847Seh146360 return (ipw2200_cmd(sc, IPW2200_CMD_SCAN, &scan, sizeof (scan), 1)); 16153847Seh146360 } 16163847Seh146360 16173847Seh146360 int 16183847Seh146360 ipw2200_auth_and_assoc(struct ipw2200_softc *sc) 16193847Seh146360 { 16203847Seh146360 struct ieee80211com *ic = &sc->sc_ic; 16213847Seh146360 struct ieee80211_node *in = ic->ic_bss; 16223847Seh146360 struct ipw2200_configuration cfg; 16233847Seh146360 struct ipw2200_rateset rs; 16243847Seh146360 struct ipw2200_associate assoc; 16253847Seh146360 uint32_t data; 16263847Seh146360 int err; 16275485Seh146360 uint8_t *wpa_level; 16285485Seh146360 16295485Seh146360 if (sc->sc_flags & IPW2200_FLAG_ASSOCIATED) { 16305485Seh146360 /* already associated */ 16315485Seh146360 return (-1); 16325485Seh146360 } 16333847Seh146360 16343847Seh146360 /* 16353847Seh146360 * set the confiuration 16363847Seh146360 */ 16373847Seh146360 if (IEEE80211_IS_CHAN_2GHZ(in->in_chan)) { 16383847Seh146360 /* enable b/g auto-detection */ 16393847Seh146360 (void) memset(&cfg, 0, sizeof (cfg)); 16403847Seh146360 cfg.bluetooth_coexistence = 1; 16413847Seh146360 cfg.multicast_enabled = 1; 16423847Seh146360 cfg.use_protection = 1; 16433847Seh146360 cfg.answer_pbreq = 1; 16443847Seh146360 cfg.noise_reported = 1; 16455485Seh146360 cfg.disable_multicast_decryption = 1; /* WPA */ 16465485Seh146360 cfg.disable_unicast_decryption = 1; /* WPA */ 16473847Seh146360 err = ipw2200_cmd(sc, IPW2200_CMD_SET_CONFIG, 16483847Seh146360 &cfg, sizeof (cfg), 1); 16493847Seh146360 if (err != DDI_SUCCESS) 16503847Seh146360 return (err); 16513847Seh146360 } 16523847Seh146360 16533847Seh146360 /* 16543847Seh146360 * set the essid, may be null/hidden AP 16553847Seh146360 */ 16563847Seh146360 IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT, 16573847Seh146360 "ipw2200_auth_and_assoc(): " 16583847Seh146360 "setting ESSID to(%u),%c%c%c%c%c%c%c%c\n", 16593847Seh146360 in->in_esslen, 16603847Seh146360 in->in_essid[0], in->in_essid[1], 16613847Seh146360 in->in_essid[2], in->in_essid[3], 16623847Seh146360 in->in_essid[4], in->in_essid[5], 16633847Seh146360 in->in_essid[6], in->in_essid[7])); 16643847Seh146360 err = ipw2200_cmd(sc, IPW2200_CMD_SET_ESSID, in->in_essid, 16653847Seh146360 in->in_esslen, 1); 16663847Seh146360 if (err != DDI_SUCCESS) 16673847Seh146360 return (err); 16683847Seh146360 16693847Seh146360 /* 16703847Seh146360 * set the rate: the rate set has already been ''negocitated'' 16713847Seh146360 */ 16723847Seh146360 rs.mode = IEEE80211_IS_CHAN_5GHZ(in->in_chan) ? 16733847Seh146360 IPW2200_MODE_11A : IPW2200_MODE_11G; 16743847Seh146360 rs.type = IPW2200_RATESET_TYPE_NEGOCIATED; 16753847Seh146360 rs.nrates = in->in_rates.ir_nrates; 16763847Seh146360 (void) memcpy(rs.rates, in->in_rates.ir_rates, in->in_rates.ir_nrates); 16773847Seh146360 IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT, 16783847Seh146360 "ipw2200_auth_and_assoc(): " 16793847Seh146360 "setting negotiated rates to(nrates = %u)\n", rs.nrates)); 16803847Seh146360 err = ipw2200_cmd(sc, IPW2200_CMD_SET_RATES, &rs, sizeof (rs), 1); 16813847Seh146360 if (err != DDI_SUCCESS) 16823847Seh146360 return (err); 16833847Seh146360 16843847Seh146360 /* 16855485Seh146360 * invoke command associate 16865485Seh146360 */ 16875485Seh146360 (void) memset(&assoc, 0, sizeof (assoc)); 16885485Seh146360 16895485Seh146360 /* 16905485Seh146360 * set opt_ie to h/w if associated is WPA, opt_ie has been verified 16915485Seh146360 * by net80211 kernel module. 16925485Seh146360 */ 16935485Seh146360 if (ic->ic_opt_ie != NULL) { 16945485Seh146360 16955485Seh146360 wpa_level = (uint8_t *)ic->ic_opt_ie; 16965485Seh146360 16975485Seh146360 IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT, 16985485Seh146360 "ipw2200_auth_and_assoc(): " 16995485Seh146360 "set wpa_ie and wpa_ie_len to h/w. " 17005485Seh146360 "length is %d\n" 17015485Seh146360 "opt_ie[0] = %02X - element vendor\n" 17025485Seh146360 "opt_ie[1] = %02X - length\n" 17035485Seh146360 "opt_ie[2,3,4] = %02X %02X %02X - oui\n" 17045485Seh146360 "opt_ie[5] = %02X - oui type\n" 17055485Seh146360 "opt_ie[6,7] = %02X %02X - spec version \n" 17065485Seh146360 "opt_ie[8,9,10,11] = %02X %02X %02X %02X - gk cipher\n" 17075485Seh146360 "opt_ie[12,13] = %02X %02X - pairwise key cipher(1)\n" 17085485Seh146360 "opt_ie[14,15,16,17] = %02X %02X %02X %02X - ciphers\n" 17095485Seh146360 "opt_ie[18,19] = %02X %02X - authselcont(1) \n" 17105485Seh146360 "opt_ie[20,21,22,23] = %02X %02X %02X %02X - authsels\n", 17115485Seh146360 wpa_level[1], wpa_level[0], wpa_level[1], 17125485Seh146360 wpa_level[2], wpa_level[3], wpa_level[4], 17135485Seh146360 wpa_level[5], wpa_level[6], wpa_level[7], 17145485Seh146360 wpa_level[8], wpa_level[9], wpa_level[10], 17155485Seh146360 wpa_level[11], wpa_level[12], wpa_level[13], 17165485Seh146360 wpa_level[14], wpa_level[15], wpa_level[16], 17175485Seh146360 wpa_level[17], wpa_level[18], wpa_level[19], 17185485Seh146360 wpa_level[20], wpa_level[21], wpa_level[22], 17195485Seh146360 wpa_level[23])); 17205485Seh146360 17215485Seh146360 err = ipw2200_cmd(sc, IPW2200_CMD_SET_OPTIE, 17225485Seh146360 ic->ic_opt_ie, ic->ic_opt_ie_len, 1); 17235485Seh146360 if (err != DDI_SUCCESS) 17245485Seh146360 return (err); 17255485Seh146360 } 17265485Seh146360 17275485Seh146360 /* 17283847Seh146360 * set the sensitive 17293847Seh146360 */ 17303847Seh146360 data = LE_32(in->in_rssi); 17313847Seh146360 IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT, 17323847Seh146360 "ipw2200_auth_and_assoc(): " 17333847Seh146360 "setting sensitivity to rssi:(%u)\n", (uint8_t)in->in_rssi)); 17343847Seh146360 err = ipw2200_cmd(sc, IPW2200_CMD_SET_SENSITIVITY, 17353847Seh146360 &data, sizeof (data), 1); 17363847Seh146360 if (err != DDI_SUCCESS) 17373847Seh146360 return (err); 17383847Seh146360 17393847Seh146360 /* 17405485Seh146360 * set mode and channel for assocation command 17413847Seh146360 */ 17423847Seh146360 assoc.mode = IEEE80211_IS_CHAN_5GHZ(in->in_chan) ? 17433847Seh146360 IPW2200_MODE_11A : IPW2200_MODE_11G; 17443847Seh146360 assoc.chan = ieee80211_chan2ieee(ic, in->in_chan); 17455485Seh146360 17463847Seh146360 /* 17473847Seh146360 * use the value set to ic_bss to retraive current sharedmode 17483847Seh146360 */ 17493847Seh146360 if (ic->ic_bss->in_authmode == WL_SHAREDKEY) { 17503847Seh146360 assoc.auth = (ic->ic_def_txkey << 4) | IPW2200_AUTH_SHARED; 17513847Seh146360 IPW2200_DBG(IPW2200_DBG_IOCTL, (sc->sc_dip, CE_CONT, 17523847Seh146360 "ipw2200_auth_and_assoc(): " 17533847Seh146360 "associate to shared key mode, set thru. ioctl")); 17543847Seh146360 } 17555485Seh146360 17565485Seh146360 if (ic->ic_flags & IEEE80211_F_WPA) 17575485Seh146360 assoc.policy = LE_16(IPW2200_POLICY_WPA); /* RSN/WPA active */ 17583847Seh146360 (void) memcpy(assoc.tstamp, in->in_tstamp.data, 8); 17593847Seh146360 assoc.capinfo = LE_16(in->in_capinfo); 17603847Seh146360 assoc.lintval = LE_16(ic->ic_lintval); 17613847Seh146360 assoc.intval = LE_16(in->in_intval); 17623847Seh146360 IEEE80211_ADDR_COPY(assoc.bssid, in->in_bssid); 17633847Seh146360 if (ic->ic_opmode == IEEE80211_M_IBSS) 17643847Seh146360 IEEE80211_ADDR_COPY(assoc.dst, ipw2200_broadcast_addr); 17653847Seh146360 else 17663847Seh146360 IEEE80211_ADDR_COPY(assoc.dst, in->in_bssid); 17673847Seh146360 17683847Seh146360 IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT, 17693847Seh146360 "ipw2200_auth_and_assoc(): " 17703847Seh146360 "associate to bssid(%2x:%2x:%2x:%2x:%2x:%2x:), " 17713847Seh146360 "chan(%u), auth(%u)\n", 17723847Seh146360 assoc.bssid[0], assoc.bssid[1], assoc.bssid[2], 17733847Seh146360 assoc.bssid[3], assoc.bssid[4], assoc.bssid[5], 17743847Seh146360 assoc.chan, assoc.auth)); 17753847Seh146360 return (ipw2200_cmd(sc, IPW2200_CMD_ASSOCIATE, 17763847Seh146360 &assoc, sizeof (assoc), 1)); 17773847Seh146360 } 17783847Seh146360 17795485Seh146360 /* 17805485Seh146360 * Send the dis-association command to h/w, will receive notification to claim 17815485Seh146360 * the connection is dis-associated. So, it's not marked as disassociated this 17825485Seh146360 * moment. 17835485Seh146360 */ 17845485Seh146360 static int 17855485Seh146360 ipw2200_disassoc(struct ipw2200_softc *sc) 17865485Seh146360 { 17875485Seh146360 struct ipw2200_associate assoc; 17885485Seh146360 assoc.type = 2; 17895485Seh146360 return (ipw2200_cmd(sc, IPW2200_CMD_ASSOCIATE, &assoc, 17905485Seh146360 sizeof (assoc), 1)); 17915485Seh146360 } 17925485Seh146360 17933847Seh146360 /* ARGSUSED */ 17943847Seh146360 static int 17953847Seh146360 ipw2200_newstate(struct ieee80211com *ic, enum ieee80211_state state, int arg) 17963847Seh146360 { 17973847Seh146360 struct ipw2200_softc *sc = (struct ipw2200_softc *)ic; 17983847Seh146360 wifi_data_t wd = { 0 }; 17993847Seh146360 18003847Seh146360 switch (state) { 18013847Seh146360 case IEEE80211_S_SCAN: 18023847Seh146360 if (!(sc->sc_flags & IPW2200_FLAG_SCANNING)) { 18033847Seh146360 ic->ic_flags |= IEEE80211_F_SCAN | IEEE80211_F_ASCAN; 18043847Seh146360 (void) ipw2200_start_scan(sc); 18053847Seh146360 } 18063847Seh146360 break; 18073847Seh146360 case IEEE80211_S_AUTH: 18085485Seh146360 /* 18095485Seh146360 * The firmware will fail if we are already associated 18105485Seh146360 */ 18115485Seh146360 if (sc->sc_flags & IPW2200_FLAG_ASSOCIATED) 18125485Seh146360 (void) ipw2200_disassoc(sc); 18133847Seh146360 (void) ipw2200_auth_and_assoc(sc); 18143847Seh146360 break; 18153847Seh146360 case IEEE80211_S_RUN: 18163847Seh146360 /* 18173847Seh146360 * We can send data now; update the fastpath with our 18183847Seh146360 * current associated BSSID and other relevant settings. 18193847Seh146360 */ 18205485Seh146360 wd.wd_secalloc = ieee80211_crypto_getciphertype(ic); 18215485Seh146360 wd.wd_opmode = ic->ic_opmode; 18223847Seh146360 IEEE80211_ADDR_COPY(wd.wd_bssid, ic->ic_bss->in_bssid); 18233847Seh146360 (void) mac_pdata_update(ic->ic_mach, &wd, sizeof (wd)); 18243847Seh146360 break; 18253847Seh146360 case IEEE80211_S_ASSOC: 18263847Seh146360 case IEEE80211_S_INIT: 18273847Seh146360 break; 18283847Seh146360 } 18293847Seh146360 18303847Seh146360 /* 18315485Seh146360 * notify to update the link, and WPA 18323847Seh146360 */ 18333847Seh146360 if ((ic->ic_state != IEEE80211_S_RUN) && (state == IEEE80211_S_RUN)) { 18345485Seh146360 ieee80211_notify_node_join(ic, ic->ic_bss); 18353847Seh146360 } else if ((ic->ic_state == IEEE80211_S_RUN) && 18364812Seh146360 (state != IEEE80211_S_RUN)) { 18375485Seh146360 ieee80211_notify_node_leave(ic, ic->ic_bss); 18383847Seh146360 } 18393847Seh146360 18403847Seh146360 IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT, 18413847Seh146360 "ipw2200_newstat(): %s -> %s\n", 18423847Seh146360 ieee80211_state_name[ic->ic_state], 18433847Seh146360 ieee80211_state_name[state])); 18443847Seh146360 18453847Seh146360 ic->ic_state = state; 18463847Seh146360 return (DDI_SUCCESS); 18473847Seh146360 } 18483847Seh146360 /* 18493847Seh146360 * GLD operations 18503847Seh146360 */ 18513847Seh146360 /* ARGSUSED */ 18523847Seh146360 static int 18533847Seh146360 ipw2200_m_stat(void *arg, uint_t stat, uint64_t *val) 18543847Seh146360 { 18557194Seh146360 ieee80211com_t *ic = (ieee80211com_t *)arg; 18567194Seh146360 struct ipw2200_softc *sc = (struct ipw2200_softc *)ic; 18573847Seh146360 18583847Seh146360 IPW2200_DBG(IPW2200_DBG_GLD, (((struct ipw2200_softc *)arg)->sc_dip, 18593847Seh146360 CE_CONT, 18603847Seh146360 "ipw2200_m_stat(): enter\n")); 18613847Seh146360 /* 18623847Seh146360 * Some of below statistic data are from hardware, some from net80211 18633847Seh146360 */ 18643847Seh146360 switch (stat) { 18657194Seh146360 case MAC_STAT_NOXMTBUF: 18667194Seh146360 *val = ic->ic_stats.is_tx_nobuf; 18677194Seh146360 break; 18687194Seh146360 case MAC_STAT_IERRORS: 18697194Seh146360 *val = sc->sc_stats.sc_rx_len_err; 18707194Seh146360 break; 18717194Seh146360 case MAC_STAT_OERRORS: 18727194Seh146360 *val = sc->sc_stats.sc_tx_discard + 18737194Seh146360 sc->sc_stats.sc_tx_alloc_fail + 18747194Seh146360 sc->sc_stats.sc_tx_encap_fail + 18757194Seh146360 sc->sc_stats.sc_tx_crypto_fail; 18767194Seh146360 break; 18773847Seh146360 case MAC_STAT_RBYTES: 18783847Seh146360 *val = ic->ic_stats.is_rx_bytes; 18793847Seh146360 break; 18803847Seh146360 case MAC_STAT_IPACKETS: 18813847Seh146360 *val = ic->ic_stats.is_rx_frags; 18823847Seh146360 break; 18833847Seh146360 case MAC_STAT_OBYTES: 18843847Seh146360 *val = ic->ic_stats.is_tx_bytes; 18853847Seh146360 break; 18863847Seh146360 case MAC_STAT_OPACKETS: 18873847Seh146360 *val = ic->ic_stats.is_tx_frags; 18883847Seh146360 break; 18893847Seh146360 /* 18903847Seh146360 * Get below from hardware statistic, retraive net80211 value once 1s 18913847Seh146360 */ 18923847Seh146360 case WIFI_STAT_TX_FRAGS: 18933847Seh146360 case WIFI_STAT_MCAST_TX: 18943847Seh146360 case WIFI_STAT_TX_FAILED: 18953847Seh146360 case WIFI_STAT_TX_RETRANS: 18963847Seh146360 /* 18973847Seh146360 * Get blow information from net80211 18983847Seh146360 */ 18993847Seh146360 case WIFI_STAT_RTS_SUCCESS: 19003847Seh146360 case WIFI_STAT_RTS_FAILURE: 19013847Seh146360 case WIFI_STAT_ACK_FAILURE: 19023847Seh146360 case WIFI_STAT_RX_FRAGS: 19033847Seh146360 case WIFI_STAT_MCAST_RX: 19043847Seh146360 case WIFI_STAT_RX_DUPS: 19053847Seh146360 case WIFI_STAT_FCS_ERRORS: 19063847Seh146360 case WIFI_STAT_WEP_ERRORS: 19073847Seh146360 return (ieee80211_stat(ic, stat, val)); 19083847Seh146360 /* 19093847Seh146360 * Need be supported later 19103847Seh146360 */ 19113847Seh146360 case MAC_STAT_IFSPEED: 19123847Seh146360 default: 19133847Seh146360 return (ENOTSUP); 19143847Seh146360 } 19153847Seh146360 return (0); 19163847Seh146360 } 19173847Seh146360 19183847Seh146360 /* ARGSUSED */ 19193847Seh146360 static int 19203847Seh146360 ipw2200_m_multicst(void *arg, boolean_t add, const uint8_t *mca) 19213847Seh146360 { 19223847Seh146360 /* not supported */ 19233847Seh146360 IPW2200_DBG(IPW2200_DBG_GLD, (((struct ipw2200_softc *)arg)->sc_dip, 19243847Seh146360 CE_CONT, 19253847Seh146360 "ipw2200_m_multicst(): enter\n")); 19263847Seh146360 19277194Seh146360 return (0); 19283847Seh146360 } 19293847Seh146360 19303847Seh146360 /* 19313847Seh146360 * Multithread handler for linkstatus, fatal error recovery, get statistic 19323847Seh146360 */ 19333847Seh146360 static void 19343847Seh146360 ipw2200_thread(struct ipw2200_softc *sc) 19353847Seh146360 { 19363847Seh146360 struct ieee80211com *ic = &sc->sc_ic; 19374298Seh146360 enum ieee80211_state ostate; 19383847Seh146360 int32_t nlstate; 19393847Seh146360 int stat_cnt = 0; 19403847Seh146360 19413847Seh146360 IPW2200_DBG(IPW2200_DBG_SOFTINT, (sc->sc_dip, CE_CONT, 19423847Seh146360 "ipw2200_thread(): enter, linkstate %d\n", sc->sc_linkstate)); 19433847Seh146360 19443847Seh146360 mutex_enter(&sc->sc_mflock); 19453847Seh146360 19463847Seh146360 while (sc->sc_mfthread_switch) { 19473847Seh146360 /* 19487194Seh146360 * when radio is off or SUSPEND status, nothing to do 19497194Seh146360 */ 19507194Seh146360 if ((ipw2200_radio_status(sc) == 0) || 19517194Seh146360 sc->sc_flags & IPW2200_FLAG_SUSPEND) { 19527194Seh146360 goto wait_loop; 19537194Seh146360 } 19547194Seh146360 19557194Seh146360 /* 19563847Seh146360 * notify the link state 19573847Seh146360 */ 19583847Seh146360 if (ic->ic_mach && (sc->sc_flags & IPW2200_FLAG_LINK_CHANGE)) { 19593847Seh146360 19603847Seh146360 IPW2200_DBG(IPW2200_DBG_SOFTINT, (sc->sc_dip, CE_CONT, 19613847Seh146360 "ipw2200_thread(): link status --> %d\n", 19623847Seh146360 sc->sc_linkstate)); 19633847Seh146360 19643847Seh146360 sc->sc_flags &= ~IPW2200_FLAG_LINK_CHANGE; 19653847Seh146360 nlstate = sc->sc_linkstate; 19663847Seh146360 19673847Seh146360 mutex_exit(&sc->sc_mflock); 19683847Seh146360 mac_link_update(ic->ic_mach, nlstate); 19693847Seh146360 mutex_enter(&sc->sc_mflock); 19703847Seh146360 } 19713847Seh146360 19723847Seh146360 /* 19733847Seh146360 * recovery fatal error 19743847Seh146360 */ 19753847Seh146360 if (ic->ic_mach && 19763847Seh146360 (sc->sc_flags & IPW2200_FLAG_HW_ERR_RECOVER)) { 19773847Seh146360 19783847Seh146360 IPW2200_DBG(IPW2200_DBG_FATAL, (sc->sc_dip, CE_CONT, 19793847Seh146360 "ipw2200_thread(): " 19803847Seh146360 "try to recover fatal hw error\n")); 19813847Seh146360 19823847Seh146360 sc->sc_flags &= ~IPW2200_FLAG_HW_ERR_RECOVER; 19833847Seh146360 mutex_exit(&sc->sc_mflock); 19844298Seh146360 19855485Seh146360 /* stop again */ 19864298Seh146360 ostate = ic->ic_state; 19873847Seh146360 (void) ipw2200_init(sc); /* Force state machine */ 19885485Seh146360 19893847Seh146360 /* 19903847Seh146360 * workround. Delay for a while after init especially 19913847Seh146360 * when something wrong happened already. 19923847Seh146360 */ 19933847Seh146360 delay(drv_usectohz(delay_fatal_recover)); 19944298Seh146360 19954298Seh146360 /* 19964298Seh146360 * Init scan will recovery the original connection if 19974298Seh146360 * the original state is run 19984298Seh146360 */ 19994298Seh146360 if (ostate != IEEE80211_S_INIT) 20004298Seh146360 ieee80211_begin_scan(ic, 0); 20014298Seh146360 20023847Seh146360 mutex_enter(&sc->sc_mflock); 20033847Seh146360 } 20043847Seh146360 20053847Seh146360 /* 20063847Seh146360 * get statistic, the value will be retrieved by m_stat 20073847Seh146360 */ 20083847Seh146360 if (stat_cnt == 10) { 20093847Seh146360 20103847Seh146360 stat_cnt = 0; /* re-start */ 20113847Seh146360 mutex_exit(&sc->sc_mflock); 20123847Seh146360 ipw2200_get_statistics(sc); 20133847Seh146360 mutex_enter(&sc->sc_mflock); 20143847Seh146360 20153847Seh146360 } else 20163847Seh146360 stat_cnt++; /* until 1s */ 20173847Seh146360 20187194Seh146360 wait_loop: 20193847Seh146360 mutex_exit(&sc->sc_mflock); 20203847Seh146360 delay(drv_usectohz(delay_aux_thread)); 20213847Seh146360 mutex_enter(&sc->sc_mflock); 20223847Seh146360 20233847Seh146360 } 20243847Seh146360 sc->sc_mf_thread = NULL; 20253847Seh146360 cv_signal(&sc->sc_mfthread_cv); 20263847Seh146360 mutex_exit(&sc->sc_mflock); 20273847Seh146360 } 20283847Seh146360 20293847Seh146360 static int 20303847Seh146360 ipw2200_m_start(void *arg) 20313847Seh146360 { 20323847Seh146360 struct ipw2200_softc *sc = (struct ipw2200_softc *)arg; 20334298Seh146360 struct ieee80211com *ic = &sc->sc_ic; 20343847Seh146360 20353847Seh146360 IPW2200_DBG(IPW2200_DBG_GLD, (sc->sc_dip, CE_CONT, 20363847Seh146360 "ipw2200_m_start(): enter\n")); 20373847Seh146360 /* 20383847Seh146360 * initialize ipw2200 hardware, everything ok will start scan 20393847Seh146360 */ 20403847Seh146360 (void) ipw2200_init(sc); 20415485Seh146360 20424298Seh146360 /* 20434298Seh146360 * set the state machine to INIT 20444298Seh146360 */ 20454298Seh146360 ieee80211_new_state(ic, IEEE80211_S_INIT, -1); 20463847Seh146360 20473847Seh146360 sc->sc_flags |= IPW2200_FLAG_RUNNING; 20483847Seh146360 20495485Seh146360 /* 20505485Seh146360 * fix KCF bug. - workaround, need to fix it in net80211 20515485Seh146360 */ 20525485Seh146360 (void) crypto_mech2id(SUN_CKM_RC4); 20535485Seh146360 20547194Seh146360 return (0); 20553847Seh146360 } 20563847Seh146360 20573847Seh146360 static void 20583847Seh146360 ipw2200_m_stop(void *arg) 20593847Seh146360 { 20603847Seh146360 struct ipw2200_softc *sc = (struct ipw2200_softc *)arg; 20614298Seh146360 struct ieee80211com *ic = &sc->sc_ic; 20623847Seh146360 20633847Seh146360 IPW2200_DBG(IPW2200_DBG_GLD, (sc->sc_dip, CE_CONT, 20643847Seh146360 "ipw2200_m_stop(): enter\n")); 20653847Seh146360 20663847Seh146360 ipw2200_stop(sc); 20674298Seh146360 /* 20684298Seh146360 * set the state machine to INIT 20694298Seh146360 */ 20704298Seh146360 ieee80211_new_state(ic, IEEE80211_S_INIT, -1); 20713847Seh146360 20723847Seh146360 sc->sc_flags &= ~IPW2200_FLAG_RUNNING; 20733847Seh146360 } 20743847Seh146360 20753847Seh146360 static int 20763847Seh146360 ipw2200_m_unicst(void *arg, const uint8_t *macaddr) 20773847Seh146360 { 20783847Seh146360 struct ipw2200_softc *sc = (struct ipw2200_softc *)arg; 20793847Seh146360 struct ieee80211com *ic = &sc->sc_ic; 20803847Seh146360 int err; 20813847Seh146360 20823847Seh146360 IPW2200_DBG(IPW2200_DBG_GLD, (sc->sc_dip, CE_CONT, 20833847Seh146360 "ipw2200_m_unicst(): enter\n")); 20843847Seh146360 20853847Seh146360 IPW2200_DBG(IPW2200_DBG_GLD, (sc->sc_dip, CE_CONT, 20863847Seh146360 "ipw2200_m_unicst(): GLD setting MAC address to " 20873847Seh146360 "%02x:%02x:%02x:%02x:%02x:%02x\n", 20883847Seh146360 macaddr[0], macaddr[1], macaddr[2], 20893847Seh146360 macaddr[3], macaddr[4], macaddr[5])); 20903847Seh146360 20913847Seh146360 if (!IEEE80211_ADDR_EQ(ic->ic_macaddr, macaddr)) { 20923847Seh146360 20933847Seh146360 IEEE80211_ADDR_COPY(ic->ic_macaddr, macaddr); 20943847Seh146360 20953847Seh146360 if (sc->sc_flags & IPW2200_FLAG_RUNNING) { 20963847Seh146360 err = ipw2200_config(sc); 20973847Seh146360 if (err != DDI_SUCCESS) { 20983847Seh146360 IPW2200_WARN((sc->sc_dip, CE_WARN, 20993847Seh146360 "ipw2200_m_unicst(): " 21003847Seh146360 "device configuration failed\n")); 21013847Seh146360 goto fail; 21023847Seh146360 } 21033847Seh146360 } 21043847Seh146360 } 21057194Seh146360 return (0); 21063847Seh146360 fail: 21077194Seh146360 return (EIO); 21083847Seh146360 } 21093847Seh146360 21103847Seh146360 static int 21113847Seh146360 ipw2200_m_promisc(void *arg, boolean_t on) 21123847Seh146360 { 21133847Seh146360 /* not supported */ 21143847Seh146360 struct ipw2200_softc *sc = (struct ipw2200_softc *)arg; 21153847Seh146360 21163847Seh146360 IPW2200_DBG(IPW2200_DBG_GLD, (sc->sc_dip, CE_CONT, 21173847Seh146360 "ipw2200_m_promisc(): enter. " 21183847Seh146360 "GLD setting promiscuous mode - %d\n", on)); 21193847Seh146360 21207194Seh146360 return (0); 21213847Seh146360 } 21223847Seh146360 21233847Seh146360 static mblk_t * 21243847Seh146360 ipw2200_m_tx(void *arg, mblk_t *mp) 21253847Seh146360 { 21263847Seh146360 struct ipw2200_softc *sc = (struct ipw2200_softc *)arg; 21273847Seh146360 struct ieee80211com *ic = &sc->sc_ic; 21283847Seh146360 mblk_t *next; 21293847Seh146360 21303847Seh146360 /* 21317194Seh146360 * when driver in on suspend state, freemsgchain directly 21327194Seh146360 */ 21337194Seh146360 if (sc->sc_flags & IPW2200_FLAG_SUSPEND) { 21347194Seh146360 IPW2200_DBG(IPW2200_DBG_SUSPEND, (sc->sc_dip, CE_CONT, 21357194Seh146360 "ipw2200_m_tx(): suspend status, discard msg\n")); 21367194Seh146360 sc->sc_stats.sc_tx_discard++; /* discard data */ 21377194Seh146360 freemsgchain(mp); 21387194Seh146360 return (NULL); 21397194Seh146360 } 21407194Seh146360 21417194Seh146360 /* 21423847Seh146360 * No data frames go out unless we're associated; this 21433847Seh146360 * should not happen as the 802.11 layer does not enable 21443847Seh146360 * the xmit queue until we enter the RUN state. 21453847Seh146360 */ 21463847Seh146360 if (ic->ic_state != IEEE80211_S_RUN) { 21473847Seh146360 IPW2200_DBG(IPW2200_DBG_GLD, (sc->sc_dip, CE_CONT, 21483847Seh146360 "ipw2200_m_tx(): discard msg, ic_state = %u\n", 21493847Seh146360 ic->ic_state)); 21507194Seh146360 sc->sc_stats.sc_tx_discard++; /* discard data */ 21513847Seh146360 freemsgchain(mp); 21523847Seh146360 return (NULL); 21533847Seh146360 } 21543847Seh146360 21553847Seh146360 while (mp != NULL) { 21563847Seh146360 next = mp->b_next; 21573847Seh146360 mp->b_next = NULL; 21583847Seh146360 if (ipw2200_send(ic, mp, IEEE80211_FC0_TYPE_DATA) == 21595485Seh146360 ENOMEM) { 21603847Seh146360 mp->b_next = next; 21613847Seh146360 break; 21623847Seh146360 } 21633847Seh146360 mp = next; 21643847Seh146360 } 21653847Seh146360 return (mp); 21663847Seh146360 } 21673847Seh146360 21685485Seh146360 /* 21695485Seh146360 * ipw2200_send(): send data. softway to handle crypto_encap. 21705485Seh146360 */ 21713847Seh146360 static int 21723847Seh146360 ipw2200_send(ieee80211com_t *ic, mblk_t *mp, uint8_t type) 21733847Seh146360 { 21743847Seh146360 struct ipw2200_softc *sc = (struct ipw2200_softc *)ic; 21753847Seh146360 struct ieee80211_node *in; 21763847Seh146360 struct ieee80211_frame *wh; 21775485Seh146360 struct ieee80211_key *k; 21785485Seh146360 mblk_t *m0, *m; 21793847Seh146360 size_t cnt, off; 21803847Seh146360 struct ipw2200_tx_desc *txdsc; 21813847Seh146360 struct dma_region *dr; 21823847Seh146360 uint32_t idx; 21835485Seh146360 int err = DDI_SUCCESS; 21843847Seh146360 /* tmp pointer, used to pack header and payload */ 21853847Seh146360 uint8_t *p; 21863847Seh146360 21873847Seh146360 ASSERT(mp->b_next == NULL); 21883847Seh146360 IPW2200_DBG(IPW2200_DBG_GLD, (sc->sc_dip, CE_CONT, 21893847Seh146360 "ipw2200_send(): enter\n")); 21903847Seh146360 21913847Seh146360 if ((type & IEEE80211_FC0_TYPE_MASK) != IEEE80211_FC0_TYPE_DATA) { 21923847Seh146360 /* 21933847Seh146360 * skip all management frames since ipw2200 won't generate any 21943847Seh146360 * management frames. Therefore, drop this package. 21953847Seh146360 */ 21963847Seh146360 freemsg(mp); 21975485Seh146360 err = DDI_FAILURE; 21983847Seh146360 goto fail0; 21993847Seh146360 } 22003847Seh146360 22013847Seh146360 mutex_enter(&sc->sc_tx_lock); 22027194Seh146360 if (sc->sc_flags & IPW2200_FLAG_SUSPEND) { 22037194Seh146360 /* 22047194Seh146360 * when sending data, system runs into suspend status, 22057194Seh146360 * return fail directly 22067194Seh146360 */ 22077194Seh146360 err = ENXIO; 22087194Seh146360 goto fail0; 22097194Seh146360 } 22103847Seh146360 22113847Seh146360 /* 22123847Seh146360 * need 1 empty descriptor 22133847Seh146360 */ 22143847Seh146360 if (sc->sc_tx_free <= IPW2200_TX_RING_MIN) { 22155485Seh146360 mutex_enter(&sc->sc_resched_lock); 22163847Seh146360 IPW2200_DBG(IPW2200_DBG_RING, (sc->sc_dip, CE_WARN, 22173847Seh146360 "ipw2200_send(): no enough descriptors(%d)\n", 22183847Seh146360 sc->sc_tx_free)); 22193847Seh146360 ic->ic_stats.is_tx_nobuf++; /* no enough buffer */ 22203847Seh146360 sc->sc_flags |= IPW2200_FLAG_TX_SCHED; 22215485Seh146360 err = ENOMEM; 22225485Seh146360 mutex_exit(&sc->sc_resched_lock); 22233847Seh146360 goto fail1; 22243847Seh146360 } 22253847Seh146360 IPW2200_DBG(IPW2200_DBG_RING, (sc->sc_dip, CE_CONT, 22263847Seh146360 "ipw2200_send(): tx-free=%d,tx-curr=%d\n", 22273847Seh146360 sc->sc_tx_free, sc->sc_tx_cur)); 22283847Seh146360 22295485Seh146360 /* 22305485Seh146360 * put the mp into one blk, and use it to do the crypto_encap 22315485Seh146360 * if necessaary. 22325485Seh146360 */ 22335485Seh146360 m = allocb(msgdsize(mp) + 32, BPRI_MED); 22345485Seh146360 if (m == NULL) { /* can not alloc buf, drop this package */ 22355485Seh146360 IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT, 22365485Seh146360 "ipw2200_send(): msg allocation failed\n")); 22375485Seh146360 freemsg(mp); 22387194Seh146360 sc->sc_stats.sc_tx_alloc_fail++; /* alloc fail */ 22397194Seh146360 ic->ic_stats.is_tx_failed++; /* trans failed */ 22405485Seh146360 err = DDI_FAILURE; 22415485Seh146360 goto fail1; 22425485Seh146360 } 22435485Seh146360 for (off = 0, m0 = mp; m0 != NULL; m0 = m0->b_cont) { 22445485Seh146360 cnt = MBLKL(m0); 22455485Seh146360 (void) memcpy(m->b_rptr + off, m0->b_rptr, cnt); 22465485Seh146360 off += cnt; 22475485Seh146360 } 22485485Seh146360 m->b_wptr += off; 22495485Seh146360 22505485Seh146360 /* 22515485Seh146360 * find tx_node, and encapsulate the data 22525485Seh146360 */ 22535485Seh146360 wh = (struct ieee80211_frame *)m->b_rptr; 22543847Seh146360 in = ieee80211_find_txnode(ic, wh->i_addr1); 22553847Seh146360 if (in == NULL) { /* can not find the tx node, drop the package */ 22567194Seh146360 sc->sc_stats.sc_tx_encap_fail++; /* tx encap fail */ 22577194Seh146360 ic->ic_stats.is_tx_failed++; /* trans failed */ 22583847Seh146360 freemsg(mp); 22595485Seh146360 err = DDI_FAILURE; 22605485Seh146360 goto fail2; 22613847Seh146360 } 22623847Seh146360 in->in_inact = 0; 22635485Seh146360 22645485Seh146360 (void) ieee80211_encap(ic, m, in); 22653847Seh146360 ieee80211_free_node(in); 22663847Seh146360 22675485Seh146360 if (wh->i_fc[1] & IEEE80211_FC1_WEP) { 22685485Seh146360 k = ieee80211_crypto_encap(ic, m); 22695485Seh146360 if (k == NULL) { /* can not get the key, drop packages */ 22705485Seh146360 IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT, 22715485Seh146360 "ipw2200_send(): " 22725485Seh146360 "Encrypting 802.11 frame failed\n")); 22737194Seh146360 sc->sc_stats.sc_tx_crypto_fail++; /* tx encap fail */ 22747194Seh146360 ic->ic_stats.is_tx_failed++; /* trans failed */ 22755485Seh146360 freemsg(mp); 22765485Seh146360 err = DDI_FAILURE; 22775485Seh146360 goto fail2; 22785485Seh146360 } 22795485Seh146360 wh = (struct ieee80211_frame *)m->b_rptr; 22805485Seh146360 } 22815485Seh146360 22823847Seh146360 /* 22835485Seh146360 * get txdsc 22843847Seh146360 */ 22853847Seh146360 idx = sc->sc_tx_cur; 22863847Seh146360 txdsc = &sc->sc_txdsc[idx]; 22873847Seh146360 (void) memset(txdsc, 0, sizeof (*txdsc)); 22883847Seh146360 /* 22893847Seh146360 * extract header from message 22903847Seh146360 */ 22913847Seh146360 p = (uint8_t *)&txdsc->wh; 22925485Seh146360 off = sizeof (struct ieee80211_frame); 22935485Seh146360 (void) memcpy(p, m->b_rptr, off); 22943847Seh146360 /* 22953847Seh146360 * extract payload from message 22963847Seh146360 */ 22973847Seh146360 dr = &sc->sc_dma_txbufs[idx]; 22983847Seh146360 p = sc->sc_txbufs[idx]; 22995485Seh146360 cnt = MBLKL(m); 23005485Seh146360 (void) memcpy(p, m->b_rptr + off, cnt - off); 23015485Seh146360 cnt -= off; 23023847Seh146360 23033847Seh146360 txdsc->hdr.type = IPW2200_HDR_TYPE_DATA; 23043847Seh146360 txdsc->hdr.flags = IPW2200_HDR_FLAG_IRQ; 23053847Seh146360 txdsc->cmd = IPW2200_DATA_CMD_TX; 23065485Seh146360 txdsc->len = LE_16(cnt); 23073847Seh146360 txdsc->flags = 0; 23083847Seh146360 23093847Seh146360 if (ic->ic_opmode == IEEE80211_M_IBSS) { 23103847Seh146360 if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) 23113847Seh146360 txdsc->flags |= IPW2200_DATA_FLAG_NEED_ACK; 23123847Seh146360 } else if (!IEEE80211_IS_MULTICAST(wh->i_addr3)) 23133847Seh146360 txdsc->flags |= IPW2200_DATA_FLAG_NEED_ACK; 23143847Seh146360 23155485Seh146360 /* always set it to none wep, because it's handled by software */ 23165485Seh146360 txdsc->flags |= IPW2200_DATA_FLAG_NO_WEP; 23173847Seh146360 23183847Seh146360 if (ic->ic_flags & IEEE80211_F_SHPREAMBLE) 23193847Seh146360 txdsc->flags |= IPW2200_DATA_FLAG_SHPREAMBLE; 23203847Seh146360 23213847Seh146360 txdsc->nseg = LE_32(1); 23223847Seh146360 txdsc->seg_addr[0] = LE_32(dr->dr_pbase); 23235485Seh146360 txdsc->seg_len[0] = LE_32(cnt); 23243847Seh146360 23253847Seh146360 /* 23263847Seh146360 * DMA sync: buffer and desc 23273847Seh146360 */ 23283847Seh146360 (void) ddi_dma_sync(dr->dr_hnd, 0, 23293847Seh146360 IPW2200_TXBUF_SIZE, DDI_DMA_SYNC_FORDEV); 23303847Seh146360 (void) ddi_dma_sync(sc->sc_dma_txdsc.dr_hnd, 23313847Seh146360 idx * sizeof (struct ipw2200_tx_desc), 23323847Seh146360 sizeof (struct ipw2200_tx_desc), DDI_DMA_SYNC_FORDEV); 23333847Seh146360 23343847Seh146360 sc->sc_tx_cur = RING_FORWARD(sc->sc_tx_cur, 1, IPW2200_TX_RING_SIZE); 23353847Seh146360 sc->sc_tx_free--; 23363847Seh146360 23373847Seh146360 /* 23383847Seh146360 * update txcur 23393847Seh146360 */ 23403847Seh146360 ipw2200_csr_put32(sc, IPW2200_CSR_TX1_WRITE_INDEX, sc->sc_tx_cur); 23413847Seh146360 23423847Seh146360 /* 23433847Seh146360 * success, free the original message 23443847Seh146360 */ 23453847Seh146360 if (mp) 23463847Seh146360 freemsg(mp); 23475485Seh146360 fail2: 23485485Seh146360 if (m) 23495485Seh146360 freemsg(m); 23503847Seh146360 fail1: 23513847Seh146360 mutex_exit(&sc->sc_tx_lock); 23523847Seh146360 fail0: 23533847Seh146360 IPW2200_DBG(IPW2200_DBG_GLD, (sc->sc_dip, CE_CONT, 23543847Seh146360 "ipw2200_send(): exit - err=%d\n", err)); 23553847Seh146360 23563847Seh146360 return (err); 23573847Seh146360 } 23583847Seh146360 23593847Seh146360 /* 23603847Seh146360 * IOCTL handlers 23613847Seh146360 */ 23623847Seh146360 #define IEEE80211_IOCTL_REQUIRED (1) 23633847Seh146360 #define IEEE80211_IOCTL_NOT_REQUIRED (0) 23643847Seh146360 static void 23653847Seh146360 ipw2200_m_ioctl(void *arg, queue_t *q, mblk_t *m) 23663847Seh146360 { 23673847Seh146360 struct ipw2200_softc *sc = (struct ipw2200_softc *)arg; 23683847Seh146360 struct ieee80211com *ic = &sc->sc_ic; 23693847Seh146360 uint32_t err; 23703847Seh146360 23713847Seh146360 IPW2200_DBG(IPW2200_DBG_GLD, (sc->sc_dip, CE_CONT, 23723847Seh146360 "ipw2200_m_ioctl(): enter\n")); 23733847Seh146360 23743847Seh146360 /* 23753847Seh146360 * Check whether or not need to handle this in net80211 23763847Seh146360 * 23773847Seh146360 */ 23783847Seh146360 if (ipw2200_ioctl(sc, q, m) == IEEE80211_IOCTL_NOT_REQUIRED) 23793847Seh146360 return; 23803847Seh146360 23813847Seh146360 err = ieee80211_ioctl(ic, q, m); 23823847Seh146360 if (err == ENETRESET) { 23833847Seh146360 if (sc->sc_flags & IPW2200_FLAG_RUNNING) { 23843847Seh146360 (void) ipw2200_m_start(sc); 23853847Seh146360 (void) ieee80211_new_state(ic, 23863847Seh146360 IEEE80211_S_SCAN, -1); 23873847Seh146360 } 23883847Seh146360 } 23893847Seh146360 if (err == ERESTART) { 23903847Seh146360 if (sc->sc_flags & IPW2200_FLAG_RUNNING) 23913847Seh146360 (void) ipw2200_chip_reset(sc); 23923847Seh146360 } 23933847Seh146360 } 23943847Seh146360 static int 23953847Seh146360 ipw2200_ioctl(struct ipw2200_softc *sc, queue_t *q, mblk_t *m) 23963847Seh146360 { 23973847Seh146360 struct iocblk *iocp; 23987194Seh146360 uint32_t len, ret, cmd, mblen; 23993847Seh146360 mblk_t *m0; 24003847Seh146360 boolean_t need_privilege; 24013847Seh146360 boolean_t need_net80211; 24023847Seh146360 24037194Seh146360 mblen = MBLKL(m); 24047194Seh146360 if (mblen < sizeof (struct iocblk)) { 24053847Seh146360 IPW2200_DBG(IPW2200_DBG_IOCTL, (sc->sc_dip, CE_CONT, 24063847Seh146360 "ipw2200_ioctl(): ioctl buffer too short, %u\n", 24077194Seh146360 mblen)); 24083847Seh146360 miocnak(q, m, 0, EINVAL); 24093847Seh146360 /* 24103847Seh146360 * Buf not enough, do not need net80211 either 24113847Seh146360 */ 24123847Seh146360 return (IEEE80211_IOCTL_NOT_REQUIRED); 24133847Seh146360 } 24143847Seh146360 24153847Seh146360 /* 24163847Seh146360 * Validate the command 24173847Seh146360 */ 24187194Seh146360 iocp = (struct iocblk *)(uintptr_t)m->b_rptr; 24193847Seh146360 iocp->ioc_error = 0; 24203847Seh146360 cmd = iocp->ioc_cmd; 24213847Seh146360 need_privilege = B_TRUE; 24223847Seh146360 switch (cmd) { 24233847Seh146360 case WLAN_SET_PARAM: 24243847Seh146360 case WLAN_COMMAND: 24253847Seh146360 break; 24263847Seh146360 case WLAN_GET_PARAM: 24273847Seh146360 need_privilege = B_FALSE; 24283847Seh146360 break; 24293847Seh146360 default: 24303847Seh146360 IPW2200_DBG(IPW2200_DBG_IOCTL, (sc->sc_dip, CE_CONT, 24313847Seh146360 "ipw2200_ioctl(): unknown cmd 0x%x", cmd)); 24323847Seh146360 miocnak(q, m, 0, EINVAL); 24333847Seh146360 /* 24343847Seh146360 * Unknown cmd, do not need net80211 either 24353847Seh146360 */ 24363847Seh146360 return (IEEE80211_IOCTL_NOT_REQUIRED); 24373847Seh146360 } 24383847Seh146360 24397408SSebastien.Roy@Sun.COM if (need_privilege && (ret = secpolicy_dl_config(iocp->ioc_cr)) != 0) { 24407408SSebastien.Roy@Sun.COM miocnak(q, m, 0, ret); 24413847Seh146360 /* 24427408SSebastien.Roy@Sun.COM * privilege check fail, do not need net80211 either 24433847Seh146360 */ 24447408SSebastien.Roy@Sun.COM return (IEEE80211_IOCTL_NOT_REQUIRED); 24453847Seh146360 } 24467408SSebastien.Roy@Sun.COM 24473847Seh146360 /* 24483847Seh146360 * sanity check 24493847Seh146360 */ 24503847Seh146360 m0 = m->b_cont; 24513847Seh146360 if (iocp->ioc_count == 0 || iocp->ioc_count < sizeof (wldp_t) || 24523847Seh146360 m0 == NULL) { 24533847Seh146360 miocnak(q, m, 0, EINVAL); 24543847Seh146360 /* 24553847Seh146360 * invalid format, do not need net80211 either 24563847Seh146360 */ 24573847Seh146360 return (IEEE80211_IOCTL_NOT_REQUIRED); 24583847Seh146360 } 24593847Seh146360 /* 24603847Seh146360 * assuming single data block 24613847Seh146360 */ 24623847Seh146360 if (m0->b_cont) { 24633847Seh146360 freemsg(m0->b_cont); 24643847Seh146360 m0->b_cont = NULL; 24653847Seh146360 } 24663847Seh146360 24673847Seh146360 need_net80211 = B_FALSE; 24683847Seh146360 ret = ipw2200_getset(sc, m0, cmd, &need_net80211); 24693847Seh146360 if (!need_net80211) { 24703847Seh146360 len = msgdsize(m0); 24713847Seh146360 24723847Seh146360 IPW2200_DBG(IPW2200_DBG_IOCTL, (sc->sc_dip, CE_CONT, 24733847Seh146360 "ipw2200_ioctl(): go to call miocack with " 24743847Seh146360 "ret = %d, len = %d\n", ret, len)); 24753847Seh146360 miocack(q, m, len, ret); 24763847Seh146360 return (IEEE80211_IOCTL_NOT_REQUIRED); 24773847Seh146360 } 24783847Seh146360 24793847Seh146360 /* 24803847Seh146360 * IEEE80211_IOCTL - need net80211 handle 24813847Seh146360 */ 24823847Seh146360 return (IEEE80211_IOCTL_REQUIRED); 24833847Seh146360 } 24843847Seh146360 24853847Seh146360 static int 24863847Seh146360 ipw2200_getset(struct ipw2200_softc *sc, mblk_t *m, uint32_t cmd, 24873847Seh146360 boolean_t *need_net80211) 24883847Seh146360 { 24893847Seh146360 wldp_t *infp, *outfp; 24903847Seh146360 uint32_t id; 24913847Seh146360 int ret; 24923847Seh146360 24937194Seh146360 infp = (wldp_t *)(uintptr_t)m->b_rptr; 24947194Seh146360 outfp = (wldp_t *)(uintptr_t)m->b_rptr; 24953847Seh146360 outfp->wldp_result = WL_NOTSUPPORTED; 24963847Seh146360 24973847Seh146360 id = infp->wldp_id; 24983847Seh146360 IPW2200_DBG(IPW2200_DBG_IOCTL, (sc->sc_dip, CE_CONT, 24993847Seh146360 "ipw2200_getset(): id = 0x%x\n", id)); 25003847Seh146360 switch (id) { 25013847Seh146360 case WL_RADIO: /* which is not supported by net80211 */ 25023847Seh146360 ret = iwi_wificfg_radio(sc, cmd, outfp); 25033847Seh146360 break; 25043847Seh146360 case WL_DESIRED_RATES: /* hardware doesn't support fix-rates */ 25053847Seh146360 ret = iwi_wificfg_desrates(outfp); 25063847Seh146360 break; 25073847Seh146360 default: 25083847Seh146360 /* 25093847Seh146360 * The wifi IOCTL net80211 supported: 25103847Seh146360 * case WL_ESSID: 25113847Seh146360 * case WL_BSSID: 25123847Seh146360 * case WL_WEP_KEY_TAB: 25133847Seh146360 * case WL_WEP_KEY_ID: 25143847Seh146360 * case WL_AUTH_MODE: 25153847Seh146360 * case WL_ENCRYPTION: 25163847Seh146360 * case WL_BSS_TYPE: 25173847Seh146360 * case WL_ESS_LIST: 25183847Seh146360 * case WL_LINKSTATUS: 25193847Seh146360 * case WL_RSSI: 25203847Seh146360 * case WL_SCAN: 25213847Seh146360 * case WL_LOAD_DEFAULTS: 25223847Seh146360 * case WL_DISASSOCIATE: 25233847Seh146360 */ 25245838Seh146360 25255838Seh146360 /* 25265838Seh146360 * When radio is off, need to ignore all ioctl. What need to 25275838Seh146360 * do is to check radio status firstly. If radio is ON, pass 25285838Seh146360 * it to net80211, otherwise, return to upper layer directly. 25295838Seh146360 * 25305838Seh146360 * Considering the WL_SUCCESS also means WL_CONNECTED for 25315838Seh146360 * checking linkstatus, one exception for WL_LINKSTATUS is to 25325838Seh146360 * let net80211 handle it. 25335838Seh146360 */ 25345838Seh146360 if ((ipw2200_radio_status(sc) == 0) && 25355838Seh146360 (id != WL_LINKSTATUS)) { 25365838Seh146360 25375838Seh146360 IPW2200_REPORT((sc->sc_dip, CE_CONT, 25385838Seh146360 "iwi: radio is OFF\n")); 25395838Seh146360 25405838Seh146360 outfp->wldp_length = WIFI_BUF_OFFSET; 25415838Seh146360 outfp->wldp_result = WL_SUCCESS; 25425838Seh146360 ret = 0; 25435838Seh146360 break; 25445838Seh146360 } 25455838Seh146360 25463847Seh146360 *need_net80211 = B_TRUE; /* let net80211 do the rest */ 25473847Seh146360 return (0); 25483847Seh146360 } 25493847Seh146360 /* 25503847Seh146360 * we will overwrite everything 25513847Seh146360 */ 25523847Seh146360 m->b_wptr = m->b_rptr + outfp->wldp_length; 25533847Seh146360 return (ret); 25543847Seh146360 } 25553847Seh146360 2556*8367SFei.Feng@Sun.COM /* 2557*8367SFei.Feng@Sun.COM * Call back functions for get/set proporty 2558*8367SFei.Feng@Sun.COM */ 2559*8367SFei.Feng@Sun.COM static int 2560*8367SFei.Feng@Sun.COM ipw2200_m_getprop(void *arg, const char *pr_name, mac_prop_id_t wldp_pr_num, 2561*8367SFei.Feng@Sun.COM uint_t pr_flags, uint_t wldp_length, void *wldp_buf, uint_t *perm) 2562*8367SFei.Feng@Sun.COM { 2563*8367SFei.Feng@Sun.COM struct ipw2200_softc *sc = (struct ipw2200_softc *)arg; 2564*8367SFei.Feng@Sun.COM struct ieee80211com *ic = &sc->sc_ic; 2565*8367SFei.Feng@Sun.COM int err = 0; 2566*8367SFei.Feng@Sun.COM 2567*8367SFei.Feng@Sun.COM switch (wldp_pr_num) { 2568*8367SFei.Feng@Sun.COM /* mac_prop_id */ 2569*8367SFei.Feng@Sun.COM case MAC_PROP_WL_DESIRED_RATES: 2570*8367SFei.Feng@Sun.COM IPW2200_DBG(IPW2200_DBG_BRUSSELS, (sc->sc_dip, CE_CONT, 2571*8367SFei.Feng@Sun.COM "ipw2200_m_getprop(): Not Support DESIRED_RATES\n")); 2572*8367SFei.Feng@Sun.COM err = ENOTSUP; 2573*8367SFei.Feng@Sun.COM break; 2574*8367SFei.Feng@Sun.COM case MAC_PROP_WL_RADIO: 2575*8367SFei.Feng@Sun.COM *(wl_linkstatus_t *)wldp_buf = ipw2200_radio_status(sc); 2576*8367SFei.Feng@Sun.COM break; 2577*8367SFei.Feng@Sun.COM default: 2578*8367SFei.Feng@Sun.COM /* go through net80211 */ 2579*8367SFei.Feng@Sun.COM err = ieee80211_getprop(ic, pr_name, wldp_pr_num, pr_flags, 2580*8367SFei.Feng@Sun.COM wldp_length, wldp_buf, perm); 2581*8367SFei.Feng@Sun.COM break; 2582*8367SFei.Feng@Sun.COM } 2583*8367SFei.Feng@Sun.COM 2584*8367SFei.Feng@Sun.COM return (err); 2585*8367SFei.Feng@Sun.COM } 2586*8367SFei.Feng@Sun.COM 2587*8367SFei.Feng@Sun.COM static int 2588*8367SFei.Feng@Sun.COM ipw2200_m_setprop(void *arg, const char *pr_name, mac_prop_id_t wldp_pr_num, 2589*8367SFei.Feng@Sun.COM uint_t wldp_length, const void *wldp_buf) 2590*8367SFei.Feng@Sun.COM { 2591*8367SFei.Feng@Sun.COM struct ipw2200_softc *sc = (struct ipw2200_softc *)arg; 2592*8367SFei.Feng@Sun.COM struct ieee80211com *ic = &sc->sc_ic; 2593*8367SFei.Feng@Sun.COM int err; 2594*8367SFei.Feng@Sun.COM 2595*8367SFei.Feng@Sun.COM switch (wldp_pr_num) { 2596*8367SFei.Feng@Sun.COM /* mac_prop_id */ 2597*8367SFei.Feng@Sun.COM case MAC_PROP_WL_DESIRED_RATES: 2598*8367SFei.Feng@Sun.COM IPW2200_DBG(IPW2200_DBG_BRUSSELS, (sc->sc_dip, CE_CONT, 2599*8367SFei.Feng@Sun.COM "ipw2200_m_setprop(): Not Support DESIRED_RATES\n")); 2600*8367SFei.Feng@Sun.COM err = ENOTSUP; 2601*8367SFei.Feng@Sun.COM break; 2602*8367SFei.Feng@Sun.COM case MAC_PROP_WL_RADIO: 2603*8367SFei.Feng@Sun.COM IPW2200_DBG(IPW2200_DBG_BRUSSELS, (sc->sc_dip, CE_CONT, 2604*8367SFei.Feng@Sun.COM "ipw2200_m_setprop(): Not Support RADIO\n")); 2605*8367SFei.Feng@Sun.COM err = ENOTSUP; 2606*8367SFei.Feng@Sun.COM break; 2607*8367SFei.Feng@Sun.COM default: 2608*8367SFei.Feng@Sun.COM /* go through net80211 */ 2609*8367SFei.Feng@Sun.COM err = ieee80211_setprop(ic, pr_name, wldp_pr_num, wldp_length, 2610*8367SFei.Feng@Sun.COM wldp_buf); 2611*8367SFei.Feng@Sun.COM break; 2612*8367SFei.Feng@Sun.COM } 2613*8367SFei.Feng@Sun.COM 2614*8367SFei.Feng@Sun.COM if (err == ENETRESET) { 2615*8367SFei.Feng@Sun.COM if (sc->sc_flags & IPW2200_FLAG_RUNNING) { 2616*8367SFei.Feng@Sun.COM (void) ipw2200_m_start(sc); 2617*8367SFei.Feng@Sun.COM (void) ieee80211_new_state(ic, 2618*8367SFei.Feng@Sun.COM IEEE80211_S_SCAN, -1); 2619*8367SFei.Feng@Sun.COM } 2620*8367SFei.Feng@Sun.COM err = 0; 2621*8367SFei.Feng@Sun.COM } 2622*8367SFei.Feng@Sun.COM 2623*8367SFei.Feng@Sun.COM return (err); 2624*8367SFei.Feng@Sun.COM } 2625*8367SFei.Feng@Sun.COM 26263847Seh146360 static int 26273847Seh146360 iwi_wificfg_radio(struct ipw2200_softc *sc, uint32_t cmd, wldp_t *outfp) 26283847Seh146360 { 26293847Seh146360 uint32_t ret = ENOTSUP; 26303847Seh146360 26313847Seh146360 switch (cmd) { 26323847Seh146360 case WLAN_GET_PARAM: 26333847Seh146360 *(wl_linkstatus_t *)(outfp->wldp_buf) = 26343847Seh146360 ipw2200_radio_status(sc); 26353847Seh146360 outfp->wldp_length = WIFI_BUF_OFFSET + sizeof (wl_linkstatus_t); 26363847Seh146360 outfp->wldp_result = WL_SUCCESS; 26373847Seh146360 ret = 0; /* command success */ 26383847Seh146360 break; 26393847Seh146360 case WLAN_SET_PARAM: 26403847Seh146360 default: 26413847Seh146360 break; 26423847Seh146360 } 26433847Seh146360 return (ret); 26443847Seh146360 } 26453847Seh146360 26463847Seh146360 static int 26473847Seh146360 iwi_wificfg_desrates(wldp_t *outfp) 26483847Seh146360 { 26493847Seh146360 /* return success, but with result NOTSUPPORTED */ 26503847Seh146360 outfp->wldp_length = WIFI_BUF_OFFSET; 26513847Seh146360 outfp->wldp_result = WL_NOTSUPPORTED; 26523847Seh146360 return (0); 26533847Seh146360 } 26543847Seh146360 /* End of IOCTL Handlers */ 26553847Seh146360 26563847Seh146360 void 26573847Seh146360 ipw2200_fix_channel(struct ieee80211com *ic, mblk_t *m) 26583847Seh146360 { 26593847Seh146360 struct ieee80211_frame *wh; 26603847Seh146360 uint8_t subtype; 26613847Seh146360 uint8_t *frm, *efrm; 26623847Seh146360 26633847Seh146360 wh = (struct ieee80211_frame *)m->b_rptr; 26643847Seh146360 26653847Seh146360 if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) != IEEE80211_FC0_TYPE_MGT) 26663847Seh146360 return; 26673847Seh146360 26683847Seh146360 subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK; 26693847Seh146360 26703847Seh146360 if (subtype != IEEE80211_FC0_SUBTYPE_BEACON && 26713847Seh146360 subtype != IEEE80211_FC0_SUBTYPE_PROBE_RESP) 26723847Seh146360 return; 26733847Seh146360 26743847Seh146360 /* 26753847Seh146360 * assume the message contains only 1 block 26763847Seh146360 */ 26773847Seh146360 frm = (uint8_t *)(wh + 1); 26783847Seh146360 efrm = (uint8_t *)m->b_wptr; 26793847Seh146360 frm += 12; /* skip tstamp, bintval and capinfo fields */ 26803847Seh146360 while (frm < efrm) { 26813847Seh146360 if (*frm == IEEE80211_ELEMID_DSPARMS) 26823847Seh146360 #if IEEE80211_CHAN_MAX < 255 26833847Seh146360 if (frm[2] <= IEEE80211_CHAN_MAX) 26843847Seh146360 #endif 26853847Seh146360 ic->ic_curchan = &ic->ic_sup_channels[frm[2]]; 26863847Seh146360 frm += frm[1] + 2; 26873847Seh146360 } 26883847Seh146360 } 26893847Seh146360 26903847Seh146360 static void 26913847Seh146360 ipw2200_rcv_frame(struct ipw2200_softc *sc, struct ipw2200_frame *frame) 26923847Seh146360 { 26933847Seh146360 struct ieee80211com *ic = &sc->sc_ic; 26943847Seh146360 uint8_t *data = (uint8_t *)frame; 26953847Seh146360 uint32_t len; 26963847Seh146360 struct ieee80211_frame *wh; 26973847Seh146360 struct ieee80211_node *in; 26983847Seh146360 mblk_t *m; 26993847Seh146360 27003847Seh146360 len = LE_16(frame->len); 27013847Seh146360 if ((len < sizeof (struct ieee80211_frame_min)) || 27023847Seh146360 (len > IPW2200_RXBUF_SIZE)) { 27033847Seh146360 IPW2200_DBG(IPW2200_DBG_RX, (sc->sc_dip, CE_CONT, 27043847Seh146360 "ipw2200_rcv_frame(): bad frame length=%u\n", 27053847Seh146360 LE_16(frame->len))); 27067194Seh146360 sc->sc_stats.sc_rx_len_err++; /* length doesn't work */ 27073847Seh146360 return; 27083847Seh146360 } 27093847Seh146360 IPW2200_DBG(IPW2200_DBG_RX, (sc->sc_dip, CE_CONT, 27103847Seh146360 "ipw2200_rcv_frame(): chan = %d, length = %d\n", frame->chan, len)); 27113847Seh146360 27125485Seh146360 /* 27135485Seh146360 * Skip the frame header, get the real data from the input 27145485Seh146360 */ 27155485Seh146360 data += sizeof (struct ipw2200_frame); 27165485Seh146360 27173847Seh146360 m = allocb(len, BPRI_MED); 27183847Seh146360 if (m) { 27193847Seh146360 (void) memcpy(m->b_wptr, data, len); 27203847Seh146360 m->b_wptr += len; 27213847Seh146360 27223847Seh146360 if (ic->ic_state == IEEE80211_S_SCAN) { 27233847Seh146360 ic->ic_ibss_chan = &ic->ic_sup_channels[frame->chan]; 27243847Seh146360 ipw2200_fix_channel(ic, m); 27253847Seh146360 } 27265485Seh146360 wh = (struct ieee80211_frame *)m->b_rptr; 27273847Seh146360 27283847Seh146360 in = ieee80211_find_rxnode(ic, wh); 27295485Seh146360 27303847Seh146360 IPW2200_DBG(IPW2200_DBG_RX, (sc->sc_dip, CE_CONT, 27313847Seh146360 "ipw2200_rcv_frame(): " 27325485Seh146360 "type = %x, subtype = %x, i_fc[1] = %x, " 27333847Seh146360 "ni_esslen:%d, ni_essid[0-5]:%c%c%c%c%c%c\n", 27345485Seh146360 wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK, 27355485Seh146360 wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK, 27365485Seh146360 wh->i_fc[1] & IEEE80211_FC1_WEP, 27373847Seh146360 in->in_esslen, 27383847Seh146360 in->in_essid[0], in->in_essid[1], in->in_essid[2], 27393847Seh146360 in->in_essid[3], in->in_essid[4], in->in_essid[5])); 27403847Seh146360 27413847Seh146360 (void) ieee80211_input(ic, m, in, frame->rssi_dbm, 0); 27423847Seh146360 27433847Seh146360 ieee80211_free_node(in); 27443847Seh146360 } 27453847Seh146360 else 27463847Seh146360 IPW2200_WARN((sc->sc_dip, CE_WARN, 27473847Seh146360 "ipw2200_rcv_frame(): " 27483847Seh146360 "cannot allocate receive message(%u)\n", 27493847Seh146360 LE_16(frame->len))); 27503847Seh146360 } 27513847Seh146360 27523847Seh146360 static void 27533847Seh146360 ipw2200_rcv_notif(struct ipw2200_softc *sc, struct ipw2200_notif *notif) 27543847Seh146360 { 27553847Seh146360 struct ieee80211com *ic = &sc->sc_ic; 27563847Seh146360 struct ipw2200_notif_association *assoc; 27573847Seh146360 struct ipw2200_notif_authentication *auth; 27583847Seh146360 uint8_t *ndata = (uint8_t *)notif; 27593847Seh146360 27603847Seh146360 IPW2200_DBG(IPW2200_DBG_NOTIF, (sc->sc_dip, CE_CONT, 27613847Seh146360 "ipw2200_rcv_notif(): type=%u\n", notif->type)); 27623847Seh146360 27633847Seh146360 ndata += sizeof (struct ipw2200_notif); 27643847Seh146360 switch (notif->type) { 27653847Seh146360 case IPW2200_NOTIF_TYPE_ASSOCIATION: 27663847Seh146360 assoc = (struct ipw2200_notif_association *)ndata; 27673847Seh146360 27683847Seh146360 IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT, 27693847Seh146360 "ipw2200_rcv_notif(): association=%u,%u\n", 27703847Seh146360 assoc->state, assoc->status)); 27713847Seh146360 27723847Seh146360 switch (assoc->state) { 27733847Seh146360 case IPW2200_ASSOC_SUCCESS: 27745485Seh146360 sc->sc_flags |= IPW2200_FLAG_ASSOCIATED; 27753847Seh146360 ieee80211_new_state(ic, IEEE80211_S_RUN, -1); 27763847Seh146360 break; 27773847Seh146360 case IPW2200_ASSOC_FAIL: 27785485Seh146360 sc->sc_flags &= ~IPW2200_FLAG_ASSOCIATED; 27795485Seh146360 ieee80211_begin_scan(ic, 1); 27803847Seh146360 break; 27813847Seh146360 default: 27823847Seh146360 break; 27833847Seh146360 } 27843847Seh146360 break; 27853847Seh146360 27863847Seh146360 case IPW2200_NOTIF_TYPE_AUTHENTICATION: 27873847Seh146360 auth = (struct ipw2200_notif_authentication *)ndata; 27883847Seh146360 27893847Seh146360 IPW2200_DBG(IPW2200_DBG_WIFI, (sc->sc_dip, CE_CONT, 27903847Seh146360 "ipw2200_rcv_notif(): authentication=%u\n", auth->state)); 27913847Seh146360 27923847Seh146360 switch (auth->state) { 27933847Seh146360 case IPW2200_AUTH_SUCCESS: 27943847Seh146360 ieee80211_new_state(ic, IEEE80211_S_ASSOC, -1); 27953847Seh146360 break; 27963847Seh146360 case IPW2200_AUTH_FAIL: 27975485Seh146360 sc->sc_flags &= ~IPW2200_FLAG_ASSOCIATED; 27983847Seh146360 break; 27993847Seh146360 default: 28003847Seh146360 IPW2200_DBG(IPW2200_DBG_NOTIF, (sc->sc_dip, CE_CONT, 28013847Seh146360 "ipw2200_rcv_notif(): " 28023847Seh146360 "unknown authentication state(%u)\n", auth->state)); 28033847Seh146360 break; 28043847Seh146360 } 28053847Seh146360 break; 28063847Seh146360 28073847Seh146360 case IPW2200_NOTIF_TYPE_SCAN_CHANNEL: 28083847Seh146360 IPW2200_DBG(IPW2200_DBG_SCAN, (sc->sc_dip, CE_CONT, 28093847Seh146360 "ipw2200_rcv_notif(): scan-channel=%u\n", 28103847Seh146360 ((struct ipw2200_notif_scan_channel *)ndata)->nchan)); 28113847Seh146360 break; 28123847Seh146360 28133847Seh146360 case IPW2200_NOTIF_TYPE_SCAN_COMPLETE: 28143847Seh146360 IPW2200_DBG(IPW2200_DBG_SCAN, (sc->sc_dip, CE_CONT, 28153847Seh146360 "ipw2200_rcv_notif():scan-completed,(%u,%u)\n", 28163847Seh146360 ((struct ipw2200_notif_scan_complete *)ndata)->nchan, 28173847Seh146360 ((struct ipw2200_notif_scan_complete *)ndata)->status)); 28183847Seh146360 28193847Seh146360 /* 28203847Seh146360 * scan complete 28213847Seh146360 */ 28223847Seh146360 sc->sc_flags &= ~IPW2200_FLAG_SCANNING; 28233847Seh146360 ieee80211_end_scan(ic); 28243847Seh146360 break; 28253847Seh146360 28263847Seh146360 case IPW2200_NOTIF_TYPE_BEACON: 28273847Seh146360 case IPW2200_NOTIF_TYPE_CALIBRATION: 28283847Seh146360 case IPW2200_NOTIF_TYPE_NOISE: 28293847Seh146360 /* 28303847Seh146360 * just ignore 28313847Seh146360 */ 28323847Seh146360 break; 28333847Seh146360 default: 28343847Seh146360 IPW2200_DBG(IPW2200_DBG_NOTIF, (sc->sc_dip, CE_CONT, 28353847Seh146360 "ipw2200_rcv_notif(): unknown notification type(%u)\n", 28363847Seh146360 notif->type)); 28373847Seh146360 break; 28383847Seh146360 } 28393847Seh146360 } 28403847Seh146360 28413847Seh146360 static uint_t 28423847Seh146360 ipw2200_intr(caddr_t arg) 28433847Seh146360 { 28447194Seh146360 struct ipw2200_softc *sc = (struct ipw2200_softc *)(uintptr_t)arg; 28453847Seh146360 struct ieee80211com *ic = &sc->sc_ic; 28463847Seh146360 uint32_t ireg, ridx, len, i; 28473847Seh146360 uint8_t *p, *rxbuf; 28483847Seh146360 struct dma_region *dr; 28493847Seh146360 struct ipw2200_hdr *hdr; 28503847Seh146360 uint32_t widx; 28513847Seh146360 28527194Seh146360 /* when it is on suspend, unclaim all interrupt directly */ 28537194Seh146360 if (sc->sc_flags & IPW2200_FLAG_SUSPEND) 28547194Seh146360 return (DDI_INTR_UNCLAIMED); 28557194Seh146360 28567194Seh146360 /* unclaim interrupt when it is not for iwi */ 28573847Seh146360 ireg = ipw2200_csr_get32(sc, IPW2200_CSR_INTR); 28587194Seh146360 if (ireg == 0xffffffff || 28597194Seh146360 !(ireg & IPW2200_INTR_MASK_ALL)) 28603847Seh146360 return (DDI_INTR_UNCLAIMED); 28613847Seh146360 28623847Seh146360 /* 28633847Seh146360 * mask all interrupts 28643847Seh146360 */ 28653847Seh146360 ipw2200_csr_put32(sc, IPW2200_CSR_INTR_MASK, 0); 28663847Seh146360 28673847Seh146360 /* 28683847Seh146360 * acknowledge all fired interrupts 28693847Seh146360 */ 28703847Seh146360 ipw2200_csr_put32(sc, IPW2200_CSR_INTR, ireg); 28713847Seh146360 28723847Seh146360 IPW2200_DBG(IPW2200_DBG_INT, (sc->sc_dip, CE_CONT, 28733847Seh146360 "ipw2200_intr(): enter. interrupt fired, int=0x%08x\n", ireg)); 28743847Seh146360 28753847Seh146360 if (ireg & IPW2200_INTR_MASK_ERR) { 28763847Seh146360 28773847Seh146360 IPW2200_DBG(IPW2200_DBG_FATAL, (sc->sc_dip, CE_CONT, 28783847Seh146360 "ipw2200 interrupt(): int= 0x%08x\n", ireg)); 28793847Seh146360 28803847Seh146360 /* 28813847Seh146360 * inform mfthread to recover hw error by stopping it 28823847Seh146360 */ 28833847Seh146360 mutex_enter(&sc->sc_mflock); 28843847Seh146360 sc->sc_flags |= IPW2200_FLAG_HW_ERR_RECOVER; 28853847Seh146360 mutex_exit(&sc->sc_mflock); 28863847Seh146360 28877194Seh146360 goto enable_interrupt; 28887194Seh146360 } 28897194Seh146360 28907194Seh146360 /* 28917194Seh146360 * FW intr 28927194Seh146360 */ 28937194Seh146360 if (ireg & IPW2200_INTR_FW_INITED) { 28947194Seh146360 mutex_enter(&sc->sc_ilock); 28957194Seh146360 sc->sc_fw_ok = 1; 28967194Seh146360 cv_signal(&sc->sc_fw_cond); 28977194Seh146360 mutex_exit(&sc->sc_ilock); 28987194Seh146360 } 28997194Seh146360 29007194Seh146360 /* 29017194Seh146360 * Radio OFF 29027194Seh146360 */ 29037194Seh146360 if (ireg & IPW2200_INTR_RADIO_OFF) { 29047194Seh146360 IPW2200_REPORT((sc->sc_dip, CE_CONT, 29057194Seh146360 "ipw2200_intr(): radio is OFF\n")); 29065485Seh146360 29075485Seh146360 /* 29087194Seh146360 * Stop hardware, will notify LINK is down. 29097194Seh146360 * Need a better scan solution to ensure 29107194Seh146360 * table has right value. 29115485Seh146360 */ 29127194Seh146360 ipw2200_stop(sc); 29137194Seh146360 } 29147194Seh146360 29157194Seh146360 /* 29167194Seh146360 * CMD intr 29177194Seh146360 */ 29187194Seh146360 if (ireg & IPW2200_INTR_CMD_TRANSFER) { 29197194Seh146360 mutex_enter(&sc->sc_cmd_lock); 29207194Seh146360 ridx = ipw2200_csr_get32(sc, 29217194Seh146360 IPW2200_CSR_CMD_READ_INDEX); 29227194Seh146360 i = RING_FORWARD(sc->sc_cmd_cur, 29237194Seh146360 sc->sc_cmd_free, IPW2200_CMD_RING_SIZE); 29247194Seh146360 len = RING_FLEN(i, ridx, IPW2200_CMD_RING_SIZE); 29257194Seh146360 29267194Seh146360 IPW2200_DBG(IPW2200_DBG_INT, (sc->sc_dip, CE_CONT, 29277194Seh146360 "ipw2200_intr(): cmd-ring,i=%u,ridx=%u,len=%u\n", 29287194Seh146360 i, ridx, len)); 29297194Seh146360 29307194Seh146360 if (len > 0) { 29317194Seh146360 sc->sc_cmd_free += len; 29327194Seh146360 cv_signal(&sc->sc_cmd_cond); 29333847Seh146360 } 29347194Seh146360 for (; i != ridx; 29357194Seh146360 i = RING_FORWARD(i, 1, IPW2200_CMD_RING_SIZE)) 29367194Seh146360 sc->sc_done[i] = 1; 29377194Seh146360 mutex_exit(&sc->sc_cmd_lock); 29387194Seh146360 29397194Seh146360 mutex_enter(&sc->sc_ilock); 29407194Seh146360 cv_signal(&sc->sc_cmd_status_cond); 29417194Seh146360 mutex_exit(&sc->sc_ilock); 29427194Seh146360 } 29437194Seh146360 29447194Seh146360 /* 29457194Seh146360 * RX intr 29467194Seh146360 */ 29477194Seh146360 if (ireg & IPW2200_INTR_RX_TRANSFER) { 29487194Seh146360 ridx = ipw2200_csr_get32(sc, 29497194Seh146360 IPW2200_CSR_RX_READ_INDEX); 29507194Seh146360 widx = ipw2200_csr_get32(sc, 29517194Seh146360 IPW2200_CSR_RX_WRITE_INDEX); 29527194Seh146360 29537194Seh146360 IPW2200_DBG(IPW2200_DBG_INT, (sc->sc_dip, CE_CONT, 29547194Seh146360 "ipw2200_intr(): rx-ring,widx=%u,ridx=%u\n", 29557194Seh146360 ridx, widx)); 29567194Seh146360 29577194Seh146360 for (; sc->sc_rx_cur != ridx; 29587194Seh146360 sc->sc_rx_cur = RING_FORWARD(sc->sc_rx_cur, 1, 29597194Seh146360 IPW2200_RX_RING_SIZE)) { 29607194Seh146360 i = sc->sc_rx_cur; 29617194Seh146360 rxbuf = sc->sc_rxbufs[i]; 29627194Seh146360 dr = &sc->sc_dma_rxbufs[i]; 29637194Seh146360 29647194Seh146360 /* 29657194Seh146360 * DMA sync 29667194Seh146360 */ 29677194Seh146360 (void) ddi_dma_sync(dr->dr_hnd, 0, 29687194Seh146360 IPW2200_RXBUF_SIZE, DDI_DMA_SYNC_FORKERNEL); 29697194Seh146360 /* 29707194Seh146360 * Get rx header(hdr) and rx data(p) from rxbuf 29717194Seh146360 */ 29727194Seh146360 p = rxbuf; 29737194Seh146360 hdr = (struct ipw2200_hdr *)p; 29747194Seh146360 p += sizeof (struct ipw2200_hdr); 29753847Seh146360 29763847Seh146360 IPW2200_DBG(IPW2200_DBG_INT, (sc->sc_dip, CE_CONT, 29777194Seh146360 "ipw2200_intr(): Rx hdr type %u\n", 29787194Seh146360 hdr->type)); 29797194Seh146360 29807194Seh146360 switch (hdr->type) { 29817194Seh146360 case IPW2200_HDR_TYPE_FRAME: 29827194Seh146360 ipw2200_rcv_frame(sc, 29837194Seh146360 (struct ipw2200_frame *)p); 29847194Seh146360 break; 29857194Seh146360 29867194Seh146360 case IPW2200_HDR_TYPE_NOTIF: 29877194Seh146360 ipw2200_rcv_notif(sc, 29887194Seh146360 (struct ipw2200_notif *)p); 29897194Seh146360 break; 29907194Seh146360 29917194Seh146360 default: 29927194Seh146360 IPW2200_DBG(IPW2200_DBG_INT, (sc->sc_dip, 29937194Seh146360 CE_CONT, 29947194Seh146360 "ipw2200_intr(): unknown Rx hdr type %u\n", 29953847Seh146360 hdr->type)); 29967194Seh146360 break; 29973847Seh146360 } 29983847Seh146360 } 29995485Seh146360 /* 30007194Seh146360 * write sc_rx_cur backward 1 step into RX_WRITE_INDEX 30015485Seh146360 */ 30027194Seh146360 ipw2200_csr_put32(sc, IPW2200_CSR_RX_WRITE_INDEX, 30037194Seh146360 RING_BACKWARD(sc->sc_rx_cur, 1, 30047194Seh146360 IPW2200_RX_RING_SIZE)); 30057194Seh146360 } 30067194Seh146360 30077194Seh146360 /* 30087194Seh146360 * TX intr 30097194Seh146360 */ 30107194Seh146360 if (ireg & IPW2200_INTR_TX1_TRANSFER) { 30117194Seh146360 mutex_enter(&sc->sc_tx_lock); 30127194Seh146360 ridx = ipw2200_csr_get32(sc, 30137194Seh146360 IPW2200_CSR_TX1_READ_INDEX); 30147194Seh146360 len = RING_FLEN(RING_FORWARD(sc->sc_tx_cur, 30157194Seh146360 sc->sc_tx_free, IPW2200_TX_RING_SIZE), 30167194Seh146360 ridx, IPW2200_TX_RING_SIZE); 30177194Seh146360 sc->sc_tx_free += len; 30187194Seh146360 IPW2200_DBG(IPW2200_DBG_RING, (sc->sc_dip, CE_CONT, 30197194Seh146360 "ipw2200_intr(): tx-ring,ridx=%u,len=%u\n", 30207194Seh146360 ridx, len)); 30217194Seh146360 mutex_exit(&sc->sc_tx_lock); 30227194Seh146360 30237194Seh146360 mutex_enter(&sc->sc_resched_lock); 30247194Seh146360 if ((sc->sc_tx_free > IPW2200_TX_RING_MIN) && 30257194Seh146360 (sc->sc_flags & IPW2200_FLAG_TX_SCHED)) { 30267194Seh146360 IPW2200_DBG(IPW2200_DBG_RING, (sc->sc_dip, 30277194Seh146360 CE_CONT, 30287194Seh146360 "ipw2200_intr(): Need Reschedule!")); 30297194Seh146360 sc->sc_flags &= ~IPW2200_FLAG_TX_SCHED; 30307194Seh146360 mac_tx_update(ic->ic_mach); 30313847Seh146360 } 30327194Seh146360 mutex_exit(&sc->sc_resched_lock); 30333847Seh146360 } 30343847Seh146360 30357194Seh146360 enable_interrupt: 30363847Seh146360 /* 30373847Seh146360 * enable all interrupts 30383847Seh146360 */ 30393847Seh146360 ipw2200_csr_put32(sc, IPW2200_CSR_INTR_MASK, IPW2200_INTR_MASK_ALL); 30403847Seh146360 30413847Seh146360 return (DDI_INTR_CLAIMED); 30423847Seh146360 } 30433847Seh146360 30443847Seh146360 30453847Seh146360 /* 30463847Seh146360 * Module Loading Data & Entry Points 30473847Seh146360 */ 30483847Seh146360 DDI_DEFINE_STREAM_OPS(ipw2200_devops, nulldev, nulldev, ipw2200_attach, 30497809SFei.Feng@Sun.COM ipw2200_detach, nodev, NULL, D_MP, NULL, ipw2200_quiesce); 30503847Seh146360 30513847Seh146360 static struct modldrv ipw2200_modldrv = { 30523847Seh146360 &mod_driverops, 30533847Seh146360 ipw2200_ident, 30543847Seh146360 &ipw2200_devops 30553847Seh146360 }; 30563847Seh146360 30573847Seh146360 static struct modlinkage ipw2200_modlinkage = { 30583847Seh146360 MODREV_1, 30593847Seh146360 &ipw2200_modldrv, 30603847Seh146360 NULL 30613847Seh146360 }; 30623847Seh146360 30633847Seh146360 int 30643847Seh146360 _init(void) 30653847Seh146360 { 30663847Seh146360 int status; 30673847Seh146360 30683847Seh146360 status = ddi_soft_state_init(&ipw2200_ssp, 30693847Seh146360 sizeof (struct ipw2200_softc), 1); 30703847Seh146360 if (status != DDI_SUCCESS) 30713847Seh146360 return (status); 30723847Seh146360 30733847Seh146360 mac_init_ops(&ipw2200_devops, IPW2200_DRV_NAME); 30743847Seh146360 status = mod_install(&ipw2200_modlinkage); 30753847Seh146360 if (status != DDI_SUCCESS) { 30763847Seh146360 mac_fini_ops(&ipw2200_devops); 30773847Seh146360 ddi_soft_state_fini(&ipw2200_ssp); 30783847Seh146360 } 30793847Seh146360 30803847Seh146360 return (status); 30813847Seh146360 } 30823847Seh146360 30833847Seh146360 int 30843847Seh146360 _fini(void) 30853847Seh146360 { 30863847Seh146360 int status; 30873847Seh146360 30883847Seh146360 status = mod_remove(&ipw2200_modlinkage); 30893847Seh146360 if (status == DDI_SUCCESS) { 30903847Seh146360 mac_fini_ops(&ipw2200_devops); 30913847Seh146360 ddi_soft_state_fini(&ipw2200_ssp); 30923847Seh146360 } 30933847Seh146360 30943847Seh146360 return (status); 30953847Seh146360 } 30963847Seh146360 30973847Seh146360 int 30983847Seh146360 _info(struct modinfo *modinfop) 30993847Seh146360 { 31003847Seh146360 return (mod_info(&ipw2200_modlinkage, modinfop)); 31013847Seh146360 } 3102